Combine WinAPI app with wxWidgets

Do you have a typical platform dependent issue you're battling with ? Ask it here. Make sure you mention your platform, compiler, and wxWidgets version.
Post Reply
yah_nosh
In need of some credit
In need of some credit
Posts: 8
Joined: Wed Jun 24, 2020 10:17 pm

Combine WinAPI app with wxWidgets

Post by yah_nosh »

Hi all,

My OS is Windows 10, I use MSVC v142 (combined with VS2019), and wxWidgets 3.1.3. I have an old Windows C++ application that uses WinAPI for its GUI features, i.e Windows message loop, using "CreateWindow", and having to "manually" create all window procedures and event handling. I want to improve this application by gradually replacing the UI using wxWidgets so I don't have to start over from scratch. I would implement new, independent UI features in wxWidgets (e.g specific dialogs), and then gradually work my way back and replace all the old UI code with a wxWidgets implementation, without having to break the app along the way.

Until I can fully replace WinAPI with wxWidgets, I need to find a way to allow the two to "coexist", with the main function and message loop being initialized by WinAPI. First of all, is this actually possible? If so, where and how should I initialize wxWidgets so that I can create custom items (e.g dialogs) at runtime? From what I could find online, I need to make a class that's derived from wxApp and use the "wxIMPLEMENT_APP_NO_MAIN" macro, but beyond that point, the explanations got rather vague.

Below is a very stripped-down skeleton of my app:

Code: Select all

LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)    
{
	// Main window procedure code...
}

int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, char* lpCmdLine, int nCmdShow)
{
	// Register main window class, etc...
	// Have the window use MainWndProc
	HWND mainwnd = CreateWindow(/* Parameters... */);
	
	do
	{
		MSG msg = {};
		while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
		{
			// Main message loop, translate and dispatch messages
			// ...
		}
		// Per-frame application logic...
	} while (msg.message != WM_QUIT);
	
	// Clean up all resources
	return 0;
}
Thanks in advance for your help!
ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 7478
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: Combine WinAPI app with wxWidgets

Post by ONEEYEMAN »

Hi,
It would be much easier in the long run to start coding from scratch.
Besides wxWidgets is an actual C++ library, just like MFC and WinAPI is using the C.

Thank you.
yah_nosh
In need of some credit
In need of some credit
Posts: 8
Joined: Wed Jun 24, 2020 10:17 pm

Re: Combine WinAPI app with wxWidgets

Post by yah_nosh »

ONEEYEMAN wrote: Thu Jun 25, 2020 6:13 am Hi,
It would be much easier in the long run to start coding from scratch.
Besides wxWidgets is an actual C++ library, just like MFC and WinAPI is using the C.

Thank you.
I'm aware of that, but starting over would take way too much time and effort all at once. I don't mind if it's a bit of a hassle to make the two frameworks cooperate for the time being, since in the long run WinAPI would be phased out entirely.
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Combine WinAPI app with wxWidgets

Post by doublemax »

You should look into the "mfc" sample that comes with wxWidgets. Although it does not exactly what you need, it shows how to initialize wxWidgets when using wxIMPLEMENT_APP_NO_MAIN and how to have a separate event loop.

I've never used this though, so i probably won't be able to help any further.
Use the source, Luke!
yah_nosh
In need of some credit
In need of some credit
Posts: 8
Joined: Wed Jun 24, 2020 10:17 pm

Re: Combine WinAPI app with wxWidgets

Post by yah_nosh »

doublemax wrote: Thu Jun 25, 2020 11:34 am You should look into the "mfc" sample that comes with wxWidgets. Although it does not exactly what you need, it shows how to initialize wxWidgets when using wxIMPLEMENT_APP_NO_MAIN and how to have a separate event loop.

I've never used this though, so i probably won't be able to help any further.
I looked into it, this does seem like a good direction, but the problem is that the sample relies on MFC, which isn't used by my app (it works entirely with raw WinAPI). The sample does actually explain how to make an MFC window and then add wxWidgets windows to it, but I can't do that without including MFC.

