关于Windows窗口的一些疑惑

这里先介绍几个概念

  • 窗口

  • 消息循环

  • 线程

窗口

      本文所指的是通过继承CWindowImpl并调用Create所创建的同类型窗口。 窗口视为一个编程构造,该构造可以:

  • 占据屏幕的特定部分。
  • 在给定时刻可能可见,也可能不可见。
  • 知道如何绘制自身。
  • 响应来自用户或操作系统的事件。

消息循环

      它是一个无限循环,不断地从操作系统获取消息并将其分发到窗口过程(Window Procedure)来进行处理。

      在 Windows 操作系统中,每个窗口都有一个窗口过程,也称为消息处理函数。窗口过程是一个回调函数,用于处理与特定窗口相关的消息,例如鼠标点击、键盘输入、窗口绘制等。

      消息循环的主要目的是从系统消息队列中获取消息,然后将消息分发给相应的窗口过程进行处理。这样,当用户进行交互操作或发生系统事件时,消息循环能够及时响应并触发相应的处理逻辑。其形如

MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
    TranslateMessage(&msg); // 翻译消息(例如将键盘消息转换为字符消息)
    DispatchMessage(&msg);  // 分发消息到窗口过程进行处理
}

线程

      线程我相信大家都不陌生,这里就简单概况。线程是计算机中执行程序的最小单位。在操作系统中,每个进程可以包含多个线程,每个线程都可以独立执行一段程序代码。

疑惑

      大家肯定会问,为什么我要单独把上面几个概念拿出来,我们都知道,窗口是事件驱动,依赖于其创建线程的消息循环。下面我们就来讲讲他们之间的关系,也是这次文章的核心,关于Windows窗口的一些疑惑

  • 没有消息循环创建窗口
  • 消息循环和窗口不在同一个线程创建
  • 窗口资源什么时候销毁比较合适

下面我们详细聊一聊

没有消息循环创建窗口

      以下为示例程序: 

#include <windows.h>

// 窗口过程
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
  switch (msg) {
    case WM_CREATE:
      break;
    case WM_CLOSE:
      DestroyWindow(hwnd);
      break;
    case WM_DESTROY:
      PostQuitMessage(0);
      break;
    default:
      return DefWindowProc(hwnd, msg, wParam, lParam);
  }
  return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow) {
  // 定义窗口类
  WNDCLASSEX wc;
  ZeroMemory(&wc, sizeof(wc));
  wc.cbSize = sizeof(wc);
  wc.lpfnWndProc = WndProc;
  wc.hInstance = hInstance;
  wc.lpszClassName = L"WindowClass";
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

  // 注册窗口类
  if (!RegisterClassEx(&wc)) {
    MessageBox(NULL, L"窗口类注册失败!", L"错误", MB_ICONERROR);
    return 0;
  }

  // 创建窗口
  HWND hwnd = CreateWindowEx(0, L"WindowClass", L"我的窗口",
                             WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
                             500, 400, NULL, NULL, hInstance, NULL);
  if (!hwnd) {
    int err = GetLastError();
    MessageBox(NULL, L"窗口创建失败!", L"错误", MB_ICONERROR);
    return 0;
  }

  // 显示窗口
  ShowWindow(hwnd, nCmdShow);
  UpdateWindow(hwnd);


  PostMessage(hwnd, WM_CLOSE, 0, 0);
  return 0;
}

我们将上诉代码运行后发现程序能处理WM_CREATE消息、创建的窗口一闪而过并且快速退出。还有就是WndProc并不能接收到程序退出前发送的WM_CLOSE消息。由此可以看出,此窗口非正常销毁和退出。如果WndProc还在执行某些操作,可能会有不确定的行为(WndProc不一定只是一个窗口处理函数,也有可能是一个窗口类等)

消息循环和窗口不在同一个线程创建

      以下为示例程序

#include <windows.h>

#include <thread>

// 窗口过程
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
  switch (msg) {
    case WM_CREATE:
      break;
    case WM_CLOSE:
      DestroyWindow(hwnd);
      break;
    case WM_DESTROY:
      PostQuitMessage(0);
      break;
    default:
      return DefWindowProc(hwnd, msg, wParam, lParam);
  }
  return 0;
}

void Create(HINSTANCE hInstance) {
  // 子线程创建窗口
  WNDCLASSEX wc;
  ZeroMemory(&wc, sizeof(wc));
  wc.cbSize = sizeof(wc);
  wc.lpfnWndProc = WndProc;
  wc.hInstance = hInstance;
  wc.lpszClassName = L"WindowClass";
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

  // 注册窗口类
  if (!RegisterClassEx(&wc)) {
    MessageBox(NULL, L"窗口类注册失败!", L"错误", MB_ICONERROR);
    return;
  }

  // 创建窗口
  HWND hwnd = CreateWindowEx(0, L"WindowClass", L"我的窗口",
                             WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
                             500, 400, NULL, NULL, hInstance, NULL);
  if (!hwnd) {
    int err = GetLastError();
    MessageBox(NULL, L"窗口创建失败!", L"错误", MB_ICONERROR);
    return;
  }

  // 显示窗口
  ShowWindow(hwnd, TRUE);
  UpdateWindow(hwnd);

  PostMessage(hwnd, WM_CLOSE, 0, 0);
  return;
};

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow) {

  std::thread thread(Create, hInstance);
  // 消息循环
  MSG msg;
  while (GetMessage(&msg, NULL, 0, 0)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  thread.join();
  return 0;
}

上诉程序通过在新的线程调用Create创建窗口,并在主线程开启消息循环,但我们运行后发现,WndProc依然只能收到WM_CREATE消息,不能处理WM_CLOSE消息,窗口一闪而过并销毁程序不退出

窗口资源什么时候销毁比较合适

      这里我们可以参考微软的官方文档。官方文档

总结

      窗口和消息循环是一对孪生兄弟,缺一不可,并且他们只有在同一个线程创建才能更好的工作。

      学无止境,知其然知其所以然。

      

      

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值