Search code examples
c++visual-c++user-interfacedllwxwidgets

wxWidgets - Add items to sizer via DLL


I have a GUI set up with wxWidgets (C++, MSVC) and part of the functionality is to add elements to the GUI via DLL's. Essentially, I would be passing in a sizer to the DLL which will in turn add the elements based on what is in the DLL.

Could someone just point me in the right direction on how to get the DLL set up? I have looked, but don't see anything that's similar to what I'm looking for. The programming of the elements is fine with me, I just need to understand how to structure the DLL programming.

Help appreciated.


UPDATE

I've done something similar to http://wiki.wxwidgets.org/Programs_That_Support_Plugins.

Essentially, I get Access violation reading location errors whenever I try using wxWidgets components from within the DLL.

In the samplePlugin.cpp, If I use

void samplePlugin::PerformTasks()
{
   wxMessageBox(_("I would if I could..."));
}

I get the error. If, however, I use

void samplePlugin::PerformTasks()
{
   MessageBox(0,"Testing this thing","Test",0);
}

which is not wxWidgets, but rather native to the OS, the message box works fine.

I've tried adding WXUSINGDLL;WXMAKINGDLL; in the Preprocessor Definitions in MSVC in both the DLL and the App projects. The DLL compiles fine, but I can't even compile the App in that case and get exceptions.

Does anyone know how to fix this issue?


Solution

  • Create a DLL project and specify the preprocessor directive e.g. /DIFLOOR_EXPORTS_COMMONPLUGINBASE (this preprocessor variable only in DLL project)

    Then create a header which specifies whether your classes are imported or exported:

    CommonPluing.h

    #ifndef _COMMONPLUGIN_H
    #define _COMMONPLUGIN_H
    
    #if defined(__WXMSW__)
    #ifdef IFLOOR_EXPORTS_COMMONPLUGINBASE
    #define IFLOOR_API_COMMONPLUGINBASE __declspec(dllexport)
    #else
    #define IFLOOR_API_COMMONPLUGINBASE __declspec(dllimport)
    #endif
    #else
    #define IFLOOR_API_COMMONPLUGINBASE
    #endif
    
    #endif // _COMMONPLUGIN_H
    

    Then create your exported class and add the specifier from first header:

    CommonConfigWindowBase.h

    class IFLOOR_API_COMMONPLUGINBASE CommonConfigWindowBase : public wxPanel 
    {
        DECLARE_DYNAMIC_CLASS(CommonConfigWindowBase)
    public:
        /// Constructors
        CommonConfigWindowBase();
        CommonConfigWindowBase(wxWindow *parent,
            wxWindowID winid = wxID_ANY,
            const wxPoint& pos = wxDefaultPosition,
            const wxSize& size = wxDefaultSize,
            long style = wxTAB_TRAVERSAL | wxNO_BORDER,
            const wxString& name = wxPanelNameStr);
    
        /// Pseudo ctor
        bool Create(wxWindow *parent,
            wxWindowID winid = wxID_ANY,
            const wxPoint& pos = wxDefaultPosition,
            const wxSize& size = wxDefaultSize,
            long style = wxTAB_TRAVERSAL | wxNO_BORDER,
            const wxString& name = wxPanelNameStr);
    
        virtual ~CommonConfigWindowBase();
    
        /// Reads config from the effect
        virtual bool ReadConfig(){return true;}
    
        /// Saves config to the effect
        virtual bool SaveConfig(){return true;}
    };
    

    Create the exported function callable from main executable (you may want to create a wrapper class and call methods which return wxWindow *). You need an exported method for creating the plugin object and for deleting it. Also you need a !!! virtual destructors !!! for exported object and for your windows. so assuming that SportEffectPlugin contains a wxWindow * CreateConfigWindow(wxWindow * parent) method:

    Exports.cpp

    #include "stdwx.h"
    #include "CommonConfigWindowBase.h"
    
    IFLOOR_API_COMMONPLUGINBASE IFloorEffectPluginBase * CreatePlugin(const wxString& sBasePath, iFloorBlobVector * blobs)
    {
        return new SportEffectPlugin(sBasePath, blobs);
    }
    
    IFLOOR_API_COMMONPLUGINBASE void DeletePlugin(IFloorEffectPluginBase * plugin)
    {
        wxDELETE(plugin);
    }
    

    Then in main app load the DLL (you will need to adopt the following code for your needs):

    Loader.cpp

    bool IFloorSystem::LoadPlugins(bool forceProgramPath)
    {
        if (!m_DefaultPlugin)
        {
            m_DefaultPlugin = new DefaultEffectPlugin(GetDefaultGraphicsPath());
            RegisterEffectPlugin(m_DefaultPlugin);
        }
    
        wxFileName fn;
        fn.AssignDir(GetPluginsPath(forceProgramPath));
        wxLogDebug(wxT("%s"), fn.GetFullPath().data());
        fn.AppendDir(wxT("effects"));
        wxLogDebug(wxT("%s"), fn.GetFullPath().data());
        if (!fn.DirExists())
            return false;
        wxDir dir(fn.GetFullPath());
        if (!dir.IsOpened())
            return false;
    
        // scan for plugins
        wxString filename;
        wxString ext = wxT("*.dll"); // TODO: change ext for different platforms
        bool bFound = dir.GetFirst(&filename, ext, wxDIR_FILES);
        while (bFound)
        {
            fn.SetFullName(filename);
            wxDynamicLibrary * dll = new wxDynamicLibrary(fn.GetFullPath());
            if (dll->IsLoaded())
            {
                wxDYNLIB_FUNCTION(CreatePlugin_function, CreatePlugin, *dll);
                if (pfnCreatePlugin)
                {
                    IFloorEffectPluginBase* plugin = pfnCreatePlugin(GetDefaultGraphicsPath(), &IFloorStorage::Instance().GetBlobs());
                    RegisterEffectPlugin(plugin);
                    m_DllList.Append(dll);
                    m_MapPluginsDll[plugin] = dll;
                }
                else
                    wxDELETE(dll);
            }
            bFound = dir.GetNext(&filename);
        }
        return true;
    }
    

    Then in the end you will need to unload plugins and delete all loaded objects by calling the function from DLL:

    bool IFloorSystem::UnRegisterEffectPlugin(IFloorEffectPluginBase * plugin)
    {
        IFloorEffectPluginBaseList::compatibility_iterator it = m_Plugins.Find(plugin);
        if (it == NULL)
            return false;
    
        do 
        {
            wxDynamicLibrary * dll = m_MapPluginsDll[plugin];
            if (!dll) // Probably plugin was not loaded from dll
                break;
    
            wxDYNLIB_FUNCTION(DeletePlugin_function, DeletePlugin, *dll);
            if (pfnDeletePlugin)
            {
                pfnDeletePlugin(plugin);
                m_Plugins.Erase(it);
                m_MapPluginsDll.erase(plugin);
                return true;
            }
        } while (false);
    
        // If plugin is not loaded from DLL
        wxDELETE(plugin);
        m_Plugins.Erase(it);
    
        return true;
    }