Problem adding items to wxMenuBar Topic is solved

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
Dark_Phoenix
Knows some wx things
Knows some wx things
Posts: 48
Joined: Sat Jul 04, 2009 2:27 pm
Location: Houston, TX

Problem adding items to wxMenuBar

Post by Dark_Phoenix » Tue Dec 29, 2009 11:09 pm

Well, I have finally figured out some answers to my plugin problems. I have created a simple app that creates a blank window with a menu bar. The app then loads a plugin (DLL) which adds a new menu item to the app's menu bar, and responds to the menu event for that addition. Now I am getting an access violation when I try to exit the app. First, let me show you some of the relevent code.

here are my headers

Code: Select all

/******************
 * file : IPlugin.h
 ******************/
#ifndef IPLUGIN_H_INCLUDED
#define IPLUGIN_H_INCLUDED

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

class IPluginHost
{
    public:
    virtual wxEvtHandler* GetEvtHandler() = 0;
    virtual wxMenuBar* GetMainMenuBar()   = 0;
    virtual wxToolBar* GetMainToolBar()   = 0;
};

class IPlugin : public wxApp
{
    public:
    virtual void InitPlugin(IPluginHost *pHost)  = 0;
    virtual void ShutdownPlugin()                = 0;
};

typedef void (*DLLFunctionPtr)(IPluginHost*, IPlugin**);

#ifdef __cplusplus
}
#endif // __cplusplus

#endif // IPLUGIN_H_INCLUDED

/********************
 * file : PluginDll.h
 ********************/
#ifndef PLUGINDLL_H_INCLUDED
#define PLUGINDLL_H_INCLUDED

#ifdef MYDLL_EXPORTS
#define DLL_IMPORT_EXPORT __declspec(dllexport)
#else
#define DLL_IMPORT_EXPORT __declspec(dllimport)
#endif

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

void DLL_IMPORT_EXPORT CreatePlugin(IPluginHost *pHost, IPlugin **ppInterface);
void DLL_IMPORT_EXPORT DestroyPlugin(IPluginHost *pHost, IPlugin **ppInterface = NULL);

class wxPlugin : public IPlugin
{
    DECLARE_DYNAMIC_CLASS(wxPlugin)
    public:
    wxPlugin();
    virtual ~wxPlugin();
    virtual void InitPlugin(IPluginHost *pFrame);
    virtual void ShutdownPlugin();

    private:
    enum
    {
        IDM_ABOUT = 100
    };
    void OnAbout(wxCommandEvent &event);
    IPluginHost *m_pHost;
};

DECLARE_APP(wxPlugin);

#ifdef __cplusplus
}
#endif // __cplusplus

#endif // PLUGINDLL_H_INCLUDED

/********************
 * file : PluginApp.h
 ********************/
#ifndef PLUGINAPP_H_INCLUDED
#define PLUGINAPP_H_INCLUDED

typedef std::map<wxDllType, IPlugin*>   PLUGIN_MAP;
typedef PLUGIN_MAP::iterator            PLUGIN_MAP_ITER;

class CMainFrame : public wxFrame, IPluginHost
{
    public:
    CMainFrame() { }
    CMainFrame(const wxString& title);
    ~CMainFrame();
    wxEvtHandler* GetEvtHandler() { return GetEventHandler(); }
    wxMenuBar* GetMainMenuBar()   { return GetMenuBar(); }
    wxToolBar* GetMainToolBar()   { return GetToolBar(); }

    private:
    enum
    {
        ID_MAIN = 10,
        IDM_FILE_QUIT,
        IDP_PANEL
    };
    void EnumPlugIns();
    void LoadPlugin(const wxString &Filename);
    void UnloadAllPlugins();
    void OnQuit(wxCommandEvent &event);
    PLUGIN_MAP   m_PluginList;
};

class CPluginApp : public wxApp
{
    public:
    virtual bool OnInit();
    virtual int OnExit();

    private:
    CMainFrame *m_pFrame;
};

#endif // PLUGINAPP_H_INCLUDED
The app calls CMainFrame::EnumPlugins from the ctor to load the plugins

Code: Select all

void CMainFrame::EnumPlugIns()
{
    wxDir dir;
    wxArrayString Files;
    size_t num = dir.GetAllFiles(wxGetCwd() + wxT("\\DLL"), &Files, wxT("*.dll"), wxDIR_FILES);
	for(size_t i = 0; i < num; i++)
	{
        wxString Filename = Files[i];
	    LoadPlugin(Filename);
	}
}

