W ++Widgets लाइब्रेरी के साथ C ++ में क्रॉस-प्लेटफ़ॉर्म मॉड्यूलर अनुप्रयोगों का विकास

परिचय



लंबे समय तक मैंने विकास के बारे में लेख नहीं लिखे हैं, हालांकि मुझे वास्तव में लेखन प्रक्रिया पसंद है और मुझे अपने विचारों को क्रम में रखने की अनुमति देता है।

और अब, हाल ही में सामने आए घटनाक्रम के बारे में बात करने का अवसर मिला है। मुझे आशा है कि यह पाठ कुछ लोगों के लिए जीवन को सरल करेगा और नई ऊंचाइयों को जीतने के लिए प्रेरणा देगा।

इस बार हम wxWidgets लाइब्रेरी का उपयोग करके C ++ प्लगइन्स के साथ क्रॉस-प्लेटफ़ॉर्म एप्लिकेशन बनाने पर ध्यान केंद्रित करेंगे। विंडोज, लिनक्स और ओएस एक्स को सबसे लोकप्रिय माना जाएगा।

हमेशा की तरह, पाठकों के लिए प्रवेश सीमा को कम करने के लिए, पहला भाग अवलोकन किया जाएगा। कुछ के लिए, पहले भाग से जानकारी स्पष्ट प्रतीत होगी (विशेष रूप से उपकरण के संबंध में), लेकिन, फिर भी, मैं इसे आवश्यक मानता हूं, क्योंकि शुरुआती लोगों के लिए जानकारी न्यूनतम प्रयास के साथ विकास प्रक्रिया को व्यवस्थित करने की अनुमति देगा।
अगला स्पष्टीकरण के साथ परीक्षण आवेदन कोड का चरणबद्ध विश्लेषण है।

उपकरण


wxWidgets


सबसे पहले हमें चाहिए:

स्रोत कोड में WxWidgets लाइब्रेरी। मैं एसवीएन से नवीनतम संस्करणों का उपयोग करता हूं। बेशक, वे बग के बिना नहीं हैं, लेकिन उन्होंने कार्यक्षमता को लागू किया है जो आमतौर पर आधिकारिक रिलीज में पर्याप्त नहीं है।

स्रोत कोड यहां लिया जा सकता है: http://svn.wxwidgets.org/svn/wx/wxWidgets/trunk

आप यहां पुस्तकालय निर्माण प्रक्रिया के बारे में अधिक पढ़ सकते हैं: http://habrahabr.ru/post/123588/

उपरोक्त लेख की तुलना में निर्माण प्रक्रिया में अंतर केवल इतना है कि आपको DLL Debug और DLL Release कॉन्फ़िगरेशन का उपयोग Debug और Release बजाय करने की आवश्यकता है। इसके अलावा, यह जरूरी है कि wxWidgets वितरण में शामिल सभी परियोजनाओं की सेटिंग में, Multi-Threaded Debug DLL Multi-Threaded DLL के मान C/C++ -> Code Generation -> Runtime Library पैरामीटर में निर्दिष्ट हैं। यह अंत में "DLL" के साथ है। इस मामले में, wxWidgets गतिशील पुस्तकालयों के रूप में और गतिशील CRT के साथ संकलित किया जाएगा।

DLL Debug और DLL Release के कॉन्फ़िगरेशन को इकट्ठा करते समय DLL Release हो सकता है कि पहली बार सभी पुस्तकालयों को इकट्ठा नहीं किया जाता है। यह सब निर्भरता को निर्दिष्ट करने की समस्याओं के कारण है। यदि नहीं, तो फिर से विधानसभा शुरू करें। आमतौर पर 2-3 पुनरावृत्तियों गतिशील पुस्तकालयों का एक पूरा सेट प्राप्त करने के लिए पर्याप्त हैं।

आपको याद दिला दूं कि wxWidgets के साथ काम करने के लिए, आपके पास %WXWIN% पर्यावरण चर (विंडोज के लिए) होना चाहिए, जो wxWidgets स्रोत फ़ोल्डर को इंगित करता है। लिनक्स और ओएस एक्स के लिए, बस configure && make && make install

कॉन्फ़िगर करने के लिए पैरामीटर:


CMake


विभिन्न सेटिंग्स के साथ विभिन्न कार्य मशीनों पर विभिन्न प्लेटफार्मों के लिए परियोजना फाइलों के निर्माण की सुविधा के लिए, हम CMake परियोजना पीढ़ी प्रणाली का उपयोग करेंगे, जिसके बारे में, वैसे, हैबे पर कुछ अच्छे अवलोकन लेख हैं, उदाहरण के लिए, यहां:

सामान्य तौर पर, CMake एक ऐसा उपकरण है जिसके साथ विभिन्न मशीनों पर हम Visual Studio (Windows), Makefile / CodeBlocks (Linux), Makefile / XCode (OS X) प्रोजेक्ट फाइल्स को सोर्स कोड और थर्ड-पार्टी लाइब्रेरी में सही लिखित रास्तों के साथ बना सकते हैं, जो हमें असेंबली स्थापित करने पर अनावश्यक काम की बड़ी मात्रा से छुटकारा पाने की अनुमति देगा।

आप यहां सीएमके डाउनलोड कर सकते हैं: http://www.cmake.org/cmake/resources/software.html

यदि आपने डिबगिंग जानकारी के साथ wxWidgets (Linux, OS X) एकत्र किया है, और फिर रिलीज़ संस्करण इंस्टॉल करना चाहते हैं, तो आपको डीबग संस्करण के लिए स्थापना रद्द करने और मैन्युअल रूप से फ़ाइलों को हटाने की आवश्यकता है

यदि उपरोक्त फ़ाइलों को मैन्युअल रूप से हटाया नहीं जा सकता है, तो लाइब्रेरी के रिलीज़ संस्करण के लिए डीबग संस्करण से सेटिंग्स का उपयोग किया जाएगा। आवेदन इकट्ठा होगा, लेकिन शुरू नहीं होगा।

आपको इस तथ्य को भी ध्यान में रखना होगा कि यदि आपने wxWidgets का डीबग संस्करण स्थापित किया है, तो लिनक्स और ओएस एक्स पर आप सबसे अधिक आवेदन के डीबग संस्करण का निर्माण करने में सक्षम होंगे। वही रिलीज़ संस्करण पर लागू होता है। और सभी क्योंकि सीएमके wx-config स्क्रिप्ट से संकलन और लिंकिंग पैरामीटर लेता है, जो डिफ़ॉल्ट रूप से एक वर्तमान कॉन्फ़िगरेशन के लिए पैरामीटर देता है। या डिबग के लिए अलग से, या रिलीज के लिए अलग से।

दृश्य C ++ (Windows)


हम Windows में स्रोत कोड से wxWidgets और हमारे एप्लिकेशन को बनाने के लिए विजुअल C ++ 2012 का उपयोग करेंगे। एक्सप्रेस संस्करण भी उपयुक्त है। इसका मतलब यह है कि आईडीई और कंपाइलर सहित सभी विकास उपकरण मुफ्त होंगे।

टैंक में उन लोगों के लिए, मुफ्त विजुअल C ++ 2012 का एक लिंक: http://www.microsoft.com/visualstudio/eng/products/visual-studio-express-products

DialogBlocks


उपयोगकर्ता इंटरफ़ेस बनाने के लिए, इसलिए हाथ से सब कुछ लिखने के लिए नहीं, मैं डायलॉगब्लॉक्स एप्लिकेशन का उपयोग करने की सलाह देता हूं। तो, हाँ, यह भुगतान किया जाता है, लेकिन एक नि: शुल्क परीक्षण संस्करण है, जो सरल रूपों को बनाने के लिए पर्याप्त है। हालांकि, फिर से, कोई भी हाथ से सब कुछ लिखने के लिए परेशान नहीं करता है (वैसे, यह शैक्षिक उद्देश्यों के लिए भी बुरा नहीं है और स्पष्ट रूप से कोड को समझने पर सकारात्मक प्रभाव पड़ता है)।

यहां डायलॉगब्लॉक डाउनलोड करें: http://www.anthemion.co.uk/dialogblocks/download.htm

शुरुआत


फ़ोल्डर संरचना


