wxWidgets/samples/dll/my_dll.cpp

307 lines
9.9 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// 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"
#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 <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);
wxDECLARE_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
// ----------------------------------------------------------------------------
wxBEGIN_EVENT_TABLE(MyDllFrame, wxFrame)
EVT_BUTTON(wxID_ABOUT, MyDllFrame::OnAbout)
wxEND_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);
Bind(wxEVT_THREAD, &MyDllApp::OnShowWindow, this, CMD_SHOW_WINDOW);
Bind(wxEVT_THREAD, &MyDllApp::OnTerminate, this, CMD_TERMINATE);
}
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
wxIMPLEMENT_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
// wxIMPLEMENT_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_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"