當Windows操作系統啟動一個程序時,它調用的就是該程序的WinMain函數( 實際是由插入到可執行文件中的啟動代碼調用的)。 WinMain是Windows程序的入口點函數,與DOS程序的入口點函數main的作用相同,當WinMain 函數結束或返回時,Windows應用程序結束。
什么是Windows API編程
- 應用編程接口
- Application Programming Interface
WinMain函數
當Windows操作系統啟動一個程序時,它調用的就是該程序的WinMain函數( 實際是由插入到可執行文件中的啟動代碼調用的)。 WinMain是Windows程序的入口點函數,與DOS程序的入口點函數main的作用相同,當WinMain 函數結束或返回時,Windows應用程序結束。
int WINAPI WinMain(
HINSTANCE hInstance, //應用程序實例
HINSTANCE hPrevInstance, //上一個應用程序實例
LPSTR lpCmdLine, //命令行參數
int nShowCmd //窗口顯示的樣式
);
- WINAPI:是一個宏,它代表的是__stdcall(注意是兩個下劃線),表示的是參數傳遞的順序:從右往左入棧,同時在函數返回前自動清空堆棧。
- hInstance:表示該程序當前運行的實例的句柄,這是一個數值。當程序在Windows下運行時,它唯一標識運行中的實例(注意,只有運行中的程序實例, 才有實例句柄)。一個應用程序可以運行多個實例,每運行一個實例,系統都會給該實例分配一個句柄值,并通過hInstance參數傳遞給 WinMain 函數。
- hPrevInstance:表示當前實例的前一個實例的句柄。在Win32環境下,這個參數總是NULL,即在Win32環境下,這個參數不再起作用。
- lpCmdLine:是一個以空終止的字符串, 指定傳遞給應用程序的命令行參數,相當于C或C++中的main函數中的參數char *argv[]。
- nShowCmd:表示一個窗口的顯示,表示它是要最大化顯示、最小化顯示、正常大小顯示還是隱藏顯示。
#include <Windows.h>
#include <cstdio>
#pragma comment(linker, "/subsystem:\"console\" /entry:\"WinMainCRTStartup\"")
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow) {
printf("%p %p %p\n", hInstance, hPrevInstance, GetModuleHandleA(NULL));
printf("%d %d\n", nCmdShow, SW_SHOWNORMAL);
printf("%s\n", lpCmdLine);
MessageBoxA(NULL, TEXT("第一個 Windows API 程序"), TEXT("黑貓編程"), MB_OK);
return 0;
}
字符集
#include <Windows.h>
#include <cstdio>
#include <cstring>
#include <clocale>
int main() {
const char* str1 = "Abc中國";
printf("%s %d\n", str1, strlen(str1));
_wsetlocale(LC_ALL, L"chs");
const wchar_t* str2 = L"ABC中國文字";
wprintf(L"%s %d\n", str2, wcslen(str2));
MessageBoxW(NULL, TEXT("hello cat."), L"coding", MB_OK);
return 0;
}
Windows 編程模型
一個完整的Win32程序(#include <windows.h>),該程序實現的功能是創建一個窗口,并在該窗口中響應鍵盤及鼠標消息,程序的實現步驟為:
- WinMain函數的定義
- 創建一個窗口
- 進行消息循環
- 編寫窗口過程函數

項目創建


窗口程序模板代碼
#include <Windows.h>
// 自定義窗口過程回調函數
LRESULT CALLBACK MyWindowProc(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
switch (Msg) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProcA(hwnd, Msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
// 注冊窗口類
WNDCLASS wnd;
wnd.cbClsExtra = 0;
wnd.cbWndExtra = 0;
wnd.hbrBackground = (HBRUSH)(GetStockObject(WHITE_BRUSH));
wnd.hCursor = LoadIcon(NULL, IDC_ARROW);
wnd.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wnd.lpfnWndProc = MyWindowProc;
wnd.lpszClassName = TEXT("blackcat");
wnd.lpszMenuName = NULL;
wnd.style = CS_HREDRAW;
wnd.hInstance = hInstance;
RegisterClassA(&wnd);
// 創建窗口 返回之前發送 VW_CREATE
HWND hwnd = CreateWindowA(
TEXT("blackcat"),
TEXT("黑貓編程"),
WS_OVERLAPPEDWINDOW,
100, 100, 300, 300, NULL, NULL, hInstance, NULL
);
// 顯示窗口
ShowWindow(hwnd, nShowCmd);
// 更新窗口
UpdateWindow(hwnd);
// 消息循環 收到 VM_QUIT 退出
MSG msg;
while (GetMessageA(&msg, hwnd, 0, 0)) {
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
return 0;
}
線程和窗口
在WIN32中,消息隊列是與線程(Thread)相關的,一個線程只能有一個消息隊列(queue)與之相對應。當一個線程里面首次調用User32.dll或GDI32.dll中的函數時,系統會為該線程創建一個消息隊列,否則就沒有消息隊列。
在一個線程中可以產生多個窗口,所以每個窗口課共用一個線程消息隊列,所有產生給某個窗口的消息,都先由創建這個窗口的線程處理,窗口在任何線程中都可以創建,但消息循環必須要和創建窗口在同一線程,否則窗口將無法從DispatchMessage()獲取任何消息,為了使窗口接受這些消息,線程必須有自己的循環。

消息分類
- 標準消息:所有以WM_開頭的消息,除了WM_COMMAND
- 命令消息:來自菜單欄、工具欄、按鈕或者快捷鍵的消息。WM_COMMAND
- 通告消息:由控件產生的消息,例如按鈕單擊、列表項的選擇等,為了向其父窗口通知事件的發生。
SendMessage和PostMessage

PostThreadMessage