मैं समझता हूं कि लगा-टिप पेन स्वाद और रंग में भिन्न हैं, और इस पर अपनी कैटलॉग संरचना को लागू करना कृतघ्न है, लेकिन कंपनी में कई वर्षों के काम के बाद हम एक निश्चित संरचना में आए, जो जटिल परियोजनाओं पर अच्छी तरह से काम करती है और जो समझने में काफी सरल है। इसलिए, इस लेख में हम इसका उपयोग करेंगे।


मुख्य CMakeList


मुख्य CMake स्क्रिप्ट में सभी परियोजनाओं के लिए सामान्य पैरामीटर और सेटिंग्स हैं, साथ ही कुछ सामान्य चर का विवरण भी है।

निर्माण / CMakeLists.txt

 cmake_minimum_required(VERSION 2.6.0) # We will generate both Debug and Release project files at the same time # for Windows and OS X if(WIN32 OR APPLE) set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE) set(LIB_SUFFIX "") endif(WIN32 OR APPLE) # For Linux we will need to execute CMake twice in order to generate # Debug and Release versions of Makefiles if(UNIX AND NOT APPLE) set(LINUX ON) set(LIB_SUFFIX /${CMAKE_BUILD_TYPE}) endif(UNIX AND NOT APPLE) set(PROJECT_NAME wxModularHost) project(${PROJECT_NAME}) # If there are any additional CMake modules (eg module which searches # for OpenCV or for DirectShow libs), then CMake should start searching # for them in current folder set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}) if(APPLE) set(OS_BASE_NAME Mac) set(CMAKE_OSX_SYSROOT "macosx10.6") endif(APPLE) if(LINUX) set(OS_BASE_NAME Linux) endif(LINUX) if(WIN32) set(OS_BASE_NAME Win) endif(WIN32) # Here we specify the list of wxWidgets libs which we will use in our project set(wxWidgets_USE_LIBS base core adv aui net gl xml propgrid html) # Here we specify that we need DLL version of wxWidgets libs and dynamic CRT # This is a MUST for applications with plugins. Both app and DLL plugin MUST # use the same instance of wxWidgets and the same event loop. set(BUILD_SHARED_LIBS 1) # Find wxWidgets library on current PC # You should have %WXWIN% environment variable which should point to the # directory where wxWidgets source code is placed. # wxWidgets libs MUST be compiled for both Debug and Release versions find_package(wxWidgets REQUIRED) # For some reason CMake generates wrong list of definitions. # Each item should start with /D but it does not. # We need to fix that manually set(wxWidgets_DEFINITIONS_TEMP) foreach(DEFINITION ${wxWidgets_DEFINITIONS}) if(NOT ${DEFINITION} MATCHES "/D.*") set(DEFINITION "/D${DEFINITION}") endif() set(wxWidgets_DEFINITIONS_TEMP ${wxWidgets_DEFINITIONS_TEMP} ${DEFINITION}) endforeach(${DEFINITION}) set(wxWidgets_DEFINITIONS ${wxWidgets_DEFINITIONS_TEMP}) # Here we add some definitions which prevent Visual Studio from # generating tons of warnings about unsecure function calls. # See http://msdn.microsoft.com/en-us/library/ttcz0bys.aspx if(WIN32) set(wxWidgets_DEFINITIONS ${wxWidgets_DEFINITIONS}; /D_CRT_SECURE_NO_DEPRECATE; /D_CRT_NONSTDC_NO_DEPRECATE; /D_UNICODE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /wd4996") endif(WIN32) # Since we are going to use wxWidgets in all subrojects, # it's OK to create the variable which will contain # common preprocessor definitions. This variable will be # used in subprojects. set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS}; ${wxWidgets_DEFINITIONS}) # Variable which points to root folder of our source code set(PROJECT_ROOT_DIR ${PROJECT_SOURCE_DIR}/..) # If any ThirdParty libraries are going to be # used in our project then it would be better to put # them into separate subfolder. We will create # the variable which points to this subfolder. set(THIRD_PARTY_DIR ${PROJECT_ROOT_DIR}/ThirdParty) set(BASE_INCLUDE_DIRECTORIES ${PROJECT_ROOT_DIR}/include) # Add wxWidgets include paths to the list of # include directories for all projects. include_directories(${wxWidgets_INCLUDE_DIRS}) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D__WXDEBUG__=1" ) # Now we can include all our subprojects. # CMake will generate project files for them add_subdirectory (../wxModularHost ../../wxModularHost/${OS_BASE_NAME}${LIB_SUFFIX}) 


परियोजनाओं के निर्माण के लिए लिपियों


उपयोग में आसानी के लिए, सीमेक शेल या बैच स्क्रिप्ट का उपयोग करना बेहतर है। यह नियमित संचालन जैसे सीएमके को कॉल करने और पर्यावरण चर स्थापित करने में थोड़ा समय बचाएगा।

विंडोज (cm.bat)

सुविधा के लिए, x86 और x64 के लिए विज़ुअल स्टूडियो प्रोजेक्ट बनाने के लिए अलग बैच स्क्रिप्ट का उपयोग करना बेहतर है, साथ ही एक सामान्य स्क्रिप्ट जो यह निर्धारित करेगी कि हम किस प्लेटफ़ॉर्म के लिए एप्लिकेशन बना रहे हैं:

 rem @echo off IF "%1" == "" GOTO NO_PARAMS IF "%1" == "x86" GOTO CMAKE_86 IF "%1" == "86" GOTO CMAKE_86 IF "%1" == "x64" GOTO CMAKE_64 IF "%1" == "64" GOTO CMAKE_64 ECHO %1 ECHO "Nothing to do" GOTO End :CMAKE_86 ECHO "Configuring for x86" cm86.bat GOTO End :CMAKE_64 ECHO "Configuring for x64" cm64.bat GOTO End :NO_PARAMS ECHO "No parameters specified" IF EXIST "%ProgramW6432%" GOTO CMAKE_64 GOTO CMAKE_86 :End 


विंडोज (cm86.bat)

 rmdir /S /Q Win mkdir Win cd Win cmake ../ -G "Visual Studio 11" cd .. 


विंडोज (cm64.bat)

 rmdir /S /Q Win mkdir Win cd Win cmake ../ -G "Visual Studio 11 Win64" cd .. 


लिनक्स (cmLinux.sh)


 #!/bin/bash echo OS Type: $OSTYPE # ---------------------------------- # build Debug configuration makefile # ---------------------------------- echo building Debug configuration makefile echo directory "LinuxDebug" rm -dr "LinuxDebug" mkdir "LinuxDebug" cd "LinuxDebug" cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE:STRING=Debug ../ cd .. # ---------------------------------- # build Release configuration makefile # ---------------------------------- echo building Release configuration makefile echo directory "LinuxRelease" rm -dr "LinuxRelease" mkdir "LinuxRelease" cd "LinuxRelease" cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE:STRING=Release ../ cd .. 


न्यूनतम wxWidgets CMake के साथ आवेदन


आरंभ करने के लिए, हमें एक एप्लिकेशन टेम्पलेट की आवश्यकता होती है जिसमें हम कार्यक्षमता जोड़ेंगे। आइए एक साधारण एप्लिकेशन बनाएं जिसमें एक एप्लिकेशन क्लास (जैसे wxModularHostApp) और एक मुख्य फॉर्म क्लास (जैसे मेनफ्रेम) शामिल हैं।

यदि आप DialogBlocks का उपयोग करते हैं, तो, प्रत्येक वर्ग के लिए h / cpp फ़ाइलों की एक जोड़ी के अलावा, हमें एक और .rc फ़ाइल मिलती है जिसमें एप्लिकेशन संसाधनों का वर्णन होता है।

मैं कोड नहीं दूंगा। एक उदाहरण पिछले लेखों या %WXWIN%\samples\minimal फ़ोल्डर से लिया जा सकता है

अब हम CMake स्क्रिप्ट बनाने के लिए तैयार हैं।

