Windows 销毁窗口资源的正确方法

Windows 销毁窗口资源的正确方法,windows,winapi,resources,Windows,Winapi,Resources,这是我基于win32 api的代码摘录: LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { return DefWindowProc(hwnd, uMsg, wParam, lParam); } void __fastcall TMyThread::Execute(void) { WNDCLASSEX wc = {0}; wc.cbSize

这是我基于win32 api的代码摘录:

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
   return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

void __fastcall TMyThread::Execute(void)
{
   WNDCLASSEX wc     = {0};
   wc.cbSize         = sizeof(WNDCLASSEX);
   wc.lpfnWndProc    = WindowProc;
   wc.hInstance      = GetModuleHandle(NULL);
   wc.lpszClassName  = class_name.c_str();

   if (!RegisterClassEx(&wc))
   {
      MessageBox(NULL, L"Window Registration Failed!", L"Error", MB_ICONEXCLAMATION | MB_OK);
      return;
   }

   hwnd = CreateWindowEx(0, class_name.c_str(), NULL, 0, 0, 0, 100, 100, HWND_MESSAGE, NULL, wc.hInstance, NULL);
   if (hwnd == NULL)
   {
      MessageBox(NULL, L"Window Creation Failed!", L"Error", MB_ICONEXCLAMATION | MB_OK);
      return;
   }

   MSG msg;
   BOOL ret;
   while ((ret = GetMessage(&msg, 0, 0, 0)) != 0)
   {
      if (ret != -1)
      {
         TranslateMessage(&msg);
         DispatchMessage(&msg);
      }
   }
}
我的问题是:

  • 退出消息循环后是否需要进行一些资源清理(例如CloseHandle)?我经常看到代码示例中没有这样的东西。对吗

  • 新创建的窗口能否在第一次调用GetMessage函数之前将消息接收到线程队列中(我们假设窗口已成功创建,即函数CreateWindowEx返回时没有错误)


  • 请记住,TMyThread不是应用程序的主线程。因此,它可以在一次应用程序运行中多次创建和销毁。请不要注意非常简化的窗口创建。此特定窗口不应在屏幕上可见。它的创建完全是为了从另一个应用程序接收消息。调用CreateWindowEx函数时,使用hWndParent参数的HWND_消息值来突出显示这一点。

    在Win32中使用资源时必须非常小心。确保您已经仔细阅读了文档,以确定Windows本身将为您卸载什么以及您必须自己卸载什么

    例如,
    HWND
    s将被销毁,因为父级
    HWND
    s将被销毁

    最好的方法是尝试卸载您个人创建的所有内容。如果在卸载时从特定函数返回错误,则可能不应该卸载它,因为它已由Windows或某些相关资源卸载

    重要的是不要在不必要的时候卸载物品,因为这可能会导致崩溃

    因此,例如,使用直接来自资源的窗口创建的图标可能不应卸载。但是,您创建的绘制到其他窗口的
    HBITMAP
    应该被卸载


    您的部分问题可以通过使用断点的快速测试来确定。我一点也不知道。

    在Win32中使用资源时必须非常小心。确保您已经仔细阅读了文档,以确定Windows本身将为您卸载什么以及您必须自己卸载什么

    例如,
    HWND
    s将被销毁,因为父级
    HWND
    s将被销毁

    最好的方法是尝试卸载您个人创建的所有内容。如果在卸载时从特定函数返回错误,则可能不应该卸载它,因为它已由Windows或某些相关资源卸载

    重要的是不要在不必要的时候卸载物品,因为这可能会导致崩溃

    因此,例如,使用直接来自资源的窗口创建的图标可能不应卸载。但是,您创建的绘制到其他窗口的
    HBITMAP
    应该被卸载


    您的部分问题可以通过使用断点的快速测试来确定。我不知道该怎么办。

    通过在上面的示例中一次又一次地创建、运行和销毁线程,我发现在退出消息循环后需要调用两个方法。第一个是DestroyWindow,第二个是UnregisterClass。在常规应用程序中,在用户确认确实要关闭应用程序后,WM_CLOSE handler中的Destroy Window函数将被销毁。DestroyWindow函数然后向window发送WM_DESTROY和WM_NCDESTROY消息。为了响应WM_销毁消息,应用程序应调用PostQuitMessage(0)函数,该函数将立即退出消息循环。因此,这部分代码在所有场景中都不是必需的。我需要显式调用DestroyWindow函数,因为我通过简单地发送WM_QUIT消息来退出消息循环。当不这样做时,有时我在尝试注销窗口类时收到错误1412(错误\u类\u有\u窗口)

    if (hwnd != NULL)
    {
       ret = DestroyWindow(hwnd);
       if (ret == 0)
       {
          str.printf(L"Window destroying failed (GetLastError = %d)!", GetLastError());
          ShowError(str);
       }
       hwnd = NULL;
    }
    
    ret = UnregisterClass(class_name.c_str(), wc.hInstance);
    if (ret == 0)
    {
       str.printf(L"Window class unregistration failed (GetLastError = %d)!", GetLastError());
       ShowError(str);
    }
    

    通过在上述示例中一次又一次地创建、运行和销毁线程,我发现在退出消息循环后需要调用两个方法。第一个是DestroyWindow,第二个是UnregisterClass。在常规应用程序中,在用户确认确实要关闭应用程序后,WM_CLOSE handler中的Destroy Window函数将被销毁。DestroyWindow函数然后向window发送WM_DESTROY和WM_NCDESTROY消息。为了响应WM_销毁消息,应用程序应调用PostQuitMessage(0)函数,该函数将立即退出消息循环。因此,这部分代码在所有场景中都不是必需的。我需要显式调用DestroyWindow函数,因为我通过简单地发送WM_QUIT消息来退出消息循环。当不这样做时,有时我在尝试注销窗口类时收到错误1412(错误\u类\u有\u窗口)

    if (hwnd != NULL)
    {
       ret = DestroyWindow(hwnd);
       if (ret == 0)
       {
          str.printf(L"Window destroying failed (GetLastError = %d)!", GetLastError());
          ShowError(str);
       }
       hwnd = NULL;
    }
    
    ret = UnregisterClass(class_name.c_str(), wc.hInstance);
    if (ret == 0)
    {
       str.printf(L"Window class unregistration failed (GetLastError = %d)!", GetLastError());
       ShowError(str);
    }
    

    由于您创建窗口的唯一目的似乎是在线程上接收消息,因此我应该指出,这并不是严格必要的。任何线程都可以使用(限制适用)接收发送到它的消息。@IInspectable:我考虑过这个选项。这很复杂,因为其他应用程序在向线程发布消息时必须知道线程ID。另一方面,在向窗口发布消息时,我可以通过FindWindow函数找到它,该函数方便且非常可靠(我使用窗口类名作为唯一ID)。由于您创建窗口的唯一目的似乎是在线程上接收消息,我应该指出,这并不是严格必要的。任何线程都可以使用(限制适用)接收发送到它的消息。@IInspectable:我考虑过这个选项。这很复杂,因为其他应用程序在向线程发布消息时必须知道线程ID。另一方面,在向窗口发布消息时,我可以通过FindWindow函数找到消息,该函数方便且非常可靠(我使用窗口类名作为唯一ID)。