LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
......
}
一、LRESULT(Long RESULT)是Win32 API中用于表示消息处理函数返回值的类型。在Windows编程中,当窗口过程(Window Procedure)处理了一个窗口消息后,它通常会返回一个LRESULT值。这个返回值用于指示消息处理的结果,例如,如果是处理一个键盘消息,LRESULT可能会返回一个表示字符代码的值。LRESULT通常被定义为32位的长整数(long),这样可以保证即使在64位操作系统上,LRESULT也保持32位的大小,以保持与32位应用程序的兼容性。
二、在Windows编程中,LPARAM
(Long PARAMeter)和WPARAM
(Word PARAMeter)是两种数据类型,用于在Win32 API中传递消息参数。这些参数通常用于窗口过程函数(Window Procedure)中,以传递与特定窗口消息相关的信息。
1.WPARAM
: 通常用于传递消息的辅助信息,如键盘的虚拟键代码、鼠标按钮状态、通知码等。WPARAM
是一个无符号的16位整数(WORD
),但在32位程序中,它被扩展为32位(DWORD
)以保持一致性。在64位Windows系统中,WPARAM
仍然是一个32位的无符号整数(UINT
),即使是在64位的应用程序中。这是为了保持与32位Windows应用程序的兼容性,因为Windows API在32位和64位系统中保持了一致性。
【问:WPARAM
在如今的32位系统中和64位系统中都为32位,可是现在很少人会用16位系统?那为什么不直接说WPARAM是一个32位的参数?】
【答:是的,目前16位操作系统已经很少使用了,而且现代的编程实践中几乎都是在32位或64位环境下进行的。但是在早期的16位Windows系统中,WPARAM
确实是一个16位的参数。随着Windows操作系统的发展,为了保持向后兼容性,在32位和64位系统中,WPARAM
为32位,但WPARAM
的定义宏没有被修改。而且许多现有的代码库、文档和教程可能会继续提到WPARAM
的16位历史。此外,Windows API的许多部分都非常古老,并且自从它们最初被设计以来就没有改变过,以保持旧应用程序的兼容性。因此,即使WPARAM
在32位和64位系统中实际上是一个32位的参数,它仍然被介绍为是一个无符号的16位整数,这是为了与旧的文档和代码保持一致,以及为了区分它与其他参数类型,如LPARAM
和 LRESULT
。】
【问:WPARAM为什么要扩展成32位的?因为32位系统通常比16位系统性能更好,所以WPARAM扩展成32位是不是性能会更好?】
【答:WPARAM
在32位系统中被“扩展”成32位时,指的是它的宽度从原来的16位增加到32位。这种扩展并不能直接提高性能,而是为了保持与旧代码的兼容性,并允许在新的系统中传递更多的信息。在16位Windows系统中,WPARAM
实际上是一个16位的 WORD
类型。随着32位Windows系统的引入,为了保持API的一致性和向后兼容性,WPARAM
被扩展为32位的 UINT
类型。这样,即使是在32位或64位系统中,旧的16位应用程序仍然可以正常运行,因为它们使用的 WPARAM
参数会被自动转换为新的大小。WPARAM
需要扩展为32位是因为在32位Windows操作系统中,所有的消息参数都被统一为32位宽度,以保持一致性和简化消息传递机制。虽然32位应用程序仍然支持16位整数类型的使用,但是在Win32 API中,WPARAM
和 LPARAM
被定义为32位类型(UINT
和 LONG
),这是为了适应更广泛的数据传递需求。】
【问:既然将WPARAM扩展为32位是为了旧的16位应用程序可以在32位系统上正常运行,那么为什么32位应用程序不需要在64位系统上被扩展为64位?】
【32位应用程序在64位系统上不需要被扩展,因为64位Windows操作系统提供了所谓的“-wow64”(Windows 32-bit on Windows 64-bit)兼容层。这个兼容层允许32位应用程序在64位版本的Windows上无缝运行。它负责将32位应用程序的指令和数据转换为64位操作系统的格式,同时也处理32位和64位地址空间之间的转换。(Wow64兼容层会使32位应用程序在64位Windows操作系统上运行时引入一定的性能开销。这是因为32位应用程序通过Wow64执行时,需要进行额外的地址空间转换和模拟操作,这些操作虽然经过优化,但仍然无法与原生的64位应用程序相提并论。)】
更多杂问:
【问:为什么当初微软没有搞32位操作系统兼容16位应用程序的兼容层?】
【微软在推出32位操作系统时,确实考虑到了向后兼容性问题,特别是对于16位应用程序的兼容性。实际上,Windows 95和后来的Windows操作系统(如Windows 98、Windows NT、Windows 2000等)都包含了一种名为“虚拟86”的模式,它允许在32位操作系统上运行16位应用程序。这种模式模拟了16位的环境,使得16位应用程序可以在32位系统上运行。然而,这种兼容性并不是通过一个完整的“兼容层”来实现的,而是通过操作系统的双重性质来实现的。Windows 95和98等操作系统在内核级别同时支持16位和32位应用程序,这是因为它们是基于MS-DOS和16位Windows的混合体。这些操作系统的设计允许它们在需要时切换到16位模式来运行旧的16位应用程序。】
【问:有WIN32API是不是还有WIN16API? 怎么好像没听说过WIN64API?】
【是的,存在所谓的Win16 API,它是指16位Windows操作系统的应用程序编程接口。Win16 API是在早期的Windows 3.x系列操作系统中使用的,这些操作系统运行在16位处理器上,如Intel 8086和80286。Win16 API提供了一套用于开发16位Windows应用程序的函数和接口。另外实际上,随着64位处理器的普及和64位操作系统的出现,Microsoft确实引入了64位的API,但这些API通常被视为Win32 API的扩展,而不是一个全新的API。64位Windows操作系统上的应用程序仍然使用Win32 API,但是这些API在64位环境下能够利用更多的寄存器和更大的内存地址空间。】
2.LPARAM
: 用于传递更复杂或更大范围的信息,如指针、句柄、偏移量等。LPARAM
是一个有符号的32位整数(LONG
),即使在64位程序中,它也保持32位大小,以确保与32位程序兼容。
【问:为什么在16位的操作系统中,能够支持32位的LPARAM?】
【答:在16位操作系统中,LPARAM
是一个32位的参数,这并不是因为它实际上需要32位的大小,而是因为它是16位Windows API的一部分,而这个API是在16位架构上设计的。在16位系统中,LPARAM
被定义为32位的 LONG
类型,这主要是因为16位Windows API的设计和规范。在设计16位Windows API时,可能考虑到了未来的扩展性,因此将LPARAM
定义为32位,以避免未来可能需要增加的参数大小限制。需要注意的是,尽管LPARAM
在16位系统中定义为32位,但这并不意味着它可以传递真正的32位数据。在16位系统中,所有的数据都是按照16位的大小进行处理的,包括LPARAM
。如果LPARAM
中包含了一个32位的值,它会被存储在两个连续的16位寄存器中,或者被转换为一个32位的值。】
【问:那为什么当初的WPARAM不设计为32位?】
【答:在早期16位Windows操作系统中,WPARAM
设计为16位是因为它主要用于传递一些简单的消息辅助信息,例如键盘的虚拟键代码、鼠标按钮状态等。这些信息通常不需要32位的大小来表示。(早期Windows API的设计哲学是尽可能简单和易于使用。通过使用16位的WPARAM
,开发者可以更容易地理解消息参数的含义和处理方式。)】
三、在Windows API中,UINT
是一个无符号整数类型,它被定义为16位或32位的大小,具体取决于操作系统版本和上下文。在早期的16位Windows操作系统中,UINT
通常被定义为16位,而在现代的32位和64位Windows操作系统中,UINT
被定义为32位。
【问:UINT 是一个无符号整数类型,那我不能直接用C语言自带的整数类型直接替代它么?】
【答:虽然你可以在C语言中使用标准库提供的无符号整数类型来替代 UINT
,但在Windows API中,UINT
有一些特定的用途和含义,这使得它不适合简单地用标准库的无符号整数类型来替换。
1.在不同的Windows版本中,UINT
被定义为不同的大小,以保持与旧代码的兼容性。在16位Windows系统中,UINT
通常是16位的,而在32位和64位Windows系统中,UINT
通常是32位的。
2.UINT
是Windows API的一部分,它被广泛用于Windows API的函数和结构中。使用标准库的无符号整数类型可能会导致与Windows API的不兼容性。
3.UINT
在Windows API中用于特定的用途,如传递消息参数、句柄、标识符等。这些用途可能需要特定的类型大小和行为,而标准库的无符号整数类型可能无法完全满足这些需求。】
四、在Windows编程中,HWND
是一个缩写,代表“handle to a window”。它是Windows API中用于表示窗口句柄的一个类型。窗口句柄是一个特殊的整数,用于唯一标识一个窗口。每个窗口在系统中都有一个唯一的 HWND
,就像一个身份证号码一样。
五、在Windows编程中,ShowWindow
函数用于控制窗口的显示状态。它接受两个参数:
ShowWindow(hwnd, nCmdShow);
1.hwnd:这是窗口的句柄,一个整数,用于标识窗口。
2.nCmdShow:这是一个枚举值,用于指定窗口的显示状态。它可以是以下值之一:
SW_SHOWNORMAL:窗口出现在屏幕上,并且有正常大小和位置。
SW_SHOWMINIMIZED:窗口最小化后出现在任务栏上。
SW_SHOWMAXIMIZED:窗口最大化后出现在屏幕上。
SW_SHOWNOACTIVATE:窗口出现在屏幕上,但不激活它。
SW_SHOW:窗口出现在屏幕上,并且有正常大小和位置。如果窗口当前是隐藏的,它将恢复到上次的状态。
SW_MINIMIZE:窗口最小化。
SW_SHOWMINNOACTIVE:窗口最小化,但不被激活。
SW_SHOWNA:窗口出现在屏幕上,但不激活它。不会改变窗口的状态,也不会显示在任务栏上。
SW_RESTORE:窗口被还原到正常大小和位置,并且成为活动窗口。
SW_SHOWDEFAULT:用于MDI应用程序,表示如果父窗口是最大化的,则激活子窗口;如果父窗口是激活的,则最小化子窗口。
【当你使用 ShowWindow
函数时,它只是告诉操作系统你想要显示窗口,但并不保证窗口的内容会立即更新到屏幕上。例如,如果你使用 SW_SHOW
参数显示一个隐藏的窗口,窗口将会出现在屏幕上,但窗口的内容可能不会立即更新,因为它可能需要重绘。】
六、在Windows编程中,UpdateWindow
函数用于强制重绘指定窗口的客户区域。当窗口的客户区域需要更新时,使用 UpdateWindow
函数可以确保所有变化被立即反映到屏幕上。
hwnd:这是窗口的句柄,一个整数,用于标识窗口。
UpdateWindow(hwnd); // 确保窗口内容立即更新到屏幕
七、在Windows编程中,MSG
是一个结构体,它用于表示消息。每个消息都包含了一系列的参数,这些参数描述了消息的类型、发送者和接收者等信息。
以下是 MSG
结构体的定义:
typedef struct tagMSG {
HWND hwnd; // 消息发送到的窗口句柄
UINT message; // 消息的类型
WPARAM wParam; // 消息的第一个参数,通常是辅助信息
LPARAM lParam; // 消息的第二个参数,通常是附加信息
DWORD time; // 消息的时间戳
POINT pt; // 鼠标位置
} MSG, *PMSG;
八、在Windows编程中,HINSTANCE
是一个指针类型,它代表一个Windows实例句柄。每个Windows应用程序都至少有一个实例句柄,这个句柄是在应用程序启动时由操作系统分配的,用于标识应用程序的实例。
关于HINSTANCE
:
唯一标识:HINSTANCE
用于唯一标识一个Windows应用程序的实例。每个应用程序实例都有其唯一的 HINSTANCE
句柄。
全局访问:HINSTANCE
句柄可以在整个应用程序中全局访问,用于在不同函数之间传递应用程序实例的引用。
窗口创建:在创建窗口时,HINSTANCE
句柄被用作窗口创建函数(如 CreateWindowEx
或 CreateWindow
)的参数之一,以确保新创建的窗口属于正确的应用程序实例。
资源访问:HINSTANCE
句柄还可以用于访问应用程序的资源,如图标、光标、菜单等。在Windows API中,资源通常与应用程序实例相关联,因此需要 HINSTANCE
句柄来加载这些资源。
模块操作:HINSTANCE
句柄还可以用于操作应用程序的模块,如动态链接库(DLLs)。在Windows API中,模块通常与应用程序实例相关联,因此需要 HINSTANCE
句柄来加载或卸载模块。
全局函数和数据:在某些情况下,HINSTANCE
句柄可以用于访问全局函数和数据,这些函数和数据在整个应用程序中是唯一的。
九、在Windows编程中,LPSTR
是一个缩写,代表“long pointer to a string”。它是C语言中的一个指针类型,指向一个字符串,即以空字符('\0'
)结尾的字符数组。
关于LPSTR
:
指向字符串:LPSTR
是一个指针,它指向一个字符串,即一个字符数组。这个字符数组以空字符('\0'
)结尾,表示字符串的结束。
无长度限制:LPSTR
没有指定字符串的长度,这使得它可以指向任意长度的字符数组。
Windows API使用:在Windows API中,LPSTR
通常用于传递字符串参数,如命令行参数、文件路径等。
与 LPCSTR
的区别:LPSTR
和 LPCSTR
都是指针类型,它们都可以指向字符串。LPSTR
是未初始化的指针,而 LPCSTR
是常量指针,表示指向一个只读字符串。
与 LPWSTR
的区别:LPSTR
用于指向8位字符(ASCII码)的字符串,而 LPWSTR
用于指向宽字符(Unicode)的字符串。
十、在Windows编程中,WINMain
函数是Windows应用程序的主入口点,它接受以下五个参数:
hInstance:这是应用程序的实例句柄,由操作系统在启动应用程序时分配。这个句柄用于标识应用程序的实例,并在整个应用程序中保持不变。
hPrevInstance:这是上一个运行的相同应用程序实例的句柄。如果应用程序是首次运行,这个参数通常为NULL
。
lpCmdLine:这是一个指向命令行字符串的指针,包含启动应用程序时传递给它的命令行参数。这个参数是一个字符串,以空字符('\0'
)结尾。
nCmdShow:这是一个整数,用于指定窗口应该如何显示。它可以是SW_SHOWNORMAL
、SW_SHOWMINIMIZED
、SW_SHOWMAXIMIZED
等值,用于控制窗口的初始显示状态。
lpReserved:这是一个保留参数,通常设置为NULL
。(这个参数是为了向后兼容而保留的,最初是为了支持多实例应用程序的创建,但现在它已经被废弃,并且不再是多实例应用程序的必需参数。)
最基本的windows窗口(可选择GB 2312的文件编码防止乱码):
//引入使用WindowsAPI所需的头文件
#include <windows.h>
//窗口过程函数【接受窗口句柄,消息,辅助消息,辅助信息】
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
//根据消息类型选择处理方式
switch (uMsg) {
//当窗口被销毁时使用
case WM_DESTROY:
//发出退出消息循环的消息
PostQuitMessage(0);
//返回0表示消息已被处理
return 0;
}
//没有与消息类型匹配的处理方式,则调用默认的窗口过程,让消息由操作系统处理(系统的预设操作)
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
//主窗口函数【操作系统分配的实例句柄,上次运行相同应用程序实例的句柄(首次则为NULL),指向命令行字符串的指针,
// 指定窗口如何显示】
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
//初始化WNDCLASS所有成员变量
WNDCLASS wc = {0};
//分配实例句柄
wc.hInstance = hInstance;
//设置窗口名称
wc.lpszClassName = "MyWindowClass";
//设置窗口过程函数
wc.lpfnWndProc = WindowProc;
//注册窗口类
RegisterClass(&wc);
//创建窗口
//【窗口类名称,窗口标题,窗口样式,窗口位置及大小,父窗口句柄,窗口菜单句柄,
// 窗口实例句柄,窗口附加创建数据】
HWND hwnd = CreateWindowEx(
0,
"MyWindowClass",
"我的窗口",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
//如果窗口创建失败,返回-1
if (hwnd == NULL) {
return -1;
}
//显示窗口
ShowWindow(hwnd, nCmdShow);
//更新窗口
UpdateWindow(hwnd);
//初始化MSG
MSG msg = {0};
//消息循环
while (GetMessage(&msg, NULL, 0, 0)) {
//将消息转换为字符输入消息
TranslateMessage(&msg);
//将消息分发到窗口过程函数
DispatchMessage(&msg);
}
//将wParam的值作为整数值返回。
return (int)msg.wParam;
}
(若在vscode里运行需要在task.json文件的arg里添加"-mwindows")
(哥们学累了浅浅放张图休息休息)