wxModularHost / CMakeLists.txt

 set(SRCS MainFrame.cpp wxModularHostApp.cpp) set(HEADERS MainFrame.h wxModularHostApp.h) set(INCLUDE_DIRECTORIES ${BASE_INCLUDE_DIRECTORIES}) if(WIN32) set(SRCS ${SRCS} wxModularHost.rc) set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS}; /D_USRDLL; /DwxUSE_NO_MANIFEST=1; /D__STDC_CONSTANT_MACROS) endif(WIN32) set(LIBS ${wxWidgets_LIBRARIES}) set(EXECUTABLE_NAME wxModularHost) add_definitions(${PREPROCESSOR_DEFINITIONS}) include_directories(${INCLUDE_DIRECTORIES}) if(WIN32) set(EXECUTABLE_TYPE WIN32) endif(WIN32) if(APPLE) set(MACOSX_BUNDLE YES) set(EXECUTABLE_TYPE MACOSX_BUNDLE) endif(APPLE) if(LINUX) set(EXECUTABLE_TYPE "") endif(LINUX) set(PROJECT_FILES ${SRCS} ${HFILES}) add_executable(${EXECUTABLE_NAME} ${EXECUTABLE_TYPE} ${PROJECT_FILES}) set(EXE_DIR bin) set(TARGET_LOCATION ${PROJECT_SOURCE_DIR}/${EXE_DIR}${LIB_SUFFIX}) set_target_properties(${EXECUTABLE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TARGET_LOCATION}) target_link_libraries(${EXECUTABLE_NAME} ${LIBS}) 


पहले से तैयार हेडर


