Issues with initializing a DLL from an EXE 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
Jaganlal
Earned a small fee
Earned a small fee
Posts: 11
Joined: Fri Dec 18, 2009 11:01 pm

Issues with initializing a DLL from an EXE

Post by Jaganlal »

I'am initializing a DLL (calling it as Login.dll) from an EXE (calling it as MainApp) using wxDynamicLibrary::Load method, to my surprise bool MainApp::OnInit() gets called twice.


COMPILING wxWidgets-2.8.10 AS DLL
Platform: WinXP, SP3
IDE: Visual Studio 2005
ALL THESE THINGS WERE WORKING VERY WELL, WHEN I COMPILED wxWidgets-2.8.10 AS STATIC LIB AND LINKED WITH BOTH MAINAPP.EXE AND LOGIN.DLL


Let me elaborate my application - MainApp.exe is the main app with controls program logic (like communicating with different hardwares) and Login.dll is used to show only UI (basically i'm creating wxDialog with few buttons and edit ctrls). So what happens is when i load Login.dll from MainApp.exe its DllMain main gets called within which i'm calling wxTheApp->CallOnInit(), which further calls MainApp::OnInit method.. Can some one help me plzzzzzzzzzzzzzz i'm struggling for this since 3 days.... no solution.... any help/clue will be greatly appreciated. Along with this i'm sending few code snippets also....

Code snippet of Login.dll

Code: Select all

IMPLEMENT_APP_NO_MAIN(wxLoginApp) 

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved 
                                         ) 
{ 
  switch (ul_reason_for_call) 
  { 
    case DLL_PROCESS_ATTACH: 
    {       //use wxInitialize() if you don't want GUI instead of the following 12 lines 
      wxSetInstance((HINSTANCE)hModule); 
      int argc = 0; 
      char **argv = NULL; 
      wxEntryStart(argc, argv); 
      if ( !wxTheApp || !wxTheApp->CallOnInit() ) 
        return FALSE;
    } 
    break; 

    case DLL_THREAD_ATTACH: 
    break; 

    case DLL_THREAD_DETACH: 
    {
      wxEntryCleanup();
    }
    break; 

    case DLL_PROCESS_DETACH: 
    wxEntryCleanup(); //use wxUninitialize() if you don't want GUI 
    break; 
  } 
  return TRUE; 
} 

Code snippet of MainApp.exe

Code: Select all

IMPLEMENT_APP(MainApp)

bool MainApp::OnInit() 
{
  m_pParentWnd = new ParentDlg(NULL);
  if (NULL == m_pParentWnd)
  {
    wxASSERT(0);  
    throw -1;
  }
  
  SetTopWindow(m_pParentWnd);
  m_pParentWnd->Show();

  wxDynamicLibrary *pUILoader = new wxDynamicLibrary;
  [b]pUILoader->Load(szDLLPath);[/b]
}
EVERY THING WORKED WELL, WHEN I COMPILED wxWidgets-2.8.10 AS STATIC LIB AND LINKED WITH BOTH MAINAPP.EXE AND LOGIN.DLL. DO I HAVE TO DO SOMETHING SPECIAL WHEN I LINK wxWidgets AS SHARED LIBRARY????

Thanks in Advance[/i]
Last edited by Jaganlal on Tue Dec 22, 2009 12:54 pm, edited 1 time in total.
Frank
Filthy Rich wx Solver
Filthy Rich wx Solver
Posts: 211
Joined: Sat Jan 01, 2005 6:19 pm

Post by Frank »

Without looking at your code: Never call code from DLLMain, when you're not sure what it does. There are things that are dangerous in DLLMain (like starting threads, using mutexes).

If you have a choice, and as I understand you have, don't use DLLMain for Initialising stuff.
Jaganlal
Earned a small fee
Earned a small fee
Posts: 11
Joined: Fri Dec 18, 2009 11:01 pm

Post by Jaganlal »

Frank:
I removed BOOL APIENTRY DllMain, this time MainApp::OnInit was called only once. But when i tried calling ShowUI (extern "C" function exported from Login.dll) from MainApp.exe i got Debug Assertion, when i looked into the code it says only main thread may call wxMutexGuiLeaveOrEnter(). Here is the call stack

Code: Select all

[color=red]wxbase28d_vc_custom.dll!wxOnAssert(const char * szFile=0x00582b0c, int nLine=1354, const char * szFunc=0x00582af4, const char * szCond=0x00582ae0, const char * szMsg=0x00582aac)  Line 713[/color]
wxbase28d_vc_custom.dll!wxMutexGuiLeaveOrEnter()  Line 1354 + 0x2c bytes
wxmsw28d_core_vc_custom.dll!wxEventLoop::OnNextIteration()  Line 203
wxmsw28d_core_vc_custom.dll!wxEventLoopManual::Run()  Line 99
wxmsw28d_core_vc_custom.dll!wxDialogModalData::RunLoop()  Line 125
wxmsw28d_core_vc_custom.dll!wxDialog::ShowModal()  Line 341
[b]Login.dll!ShowUI(unsigned long handle=197968, AbstractUIHandler * pUIEventReceiver=0x00d5c7e8)  Line 75[/b] 
[/b]
Jaganlal
Earned a small fee
Earned a small fee
Posts: 11
Joined: Fri Dec 18, 2009 11:01 pm