Having looked around, however, I found that one potential solution could be to just run wxWidgets on a separate thread. Would that also work, and if so, what dangers should I be aware of?
OS: Windows 10
Compiler: MSVC v142 (with VS 2019)
wxWidgets version: 3.1.3
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Combine WinAPI app with wxWidgets

Post by doublemax »

The sample does actually explain how to make an MFC window and then add wxWidgets windows to it, but I can't do that without including MFC.
Can't you just replace the MFC part with the Win32 API?
Having looked around, however, I found that one potential solution could be to just run wxWidgets on a separate thread. Would that also work, and if so, what dangers should I be aware of?
That would work theoretically, but all communication/data exchange etc. between the two different "worlds" would have to be done through events. I don't think that's reasonable for what you're trying to do.
Use the source, Luke!
ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 7478
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: Combine WinAPI app with wxWidgets

Post by ONEEYEMAN »

Hi,
If you mean to create a thread and inside this thread create wxApp-derived object - then it might work.
Otgher than that - no.

All wxWidgets classes are not MT-safe and therefore it is not a good idea to access wxWidgets GUI from a secondary thread (secondary in this context means the thread that didn't create a wxApp-derived class).

But as I say - it is much easier to just rewrite the application with C++/wxWidgets.

Thank you.
yah_nosh
In need of some credit
In need of some credit
Posts: 8
Joined: Wed Jun 24, 2020 10:17 pm

Re: Combine WinAPI app with wxWidgets

Post by yah_nosh »

doublemax wrote: Fri Jun 26, 2020 6:38 pmCan't you just replace the MFC part with the Win32 API?
I'm not sure, but I may be misunderstanding the issue altogether. I just found this thread, which appears to have a similar problem to solve, and the answers given seem quite simple.

So, to clarify: if I set up a wxApp, would its message loop dispatch any WinAPI messages, for which you would normally set up a loop like the one in my original post? In other words, is it enough to just let wxWidgets handle the message loop, and would that allow windows that are created via standard WinAPI calls (i.e "CreateWindow") to work as before?

If so, I am quite happy to just get rid of the Windows message loop and use a class derived from wxApp instead (I might not even need the APP_NO_MAIN macro?) My only concern is that I want to avoid having to convert every window procedure and "CreateWindow" call with wxWidgets code right away.
OS: Windows 10
Compiler: MSVC v142 (with VS 2019)
wxWidgets version: 3.1.3
ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 7478
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: Combine WinAPI app with wxWidgets

Post by ONEEYEMAN »

Hi,
You could try the suggestion there.
However, keep in mind that wxWidgets is a C++ library and WinAPI is C-based.

So it is possible that at one point of time you will hit the brick and will just do everything from scratch.

Besides that thread is old. There are too many changes in the wx API which may screw this.

All in all - it might be time consuming to rewrite everything but it will save you the trouble in the long run.

Thank you.
yah_nosh
In need of some credit
In need of some credit
Posts: 8
Joined: Wed Jun 24, 2020 10:17 pm

Re: Combine WinAPI app with wxWidgets

Post by yah_nosh »

So I managed to get it to work, sort of. Below is an updated skeleton:

Code: Select all

// Main WinAPI window procedure
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)    
{
	switch (uMsg)
	{
		// Code to handle other messages
		// ...
	case WM_CLOSE:
		// Closing this window should shut down the app
		PostQuitMessage(0);
		break;
	}
	
	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

class MyApp : public wxApp
{
public:
	virtual bool OnInit() override
	{
		if(!Old_Init())
		{
			// Perform cleanup in case something goes wrong
			Old_Exit();
			return false;
		}
		
		// Wrap the WinAPI window in a dummy wxWindow
		m_dummyMainWindow = new wxWindow();
		m_dummyMainWindow->SetHWND(m_mainWnd);
		
		return true;
	}
	
	int OnExit() override
	{
		// Unset the dummy window HWND and delete it (is this necessary?)
		m_dummyMainWindow->SetHWND(NULL);
		delete m_dummyMainWindow;
		
		// Clean up everything else
		return Old_Exit();
	}
	
private:
	bool Old_Init()
	{
		// Perform the old initialization
		// ...
		m_mainWnd = CreateWindow(/* CreateWindow args... */);
		
		if(m_mainWnd)
		{	
			return true;
		}
		else
		{
			return false;
		}
	}
	
	int Old_Exit()
	{
		// Perform the old cleanup (previously done after exiting the Windows message loop)
		// ...
		return 0;
	}
	
	HWND m_mainWnd;
	wxWindow* m_dummyMainWindow;
};

wxIMPLEMENT_APP_NO_MAIN(MyApp);

// App entrypoint
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, char* lpCmdLine, int nCmdShow)
{
	if (wxEntry())
	{
		// Exit if something goes wrong (this might not be the correct way to do it?)
		wxExit();
	}
    
    return 0;
}
So far this seems to be working without any issues, all the old UI functionality seems unaffected. I can even add a child wxWindow to the dummy window above, and it behaves as expected. My debugger does complain about memory leaks, however, the number of which seems to go up each time I do things like open and close dialogs, so I suspect WinAPI resources aren't getting cleaned up correctly. Am I missing something?