void CMainFrame::LoadPlugin(const wxString &Filename)
{
    wxString PluginName = wxT("CreatePlugin");
    wxDynamicLibrary Loader;
    if (!Loader.Load(Filename, wxDL_NOW) )
    {
        wxMessageBox(wxString::Format(wxT("Could not load Plugin\n'%s'"), Filename), wxT("Error"), wxOK, this);
        return;
    }
    if(Loader.IsLoaded())
    {
        if(Loader.HasSymbol(PluginName) )
        {
            DLLFunctionPtr pFuncCreatePlugin = (DLLFunctionPtr)Loader.GetSymbol(PluginName);
            if(pFuncCreatePlugin)
            {
                IPlugin *pPlugin = NULL;
                pFuncCreatePlugin(this, &pPlugin);
                wxDllType Handle = Loader.Detach();
                m_PluginList.insert(std::make_pair(Handle, pPlugin) );
            }
            else
            {
                wxMessageBox(wxT("GetSymbol returned NULL"), wxT("Error"), wxOK, this);
            }
        }
        else
        {
            wxMessageBox(wxString::Format(wxT("Symbol '%s' not found!"), PluginName), wxT("Error"), wxOK, this);
        }
    }
}
when the app is closed CMainFrame::OnQuit is called

Code: Select all

void CMainFrame::OnQuit(wxCommandEvent& event)
{
    UnloadAllPlugins();
    Destroy();
}

void CMainFrame::UnloadAllPlugins()
{
    PLUGIN_MAP_ITER iter;
    for (iter = m_PluginList.begin(); iter != m_PluginList.end(); ++iter)
    {
        wxDllType t = iter->first;
        IPlugin *pPlugin = iter->second;
        if (pPlugin)
        {
            pPlugin->ShutdownPlugin();
        }
        wxDynamicLibrary::Unload(iter->first);
    }
}
the access violation occurs after all the above code has finished, and I have traced it down to the destructor for wxMenuBarBase on this line

Code: Select all

wxMenuBarBase::~wxMenuBarBase()
{
    WX_CLEAR_LIST(wxMenuList, m_menus);
}
I am not sure what WX_CLEAR_LIST does but I have a pretty good idea, I just can't figure out why it is giving me the access violation.

Can anyone shed some light on this for me?

Dark_Phoenix
Knows some wx things
Knows some wx things
Posts: 48
Joined: Sat Jul 04, 2009 2:27 pm
Location: Houston, TX

Post by Dark_Phoenix » Wed Dec 30, 2009 10:08 pm

Well, I have figured out that the problem has something to do with either how I am adding items to the menubar and/or how I am loading the DLL. If I take the routine that adds the item to the menubar out of the DLL and put it in the app then everything runs fine.

I can't figure this out for the life of me... Anyone have any ideas?

JimFairway
wxWorld Domination!
wxWorld Domination!
Posts: 1059
Joined: Sun Dec 30, 2007 6:40 pm
Location: Canada

Post by JimFairway » Thu Dec 31, 2009 11:57 am

Hi,

What does the code that adds menus to the menubar look like?
The DLL isn't running in a separate thread is it?


Jim
OS: Vista SP1, wxWidgets 2.8.7.

Dark_Phoenix
Knows some wx things
Knows some wx things
Posts: 48
Joined: Sat Jul 04, 2009 2:27 pm
Location: Houston, TX

Post by Dark_Phoenix » Thu Dec 31, 2009 12:09 pm

What does the code that adds menus to the menubar look like?

Code: Select all

void wxPlugin::InitPlugin(IPluginHost *pHost)
{
    // interface to the host
    m_pHost = pHost;

    // get the menubar from the host
    wxMenuBar *pHostMenuBar = m_pHost->GetMainMenuBar();

    // set up a new menu option
    wxMenu *pMenu = new wxMenu;
    pMenu->Append(IDM_ABOUT, wxT("&About"), wxT("About this program") );

    // add the new menu option to the host's menu bar
    pHostMenuBar->Append(pMenu, wxT("&Dll Menu Items") );

    // connect a handler to the option
    wxEvtHandler *pHostEvtHandler = pHost->GetEvtHandler();
    pHostEvtHandler->Connect(IDM_ABOUT, wxEVT_COMMAND_MENU_SELECTED,
            wxCommandEventHandler(wxPlugin::OnAbout) );
}
The DLL isn't running in a separate thread is it?
I don't think so... At least I don't explicitly set it up that way... see OP for routine that loads the DLL[/quote]

