Module 1. Your First Windows Program (Windows)
模块1. 你的第一个Windows程序
In this module, we will write a minimal Windows program. All it does is create and show a blank window. This first program contains about 50 lines of code, not counting blank lines and comments. It will be our starting point; later we'll add graphics, text, user input, and other features.
在这个模块,我们将写一个显示空白窗口的迷你的Windows程序。不计空行跟注释的话这个程序包含50行代码,这是我们步入Windows编程的开端,稍后我们将添加图形、文本、用户输入以及其他的特性。
Screen shot of the example program
示例程序的截图
Here is the complete code for the program:
这里有完整的代码:
#ifndef UNICODE #define UNICODE #endif #include <windows.h> LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow) { // Register the window class. const wchar_t CLASS_NAME[] = L"Sample Window Class"; WNDCLASS wc = { }; wc.lpfnWndProc = WindowProc; wc.hInstance = hInstance; wc.lpszClassName = CLASS_NAME; RegisterClass(&wc); // Create the window. HWND hwnd = CreateWindowEx( 0, // Optional window styles. CLASS_NAME, // Window class L"Learn to Program Windows", // Window text WS_OVERLAPPEDWINDOW, // Window style // Size and position CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, // Parent window NULL, // Menu hInstance, // Instance handle NULL // Additional application data ); if (hwnd == NULL) { return 0; } ShowWindow(hwnd, nCmdShow); // Run the message loop. MSG msg = { }; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; } LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_DESTROY: PostQuitMessage(0); return 0; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps); FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1)); EndPaint(hwnd, &ps); } return 0; } return DefWindowProc(hwnd, uMsg, wParam, lParam); }
You can download the complete Visual Studio project from Windows Hello World Sample.
你可以下载完整的Visual Studio工程:Windows Hello World Sample。
It may be useful to give a brief outline of what this code does. Later topics will examine the code in detail.
对这些代码先做一个简要介绍也许有一定作用,在以后的主题里再详细解读代码。
1.wWinMain is the program entry point. When the program starts, it registers some information about the behavior of the application window. One of the most important items is the address of a function, named WindowProc in this example. This function defines the behavior of the window—its appearance, how it interacts with the user, and so forth.
1.wWinMain是程序的入口点,当程序开始时,一些关于应用程序窗口的信息将被注册,其中最重要的项目是函数地址,也就是例子中的WindowProc。这个函数定义了窗口的行为——他如何显示,如何与用户交互并以此类推。
2.Next, the program creates the window and receives a handle that uniquely identifies the window.
2.下一步,程序创建了窗口并接收一个句柄作为唯一标识。
3.If the window is created successfully, the program enters awhile loop. The program remains in this loop until the user closes the window and exits the application.
3.如果窗口创建成功,程序就进入了while循环,直到用户关闭窗口退出程序。
Notice that the program does not explicitly call the WindowProc function, even though we said this is where most of the application logic is defined. Windows communicates with your program by passing it a series ofmessages. The code inside the while loop drives this process. Each time the program calls theDispatchMessage function, it indirectly causes Windows to invoke the WindowProc function, once for each message.
注意:程序不显式地调用WindowProc函数,虽然我们说这是大部分程序的逻辑定义。Windows通过一系列的messages与你的程序通信。这些代码在while循环里驱动着进程,针对每一条message,程序都调用DispatchMessage函数,这间接引起Windows去调用WindowProc函数。
Creating a Window (Windows)
Window Classes
A window class defines a set of behaviors that several windows might have in common. For example, in a group of buttons, each button has a similar behavior when the user clicks the button. Of course, buttons are not completely identical; each button displays its own text string and has its own screen coordinates. Data that is unique for each window is called instance data.
窗口类定义了数个窗口共同的一组行为,例如,一组按钮,每个按钮在用户点击的时候都有相似的行为。当然,这些按钮不可能都是一模一样的,每个按钮有自己的坐标跟文字,每一个有独特数据的窗口被称之为“数据实例”。
Every window must be associated with a window class, even if your program only ever creates one instance of that class. It is important to understand that a window class is not a "class" in the C++ sense. Rather, it is a data structure used internally by the operating system. Window classes are registered with the system at run time. To register a new window class, start by filling in a WNDCLASS structure:
每一个窗口都必须关联一个窗口类,即使你的程序只创建一个类的实例。要知道一个窗口的类跟C++中类不一样,相反,它是一种在操作系统内部使用的数据结构。窗口类都在运行期中向系统注册过,而注册一个窗口类,是从给WNDCLASS结构体的赋值开始。
// Register the window class. 注册窗口类 const wchar_t CLASS_NAME[] = L"Sample Window Class"; WNDCLASS wc = { }; wc.lpfnWndProc = WindowProc; wc.hInstance = hInstance; wc.lpszClassName = CLASS_NAME;
You must set the following structure members:
你必须设置一下结构体成员:
- lpfnWndProc is a pointer to an application-defined function called the window procedure or "window proc." The window procedure defines most of the behavior of the window. We'll examine the window procedure in detail later. For now, just treat this as a forward reference.
- lpfnWndProc是一个已定义的应用程序函数指针,叫做“窗口过程”或者“window proc”。窗口过程定义了大部分窗口动作,之后我们将详细解读“窗口过程”这一概念,现在,仅作为预习;
- hInstance is the handle to the application instance. Get this value from the hInstance parameter of wWinMain.
- hInstance是应用程序实例的句柄,从wWinMain的hIntance参数得到这个值;
- lpszClassName is a string that identifies the window class.
-
lpszClassName是窗口类的字符串标识符。
Class names are local to the current process, so the name only needs to be unique within the process. However, the standard Windows controls also have classes. If you use any of those controls, you must pick class names that do not conflict with the control class names. For example, the window class for the button control is named "Button".
类名称局限于当前进程,所以只要在这个进程内保证唯一即可。然而,标准的Windows控件也包含许多类,如果你用了里面的任何一些,都必须保证类名不能冲突,例如作为按钮的窗口类名为“Button”,你就不能再使用这个类名。
The WNDCLASS structure has other members not shown here. You can set them to zero, as shown in this example, or fill them in. The MSDN documentation describes the structure in detail.
在这里WNDCLASS还有其他的结构体成员没有展示出来,你可以就像示例里面一样把他们置零或给他们赋值,MSDN文档描述里面有详细的结构体信息可供查阅。
Next, pass the address of the WNDCLASS structure to the RegisterClass function. This function registers the window class with the operating system.
下一步,通过把WNDCLASS结构体的地址传给RegisterClass函数,把窗口类注册到操作系统。代码如下:
RegisterClass(&wc);
Creating the Window
创建窗口
To create a new instance of a window, call the CreateWindowEx function:
建立一个新的窗口实例,可以调用CreateWindowEx函数:
HWND hwnd = CreateWindowEx( 0, // Optional window styles. 窗口样式选项 CLASS_NAME, // Window class 窗口类 L"Learn to Program Windows", // Window text 窗口文本 WS_OVERLAPPEDWINDOW, // Window style 窗口样式 // Size and position CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, // Parent window 父窗口 NULL, // Menu 菜单 hInstance, // Instance handle 实例句柄 NULL // Additional application data 额外的应用程序数据 ); if (hwnd == NULL) { return 0; }
You can read detailed parameter descriptions on MSDN, but here is a quick summary:
你可以详细阅读MSDN上关于这些参数的描述,但是这里有一些快捷的小结:
- The first parameter lets you specify some optional behaviors for the window (for example, transparent windows). Set this parameter to zero for the default behaviors.
- 第一个参数可以让你指定窗口中的一些可选行为(例如,窗口透明度),将此参数设置为0则为默认行为;
- CLASS_NAME is the name of the window class. This defines the type of window you are creating.
- CLASS_NAME是窗口类的类名,这定义了创建窗口的类型;
- The window text is used in different ways by different types of windows. If the window has a title bar, the text is displayed in the title bar.
- “window text(窗口文本)”用于区别不同类型的窗口,如果窗口有标题栏,这个文本将显示在其中;
- The window style is a set of flags that define some of the look and feel of a window. The constant WS_OVERLAPPEDWINDOW is actually several flags combined with a bitwise OR. Together these flags give the window a title bar, a border, a system menu, and Minimize and Maximize buttons. This set of flags is the most common style for a top-level application window.
- “window style(窗口样式)”是一个标志位,定义了一些窗口的感官特性,常量WS_OVERLAPPEDWINDOW实际上是几个标志位作或运算的结果,这些标志位一起赋予了窗口的标题栏、边框、系统菜单、最大化、最小化按钮。这些标志位是一个顶层应用程序中最通用的样式;
- For position and size, the constant CW_USEDEFAULT means to use default values.
- 对于位置和尺寸,常量CW_USEDEFAULT意味着使用默认值;
- The next parameter sets a parent window or owner window for the new window. Set the parent if you are creating a child window. For a top-level window, set this to NULL.
- 下一个参数设置了新窗口的父窗口或宿主窗口,当你创建了一个新的子窗口的时候就要设定这个值,对于顶层窗口,这个值设为NULL;
- For an application window, the next parameter defines the menu for the window. This example does not use a menu, so the value is NULL.
- 对于一个应用程序窗口,下一个参数定义了窗口的菜单栏,这个例子没有使用菜单栏,所以这个值为NULL;
- hInstance is the instance handle, described previously. (See WinMain: The Application Entry Point.)
- hInstance是句柄实例,之前已经讲过了(见学习C++ Windows 编程(Ⅱ、如何使用C++进行windows编程【5】));
- The last parameter is a pointer to arbitrary data of type void*. You can use this value to pass a data structure to your window procedure. We'll show one possible way to use this parameter in the section Managing Application State.
- 最后一个参数是任意数据类型的指针,即void*,你可以使用这个值让一个结构体进你的窗口过程,我们将在Managing Application State(之后博主会翻译这一节的,此处暂时不理会即可。)这一部分用一种可行的方式展示如何使用这个参数。
CreateWindowEx returns a handle to the new window, or zero if the function fails. To show the window—that is, make the window visible —pass the window handle to the ShowWindow function:
CreateWindowEx成功时返回一个窗口句柄,失败时返回0。为了显示出窗口——也就是使窗口变得可见——得通过向ShowWindow函数传送窗口句柄。代码如下:
ShowWindow(hwnd, nCmdShow);
The hwnd parameter is the window handle returned by CreateWindowEx. The nCmdShow parameter can be used to minimize or maximize a window. The operating system passes this value to the program through the wWinMainfunction.
这个hwnd参数就是上一步CreateWindowEx返回的窗口句柄,对于nCmdShow参数,操作系统可以通过这个值在wWinMain执行时使窗口尺寸可变得最小化或最大化。
Here is the complete code to create the window. Remember that WindowProc is still just a forward declaration of a function.
这里有创建窗口的完整代码,记住WindowProc函数总是得在前头先声明:
// Register the window class. const wchar_t CLASS_NAME[] = L"Sample Window Class"; WNDCLASS wc = { }; wc.lpfnWndProc = WindowProc; wc.hInstance = hInstance; wc.lpszClassName = CLASS_NAME; RegisterClass(&wc); // Create the window. HWND hwnd = CreateWindowEx( 0, // Optional window styles. CLASS_NAME, // Window class L"Learn to Program Windows", // Window text WS_OVERLAPPEDWINDOW, // Window style // Size and position CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, // Parent window NULL, // Menu hInstance, // Instance handle NULL // Additional application data ); if (hwnd == NULL) { return 0; } ShowWindow(hwnd, nCmdShow);
Congratulations, you've created a window! Right now, the window does not contain any content or interact with the user. In a real GUI application, the window would respond to events from the user and the operating system. The next section describes how window messages provide this sort of interactivity.
恭喜你,你已经创建出了一个窗口!现在,这个窗口没有包含任何内容或者与用户互动的元素。在一个实际的用户图形界面应用程序中,窗口可以响应来自用户的事件或者操作系统的信息,在下一节中描述了“window message(窗口消息)”是如何提供这种交互性的。