GUI应用程序的入口函数是 WinMain , 这是一个自定义的入口函数。WinMain函数采用的是windows标准调用方式。下面是简单的windows程序:
#include <windows.h> int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR lpCmdLine, int nCmdShow) { ::MessageBox(nullptr, TEXT("Hello, Win32 Application"), TEXT("First Win32 Window"), MB_OK); return 0; }
系统传给WinMain函数几个参数定义如下:
hInsatnce = (HINSTANCE)GetModuleHandle(nullptr);
GetModuleHandle 函数的唯一参数是模块名称,函数返回这个模块句柄。如果传递nullptr的话函数返回可执行文件所在模块的模块句柄。
当Windows向程序发送消息时,他调用程序的一个函数,这个函数的参数精确的描述了Windows发送的消息。在程序中称为窗口函数或消息处理函数。它是一个自定义的回掉函数,原型如下:
下面是一个简单示例
#include <windows.h> int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd = ::FindWindow(nullptr, TEXT("无标题 - 记事本")); if (hWnd != nullptr) ::SendMessage(hWnd, WM_CLOSE, 0, 0); return 0; }
FindWindow 函数会查找窗口类名和窗口标题与指定字符串相匹配的窗口。返回找到的窗口句柄; SendMessage 用于向窗口中发送消息。
下面是创建一个窗口的完整代码
#include <windows.h> #include <iostream> LRESULT CALLBACK MainWindProc(HWND, UINT, WPARAM, LPARAM); int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wndClass; wndClass.cbSize = sizeof(wndClass); wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = MainWindProc; wndClass.cbClsExtra = 0; wndClass.cbWndExtra = 0; wndClass.hInstance = hInstance; wndClass.hIcon = ::LoadIcon(nullptr, IDI_APPLICATION); wndClass.hCursor = ::LoadCursor(nullptr, IDC_ARROW); wndClass.hbrBackground = (HBRUSH)::GetStockObject(WHITE_BRUSH); wndClass.lpszMenuName = nullptr; wndClass.lpszClassName = TEXT("MainWClass"); wndClass.hIconSm = nullptr; // 注册窗口类 ::RegisterClassEx(&wndClass); // 创建窗口 HWND hwnd = ::CreateWindowEx(\ 0, \ TEXT("MainWClass"), \ TEXT("My First Window"), \ WS_OVERLAPPEDWINDOW, \ CW_USEDEFAULT, \ CW_USEDEFAULT, \ CW_USEDEFAULT, \ CW_USEDEFAULT, \ nullptr, \ nullptr, \ hInstance, \ nullptr); if (hwnd == nullptr) { std::cout << "Create Window Error!" << std::endl; return -1; } // 显示窗口 ::ShowWindow(hwnd, nCmdShow); // 刷新窗口客户区 ::UpdateWindow(hwnd); MSG msg; while (::GetMessage(&msg, nullptr, 0, 0)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } return msg.wParam; } LRESULT CALLBACK MainWindProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = ::BeginPaint(hwnd, &ps); std::wstring str = L"This is Test Text!"; ::TextOut(hdc, 0, 0, str.c_str(), str.size()); ::EndPaint(hwnd, &ps); return 0; } case WM_DESTROY: ::PostQuitMessage(0); return 0; } return ::DefWindowProc(hwnd, message, wParam, lParam); }
上面的代码是创建一个窗口程序的一般步骤,具体流程如下:
注册窗口类使用函数RegisterClassEx,一个窗口类定义了窗口的一些主要属性,如图标、光标、背景色和负责处理消息的窗口函数等。这些属性定义在WNDCLASSEX结构中。
typedef struct _WNDCLASSEX{ UINT cbSize; // WNDCLASSEX结构的大小 UINT style; // 从窗口派生的窗口具有的风格 WNDPROC lpfnWndProc; // 消息处理函数指针 int cbClsExtra; // 指定紧跟在窗口类结构后的附加字节数 int cbWndExtra; // 指定紧跟在窗口示例后的附加字节数 HINSTANCE hInstance; // 本模块的实例句柄 HICON hIcon; // 窗口左上角图标的句柄 HCURSOR hCursor; // 光标的句柄 HBRUSH hbrBackground; // 背景画刷的句柄 LPCSTR lpszMenuName; // 菜单名 LPCSTR lpszClassName; // 该窗口类的名称 HICON hIconSm; // 小图标句柄 }WNDCLASSEX;
wndClass.style = CS_HREDRAW | CS_VREDRAW; // 指定如果大小改变就重画
前缀CS_意为class style,在WINUSER.H中定义了全部可选样式。
#define CS_VREDRAW 0x0001 #define CS_HREDRAW 0x0002 #define CS_DBLCLKS 0x0008 #define CS_OWNDC 0x0020 #define CS_CLASSDC 0x0040 #define CS_PARENTDC 0x0080 #define CS_NOCLOSE 0x0200 #define CS_SAVEBITS 0x0800 #define CS_BYTEALIGNCLIENT 0x1000 #define CS_BYTEALIGNWINDOW 0x2000 #define CS_GLOBALCLASS 0x4000
wndClass.lpfnWndProc = MainWindProc;
WNDCLASSEX 结构成员lpfnWndProc 指定了基于此类窗口的窗口函数。当窗口收到消息时Windows即自动调用这个函数通知应用程序。
wndClass.hInstance = hInstance;
wndClass.hIcon = ::LoadIcon(nullptr, IDI_APPLICATION); wndClass.hCursor = ::LoadCursor(nullptr, IDC_ARROW);
LoadIcon 函数装载了一个预定义图标,命名为 IDI_APPLICATION
LoadCursor 函数装载了一个预定义的光标,命名为 IDC_ARROW
wndClass.hbrBackground = (HBRUSH)::GetStockObject(WHITE_BRUSH); // 使用白色画刷
wndClass.hbrBackground = (HBRUSH)::GetStockObject(WHITE_BRUSH); // 使用白色画刷
填充完WNDCLASSEX 结构,就可以进行注册了。RegisterClassEx 函数调用失败将返回0
::RegisterClassEx(&wndClass);
要创建窗口,使用函数 CreateWindowEx 。
HWND hwnd = ::CreateWindowEx(\ 0, \ // dwExStyle, 扩展样式 TEXT("MainWClass"), \ // 类名 TEXT("My First Window"), \ // 标题 WS_OVERLAPPEDWINDOW, \ // 窗口风格 CW_USEDEFAULT, \ // X, 初始X坐标 CW_USEDEFAULT, \ // Y, 初始Y坐标 CW_USEDEFAULT, \ // nWidth, 宽度 CW_USEDEFAULT, \ // nHeight, 高度 nullptr, \ // hWndParent, 父窗口句柄 nullptr, \ // hMenu,菜单句柄 hInstance, \ // hInstance, 程序实例句柄 nullptr); // lpParam, 用户数据
函数调用成功将返回窗口句柄,失败返回nullptr。
第四个参数dwStyle的值是 WS_OVERLAPPEDWINDOW,即重叠窗口。由他指定的窗口有标题栏、系统菜单、可以改变大小的边框,以及最大化、最小化和关闭按钮。这是一个标准的窗口样式。下面是一些常见风格定于:
::ShowWindow(hwnd, nCmdShow);
ShowWindow 函数用于指定窗口的显示状态。nCmdShow 取值可以是 SW_SHOW 、SW_HIDE 、SW_MINIMIZE
::UpdateWindow(hwnd);
如果指定窗口的更新区域不为空的话,UpdateWindow 函数通过向这个窗口发送一个 WM_PAINT 消息更新它的客户区。当窗口显示在屏幕上时,窗口的客户区被 WNDCLASSEX 中指定的刷子擦去,调用 UpdateWindow 函数将促使客户区重画,以显示其内容。
Windows为每一个线程维护一个消息队列,每当有一个输入发生,Windows就把用户输入翻译成消息放在消息队列中。GetMessage 函数可以从消息队列中取一个消息填充MSG结构。
如果消息队列中没有消息,这个函数会一直等下去,直到有消息进入消息队列为止。
typedef struct tagMSG { HWND hwnd; // 消息要发向的窗口句柄 UINT message; // 消息标识符,以WM_开头的预定义值(Windows Message) WPARAM wParam; // 消息的参数之一 LPARAM lParam; // 消息的参数之二 DWORD time; // 消息放入消息队列的时间 POINT pt; // 这是一个POINT数据结构,表示消息放入消息队列时鼠标的位置 } MSG,
GetMessage 函数从消息队列中取得的消息如果不是WM_QUIT , 则返回非零值。一个WM_QUIT 消息会使GetMessage 函数返回0,从而消息循环结束。
::TranslateMessage(&msg);
此调用把键盘输入翻译成可调用的消息。
::DispatchMessage(&msg);
DispatchMessage 函数分发一个消息到对应窗口的窗口函数。 MainWndProc 处理消息后把控制权交给Windows,此时 DispatchMessage 函数仍然继续工作,当他返回时,消息队列从调用 GetMessage 函数开始进入下一轮。
所有消息不做处理的消息都必须返回一个名为 DefWindowProc的函数让Windows做默认处理, 从DefWindowProc 函数返回的值也必须从消息处理函数返回。
每当客户区变为无效,消息处理函数就会收到一个新的WM_PAINT 消息。
WM_DESTORY 是窗口必须处理的一个消息,当用户关闭窗口时,消息处理函数就会收到一 个
WM_DESTORY消息。当接收到这个消息的时候,说明窗口正在销毁。
::PostQuitMessage(0);
PostQuitMessage函数会会向消息队列中插入一个 WM_QUIT 消息。 GetMessage 函数如果从消息队列中获得到消息时 WM_QUIT ,它将返回0。从而退出消息循环。如果不使用函数 PostQuitMessage 发送 WM_QUIT 消息,则界面虽然被销毁了,但是消息循环还在继续,程序还没有结束。
预览: