演练:创建传统的 Windows 桌面应用程序 (C++) 您所在的位置:网站首页 计算器软件图标 演练:创建传统的 Windows 桌面应用程序 (C++)

演练:创建传统的 Windows 桌面应用程序 (C++)

2023-07-19 02:32| 来源: 网络整理| 查看: 265

演练:创建传统的 Windows 桌面应用程序 (C++) 项目 06/16/2023

本演练演示如何在 Visual Studio 中创建传统的 Windows 桌面应用程序。 你将创建的示例应用程序使用 Windows API 在窗口中显示“Hello, Windows desktop!”。 可以将本演练中开发的代码作为模式来创建其他 Windows 桌面应用程序。

Windows API(也称为 Win32 API、Windows 桌面 API 和 Windows Classic API)是用于创建 Windows 应用程序的基于 C 语言的框架。 它诞生自 20 世纪 80 年代,几十年来一直用于创建 Windows 应用程序。 在 Windows API 的基础上,构建了更高级、更易于编程的框架。 例如,MFC、ATL、.NET 框架。 即使是用 C++/WinRT 针对 UWP 和应用商店应用编写的最现代的 Windows 运行时代码,也使用底层的 Windows API。 有关 Windows API 的详细信息,请参阅 Windows API 索引。 创建 Windows 应用程序的方法有很多,但上述过程是首选。

重要

为简洁起见,文本中省略了一些代码语句。 本文档末尾的生成代码部分显示了完整代码。

先决条件

运行 Microsoft Windows 7 或更高版本的计算机。 为了获得最佳开发体验,建议使用 Windows 10 或更高版本。

Visual Studio 的副本。 有关如何下载和安装 Visual Studio 的信息,请参阅安装 Visual Studio。 运行安装程序时,请务必选中“使用 C++ 的桌面开发” 工作负载。 如果在安装 Visual Studio 时未安装此工作负载,请不要担心。 可以再次运行安装程序并立即安装。

了解使用 Visual Studio IDE 的基础知识。 如果你之前使用过 Windows 桌面应用,可能具备一定的相关知识。 有关简介,请参阅 Visual Studio IDE 功能导览。

了解足够的 C++ 语言基础知识以供继续操作。 别担心,我们不会执行过于复杂的操作。

创建 Windows 桌面项目

按照以下步骤创建你的第一个 Windows 桌面项目。 在进行过程中,你将输入一个可正常运行的 Windows 桌面应用程序的代码。 若要查看 Visual Studio 首选项的文档,请使用“版本”选择器控件。 它位于此页面上目录表的顶部。

在 Visual Studio 中创建 Windows 桌面项目

在主菜单中,选择“文件”>“新建”>“项目”,打开“创建新项目”对话框。

在对话框顶部,将“语言”设置为“C++”,将“平台”设置为“Windows”,将“项目类型”设置为“桌面”。

从经过筛选的项目类型列表中,选择“Windows 桌面向导”,然后选择“下一步”。 在下一页中,输入项目的名称,例如 DesktopApp。

选择“创建”按钮创建项目。

随即显示“Windows 桌面项目”对话框。 在“应用程序类型”下,选择“桌面应用程序(.exe)”。 在“附加选项” 下,选择“空项目” 。 选择“确定”,创建项目 。

在解决方案资源管理器中,右键单击“DesktopApp”项目,选择“添加”,然后选择“新项”。

在“添加新项” 对话框中选择“C++ 文件(.cpp)” 。 在“名称”框中,键入文件名,例如 HelloWindowsDesktop.cpp。 选择“添加” 。

现已创建项目,源文件在编辑器中打开。 若要继续,请跳至创建代码。

在 Visual Studio 2017 中创建 Windows 桌面项目

在“文件”菜单上选择“新建”,再选择“项目”。

在“新建项目”对话框的左窗格中,展开“已安装”>“Visual C++”,然后选择“Windows 桌面”。 在中间窗格中,选择“Windows 桌面向导”。

在“名称”框中,键入项目名称,例如 DesktopApp。 选择 “确定” 。

在“Windows 桌面项目”对话框的“应用程序类型”下,选择“Windows 应用程序(.exe)”。 在“附加选项” 下,选择“空项目” 。 确保未选择“预编译标头”。 选择“确定”,创建项目 。

在解决方案资源管理器中,右键单击“DesktopApp”项目,选择“添加”,然后选择“新项”。

在“添加新项” 对话框中选择“C++ 文件(.cpp)” 。 在“名称”框中,键入文件名,例如 HelloWindowsDesktop.cpp。 选择“添加” 。

现已创建项目,源文件在编辑器中打开。 若要继续,请跳至创建代码。

在 Visual Studio 2015 中创建 Windows 桌面项目

在“文件”菜单上选择“新建”,再选择“项目”。

在“新建项目”对话框的左窗格中,展开“已安装”>“模板”>“Visual C++”,然后选择“Win32”。 在中间窗格中,选择“Win32 项目” 。

在“名称”框中,键入项目名称,例如 DesktopApp。 选择 “确定” 。

