创建 Win32 应用程序 (C++)

更新:2007 年 11 月

Win32 API(也称为 Windows API)是用于创建 Windows 应用程序的基于 C 的框架,自 Windows 1.0 以来就已存在。在 Windows API 中可以找到有关此 API 的大量文档。

在本过程中,我们将创建向窗口显示“Hello, World!”的简单 Win32 应用程序。过程中的步骤对于所有 Win32 应用程序都是相同的。完成此过程后,您可以将这里创建的代码用作创建任何其他 Win32 应用程序的主干。

先决条件

本主题假定您具备 C++ 语言的基础知识。

链接到视频 有关视频演示,请参见 Video How to: Creating Win32 Applications (C++)(视频帮助:创建 Win32 应用程序 (C++))。

创建新的 Win32 项目

  1. 在“文件”菜单上,单击“新建”,然后单击“项目...”。

  2. 在“项目类型”窗格中,选择“Visual C++”节点中的“Win32”,然后在“模板”窗格中选择“Win32 项目”。

    键入项目的名称,如 win32app。您可以接受默认位置、键入一个位置或者导航到要保存项目的目录。

  3. 在“Win32 应用程序向导”中,选择“下一步”。

  4. 在“Win32 应用程序向导”中,在“应用程序类型”下选择“Windows 应用程序”。在“附加选项”下选择“空项目”。原样保留剩余的选项。单击“完成”创建项目。

  5. 在“项目”菜单中选择“添加新项...”,将 C++ 文件添加到项目中。在“添加新项”对话框中选择“C++ 文件(.cpp)”。为文件键入一个名称,如 GT_HelloWorldWin32.cpp,并单击“添加”。

启动 Win32 应用程序

  1. 正如您了解的,每个 C 和 C++ 应用程序必须具有一个 main 函数。此函数是应用程序的起始点。类似地,在 Win32 应用程序中,每个应用程序必须具有一个 WinMain 函数。WinMain 的语法如下所示:

    int WINAPI WinMain(HINSTANCE hInstance,
                       HINSTANCE hPrevInstance,
                       LPSTR lpCmdLine,
                       int nCmdShow);
    

    有关此函数的参数和返回值的解释,请参见 WinMain 函数

  2. WinMain 外,每个 Win32 应用程序还必须具有第二个函数(通常称为 WndProc),它代表窗口过程。WndProc 的语法如下所示:

    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
    

    此函数的用途是处理应用程序从操作系统接收的任何消息。应用程序何时从操作系统接收消息?始终接收!例如,假设我们创建了包含“确定”按钮的对话框。当用户单击该按钮时,操作系统向应用程序发送消息,使我们知道某位用户按下了此按钮。WndProc 函数负责响应该事件。在本示例中,适当的响应可能是关闭对话框。

    有关更多信息,请参见窗口过程

