WX as a plug in for Non-WX program Topic is solved
-
- In need of some credit
- Posts: 6
- Joined: Wed Aug 26, 2009 7:43 am
- Location: Redmond, WA
- Contact:
WX as a plug in for Non-WX program
I'm writing a plug-in for an application that does not use WX and the application also runs the message pump. When the application loads the plug-in, I create a wxApp using IMPLEMENT_APP_NO_MAIN(MyApp) and wxEntryStart(...). In the OnInit() of the app I create a wxFrame. The application simply uses the windows functions GetMessage(...), TranslateMessage(...) and DispatchMessage(...); hence, my WX plugin does not invoke the normal WX MainLoop(). With the app running the message pump the wxFrame appears to respond to the input, but when the "X" of the frame is clicked WX does nothing. How can I fix this? What is the appropriate way to integrate WX into an application that wants to run the message pump?
-
- In need of some credit
- Posts: 6
- Joined: Wed Aug 26, 2009 7:43 am
- Location: Redmond, WA
- Contact:
I injected MDI children into a non-Widgets app
I had a situation similar to yours where I could extend an application through a DLL, and the app did not use Widgets, but I wanted to. Doing MDI child injection is not easy - there are a lot of bases to cover. But if you aren't using MDI children, widgets almost works right out of the box.
As you found out, though, there a few things that don't quite work, including tooltips, window cleanup after closure, and some combobox operations. However, you can get almost everything to work with just a few painless hacks.
Widgets depends on the idle loop for getting idles, but since the application is already hogging the loop, we need to hack into it ourselves. You provide the hook process like this
Widgets also needs the message loop for a couple items. We can hack it like this.
There are many other things you have to do to make MDI children fully work, including adding to some of the above code. But this should make all non-MDI child widgets code work, including all the controls.
Good luck with your project!
As you found out, though, there a few things that don't quite work, including tooltips, window cleanup after closure, and some combobox operations. However, you can get almost everything to work with just a few painless hacks.
Widgets depends on the idle loop for getting idles, but since the application is already hogging the loop, we need to hack into it ourselves. You provide the hook process like this
Code: Select all
HHOOK hForegroundIdleHook = NULL;
DWORD CALLBACK ForegroundIdleProc(int nCode, DWORD wParam, LONG lParam)
{
if (nCode >= 0)
{
// pump widget idle loops until done
// undocumented call to the application's idle loop, which also handles top-level windows
while (wxTheApp->ProcessIdle())
;
}
return CallNextHookEx(hForegroundIdleHook, nCode, wParam, lParam);
}
// elsewhere...
hForegroundIdleHook = SetWindowsHookEx(WH_FOREGROUNDIDLE, (HOOKPROC)ForegroundIdleProc, NULL, GetCurrentThreadId());
Code: Select all
HHOOK hGetMsgHook = NULL;
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode >= 0 && wParam == PM_REMOVE)
{
LPMSG lpMsg = (LPMSG)lParam;
switch (lpMsg->message)
{
// undocumented call - need to relay these to widgets so tooltips work
case WM_MOUSEMOVE:
wxToolTip::RelayEvent((LPMSG)lParam);
break;
// widgets pushes in NULL messages on each pending event
// using this undocumented call, we effectively forward these messages
case WM_NULL:
wxTheApp->ProcessPendingEvents();
break;
}
}
return CallNextHookEx(hGetMsgHook, nCode, wParam, lParam);
}
// elsewhere...
hGetMsgHook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, NULL, GetCurrentThreadId());
Good luck with your project!
Re: I injected MDI children into a non-Widgets app
I am sure that there are many users who would like to know how to do this with a MDI program too. Any chance you could write a tutorial to do this? Perhaps share your code in the code dump?dsmtoday wrote:I had a situation similar to yours where I could extend an application through a DLL, and the app did not use Widgets, but I wanted to. Doing MDI child injection is not easy - there are a lot of bases to cover. But if you aren't using MDI children, widgets almost works right out of the box.
<snip>
There are many other things you have to do to make MDI children fully work, including adding to some of the above code. But this should make all non-MDI child widgets code work, including all the controls.
Good luck with your project!
Follow the development of my screenplay authoring program at http://wxscreenplaywriter.blogspot.com/
Hello. I'm developing wx-plugin in non wx application too.
And thx to dsmtoday. Your code is works. But some things do not work properly.
For example wxProgressDialog wants event loop (check wxProgressDialog::Update()) for process gui events. So I created gui event loop for this situation in wxApp:
Now wxProgressDialog works fine.
Also shortcuts do not work too (and something wrong with TAB events). And I extended GetMsgProc():
I'm using above code in the project and hope it is valid)
Does anyone knows tricks about how to process wx events outside wx event loop?
And thx to dsmtoday. Your code is works. But some things do not work properly.
For example wxProgressDialog wants event loop (check wxProgressDialog::Update()) for process gui events. So I created gui event loop for this situation in wxApp:
Code: Select all
bool Application::OnInit()
{
...
// Note: do not forget to delete event loop in the desctructor
wxEventLoopBase *loop = wxEventLoopBase::GetActive();
wxEventLoopBase::SetActive(new wxGUIEventLoop());
wxDELETE(loop);
...
}
Also shortcuts do not work too (and something wrong with TAB events). And I extended GetMsgProc():
Code: Select all
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
...
default:
{
// code from wxGUIEventLoop::PreProcessMessage()
// (in src/msw/evtloop.cpp)
// for processing accelerators
HWND hwnd = lpMsg->hwnd;
WXDLLIMPEXP_CORE wxWindow *wxGetWindowFromHWND(WXHWND hwnd);
wxWindow *wndThis = wxGetWindowFromHWND((WXHWND)hwnd);
wxWindow *wnd;
// try translations first: the accelerators override everything
for ( wnd = wndThis; wnd; wnd = wnd->GetParent() )
{
if ( wnd->MSWTranslateMessage((WXMSG *)lpMsg))
break;
// stop at first top level window, i.e. don't try to process the key
// strokes originating in a dialog using the accelerators of the parent
// frame - this doesn't make much sense
if ( wnd->IsTopLevel() )
break;
}
break;
}
...
}
Does anyone knows tricks about how to process wx events outside wx event loop?
WinXp, wxWidgets 2.8.9, wxWidgets-svn, MSVC 9
Thanks for your suggestions about the progress meter and accelerators. I wasn't using either of those so I didn't see the problems. I had tabs working, but these were MDI child windows.
In the code snippets above, I tried to pull out all the hacks I did for the MDI child windows, because there were a lot, and because they were tangled in with hacks I needed to do to make it work with the EXE I am developing the plugin for. So I might have missed something. I did the work in this area of code a year or so ago, but I'm back into it and upgrading everything to 2.9.0. I will post additional items as I find them.
I would like to do a mini tutorial on the mdi child window plugin stuff, but you know how it is with time.
Here are some items I ripped out from the code snippets above. One thing I had to do was create a fake MDI parent frame that didn't really exist with a real window just so the widgets code would be happy. I made a function on it to idle all of its child frames, and I would call that function from the above idle loop hack. I had to keep my own list of child frames since I couldn't use any of the functions to attach them to the fake parent window or else there'd be problems with them being double added within Microsoft Windows (once by the EXE application I'm hacking, once by my fake frame). Anyway, that idle hack right there got rid of most of the problems with controls in my child windows not quite working (I think). But I only checked the controls I needed, I was not exhaustive.
Another problem with child window injection is the WM_MDIACTIVATE message (have to prevent widgets from using it while still allowing Microsoft to use it). Destruction and its affect on the vtable while WM_MDIACTIVATE is being processed is kinda hairy to get around as well.
I would really just like to release code that handles this stuff, unfortunately, it is bound up with additional code I needed to added to make it play well with the EXE I'm writing the plugin for, thus it will take some time to release something that is separated.
In the code snippets above, I tried to pull out all the hacks I did for the MDI child windows, because there were a lot, and because they were tangled in with hacks I needed to do to make it work with the EXE I am developing the plugin for. So I might have missed something. I did the work in this area of code a year or so ago, but I'm back into it and upgrading everything to 2.9.0. I will post additional items as I find them.
I would like to do a mini tutorial on the mdi child window plugin stuff, but you know how it is with time.
Here are some items I ripped out from the code snippets above. One thing I had to do was create a fake MDI parent frame that didn't really exist with a real window just so the widgets code would be happy. I made a function on it to idle all of its child frames, and I would call that function from the above idle loop hack. I had to keep my own list of child frames since I couldn't use any of the functions to attach them to the fake parent window or else there'd be problems with them being double added within Microsoft Windows (once by the EXE application I'm hacking, once by my fake frame). Anyway, that idle hack right there got rid of most of the problems with controls in my child windows not quite working (I think). But I only checked the controls I needed, I was not exhaustive.
Another problem with child window injection is the WM_MDIACTIVATE message (have to prevent widgets from using it while still allowing Microsoft to use it). Destruction and its affect on the vtable while WM_MDIACTIVATE is being processed is kinda hairy to get around as well.
I would really just like to release code that handles this stuff, unfortunately, it is bound up with additional code I needed to added to make it play well with the EXE I'm writing the plugin for, thus it will take some time to release something that is separated.
Here is how I do to trigger the idle event :
Here is how I do to handle the "cross event"
Did you manage to make your DLL work when it's called from a window application though ? (Mine works only when it's called from a console application)
Code: Select all
void AppWx::sendIdleEvent()
{
SendIdleEvents(_mainFrame,wxIdleEvent());
}
Code: Select all
BEGIN_EVENT_TABLE(FrameWx, wxFrame)
EVT_CLOSE(FrameWx::EvtClose)
END_EVENT_TABLE()
void FrameWx::EvtClose(wxCloseEvent& event)
{
PostQuitMessage(0);
}
Code: Select all
while(!_stopApplication)
{
MSG msg; // Windows Message Structure
// Is There A Message Waiting?
msg.hwnd;
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
_hWnd = msg.hwnd;
// Have We Received A Quit Message?
if (msg.message==WM_QUIT)
{
_stopApplication = true;
}
else
{
// Translate The Message
TranslateMessage(&msg);
// Dispatch The Message
DispatchMessage(&msg);
}
}
else// If There Are No Messages
{
clock_t now = clock();
if(difftime(now, _lastFrame) < _delta)
{
_app->sendIdleEvent();
Sleep(_lastFrame + _delta - now);
}
_lastFrame = now;
_app->refreshRenderingTarget();
//send refresh
}
}
My app is an extension for a host program. So it works from a window application...
I think the main difference between your solution and our is that we use hooks for process messages and pass them to the wx event loop and you use just loop with PeekMessage().
So for gui application you have to use hooks i think...
But anyway i can't figure out how to process AUI manager's events with hooks..
I think the main difference between your solution and our is that we use hooks for process messages and pass them to the wx event loop and you use just loop with PeekMessage().
So for gui application you have to use hooks i think...
But anyway i can't figure out how to process AUI manager's events with hooks..
WinXp, wxWidgets 2.8.9, wxWidgets-svn, MSVC 9
Does wxAUI work now in your application ?
Also how did you initialize wxWidgets so it works in a window application ?
At the moment I'm doing like this :
But for some reason it works only for consoles.
Also how did you initialize wxWidgets so it works in a window application ?
At the moment I'm doing like this :
Code: Select all
_app = new AppWx();
wxApp::SetInstance(_app);
int argc=1;
char** argv=0;
CHECK_FAIL_MSG(wxEntryStart(argc,argv),"wx initialization failed");
_app->CallOnInit();
Well, it works, but it have some problems when you try to move/drag aui panel. Everything (or almost everything) freezes until you release mouse button... And i do not know what to do.HarKoT wrote: Does wxAUI work now in your application ?
Here is my way (i donot post all code):HarKoT wrote: Also how did you initialize wxWidgets so it works in a window application ?
Code: Select all
// header file
class Application: public wxApp
{
public:
Application();
~Application();
bool OnInit();
int OnExit();
void OnFatalException();
static bool initWx();
static bool uninitWx();
private:
static HHOOK m_idleHook;
static HHOOK m_msgHook;
static DWORD CALLBACK IdleProc(int nCode, DWORD wParam, LONG lParam);
static LRESULT CALLBACK MsgProc(int nCode, WPARAM wParam, LPARAM lParam);
}
...
// cpp file
extern HINSTANCE module_instance;
HHOOK Application::m_idleHook = NULL;
HHOOK Application::m_msgHook = NULL;
IMPLEMENT_APP_NO_MAIN(Application)
Application::Application()
{
// call this to tell the library to call our OnFatalException()
wxHandleFatalExceptions();
}
//--------------------------------------------------------------------------------
Application::~Application()
{
wxEventLoopBase *e = wxEventLoopBase::GetActive();
wxEventLoopBase::SetActive(NULL);
wxDELETE(e);
}
//--------------------------------------------------------------------------------
bool Application::initWx()
{
WXDLLIMPEXP_BASE void wxSetInstance(HINSTANCE hInst);
wxSetInstance(module_instance);
int argc = 0;
wxChar **argv = NULL;
wxEntryStart(argc, argv);
if(!wxTheApp || !wxTheApp->CallOnInit())
return false;
m_idleHook = ::SetWindowsHookEx(
WH_FOREGROUNDIDLE,
(HOOKPROC)IdleProc,
NULL,
GetCurrentThreadId());
m_msgHook = ::SetWindowsHookEx(
WH_GETMESSAGE,
MsgProc,
NULL,
GetCurrentThreadId());
return true;
}
//--------------------------------------------------------------------------------
bool Application::uninitWx()
{
if( !wxTheApp )
return false;
MainWindow *win = aGui().mainWindow();
win->Close(true);
::UnhookWindowsHookEx(m_idleHook);
::UnhookWindowsHookEx(m_msgHook);
if( wxTheApp )
wxTheApp->OnExit();
wxEntryCleanup();
return true;
}
//--------------------------------------------------------------------------------
DWORD CALLBACK Application::IdleProc(int nCode, DWORD wParam, LONG lParam)
{
if (nCode >= 0) {
// pump widget idle loops until done
// undocumented call to the application's idle loop,
// which also handles top-level windows
while (wxTheApp->ProcessIdle());
}
return CallNextHookEx(m_idleHook, nCode, wParam, lParam);
}
//--------------------------------------------------------------------------------
LRESULT CALLBACK Application::MsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if(nCode >= 0 && wParam == PM_REMOVE){
LPMSG lpMsg = (LPMSG)lParam;
switch (lpMsg->message){
// undocumented call - need to relay these to widgets so tooltips work
case WM_MOUSEMOVE:
wxToolTip::RelayEvent((LPMSG)lParam);
break;
// widgets pushes in NULL messages on each pending event
// using this undocumented call, we effectively forward these messages
case WM_NULL:
wxTheApp->ProcessPendingEvents();
break;
default:
{
// code from wxGUIEventLoop::PreProcessMessage()
// (in src/msw/evtloop.cpp)
// for processing accelerators
HWND hwnd = lpMsg->hwnd;
WXDLLIMPEXP_CORE wxWindow *wxGetWindowFromHWND(WXHWND hwnd);
wxWindow *wndThis = wxGetWindowFromHWND((WXHWND)hwnd);
wxWindow *wnd;
// try translations first: the accelerators override everything
for(wnd = wndThis; wnd; wnd = wnd->GetParent()){
if(wnd->MSWTranslateMessage((WXMSG *)lpMsg))
break;
// stop at first top level window, i.e. don't try to process the key
// strokes originating in a dialog using the accelerators of the parent
// frame - this doesn't make much sense
if( wnd->IsTopLevel() )
break;
}
break;
}
} // switch
} // if
return CallNextHookEx(m_msgHook, nCode, wParam, lParam);
}
//--------------------------------------------------------------------------------
bool Application::OnInit()
{
if( !wxApp::OnInit() )
return false;
wxInitAllImageHandlers();
SetExitOnFrameDelete(true);
...
// Here we set gui event loop for stuff
// like pregress dialog Update() method.
// just to be sure..
wxEventLoopBase *loop = wxEventLoopBase::GetActive();
wxEventLoopBase::SetActive(new wxGUIEventLoop());
wxDELETE(loop);
return true;
}
//--------------------------------------------------------------------------------
WinXp, wxWidgets 2.8.9, wxWidgets-svn, MSVC 9
I found the problem with AUI.
AUI panel asking to process idle events while you moving it.
Chunk of the code from src\aui\floatpane.cpp:
event.RequestMore() - ask more processing.
And we process idle evens like:
And above loop will executes until you release mouse button...
So we have to do something in this loop to process some other events, i think.
But i do not know what to do in the loop..
Another solution is to remove while loop and do one call of the wxTheApp->ProcessIdle():
And this is works... Now we can move panel and everything seems fine...
Also i have another idea:
Here we will process only a few idle events at once (4 in this example).
Am I right? Can anybody tell me...
Oh.. f*cking events.
AUI panel asking to process idle events while you moving it.
Chunk of the code from src\aui\floatpane.cpp:
Code: Select all
void wxAuiFloatingFrame::OnIdle(wxIdleEvent& event)
{
if (m_moving)
{
if (!isMouseDown())
{
m_moving = false;
OnMoveFinished();
}
else
{
event.RequestMore();
}
}
}
And we process idle evens like:
Code: Select all
while (wxTheApp->ProcessIdle());
So we have to do something in this loop to process some other events, i think.
Code: Select all
while(wxTheApp->ProcessIdle()){
// do something.
}
Another solution is to remove while loop and do one call of the wxTheApp->ProcessIdle():
Code: Select all
DWORD CALLBACK Application::IdleProc(int nCode, DWORD wParam, LONG lParam)
{
if (nCode >= 0) {
//while (wxTheApp->ProcessIdle());
wxTheApp->ProcessIdle();
}
return CallNextHookEx(m_idleHook, nCode, wParam, lParam);
}
Also i have another idea:
Code: Select all
DWORD CALLBACK Application::IdleProc(int nCode, DWORD wParam, LONG lParam)
{
static int count = 0;
static const int max_count = 4;
if (nCode >= 0) {
while(wxTheApp->ProcessIdle() && ++count < max_count);
count = 0;
}
return CallNextHookEx(m_idleHook, nCode, wParam, lParam);
}
Am I right? Can anybody tell me...
Oh.. f*cking events.
WinXp, wxWidgets 2.8.9, wxWidgets-svn, MSVC 9