第四十三课 win32 宽字节
1.编码
0x00.ASCII码
1、ASCII 码使用指定的 7 位或 8 位二进制数组合来表示 128 或 256 种可能的字符
2、标准 ASCII 码使用 7 位二进制数来表示所有的大写和小写字母,数字 0 到 9、标点符号,以及在美式英语中使用的特殊控制字符。
3、扩展 ASCII 码允许将每个字符的第 8 位用于确定附加的 128 个特殊符号字符、外来语字母和图形符号。
0x01.GB2312编码
计算机发明之处及后面很长一段时间,只用应用于美国及西方一些发达国家,ASCII能够很好满足用户的需求。但是当天朝也有了计算机之后,为了显示中文,必须设计一套编码规则用于将汉字转换为计算机可以接受的数字系统的数。<br /> 天朝专家把那些127号之后的奇异符号们(即EASCII)取消掉,规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到 0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。<br /> 在这些编码里,还把数学符号、罗马希腊的 字母、日文的假名们都编进去了,连在ASCII里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号以下的那些就叫"半角"字符了。<br />**上述编码规则就是GB2312或GB2312-80**<br />![image.png](https://cdn.nlark.com/yuque/0/2024/png/28321768/1705910665362-9884b637-4fcc-4a2b-b348-eec9b83b37b6.png#averageHue=%23e5e5e5&clientId=u484cabe6-6caf-4&from=paste&height=353&id=u730dcfee&originHeight=618&originWidth=763&originalType=binary&ratio=1.75&rotation=0&showTitle=false&size=173761&status=done&style=none&taskId=u39f327ee-644d-4daa-af49-60cdd9a9202&title=&width=436)
容易出现的问题:
1、容易和其他国家的符号冲突,变成乱码
2、在计算符号数时,经常会把一个中文当成两个符号,增加计算难度
0x02.Unicode
2.宽字节
0x00 宽字符
很明显,使用char时中字被截断了。
使用wchar_t在内存中则写入两个字节,但是他仍然按ASCII表存入
在值前面加L,表示使用UNICODE编码
0x01 宽字符串
0x02 控制台打印
0x03 字符串长度
3. Win32 API中的宽字符和多字节字符
0x00 宽字符和多字节字符
TCHAR类型就是放到ascii环境就是ascii,放到unicode环境就是unicode
那些messageboxA和messageboxW函数,到了底层系统还是会统一转成W处理
0x01 字符数组赋值
0x02 字符串指针赋值
0x03 各种版本的MessageBox
4.win32入口程序
int CALLBACK WinMain(
_In_ HINSTANCE hInstance,
_In_ HINSTANCE hPrevInstance,
_In_ LPSTR lpCmdLine,
_In_ int nCmdShow
)
{
return 0;
}
:::info
hInstance参数:pe的base基址
lpCmdLine参数:命令行启动程序时后面跟的参数
:::
5.在Win32程序中打印信息
win32项目没有控制台了,没办法用printf()函数,我们用另外的OutputDebugStringA()函数来打印调试
问题:
但是没办法打印变量,比如OutputDebugStringA("我是%s",x);
解决办法:
创建一个tool的类,把头文件和cpp文件都放上去
判断是否debug版,release版则会无输出
tools.h
void OutPutDebugStringF(const char * format, ...);
//判断是否debug版,release版则会无输出
#ifdef _DEBUG
#define DebugPrints OutPutDebugStringF
#else
#define DebugPrints
#endif
tools.cpp
#include "tools.h"
#include "stdio.h"
#include <windows.h>
void OutPutDebugStringF(const char* format, ...) {
va_list vlArgs;
char* strBuffer = (char*)GlobalAlloc(GPTR, 4096);//GPTR:分配固定内存并初始化为0
va_start(vlArgs, format);
_vsnprintf_s(strBuffer, 4096, 4096 - 1, format, vlArgs);//编写使用指针参数列表的格式化输出
va_end(vlArgs);
OutputDebugStringA(strBuffer);
GlobalFree(strBuffer);//释放内存
}
6.打印报错信息(GetLastError的使用
没有控制台输出,有错误了就只是不会弹窗,并不会告诉你错在哪,需要自己加一个函数,返回的值就是最后一个错误的错误代码
第四十四课 win32 事件_消息_消息处理函数
1.事件
2.消息
Windows为了能够准确的描述这些信息,提供了一个结构体:MSG,该结构体里面记录的事件的详细信息.
typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG, *PMSG;
:::info
1、hwnd:
表示消息所属的窗口
一个消息一般都是与某个窗口相关联的
在Windows中 HWND类型的变量通常用来标识窗口。
2、message
在Windows中,消息是由一个数值来表示的
但是由于数值不便于记忆,所以Windows将消息对应的数值定义为WM_XXX宏(WM == Window Message
鼠标左键按下 WM_LBUTTONDOWN 键盘按下 WM_KEYDOWN
3、wParam 和 lParam
32位消息的特定附加信息,具体表示什么处决于message
4、time
消息创建时的时间
5、消息创建时的鼠标位置
:::
3.一个完整的消息流程
设计窗口类(说明要画的窗口的相关信息)->注册窗口(让windows知道这个类的存在)->创建窗口->显示窗口->写一个消息循环不断接收窗口的消息->回调函数处理消息
系统消息队列与应用程序消息队列:
最后把各个消息取出,看看是不是窗口自己关心的,是就处理,不是就丢给windows系统处理
通俗点形容:我们点击一个应用窗口的一些按钮,这些是应用窗口自己的功能,这就是应用窗口自己关心的,拖动这个窗口等操作就是windows系统处理的
4.第一个图形界面程序
0x00 步骤1
0x01 步骤2
0x02 步骤3
在新的cpp文件中添加:#include <Windows.h>
int CALLBACK WinMain( CALLBACK 是一个宏
_In_ HINSTANCE hInstance, #define CALLBACK __stdcall
_In_ HINSTANCE hPrevInstance,
_In_ LPSTR lpCmdLine,
_In_ int nCmdShow
) 所有的Win32 API函数都遵循该约定
{
return 0;
}
0x04 步骤4 设计窗口类
先定义一个窗口类以及这个类所需的参数,wndclass = {0}必须加,给其余参数赋值0,不然会报错
//窗口的类名
TCHAR className[] = "My First Window";
// 创建窗口类的对象
WNDCLASS wndclass = {0}; //一定要先将所有值赋值
wndclass.hbrBackground = (HBRUSH)COLOR_MENU; //窗口的背景色
wndclass.lpfnWndProc = WindowProc; //窗口过程函数
wndclass.lpszClassName = className; //窗口类的名字
wndclass.hInstance = hInstance; //定义窗口类的应用程序的实例句柄
0x05 步骤5 注册窗口类
0x06 步骤6 创建窗口
// 创建窗口
HWND hwnd = CreateWindow(
className, //类名
TEXT("我的第一个窗口"), //窗口标题
WS_OVERLAPPEDWINDOW, //窗口外观样式
10, //相对于父窗口的X坐标
10, //相对于父窗口的Y坐标
600, //窗口的宽度
300, //窗口的高度
NULL, //父窗口句柄,为NULL
NULL, //菜单句柄,为NULL
hInstance, //当前应用程序的句柄
NULL); //附加数据一般为NULL
if(hwnd == NULL) //是否创建成功
return 0;
0x07 步骤7 显示窗口
0x08 步骤8 消息循环
这个步骤其实只是获取消息对其进行加工,加工后传递给系统,系统调用上面定义类时指定的WindowProc()窗口过程函数去处理
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
0x09 步骤9 回调函数
1、窗口回调函数处理过的消息,必须传回0.
2、窗口回调不处理的消息,由DefWindowProc函数(windows系统)自己来处理.
LRESULT CALLBACK WindowProc(
IN HWND hwnd,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam
)
{
switch(uMsg)
{
//窗口消息
case WM_CREATE:
{
DbgPrintf("WM_CREATE %d %d\n",wParam,lParam);
CREATESTRUCT* createst = (CREATESTRUCT*)lParam;
DbgPrintf("CREATESTRUCT %s\n",createst->lpszClass);
return 0;
}
case WM_MOVE:
{
DbgPrintf("WM_MOVE %d %d\n",wParam,lParam);
POINTS points = MAKEPOINTS(lParam);
DbgPrintf("X Y %d %d\n",points.x,points.y);
return 0;
}
case WM_SIZE:
{
DbgPrintf("WM_SIZE %d %d\n",wParam,lParam);
int newWidth = (int)(short) LOWORD(lParam);
int newHeight = (int)(short) HIWORD(lParam);
DbgPrintf("WM_SIZE %d %d\n",newWidth,newHeight);
return 0;
}
case WM_DESTROY:
{
DbgPrintf("WM_DESTROY %d %d\n",wParam,lParam);
PostQuitMessage(0);
return 0;
}
//键盘消息
case WM_KEYUP:
{
DbgPrintf("WM_KEYUP %d %d\n",wParam,lParam);
return 0;
}
case WM_KEYDOWN:
{
DbgPrintf("WM_KEYDOWN %d %d\n",wParam,lParam);
return 0;
}
//鼠标消息
case WM_LBUTTONDOWN:
{
DbgPrintf("WM_LBUTTONDOWN %d %d\n",wParam,lParam);
POINTS points = MAKEPOINTS(lParam);
DbgPrintf("WM_LBUTTONDOWN %d %d\n",points.x,points.y);
return 0;
}
}
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
5.wParam和lParam字段
他们是不固定的,是消息id的附加信息,且在不同消息类型内有不同的含义
WM_CREATE消息
WM_MOVE消息
移动一次触发一次
msdn文档也写出了使用宏转换POINTS结构方便读取