1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: Sample showing how to use wx from a DLL
4 // Author: Vaclav Slavik
7 // Copyright: (c) 2009 Vaclav Slavik
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // ============================================================================
13 // ============================================================================
15 // ----------------------------------------------------------------------------
17 // ----------------------------------------------------------------------------
19 #include "wx/wxprec.h"
26 #error "This sample is MSW-only"
33 #include "wx/stattext.h"
34 #include "wx/button.h"
35 #include "wx/thread.h"
36 #include "wx/msgdlg.h"
37 #include "wx/msw/wrapwin.h"
39 #include <process.h> // for _beginthreadex()
43 // ----------------------------------------------------------------------------
45 // ----------------------------------------------------------------------------
47 class MyDllFrame
: public wxFrame
50 MyDllFrame(wxWindow
*parent
, const wxString
& label
);
52 void OnAbout(wxCommandEvent
& event
);
58 static const int CMD_SHOW_WINDOW
= wxNewId();
59 static const int CMD_TERMINATE
= wxNewId();
61 class MyDllApp
: public wxApp
67 void OnShowWindow(wxThreadEvent
& event
);
68 void OnTerminate(wxThreadEvent
& event
);
72 // ============================================================================
74 // ============================================================================
76 // ----------------------------------------------------------------------------
78 // ----------------------------------------------------------------------------
80 BEGIN_EVENT_TABLE(MyDllFrame
, wxFrame
)
81 EVT_BUTTON(wxID_ABOUT
, MyDllFrame::OnAbout
)
84 MyDllFrame::MyDllFrame(wxWindow
*parent
, const wxString
& label
)
85 : wxFrame(parent
, wxID_ANY
, label
)
87 wxPanel
*p
= new wxPanel(this, wxID_ANY
);
88 wxSizer
*sizer
= new wxBoxSizer(wxVERTICAL
);
98 "wxApp instance is %p, thread ID %ld",
100 wxApp::GetInstance(),
101 wxThread::GetCurrentId()
104 wxSizerFlags(1).Expand().Border(wxALL
, 10)
109 new wxButton(p
, wxID_ABOUT
, "Show info"),
110 wxSizerFlags(0).Right().Border(wxALL
, 10)
113 p
->SetSizerAndFit(sizer
);
115 wxSizer
*fsizer
= new wxBoxSizer(wxVERTICAL
);
116 fsizer
->Add(p
, wxSizerFlags(1).Expand());
117 SetSizerAndFit(fsizer
);
120 void MyDllFrame::OnAbout(wxCommandEvent
& WXUNUSED(event
))
122 wxMessageBox("This window is running in its own thread,\n"
123 "using private wxWidgets instance compiled into the DLL.",
125 wxOK
| wxICON_INFORMATION
);
129 // ----------------------------------------------------------------------------
131 // ----------------------------------------------------------------------------
135 // Keep the wx "main" thread running even without windows. This greatly
136 // simplifies threads handling, because we don't have to correctly
137 // implement wx-thread restarting.
139 // Note that this only works if you don't explicitly call ExitMainLoop(),
140 // except in reaction to wx_dll_cleanup()'s message. wx_dll_cleanup()
141 // relies on the availability of wxApp instance and if the event loop
142 // terminated, wxEntry() would return and wxApp instance would be
145 // Also note that this is efficient, because if there are no windows, the
146 // thread will sleep waiting for a new event. We could safe some memory
147 // by shutting the thread down when it's no longer needed, though.
148 SetExitOnFrameDelete(false);
150 Connect(wxEVT_IDLE
, wxIdleEventHandler(MyDllApp::OnIdle
));
151 Connect(CMD_SHOW_WINDOW
,
152 wxEVT_COMMAND_THREAD
,
153 wxThreadEventHandler(MyDllApp::OnShowWindow
));
154 Connect(CMD_TERMINATE
,
155 wxEVT_COMMAND_THREAD
,
156 wxThreadEventHandler(MyDllApp::OnTerminate
));
159 void MyDllApp::OnShowWindow(wxThreadEvent
& event
)
161 wxFrame
*f
= new MyDllFrame(NULL
, event
.GetString());
165 void MyDllApp::OnTerminate(wxThreadEvent
& WXUNUSED(event
))
171 // ----------------------------------------------------------------------------
172 // application startup
173 // ----------------------------------------------------------------------------
175 // we can't have WinMain() in a DLL and want to start the app ourselves
176 IMPLEMENT_APP_NO_MAIN(MyDllApp
)
181 // Critical section that guards everything related to wxWidgets "main" thread
182 // startup or shutdown.
183 wxCriticalSection gs_wxStartupCS
;
184 // Handle of wx "main" thread if running, NULL otherwise
185 HANDLE gs_wxMainThread
= NULL
;
188 // wx application startup code -- runs from its own thread
189 __stdcall
unsigned MyAppLauncher(void* event
)
191 // Note: The thread that called run_wx_gui_from_dll() holds gs_wxStartupCS
192 // at this point and won't release it until we signal it.
194 // We need to pass correct HINSTANCE to wxEntry() and the right value is
195 // HINSTANCE of this DLL, not of the main .exe.
197 // This method of obtaining DLL's instance handle requires at least
198 // Windows XP/2003. We could also implement DllMain() and remember it from
199 // there, that would work on older systems too.
201 int ret
= GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
,
202 (LPCTSTR
)&MyAppLauncher
,
205 return 0; // failed to get DLL's handle
207 // IMPLEMENT_WXWIN_MAIN does this as the first thing
208 wxDISABLE_DEBUG_SUPPORT();
210 // We do this before wxEntry() explicitly, even though wxEntry() would
211 // do it too, so that we know when wx is initialized and can signal
212 // run_wx_gui_from_dll() about it *before* starting the event loop.
213 wxInitializer wxinit
;
214 if ( !wxinit
.IsOk() )
215 return 0; // failed to init wx
217 // Signal run_wx_gui_from_dll() that it can continue
218 HANDLE hEvent
= *(static_cast<HANDLE
*>(event
));
219 if ( !SetEvent(hEvent
) )
220 return 0; // failed setting up the mutex
228 } // anonymous namespace
230 // ----------------------------------------------------------------------------
231 // public DLL interface
232 // ----------------------------------------------------------------------------
235 void run_wx_gui_from_dll(const char *title
)
237 // In order to prevent conflicts with hosting app's event loop, we
238 // launch wx app from the DLL in its own thread.
240 // We can't even use wxInitializer: it initializes wxModules and one of
241 // the modules it handles is wxThread's private module that remembers
242 // ID of the main thread. But we need to fool wxWidgets into thinking that
243 // the thread we are about to create now is the main thread, not the one
244 // from which this function is called.
246 // Note that we cannot use wxThread here, because the wx library wasn't
247 // initialized yet. wxCriticalSection is safe to use, though.
249 wxCriticalSectionLocker
lock(gs_wxStartupCS
);
251 if ( !gs_wxMainThread
)
253 HANDLE hEvent
= CreateEvent
255 NULL
, // default security attributes
257 FALSE
, // initially non-signaled
263 // NB: If your compiler doesn't have _beginthreadex(), use CreateThread()
264 gs_wxMainThread
= (HANDLE
)_beginthreadex
266 NULL
, // default security
267 0, // default stack size
269 &hEvent
, // arguments
274 if ( !gs_wxMainThread
)
280 // Wait until MyAppLauncher signals us that wx was initialized. This
281 // is because we use wxMessageQueue<> and wxString later and so must
282 // be sure that they are in working state.
283 WaitForSingleObject(hEvent
, INFINITE
);
287 // Send a message to wx thread to show a new frame:
288 wxThreadEvent
*event
=
289 new wxThreadEvent(wxEVT_COMMAND_THREAD
, CMD_SHOW_WINDOW
);
290 event
->SetString(title
);
291 wxQueueEvent(wxApp::GetInstance(), event
);
296 void wx_dll_cleanup(void)
298 wxCriticalSectionLocker
lock(gs_wxStartupCS
);
300 if ( !gs_wxMainThread
)
303 // If wx main thread is running, we need to stop it. To accomplish this,
304 // send a message telling it to terminate the app.
305 wxThreadEvent
*event
=
306 new wxThreadEvent(wxEVT_COMMAND_THREAD
, CMD_TERMINATE
);
307 wxQueueEvent(wxApp::GetInstance(), event
);
309 // We must then wait for the thread to actually terminate.
310 WaitForSingleObject(gs_wxMainThread
, INFINITE
);
311 CloseHandle(gs_wxMainThread
);
312 gs_wxMainThread
= NULL
;