创建一个窗口
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <tchar.h>
const TCHAR CLSNAME[] = TEXT("helloworldWClass");
LRESULT CALLBACK winproc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp);
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, PTSTR cmdline,
int cmdshow)
{
WNDCLASSEX wc = { };
MSG msg;
HWND hwnd;
wc.cbSize = sizeof (wc);
wc.style = 0;
wc.lpfnWndProc = winproc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor (NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = CLSNAME;
wc.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wc)) {
MessageBox(NULL, TEXT("Could not register window class"),
NULL, MB_ICONERROR);
return 0;
}
hwnd = CreateWindowEx(WS_EX_LEFT,
CLSNAME,
NULL,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInst,
NULL);
if (!hwnd) {
MessageBox(NULL, TEXT("Could not create window"), NULL, MB_ICONERROR);
return 0;
}
ShowWindow(hwnd, cmdshow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK winproc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
{
return DefWindowProc(hwnd, wm, wp, lp);
}
我们看到的第一件事是两个宏定义,UNICODE
和 _UNICODE
。这些宏使我们的程序理解宽字符串(wchar_t[n]
),而不是简单的窄字符串(char[n]
)。因此,所有字符串文字都必须包含在 TEXT(
宏中。Win32 字符串的通用字符类型是 TCHAR
,其定义取决于是否定义了 UNICODE
。包含一个新标题:<tchar.h>
包含 TCHAR
的声明。
窗口由所谓的窗口类组成。它描述了有关在其实例之间共享的窗口的信息,如图标,光标等。窗口类由窗口类名称标识,在此示例中,CLSNAME
全局变量中给出了该窗口类名称。WinMain
的第一个动作是填写窗口类结构 WNDCLASSEX wc
。成员是:
- cbSize:结构的大小(以字节为单位)
- style:窗口类样式。现在这是 0。
- lpfnWndProc:这是一个比较重要的领域。它存储窗口过程的地址。窗口过程是一个函数,它处理作为此窗口类实例的所有窗口的事件。
- cbClsExtra:为窗口类分配的额外字节数。在大多数情况下,此成员为 0。
- cbWndExtra:为每个窗口分配的额外字节数。不要将此与
cbClsExtra
混淆,后者对所有情况都很常见。这通常是 0。 - hInstance:实例句柄。只需将
WinMain
中的hInst
参数分配给该字段即可。 - hIcon:窗口类的图标句柄。
LoadIcon(NULL, IDI_APPLICATION)
加载默认的应用程序图标。 - hCursor:窗口类的光标句柄。
LoadCursor(NULL, IDC_ARROW)
加载默认光标。 - hbrBackground:背景画笔的句柄。
GetStockObject (WHITE_BRUSH)
给出了白色笔刷的句柄。必须转换返回值,因为GetStockObject
返回一个通用对象。 - lpszMenuName:要使用的菜单栏的资源名称。如果不需要菜单栏,则此字段可以为 NULL。
- lpszClassName:标识此窗口类结构的类名。在此示例中,
CLSNAME
全局变量存储窗口类名称。 - hIconSm:小类图标的句柄。
初始化此结构后,将调用 RegisterClassEx
函数。这会导致窗口类在 Windows 中注册,使应用程序知道它。失败时返回 0。
现在窗口类已经注册,我们可以使用 CreateWindowEx
显示窗口。论点是:
- stylesex:扩展窗口样式。默认值为 WS_EX_LEFT。
- clsname:类名
- cap:窗口标题或标题。在这种情况下,它是在窗口标题栏中显示的标题。
- styles:窗口样式。如果要创建像这样的顶级(父)窗口,则传入的标志是 WS_OVERLAPPEDWINDOW。
- x:窗口左上角的 x 坐标。
- y:窗口左上角的 y 坐标
- cx:窗口的宽度
- cy:窗口的高度
- hwndParent:父窗口的句柄。由于此窗口本身就是父窗口,因此该参数为 NULL。
- hMenuOrID:如果正在创建的窗口是父窗口,则此参数是窗口菜单的句柄。不要把它与类菜单混为一谈,这就是
WNDCLASSEX::lpszClassName
。类菜单对于具有相同类名的所有窗口实例都是通用的。但是,这个论点仅适用于此实例。如果正在创建的窗口是子窗口,则这是子窗口的 ID。在这种情况下,我们创建一个没有菜单的父窗口,因此传递 NULL。 - hInst:应用程序实例的句柄。
- etc:传递给窗口窗口过程的额外信息。如果不传输额外信息,则传递 NULL。
如果 x
或 y
或 cx
或 cy
是 CW_USEDEFAULT
,则该参数的值将由 Windows 确定。这是在这个例子中完成的。
CreateWindowEx
将句柄返回到新创建的窗口。如果窗口创建失败,则返回 NULL
。
然后我们通过调用 ShowWindow
来显示窗口。该函数的第一个参数是窗口的句柄。第二个参数是 show style,它指示窗口的显示方式。大多数应用程序只传递 WinMain
中传递的 cmdshow
参数。显示窗口后,必须通过调用 UpdateWindow
更新它。它会将更新消息发送到窗口。我们将在另一个教程中了解这意味着什么。
现在是应用程序的核心:消息泵。它泵送操作系统发送到此应用程序的消息,并将消息分派给窗口过程。GetMessage
调用返回非零,直到应用程序收到导致它退出的消息,在这种情况下它返回 0.唯一涉及我们的参数是指向 MSG
结构的指针,该结构将填入有关消息的信息。其他参数都是 0。
在消息循环中,TranslateMessage
将虚拟密钥消息转换为字符消息。这再次对我们来说并不重要。它需要一个指向 MSG
结构的指针。直接跟随它的调用 DispatchMessage
将其参数指向的消息调度到窗口的窗口过程。WinMain
必须做的最后一件事是返回一个状态代码。MSG
结构的 wParam
成员包含此返回值,因此返回。
但这仅仅适用于 WinMain
功能。另一个功能是 winproc
,窗口程序。它将处理 Windows 发送给它的窗口的消息。winproc
的签名是:
- hwnd:正在处理其消息的窗口句柄。
- wm:窗口消息标识符
- wp:消息信息参数之一。这取决于
wm
的论点 - lp:消息信息参数之一。这取决于
wm
的论点。此参数通常用于传输指针或句柄
在这个简单的程序中,我们自己不处理任何消息。但这并不意味着 Windows 也没有。这就是为什么必须调用包含默认窗口处理代码的 DefWindowProc
的原因。必须在每个窗口过程结束时调用此函数。
什么是手柄?
一个手柄是代表一个唯一的对象的数据类型。它们是指针,但是由操作系统维护的秘密数据结构。这些结构的细节不需要我们关注。用户需要做的只是使用 API调用创建/检索句柄,并将其传递给采用该类型句柄的其他 API 调用。我们使用的唯一一种手柄是 CreateWindowEx
返回的 HWND
。
常量
在这个例子中,我们遇到了一些常量,这些常量都是全部大写,并以 2 或 3 个字母前缀开头。 (Windows 类型也是全部大写)
- IDI_APPLICATION:包含默认应用程序图标的资源名称。它与
LoadIcon
或LoadImage
(本例中为 LoadIcon)一起使用。 - IDC_ARROW:计算默认应用程序游标的资源名称。它与
LoadIcon
或LoadImage
(本例中为 LoadIcon)一起使用。 - WHITE_BRUSH:股票对象的名称。这个库存对象是白色画笔。
- MB_ICONERROR:与
MessageBox
一起使用的标志,用于显示错误图标。 - WS_EX_LEFT:默认的扩展窗口样式。这会导致窗口具有左对齐属性。
- WS_OVERLAPPEDWINDOW:一种窗口样式,指示窗口应该是具有标题栏,大小框和顶级窗口典型的其他元素的父窗口。
- CW_USEDEFAULT:与
CreateWindowEx
的x
,y
,cx
或cy
参数一起使用。使 Windows 为传递CW_USEDEFAULT
的参数选择有效值。
Windows 类型
在为 Windows 编程时,你将不得不习惯 Win32 类型,它们是内置类型的别名。这些类型都是全部大写。此程序中使用的别名类型是:
- TCHAR:通用字符类型。如果定义了
UNICODE
,这是一个wchar_t
。其他,这是一个char
。 - UINT:无符号整数。用于表示窗口过程中的消息标识符以及其他用途。
- WPARAM:在 Win16 中,这是一个 WORD 参数(因此是
W
前缀)。然而,随着 Win32 的推出,现在这已经成为了一个传播。这说明了这些 Windows 别名的重点; 他们在那里保护计划免受变化。 - LPARAM:这是一个
LONG
参数(Win64 中的LONG_PTR
)。 - PTSTR:
P
表示指针。T
表示通用字符,STR
表示字符串。因此,这是一个指向TCHAR
字符串的指针。其他字符串类型包括:- LPTSTR:与
PTSTR
相同 - LPCTSTR:意为
const TCHAR *
- PCTSTR:与
LPCTSTR
相同 - LPWSTR:宽字符串(
wchar_t *
) - LPCWSTR:意为
const wchar_t *
- PWSTR:与
LPWSTR
相同 - 还有更多正如你所看到的,Win32 类型可能很难理解,特别是有很多同义类型,这是 Win16 的工件。
- LPTSTR:与
- LRESULT:此类型用于表示窗口过程的返回值。它通常是一个长期(因此
L
)。