在“Win32 应用程序向导”的“概述”页面上,选择“下一步”。

在“应用程序设置”页面的“应用程序类型”下,选择“Windows 应用程序”。 在“其他选项”下,取消选中“预编译标头”,然后选中“空项目”。 选择“完成”以创建项目 。

在解决方案资源管理器中,右键单击 DesktopApp 项目,选择“添加”,然后选择“新建项”。

在“添加新项” 对话框中选择“C++ 文件(.cpp)” 。 在“名称”框中,键入文件名,例如 HelloWindowsDesktop.cpp。 选择“添加” 。

现已创建项目,源文件在编辑器中打开。

创建代码

接下来介绍如何在 Visual Studio 中为 Windows 桌面应用程序创建代码。

启动 Windows 桌面应用程序

正如每个 C 应用程序和 C++ 应用程序在起始点必须具有 main 函数,每个 Windows 桌面应用程序也必须具有 WinMain 函数。 WinMain 具有以下语法。

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

有关此函数的参数和返回值的信息,请参阅 WinMain 入口点。

备注

这些额外的单词(如 WINAPI、CALLBACK、HINSTANCE 或 _In_)是什么? 传统的 Windows API 广泛使用 typedef 和预处理器宏,来抽象化类型和特定于平台的代码的一些详细信息,例如调用约定、__declspec 声明和编译器 pragma。 在 Visual Studio 中,可以使用 IntelliSense 快速信息功能查看这些 typedef 和宏定义的内容。 将鼠标悬停在感兴趣的单词上,或选择它并按 Ctrl+K、Ctrl+I 打开一个包含定义的小型弹出窗口。 有关详细信息,请参阅使用 IntelliSense。 参数和返回类型通常使用 SAL 注释来帮助捕获编程错误。 有关详细信息,请参阅使用 SAL 注释减少 C/C++ 代码缺陷。

Windows 桌面程序需要 。 定义了 TCHAR 宏,如果在项目中定义了 UNICODE 符号,该宏最终会解析为 wchar_t,否则解析为 char。 如果总是在启用 UNICODE 的情况下生成,则不需要 TCHAR,可以直接使用 wchar_t。

#include #include

除了 WinMain 函数之外,每个 Windows 桌面应用程序还必须具有一个窗口过程函数。 此函数通常命名为 WndProc,但你可以根据自己的喜好命名。 WndProc 具有以下语法。

LRESULT CALLBACK WndProc( _In_ HWND hWnd, _In_ UINT message, _In_ WPARAM wParam, _In_ LPARAM lParam );

在此函数中编写代码,以处理发生事件时应用程序从 Windows 收到的消息。 例如,如果用户在应用程序中选择“确定”按钮,Windows 将向你发送一条消息,你可以在 WndProc 函数中编写代码来执行任何适当的工作。 此过程称为处理事件。 你只需处理与应用程序相关的事件。

有关详细信息,请参阅 窗口过程。

向 WinMain 函数添加功能

在 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(wcex.hInstance, 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, IDI_APPLICATION);

有关上述结构的字段的信息,请参阅 WNDCLASSEX。

向 Windows 注册 WNDCLASSEX,使其了解你的窗口以及如何向窗口发送消息。 使用 RegisterClassEx 函数,并将窗口类结构作为参数传递。 使用 _T 宏是因为我们使用 TCHAR 类型。

if (!RegisterClassEx(&wcex)) { MessageBox(NULL, _T("Call to RegisterClassEx failed!"), _T("Windows Desktop Guided Tour"), NULL); return 1; }

现在可以创建窗口了。 使用 CreateWindowEx 函数。

static TCHAR szWindowClass[] = _T("DesktopApp"); static TCHAR szTitle[] = _T("Windows Desktop Guided Tour Application"); // The parameters to CreateWindowEx explained: // WS_EX_OVERLAPPEDWINDOW : An optional extended window style. // 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 does not have a menu bar // hInstance: the first parameter from WinMain // NULL: not used in this application HWND hWnd = CreateWindowEx( WS_EX_OVERLAPPEDWINDOW, szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 100, NULL, NULL, hInstance, NULL ); if (!hWnd) { MessageBox(NULL, _T("Call to CreateWindowEx failed!"), _T("Windows Desktop Guided Tour"), NULL); return 1; }

此函数返回一个 HWND,它是窗口句柄。 句柄有点像 Windows 用来跟踪打开的窗口的指针。 有关详细信息,请参阅 Windows 数据类型。

至此,窗口已创建完毕,但仍需要告知 Windows 使其可见。 这就是这段代码的作用:

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

显示的窗口内容不多,因为尚未实现 WndProc 函数。 换句话说,应用程序尚未处理 Windows 正在向其发送的消息。

为了处理消息,我们首先添加一个消息循环来侦听 Windows 发送的消息。 当应用程序收到消息时,此循环将该消息调度到 WndProc 函数进行处理。 该消息循环类似于以下代码。

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

有关消息循环中的结构和函数的详细信息,请参阅 MSG、 GetMessage、 TranslateMessage和 DispatchMessage。

