Пошаговое руководство. Создание традиционного классического приложения Windows (C++)
В этом пошаговом руководстве показано, как создать традиционное классическое приложение Windows в Visual Studio. Приложение, которое вы создаете, использует API Windows для отображения "Hello, Windows desktop!" в окне. Код, который вы разрабатываете в этом пошаговом руководстве, можно использовать в качестве шаблона для создания классических приложений Windows.
API Windows (также известный как API Win32, КЛАССИЧЕСКИЙ API Windows и Классический API Windows) — это платформа на основе языка C для создания приложений Windows. Он использовался для создания приложений Windows на протяжении десятилетий. Более сложные и простые платформы программ были созданы на основе API Windows. Например, MFC, ATL, платформы .NET. Даже самый современный код среда выполнения Windows для приложений UWP и Store, написанных в C++/WinRT, использует API Windows в нижней части. Дополнительные сведения об API Windows см. в разделе "Индекс API Windows".
Внимание
В разделе "Сборка кода " в конце этого документа показан полный код. В этом пошаговом руководстве рассматриваются различные фрагменты кода, которые входят в приложение Windows, но вы не будете кодировать по мере использования, так как некоторые сведения опущены в фрагментах кода, чтобы сосредоточиться на наиболее важных частях. Вы можете скопировать полный код и вставить его в проект в конце.
Необходимые компоненты
Компьютер под управлением Microsoft Windows 7 или более поздних версий. Мы рекомендуем Windows 11 или более поздней версии для оптимальной разработки.
копия Visual Studio. Сведения о скачивании и установке Visual Studio см. в этой статье. Когда вы запускаете установщик, убедитесь, что установлена рабочая нагрузка Разработка классических приложений на C++. Не беспокойтесь, если вы не установили эту рабочую нагрузку при установке Visual Studio. Вы можете снова запустить установщик и установить ее сейчас.
Базовое понимание использования интегрированной среды разработки Visual Studio. Если вы уже использовали классические приложения для Windows, вы, вероятно, справитесь. Общие сведения см. в обзоре возможностей интегрированной среды разработки Visual Studio.
Основные навыки владения языком C++. Не волнуйтесь, мы не будем делать ничего сложного.
Создание классического проекта Windows
Выполните следующие действия, чтобы создать первый классический проект Windows. Запишите в начале этого пошагового руководства полный код доступен в разделе "Сборка кода" в конце пошагового руководства. Следуйте инструкциям по созданию проекта, но удерживайте вставку следующих разделов кода до конца, когда будет представлен полный код приложения. Некоторые сведения опущены в фрагментах кода, чтобы сосредоточиться на наиболее важных частях. Вы можете скопировать полный код и вставить его в проект в конце.
Чтобы упростить объяснение. Чтобы ознакомиться с документацией по предпочтительной версии Visual Studio, используйте селектор Версия. Он расположен в верхней части оглавление на этой странице.
Создание классического проекта Windows в Visual Studio
В главном меню выберите Файл >Создать >Проект, чтобы открыть диалоговое окно Создание проекта.
В верхней части диалогового окна задайте для языка C++, установите для платформы значение "Платформа" в Windows и задайте для типа Project значение Desktop.
Из отфильтрованного списка типов проектов выберите мастер рабочего стола Windows и нажмите кнопку "Далее". На следующей странице введите имя проекта, например DesktopApp.
Нажмите кнопку Создать, чтобы создать проект.
Откроется диалоговое окно "Проект рабочего стола Windows". В раскрывающемся списке "Тип приложения" выберите классическое приложение (.exe). Так как мы делаем приложение Windows, выбор консольного приложения приводит к созданию проекта, который не будет строиться с учетом кода, который мы будем использовать. Затем в разделе "Дополнительные параметры" выберите "Пустой проект". Нажмите кнопку ОК, чтобы создать проект.
В Обозреватель решений щелкните правой кнопкой мыши проект DesktopApp, выберите "Добавить" и выберите "Создать элемент".
Анимация показывает правой кнопкой мыши имя проекта в Обозреватель решений, выбрав "Добавить" в появившемся меню и выбрав "Создать элемент".
В диалоговом окне Добавление нового элемента выберите Файл C++ (.cpp). В поле "Имя" введите имя файла, например HelloWindowsDesktop.cpp. Нажмите кнопку Добавить.
Теперь проект создается, и исходный файл открывается в редакторе.
Создание классического проекта Windows в Visual Studio 2017
В меню Файл выберите команду Создать, а затем пункт Проект.
В диалоговом окне "Создать проект" в левой области разверните узел "Установленный>Visual C++", а затем выберите "Рабочий стол Windows". В средней области выберите мастер рабочего стола Windows.
В поле "Имя" введите имя проекта, например DesktopApp. Выберите OK.
В диалоговом окне "Классический проект Windows" в разделе "Тип приложения" выберите приложение Windows (.exe). В поле Дополнительные параметрывыберите Пустой проект. Убедитесь, что предварительно скомпилированные заголовки не выбраны. Нажмите кнопку ОК, чтобы создать проект.
В Обозреватель решений щелкните правой кнопкой мыши проект DesktopApp, выберите "Добавить" и выберите "Создать элемент".
Анимация показывает правой кнопкой мыши имя проекта в Обозреватель решений, выбрав "Добавить" в появившемся меню, а затем выберите новый элемент.
В диалоговом окне Добавление нового элемента выберите Файл C++ (.cpp). В поле "Имя" введите имя файла, например HelloWindowsDesktop.cpp. Нажмите кнопку Добавить.
Теперь проект создается, и исходный файл открывается в редакторе.
Создание классического проекта Windows в Visual Studio 2015
В меню Файл выберите команду Создать, а затем пункт Проект.
В диалоговом окне "Создать проект" в левой области разверните узел "Установленные>шаблоны>Visual C++", а затем выберите Win32. В средней области выберите шаблон Проект Win32.
В поле "Имя" введите имя проекта, например DesktopApp. Выберите OK.
На странице "Обзор" мастера приложений Win32 нажмите кнопку "Далее".
На странице "Параметры приложения" в разделе "Тип приложения" выберите приложение Windows. В разделе "Дополнительные параметры" снимите флажок "Предварительно скомпилированные заголовки", а затем выберите "Пустой проект". Чтобы создать проект, нажмите кнопку Готово.
В Обозреватель решений щелкните правой кнопкой мыши проект DesktopApp, выберите "Добавить" и выберите "Создать элемент".
Анимация показывает правой кнопкой мыши имя проекта в Обозреватель решений, выбрав "Добавить" в появившемся меню и выбрав "Создать элемент".
В диалоговом окне Добавление нового элемента выберите Файл C++ (.cpp). В поле "Имя" введите имя файла, например HelloWindowsDesktop.cpp. Нажмите кнопку Добавить.
Теперь проект создается, и исходный файл открывается в редакторе.
Код
Далее вы узнаете, как создать код для классического приложения Windows в Visual Studio.
Где код запускается в классическом приложении 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_
? Традиционный API Windows использует макросы typedefs и препроцессоров для абстрагирования некоторых сведений о типах и коде для конкретной платформы, таких как соглашения о вызовах,__declspec
объявлениях и pragmas компилятора. В Visual Studio вы можете использовать функцию быстрого сведений IntelliSense, чтобы узнать, что определяют эти типдефы и макросы. Наведите указатель мыши на слово, интересующее вас, или выберите его и нажмите клавиши CTRL K, CTRL++I для небольшого всплывающего окна, содержащего определение. Дополнительные сведения см. в разделе Using IntelliSense. Параметры и типы возвращаемых значений часто используют заметки SAL для перехвата ошибок программирования. Дополнительные сведения см. в статье "Использование заметок SAL для уменьшения дефектов кода C/C++".Требуются
<windows.h>
классические программы Windows. Вы также часто увидите#include <tchar.h>
. Это упрощает запись приложения, с которыми можно работатьchar
wchar_t
либо. Таким образом, вы вместо этого используетеTCHAR
макрос в коде, который в конечном счете разрешает,wchar_t
еслиUNICODE
символ определен в проекте, в противном случае он разрешаетсяchar
. Если вы всегда создаете с поддержкой ЮНИКОДа, вам не нужноTCHAR
использовать егоwchar_t
напрямую. Дополнительные сведения см. в разделе "Использование универсальных текстовых сопоставлений". В следующем коде показаны две#include
инструкции в верхней части файла.#include <windows.h> #include <tchar.h>
Наряду с функцией, каждое классическое
WinMain
приложение Windows также должно иметь функцию window-procedure. Эта функция вызывается,WndProc
но вы можете присвоить ему любое имя, которое вы хотите в коде.WndProc
имеет следующий синтаксис:LRESULT CALLBACK WndProc( _In_ HWND hWnd, _In_ UINT message, _In_ WPARAM wParam, _In_ LPARAM lParam );
В этой функции вы пишете код для обработки сообщений , получаемых приложением из Windows при возникновении событий . Например, если пользователь нажимает кнопку "ОК" в приложении, Windows отправляет вам сообщение. Код записывается в
WndProc
функцию, которая выполняет все необходимые действия. Он называется обработкой события. Вы обрабатываете только события, относящиеся к приложению.Дополнительные сведения см. в разделе Процедуры окна.
Добавление функций в функцию WinMain
WinMain
В функции необходимо записать некоторые основные сведения о главном окне. Это можно сделать, заполнив структуру типаWNDCLASSEX
. Структура содержит сведения о окне, таком как значок приложения, цвет фона окна, имя, отображаемое в строке заголовка, помимо прочего. Важно отметить, что он содержит указатель функции на процедуру окна, которая обрабатывает сообщения, которые Windows отправляет в приложение. В следующем примере показана типичная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
.WNDCLASSEX
После заполнения структуры зарегистрируйте ее в Windows, чтобы она знала о окне и о том, как отправлять сообщения в него.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
функция, которая создает главное окно приложения и прослушивает сообщения, которые Windows отправляет приложение, будут выглядеть следующим образом: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
функции
Для обработки сообщений, получаемых приложением, вы реализуете инструкцию
switch
вWndProc
функции.Важное сообщение для обработки .
WM_PAINT
Приложение получаетWM_PAINT
сообщение, когда необходимо обновить часть отображаемого окна. Событие может возникать, когда пользователь перемещает окно перед окном и снова перемещает его. Оно получает это сообщение при первом отображении окна, что дает возможность отображать пользовательский интерфейс приложения. Приложение узнает об этих событиях, когда Windows отправляет их. При первом отображении окна все его необходимо обновить.Чтобы обработать сообщение, сначала вызовите, а затем обработайте
WM_PAINT
BeginPaint
всю логику для размещения текста, кнопок и других элементов управления в окне. Затем вызовитеEndPaint
. Для этого приложения код междуBeginPaint()
и отображаетсяHello, Windows desktop!
в окне, созданном вWinMain()
EndPaint()
. В следующем коде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 <windows.h> #include <stdlib.h> #include <string.h> #include <tchar.h> // 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.