Win32编程之创建第一个windows窗口(一)
一、窗口创建过程
- 定义WinMain函数
- 定义窗口处理函数(自定义,处理消息)
- 注册窗口类(向操作系统写入一些数据)
- 创建窗口(内存中创建窗口)
- 显示窗口(绘制窗口的图像)
- 消息循环(获取/翻译/派发消息)
- 消息处理
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | #include <Windows.h> //窗口处理函数(自定义,处理消息) LRESULT CALLBACK WindProc( HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) { return DefWindowProc(hWnd, msgID, wParam, lParam); } //入口函数 int CALLBACK WinMain( HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow) { //设计窗口类 WNDCLASS wc = { 0 }; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hbrBackground = ( HBRUSH )(COLOR_WINDOW + 1); wc.hCursor = NULL; wc.hIcon = NULL; wc.hInstance = hIns; wc.lpfnWndProc = WindProc; wc.lpszClassName = TEXT( "Main" ); wc.lpszMenuName = NULL; wc.style = CS_HREDRAW | CS_VREDRAW; //注册窗口类 RegisterClass(&wc); //将以上所有赋值全部写入操作系统 //在内存中创建窗口 HWND hWnd = CreateWindow(wc.lpszClassName, TEXT( "Window" ), WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hIns, NULL); //显示窗口 ShowWindow(hWnd, SW_SHOW); UpdateWindow(hWnd); //消息循环 MSG nMsg = { 0 }; while (GetMessage(&nMsg, NULL, 0, 0)) { TranslateMessage(&nMsg); //将按键消息转换为字符消息 DispatchMessage(&nMsg); //将消息分发给窗口处理函数 } return 0; } |
二、字符编码
1 2 3 4 5 6 7 8 9 10 11 | DBCS字符编码: A 我 是 程 序 员 01 0203 0405 0607 0809 0A0B 但是解析时,可能为: 01 0203 0405 0607 0809 0A0B 0102 0304 0506 0708 090A 0B UNICODE编码: A 我 是 程 序 员 0001 0203 0405 0607 0809 0A0B 不存在解析的问题 |
1.宽字节
- wchar_t:每个字符占2个字节,wchar_t实际上是unsigned shot类型,定义时,需要增加"L",通知编译器按照双字节编译字符串,采用UNICODE编码
- 需要使用支持wchar_t函数操作宽字节字符串,例如:wchar_t* pwszText = L"Hello wchar";wprint(L"%s\n", pwszText )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #include <Windows.h> #include <stdio.h> void C_Char() { const char * pszText = "hello char" ; printf ( "%s\n" , pszText); } void W_Char() { const wchar_t * pszText = L "hello wchar" ; int len = wcslen(pszText); wprintf(L "%s %d\n" , pszText, len); } void T_Char() { const TCHAR * pszText = TEXT( "hello txt" ); #ifdef UNICODE wprintf(L "%s\n" , pszText); #else printf ( "单:%s\n" , pszText); #endif } int main() { W_Char(); C_Char(); T_Char(); system ( "pause" ); return 0; } |
2.UNICODE字符的打印
wprintf对UNICODE字符打印支持不完善,可以在windows下使用WriteConsole API打印UNICODE字符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #include <Windows.h> #include <stdio.h> void PrintUnicode() { const wchar_t * pszText = L "早上好" ; //wprintf(L"%s\n", pszText); //获取标准输出句柄 HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); WriteConsole(hOut, pszText, wcslen(pszText), NULL, NULL); } int main() { PrintUnicode(); system ( "pause" ); return 0; } |
三、窗口类的概念
- 窗口类包含了窗口的各种参数信息的数据结构
- 每个窗口都具有窗口类,基于窗口类创建窗口
- 每个窗口类都具有一个名称,使用前必须注册到系统
窗口类的分类:
- 系统已经定义好的窗口类,所有应用程序都可以直接使用
- 应用程序全局窗口类,当前应用程序所有模块都可以使用
- 应用程序局部窗口类,当前应用程序中本模块可以使用
1.系统窗口类
不要注册,直接使用窗口类即可,系统已经注册好了
例如:按钮-BUTTON、编辑框-EDIT
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #include <Windows.h> //入口函数 int CALLBACK WinMain( HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow) { //在内存中创建窗口 HWND hWnd = CreateWindow(TEXT( "Button" ), TEXT( "Window" ), WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hIns, NULL); //显示窗口 ShowWindow(hWnd, SW_SHOW); UpdateWindow(hWnd); //消息循环 MSG nMsg = { 0 }; while (GetMessage(&nMsg, NULL, 0, 0)) { TranslateMessage(&nMsg); //将按键消息转换为字符消息 DispatchMessage(&nMsg); //将消息分发给窗口处理函数 } return 0; } |
2.全局及局部窗口类
注册窗口类的函数:
1 | ATOM RegisterClass(CONST WNDCLASS *lpWndClass); |
lpWndClass:窗口类的数据,注册成功后,返回一个数字标识
注册窗口类的结构体:
1 2 3 4 5 6 7 8 9 10 11 12 | typedef struct tagWNDCLASSA { UINT style; //窗口类的风格 WNDPROC lpfnWndProc; //窗口处理函数 int cbClsExtra; //窗口类的附件数据buff的大小 int cbWndExtra; //窗口的附件数据buff的大小 HINSTANCE hInstance; //当前模块的实例句柄 HICON hIcon; //窗口图标句柄 HCURSOR hCursor; //鼠标的句柄 HBRUSH hbrBackground; //绘制窗口背景的画刷句柄 LPCSTR lpszMenuName; //窗口菜单的资源ID字符串 LPCSTR lpszClassName; //窗口类的名称 } WNDCLASSA |
style窗口类风格:
应用程序全局窗口类的注册,需要在窗口类的风格中增加CS_GLOBALCLASS
例如:
WNDCLASS wce = {0};
wce.style = ...|CS_GLOBALCLASS;
应用程序局部窗口类:在注册窗口类时,不添加CS_GLOBALCLASS风格
- CS_HREDRAW:当窗口水平变化时,窗口重新绘制
- CS_VREDRAW:当窗口垂直变化时,窗口重新绘制
- CS_DBLCLKS:允许窗口接收鼠标双击
- CS_NOCLOSE:窗口没有关闭按钮
四、创建窗口
1 2 3 4 5 6 7 8 9 10 11 12 13 | WINUSERAPI HWND WINAPI CreateWindowExA( _In_ DWORD dwExStyle, //窗口的扩展风格 _In_opt_ LPCSTR lpClassName, //已经注册的窗口类名称 _In_opt_ LPCSTR lpWindowName, //窗口标题栏的名字 _In_ DWORD dwStyle, //窗口的基本风格 _In_ int X, //窗口左上角水平坐标位置 _In_ int Y, //窗口左上角垂直坐标位置 _In_ int nWidth, //窗口的宽度 _In_ int nHeight, //窗口的高度 _In_opt_ HWND hWndParent, //窗口的父窗口句柄 _In_opt_ HMENU hMenu, //窗口菜单的句柄 _In_opt_ HINSTANCE hInstance, //应用程序实例句柄 _In_opt_ LPVOID lpParam); //窗口创建附件参数 |
窗口的创建可以使用CreateWindow或CreateWindowExA,若是窗口创建成功则返回窗口句柄
CreateWindow函数执行流程:
- 系统根据传入的窗口名称,在应用程序局部窗口类中查找,如果找到执行第二步,如果未找到则执行第三步
- 比较局部窗口类与创建窗口时传入的HINSTANCE变量,如果发现相等,创建和注册的窗口类在同一模块,创建窗口返回,如果不相等,则继续执行第三步
- 在应用程序全局窗口类,如果找到,执行第四部,如果未找到则执行第5步
- 使用找到的窗口类的信息,创建窗口返回
- 在系统窗口类中查找,如果找到创建窗口返回,否则创建窗口失败
函数内部大致执行流程:
1 2 3 4 5 6 7 | if (找到窗口类) { 申请一大块内存,将窗口的数据信息存入这块内存 return 这块内存的句柄 } else { return NULL } |
五、子窗口的创建
- 创建时要设置父窗口句柄
- 创建风格要增减WS_CHILD|WS_VISIBLE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | #include <Windows.h> //窗口处理函数(自定义,处理消息) LRESULT CALLBACK WindProc( HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) { switch (msgID) { case WM_DESTROY: PostQuitMessage(0); break ; default : break ; } return DefWindowProc(hWnd, msgID, wParam, lParam); } //入口函数 int CALLBACK WinMain( HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow) { //设计窗口类 WNDCLASS wc = { 0 }; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hbrBackground = ( HBRUSH )(COLOR_WINDOW + 1); wc.hCursor = NULL; wc.hIcon = NULL; wc.hInstance = hIns; wc.lpfnWndProc = WindProc; wc.lpszClassName = TEXT( "Main" ); wc.lpszMenuName = NULL; wc.style = CS_HREDRAW | CS_VREDRAW; //注册窗口类 RegisterClass(&wc); //将以上所有赋值全部写入操作系统 //在内存中创建窗口 HWND hWnd = CreateWindow(wc.lpszClassName, TEXT( "Window" ), WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hIns, NULL); //设计子窗口类 WNDCLASS wcChild = { 0 }; wcChild.cbClsExtra = 0; wcChild.cbWndExtra = 0; wcChild.hbrBackground = ( HBRUSH )(COLOR_WINDOW + 1); wcChild.hCursor = NULL; wcChild.hIcon = NULL; wcChild.hInstance = hIns; wcChild.lpfnWndProc = DefWindowProc; wcChild.lpszClassName = TEXT( "Child" ); wcChild.lpszMenuName = NULL; wcChild.style = CS_HREDRAW | CS_VREDRAW; //注册子窗口类 RegisterClass(&wcChild); //创建子窗口 HWND hChildWnd = CreateWindowEx(0, wcChild.lpszClassName, TEXT( "SubWindow" ), WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW, 0, 0, 200, 200, hWnd, NULL, hIns, NULL); HWND hChildWnd1 = CreateWindowEx(0, wcChild.lpszClassName, TEXT( "SubWindow1" ), WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW, 200, 0, 200, 200, hWnd, NULL, hIns, NULL); //显示窗口 ShowWindow(hWnd, SW_SHOW); UpdateWindow(hWnd); //消息循环 MSG nMsg = { 0 }; while (GetMessage(&nMsg, NULL, 0, 0)) { TranslateMessage(&nMsg); //将按键消息转换为字符消息 DispatchMessage(&nMsg); //将消息分发给窗口处理函数 } return 0; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?