此时, 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(wcex.hInstance, 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, IDI_APPLICATION); if (!RegisterClassEx(&wcex)) { MessageBox(NULL, _T("Call to RegisterClassEx failed!"), _T("Windows Desktop Guided Tour"), NULL); return 1; } // Store instance handle in our global variable hInst = hInstance; // The parameters to CreateWindowEx explained: // WS_EX_OVERLAPPEDWINDOW : An optional extended window style. // 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 = CreateWindowEx( WS_EX_OVERLAPPEDWINDOW, szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 100, NULL, NULL, hInstance, NULL ); if (!hWnd) { MessageBox(NULL, _T("Call to CreateWindow failed!"), _T("Windows Desktop 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 函数添加功能

若要启用 WndProc 函数以处理应用程序收到的消息,请实现 switch 语句。

要处理的一条重要消息是 WM_PAINT 消息。 当所显示的窗口的一部分必须更新时,应用程序就会收到 WM_PAINT 消息。 当用户将窗口移到你的窗口前面,然后又将其移开时,可能会发生该事件。 应用程序不知道这些事件何时发生。 只有 Windows 知道,因此它会通过 WM_PAINT 消息通知应用。 第一次显示窗口时,必须更新整个窗口。

要处理 WM_PAINT 消息,首先应调用 BeginPaint,然后处理所有的逻辑以在窗口中布局文本、按钮和其他控件,然后调用 EndPaint。 对于应用程序,开始调用和结束调用之间的逻辑将在窗口中显示字符串“Hello, Windows desktop!”。 在以下代码中,TextOut 函数用于显示该字符串。

PAINTSTRUCT ps; HDC hdc; TCHAR greeting[] = _T("Hello, Windows desktop!"); switch (message) { case WM_PAINT: hdc = BeginPaint(hWnd, &ps); // Here your application is laid out. // For this introduction, we just print out "Hello, Windows desktop!" // in the top left corner. TextOut(hdc, 5, 5, greeting, _tcslen(greeting)); // End application-specific layout section. EndPaint(hWnd, &ps); break; }

代码中的 HDC 是设备上下文的句柄,用于在窗口的工作区中进行绘制。 BeginPaint 和 EndPaint 函数用于准备并完成工作区中的绘制。 BeginPaint 返回用于在工作区进行绘制的显示设备上下文的句柄;EndPaint 结束绘制请求并释放设备上下文。

应用程序通常还处理许多其他消息。 例如,第一次创建窗口时的 WM_CREATE,以及关闭窗口时的 WM_DESTROY。 以下代码显示基本但完整的 WndProc 函数。

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HDC hdc; TCHAR greeting[] = _T("Hello, Windows desktop!"); switch (message) { case WM_PAINT: hdc = BeginPaint(hWnd, &ps); // Here your application is laid out. // For this introduction, we just print out "Hello, Windows desktop!" // 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; } 生成代码

正如之前承诺的那样,下面是可正常运行的应用程序的完整代码。

生成此示例

删除在编辑器的 HelloWindowsDesktop.cpp 中输入的所有代码。 复制此示例代码,然后将其粘贴到 HelloWindowsDesktop.cpp 中:

// HelloWindowsDesktop.cpp // compile with: /D_UNICODE /DUNICODE /DWIN32 /D_WINDOWS /c #include #include #include #include // Global variables // The main window class name. static TCHAR szWindowClass[] = _T("DesktopApp"); // The string that appears in the application's title bar. static TCHAR szTitle[] = _T("Windows Desktop Guided Tour Application"); // Stored instance handle for use in Win32 API calls such as FindResource HINSTANCE hInst; // Forward declarations of functions included in this code module: LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ 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(wcex.hInstance, 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, IDI_APPLICATION); if (!RegisterClassEx(&wcex)) { MessageBox(NULL, _T("Call to RegisterClassEx failed!"), _T("Windows Desktop Guided Tour"), NULL); return 1; } // Store instance handle in our global variable hInst = hInstance; // The parameters to CreateWindowEx explained: // WS_EX_OVERLAPPEDWINDOW : An optional extended window style. // 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 does not have a menu bar // hInstance: the first parameter from WinMain // NULL: not used in this application HWND hWnd = CreateWindowEx( WS_EX_OVERLAPPEDWINDOW, szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 100, NULL, NULL, hInstance, NULL ); if (!hWnd) { MessageBox(NULL, _T("Call to CreateWindow failed!"), _T("Windows Desktop 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, Windows desktop!"); switch (message) { case WM_PAINT: hdc = BeginPaint(hWnd, &ps); // Here your application is laid out. // For this introduction, we just print out "Hello, Windows desktop!" // 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; }

在 “生成” 菜单上,选择 “生成解决方案” 。 编译结果应显示在 Visual Studio 的“输出”窗口中。

若要运行应用程序,请按 F5。 包含文本“Hello, Windows desktop!”的窗口应出现在显示屏的左上角。

祝贺你! 你已完成本演练并生成了传统的 Windows 桌面应用程序。

另请参阅

Windows 桌面应用程序



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有