संकलन प्रक्रिया को गति देने के लिए, पहले से तैयार हेडर ( http://en.wikipedia.org/wiki/Precompiled_header ) का उपयोग करना संभव है।

इस सुविधा को लागू करने के लिए, हमें दो फ़ाइलों की आवश्यकता है:
शामिल / stdwx.h

 #ifndef _STDWX_H_ #define _STDWX_H_ #if defined(WIN32) || defined(WINDOWS) #include <windows.h> #include <winnt.h> #define PLUGIN_EXPORTED_API WXEXPORT #else #define PLUGIN_EXPORTED_API extern "C" #endif // SYSTEM INCLUDES // For compilers that support precompilation, includes "wx/wx.h". #include "wx/wxprec.h" #ifdef __BORLANDC__ #pragma hdrstop #endif #include "wx/wx.h" #include <wx/cmdline.h> #include <wx/config.h> #include <wx/defs.h> #include <wx/dir.h> #include <wx/display.h> #include <wx/dynlib.h> #include <wx/dynload.h> #include <wx/fileconf.h> #include <wx/filename.h> #include <wx/frame.h> #include <wx/glcanvas.h> #include <wx/hashmap.h> #include <wx/image.h> #include <wx/imaglist.h> #include <wx/intl.h> #include <wx/list.h> #include <wx/notebook.h> #include <wx/stdpaths.h> #include <wx/sstream.h> #include <wx/thread.h> #include <wx/treebook.h> #include <wx/wfstream.h> #include <wx/wupdlock.h> #include <wx/textfile.h> #include <wx/socket.h> #include <wx/mimetype.h> #include <wx/ipc.h> #endif 


शामिल / stdwx.cpp

 #include "stdwx.h" 


सी ++ स्रोत फ़ाइलों के अलावा, हमें विजुअल स्टूडियो प्रोजेक्ट के लिए पूर्वनिर्धारित हेडर के साथ काम करने के लिए आवश्यक नियम जोड़ने के लिए सीएमके को पढ़ाने की भी आवश्यकता है। इसके लिए, एक विशेष मॉड्यूल हमारी मदद करेगा। मुझे याद नहीं है कि यह कहाँ से आया है, लेकिन यह यहाँ से आता है ( http://public.kitware.com/Bug/file_download.php?file_id=901&type=bug )। Precompiled शीर्षकों का समर्थन करने के लिए CMake मॉड्यूल का स्रोत कोड यहां देखा जा सकता है: https://github.com/T-Rex/wxModularApp/blob/master/build/PCHSupport.cmake

इस मॉड्यूल को बिल्ड / CmakeLists.txt में इस तरह शामिल किया जाना चाहिए:

निर्माण / CMakeLists.txt

 cmake_minimum_required(VERSION 2.6.0) include(PCHSupport.cmake) ... 


Precompiled हेडर को प्रोजेक्ट से कनेक्ट करने के बाद, सभी प्रोजेक्ट में पहली लाइन .CPP फाइलें लाइन होनी चाहिए

 #include "stdwx.h" 


GUI के बिना सबसे सरल प्लगइन


बेस क्लास लाइब्रेरी


प्लगइन के विकास के लिए, और वांछित प्रकार के प्लगइन्स को कुशलता से लोड करने के लिए आवेदन के लिए, आपको कुछ प्रारंभिक कार्य करने की आवश्यकता है। हमें एक पुस्तकालय बनाने की आवश्यकता है जिसमें प्लगइन का आधार सार वर्ग होगा, जिसकी कार्यक्षमता हमें प्रत्येक विशिष्ट प्लगइन में लागू करने की आवश्यकता होगी। इसके अलावा, हमारे मुख्य एप्लिकेशन में इस प्रकार के पॉइंटर्स होंगे, जो पुस्तकालयों के अंदर बनाए गए थे और विशिष्ट प्लगइन कार्यान्वयन के लिए संदर्भित थे।

यानी नतीजतन, हमें एक लाइब्रेरी सीखनी चाहिए जिसमें प्लगइन्स और मुख्य एप्लिकेशन दोनों में उपयोग किए जाने वाले डेटा प्रकार हैं।

wxNonGuiPluginBase / घोषणाएँ

 #ifndef _DECLARATIONS_H #define _DECLARATIONS_H #if defined(__WXMSW__) #ifdef DEMO_PLUGIN_EXPORTS #define DEMO_API __declspec(dllexport) #else #define DEMO_API __declspec(dllimport) #endif #else #define DEMO_API #endif #endif // _DECLARATIONS_H 


wxNonGuiPluginBase / wxNonGuiPluginBase.h

 #pragma once #include "Declarations.h" class DEMO_API wxNonGuiPluginBase : public wxObject { DECLARE_ABSTRACT_CLASS(wxNonGuiPluginBase) public: wxNonGuiPluginBase(); virtual ~wxNonGuiPluginBase(); virtual int Work() = 0; }; typedef wxNonGuiPluginBase * (*CreatePlugin_function)(); typedef void (*DeletePlugin_function)(wxNonGuiPluginBase * plugin); 


Declarations.h फ़ाइल में DEMO_API मैक्रो की परिभाषा है, जो इंगित करता है wxNonGuiPluginBase वर्ग हमसे निर्यात किया गया है या आयात किया गया है। यह DEMO_PLUGIN_EXPORTS प्रीप्रोसेसर DEMO_PLUGIN_EXPORTS की उपलब्धता के आधार पर dllexport/dllimport ( http://msdn.microsoft.com/en-us/library/3y1sfaz2(v=vs.90).aspx देखें ) का उपयोग करके किया जाता है। जब wxNonGuiPluginBase लाइब्रेरी का निर्माण करते हैं, तो हम DEMO_PLUGIN_EXPORTS को प्रीप्रोसेसर निर्देशों की सूची में निर्दिष्ट करते हैं, और जब प्लगिंग निर्माण करते हैं जो wxNonGuiGluginBase लाइब्रेरी पर निर्भर करते हैं और मुख्य एप्लिकेशन का निर्माण करते समय, हम निर्दिष्ट नहीं करते हैं। इस प्रकार, wxNonGuiPluginBase परियोजना के लिए, DEMO_API मान में dllexport विशेषता और अन्य सभी परियोजनाओं के लिए dllimport मान होगा।

wxNonGuiPluginBase / wxNonGuiPluginBase.cpp

 #include "stdwx.h" #include "wxNonGuiPluginBase.h" IMPLEMENT_ABSTRACT_CLASS(wxNonGuiPluginBase, wxObject) wxNonGuiPluginBase::wxNonGuiPluginBase() { } wxNonGuiPluginBase::~wxNonGuiPluginBase() { } 


wxNonGuiPluginBase / CMakeLists.txt

 set (SRCS wxNonGuiPluginBase.cpp) set (HEADERS Declarations.h wxNonGuiPluginBase.h) set(LIBRARY_NAME wxNonGuiPluginBase) if(WIN32) # Only for Windows: # we add additional preprocessor definitons set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS}; /D_USRDLL;/DDEMO_PLUGIN_EXPORTS;/D__STDC_CONSTANT_MACROS) endif(WIN32) # Add 2 files for precompiled headers set(SRCS ${SRCS} ${HEADERS} ${PROJECT_ROOT_DIR}/include/stdwx.h ${PROJECT_ROOT_DIR}/include/stdwx.cpp) # Set preprocessor definitions add_definitions(${PREPROCESSOR_DEFINITIONS}) # Set include directories include_directories(${INCLUDE_DIRECTORIES} ${BASE_INCLUDE_DIRECTORIES}) # Set library search paths link_directories(${LINK_DIRECTORIES}) # Setup the project name and assign the source files for this project add_library(${LIBRARY_NAME} SHARED ${SRCS}) #Setup the output folder set(DLL_DIR bin) set(TARGET_LOCATION ${PROJECT_SOURCE_DIR}/${DLL_DIR}${LIB_SUFFIX}) set_target_properties(${LIBRARY_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TARGET_LOCATION}) # Set additional dependencies target_link_libraries(${LIBRARY_NAME} ${wxWidgets_LIBRARIES}) # Setup precompiled headers set_precompiled_header(${LIBRARY_NAME} ${PROJECT_ROOT_DIR}/include/stdwx.h ${PROJECT_ROOT_DIR}/include/stdwx.cpp) 


जैसा कि पहले उल्लेख किया गया है, DEMO_PLUGIN_EXPORTS मैक्रो में DEMO_PLUGIN_EXPORTS मैक्रो की घोषणा है, जिसका उपयोग परिभाषाओं में किया जाता है। DEMO_PLUGIN_EXPORTS फ़ाइल

पहला प्लगइन


प्लगइन में, हमें wxNonGuiPluginBase से प्राप्त एक वर्ग बनाने की जरूरत है, wxNonGuiPluginBase कार्यशील कार्यक्षमता को लागू करना है, और वर्ग का एक उदाहरण बनाने और इसे हटाने के लिए निर्यात किए गए फ़ंक्शन भी करना है। इन कार्यों को मुख्य एप्लिकेशन द्वारा बुलाया जाएगा।

सेम्पलोनगूइपलुगिन / सैंपलनोनगुईपलुगिन.ह

 #pragma once #include <wxNonGuiPluginBase.h> class SampleNonGuiPlugin : public wxNonGuiPluginBase { DECLARE_DYNAMIC_CLASS(SampleNonGuiPlugin) public: SampleNonGuiPlugin(); virtual ~SampleNonGuiPlugin(); virtual int Work(); }; 


सेम्पलोनगूइपलुगिन / सैंपलोनगुईपलुगिन.कैप

 #include "stdwx.h" #include "SampleNonGuiPlugin.h" IMPLEMENT_DYNAMIC_CLASS(SampleNonGuiPlugin, wxObject) SampleNonGuiPlugin::SampleNonGuiPlugin() { } SampleNonGuiPlugin::~SampleNonGuiPlugin() { } int SampleNonGuiPlugin::Work() { return 10; } 


सेम्पलोनगूइपलुगिन / सैंपलनोनगुईपलुगिन.डेफ

 LIBRARY "SampleNonGuiPlugin" EXPORTS CreatePlugin=CreatePlugin DeletePlugin=DeletePlugin 


सेम्पलोनगूइपलुगिन / सैंपलनगूइग्लुगिन एक्सपोर्ट्स। पेपर

 #include "stdwx.h" #include <wxNonGuiPluginBase.h> #include "SampleNonGuiPlugin.h" PLUGIN_EXPORTED_API wxNonGuiPluginBase * CreatePlugin() { return new SampleNonGuiPlugin; } PLUGIN_EXPORTED_API void DeletePlugin(wxNonGuiPluginBase * plugin) { wxDELETE(plugin); } 


सेम्पलोनगूइपलुगिन / सीएमकेलिस्ट्स.टैक्स

 set (SRCS SampleNonGuiPlugin.cpp SampleNonGuiPluginExports.cpp) set (HEADERS SampleNonGuiPlugin.h) set(LIBRARY_NAME SampleNonGuiPlugin) if(WIN32) set(SRCS ${SRCS} ${LIBRARY_NAME}.def) set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/D_USRDLL;/D__STDC_CONSTANT_MACROS) set(LINK_DIRECTORIES ${PROJECT_ROOT_DIR}/wxNonGuiPluginBase/${OS_BASE_NAME}${LIB_SUFFIX}/$(ConfigurationName)) set(DEMO_LIBS wxNonGuiPluginBase.lib) endif(WIN32) set(SRCS ${SRCS} ${HEADERS} ${PROJECT_ROOT_DIR}/include/stdwx.h ${PROJECT_ROOT_DIR}/include/stdwx.cpp) add_definitions(${PREPROCESSOR_DEFINITIONS}) include_directories(${INCLUDE_DIRECTORIES} ${BASE_INCLUDE_DIRECTORIES} ${PROJECT_ROOT_DIR}/wxNonGuiPluginBase) link_directories(${LINK_DIRECTORIES}) add_library(${LIBRARY_NAME} SHARED ${SRCS}) set(DLL_DIR bin) set(TARGET_LOCATION ${PROJECT_SOURCE_DIR}/${DLL_DIR}/${CMAKE_CFG_INTDIR}/plugins) set_target_properties(${LIBRARY_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TARGET_LOCATION}) target_link_libraries(${LIBRARY_NAME} ${DEMO_LIBS} ${wxWidgets_LIBRARIES}) add_dependencies(${LIBRARY_NAME} wxNonGuiPluginBase) set_precompiled_header(${LIBRARY_NAME} ${PROJECT_ROOT_DIR}/include/stdwx.h ${PROJECT_ROOT_DIR}/include/stdwx.cpp) 


DEF फ़ाइल को गलती से स्रोत कोड फ़ाइलों की सूची में शामिल नहीं किया गया है। इसके उपयोग के बिना, निर्यात किए गए कार्यों के नाम को सजाया जाएगा और आवेदन DLL से इन कार्यों के लिए एक संकेतक प्राप्त करने में सक्षम नहीं होगा। आप डीएलएफ से डीएलएफ फ़ाइलों और निर्यात कार्यों का उपयोग करने के बारे में पढ़ सकते हैं:


प्लगइन प्रबंधन मॉड्यूल


इसलिए, इस समय हमारे पास एक होस्ट एप्लिकेशन और एक न्यूनतम प्लगइन है। अब आपको एप्लिकेशन में प्लगइन के डाउनलोड और उपयोग को लागू करने की आवश्यकता है। सार्वभौमिकता के उद्देश्य के लिए, कोड को अलग करना बेहतर होता है जो एक अलग कक्षा में मेमोरी से प्लगइन्स की खोज, लोडिंग और अनलोडिंग से निपटेगा और इससे भी बेहतर - एक अलग लाइब्रेरी में।

हमारे प्लगइन्स के कार्यान्वयन को एक बार फिर से याद करें:

इन आवश्यकताओं के आधार पर, हम निम्नलिखित निष्कर्ष पर आ सकते हैं:

इन आवश्यकताओं और निष्कर्षों के आधार पर, हम प्लगइन प्रबंधन वर्ग और कंटेनरों को लागू करते हैं:
wxModularCore / wxModularCore.h

 #pragma once #include <wxNonGuiPluginBase.h> // We need to know which DLL produced the specific plugin object. WX_DECLARE_HASH_MAP(wxNonGuiPluginBase*, wxDynamicLibrary*, wxPointerHash, wxPointerEqual, wxNonGuiPluginToDllDictionary); // We also need to keep the list of loaded DLLs WX_DECLARE_LIST(wxDynamicLibrary, wxDynamicLibraryList); // And separate list of loaded plugins for faster access. WX_DECLARE_LIST(wxNonGuiPluginBase, wxNonGuiPluginBaseList); class wxModularCoreSettings; class wxModularCore { public: wxModularCore(); virtual ~wxModularCore(); virtual wxString GetPluginsPath(bool forceProgramPath) const; virtual wxString GetPluginExt(); bool LoadPlugins(bool forceProgramPath); bool UnloadPlugins(); const wxNonGuiPluginBaseList & GetNonGuiPlugins() const; void Clear(); private: bool LoadNonGuiPlugins(const wxString & pluginsDirectory); bool UnloadNonGuiPlugins(); bool RegisterNonGuiPlugin(wxNonGuiPluginBase * plugin); bool UnRegisterNonGuiPlugin(wxNonGuiPluginBase * plugin); wxDynamicLibraryList m_DllList; wxNonGuiPluginToDllDictionary m_MapNonGuiPluginsDll; wxNonGuiPluginBaseList m_NonGuiPlugins; wxModularCoreSettings * m_Settings; }; 


कोड पर विस्तार से विचार करें:

wxModularCore/wxModularCore.cpp

 #include "stdwx.h" #include "wxModularCore.h" #include "wxModularCoreSettings.h" #include <wx/listimpl.cpp> WX_DEFINE_LIST(wxDynamicLibraryList); WX_DEFINE_LIST(wxNonGuiPluginBaseList); wxModularCore::wxModularCore() :m_Settings(new wxModularCoreSettings) { // This will allow to delete all objects from this list automatically m_DllList.DeleteContents(true); } wxModularCore::~wxModularCore() { Clear(); wxDELETE(m_Settings); } void wxModularCore::Clear() { UnloadPlugins(); // TODO: Add the code which resets the object to initial state } bool wxModularCore::LoadPlugins(bool forceProgramPath) { wxString pluginsRootDir = GetPluginsPath(forceProgramPath); wxFileName fn; fn.AssignDir(pluginsRootDir); wxLogDebug(wxT("%s"), fn.GetFullPath().data()); fn.AppendDir(wxT("plugins")); wxLogDebug(wxT("%s"), fn.GetFullPath().data()); if (!fn.DirExists()) return false; return LoadNonGuiPlugins(fn.GetFullPath()); } bool wxModularCore::UnloadPlugins() { return UnloadNonGuiPlugins(); } bool wxModularCore::LoadNonGuiPlugins(const wxString & pluginsDirectory) { wxFileName fn; fn.AssignDir(pluginsDirectory); wxLogDebug(wxT("%s"), fn.GetFullPath().data()); fn.AppendDir(wxT("nongui")); wxLogDebug(wxT("%s"), fn.GetFullPath().data()); if (!fn.DirExists()) return false; if(!wxDirExists(fn.GetFullPath())) return false; wxString wildcard = wxString::Format(wxT("*.%s"), GetPluginExt().GetData()); wxArrayString pluginPaths; wxDir::GetAllFiles(fn.GetFullPath(), &pluginPaths, wildcard); for(size_t i = 0; i < pluginPaths.GetCount(); ++i) { wxString fileName = pluginPaths[i]; wxDynamicLibrary * dll = new wxDynamicLibrary(fileName); if (dll->IsLoaded()) { wxDYNLIB_FUNCTION(CreatePlugin_function, CreatePlugin, *dll); if (pfnCreatePlugin) { wxNonGuiPluginBase* plugin = pfnCreatePlugin(); RegisterNonGuiPlugin(plugin); m_DllList.Append(dll); m_MapNonGuiPluginsDll[plugin] = dll; } else wxDELETE(dll); } } return true; } bool wxModularCore::UnloadNonGuiPlugins() { bool result = true; wxNonGuiPluginBase * plugin = NULL; while (m_NonGuiPlugins.GetFirst() && (plugin = m_NonGuiPlugins.GetFirst()->GetData())) { result &= UnRegisterNonGuiPlugin(plugin); } return result; } wxString wxModularCore::GetPluginsPath(bool forceProgramPath) const { wxString path; if (m_Settings->GetStoreInAppData() && !forceProgramPath) path = wxStandardPaths::Get().GetConfigDir(); else path = wxPathOnly(wxStandardPaths::Get().GetExecutablePath()); return path; } wxString wxModularCore::GetPluginExt() { return #if defined(__WXMSW__) wxT("dll"); #else wxT("so"); #endif } bool wxModularCore::RegisterNonGuiPlugin(wxNonGuiPluginBase * plugin) { m_NonGuiPlugins.Append(plugin); return true; } bool wxModularCore::UnRegisterNonGuiPlugin(wxNonGuiPluginBase * plugin) { wxNonGuiPluginBaseList::compatibility_iterator it = m_NonGuiPlugins.Find(plugin); if (it == NULL) return false; do { wxDynamicLibrary * dll = m_MapNonGuiPluginsDll[plugin]; if (!dll) // Probably plugin was not loaded from dll break; wxDYNLIB_FUNCTION(DeletePlugin_function, DeletePlugin, *dll); if (pfnDeletePlugin) { pfnDeletePlugin(plugin); m_NonGuiPlugins.Erase(it); m_MapNonGuiPluginsDll.erase(plugin); return true; } } while (false); // If plugin is not loaded from DLL (eg embedded into executable) wxDELETE(plugin); m_NonGuiPlugins.Erase(it); return true; } const wxNonGuiPluginBaseList & wxModularCore::GetNonGuiPlugins() const { return m_NonGuiPlugins; } 


यह एक ऐसी विधि पर ध्यान देने के लिए समझ में आता है LoadNonGuiPlugins()जिसमें एक मैक्रो की मदद से wxDYNLIB_FUNCTIONहम एक फ़ंक्शन को एक पॉइंटर प्राप्त करते हैं CreatePlugin()पॉइंटर प्रकार को CreatePlugin_functionwxNonGuiPluginBase.h में परिभाषित किया गया है।

यह उस विधि पर ध्यान देने के लिए भी समझ में आता है UnRegisterNonGuiPlugin()जिसमें पत्राचार तालिका में प्लगइन की खोज की जाती है; यदि प्लगइन पाया जाता है, तो इसके लिए मिली लाइब्रेरी से एक फ़ंक्शन को बुलाया जाता है DeletePlugin()और लाइब्रेरी को अनलोड किया जाता है। यदि प्लगइन तालिका में नहीं मिला है (उदाहरण के लिए, इसे एप्लिकेशन में लागू किया गया है और हमने इसे मैन्युअल रूप से सूची में जोड़ा है), तो यह बस मेमोरी से हटा दिया जाता है।

wxModularCore / wxModularCoreSettings.h

 #pragma once class wxModularCoreSettings { public: wxModularCoreSettings(); wxModularCoreSettings(const wxModularCoreSettings & settings); wxModularCoreSettings & operator = (const wxModularCoreSettings & settings); virtual ~wxModularCoreSettings(); void SetStoreInAppData(const bool & val); bool GetStoreInAppData() const; protected: virtual void CopyFrom(const wxModularCoreSettings & settings); private: bool m_bStoreInAppData; // Should we store data in Application Data folder or in .exe folder }; 


wxModularCore / wxModularCoreSettings.cpp

 #include "stdwx.h" #include "wxModularCoreSettings.h" wxModularCoreSettings::wxModularCoreSettings() : m_bStoreInAppData(false) { } wxModularCoreSettings::wxModularCoreSettings(const wxModularCoreSettings & settings) { CopyFrom(settings); } wxModularCoreSettings & wxModularCoreSettings::operator = (const wxModularCoreSettings & settings) { if (this != &settings) { CopyFrom(settings); } return *this; } wxModularCoreSettings::~wxModularCoreSettings() { } void wxModularCoreSettings::CopyFrom(const wxModularCoreSettings & settings) { m_bStoreInAppData = settings.m_bStoreInAppData; } void wxModularCoreSettings::SetStoreInAppData(const bool & value) { m_bStoreInAppData = value; } bool wxModularCoreSettings::GetStoreInAppData() const { return m_bStoreInAppData; } 


wxModularCore / CMakeLists.txt

 set (SRCS wxModularCore.cpp wxModularCoreSettings.cpp) set (HEADERS wxModularCore.h wxModularCoreSettings.h) set(LIBRARY_NAME wxModularCore) if(WIN32) set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/D__STDC_CONSTANT_MACROS) set(LINK_DIRECTORIES ${PROJECT_ROOT_DIR}/wxNonGuiPluginBase/${OS_BASE_NAME}${LIB_SUFFIX}/$(ConfigurationName)) set(DEMO_LIBS wxNonGuiPluginBase.lib) endif(WIN32) set(SRCS ${SRCS} ${HEADERS} ${PROJECT_ROOT_DIR}/include/stdwx.h ${PROJECT_ROOT_DIR}/include/stdwx.cpp) add_definitions(${PREPROCESSOR_DEFINITIONS}) include_directories(${INCLUDE_DIRECTORIES} ${BASE_INCLUDE_DIRECTORIES} ${PROJECT_ROOT_DIR}/wxNonGuiPluginBase) link_directories(${LINK_DIRECTORIES}) add_library(${LIBRARY_NAME} STATIC ${SRCS}) set(DLL_DIR bin) set(TARGET_LOCATION ${PROJECT_SOURCE_DIR}/${DLL_DIR}/${CMAKE_CFG_INTDIR}) set_target_properties(${LIBRARY_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TARGET_LOCATION}) target_link_libraries(${LIBRARY_NAME} ${DEMO_LIBS} ${wxWidgets_LIBRARIES}) add_dependencies(${LIBRARY_NAME} wxNonGuiPluginBase) set_precompiled_header(${LIBRARY_NAME} ${PROJECT_ROOT_DIR}/include/stdwx.h ${PROJECT_ROOT_DIR}/include/stdwx.cpp) 


और आपको मुख्य CMakeLists.txt में wxModularCore प्रोजेक्ट का पथ शामिल करना भी याद रखना चाहिए:

build / CMakeLists.txt

 ... add_subdirectory (../wxModularCore ../../wxModularCore/${OS_BASE_NAME}${LIB_SUFFIX}) ... 


अनुप्रयोग में GUI के बिना प्लगइन्स का उपयोग करना


प्लगइन्स को प्रबंधित करने वाला वर्ग तैयार हो जाने के बाद, हम एप्लिकेशन में इसका उपयोग शुरू कर सकते हैं।

सबसे पहले, wxModularCoreआवेदन वर्ग के लिए एक सूचक क्षेत्र :

wxModularHost / wxModularHostApp.h

 ... class wxModularHostApp: public wxApp { void TestNonGuiPlugins(); ... wxModularCore * m_PluginManager; ... }; 


wxModularHost / wxModularHostApp.cpp

 void wxModularHostApp::Init() { ////@begin wxModularHostApp member initialisation m_PluginManager = new wxModularCore; ////@end wxModularHostApp member initialisation } 


और इस तरह हम प्लगइन लोडिंग विधि को कॉल करेंगे और स्वयं प्लगइन्स का उपयोग करेंगे:

wxModularHost / wxModularHostApp.cpp

 bool wxModularHostApp::OnInit() { ... TestNonGuiPlugins(); MainFrame* mainWindow = new MainFrame( NULL ); mainWindow->Show(true); return true; } /* * Cleanup for wxModularHostApp */ int wxModularHostApp::OnExit() { wxDELETE(m_PluginManager); ////@begin wxModularHostApp cleanup return wxApp::OnExit(); ////@end wxModularHostApp cleanup } void wxModularHostApp::TestNonGuiPlugins() { if(m_PluginManager) { if(m_PluginManager->LoadPlugins(true)) { for(wxNonGuiPluginBaseList::Node * node = m_PluginManager->GetNonGuiPlugins().GetFirst(); node; node = node->GetNext()) { wxNonGuiPluginBase * plugin = node->GetData(); if(plugin) { wxLogDebug(wxT("Non-GUI plugin returns %i"), plugin->Work()); } } } } } 


विधि में, TestNonGuiPlugins()हम पहले से विधि LoadPlugins()को कॉल करते हैं wxModularCore, अगर यह सही तरीके से काम करता है, तो हम प्लगइन्स की सूची से गुजरते हैं और सूची के प्रत्येक तत्व के लिए विधि को कॉल करते हैं Work()(याद रखें, इसे wxNonGuiPluginBase प्रोजेक्ट में घोषित किया गया है, लेकिन वास्तव में यह प्रत्येक भरी हुई लाइब्रेरी के लिए एक अलग कार्यान्वयन है)।

सरलतम जीयूआई प्लगइन


केवल लॉजिक वाले मॉड्यूल कैसे बनाएं, यह पता लगाया गया है। अब एक मॉड्यूल के उदाहरण पर विचार करें जो एक विंडो बनाने की हिम्मत करता है:

wxGuiPluginBase / wxGuiPluginBase.h

 #pragma once #include "Declarations.h" class DEMO_API wxGuiPluginBase : public wxObject { DECLARE_ABSTRACT_CLASS(wxGuiPluginBase) public: wxGuiPluginBase(); virtual ~wxGuiPluginBase(); virtual wxString GetName() const = 0; virtual wxString GetId() const = 0; virtual wxWindow * CreatePanel(wxWindow * parent) = 0; }; typedef wxGuiPluginBase * (*CreateGuiPlugin_function)(); typedef void (*DeleteGuiPlugin_function)(wxGuiPluginBase * plugin); 


सार्वजनिक आभासी तरीके:

इस इंटरफ़ेस के आधार पर प्लगिन कार्यान्वयन:

सेम्पलगुइप्लगिन 1 / सैंपलगुईपलुगिन 1। एच

 #pragma once #include <wxGuiPluginBase.h> class SampleGuiPlugin1 : public wxGuiPluginBase { DECLARE_DYNAMIC_CLASS(SampleGuiPlugin1) public: SampleGuiPlugin1(); virtual ~SampleGuiPlugin1(); virtual wxString GetName() const; virtual wxString GetId() const; virtual wxWindow * CreatePanel(wxWindow * parent); }; 


सेम्पलगुइपलुगिन 1 / सैंपलग्यप्लुगिन 1।Cpp

 #include "stdwx.h" #include "SampleGuiPlugin1.h" IMPLEMENT_DYNAMIC_CLASS(SampleGuiPlugin1, wxObject) SampleGuiPlugin1::SampleGuiPlugin1() { } SampleGuiPlugin1::~SampleGuiPlugin1() { } wxString SampleGuiPlugin1::GetName() const { return _("GUI Plugin 1"); } wxString SampleGuiPlugin1::GetId() const { return wxT("{4E97DF66-5FBB-4719-AF17-76C1C82D3FE1}"); } wxWindow * SampleGuiPlugin1::CreatePanel(wxWindow * parent) { wxWindow * result= new wxPanel(parent, wxID_ANY); result->SetBackgroundColour(*wxRED); return result; } 


इस प्लगइन के लिए CMakeLists.txt लगभग वही है जो हमने GUI के बिना प्लगइन के लिए लिखा था। अंतर केवल परियोजना के नाम पर और परियोजना में शामिल फाइलों की सूची में होगा।

प्लगइन प्रबंधन मॉड्यूल Refactoring


फिलहाल, हमारे पास दो प्रकार के प्लगइन्स हैं। GUI के बिना प्लग इन के लिए, प्लगइन प्रबंधन वर्ग में पुस्तकालयों को लोड करने, प्लग इन को पंजीकृत करने, प्लग इन को अक्षम करने के लिए एक विशेष तरीका है। इस दृष्टिकोण के साथ, हमें प्रत्येक प्रकार के प्लगइन के लिए इन सभी तरीकों की नकल करने की आवश्यकता होगी। और उनमें से 5-10 होंगे, तब वर्ग अनुचित रूप से आकार में बढ़ेगा। इसलिए, तरीकों LoadXXXPlugins(), UnloadXXXPlugins(), RegisterXXXPlugin(), UnRegisterXXXPlugin()यह टेम्पलेट्स बनाने का फैसला किया गया था, सूचियों और हैश तालिकाओं एक वर्ग में खड़े उस वर्ग से विरासत में मिली wxModularCoreहै कि कोड है कि हमारे आवेदन के लिए विशिष्ट है शामिल होंगे।
wxModularCore / wxModularCore.h

 #pragma once // We need to keep the list of loaded DLLs WX_DECLARE_LIST(wxDynamicLibrary, wxDynamicLibraryList); class wxModularCoreSettings; class wxModularCore { public: wxModularCore(); virtual ~wxModularCore(); virtual wxString GetPluginsPath(bool forceProgramPath) const; virtual wxString GetPluginExt(); virtual bool LoadAllPlugins(bool forceProgramPath) = 0; virtual bool UnloadAllPlugins() = 0; virtual void Clear(); protected: wxDynamicLibraryList m_DllList; wxModularCoreSettings * m_Settings; template<typename PluginType, typename PluginListType> bool RegisterPlugin(PluginType * plugin, PluginListType & list) { list.Append(plugin); return true; } template<typename PluginType, typename PluginListType, typename PluginToDllDictionaryType, typename DeletePluginFunctionType> bool UnRegisterPlugin( PluginType * plugin, PluginListType & container, PluginToDllDictionaryType & pluginMap) { typename PluginListType::compatibility_iterator it = container.Find(plugin); if (it == NULL) return false; do { wxDynamicLibrary * dll = (wxDynamicLibrary *)pluginMap[plugin]; if (!dll) // Probably plugin was not loaded from dll break; wxDYNLIB_FUNCTION(DeletePluginFunctionType, DeletePlugin, *dll); if (pfnDeletePlugin) { pfnDeletePlugin(plugin); container.Erase(it); pluginMap.erase(plugin); return true; } } while (false); // If plugin is not loaded from DLL (eg embedded into executable) wxDELETE(plugin); container.Erase(it); return true; } template<typename PluginType, typename PluginListType, typename PluginToDllDictionaryType, typename DeletePluginFunctionType> bool UnloadPlugins(PluginListType & list, PluginToDllDictionaryType & pluginDictoonary) { bool result = true; PluginType * plugin = NULL; while (list.GetFirst() && (plugin = list.GetFirst()->GetData())) { result &= UnRegisterPlugin<PluginType, PluginListType, PluginToDllDictionaryType, DeletePluginFunctionType>(plugin, list, pluginDictoonary); } return result; } template <typename PluginType, typename PluginListType, typename PluginToDllDictionaryType, typename CreatePluginFunctionType> bool LoadPlugins(const wxString & pluginsDirectory, PluginListType & list, PluginToDllDictionaryType & pluginDictionary, const wxString & subFolder) { wxFileName fn; fn.AssignDir(pluginsDirectory); wxLogDebug(wxT("%s"), fn.GetFullPath().data()); fn.AppendDir(subFolder); wxLogDebug(wxT("%s"), fn.GetFullPath().data()); if (!fn.DirExists()) return false; if(!wxDirExists(fn.GetFullPath())) return false; wxString wildcard = wxString::Format(wxT("*.%s"), GetPluginExt().GetData()); wxArrayString pluginPaths; wxDir::GetAllFiles(fn.GetFullPath(), &pluginPaths, wildcard); for(size_t i = 0; i < pluginPaths.GetCount(); ++i) { wxString fileName = pluginPaths[i]; wxDynamicLibrary * dll = new wxDynamicLibrary(fileName); if (dll->IsLoaded()) { wxDYNLIB_FUNCTION(CreatePluginFunctionType, CreatePlugin, *dll); if (pfnCreatePlugin) { PluginType * plugin = pfnCreatePlugin(); RegisterPlugin(plugin, list); m_DllList.Append(dll); pluginDictionary[plugin] = dll; } else wxDELETE(dll); } } return true; } }; 


wxModularHost / SampleModularCore.h

 #pragma once #include <wxModularCore.h> #include <wxNonGuiPluginBase.h> #include <wxGuiPluginBase.h> // We need to know which DLL produced the specific plugin object. WX_DECLARE_HASH_MAP(wxNonGuiPluginBase*, wxDynamicLibrary*, wxPointerHash, wxPointerEqual, wxNonGuiPluginToDllDictionary); WX_DECLARE_HASH_MAP(wxGuiPluginBase*, wxDynamicLibrary*, wxPointerHash, wxPointerEqual, wxGuiPluginToDllDictionary); // And separate list of loaded plugins for faster access. WX_DECLARE_LIST(wxNonGuiPluginBase, wxNonGuiPluginBaseList); WX_DECLARE_LIST(wxGuiPluginBase, wxGuiPluginBaseList); class SampleModularCore : public wxModularCore { public: virtual ~SampleModularCore(); virtual bool LoadAllPlugins(bool forceProgramPath); virtual bool UnloadAllPlugins(); const wxNonGuiPluginBaseList & GetNonGuiPlugins() const; const wxGuiPluginBaseList & GetGuiPlugins() const; private: wxNonGuiPluginToDllDictionary m_MapNonGuiPluginsDll; wxNonGuiPluginBaseList m_NonGuiPlugins; wxGuiPluginToDllDictionary m_MapGuiPluginsDll; wxGuiPluginBaseList m_GuiPlugins; }; 


wxModularHost / SampleModularCore.cpp

 #include "stdwx.h" #include "SampleModularCore.h" #include <wx/listimpl.cpp> WX_DEFINE_LIST(wxNonGuiPluginBaseList); WX_DEFINE_LIST(wxGuiPluginBaseList); SampleModularCore::~SampleModularCore() { Clear(); } bool SampleModularCore::LoadAllPlugins(bool forceProgramPath) { wxString pluginsRootDir = GetPluginsPath(forceProgramPath); bool result = true; result &= LoadPlugins<wxNonGuiPluginBase, wxNonGuiPluginBaseList, wxNonGuiPluginToDllDictionary, CreatePlugin_function>(pluginsRootDir, m_NonGuiPlugins, m_MapNonGuiPluginsDll, wxT("nongui")); result &= LoadPlugins<wxGuiPluginBase, wxGuiPluginBaseList, wxGuiPluginToDllDictionary, CreateGuiPlugin_function>(pluginsRootDir, m_GuiPlugins, m_MapGuiPluginsDll, wxT("gui")); // You can implement other logic which takes in account // the result of LoadPlugins() calls return true; } bool SampleModularCore::UnloadAllPlugins() { return UnloadPlugins<wxNonGuiPluginBase, wxNonGuiPluginBaseList, wxNonGuiPluginToDllDictionary, DeletePlugin_function>(m_NonGuiPlugins, m_MapNonGuiPluginsDll) && UnloadPlugins<wxGuiPluginBase, wxGuiPluginBaseList, wxGuiPluginToDllDictionary, DeleteGuiPlugin_function>(m_GuiPlugins, m_MapGuiPluginsDll); } const wxNonGuiPluginBaseList & SampleModularCore::GetNonGuiPlugins() const { return m_NonGuiPlugins; } const wxGuiPluginBaseList & SampleModularCore::GetGuiPlugins() const { return m_GuiPlugins; } 


टेम्पलेट विधियों को लागू करने के बाद, जीयूआई प्लगइन्स के लिए समर्थन जोड़ने में काफी थोड़ा कोड लिया गया।

एक आवेदन में जीयूआई प्लगइन्स का उपयोग करना


आवेदन में, हमारे पास मुख्य पैनल के रूप में डॉकिंग विंडो मैनेजर और wxAuiNotebook है। विचार करें कि आप इस wxAuiNotebook पर प्लगइन्स से नियंत्रण कैसे जोड़ सकते हैं:
wxModularHost / MainFrame.cpp
 void MainFrame::AddPagesFromGuiPlugins() { SampleModularCore * pluginManager = wxGetApp().GetPluginManager(); for(wxGuiPluginBaseList::Node * node = pluginManager->GetGuiPlugins().GetFirst(); node; node = node->GetNext()) { wxGuiPluginBase * plugin = node->GetData(); if(plugin) { wxWindow * page = plugin->CreatePanel(m_Notebook); if(page) { m_Notebook->AddPage(page, plugin->GetName()); } } } } 

नतीजतन, हमें ऐसी टैब विंडो मिलती है:

छवि

टैब हेडर GetName()प्रत्येक प्लग-इन की विधि से लिया जाता है, टैब स्वयं CreatePanel()प्लग-इन विधि द्वारा बनाए जाते हैं

लिनक्स के लिए सुधार सीएमके-स्क्रिप्ट


विंडोज में, हमने फ़ोल्डर को निर्दिष्ट करने के लिए सेटिंग्स का उपयोग करके निर्दिष्ट किया है जिसमें डायनामिक लाइब्रेरी एकत्र की जाएंगी RUNTIME_OUTPUT_DIRECTORYलिनक्स में, के रूप में प्लगइन एक गतिशील पुस्तकालय (अर्थात् एक पुस्तकालय) है, सेटिंग का उपयोग किया जाता है LIBRARY_OUTPUT_DIRECTORYलेकिन यहां हमें एक समस्या का सामना करना पड़ रहा है: यदि आप बिन फ़ोल्डर के अंदर सीधे पुस्तकालयों का निर्माण करते हैं, तो आश्रित परियोजनाओं के निर्माण के दौरान लिंकर को यह पुस्तकालय नहीं मिलेगा। इन उद्देश्यों के लिए, आपको एक स्क्रिप्ट जोड़ने की आवश्यकता है जो लाइब्रेरी बनाने के बाद बाहर काम करेगी और इसे बिन फ़ोल्डर के अंदर सही जगह पर कॉपी कर सकती है। यह सभी गतिशील पुस्तकालयों (मूल और प्लग-इन दोनों के लिए) के लिए किए जाने की आवश्यकता होगी:

SampleGuiPlugin2 / CMakeLists.txt
 ... set(DLL_DIR bin) if(LINUX) set(TARGET_LOCATION ${PROJECT_SOURCE_DIR}/${DLL_DIR}${LIB_SUFFIX}/plugins/nongui) else(LINUX) set(TARGET_LOCATION ${PROJECT_SOURCE_DIR}/${DLL_DIR}/${CMAKE_CFG_INTDIR}/plugins/nongui) get_target_property(RESULT_FULL_PATH ${LIBRARY_NAME} LOCATION) get_filename_component(RESULT_FILE_NAME ${RESULT_FULL_PATH} NAME) endif(LINUX) set_target_properties(${LIBRARY_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TARGET_LOCATION}) ... if(LINUX) add_custom_command(TARGET ${LIBRARY_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${TARGET_LOCATION} COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${LIBRARY_NAME}> ${TARGET_LOCATION}/${RESULT_FILE_NAME} ) endif(LINUX) 


लिनक्स पर सभी प्लगइन्स के लिए, हमें निर्भरता की एक सूची भी निर्दिष्ट करनी होगी:

SampleGuiPlugin2 / CMakeLysts.txt
 ... if(WIN32) set(SRCS ${SRCS} ${LIBRARY_NAME}.def) set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/D_USRDLL;/D__STDC_CONSTANT_MACROS) set(LINK_DIRECTORIES ${PROJECT_ROOT_DIR}/wxNonGuiPluginBase/${OS_BASE_NAME}${LIB_SUFFIX}/$(ConfigurationName)) set(DEMO_LIBS wxNonGuiPluginBase.lib) endif(WIN32) if(LINUX) set(DEMO_LIBS wxNonGuiPluginBase) endif(LINUX) ... 


सब कुछ ठीक लग रहा है, प्रोजेक्ट चल रहा है। लेकिन जब हम एप्लिकेशन को शुरू करने का प्रयास करते हैं, तो हम पाते हैं कि डायनेमिक लाइब्रेरी लोड नहीं होती हैं (यदि आप पहले से निर्मित एप्लिकेशन को किसी अन्य मशीन में ट्रांसफर करते हैं तो यह एक अप्रिय आश्चर्य हो सकता है)।
और सभी क्योंकि पुस्तकालय के अंदर निर्माण करते समय, पूर्ण पथ वाले निर्भरता के लिंक पंजीकृत होते हैं। इसलिए, उदाहरण के लिए, प्रत्येक प्लगइन के लिए बेस कक्षाओं के साथ पुस्तकालय का पूरा रास्ता इंगित किया जाएगा, जो अन्य कामकाजी मशीन पर नहीं होगा। आप ldd उपयोगिता का उपयोग करके इसे सत्यापित कर सकते हैं:

 ldd libSampleGuiPlugin2.so | grep wxSampleGuiPluginBase 


पूर्ण पथ से छुटकारा पाने के लिए, CMake में आपको RPATHपुस्तकालयों के लिए एक विकल्प निर्दिष्ट करने की आवश्यकता है जो हमारे समाधान से अन्य गतिशील पुस्तकालयों का संदर्भ देते हैं:

SampleGuiPlugin2 / CMakeLists.txt
 if(WIN32) set(SRCS ${SRCS} ${LIBRARY_NAME}.def) set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/D_USRDLL;/D__STDC_CONSTANT_MACROS) set(LINK_DIRECTORIES ${PROJECT_ROOT_DIR}/wxNonGuiPluginBase/${OS_BASE_NAME}${LIB_SUFFIX}/$(ConfigurationName)) set(DEMO_LIBS wxNonGuiPluginBase.lib) endif(WIN32) if(LINUX) set(DEMO_LIBS wxNonGuiPluginBase) SET(CMAKE_SKIP_BUILD_RPATH FALSE) SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) SET(CMAKE_INSTALL_RPATH ".:./../../") endif(LINUX) 


क्योंकि प्लगइन उपनिर्देशिका प्लगइन्स / गुई में स्थित है, तो wxGuiPluginBase लाइब्रेरी को दो स्तरों पर खोजा जाना चाहिए, जैसा कि CMakeLists.txt में दर्शाया गया है

OS X के लिए CMake स्क्रिप्ट में सुधार करना


लिनक्स की तरह, ओएस एक्स में हमें प्लगइन्स के लिए निर्भरता लोड करने की समस्या है। OS X में, डायनेमिक लाइब्रेरी के पथ को ठीक करने के लिए, आप install_name_tool उपयोगिता का उपयोग कर सकते हैं।
चलो CMakeLists.txt में कोड जोड़ते हैं, जो पुस्तकालय पथ को रिश्तेदार के साथ बदल देता है:

SampleGuiPlugin2 / CMakeLists.txt
 if(APPLE) FOREACH(DEP_LIB ${DEMO_LIBS}) get_filename_component(ABS_ROOT_DIR ${PROJECT_ROOT_DIR} ABSOLUTE) set(LIBNAME_FULL "${ABS_ROOT_DIR}/${DEP_LIB}/${OS_BASE_NAME}${LIB_SUFFIX}/$(CONFIGURATION)/lib${DEP_LIB}.dylib") add_custom_command(TARGET ${LIBRARY_NAME} POST_BUILD COMMAND install_name_tool -change "${LIBNAME_FULL}" "@executable_path/../Frameworks/lib${DEP_LIB}.dylib" $<TARGET_FILE:${LIBRARY_NAME}>) ENDFOREACH(DEP_LIB) endif(APPLE) 


एप्लिकेशन की CMake स्क्रिप्ट में, आपको wxModularHost / CMakeLists.txt पर समान परिवर्तन करने की

आवश्यकता है
 if(APPLE) FOREACH(DEP_LIB ${DEMO_LIBS_SHARED}) get_filename_component(ABS_ROOT_DIR ${PROJECT_ROOT_DIR} ABSOLUTE) set(LIBNAME_FULL "${ABS_ROOT_DIR}/${DEP_LIB}/${OS_BASE_NAME}${LIB_SUFFIX}/$(CONFIGURATION)/lib${DEP_LIB}.dylib") add_custom_command(TARGET ${EXECUTABLE_NAME} POST_BUILD COMMAND install_name_tool -change "${LIBNAME_FULL}" "@executable_path/../Frameworks/lib${DEP_LIB}.dylib" $<TARGET_FILE:${EXECUTABLE_NAME}>) ENDFOREACH(DEP_LIB) endif(APPLE) 


निष्कर्ष में


हमने क्रॉस-प्लेटफ़ॉर्म मॉड्यूलर अनुप्रयोगों को कैसे बनाया जाए, साथ ही सीएमके का उपयोग करके विंडोज और ओएस एक्स मेकफाइल के लिए प्रोजेक्ट बनाने की प्रक्रिया को देखा। मुझे आशा है कि किसी को यह सामग्री उपयोगी लगेगी।
लेख में चर्चा की गई परियोजना का पूर्ण स्रोत कोड GitHub: https://github.com/T-Rex/wxModularApp

PS पर पाया जा सकता है: लेख के लेखन के दौरान, wxWidgets (3.0) का एक नया संस्करण जारी किया गया है, जिसके साथ CMake अभी भी काम नहीं कर सकता है (द्वारा) 2.9.x के लिए काम करने वाली कम से कम स्क्रिप्ट 3.0 से काम नहीं करती है। परीक्षण के लिए, शाखा 2.9 से कोड का उपयोग करना बेहतर होता है: svn.wxwidgets.org/svn/wx/wxWidgets/tags/WX5_9_5

Source: https://habr.com/ru/post/In203322/


All Articles