让我们与星光探索者一起,探索Windows吧!

探索Windows程序类型

我们可以给Windows程序分类

  • 控制台程序Console

    DOS程序,本身没有窗口,通过Windows DOS窗口执行。

    在Windows通常为.exe文件,控制台程序的入口为main函数

  • 窗口程序

    拥有自己的窗口,可以和用户交互

    通常为.exe文件,情况下窗口程序的入口为WinMain函数

  • 库程序

    存放代码,数据的程序,执行文件可以从中取出代码执行和获取数据

    • 静态库程序:拓展名.lib。在链接阶段将代码放入可执行文件中,无入口函数

    • 动态库程序:拓展名.dll。在执行文件中执行时从中获得代码,入口函数为DllMain

编译工具

通常编写Win32程序应该使用微软的工具,毕竟Windows是微软的。

下面让我们一起探索Visual C++的编译工具吧!

编译器cl.exe:将源代码(.c或.cpp)编译成目标代码.obj

链接器link.exe:将目标代码,资源,库等链接,生成最终文件

资源编译器rc.exe:将资源文件(.rc)将资源编译成.res文件,最终通过链接器写入最终文件

如果你安装了Visual Studio,那么这些编译工具的路径可能为:

C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.34.31931\bin\Hostx64\x64\

Windows库和头文件

下面只是列举部分库和对应的头文件,并不是全部内容

  • kernel32.dll 提供了核心的API,如进程,线程,内存管理

  • user32.dll 提供了窗口,消息等用户常用的API

  • gdi32.dll 绘图相关的API

路径: C:\Windows\System32\

Windows的头文件

  • windows.h 大部分Windows头文件的集合,包含了下列提到的头文件等文件

  • windef.h windows基本数据类型定义

  • winbase.h kernel32.dll 的API

  • wingdi.h gdi32.dll的API

  • winuser.h user32.dll的API

  • winnt.h UNICODE字符集的支持

路径:C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.34.31931\include

第一个Win32程序

首先先新建一个文件夹,并创建test.cpp源代码文件,在test.cpp文件书写这样的代码:

1
2
3
4
5
6
7
8
9
10
11
12
#include <windows.h>

INT WINAPI WinMain(
HINSTANCE hInstance, // 当前实例句柄
HINSTANCE hPrevInstance, // 上一个实例句柄
LPSTR lpCmdLine, // 运行程序的命令行
int nCmdShow // 程序最初显示方式
)
{
MessageBox(NULL, TEXT("探索Windows!"), TEXT("星光探索者"), MB_OK);
return 0;
}

星光探索者非常喜欢探索,于是星光探索者在命令行调用Visual C++编译工具,输入了像这样的命令行cl test.cpp就可以编译这个Win32程序了。如果有的小伙伴提示错误信息unresolved symbol无法解析的外部符号,那么就需要手动链接相关的库了。MessageBox函数属于user32.dll,因此我们需要使用link指令:cl test.cpp /link user32.lib

当然用MinGW也是可以的,只不过需要加上-mwindows参数,要不然MinGW不会认为是编译Win32程序,而是认为是控制台程序

运行这个程序,可以看到显示了一个消息框。

深入探索

WinMain函数的结构如下:

1
2
3
4
5
6
INT WINAPI WinMain(
HINSTANCE hInstance, // 当前实例句柄
HINSTANCE hPrevInstance, // 上一个实例句柄
LPSTR lpCmdLine, // 运行程序的命令行
int nCmdShow // 程序最初显示方式
);
  • hInstance 为当前实例句柄。在Windows句柄的概念的随处可见

  • hPrevInstance 为上一个实例句柄。他在16位Windows系统使用,现在不使用,值总是为NULL(定义为0)

  • pCmdLine 包含命令行参数的字符串。如果需要获取整个命令行,请使用GetCommandLine

  • nCmdShow 窗口的最初显示方式(最小化,正常,最大化)

这个函数返回INT(定义为int)类型的值,操作系统不使用返回值,但可以传递给其他程序

WINAPI是Windows的函数调用约定,定义为__stdcall,使用APIENTRY也是相同的结果

上面那个简易的程序还调用了MessageBox函数,

1
2
3
4
5
6
int MessageBox(
HWND hWnd, //窗口句柄
LPCTSTR lpText, //弹窗内容
LPCTSTR lpCaption, //弹窗标题
UINT uType //弹窗类型
);
  • hWnd 为显示消息框的窗口句柄,也就是消息框的父窗口,如果没有父窗口就传NULL,关于窗口的更多信息下期星光探索者继续探索。 HWND类型为handle of the windows的意思。

  • lpText 是一个TCHAR类型的字符串,为消息框的内容

    TCHAR是windows基本数据类型,是宏定义。如果使用多字节字符集的项目,TCHAR被定义为char;如果使用Unicode字符集的项目,TCHAR被定义为wchar_t。类型LPCTSTRconst TCHAR*,在下一期会深入探索。

  • lpCaption是一个TCHAR类型的字符串,为消息框的标题内容

  • uType为消息框的显示按钮及消息框标题栏的图标,默认显示一个确定(OK)按钮。这个函数的返回值即为用户按下的按钮。

    可选择的部分样式如下,消息框显示的按钮文本取决于系统的语言,想知道更多信息请点击我!

    消息框的按钮:

    按钮样式 效果
    MB_OK 消息框包含一个按钮确定,这是默认值
    MB_OKCANCEL 消息框包含两个按钮,确定取消
    MB_YESNO 消息框包含两个按钮,是和否
    MB_RETRYCANCEL 消息框包含两个按钮,重试或取消

    消息框的图标:

    图标样式 效果
    MB_ICONWARNING 消息框中会显示一个感叹号图标
    MB_ICONINFORMATION 消息框会有一个圆圈,圆圈中间写着小写字母 i(意思为infomation,信息的意思)
    MB_ICONQUESTION 消息框会显示一个问号图标

    默认选中的按钮

    默认选中的按钮 效果
    MB_DEFBUTTON1 选中第一个按钮(默认值)
    MB_DEFBUTTON2 选中第二个按钮
    MB_DEFBUTTON3 选中第三个按钮
    MB_DEFBUTTON4 选中第四个按钮

    等很多功能,但都是以MB_开头,可根据字面意思猜测功能。

MessageBox的返回值为点击的按钮,都是以ID开头。例如IDOK(确认按钮),IDCANCEL(取消按钮)

使用案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
int main()
{
while (MBCANCEL != MessageBox(NULL, TEXT("是否重试?"), TEXT("Error"), MB_YSENO))
{
// 如果消息框想用多个效果,使用 | 运算符
MessageBox(NULL,
TEXT("正在重试"),
TEXT("fixing"),
MB_OK | MB_ICONINFOMATION | MB_DEFBUTTON1
);
}
return 0;
}

还有一个TEXT是windows提供的一个宏函数,会根据当前使用的字符集将”…”这样的字符串转换成TCHAR类型的字符串。因为Windows的API要根据不同的字符集调不同的函数,对于要调API的字符串我们可以用TEXT宏来包裹

定义的效果如下:

1
2
3
4
5
#ifdef UNICODE  // 如果使用UNICODE字符集
#define TEXT(quote) L##quote
#else
#define TEXT(quote) quote
#endif

刚才上文提到了项目使用的字符集,那个在哪里设置呢?

打开解决方案管理器(Solution Explorer)

对准你要设置字符集的项目按鼠标右键,看到菜单,然后点击 属性(Properties)

然后选择字符集(Character Set),在这里就可以设置你项目使用的字符集了。

本期到此结束,下期看星光探索者探索Windows常用基本数据类型和字符编码