I'm currently using Windows 10, MSVC v142 (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 work my way back and replace all the old UI code with a wxWidgets implementation, without having to break the app along the way.
Below is a skeleton of how my app is currently set up:
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// Handling window messages, e.g menus, buttons, etc.
}
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, char* lpCmdLine, int nCmdShow)
{
// Initialize resources, register main window class using MainWndProc, etc.
// ...
HWND mainwnd = CreateWindow(/* CreateWindow args... */);
do
{
MSG msg = {};
while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Per-frame application logic
// ...
} while (msg.message != WM_QUIT);
// Clean up resources
// ...
return 0;
}
How would I need to modify this so that all the WinAPI objects continue to function, but I can now also create windows using wxWidgets? I previously tried replacing the message loop above by initializing wxWidgets through a custom class derived from wxApp, but my application kept crashing during the cleanup code (which it sometimes wouldn't even reach).
EDIT: I managed to make it work, updated skeleton can be found below.
// 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;
}
This does seem to function, all the old UI elements are working as before, but I am not 100% sure this is a stable solution. My debugger is warning me about memory leaks, which appear to increase in number when I activate UI elements (e.g open and close dialogs). I suspect WinAPI resources might not be cleaned up correctly. Am I missing anything?
EDIT2: I did some more debugging, and the code in my original app (i.e without wxWidgets) causes those "memory leaks" as well, and I can't replicate it in a minimal working example, so I suspect the issue is not related to wxWidgets at all. I am therefore confident that the approach described above should solve my problem, but I would not mind a second opinion.
Ultimate++. - i486