Post by Jaganlal »

As frank said, DLLMain function is not required. Which resolves the problem of getting MainApp::OnInit being called twice. Now i ended up with the following problem.
From my MainApp i'm creating separate thread (UILoader) to load Login.dll and call Show method (extern "C" function exported from Login.dll). But when i call Show method i get debug assertion saying
only main thread may call wxMutexGuiLeaveOrEnter()
.

UILoader code snippet

Code: Select all

typedef void (*SHOW_UI)();

class UILoader : public wxThreadHelper
{
wxDynamicLibrary* m_pLoader;
SHOW_UI Show;
public:
  UILoader()
  {
    m_pLoader->Load(szComponentPath);
    Show = (SHOW_UI)m_pLoader->GetSymbol( wxT("ShowUI") );
  }

  wxThread::ExitCode Entry()
  {
    if (Show)
      Show(); // wait until ShowModal returns
  }
}
Login.dll

Code: Select all

extern "C" LOGIN_API void ShowUI()
{
  Login *pLogin = new Login(0);
  pLogin->ShowModal();
  
  delete pLogin;
  pLogin = NULL;
}
call stack for the assertion

wxbase28d_vc_custom.dll!wxOnAssert(const char * szFile=0x00582b0c, int nLine=1354, const char * szFunc=0x00582af4, const char * szCond=0x00582ae0, const char * szMsg=0x00582aac) Line 713
wxbase28d_vc_custom.dll!wxMutexGuiLeaveOrEnter() Line 1354 + 0x2c bytes
wxmsw28d_core_vc_custom.dll!wxEventLoop::OnNextIteration() Line 203
wxmsw28d_core_vc_custom.dll!wxEventLoopManual::Run() Line 99
wxmsw28d_core_vc_custom.dll!wxDialogModalData::RunLoop() Line 125
wxmsw28d_core_vc_custom.dll!wxDialog::ShowModal() Line 341
Login.dll!ShowUI(unsigned long handle=197968, AbstractUIHandler * pUIEventReceiver=0x00d5c7e8) Line 75
Jaganlal
Earned a small fee
Earned a small fee
Posts: 11
Joined: Fri Dec 18, 2009 11:01 pm

Post by Jaganlal »

To my surprise, everything works fine if Show() is called within MainApp without creating separate thread.

Something like this

Code: Select all

typedef void (*SHOW_UI)();

bool MainApp::OnInit() 
{
  wxDynamicLibrary lib;
  lib.Load("Login.dll");
  
  SHOW_UI Show = (SHOW_UI)lib.GetSymbol( wxT("ShowUI") );
  Show();
}
No changes in Login.dll

Code: Select all

extern "C" LOGIN_API void ShowUI()
{
  LoginDlg *pLoginDlg = new LoginDlg(0);
  pLoginDlg->ShowModal();
  
  delete pLoginDlg;
  pLoginDlg= NULL;
}
is it because of the fact that
1) wxWidgets is used as shared library by both MainApp.exe and Login.dll
2) since wxWidgets is shared, only one APP can be present (which in my case is MainApp) and only that APP can INVOKE FUNCTIONS EXPORTED BY LOGIN.DLL?

EXPECTING SOME EXPERTS SUGGESTION ON THIS

If the above statement is true, then my MainApp::OnInit will be blocked until LoginDlg::ShowModal() returns which again needs to be resolved HUH!!!!! :x
Frank
Filthy Rich wx Solver
Filthy Rich wx Solver
Posts: 211
Joined: Sat Jan 01, 2005 6:19 pm

Post by Frank »

Ever wondered why the Mainthread is called GUI-Thread ;)

Because it's the thread in wich the GUI happens.
Jaganlal
Earned a small fee
Earned a small fee
Posts: 11
Joined: Fri Dec 18, 2009 11:01 pm

Post by Jaganlal »

Frank wrote:Ever wondered why the Mainthread is called GUI-Thread ;)

Because it's the thread in wich the GUI happens.
But when i statically linked wxWidgets library, i was able to call Show() from separate thread (in my case UILoader). I dont think there are restrictions calling GUI related methods (like ShowModal) from any workers thread (non UI thread/primary thread/GUI thread).
Is there any thing i have to do prior to load login.dll??? like setting dll's hInstance.... or something similar????
tan
wxWorld Domination!
wxWorld Domination!
Posts: 1471
Joined: Tue Nov 14, 2006 7:58 am
Location: Saint-Petersburg, Russia

Post by tan »

