达内Win32(一) 结构、字符编码、Helloword程序
一、应用程序分类
-
控制台程序Console
DOS程序,黑框框... -
窗口程序
拥有GUI -
库程序
存放代码、数据的程序,执行文件exe可以从中取出代码执行和获取数据,分为两种:- 静态库程序:扩展名LIB,在编译链接程序时,将代码放入到执行文件中
- 动态库程序:扩展名DLL,在执行文件执行时从中获取代码
其他
入口函数_tWinMain()
宏替换了真正的入口函数WinMain()
创建后的属性win32空项目使用多字节字符集
静态库没有入口函数,动态库有入口函数不过需要依附其他程序运行
有入口函数意味着什么?
有入口函数意味着能够运行,能够进入内存,并生成内存映像,这样能够被系统工具捕获和检测到。
二、开发工具和库
编译工具
- 编译器 CL.EXE 将源代码编译成目标文件 .obj
- 连接器 LINK.EXE 将目标代码、库连接成最终文件
- 资源编译器 RC.EXE (.rc)将资源编译,最终通过链接器存入最终文件
库和头文件
-
windows库
kernel32.dll - 提供了核心API,例如进程、线程、内存管理等
user32.dll - 提供了窗口、消息等API
gdi32.dll - 绘图相关的API -
头文件
windows.h - 所有windows头文件的集合
windef.h - windows数据类型
winbase.h - kernel32的API
wingdi.h - gdi32的API
winuser.h - user32的API
winnt.h - UNICODE字符集支持
Win32中的数据类型怎么来的?
给基础数据类型起别名。
typedef unsigned long ULONG; typedef int BOOL;
而且在Win32中尽量使用这种别名来创建变量🤣。好处是编写的程序通用性、兼容性更好些,在微软的这么长历史中也许别名的定义会有变化,而且也不能保证以后是不是会改变。
相关函数 WinMain()
int WINAPI WinMain( HINSTANCE hInstance, // 当前程序的实例句柄 HINSTANCE hPrevInst, // 当前程序前一个实例句柄 LPSTR lpszCmdLine, // 命令行参数字符串 int nCmdShow // 窗口的显示方式 );
从Win95开始Windows进入64位时代,第二个参数废弃不再使用
第三个参数,与main()
的命令行参数不同,LPSTR
是char*
类型代表只能传一个命令行参数,而main()
是char*[]
能够传入多个参数。PS:我没有验证只是听老师这么说,有时间可以验证下。
第四个参数,有三种:最大化、最小化、正常显示
什么是句柄?
课程最后一天会真正解释,暂时理解为:
“句柄就是一个用来找到内存的东西,但绝对不是指针”。
hInstance当前程序的实例句柄
可以找到本进程占据的那块内存。
相关函数 MessageBox()
int MessageBox( HWND hWnd, // 父窗口句柄 LPCTSTR lpText, // 显示在消息框中的文字 LPCTSTR lpCaption, // 显示在标题栏中的文字 UINT uType // 消息框中的按钮、图标显示类型 );// 返回点击的按钮ID
句柄的类型
“H”开头的类型在Win32中大概率是句柄类型,句柄也分为不同的类型,但都是寻找内存的作用
- hInstance 是进程实例句柄,寻找进程内存位置
- hWnd 是窗口句柄,寻找窗口数据内存位置
实例:ShowMessage
#include <Windows.h> int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpszCmdLine, int nCmdShow ) { MessageBox(NULL, "Hello World", "Information", MB_YESNOCANCEL|MB_ICONERROR ); return 0; } // ShowMessage.cpp
编译方式要通过命令行,在VS中找到vcvars32.bat
位置终端中运行这个脚本,之后再定位到源码文件进行编译链接。
# 编译完生成.obj文件 cl ShowMessage.cpp -c # 链接user32.dll 生成.exe文件 link ShowMessage.obj user32.lib
阻塞函数
函数执行后不会立即返回,应该从两个方面考虑;
- 函数触发阻塞的条件
- 函数解除阻塞的条件
三、第一个Windows窗口
窗口创建的过程
- 定义WinMain函数
- 定义窗口处理函数(自定义,处理消息)
- 注册窗口类(向操作系统内核写入一些数据)
- 创建窗口(内存中创建窗口,窗口的各种数据)
- 显示窗口(绘制窗口的图像)
- 详细循环(获取/翻译/派发消息)
- 消息处理
源码
/* * 也许会有的问题 * * 1、 项目->链接器->系统->子系统中要选用窗口而不是控制台。 * 2、 选择多字符集 * * 窗口创建可以,但是无法关闭 */ #include <windows.h> // 窗口处理函数(自定义,处理消息) LRESULT CALLBACK WndProc( HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam ) { return DefWindowProc(hWnd, msgID, wParam, lParam); } // 入口函数 int CALLBACK WinMain( HINSTANCE hInstance, // 当前程序的实例句柄 HINSTANCE hPrevInst, // 当前程序前一个实例句柄 LPSTR lpszCmdLine, // 命令行参数字符串 int nCmdShow ) { // 1 注册窗口类 WNDCLASS wc = { 0 }; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.hCursor = NULL; wc.hIcon = NULL; wc.hInstance = hInstance; wc.lpfnWndProc = WndProc; wc.lpszClassName = "DaNei Win32 01"; wc.lpszMenuName = NULL; wc.style = CS_HREDRAW | CS_VREDRAW; RegisterClass(&wc);// 将以上所有赋值全部写入操作系统 // 2 在内存创建窗口 HWND hWnd = CreateWindow( "DaNei Win32 01", "windwos", WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hInstance, NULL); // 3 显示窗口 ShowWindow(hWnd, SW_SHOW); UpdateWindow(hWnd); // 4 消息循环 MSG nMsg = { 0 }; while (GetMessage(&nMsg,NULL,0,0)) { TranslateMessage(&nMsg); DispatchMessage(&nMsg);// 将消息交给窗口处理函数来处理 } return 0; }
四、 字符编码
编码历史背景
- ASC(7位 128个)
- ASCII(8位 256个)
- DBCS(单双字节混合编码)
- UNICODE(万国码)
老师讲这段历史的时候还是挺有意思的
DBCS和UNICODE码
-
DBCS(单双混合会导致解析问题)
-
UNICODE(不存在解析问题,有很多实现方式比如utf-8,utf-16)
utf-16
不管什么都字符都用两个字节,多出来的一位补零。utf-16在Windows系统中使用较多。
宽字节字符
新的数据类型wchar_t
- wchar_t 每个字符占2个字节
char每个字符占1个字节
wchar_t 实际是 unsigned short 类型,定义时,需要增加“L”,通知编译器按照双字节编译字符串,采用UNICODE编码 - 需要使用支持wchar_t函数操作宽字节字符串:
wchar_t* pwszText = L“Hello wchar”;
wprintf(L“%s\n”,pwszText);
#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); } int main() { W_char(); // 11 个有效字符 并不是占了多少个内存的字节 C_char(); getchar(); return 0; }
char还是wchar_t?
微软建议使用TCHAR
,在winnt.h中有如下定义:
#ifdef UNICODE typedef WCHAR TCHAR, *PTCHAR; #define __TEXT(quote) L##quote // r_winnt #else /* UNICODE */ // r_winnt typedef char TCHAR, *PTCHAR; #define __TEXT(quote) quote // r_winnt
void T_char() { const TCHAR* pszText = __TEXT("hello txt"); #ifdef UNICODE wprintf(L"%s \n", pszText); #else printf("单%s\n", pszText); #endif }
打印UNICODE字符
wprintf对Unicode字符打印支持不完善,英文字符可以,但是中文容易打印不出来。
在Windows下使用WriteConsole API打印UNICDOE 字符。
标准句柄
微软提供的三个标准句柄:
- 标准输入句柄
- 标准输出句柄
- 标准错误句柄
需要通过专门的API来获取GetStdHandle
WriteConsole
void PrintUnicode() { const wchar_t* pszText = L"测试内容"; HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); // 获取标准输出句柄 WriteConsole(hOut, pszText, wcslen(pszText), NULL, NULL); }
这个函数很强大,也可以打印单字节字符串char*
为什么更改Unicode字符集?
因为项目属性设置完Unicode后,编译器会默认增加Unicode宏定义,代码中的效果可能和预想中的不一样。为了少写个“L” =。 =
本文作者:在下阿shen
本文链接:https://www.cnblogs.com/shen97/p/17120008.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步