This is necessary to properly define MY_DLL_DECL as either WXIMPORT or WXEXPORT depending on whether my_dll.h is included from DLL itself or from outside it. Without this, MSVC generated warnings about inconsistent dll linkage. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@62802 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
		
			
				
	
	
		
			316 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			316 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /////////////////////////////////////////////////////////////////////////////
 | |
| // Name:        my_dll.cpp
 | |
| // Purpose:     Sample showing how to use wx from a DLL
 | |
| // Author:      Vaclav Slavik
 | |
| // Created:     2009-12-03
 | |
| // RCS-ID:      $Id$
 | |
| // Copyright:   (c) 2009 Vaclav Slavik
 | |
| // Licence:     wxWindows licence
 | |
| /////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
| // ============================================================================
 | |
| // declarations
 | |
| // ============================================================================
 | |
| 
 | |
| // ----------------------------------------------------------------------------
 | |
| // headers
 | |
| // ----------------------------------------------------------------------------
 | |
| 
 | |
| #include "wx/wxprec.h"
 | |
| 
 | |
| #ifdef __BORLANDC__
 | |
|     #pragma hdrstop
 | |
| #endif
 | |
| 
 | |
| #ifndef __WXMSW__
 | |
|     #error "This sample is MSW-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 <process.h> // 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(wxEVT_IDLE, wxIdleEventHandler(MyDllApp::OnIdle));
 | |
|     Connect(CMD_SHOW_WINDOW,
 | |
|             wxEVT_COMMAND_THREAD,
 | |
|             wxThreadEventHandler(MyDllApp::OnShowWindow));
 | |
|     Connect(CMD_TERMINATE,
 | |
|             wxEVT_COMMAND_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<HANDLE*>(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_COMMAND_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_COMMAND_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"
 |