创建一个窗口

#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。

如果 xycxcyCW_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:包含默认应用程序图标的资源名称。它与 LoadIconLoadImage(本例中为 LoadIcon)一起使用。
  • IDC_ARROW:计算默认应用程序游标的资源名称。它与 LoadIconLoadImage(本例中为 LoadIcon)一起使用。
  • WHITE_BRUSH:股票对象的名称。这个库存对象是白色画笔。
  • MB_ICONERROR:与 MessageBox 一起使用的标志,用于显示错误图标。
  • WS_EX_LEFT:默认的扩展窗口样式。这会导致窗口具有左对齐属性。
  • WS_OVERLAPPEDWINDOW:一种窗口样式,指示窗口应该是具有标题栏,大小框和顶级窗口典型的其他元素的父窗口。
  • CW_USEDEFAULT:与 CreateWindowExxycxcy 参数一起使用。使 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 的工件。
  • LRESULT:此类型用于表示窗口过程的返回值。它通常是一个长期(因此 L)。