Hi,
Jaganlal wrote:
is it because of the fact that
1) wxWidgets is used as shared library by both MainApp.exe and Login.dll
2) since wxWidgets is shared, only one APP can be present (which in my case is MainApp) and only that APP can INVOKE FUNCTIONS EXPORTED BY LOGIN.DLL?
yes, it is, in general, correct.
Jaganlal wrote: If the above statement is true, then my MainApp::OnInit will be blocked until LoginDlg::ShowModal() returns which again needs to be resolved HUH!!!!! :x
Hmm... I don't get the problem. If you dont't want to block the main thread you can use LoginDlg::Show() instead of ShowModal().
OS: Windows XP Pro
Compiler: MSVC++ 7.1
wxWidgets: 2.8.10
Jaganlal
Earned a small fee
Earned a small fee
Posts: 11
Joined: Fri Dec 18, 2009 11:01 pm

Post by Jaganlal »

In a nut shell my application has
1 Executable - for program logic and a CONTROLLER class to control both UI and hardware component
2 DLLs - 1 to show UI and other to interact with bar code reader hardware

My intention is to use the CONTROLLER class to control both GUI and Hardware, this way i can decouple MainApp and use it for other purpose (like logging, performance monitoring etc...). So, again my question is - Is there anyway to INVOKE extern C functions exported by both UI.dll and Hardware.dll, without using MAINAPP???

Along with this i hv attached my blockdiagram (BlockDiagram.jpg), please look into that and pass on your valuable comments

Thanks in Advance,
Jaganlal
Attachments
BlockDiagram of my application
BlockDiagram of my application
BlockDiagram.JPG (16.05 KiB) Viewed 6014 times
Jaganlal
Earned a small fee
Earned a small fee
Posts: 11
Joined: Fri Dec 18, 2009 11:01 pm

Post by Jaganlal »

With my little understanding, i think only MainApp (or THE APP for ur application) can invoke extern "C" functions (exported by a dll) which involve GUI related stuffs like showmodal/show as in my case, i never tried other scenarios. Other non gui related dll functions can be called by any thread...

DO NOT INVOKE GUI RELATED FUNCTIONS, EXPORTED BY A DLL FROM A NON GUI THREAD.

Which makes more sense, coz there'll be only one app registered as "THE APP" with wxWidgets and only that APP takes the responsibility of UI related stuffs

NB: Again this conclusion holds good only when wxWidgets is used as a SHARED LIBRARY (DLL). And my experience is very limited to WINDOWS

TAN & FRANK thank you very much for your kind support, without your tips and information, i wudn't hv cracked this. Thank you once again....
User avatar
doublemax
Moderator
Moderator
Posts: 19114
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Post by doublemax »

DO NOT INVOKE GUI RELATED FUNCTIONS, EXPORTED BY A DLL FROM A NON GUI THREAD.
Just for clarification: This has nothing to do with DLL or not. Only the main thread can make gui calls. That's it.

The fact that it worked when you linked statically was unintentional. If your application *and* the dll use wxWidgets, you *must* use shared linking. Otherwise you end up with two different, independent wxWidgets instances. That's why it seemed to work for you.
Use the source, Luke!
tan
wxWorld Domination!
wxWorld Domination!
Posts: 1471
Joined: Tue Nov 14, 2006 7:58 am
Location: Saint-Petersburg, Russia

Post by tan »

Hi, just in addition
doublemax wrote:
DO NOT INVOKE GUI RELATED FUNCTIONS, EXPORTED BY A DLL FROM A NON GUI THREAD.
Just for clarification: This has nothing to do with DLL or not. Only the main thread can make gui calls. That's it.
It is not absolutely exact. Only the thread in which has been initialized WX can make gui calls. It is common case to initialize WX in the main thread, but it is not mandatory.
doublemax wrote: The fact that it worked when you linked statically was unintentional. If your application *and* the dll use wxWidgets, you *must* use shared linking.
It is not absolutely exact too. It is possible to link the app and dll using static linkage, but it is difficult trick :)
BTW, in last revisions in the trunk (don't remember in which revision exactly), Vadim has already added an example dll using static WX linkage (WXROOT/samples/dll). I very much recommend to look at.
doublemax wrote: Otherwise you end up with two different, independent wxWidgets instances. That's why it seemed to work for you.
Right.
OS: Windows XP Pro
Compiler: MSVC++ 7.1
wxWidgets: 2.8.10
Jaganlal
Earned a small fee
Earned a small fee
Posts: 11
Joined: Fri Dec 18, 2009 11:01 pm

Post by Jaganlal »

tan wrote:Hi, just in addition
doublemax wrote:
DO NOT INVOKE GUI RELATED FUNCTIONS, EXPORTED BY A DLL FROM A NON GUI THREAD.
Just for clarification: This has nothing to do with DLL or not. Only the main thread can make gui calls. That's it.
It is not absolutely exact. Only the thread in which has been initialized WX can make gui calls. It is common case to initialize WX in the main thread, but it is not mandatory.
Tan i agree with u, inside wxEntryStart every exe'll register its APP by calling SetInstance (which sets wxTheApp). So, only that "APP" can invoke GUI methods and this is wat i observed when linked wxWidgets as shared library.
When linking wxWidgets as static lib, every component (whether exe or dll) will have its own "wxTheApp".
Post Reply