Win32 API 创建一个窗口

示例

#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:实例句柄。只需将hInst参数分配WinMain给该字段即可。

  • 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:窗口标题或标题。在这种情况下,标题会显示在窗口的标题栏中。

  • 样式:窗口样式。如果要创建像这样的顶级(父)窗口,则要传入的标志是WS_OVERLAPPEDWINDOW。

  • x:窗口左上角的x坐标。

  • y:窗口左上角的y坐标

  • cx:窗口的宽度

  • cy:窗口的高度

  • hwndParent:父窗口的句柄。由于此窗口本身是父窗口,因此此参数为NULL。

  • hMenuOrID:如果正在创建的窗口是父窗口,则此参数是窗口菜单的句柄。请勿将其与类菜单混淆WNDCLASSEX::lpszClassName。类菜单对于具有相同类名称的所有Windows实例都是通用的。但是,此参数仅适用于此实例。如果正在创建的窗口是子窗口,则这是子窗口的ID。在这种情况下,我们将创建一个没有菜单的父窗口,因此将传递NULL。

  • hInst:应用程序实例的句柄。

  • 等:传递给窗口的窗口过程的额外信息。如果没有其他信息要传输,则传递NULL。

如果xory或cxorcy为CW_USEDEFAULT,则该参数的值将由Windows确定。这就是本例中所做的。

CreateWindowEx将句柄返回到新创建的窗口。如果窗口创建失败,则返回NULL。

然后,我们通过调用显示窗口ShowWindow。该函数的第一个参数是窗口的句柄。第二个参数是显示样式,它指示如何显示窗口。大多数应用程序只是传递传入的cmdshow参数WinMain。显示该窗口后,必须通过调用来更新它UpdateWindow。它导致将更新消息发送到窗口。我们将在另一个教程中了解这意味着什么。

现在是应用程序的核心:消息泵。它抽取操作系统发送给该应用程序的消息,并将消息分派到窗口过程。的GetMessage,直到应用程序receieves导致它退出一个消息,在这种情况下返回0的是涉及的是指向一个的唯一参数调用返回非零MSG,将在与有关邮件的信息被填充结构。其他参数均为0。

在消息循环内,TranslateMessage将虚拟键消息转换为字符消息。同样,这对我们来说并不重要。它需要一个指向MSG结构的指针。直接在其后面的调用DispatchMessage将其参数所指向的消息调度到窗口的窗口过程。最后WinMain要做的就是返回状态码。结构的wParam成员MSG包含此返回值,因此将其返回。

但这仅用于WinMain功能。另一个功能是winproc窗口程序。它将处理Windows发送给该窗口的消息。的签名winproc是:

  • hwnd:正在处理其消息的窗口的句柄。

  • wm:窗口消息标识符

  • wp:消息信息参数之一。这取决于wm论点

  • lp:消息信息参数之一。这取决于wm论点。此参数通常用于传输指针或句柄

在这个简单的程序中,我们自己不会处理任何消息。但这并不意味着Windows也不会。这就是为什么必须调用的原因DefWindowProc,其中包含默认的窗口处理代码。必须在每个窗口过程的末尾调用此函数。

什么是手柄?

手柄是代表一个唯一的对象的数据类型。它们是指针,但指向操作系统维护的秘密数据结构。这些结构的细节与我们无关。用户所需要做的就是简单地使用API调用创建/获取句柄,并将其传递给采用该句柄类型的其他API调用。我们使用的唯一句柄类型是的HWND返回CreateWindowEx。

常数

在此示例中,我们遇到了几个常量,它们全部用大写字母开头,并以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争论。使WindowsCW_USEDEFAULT为传递的参数选择有效值。

Windows类型

当为Windows编程时,您将不得不习惯Win32类型,它们是内置类型的别名。这些类型大写。该程序中使用的别名类型为:

  • TCHAR:通用字符类型。如果UNICODE已定义,则为wchar_t。另外,它是一个char。

  • UINT:无符号整数。用于表示窗口过程和其他目的中的消息标识符。

  • WPARAM:在Win16中,这是一个WORD参数(因此带有W前缀)。但是,随着Win32的引入,它现在是一个UINT_PTR。这说明了这些Windows别名的要点。他们在那里是为了保护程序免受更改。

  • LPARAM:这是一个LONG参数(LONG_PTR在Win64中)。

  • PTSTR:P均值指针。该T指一般的性格和STR手段字符串。因此,这是指向TCHAR字符串的指针。其他字符串类型包括:

    • LPTSTR:与 PTSTR

    • LPCTSTR:均值 const TCHAR *

    • PCTSTR:与 LPCTSTR

    • LPWSTR:宽字符串(wchar_t *)

    • LPCWSTR:均值 const wchar_t *

    • PWSTR:与 LPWSTR

    • 以及更多正如您所看到的,Win32类型可能很难理解,尤其是对于这么多同义类型,这是Win16的产物。

  • LRESULT:此类型用于表示窗口过程的返回值。通常是LONG(因此L)。