【问题标题】:Port Win32/WinAPI application to wxWidgets将 Win32/WinAPI 应用程序移植到 wxWidgets
【发布时间】:2020-10-17 15:17:48
【问题描述】:

我目前使用的是 Windows 10、MSVC v142(带有 VS2019)和 wxWidgets 3.1.3。我有一个旧的 Windows C++ 应用程序,它使用 WinAPI 作为其 GUI 功能,即 Windows 消息循环,使用“CreateWindow”,并且必须“手动”创建所有窗口过程和事件处理。

我想通过逐步使用 wxWidgets 替换 UI 来改进这个应用程序,这样我就不必从头开始。我会在 wxWidgets 中实现新的、独立的 UI 功能(例如特定对话框),然后返回并用 wxWidgets 实现替换所有旧的 UI 代码,而不必一路中断应用程序。

下面是我的应用程序当前设置的框架:

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;
}

我需要如何修改它以使所有 WinAPI 对象继续运行,但我现在也可以使用 wxWidgets 创建窗口?我之前尝试通过从wxApp 派生的自定义类初始化 wxWidgets 来替换上面的消息循环,但我的应用程序在清理代码期间不断崩溃(有时甚至无法到达)。

编辑:我设法让它工作,更新的骨架可以在下面找到。

// 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;
}

这似乎确实有效,所有旧的 UI 元素都像以前一样工作,但我不能 100% 确定这是一个稳定的解决方案。我的调试器警告我内存泄漏,当我激活 UI 元素(例如打开和关闭对话框)时,内存泄漏的数量似乎增加了。我怀疑 WinAPI 资源可能没有被正确清理。我错过了什么吗?

EDIT2:我做了更多的调试,我的原始应用程序中的代码(即没有 wxWidgets)也会导致那些“内存泄漏”,我无法在最小的工作示例中复制它,所以我怀疑这个问题根本与 wxWidgets 无关。因此,我相信上述方法应该可以解决我的问题,但我不介意第二意见。

【问题讨论】:

  • 计划是什么 - 多平台应用程序?如果不是,Win32 比 wxWidgets 好。
  • @i486 我想要一个对 OOP 更友好并且有不错的 RAD 工具的框架。 Qt 是我最初的选择,考虑到我的使用经验,但没有办法可靠地设置它,而无需基本上重写整个应用程序。
  • Win32 唯一的缺点是控件数量有限。 OOP 会使事情变得复杂而没有显着的好处。你可以试试Ultimate++
  • @i486 Win32 确实可以很强大,但它会占用我所有的时间来设置它足够灵活,而且缺乏 RAD 工具使情况变得更糟。我真的不想滥用 OOP,我的意思是我不想花时间重新发明轮子。如果有人实现了我需要的所有基本 UI 工具,并为它们提供了一个方便的 OOP 包装器,这样我就可以实例化对象,那就足够了。

标签: c++ user-interface winapi wxwidgets


【解决方案1】:

我建议查看MFC sample 以查看与您想要做的事情接近的工作示例。当然,MFC 与 Win32 API 不太一样,但它仍然应该是一个很好的起点。

值得注意的是,它显示了如何在运行自己的事件循环时将消息传递给 wx 事件循环(这部分在 include/wx/msw/mfc.hwxMFCApp 类中)。当然,如果可以切换到运行 wx 事件循环,那就更简单了。

知道您可以使用AssociateHandle() 将您创建的HWNDs 包装到wxWindows 中并通过覆盖MSWHandleMessage() 甚至直接使用wxNativeWindow 来处理他们的消息,您可能还会发现这很有帮助。

祝你好运!

【讨论】:

  • 感谢您的回复,我在最近的一些实验后更新了我的帖子。它似乎可以在最小的应用程序中正常工作,但是如果我有更复杂和动态的 UI(例如,按需创建窗口),我还没有测试它的行为。
  • 附录:我设法制作了一个工作应用程序,但遇到了一些问题。具体来说,即使我按照您的建议将主 Win32 窗口包装在 wxWindow 中,如果我尝试创建模态 wxWidgets 对话框,它们也会拒绝表现得像模态对话框。我仍然可以单击主窗口,从而改变焦点,甚至关闭主窗口(这会使应用程序处于无效状态)。我错过了什么?我也尝试过使用wxNativeWindow,但这需要一个父级,目前我的顶级窗口在 Win32 中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-27
  • 1970-01-01
  • 1970-01-01
  • 2011-10-10
  • 1970-01-01
  • 1970-01-01
  • 2017-05-21
相关资源
最近更新 更多