长期的一致性,胜过短期的高强度。|

在下阿shen

园龄:3年7个月粉丝:0关注:5

达内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()的命令行参数不同,LPSTRchar*类型代表只能传一个命令行参数,而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 中国大陆许可协议进行许可。

posted @   在下阿shen  阅读(81)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起