///////////////////////////////////////////////////////////////////////////// // Name: my_dll.cpp // Purpose: Sample showing how to use wx from a DLL // Author: Vaclav Slavik // Created: 2009-12-03 // Copyright: (c) 2009 Vaclav Slavik // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// // ============================================================================ // declarations // ============================================================================ // ---------------------------------------------------------------------------- // headers // ---------------------------------------------------------------------------- #include "wx/wxprec.h" #ifdef __BORLANDC__ #pragma hdrstop #endif #ifndef __WINDOWS__ #error "This sample is Windows-only" #endif #include "wx/app.h" #include "wx/dynlib.h" #include "wx/frame.h" #include "wx/panel.h" #include "wx/sizer.h" #include "wx/stattext.h" #include "wx/button.h" #include "wx/thread.h" #include "wx/msgdlg.h" #include "wx/msw/wrapwin.h" #include // for _beginthreadex() #include "my_dll.h" // ---------------------------------------------------------------------------- // GUI classes // ---------------------------------------------------------------------------- class MyDllFrame : public wxFrame { public: MyDllFrame(wxWindow *parent, const wxString& label); void OnAbout(wxCommandEvent& event); DECLARE_EVENT_TABLE() }; static const int CMD_SHOW_WINDOW = wxNewId(); static const int CMD_TERMINATE = wxNewId(); class MyDllApp : public wxApp { public: MyDllApp(); private: void OnShowWindow(wxThreadEvent& event); void OnTerminate(wxThreadEvent& event); }; // ============================================================================ // implementation // ============================================================================ // ---------------------------------------------------------------------------- // MyDllFrame // ---------------------------------------------------------------------------- BEGIN_EVENT_TABLE(MyDllFrame, wxFrame) EVT_BUTTON(wxID_ABOUT, MyDllFrame::OnAbout) END_EVENT_TABLE() MyDllFrame::MyDllFrame(wxWindow *parent, const wxString& label) : wxFrame(parent, wxID_ANY, label) { wxPanel *p = new wxPanel(this, wxID_ANY); wxSizer *sizer = new wxBoxSizer(wxVERTICAL); sizer->Add ( new wxStaticText ( p, wxID_ANY, wxString::Format ( "Running using %s\n" "wxApp instance is %p, thread ID %ld", wxVERSION_STRING, wxApp::GetInstance(), wxThread::GetCurrentId() ) ), wxSizerFlags(1).Expand().Border(wxALL, 10) ); sizer->Add ( new wxButton(p, wxID_ABOUT, "Show info"), wxSizerFlags(0).Right().Border(wxALL, 10) ); p->SetSizerAndFit(sizer); wxSizer *fsizer = new wxBoxSizer(wxVERTICAL); fsizer->Add(p, wxSizerFlags(1).Expand()); SetSizerAndFit(fsizer); } void MyDllFrame::OnAbout(wxCommandEvent& WXUNUSED(event)) { wxMessageBox("This window is running in its own thread,\n" "using private wxWidgets instance compiled into the DLL.", "About", wxOK | wxICON_INFORMATION); } // ---------------------------------------------------------------------------- // MyDllApp // ---------------------------------------------------------------------------- MyDllApp::MyDllApp() { // Keep the wx "main" thread running even without windows. This greatly // simplifies threads handling, because we don't have to correctly // implement wx-thread restarting. // // Note that this only works if you don't explicitly call ExitMainLoop(), // except in reaction to wx_dll_cleanup()'s message. wx_dll_cleanup() // relies on the availability of wxApp instance and if the event loop // terminated, wxEntry() would return and wxApp instance would be // destroyed. // // Also note that this is efficient, because if there are no windows, the // thread will sleep waiting for a new event. We could safe some memory // by shutting the thread down when it's no longer needed, though. SetExitOnFrameDelete(false); Connect(CMD_SHOW_WINDOW, wxEVT_THREAD, wxThreadEventHandler(MyDllApp::OnShowWindow)); Connect(CMD_TERMINATE, wxEVT_THREAD, wxThreadEventHandler(MyDllApp::OnTerminate)); } void MyDllApp::OnShowWindow(wxThreadEvent& event) { wxFrame *f = new MyDllFrame(NULL, event.GetString()); f->Show(true); } void MyDllApp::OnTerminate(wxThreadEvent& WXUNUSED(event)) { ExitMainLoop(); } // ---------------------------------------------------------------------------- // application startup // ---------------------------------------------------------------------------- // we can't have WinMain() in a DLL and want to start the app ourselves IMPLEMENT_APP_NO_MAIN(MyDllApp) namespace { // Critical section that guards everything related to wxWidgets "main" thread // startup or shutdown. wxCriticalSection gs_wxStartupCS; // Handle of wx "main" thread if running, NULL otherwise HANDLE gs_wxMainThread = NULL; // wx application startup code -- runs from its own thread unsigned wxSTDCALL MyAppLauncher(void* event) { // Note: The thread that called run_wx_gui_from_dll() holds gs_wxStartupCS // at this point and won't release it until we signal it. // We need to pass correct HINSTANCE to wxEntry() and the right value is // HINSTANCE of this DLL, not of the main .exe, use this MSW-specific wx // function to get it. Notice that under Windows XP and later the name is // not needed/used as we retrieve the DLL handle from an address inside it // but you do need to use the correct name for this code to work with older // systems as well. const HINSTANCE hInstance = wxDynamicLibrary::MSWGetModuleHandle("my_dll", &gs_wxMainThread); if ( !hInstance ) return 0; // failed to get DLL's handle // IMPLEMENT_WXWIN_MAIN does this as the first thing wxDISABLE_DEBUG_SUPPORT(); // We do this before wxEntry() explicitly, even though wxEntry() would // do it too, so that we know when wx is initialized and can signal // run_wx_gui_from_dll() about it *before* starting the event loop. wxInitializer wxinit; if ( !wxinit.IsOk() ) return 0; // failed to init wx // Signal run_wx_gui_from_dll() that it can continue HANDLE hEvent = *(static_cast(event)); if ( !SetEvent(hEvent) ) return 0; // failed setting up the mutex // Run the app: wxEntry(hInstance); return 1; } } // anonymous namespace // ---------------------------------------------------------------------------- // public DLL interface // ---------------------------------------------------------------------------- extern "C" { void run_wx_gui_from_dll(const char *title) { // In order to prevent conflicts with hosting app's event loop, we // launch wx app from the DLL in its own thread. // // We can't even use wxInitializer: it initializes wxModules and one of // the modules it handles is wxThread's private module that remembers // ID of the main thread. But we need to fool wxWidgets into thinking that // the thread we are about to create now is the main thread, not the one // from which this function is called. // // Note that we cannot use wxThread here, because the wx library wasn't // initialized yet. wxCriticalSection is safe to use, though. wxCriticalSectionLocker lock(gs_wxStartupCS); if ( !gs_wxMainThread ) { HANDLE hEvent = CreateEvent ( NULL, // default security attributes FALSE, // auto-reset FALSE, // initially non-signaled NULL // anonymous ); if ( !hEvent ) return; // error // NB: If your compiler doesn't have _beginthreadex(), use CreateThread() gs_wxMainThread = (HANDLE)_beginthreadex ( NULL, // default security 0, // default stack size &MyAppLauncher, &hEvent, // arguments 0, // create running NULL ); if ( !gs_wxMainThread ) { CloseHandle(hEvent); return; // error } // Wait until MyAppLauncher signals us that wx was initialized. This // is because we use wxMessageQueue<> and wxString later and so must // be sure that they are in working state. WaitForSingleObject(hEvent, INFINITE); CloseHandle(hEvent); } // Send a message to wx thread to show a new frame: wxThreadEvent *event = new wxThreadEvent(wxEVT_THREAD, CMD_SHOW_WINDOW); event->SetString(title); wxQueueEvent(wxApp::GetInstance(), event); } void wx_dll_cleanup() { wxCriticalSectionLocker lock(gs_wxStartupCS); if ( !gs_wxMainThread ) return; // If wx main thread is running, we need to stop it. To accomplish this, // send a message telling it to terminate the app. wxThreadEvent *event = new wxThreadEvent(wxEVT_THREAD, CMD_TERMINATE); wxQueueEvent(wxApp::GetInstance(), event); // We must then wait for the thread to actually terminate. WaitForSingleObject(gs_wxMainThread, INFINITE); CloseHandle(gs_wxMainThread); gs_wxMainThread = NULL; } } // extern "C"