]>
Commit | Line | Data |
---|---|---|
61aba460 VS |
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 | // RCS-ID: $Id$ | |
7 | // Copyright: (c) 2009 Vaclav Slavik | |
8 | // Licence: wxWindows licence | |
9 | ///////////////////////////////////////////////////////////////////////////// | |
10 | ||
11 | // ============================================================================ | |
12 | // declarations | |
13 | // ============================================================================ | |
14 | ||
15 | // ---------------------------------------------------------------------------- | |
16 | // headers | |
17 | // ---------------------------------------------------------------------------- | |
18 | ||
19 | #include "wx/wxprec.h" | |
20 | ||
21 | #ifdef __BORLANDC__ | |
22 | #pragma hdrstop | |
23 | #endif | |
24 | ||
bb5a9514 VZ |
25 | #ifndef __WINDOWS__ |
26 | #error "This sample is Windows-only" | |
61aba460 VS |
27 | #endif |
28 | ||
29 | #include "wx/app.h" | |
c118a476 | 30 | #include "wx/dynlib.h" |
61aba460 VS |
31 | #include "wx/frame.h" |
32 | #include "wx/panel.h" | |
33 | #include "wx/sizer.h" | |
34 | #include "wx/stattext.h" | |
35 | #include "wx/button.h" | |
36 | #include "wx/thread.h" | |
37 | #include "wx/msgdlg.h" | |
38 | #include "wx/msw/wrapwin.h" | |
39 | ||
40 | #include <process.h> // for _beginthreadex() | |
41 | ||
42 | #include "my_dll.h" | |
43 | ||
44 | // ---------------------------------------------------------------------------- | |
45 | // GUI classes | |
46 | // ---------------------------------------------------------------------------- | |
47 | ||
48 | class MyDllFrame : public wxFrame | |
49 | { | |
50 | public: | |
51 | MyDllFrame(wxWindow *parent, const wxString& label); | |
52 | ||
53 | void OnAbout(wxCommandEvent& event); | |
54 | ||
55 | DECLARE_EVENT_TABLE() | |
56 | }; | |
57 | ||
58 | ||
59 | static const int CMD_SHOW_WINDOW = wxNewId(); | |
60 | static const int CMD_TERMINATE = wxNewId(); | |
61 | ||
62 | class MyDllApp : public wxApp | |
63 | { | |
64 | public: | |
65 | MyDllApp(); | |
66 | ||
67 | private: | |
68 | void OnShowWindow(wxThreadEvent& event); | |
69 | void OnTerminate(wxThreadEvent& event); | |
70 | }; | |
71 | ||
72 | ||
73 | // ============================================================================ | |
74 | // implementation | |
75 | // ============================================================================ | |
76 | ||
77 | // ---------------------------------------------------------------------------- | |
78 | // MyDllFrame | |
79 | // ---------------------------------------------------------------------------- | |
80 | ||
81 | BEGIN_EVENT_TABLE(MyDllFrame, wxFrame) | |
82 | EVT_BUTTON(wxID_ABOUT, MyDllFrame::OnAbout) | |
83 | END_EVENT_TABLE() | |
84 | ||
85 | MyDllFrame::MyDllFrame(wxWindow *parent, const wxString& label) | |
86 | : wxFrame(parent, wxID_ANY, label) | |
87 | { | |
88 | wxPanel *p = new wxPanel(this, wxID_ANY); | |
89 | wxSizer *sizer = new wxBoxSizer(wxVERTICAL); | |
90 | ||
91 | sizer->Add | |
92 | ( | |
93 | new wxStaticText | |
94 | ( | |
95 | p, wxID_ANY, | |
96 | wxString::Format | |
97 | ( | |
98 | "Running using %s\n" | |
99 | "wxApp instance is %p, thread ID %ld", | |
100 | wxVERSION_STRING, | |
101 | wxApp::GetInstance(), | |
102 | wxThread::GetCurrentId() | |
103 | ) | |
104 | ), | |
105 | wxSizerFlags(1).Expand().Border(wxALL, 10) | |
106 | ); | |
107 | ||
108 | sizer->Add | |
109 | ( | |
110 | new wxButton(p, wxID_ABOUT, "Show info"), | |
111 | wxSizerFlags(0).Right().Border(wxALL, 10) | |
112 | ); | |
113 | ||
114 | p->SetSizerAndFit(sizer); | |
115 | ||
116 | wxSizer *fsizer = new wxBoxSizer(wxVERTICAL); | |
117 | fsizer->Add(p, wxSizerFlags(1).Expand()); | |
118 | SetSizerAndFit(fsizer); | |
119 | } | |
120 | ||
121 | void MyDllFrame::OnAbout(wxCommandEvent& WXUNUSED(event)) | |
122 | { | |
123 | wxMessageBox("This window is running in its own thread,\n" | |
124 | "using private wxWidgets instance compiled into the DLL.", | |
125 | "About", | |
126 | wxOK | wxICON_INFORMATION); | |
127 | } | |
128 | ||
129 | ||
130 | // ---------------------------------------------------------------------------- | |
131 | // MyDllApp | |
132 | // ---------------------------------------------------------------------------- | |
133 | ||
134 | MyDllApp::MyDllApp() | |
135 | { | |
136 | // Keep the wx "main" thread running even without windows. This greatly | |
137 | // simplifies threads handling, because we don't have to correctly | |
138 | // implement wx-thread restarting. | |
139 | // | |
140 | // Note that this only works if you don't explicitly call ExitMainLoop(), | |
141 | // except in reaction to wx_dll_cleanup()'s message. wx_dll_cleanup() | |
142 | // relies on the availability of wxApp instance and if the event loop | |
143 | // terminated, wxEntry() would return and wxApp instance would be | |
144 | // destroyed. | |
145 | // | |
146 | // Also note that this is efficient, because if there are no windows, the | |
147 | // thread will sleep waiting for a new event. We could safe some memory | |
148 | // by shutting the thread down when it's no longer needed, though. | |
149 | SetExitOnFrameDelete(false); | |
150 | ||
61aba460 | 151 | Connect(CMD_SHOW_WINDOW, |
c1b293bb | 152 | wxEVT_THREAD, |
61aba460 VS |
153 | wxThreadEventHandler(MyDllApp::OnShowWindow)); |
154 | Connect(CMD_TERMINATE, | |
c1b293bb | 155 | wxEVT_THREAD, |
61aba460 VS |
156 | wxThreadEventHandler(MyDllApp::OnTerminate)); |
157 | } | |
158 | ||
159 | void MyDllApp::OnShowWindow(wxThreadEvent& event) | |
160 | { | |
161 | wxFrame *f = new MyDllFrame(NULL, event.GetString()); | |
162 | f->Show(true); | |
163 | } | |
164 | ||
165 | void MyDllApp::OnTerminate(wxThreadEvent& WXUNUSED(event)) | |
166 | { | |
167 | ExitMainLoop(); | |
168 | } | |
169 | ||
170 | ||
171 | // ---------------------------------------------------------------------------- | |
172 | // application startup | |
173 | // ---------------------------------------------------------------------------- | |
174 | ||
175 | // we can't have WinMain() in a DLL and want to start the app ourselves | |
176 | IMPLEMENT_APP_NO_MAIN(MyDllApp) | |
177 | ||
178 | namespace | |
179 | { | |
180 | ||
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; | |
186 | ||
187 | ||
188 | // wx application startup code -- runs from its own thread | |
0530434e | 189 | unsigned wxSTDCALL MyAppLauncher(void* event) |
61aba460 VS |
190 | { |
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. | |
193 | ||
194 | // We need to pass correct HINSTANCE to wxEntry() and the right value is | |
c118a476 VZ |
195 | // HINSTANCE of this DLL, not of the main .exe, use this MSW-specific wx |
196 | // function to get it. Notice that under Windows XP and later the name is | |
197 | // not needed/used as we retrieve the DLL handle from an address inside it | |
198 | // but you do need to use the correct name for this code to work with older | |
199 | // systems as well. | |
200 | const HINSTANCE | |
201 | hInstance = wxDynamicLibrary::MSWGetModuleHandle("my_dll", | |
202 | &gs_wxMainThread); | |
203 | if ( !hInstance ) | |
61aba460 VS |
204 | return 0; // failed to get DLL's handle |
205 | ||
206 | // IMPLEMENT_WXWIN_MAIN does this as the first thing | |
207 | wxDISABLE_DEBUG_SUPPORT(); | |
208 | ||
209 | // We do this before wxEntry() explicitly, even though wxEntry() would | |
210 | // do it too, so that we know when wx is initialized and can signal | |
211 | // run_wx_gui_from_dll() about it *before* starting the event loop. | |
212 | wxInitializer wxinit; | |
213 | if ( !wxinit.IsOk() ) | |
214 | return 0; // failed to init wx | |
215 | ||
216 | // Signal run_wx_gui_from_dll() that it can continue | |
217 | HANDLE hEvent = *(static_cast<HANDLE*>(event)); | |
218 | if ( !SetEvent(hEvent) ) | |
219 | return 0; // failed setting up the mutex | |
220 | ||
221 | // Run the app: | |
222 | wxEntry(hInstance); | |
223 | ||
224 | return 1; | |
225 | } | |
226 | ||
227 | } // anonymous namespace | |
228 | ||
229 | // ---------------------------------------------------------------------------- | |
230 | // public DLL interface | |
231 | // ---------------------------------------------------------------------------- | |
232 | ||
9f38fe4b VZ |
233 | extern "C" |
234 | { | |
235 | ||
61aba460 VS |
236 | void run_wx_gui_from_dll(const char *title) |
237 | { | |
238 | // In order to prevent conflicts with hosting app's event loop, we | |
239 | // launch wx app from the DLL in its own thread. | |
240 | // | |
241 | // We can't even use wxInitializer: it initializes wxModules and one of | |
242 | // the modules it handles is wxThread's private module that remembers | |
243 | // ID of the main thread. But we need to fool wxWidgets into thinking that | |
244 | // the thread we are about to create now is the main thread, not the one | |
245 | // from which this function is called. | |
246 | // | |
247 | // Note that we cannot use wxThread here, because the wx library wasn't | |
248 | // initialized yet. wxCriticalSection is safe to use, though. | |
249 | ||
250 | wxCriticalSectionLocker lock(gs_wxStartupCS); | |
251 | ||
252 | if ( !gs_wxMainThread ) | |
253 | { | |
254 | HANDLE hEvent = CreateEvent | |
255 | ( | |
256 | NULL, // default security attributes | |
257 | FALSE, // auto-reset | |
258 | FALSE, // initially non-signaled | |
259 | NULL // anonymous | |
260 | ); | |
261 | if ( !hEvent ) | |
262 | return; // error | |
263 | ||
264 | // NB: If your compiler doesn't have _beginthreadex(), use CreateThread() | |
265 | gs_wxMainThread = (HANDLE)_beginthreadex | |
266 | ( | |
267 | NULL, // default security | |
268 | 0, // default stack size | |
269 | &MyAppLauncher, | |
270 | &hEvent, // arguments | |
271 | 0, // create running | |
272 | NULL | |
273 | ); | |
274 | ||
275 | if ( !gs_wxMainThread ) | |
276 | { | |
277 | CloseHandle(hEvent); | |
278 | return; // error | |
279 | } | |
280 | ||
281 | // Wait until MyAppLauncher signals us that wx was initialized. This | |
282 | // is because we use wxMessageQueue<> and wxString later and so must | |
283 | // be sure that they are in working state. | |
284 | WaitForSingleObject(hEvent, INFINITE); | |
285 | CloseHandle(hEvent); | |
286 | } | |
287 | ||
288 | // Send a message to wx thread to show a new frame: | |
289 | wxThreadEvent *event = | |
c1b293bb | 290 | new wxThreadEvent(wxEVT_THREAD, CMD_SHOW_WINDOW); |
61aba460 VS |
291 | event->SetString(title); |
292 | wxQueueEvent(wxApp::GetInstance(), event); | |
293 | } | |
294 | ||
9f38fe4b | 295 | void wx_dll_cleanup() |
61aba460 VS |
296 | { |
297 | wxCriticalSectionLocker lock(gs_wxStartupCS); | |
298 | ||
299 | if ( !gs_wxMainThread ) | |
300 | return; | |
301 | ||
302 | // If wx main thread is running, we need to stop it. To accomplish this, | |
303 | // send a message telling it to terminate the app. | |
304 | wxThreadEvent *event = | |
c1b293bb | 305 | new wxThreadEvent(wxEVT_THREAD, CMD_TERMINATE); |
61aba460 VS |
306 | wxQueueEvent(wxApp::GetInstance(), event); |
307 | ||
308 | // We must then wait for the thread to actually terminate. | |
309 | WaitForSingleObject(gs_wxMainThread, INFINITE); | |
310 | CloseHandle(gs_wxMainThread); | |
311 | gs_wxMainThread = NULL; | |
312 | } | |
9f38fe4b VZ |
313 | |
314 | } // extern "C" |