在 64 位 Windows 系統上,使用 VS2012 自帶的 64 位 Spy++ 檢視系統自帶 Notepad(notepad.exe)的窗口句柄(HWND),發現顯示為 32 位值(如 0x00120ABC),而非預期的 64 位值。這是否意味著 Notepad 是 32 位程序?句柄在 64 位系統下到底是怎麼分配的?本文將從 Windows 句柄架構、兼容性設計、WoW64 機制三個層面揭開謎底。
Windows 窗口句柄(HWND)在 64 位系統上本質上仍是 32 位值,無論進程是 32 位還是 64 位。這是 Windows 為了跨架構窗口消息互通、向前兼容、節省內存而做的刻意設計。
HWND、HANDLE、HINSTANCE 等都是不透明標識符,內部為內核對象表索引,非用戶態虛擬地址。內核維護一張全局句柄表,表項大小固定,索引範圍遠小于 2^32。
CreateWindowEx 返回的 HWND 也是 32 位整數(高 32 位始終為 0)。SendMessage(hwnd, ...) 跨 32/64 位進程傳遞句柄時,參數大小一致,無需截斷/擴展。C:\Windows\System32\notepad.exe → 64 位 PE (PE32+)
C:\Windows\SysWOW64\notepad.exe → 32 位 PE (PE32) # 兼容層
即使 Notepad 是 64 位,其窗口句柄依然是 32 位值。Spy++ 顯示「32 位」是指句柄數值寬度,而非進程位數。
user32.dll 32 位版本。wParam/lParam 中的句柄字段在 32/64 位間無縫轉換,因為句柄本身就是 32 位。GetWindowLongPtr(hwnd, GWLP_WNDPROC) 返回 64 位指針時需用 LONG_PTR,但 hwnd 本身不變。// C++ 驗證
HWND h = CreateWindowEx(0, L"STATIC", L"Test", WS_OVERLAPPEDWINDOW,
0,0,200,100, nullptr, nullptr, hInst, nullptr);
printf("HWND value: 0x%p (size=%zu bits)\n", h, sizeof(h)*8);
// 64 位編譯輸出:HWND value: 0x00120ABC (size=64 bits)
// 但實際數值高 32 位為 0,有效位僅 32 位
註:sizeof(HWND) 在 64 位編譯下為 8 字節(指針大小),但有效數值域僅低 32 位。
| 設計目標 | 採用 32 位句柄的收益 |
|---|---|
| 跨架構互通 | 32/64 位進程無縫發送消息、共享窗口 |
| 向前兼容 | 舊 32 位代碼無需修改即可在 64 位系統運行 |
| 內存效率 | 句柄表項更小,內核對象表更緊湊 |
| API 穩定 | USER32/GDI32 API 簽名近 30 年幾乎不變 |
GetWindowLongPtr(hwnd, GWLP_USERDATA) 存 LONG_PTR(64 位下 8 字節)。Windows 句柄的 32 位本質,是微軟在 16→32→64 位演進中,為了生態兼容性做出的經典工程妥協。理解這一點,能讓你在編寫跨架構 Hook、UI 自動化、遠程桌面協議時避開無數坑。