向 WinMain 添加功能

  1. 首先,在 WinMain 函数内部创建 WNDCLASSEX 类型的窗口类结构。此结构包含有关窗口的信息,如应用程序图标、窗口的背景色、在标题栏中显示的名称、窗口过程函数的名称等等。典型的 WNDCLASSEX 结构如下:

        WNDCLASSEX wcex;
    
        wcex.cbSize = sizeof(WNDCLASSEX);
        wcex.style          = CS_HREDRAW | CS_VREDRAW;
        wcex.lpfnWndProc    = WndProc;
        wcex.cbClsExtra     = 0;
        wcex.cbWndExtra     = 0;
        wcex.hInstance      = hInstance;
        wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
        wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
        wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
        wcex.lpszMenuName   = NULL;
        wcex.lpszClassName  = szWindowClass;
        wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
    

    有关此结构的字段解释,请参见 WNDCLASSEX

  2. 现在已经创建了窗口类,接下来您必须注册它。使用 RegisterClassEx 函数,并将窗口类结构作为参数传递:

        if (!RegisterClassEx(&wcex))
        {
            MessageBox(NULL,
                _T("Call to RegisterClassEx failed!"),
                _T("Win32 Guided Tour"),
                NULL);
    
            return 1;
        }
    
  3. 现在已经注册了您自己的类,接下来创建窗口。使用 CreateWindow 函数,如下所示:

    static TCHAR szWindowClass[] = _T("win32app");
    static TCHAR szTitle[] = _T("Win32 Guided Tour Application");
    // The parameters to CreateWindow explained:
    // szWindowClass: the name of the application
    // szTitle: the text that appears in the title bar
    // WS_OVERLAPPEDWINDOW: the type of window to create
    // CW_USEDEFAULT, CW_USEDEFAULT: initial position (x, y)
    // 500, 100: initial size (width, length)
    // NULL: the parent of this window
    // NULL: this application dows not have a menu bar
    // hInstance: the first parameter from WinMain
    // NULL: not used in this application
    HWND hWnd = CreateWindow(
        szWindowClass,
        szTitle,
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT,
        500, 100,
        NULL,
        NULL,
        hInstance,
        NULL
    );
    if (!hWnd)
    {
        MessageBox(NULL,
            _T("Call to CreateWindow failed!"),
            _T("Win32 Guided Tour"),
            NULL);
    
        return 1;
    }
    

    此函数返回 HWND,它是某个窗口的句柄。有关更多信息,请参见 Windows 数据类型

  4. 创建了窗口后,我们可以使用以下代码将其显示在屏幕上:

    // The parameters to ShowWindow explained:
    // hWnd: the value returned from CreateWindow
    // nCmdShow: the fourth parameter from WinMain
    ShowWindow(hWnd,
        nCmdShow);
    UpdateWindow(hWnd);
    

    到目前为止,此窗口还不会显示,因为我们尚未实现 WndProc 函数。

  5. WinMain 的最后一步是消息循环。此循环的用途是侦听操作系统发送的消息。应用程序收到消息后,将该消息调度到 WndProc 函数,以便进行处理。消息循环类似于:

        MSG msg;
        while (GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    
        return (int) msg.wParam;
    

    有关消息循环中使用的结构和函数的更多信息,请参见 MSGGetMessageTranslateMessageDispatchMessage

  6. 您刚才完成的步骤为大多数 Win32 应用程序所共用。此时,WinMain 函数应该与下面的内容类似:

    int WINAPI WinMain(HINSTANCE hInstance,
                       HINSTANCE hPrevInstance,
                       LPSTR lpCmdLine,
                       int nCmdShow)
    {
        WNDCLASSEX wcex;
    
        wcex.cbSize = sizeof(WNDCLASSEX);
        wcex.style          = CS_HREDRAW | CS_VREDRAW;
        wcex.lpfnWndProc    = WndProc;
        wcex.cbClsExtra     = 0;
        wcex.cbWndExtra     = 0;
        wcex.hInstance      = hInstance;
        wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
        wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
        wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
        wcex.lpszMenuName   = NULL;
        wcex.lpszClassName  = szWindowClass;
        wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
    
        if (!RegisterClassEx(&wcex))
        {
            MessageBox(NULL,
                _T("Call to RegisterClassEx failed!"),
                _T("Win32 Guided Tour"),
                NULL);
    
            return 1;
        }
    
        hInst = hInstance; // Store instance handle in our global variable
    
        // The parameters to CreateWindow explained:
        // szWindowClass: the name of the application
        // szTitle: the text that appears in the title bar
        // WS_OVERLAPPEDWINDOW: the type of window to create
        // CW_USEDEFAULT, CW_USEDEFAULT: initial position (x, y)
        // 500, 100: initial size (width, length)
        // NULL: the parent of this window
        // NULL: this application dows not have a menu bar
        // hInstance: the first parameter from WinMain
        // NULL: not used in this application
        HWND hWnd = CreateWindow(
            szWindowClass,
            szTitle,
            WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT, CW_USEDEFAULT,
            500, 100,
            NULL,
            NULL,
            hInstance,
            NULL
        );
    
        if (!hWnd)
        {
            MessageBox(NULL,
                _T("Call to CreateWindow failed!"),
                _T("Win32 Guided Tour"),
                NULL);
    
            return 1;
        }
    
        // The parameters to ShowWindow explained:
        // hWnd: the value returned from CreateWindow
        // nCmdShow: the fourth parameter from WinMain
        ShowWindow(hWnd,
            nCmdShow);
        UpdateWindow(hWnd);
    
        // Main message loop:
        MSG msg;
        while (GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    
        return (int) msg.wParam;
    }
    

向 WndProc 添加功能

  1. WndProc 函数的用途是处理应用程序接收的消息。通常使用 Switch 函数实现此操作。

    我们将处理的第一个消息是 WM_PAINT 消息。当必须更新应用程序窗口的一部分时,应用程序会收到此消息。首次创建窗口时,必须更新整个窗口,并传递此消息以指示此操作。

    当处理 WM_PAINT 消息时,首先应做的是调用 BeginPaint,最后应做的是调用 EndPaint。在这两个函数调用之间,您可以处理所有的逻辑,以在窗口中排列文本、按钮和其他控件。对于此应用程序,我们在窗口中显示字符串“Hello, World!”。若要显示文本,请使用 TextOut 函数,如下所示:

    PAINTSTRUCT ps;
    HDC hdc;
    TCHAR greeting[] = _T("Hello, World!");
    
    switch (message)
    {
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
    
        // Here your application is laid out.
        // For this introduction, we just print out "Hello, World!"
        // in the top left corner.
        TextOut(hdc,
            5, 5,
            greeting, _tcslen(greeting));
        // End application-specific layout section.
    
        EndPaint(hWnd, &ps);
        break;
    }
    
  2. 应用程序通常会处理许多其他消息,如 WM_CREATEWM_DESTROY。一个简单而完整的 WndProc 函数如下:

    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        PAINTSTRUCT ps;
        HDC hdc;
        TCHAR greeting[] = _T("Hello, World!");
    
        switch (message)
        {
        case WM_PAINT:
            hdc = BeginPaint(hWnd, &ps);
    
            // Here your application is laid out.
            // For this introduction, we just print out "Hello, World!"
            // in the top left corner.
            TextOut(hdc,
                5, 5,
                greeting, _tcslen(greeting));
            // End application specific layout section.
    
            EndPaint(hWnd, &ps);
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
            break;
        }
    
        return 0;
    }
    

示例

说明

完成所有步骤之后,代码应该与下面的内容类似:若要生成应用程序,请从“生成”菜单选择“生成解决方案”。如果应用程序编译时没有任何错误,您可以通过按 F5 来运行该应用程序。在屏幕的左上角附近将显示带有文本“Hello, World!”的简单窗口。

代码

// GT_HelloWorldWin32.cpp
// compile with: /D_UNICODE /DUNICODE /DWIN32 /D_WINDOWS /c

#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <tchar.h>

// Global variables

// The main window class name.
static TCHAR szWindowClass[] = _T("win32app");

// The string that appears in the application's title bar.
static TCHAR szTitle[] = _T("Win32 Guided Tour Application");

HINSTANCE hInst;

// Forward declarations of functions included in this code module:
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow)
{
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = NULL;
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));

    if (!RegisterClassEx(&wcex))
    {
        MessageBox(NULL,
            _T("Call to RegisterClassEx failed!"),
            _T("Win32 Guided Tour"),
            NULL);

        return 1;
    }

    hInst = hInstance; // Store instance handle in our global variable

    // The parameters to CreateWindow explained:
    // szWindowClass: the name of the application
    // szTitle: the text that appears in the title bar
    // WS_OVERLAPPEDWINDOW: the type of window to create
    // CW_USEDEFAULT, CW_USEDEFAULT: initial position (x, y)
    // 500, 100: initial size (width, length)
    // NULL: the parent of this window
    // NULL: this application dows not have a menu bar
    // hInstance: the first parameter from WinMain
    // NULL: not used in this application
    HWND hWnd = CreateWindow(
        szWindowClass,
        szTitle,
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT,
        500, 100,
        NULL,
        NULL,
        hInstance,
        NULL
    );

    if (!hWnd)
    {
        MessageBox(NULL,
            _T("Call to CreateWindow failed!"),
            _T("Win32 Guided Tour"),
            NULL);

        return 1;
    }

    // The parameters to ShowWindow explained:
    // hWnd: the value returned from CreateWindow
    // nCmdShow: the fourth parameter from WinMain
    ShowWindow(hWnd,
        nCmdShow);
    UpdateWindow(hWnd);

    // Main message loop:
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int) msg.wParam;
}

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_PAINT    - Paint the main window
//  WM_DESTROY  - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    PAINTSTRUCT ps;
    HDC hdc;
    TCHAR greeting[] = _T("Hello, World!");

    switch (message)
    {
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);

        // Here your application is laid out.
        // For this introduction, we just print out "Hello, World!"
        // in the top left corner.
        TextOut(hdc,
            5, 5,
            greeting, _tcslen(greeting));
        // End application-specific layout section.

        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
        break;
    }

    return 0;
}

后续步骤

上一部分:创建 Windows 应用程序 (C++) | 下一部分:通过使用 .NET Framework 创建 Windows 窗体应用程序 (C++)

请参见

任务

创建 Windows 应用程序 (C++)