嗯,本座又来了(咦?为什么说又?)。谁炼了本座推荐的《葵花宝典》[《Windows 核心编程》]的举手。我数数,0,……真是失望啊,一个都没有,还是有人偷偷练了却不说(嘿嘿,某人奸笑中)。
算了,开始讲讲Windows消息。我们知道Windows的用户界面是由消息驱动的,比如你点击了某个按钮,那么这个按钮的主窗口将接收到一个鼠标消息,然后将鼠标消息翻译成按钮单击消息。
那么我们怎么知道在哪里处理这些消息呢?不要急,听我慢慢来吹(住手,说好不打脸的……)。
通常每一个句柄都属于某个窗口注册类,我们用VC的工具Spy++就可以看见,在该类的注册信息里有一个该类的默认处理过程。在RegisterClass 的传入参数里描述的很清楚的。
ATOM RegisterClass( CONST WNDCLASS
*
lpWndClass);
typedef struct {
UINT style;
WNDPROC lpfnWndProc;
//
这里就是默认注册的类处理过程
int
cbClsExtra;
int
cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
} WNDCLASS,
*
PWNDCLASS;
但是很多时候,我们要的是自己处理该类的动作,比如Button类,我们要自己绘制它的样子,就像开始菜单按钮。
我们在Spy++里可以看见开始菜单按钮的处理过程后面有个描述Subclassed(中文译:子类化)。通常是我们用自己的处理过程替换了默认的处理过程。通常我们用SetWindowLong来改变处理过程。
m_lpfnOldWndProc = (WNDPROC) SetWindowLong(pThis->m_hAttachWnd,GWL_WNDPROC,(LONG)NewDefaultProc);
我们先来看看一个默认的SDK是怎样建立窗口的。
#include <windows.h>
// Global variable
HINSTANCE hinst;
// Function prototypes.
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
InitApplication(HINSTANCE);
InitInstance(HINSTANCE, int);
LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);
// Application entry point.
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
//注册窗口类
if (!InitApplication(hinstance))
return FALSE;
//创建窗口
if (!InitInstance(hinstance, nCmdShow))
return FALSE;
BOOL fGotMessage;
while ((fGotMessage = GetMessage(&msg, (HWND) NULL, 0, 0)) != 0 && fGotMessage != -1)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
UNREFERENCED_PARAMETER(lpCmdLine);
}
BOOL InitApplication(HINSTANCE hinstance)
{
WNDCLASSEX wcx;
// Fill in the window class structure with parameters
// that describe the main window.
wcx.cbSize = sizeof(wcx); // size of structure
wcx.style = CS_HREDRAW |
CS_VREDRAW; // redraw if size changes
wcx.lpfnWndProc = MainWndProc; // points to window procedure
wcx.cbClsExtra = 0; // no extra class memory
wcx.cbWndExtra = 0; // no extra window memory
wcx.hInstance = hinstance; // handle to instance
wcx.hIcon = LoadIcon(NULL,
IDI_APPLICATION); // predefined app. icon
wcx.hCursor = LoadCursor(NULL,
IDC_ARROW); // predefined arrow
wcx.hbrBackground = GetStockObject(
WHITE_BRUSH); // white background brush
wcx.lpszMenuName = "MainMenu"; // name of menu resource
wcx.lpszClassName = "MainWClass"; // name of window class
wcx.hIconSm = LoadImage(hinstance, // small class icon
MAKEINTRESOURCE(5),
IMAGE_ICON,
GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON),
LR_DEFAULTCOLOR);
// Register the window class.
return RegisterClassEx(&wcx);
}
BOOL InitInstance(HINSTANCE hinstance, int nCmdShow)
{
HWND hwnd;
// Save the application-instance handle.
hinst = hinstance;
// Create the main window.
hwnd = CreateWindow(
"MainWClass", // name of window class
"Sample", // title-bar string
WS_OVERLAPPEDWINDOW, // top-level window
CW_USEDEFAULT, // default horizontal position
CW_USEDEFAULT, // default vertical position
CW_USEDEFAULT, // default width
CW_USEDEFAULT, // default height
(HWND) NULL, // no owner window
(HMENU) NULL, // use class menu
hinstance, // handle to application instance
(LPVOID) NULL); // no window-creation data
if (!hwnd)
return FALSE;
// Show the window and send a WM_PAINT message to the window
// procedure.
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
return TRUE;
}
LRESULT CALLBACK MainWndProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam) // second message parameter
{
//这里响应窗口消息
switch (uMsg)
{
case WM_CREATE:
// Initialize the window.
return 0;
case WM_PAINT:
// Paint the window's client area.
return 0;
case WM_SIZE:
// Set the size and position of the window.
return 0;
case WM_DESTROY:
// Clean up window-specific data objects.
return 0;
//
// Process other messages.
//
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
我们看到通常建立了窗口后,还要建立消息循环才能响应消息。
我们来总结一下如何创建窗口的几中方式(对话框是一种特殊的窗口):
使用Windows默认提供的类创建窗口(CreateWindow[Ex]、CreateDialog[Ex]等)
定义处理过程的窗口(RegisterClass后再CreateWindow)
子类化处理窗口(CreateWindow后SubClassWindow)
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
if (!InitApplication(hinstance))
return FALSE;
if (!InitInstance(hinstance, nCmdShow))
return FALSE;
//这里建立消息循环
BOOL fGotMessage;
while ((fGotMessage = GetMessage(&msg, (HWND) NULL, 0, 0)) != 0 && fGotMessage != -1)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
这样窗口就能响应消息了,可以等待Windows的消息通知,我们的消息通知,和其他用户的消息通知。至于怎么通知下次再说喽。(某人从背后抽出一把剑,然后大喝一声:御剑飞仙,然后往下一跳。不一会儿传来凄厉的惨叫~ 。小朋友切勿模仿)