0
votes

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.

1
What is the plan - multiplatform app? If not, Win32 is better than wxWidgets. - i486
@i486 I wanted a framework that is more friendly to OOP and has decent RAD tools. Qt was my initial choice, given my experience with it, but there was no way to reliably set it up without basically having to rewrite the whole app. - yah_nosh
The only negative side of Win32 is the limited number of controls. OOP will make things complex without significant benefit. You may try Ultimate++. - i486
@i486 Win32 can indeed be really powerful, but it would consume all of my time just setting it up to be flexible enough, and the lack of RAD tools makes it worse. I don't really want to abuse OOP, all I mean is that I don't want to spend time reinventing the wheel. If someone implemented all the basic UI tools I would need and gave them all a convenient OOP wrapper so I can just instantiate objects, that's more than enough. - yah_nosh

1 Answers

1
votes

I recommend looking at the MFC sample to see a working example of something close to what you want to do. MFC is, of course, not quite the same as Win32 API, but it should still be a good starting point.

Notably it shows how to pass the messages to wx event loop when you're running your own one (this part is in wxMFCApp class in include/wx/msw/mfc.h). Of course, if you can switch to running wx event loop, it would be even simpler.

You may also find it helpful to know that you can wrapp HWNDs you create into wxWindows using AssociateHandle() and handle their messages by overriding MSWHandleMessage() or even just using wxNativeWindow directly.

Good luck!