JimFairway
wxWorld Domination!
wxWorld Domination!
Posts: 1059
Joined: Sun Dec 30, 2007 6:40 pm
Location: Canada

Post by JimFairway » Thu Dec 31, 2009 12:33 pm

Hi,

A couple of things,but I'm not sure if they're the cause of your issue.

#1: I assume you have more than 1 plug in, if so, I've never tried using the same ID for more than 1 menu item, it looks like every plug in will use IDM_ABOUT for the menu ID.

#2: I believe:

Code: Select all

    pHostEvtHandler->Connect(IDM_ABOUT, wxEVT_COMMAND_MENU_SELECTED,
            wxCommandEventHandler(wxPlugin::OnAbout) );
should be:

Code: Select all

    pHostEvtHandler->Connect(IDM_ABOUT, wxEVT_COMMAND_MENU_SELECTED,
            wxCommandEventHandler(wxPlugin::OnAbout), 0, this);
That's because your pHost object event is being handled by a wxPlugin object.

Jim
OS: Vista SP1, wxWidgets 2.8.7.

Dark_Phoenix
Knows some wx things
Knows some wx things
Posts: 48
Joined: Sat Jul 04, 2009 2:27 pm
Location: Houston, TX

Post by Dark_Phoenix » Thu Dec 31, 2009 12:59 pm

Right now I only have one plugin... not sure if the ID will cause any problems for other plugins or not... I'll test that when I get the current problem resolved.
I suppose I could add a member function to the IPluginHost interface that would return wxNewId() so that I could be sure to have a unique ID for each menu item.. maybe that would help

I did forget to put the Event Sink parameter in, thanks for pointing that out

I have some stuff I have to do now... I will tyry these modifcations when I get back and see what happens.

Thanks

Dark_Phoenix
Knows some wx things
Knows some wx things
Posts: 48
Joined: Sat Jul 04, 2009 2:27 pm
Location: Houston, TX

Post by Dark_Phoenix » Fri Jan 01, 2010 5:30 pm

I think I may have figured out what my major problem is... I just need a little verification.

I am using VCToolkit 2008 as my compiler.

I have installed wxWidgets using the current version of wxPack. I was looking at the installer again and I see that the option for wxWidgets compiled by Visual C++ for DLL's has another check for Visual C++ 8.0 compiled.

I think that VC8.0 is the VCToolkit 2003
I also think that VC9.0 is the VCToolkit 2008

If I am correct here then any app I write must be compiled with the same version of compiler that wxWidgets is compiled with. If these assumptions are true then do I need to recompile the wxWidgets library using 2008?

Or does this even make any difference?

Dark_Phoenix
Knows some wx things
Knows some wx things
Posts: 48
Joined: Sat Jul 04, 2009 2:27 pm
Location: Houston, TX

Post by Dark_Phoenix » Sat Jan 02, 2010 6:16 pm

OK, I found out that I was at least partially wrong about some of my assumptions
I think that VC8.0 is the VCToolkit 2003
I also think that VC9.0 is the VCToolkit 2008
2003 is VC71, 2005 is VC80 and 2008 is VC90

Anyway, I rebuilt wxWidgets from scratch with VC90... I build two versions, debug DLL and release DLL. I rebuilt my project with VC90 to use wxWidgets as DLL's, again a debug and release version.

Still have the same problem

I am getting a headache from banging my head on the desk tring to figure it out.... I know it is something simple I am overlooking I just can't see it.

Ideas? Anyone??

mikejonesey
Knows some wx things
Knows some wx things
Posts: 25
Joined: Thu Nov 26, 2009 12:32 am
Contact:

Post by mikejonesey » Tue Jan 12, 2010 12:49 am

do you have a Disconnect for your button in the decounstructor, as it has a Connect?

idk if wx_clear_list is sufficient?
Signature:
This is a block of text that can be added to posts you make.

Dark_Phoenix
Knows some wx things
Knows some wx things
Posts: 48
Joined: Sat Jul 04, 2009 2:27 pm
Location: Houston, TX

Post by Dark_Phoenix » Sat Feb 13, 2010 1:46 pm

do you have a Disconnect for your button in the decounstructor, as it has a Connect?
This was almost right. It was not only that I needed to disconnect it but I needed to remove the menu item from the menu bar from inside the plugin's destructor as well.

Post Reply