WIN32编程
WIN32编程
APIENTRY WinMain 和WINAPI WinMain的区别
在 Windows 编程中,WinMain
是 Windows 图形用户界面(GUI)应用程序的入口点(Entry Point)。在不同的编译器和工具集中,函数的声明可能略有不同,但通常有两种常见的声明方式:
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR lpCmdLine, int nCmdShow)
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
这两者之间的主要区别在于调用约定(Calling Convention):
APIENTRY
是一个宏,它会根据编译器和目标平台选择正确的调用约定。在 Windows 上,它通常被定义为__stdcall
。__stdcall
是一种标准的调用约定,它规定了参数的传递方式和堆栈的清理方式。WINAPI
是另一个宏,它也通常被定义为__stdcall
。在实际使用中,APIENTRY
和WINAPI
可以互换使用,它们都表示使用标准的__stdcall
调用约定。
所以,这两个函数声明在实际使用中基本上是等效的,你可以根据个人或项目的偏好选择其中一个使用。通常来说,WINAPI
是更常见的使用方式。
win32编程为什么叫win32
“Win32” 是指 Windows 32-bit 的编程模型。它起源于早期的 Windows 操作系统版本,当时 Windows 主要运行在 32 位的处理器架构上。”Win32” 编程通常涉及使用 Windows API(应用程序编程接口)来创建图形用户界面(GUI)应用程序。
Windows API 提供了一组函数和数据结构,允许开发者创建和管理窗口、处理用户输入、绘制图形等。在 32 位架构中,这些 API 被称为 “Win32 API”,而对应的编程模型就被称为 “Win32 编程”。
随着时间的推移,Windows 操作系统逐渐演变,引入了新的架构和功能,包括 64 位架构、UWP(Universal Windows Platform)等。因此,”Win32” 编程的术语在某种程度上成为一个历史遗留的称呼,用来指代传统的 32 位 Windows 编程模型。但即便在现代 Windows 开发中,”Win32” 仍然被广泛使用,尤其是在传统的桌面应用程序开发中。
CreateWindowEx和CreateWindow的区别是什么
CreateWindowEx
和 CreateWindow
是 Windows API 中用于创建窗口的两个函数,它们之间的主要区别在于 CreateWindowEx
允许你指定扩展的窗口样式,而 CreateWindow
则仅支持基本的窗口样式。
CreateWindowEx:
1 | cCopy codeHWND CreateWindowEx( |
dwExStyle
允许你指定扩展样式,如 WS_EX_TOPMOST、WS_EX_TOOLWINDOW 等。- 支持更多的窗口样式和扩展功能。
CreateWindow:
1 | cCopy codeHWND CreateWindow( |
- 不支持扩展样式(没有
dwExStyle
参数)。 - 用于创建基本的窗口。
通常情况下,如果你的窗口需要使用一些扩展样式,例如创建工具栏窗口、置顶窗口等,你会选择使用 CreateWindowEx
。如果你的窗口只需要基本的窗口样式,那么使用 CreateWindow
即可。
在实际使用中,两者的选择取决于你的窗口的需求和设计。
也就是说EX代表的是扩展的意思,具有更多功能相对于本身的函数来说。
::MessageBox为什么前面有两个冒号
在 C++ 中,::
是作用域解析运算符(Scope Resolution Operator)。它用于指明使用全局命名空间中的符号,或者指定某个命名空间的成员。
::MessageBox
中的 ::
表示调用全局命名空间中的 MessageBox
函数。在 Windows 编程中,MessageBox
函数用于显示消息框。通过使用作用域解析运算符,你可以确保调用的是全局命名空间中的函数,而不是某个局部命名空间中可能存在的同名函数。
举个例子,如果你在某个命名空间中定义了自己的 MessageBox
函数,那么在调用时如果使用 ::MessageBox
,就能够确保调用的是全局命名空间中的 MessageBox
,而不会产生命名冲突。
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR lpCmdLine, int nCmdShow) 参数说明解释一下
WinMain
是 Windows GUI 应用程序的入口点,它接收并处理应用程序的启动参数,初始化应用程序的主窗口,然后进入消息循环以响应用户输入。下面是对 WinMain
函数参数的简要解释:
hInstance (HINSTANCE):
- 描述:应用程序实例的句柄。
- 解释:
hInstance
是当前应用程序实例的标识符,它在应用程序启动时由操作系统传递。这个句柄用于标识应用程序的内存空间和其他资源。
hPrevInstance (HINSTANCE):
- 描述:先前实例的句柄。
- 解释:在现代 Windows 中,这个参数通常被忽略,因为多数 Windows 程序都是单实例的。这个参数的存在是为了兼容旧的 16 位 Windows 应用程序。
lpCmdLine (LPSTR):
- 描述:命令行参数。
- 解释:
lpCmdLine
包含了启动应用程序时传递的命令行参数。这是一个以空格分隔的字符串,可以包含应用程序需要的任何信息。需要注意,这是一个以空格为分隔符的单个字符串,而不是一个字符串数组。
nCmdShow (int):
描述:窗口显示状态。
解释:
1
nCmdShow
用于指定应用程序窗口的初始显示状态。它可以是以下常量之一:
SW_HIDE
: 隐藏窗口。SW_SHOWNORMAL
: 显示窗口,并将其激活。SW_SHOWMINIMIZED
: 显示为最小化窗口。SW_SHOWMAXIMIZED
: 显示为最大化窗口。SW_SHOWNOACTIVATE
: 显示窗口,但不将其激活。SW_RESTORE
: 激活并显示窗口。如果窗口最小化或最大化,将恢复其原始大小和位置。SW_SHOWDEFAULT
: 使用操作系统默认显示状态。
WinMain
的返回值通常是程序的退出代码。在 Windows 中,一般约定返回 0 表示程序正常退出。
HWND hWnd = ::FindWindow(nullptr, TEXT(“无标题 - 记事本”)); 解释一下这段代码并且列举出FindWindow函数的参数
这段代码使用了 Windows API 中的 FindWindow
函数来查找指定类名和窗口标题的顶层窗口,并将找到的窗口句柄存储在 hWnd
变量中。以下是对代码和 FindWindow
函数参数的解释:
代码解释:
1 | cppCopy code |
HWND hWnd
:- 描述:窗口句柄。
- 解释:
hWnd
是一个句柄变量,用于存储找到的窗口的句柄。HWND
是 Windows 中表示窗口句柄的数据类型。
::FindWindow(nullptr, TEXT("无标题 - 记事本"))
:描述:调用
FindWindow
函数来查找指定窗口。解释:
1
FindWindow
函数用于在桌面窗口中查找符合指定类名和窗口标题的顶层窗口。在这里:
- 第一个参数
nullptr
表示没有指定窗口类名,即不限制窗口的类。 - 第二个参数是窗口标题,这里是 “无标题 - 记事本”。通过这两个参数,
FindWindow
将尝试找到符合条件的顶层窗口。
- 第一个参数
FindWindow 函数的参数:
1 | cppCopy codeHWND FindWindow( |
lpClassName
:- 描述:窗口类名。
- 解释:如果不关心类名,可以将其设为
nullptr
。否则,可以指定要查找的窗口类名。
lpWindowName
:- 描述:窗口标题。
- 解释:指定要查找的窗口的标题。可以使用部分标题文本来匹配窗口。
总体思路: 通过 FindWindow
函数,在桌面上查找指定类名(或不限制类名)和指定标题的顶层窗口,如果找到,则返回该窗口的句柄给 hWnd
变量。如果没有找到匹配的窗口,hWnd
将被设为 nullptr
。
注册窗口类
注册窗口类使用函数RegisterClassEx,一个窗口类定义了窗口的一些主要属性,如图标、光标、背景色和负责处理消息的窗口函数等。这些属性定义在WNDCLASSEX结构中。
1 | typedef struct _WNDCLASSEX{ |
(1) 指定窗口类的风格
1 | wndClass.style = CS_HREDRAW | CS_VREDRAW; // 指定如果大小改变就重画 |
前缀CS_意为class style,在WINUSER.H中定义了全部可选样式。
1 |
(2)指定窗口处理函数地址
1 | wndClass.lpfnWndProc = MainWindProc; |
WNDCLASSEX 结构成员lpfnWndProc 指定了基于此类窗口的窗口函数。当窗口收到消息时Windows即自动调用这个函数通知应用程序。
(3) 本程序的实例句柄传给hInstance成员
1 | wndClass.hInstance = hInstance; |
(4) 设置图标和光标
1 | wndClass.hIcon = ::LoadIcon(nullptr, IDI_APPLICATION); |
LoadIcon 函数装载了一个预定义图标,命名为 IDI_APPLICATION
LoadCursor 函数装载了一个预定义的光标,命名为 IDC_ARROW
(5)指定窗口重画客户区画刷
1 | wndClass.hbrBackground = (HBRUSH)::GetStockObject(WHITE_BRUSH); // 使用白色画刷 |
(6)指定窗口类名称
1 | wndClass.lpszClassName = TEXT("MainWClass"); // 窗口类名称 |
填充完WNDCLASSEX 结构,就可以进行注册了。RegisterClassEx 函数调用失败将返回0
1 | ::RegisterClassEx(&wndClass); |
创建窗口
要创建窗口,使用函数 CreateWindowEx 。
1 | HWND hwnd = ::CreateWindowEx(\ |
函数调用成功将返回窗口句柄,失败返回nullptr。
第四个参数dwStyle的值是 WS_OVERLAPPEDWINDOW,即重叠窗口。由他指定的窗口有标题栏、系统菜单、可以改变大小的边框,以及最大化、最小化和关闭按钮。这是一个标准的窗口样式。下面是一些常见风格定于:
- WS_BORDER 创建一个单边框窗口
- WS_CAPTION 创建一个有标题框的窗口
- WS_CHID 创建一个子窗口。这个风格不能与WS_POPVP合用
- WS_DISABLED 创建一个初始状态为禁止的子窗口。
- WS_DLGFRAME 创建一个带对话框边框风格的窗口,这种风格的窗口不能带标题条
- WS_HSCROLL 创建一个有水平滚动条的窗口
- WS_VSCROLL 创建一个有垂直滚动条的窗口
- WS_ICONIC 创建一个初始状态为最小化状态的窗口,与WS_MINIMIZE风格相同
- WS_MAXIMIZE 创建一个具有最大化按钮的窗口。
- WS_OVERLAPPED 产生一个层叠的窗口。
- WS_OVERLAPPEDWINDOW 创建一个具有WS_OVERLAPPED、WS_CAPTION、
- WS_SYSMENU、WS_THICKFRAME、WS_MINIMZEBOX、WS_MAXMIZEBOX 风格的层叠窗口。
- WS_POPUP 创建一个弹出式窗口
- WS_SIZEBOX 创建一个可调边框的窗口。
- WS_SYSMENU 创建一个在标题条上带有窗口菜单的窗口。
- WS_THICKFRAME 创建一个具有可调边框的窗口
- WS_VISIBLE 创建一个初始状态为可见的窗口
进入无限的消息循环
Windows为每一个线程维护一个消息队列,每当有一个输入发生,Windows就把用户输入翻译成消息放在消息队列中。GetMessage 函数可以从消息队列中取一个消息填充MSG结构。
如果消息队列中没有消息,这个函数会一直等下去,直到有消息进入消息队列为止。
1 | typedef struct tagMSG { |
GetMessage 函数从消息队列中取得的消息如果不是WM_QUIT ,则返回非零值。一个WM_QUIT 消息会使GetMessage 函数返回0,从而消息循环结束。
1 | ::TranslateMessage(&msg); |
此调用把键盘输入翻译成可调用的消息。
1 | ::DispatchMessage(&msg); |
DispatchMessage 函数分发一个消息到对应窗口的窗口函数。MainWndProc 处理消息后把控制权交给Windows,此时DispatchMessage 函数仍然继续工作,当他返回时,消息队列从调用GetMessage 函数开始进入下一轮。
处理消息代码
所有消息不做处理的消息都必须返回一个名为DefWindowProc的函数让Windows做默认处理,从DefWindowProc 函数返回的值也必须从消息处理函数返回。
每当客户区变为无效,消息处理函数就会收到一个新的WM_PAINT 消息。
WM_DESTORY 是窗口必须处理的一个消息,当用户关闭窗口时,消息处理函数就会收到一 个 WM_DESTORY消息。当接收到这个消息的时候,说明窗口正在销毁。
1 | ::PostQuitMessage(0); |
PostQuitMessage函数会会向消息队列中插入一个 WM_QUIT 消息。GetMessage 函数如果从消息队列中获得到消息时 WM_QUIT ,它将返回0。从而退出消息循环。如果不使用函数 PostQuitMessage 发送WM_QUIT 消息,则界面虽然被销毁了,但是消息循环还在继续,程序还没有结束。