【LibUIDK界面庫系列文章】窗口與消息



LibUIDK界面庫原創文章


窗口的創建


一個簡單的win32程序如下(假設工程名爲“HelloWin32”,下面的代碼是使用vc6.0創建一個名爲HelloWin32的“Win32 Application”,並且選擇“A typical "Hello World" application”後創建的代碼精簡後得到):

// HelloWin32.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "resource.h"


LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
 // Register class.
 static TCHAR szClassName[] = _T("HelloWin32");
 WNDCLASSEX wcex;
 
 wcex.cbSize = sizeof(WNDCLASSEX);
 
 wcex.style   = CS_HREDRAW | CS_VREDRAW;
 wcex.lpfnWndProc = (WNDPROC)WndProc;
 wcex.cbClsExtra  = 0;
 wcex.cbWndExtra  = 0;
 wcex.hInstance  = hInstance;
 wcex.hIcon   = LoadIcon(hInstance, (LPCTSTR)IDI_HELLOWIN32);
 wcex.hCursor  = LoadCursor(NULL, IDC_ARROW);
 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
 wcex.lpszMenuName = (LPCSTR)IDC_HELLOWIN32;
 wcex.lpszClassName = szClassName;
 wcex.hIconSm  = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);
 
 RegisterClassEx(&wcex);
 
 // Perform application initialization:
 HWND hWnd = CreateWindow(szClassName, _T("The hello win32 app"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
 
 if (hWnd == NULL)
 {
  return 0;
 }
 
 ShowWindow(hWnd, nCmdShow);
 UpdateWindow(hWnd);
 
 HACCEL hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_HELLOWIN32);
 
 // Main message loop:
 MSG msg;
 while (GetMessage(&msg, NULL, 0, 0))
 {
  if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
  {
   TranslateMessage(&msg);
   DispatchMessage(&msg);
  }
 }
 
 return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
 PAINTSTRUCT ps;
 HDC hdc;
 static TCHAR szHello[] = _T("Hello win32");
 
 switch (message)
 {
 case WM_CREATE:
  break;

 case WM_PAINT:
  hdc = BeginPaint(hWnd, &ps);
  // TODO: Add any drawing code here...
  RECT rt;
  GetClientRect(hWnd, &rt);
  DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);
  EndPaint(hWnd, &ps);
  break;
  
 case WM_DESTROY:
  PostQuitMessage(0);
  break;
  
 default:
  return DefWindowProc(hWnd, message, wParam, lParam);
 }
 
 return 0;
}

我們單步調試上面的代碼,在WndProc的WM_PAINT消息中加個斷點,在執行UpdateWindow(hWnd)這行代碼時,我們就會發現會進入到WM_PAINT中,但這時,還沒有進入下面的消息循環。我們知道,消息循環是從消息隊列中取消息,既然沒有執行消息循環前就可以處理WM_PAINT消息,說明這個WM_PAINT消息是直接發往窗口、而沒有進入消息隊列的。這句話的意思是:消息循環會調用WndProc,但並不是只有消息循環可以調用WndPorc,windows本身也可以調用。我們可以想像一下CreateWindow的僞代碼:


HWND CreateWindow(...)
{
 HWND hWnd = NULL;
 // 分配窗口內存,生成窗口
 hWnd = ...;

 CREATESTRUCT cs;
 // 初始化cs.
 WndProc(hWnd, WM_CREATE, 0, LPARAM(&cs));  // CreateWindow內部不經消息循環,直接調用WndProc

 // 執行其它操作

 return hWnd;
}


DispatchMessage的僞代碼可能如下:
LRESULT DispatchMessage(const MSG *lpmsg)
{
 // 執行其它操作

 WndProc(lpmsg->hwnd, lpmsg->message, lpmsg->wParam, lpmsg->lParam);

 // 執行其它操作

 return 0;
}


其實,早在CreateWindow時,Windows就調用WndProc並傳遞了WM_CREATE消息。WM_CREATE是程序收到的第一個消息。類似的,ShowWindow可能會觸發WM_SIZE和WM_SHOWWINDOW消息。


被投遞到消息隊列中的消息叫“隊列消息”,不在隊列中的爲“非隊列消息”。一個消息是隊列消息還是非隊列消息並不是絕對的,比如WM_PAINT可以是隊列消息(窗口顯示之後調用InvalidateRect,就會往消息隊列中增加一條WM_PAINT消息),也可以是非隊列消息(上面UpdateWindow發出的那個),對於程序員,大部分時間,不用關心某個消息是隊列消息,還是非隊列消息,你只需知道它們都最終是交給WndProc窗口過程、以同步方式 處理的就可以了。


在CreateWindow時,指定窗口初始位置和尺寸時,CW_USEDEFAULT參數表示使用默認值,默認值意味着:Windows會將連續新建的窗口的左上角位置沿水平方向和垂直方向分別作步長爲1的偏移。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章