]> git.saurik.com Git - wxWidgets.git/blob - samples/dll/my_dll.cpp
Make storing non-trivial data in wxThreadSpecificInfo possible.
[wxWidgets.git] / samples / dll / my_dll.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: my_dll.cpp
3 // Purpose: Sample showing how to use wx from a DLL
4 // Author: Vaclav Slavik
5 // Created: 2009-12-03
6 // Copyright: (c) 2009 Vaclav Slavik
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 // ============================================================================
11 // declarations
12 // ============================================================================
13
14 // ----------------------------------------------------------------------------
15 // headers
16 // ----------------------------------------------------------------------------
17
18 #include "wx/wxprec.h"
19
20 #ifdef __BORLANDC__
21 #pragma hdrstop
22 #endif
23
24 #ifndef __WINDOWS__
25 #error "This sample is Windows-only"
26 #endif
27
28 #include "wx/app.h"
29 #include "wx/dynlib.h"
30 #include "wx/frame.h"
31 #include "wx/panel.h"
32 #include "wx/sizer.h"
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"
38
39 #include <process.h> // for _beginthreadex()
40
41 #include "my_dll.h"
42
43 // ----------------------------------------------------------------------------
44 // GUI classes
45 // ----------------------------------------------------------------------------
46
47 class MyDllFrame : public wxFrame
48 {
49 public:
50 MyDllFrame(wxWindow *parent, const wxString& label);
51
52 void OnAbout(wxCommandEvent& event);
53
54 DECLARE_EVENT_TABLE()
55 };
56
57
58 static const int CMD_SHOW_WINDOW = wxNewId();
59 static const int CMD_TERMINATE = wxNewId();
60
61 class MyDllApp : public wxApp
62 {
63 public:
64 MyDllApp();
65
66 private:
67 void OnShowWindow(wxThreadEvent& event);
68 void OnTerminate(wxThreadEvent& event);
69 };
70
71
72 // ============================================================================
73 // implementation
74 // ============================================================================
75
76 // ----------------------------------------------------------------------------
77 // MyDllFrame
78 // ----------------------------------------------------------------------------
79
80 BEGIN_EVENT_TABLE(MyDllFrame, wxFrame)
81 EVT_BUTTON(wxID_ABOUT, MyDllFrame::OnAbout)
82 END_EVENT_TABLE()
83
84 MyDllFrame::MyDllFrame(wxWindow *parent, const wxString& label)
85 : wxFrame(parent, wxID_ANY, label)
86 {
87 wxPanel *p = new wxPanel(this, wxID_ANY);
88 wxSizer *sizer = new wxBoxSizer(wxVERTICAL);
89
90 sizer->Add
91 (
92 new wxStaticText
93 (
94 p, wxID_ANY,
95 wxString::Format
96 (
97 "Running using %s\n"
98 "wxApp instance is %p, thread ID %ld",
99 wxVERSION_STRING,
100 wxApp::GetInstance(),
101 wxThread::GetCurrentId()
102 )
103 ),
104 wxSizerFlags(1).Expand().Border(wxALL, 10)
105 );
106
107 sizer->Add
108 (
109 new wxButton(p, wxID_ABOUT, "Show info"),
110 wxSizerFlags(0).Right().Border(wxALL, 10)
111 );
112
113 p->SetSizerAndFit(sizer);
114
115 wxSizer *fsizer = new wxBoxSizer(wxVERTICAL);
116 fsizer->Add(p, wxSizerFlags(1).Expand());
117 SetSizerAndFit(fsizer);
118 }
119
120 void MyDllFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
121 {
122 wxMessageBox("This window is running in its own thread,\n"
123 "using private wxWidgets instance compiled into the DLL.",
124 "About",
125 wxOK | wxICON_INFORMATION);
126 }
127
128
129 // ----------------------------------------------------------------------------
130 // MyDllApp
131 // ----------------------------------------------------------------------------
132
133 MyDllApp::MyDllApp()
134 {
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.
138 //
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
143 // destroyed.
144 //
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);
149
150 Connect(CMD_SHOW_WINDOW,
151 wxEVT_THREAD,
152 wxThreadEventHandler(MyDllApp::OnShowWindow));
153 Connect(CMD_TERMINATE,
154 wxEVT_THREAD,
155 wxThreadEventHandler(MyDllApp::OnTerminate));
156 }
157
158 void MyDllApp::OnShowWindow(wxThreadEvent& event)
159 {
160 wxFrame *f = new MyDllFrame(NULL, event.GetString());
161 f->Show(true);
162 }
163
164 void MyDllApp::OnTerminate(wxThreadEvent& WXUNUSED(event))
165 {
166 ExitMainLoop();
167 }
168
169
170 // ----------------------------------------------------------------------------
171 // application startup
172 // ----------------------------------------------------------------------------
173
174 // we can't have WinMain() in a DLL and want to start the app ourselves
175 IMPLEMENT_APP_NO_MAIN(MyDllApp)
176
177 namespace
178 {
179
180 // Critical section that guards everything related to wxWidgets "main" thread
181 // startup or shutdown.
182 wxCriticalSection gs_wxStartupCS;
183 // Handle of wx "main" thread if running, NULL otherwise
184 HANDLE gs_wxMainThread = NULL;
185
186
187 // wx application startup code -- runs from its own thread
188 unsigned wxSTDCALL MyAppLauncher(void* event)
189 {
190 // Note: The thread that called run_wx_gui_from_dll() holds gs_wxStartupCS
191 // at this point and won't release it until we signal it.
192
193 // We need to pass correct HINSTANCE to wxEntry() and the right value is
194 // HINSTANCE of this DLL, not of the main .exe, use this MSW-specific wx
195 // function to get it. Notice that under Windows XP and later the name is
196 // not needed/used as we retrieve the DLL handle from an address inside it
197 // but you do need to use the correct name for this code to work with older
198 // systems as well.
199 const HINSTANCE
200 hInstance = wxDynamicLibrary::MSWGetModuleHandle("my_dll",
201 &gs_wxMainThread);
202 if ( !hInstance )
203 return 0; // failed to get DLL's handle
204
205 // IMPLEMENT_WXWIN_MAIN does this as the first thing
206 wxDISABLE_DEBUG_SUPPORT();
207
208 // We do this before wxEntry() explicitly, even though wxEntry() would
209 // do it too, so that we know when wx is initialized and can signal
210 // run_wx_gui_from_dll() about it *before* starting the event loop.
211 wxInitializer wxinit;
212 if ( !wxinit.IsOk() )
213 return 0; // failed to init wx
214
215 // Signal run_wx_gui_from_dll() that it can continue
216 HANDLE hEvent = *(static_cast<HANDLE*>(event));
217 if ( !SetEvent(hEvent) )
218 return 0; // failed setting up the mutex
219
220 // Run the app:
221 wxEntry(hInstance);
222
223 return 1;
224 }
225
226 } // anonymous namespace
227
228 // ----------------------------------------------------------------------------
229 // public DLL interface
230 // ----------------------------------------------------------------------------
231
232 extern "C"
233 {
234
235 void run_wx_gui_from_dll(const char *title)
236 {
237 // In order to prevent conflicts with hosting app's event loop, we
238 // launch wx app from the DLL in its own thread.
239 //
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.
245 //
246 // Note that we cannot use wxThread here, because the wx library wasn't
247 // initialized yet. wxCriticalSection is safe to use, though.
248
249 wxCriticalSectionLocker lock(gs_wxStartupCS);
250
251 if ( !gs_wxMainThread )
252 {
253 HANDLE hEvent = CreateEvent
254 (
255 NULL, // default security attributes
256 FALSE, // auto-reset
257 FALSE, // initially non-signaled
258 NULL // anonymous
259 );
260 if ( !hEvent )
261 return; // error
262
263 // NB: If your compiler doesn't have _beginthreadex(), use CreateThread()
264 gs_wxMainThread = (HANDLE)_beginthreadex
265 (
266 NULL, // default security
267 0, // default stack size
268 &MyAppLauncher,
269 &hEvent, // arguments
270 0, // create running
271 NULL
272 );
273
274 if ( !gs_wxMainThread )
275 {
276 CloseHandle(hEvent);
277 return; // error
278 }
279
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);
284 CloseHandle(hEvent);
285 }
286
287 // Send a message to wx thread to show a new frame:
288 wxThreadEvent *event =
289 new wxThreadEvent(wxEVT_THREAD, CMD_SHOW_WINDOW);
290 event->SetString(title);
291 wxQueueEvent(wxApp::GetInstance(), event);
292 }
293
294 void wx_dll_cleanup()
295 {
296 wxCriticalSectionLocker lock(gs_wxStartupCS);
297
298 if ( !gs_wxMainThread )
299 return;
300
301 // If wx main thread is running, we need to stop it. To accomplish this,
302 // send a message telling it to terminate the app.
303 wxThreadEvent *event =
304 new wxThreadEvent(wxEVT_THREAD, CMD_TERMINATE);
305 wxQueueEvent(wxApp::GetInstance(), event);
306
307 // We must then wait for the thread to actually terminate.
308 WaitForSingleObject(gs_wxMainThread, INFINITE);
309 CloseHandle(gs_wxMainThread);
310 gs_wxMainThread = NULL;
311 }
312
313 } // extern "C"