EDIT: I checked the unmodified build of my app in the debugger, and those "memory leaks" exist there as well, so the issue is almost certainly not in wxWidgets. This makes me more confident that the above solution is correct, but I would not mind a second opinion.
OS: Windows 10
Compiler: MSVC v142 (with VS 2019)
wxWidgets version: 3.1.3
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Combine WinAPI app with wxWidgets

Post by doublemax »

I can't tell if this is a good solution or not, this is almost "uncharted territory".

But it's a little different what you initially planned, isn't it? Now you have basically a wxWidgets application with the option to add windows through the plain Win32 API, not vice versa.

I'm a little surprised this works at all. Are you going to move the whole event handling on the Win32 part into "MainWndProc"?
Use the source, Luke!
yah_nosh
In need of some credit
In need of some credit
Posts: 8
Joined: Wed Jun 24, 2020 10:17 pm

Re: Combine WinAPI app with wxWidgets

Post by yah_nosh »

doublemax wrote: Sun Jun 28, 2020 9:41 am But it's a little different what you initially planned, isn't it? Now you have basically a wxWidgets application with the option to add windows through the plain Win32 API, not vice versa.
Indeed, it's not exactly what I had planned, though simply because I assumed I could just insert wxWidgets at any point in the code, without having to mess with the app entrypoint. As long as I can keep all the old UI code working without much trouble, however, any solution works for me. And since I already modified the entrypoint, I'll be one step further ahead by when I start replacing WinAPI entirely.

In retrospect, it makes perfect sense: while debugging, I noticed that the wxWidgets event loop dispatches messages that belong to WinAPI through the standard calls. Win32 is the standard C++ UI option on Windows, so I assume that wxWidgets just acts as a "wrapper" around that API in a Windows app.
doublemax wrote: Sun Jun 28, 2020 9:41 am I'm a little surprised this works at all. Are you going to move the whole event handling on the Win32 part into "MainWndProc"?
That code is just a skeleton for my original application. It was the main window procedure that handled things like the menu bar and paint events. Since I can create the WinAPI window exactly as before, I can just leave the original code alone for now and focus on making specific dialogs in wxWidgets. The only change I made was wrapping the main window in a blank wxWindow so I can add it as a parent to such dialogs.
OS: Windows 10
Compiler: MSVC v142 (with VS 2019)
wxWidgets version: 3.1.3
yah_nosh
In need of some credit
In need of some credit
Posts: 8
Joined: Wed Jun 24, 2020 10:17 pm

Re: Combine WinAPI app with wxWidgets

Post by yah_nosh »

So the solution I posted has been working fine so far, I just had to modify the initialization side by subclassing wxWindow to make sure the message handling worked correctly.

