如何使用windows.h创建窗口?能否不引入头文件实现窗口创建?
关于Windows窗口创建底层原理与<windows.h>的疑问解答
一、<windows.h>的内部原理
<windows.h>是Windows SDK提供的核心头文件,本质上是一堆C语言风格的宏定义、数据类型声明、函数原型声明的集合,它本身没有可执行代码,核心作用是给C/C++编译器提供Windows API的接口信息,让你的代码能正确调用系统底层提供的函数。
它会自动包含一系列细分功能的头文件,比如负责窗口管理的<winuser.h>、图形绘制的<wingdi.h>、系统内核功能的<kernel32.h>等,把分散的Windows API接口统一打包暴露给开发者,避免手动逐个引入细分头文件的繁琐。
二、<windows.h>是不是C/C++代码?
是的,它完全由合法的C/C++代码构成(准确说是兼容C的代码,因为Windows API设计时采用C接口以保证跨语言兼容性)。但要明确:它只包含声明,不包含实现——真正的Windows API实现是在系统的动态链接库(如user32.dll、kernel32.dll)里,这些是微软编译好的二进制文件,属于Windows系统的核心组件。
三、能不能不引入任何头文件创建窗口?
可以,但过程非常繁琐,因为你需要手动完成所有原本由头文件提供的工作:
- 手动声明所有需要的数据类型(如
WNDCLASS、MSG) - 手动定义所有需要的宏(如
CS_HREDRAW、WS_OVERLAPPEDWINDOW,或者直接用它们对应的数值) - 手动声明需要调用的Windows API函数原型,还要指定正确的调用约定(比如
__stdcall) - 编译时必须显式链接对应的系统库(如
user32.lib、kernel32.lib)
极简示例(不引入任何头文件)
// 手动声明基本类型 typedef unsigned int UINT; typedef unsigned long DWORD; typedef long LONG; typedef void* HINSTANCE; typedef void* HWND; typedef const char* LPCSTR; typedef unsigned int WPARAM; typedef long LPARAM; typedef int BOOL; // 手动定义宏 #define CS_HREDRAW 0x0002 #define WS_OVERLAPPEDWINDOW 0x00CF0000 #define SW_SHOW 5 #define WM_DESTROY 0x0002 #define WM_QUIT 0x0012 #define TRUE 1 #define FALSE 0 // 手动声明函数原型(注意__stdcall调用约定) typedef BOOL (__stdcall* ShowWindowPtr)(HWND, int); typedef HWND (__stdcall* CreateWindowExPtr)(DWORD, LPCSTR, LPCSTR, DWORD, int, int, int, int, HWND, void*, HINSTANCE, void*); typedef BOOL (__stdcall* RegisterClassPtr)(const void*); typedef int (__stdcall* MessageBoxPtr)(HWND, LPCSTR, LPCSTR, UINT); typedef int (__stdcall* GetMessagePtr)(void*, HWND, UINT, UINT); typedef BOOL (__stdcall* TranslateMessagePtr)(const void*); typedef LONG (__stdcall* DispatchMessagePtr)(const void*); typedef void (__stdcall* PostQuitMessagePtr)(int); typedef HINSTANCE (__stdcall* LoadLibraryPtr)(LPCSTR); typedef void* (__stdcall* GetProcAddressPtr)(HINSTANCE, LPCSTR); // 窗口过程函数 LONG __stdcall WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { if (msg == WM_DESTROY) { HINSTANCE user32 = ((LoadLibraryPtr)__builtin_get_proc_address((HINSTANCE)-1, "LoadLibraryA"))("user32.dll"); PostQuitMessagePtr PostQuitMessage = (PostQuitMessagePtr)((GetProcAddressPtr)__builtin_get_proc_address((HINSTANCE)-1, "GetProcAddress"))(user32, "PostQuitMessage"); PostQuitMessage(0); return 0; } return 0; } // 入口函数 int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, char* lpCmdLine, int nCmdShow) { // 获取LoadLibrary和GetProcAddress地址(kernel32.dll已默认加载) LoadLibraryPtr LoadLibrary = (LoadLibraryPtr)__builtin_get_proc_address((HINSTANCE)-1, "LoadLibraryA"); GetProcAddressPtr GetProcAddress = (GetProcAddressPtr)__builtin_get_proc_address((HINSTANCE)-1, "GetProcAddress"); HINSTANCE user32 = LoadLibrary("user32.dll"); RegisterClassPtr RegisterClass = (RegisterClassPtr)GetProcAddress(user32, "RegisterClassA"); CreateWindowExPtr CreateWindowEx = (CreateWindowExPtr)GetProcAddress(user32, "CreateWindowExA"); ShowWindowPtr ShowWindow = (ShowWindowPtr)GetProcAddress(user32, "ShowWindow"); // 手动构造WNDCLASS(成员顺序必须和系统定义完全一致) struct WNDCLASS wc = {0}; wc.style = CS_HREDRAW; wc.lpfnWndProc = WindowProc; wc.hInstance = hInstance; wc.lpszClassName = "RawWindowClass"; RegisterClass(&wc); HWND hwnd = CreateWindowEx(0, "RawWindowClass", "No-Header Window", WS_OVERLAPPEDWINDOW, 100, 100, 400, 300, 0, 0, hInstance, 0); ShowWindow(hwnd, nCmdShow); // 消息循环 GetMessagePtr GetMessage = (GetMessagePtr)GetProcAddress(user32, "GetMessageA"); TranslateMessagePtr TranslateMessage = (TranslateMessagePtr)GetProcAddress(user32, "TranslateMessage"); DispatchMessagePtr DispatchMessage = (DispatchMessagePtr)GetProcAddress(user32, "DispatchMessageA"); struct MSG msg; while (GetMessage(&msg, 0, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }
注:这个示例用了GCC的内置函数来获取系统函数地址,不同编译器(如MSVC)需要调整对应逻辑。实际运行还需补充窗口过程的默认消息处理等细节,这里为了简化省略了。
内容的提问来源于stack exchange,提问作者Miodek
相关产品推荐
相关产品推荐

