达内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” =。 =

浙公网安备 33010602011771号