来学学 windows 的基础知识,在 C++ 中的实现。
字符类型
在 C++ 中,两个标准的字符类型是 char
和 wchar_t
类型,分别是代表 ASCII 字符和 UNICODE 字符。windows 对它进行了二次定义,变成了 CHAR
和 WCHAR
类型。并且为了兼容环境,还出现了 TCHAR
,它会跟着项目环境选择 CHAR
还是 WCHAR
。
有个函数可以把单字节转为宽字节:MultiByteToWideChar
。
1 2 3 4
| CHAR* s = (CHAR*)"xia0ji233"; WCHAR buffer[50]; MultiByteToWideChar(CP_ACP, NULL, s, -1, buffer, 50); printf("%S", buffer);
|
这里格式化字符串 %S
不同于 %s
,%S
是宽字符,如果使用 %s
则会因为 ASCII 字符转 unicode 多了一个 00 字节而被截断。
还有个函数可以转回去。
1 2 3 4
| WCHAR* ws = (WCHAR*)L"xia0ji233"; CHAR buffer[50]; WideCharToMultiByte(CP_ACP, NULL, ws, -1, buffer, 50, NULL, NULL); printf("%s\n", buffer);
|
控制台程序的入口是 int main(int argc,char *argv[],char *env[])
,而 windows 程序的入口是 WinMain()
,参数介绍如下:
- hInstance 实例句柄 指向 exe 模块
- hPrevInstance 废弃了
- lpCmdLine 命令行参数
- nCmdShow程序显示模式,最大化显示,最小化
MFC 就是对这个函数进行的一个封装。
Windows 的程序是基于消息的。
MessageBox API介绍
先学第一个 API——MessageBox,作用是弹窗。
MessageBox 有四个参数:
- hwnd:窗口句柄
- lptext:内容
- lpCaption:标题
- uType:按钮类型
返回值会判断我们对这个窗口做了哪些处理,也是由一些宏实现的,这里也不想记流水账,就写一下 IDOK 返回值表示我们按了确定按钮。
MessageBox有很多版本,ASCII 版(MessageBoxA),宽字版(MessageBoxW),而 MessageBox 会对我们当前的环境判断而选择合适的版本。并且还推出了 Ex 版,主要是因为想把 MessageBox 的定义改一下,但是又要保持兼容性,因此出了这样的增强版,增强版多了一个参数就是语言类型。
windows程序开发入门
就跟着做一下。
创建空项目,把链接器→系统中的子系统改为 windows,随后就可以开始 windows 编程了。
之后我们要以 WinMain 函数为原型,并实现它,windows 默认里面会有一个死的消息循环,我们不需要使用某些暂停的手段,我们也不能使用标准输入输出函数打印内容。
创建窗口有以下几个过程:
- 填充窗口类
- 创建窗口
- 显示窗口
- 更新窗口
- 建立消息循环
- 实现窗口过程函数
这里穿插一个小知识:类似的对结构体的第一个字段赋值 sizeof(type) 是为了防止结构体更新导致的不能向下兼容。
填充窗口类
写一个函数填充窗口结构,包括图标,鼠标指针,任务栏图标……,最后调用一个 RegisterClassExW
去根据这个结构注册窗口类。
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
| ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEXW wcex; wcex.cbSize = sizeof(WNDCLASSEXW); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = 0; wcex.hCursor = 0; wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wcex.lpszMenuName = 0; wcex.lpszClassName = wszClassName; wcex.hIconSm = 0; return RegisterClassEx(&wcex); }
|
创建窗口
创建窗口,填一些参数,返回一个窗口句柄。
1 2 3 4 5 6 7 8 9 10 11
| BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd = CreateWindow(wszClassName, TITLE, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 500, 500, NULL, NULL, hInstance, NULL); if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }
|
这里面还包含了显示窗口和更新窗口。
消息循环与实现处理函数
就是消息的翻译和分发。
1 2 3 4 5 6
| MSG msg;
while (GetMessage(&msg, nullptr, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); }
|
实现处理函数,这里主要对窗口绘制和退出做一个动作,其它的过程函数都是在这里实现的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_COMMAND: { } break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }
|
最终代码
最后的代码就是这样的:
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 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
| #include<stdio.h> #include<windows.h> using namespace std; const WCHAR* wszClassName = L"xia0ji233"; const WCHAR* TITLE = L"Create By xia0ji233"; LRESULT CALLBACK WndProc(HWND hWnd, UINT, WPARAM, LPARAM); ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); int WinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd ) { MyRegisterClass(hInstance); if (InitInstance(hInstance, nShowCmd)) { MSG msg;
while (GetMessage(&msg, nullptr, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } } } ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEXW wcex; wcex.cbSize = sizeof(WNDCLASSEXW); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = 0; wcex.hCursor = 0; wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wcex.lpszMenuName = 0; wcex.lpszClassName = wszClassName; wcex.hIconSm = 0; return RegisterClassEx(&wcex); } BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd = CreateWindow(wszClassName, TITLE, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 500, 500, NULL, NULL, hInstance, NULL); if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_COMMAND: { } break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }
|
直接运行,成功!