Code: Select all

class MyMainWindow : public wxWindow
{
public:
	virtual WXLRESULT MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) override
	{
		// Pass the messages to the original WinAPI window procedure
		return MainWindowProc(m_hWnd, nMsg, wParam, lParam);
	}
};

// ...

HWND mainWindowHWND;

bool MyWxApp::OnInit()
{
	// Use WinAPI window handle to wrap it in a wxWindow
	MyMainWindow* mainWindow = new MyMainWindow();
	mainWindow->AssociateHandle(mainWindowHWND);
	SetTopWindow(mainWindow);
}
As before, once I created the main window using the CreateWindow WinAPI function, I would then create a MyMainWindow instance and set its HWND to the appropriate handle. All of this seems to work fine, the old behavior of the app did not seem to change, but I ran into an issue with dialogs. Specifically, I subclassed wxDialog and tried to create it using ShowModal in the following manner:

Code: Select all

// Global pointer to the main app window
MyMainWindow* mainWindow;

// ...

// Modal dialog with the main window as its parent
MyDialog myDialog(mainWindow);
myDialog.ShowModal();

// ...
For some reason, this does not work, as the resulting dialog is not actually modal. I can still click outside it and shift focus onto the main window. What's confusing is that things like wxMessageBox do not have this problem at all, the dialogs they create block any input to all other windows, exactly as intended.

What am I missing? I tried digging around, apparently this issue does pop up every now and then, but there doesn't seem to be a consistent solution.
OS: Windows 10
Compiler: MSVC v142 (with VS 2019)
wxWidgets version: 3.1.3
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Combine WinAPI app with wxWidgets

Post by doublemax »

For some reason, this does not work, as the resulting dialog is not actually modal. I can still click outside it and shift focus onto the main window. What's confusing is that things like wxMessageBox do not have this problem at all, the dialogs they create block any input to all other windows, exactly as intended.
I'm not 100% sure, but here's my theory: wxMessageBox etc. uses native a native Windows messagebox under the hood, so Windows handles the disabling of other windows. If you have a wxDialog, wxWidgets does not know about any windows outside the wx domain, so it can't disable them.
Use the source, Luke!
yah_nosh
In need of some credit
In need of some credit
Posts: 8
Joined: Wed Jun 24, 2020 10:17 pm

Re: Combine WinAPI app with wxWidgets

Post by yah_nosh »

I managed to implement a somewhat hacky fix for it, by creating dialogs with the top level WinAPI window as the parent, and while the dialog is open, I disable the child window (thus it guarantees that both the main app window and the child wxWindow is blocked by a modal dialog).

Meanwhile, a different problem came up: my latest approach was to wrap the WinAPI main window in a wxNativeContainerWindow, which seemed to offer the cleanest way to just pass it a HWND and wrap it in a wxWindow (pseudocode below)

Code: Select all

wxNativeContainerWindow* mainWindow = new wxNativeContainerWindow(mainWindowHWND);
wxTheApp->SetTopWindow(mainWindow);
This seems to work without problems, except for one thing: resizing the window does not update the WinAPI window that is wrapped by the wxNativeContainerWindow. So the frame and the buttons in the top right corner are properly resized, but the actual contents of the window do not change (resizing beyond its size at initialization will just create a black void beyond the edges). After doing some debugging, I found that WM_SIZE messages aren't propagated to the Win32 window procedure (where it would reorganize the children according to the new size), but are instead handled in "wxWindowMSW::MSWHandleMessage", which only seems to resize the frame.

Is there a way to alter this behavior? Previously I managed to do it by inheriting from wxWindow and overriding its "MSWWindowProc" function to send everything to the Win32 window procedure, but this seems like overkill. I would assume wxNativeContainerWindow would know to send resize events to its contents, so I think I'm missing something.
OS: Windows 10
Compiler: MSVC v142 (with VS 2019)
wxWidgets version: 3.1.3
Post Reply