Tutorial: Crear una aplicación Windows Desktop tradicional (C++)
En este tutorial se muestra cómo crear una aplicación Windows escritorio tradicional en Visual Studio. La aplicación de ejemplo que va a crear usa Windows API para mostrar "Hello, Windows desktop!" en una ventana. Puede utilizar el código que va a desarrollar en este tutorial como modelo para crear otras aplicaciones de escritorio de Windows.
La API Windows (también conocida como API Win32, Windows Desktop API y Windows Classic API) es un marco basado en C para crear Windows aplicaciones. Existe desde la década de 1980 y se ha usado para crear aplicaciones Windows durante décadas. Se han creado marcos de trabajo más avanzados y fáciles de programar sobre la API Windows api. Por ejemplo, MFC, ATL o .NET Framework. Incluso el código más moderno Windows Runtime para UWP y aplicaciones de la Tienda escritos en C++/WinRT usa la API Windows debajo. Para obtener más información sobre la API Windows, consulte Windows API Index. Hay muchas maneras de crear aplicaciones Windows, pero el proceso anterior fue el primero.
Importante
Por razones de brevedad, algunas instrucciones de código se omiten en el texto. En la sección Compilar el código al final de este documento se muestra el código completo.
Requisitos previos
Un equipo que ejecuta Microsoft Windows 7 o versiones posteriores. Se recomienda Windows 10 o posterior para obtener la mejor experiencia de desarrollo.
Una copia de Visual Studio. Para obtener información sobre cómo descargar e instalar Visual Studio, consulte Instalación de Visual Studio. Al ejecutar el programa de instalación, asegúrese de que la carga de trabaja Desarrollo para el escritorio con C++ está activada. No se preocupe si no ha instalado esta carga de trabajo al instalar Visual Studio. Puede ejecutar de nuevo el programa de instalación e instalarla ahora.
Detalles de la carga de trabajo Desarrollo para el escritorio Comprensión de los aspectos básicos del uso de IDE de Visual Studio. Si ha usado antes las aplicaciones de escritorio de Windows, probablemente esté al día. Para ver una introducción, consulte Paseo por las características del IDE de Visual Studio.
Conocer todos los fundamentos del lenguaje C++ para poder continuar. No se preocupe, no hacemos nada que sea muy complicado.
Creación de un proyecto Windows escritorio
Siga estos pasos para crear su primer proyecto Windows escritorio. A medida que vaya, escribirá el código de una aplicación de escritorio Windows trabajo. Para ver la documentación de su versión preferida de Visual Studio, use el control de selector Versión. Se encuentra en la parte superior de la tabla de contenido de esta página.
Para crear un proyecto Windows escritorio en Visual Studio
En el menú principal, seleccione ArchivoNuevoProyecto para abrir el cuadro de diálogo Crear nuevo proyecto.
En la parte superior del cuadro de diálogo, establezca Lenguaje en C++,establezca Plataforma en Windowsy Project tipo en Escritorio.
En la lista filtrada de tipos de proyecto, elija Windows Desktop Wizard (Asistente para escritorio) y elija Next (Siguiente). En la página siguiente, escriba un nombre para el proyecto, por ejemplo, DesktopApp.
Elija el botón Crear para crear el proyecto.
Ahora Windows cuadro de diálogo Project escritorio. En Tipo de aplicación,seleccione Aplicación de escritorio (.exe). En Opciones adicionales, seleccione Proyecto vacío. Seleccione Aceptar para crear el proyecto.
En Explorador de soluciones, haga clic con el botón derecho en el proyecto DesktopApp,elija Agregary, a continuación, elija Nuevo elemento.

En el cuadro de diálogo Agregar nuevo elemento , seleccione Archivo C++ (.cpp). En el cuadro Nombre, escriba un nombre para el archivo, por ejemplo, HelloWindowsDesktop.cpp. Seleccione Agregar.

Ahora se crea el proyecto y el archivo de código fuente se abre en el editor. Para continuar, vaya directamente a Creación del código.
Para crear un proyecto Windows escritorio en Visual Studio 2017
En el menú Archivo, elija Nuevo y después Proyecto.
En el cuadro de diálogo Nuevo Project, en el panel izquierdo, expanda Instalado Visual C++ y, a continuación, seleccione Windows Escritorio. En el panel central, seleccione Windows De escritorio.
En el cuadro Nombre, escriba un nombre para el proyecto, por ejemplo, DesktopApp. Elija Aceptar.
Captura de pantalla del cuadro de diálogo Nuevo Project en Visual Studio 2017 con Instalado Visual C más Windows Desktop seleccionado, la opción Asistente para escritorio de Windows resaltada y DesktopApp escrito en el cuadro de texto En el cuadro Windows de diálogo Project escritorio, en Tipo de aplicación, seleccione Windows aplicación (.exe). En Opciones adicionales, seleccione Proyecto vacío. Asegúrese de que encabezado precompilado no está seleccionado. Seleccione Aceptar para crear el proyecto.
En Explorador de soluciones, haga clic con el botón derecho en el proyecto DesktopApp,elija Agregary, a continuación, elija Nuevo elemento.

En el cuadro de diálogo Agregar nuevo elemento , seleccione Archivo C++ (.cpp). En el cuadro Nombre, escriba un nombre para el archivo, por ejemplo, HelloWindowsDesktop.cpp. Seleccione Agregar.
Captura de pantalla del cuadro de diálogo Agregar nuevo elemento de Visual Studio 2017 con instalado Visual C más seleccionado y la opción C más archivo
Ahora se crea el proyecto y el archivo de código fuente se abre en el editor. Para continuar, vaya directamente a Creación del código.
Para crear un proyecto Windows escritorio en Visual Studio 2015
En el menú Archivo, elija Nuevo y después Proyecto.
En el cuadro de Project nuevo, en el panel izquierdo, expanda Plantillas instaladas Visual C++ y, a continuación, seleccione Win32. En el panel central, seleccione Proyecto Win32.
En el cuadro Nombre, escriba un nombre para el proyecto, por ejemplo, DesktopApp. Elija Aceptar.
Captura de pantalla del cuadro de diálogo Nuevo Project en Visual Studio 2015 con plantillas instaladas de Visual C más la opción Win32 Project resaltada y DesktopApp escrito en el cuadro de texto Nombre. En la página Información general del Asistente para aplicaciones Win32,elija Siguiente.

En la página Aplicación Configuración, en Tipo de aplicación,seleccione Windows aplicación. En Opciones adicionales,desactive Encabezado precompilado y,a continuación, seleccione Proyecto vacío. Haga clic en Finalizar para crear el proyecto.
En Explorador de soluciones, haga clic con el botón derecho en el proyecto DesktopApp, elija Agregary, a continuación, elija Nuevo elemento.

En el cuadro de diálogo Agregar nuevo elemento , seleccione Archivo C++ (.cpp). En el cuadro Nombre, escriba un nombre para el archivo, por ejemplo, HelloWindowsDesktop.cpp. Seleccione Agregar.
Captura de pantalla del cuadro de diálogo Agregar nuevo elemento de Visual Studio 2015 con instalado Visual C más seleccionado y la opción C más archivo
Ahora se crea el proyecto y el archivo de código fuente se abre en el editor.
Creación del código
A continuación, aprenderá a crear el código para una aplicación Windows escritorio en Visual Studio.
Para iniciar una aplicación de escritorio de Windows
Al igual que todas las aplicaciones de C y C++ deben tener una función como punto de partida, todas Windows aplicación de
mainescritorio deben tener unaWinMainfunción.WinMaintiene la siguiente sintaxis.int WINAPI WinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow );Para obtener información sobre los parámetros y el valor devuelto de esta función, vea Punto de entrada de WinMain.
Nota
¿Cuáles son todas esas palabras adicionales,
WINAPIcomo , o , oCALLBACKHINSTANCE?_In_La API Windows usa definiciones de tipos y macros de preprocesador ampliamente para abstraer algunos de los detalles de tipos y código específico de la plataforma, como convenciones de llamada, declaraciones y__declspecpragmas del compilador. En Visual Studio, puede usar la característica información rápida de IntelliSense para ver lo que definen estas definiciones de tipo y macros. Mantenga el mouse sobre la palabra de interés o selecciónelo y presione CtrlK , Ctrl I para ver una pequeña ventana emergente que contiene la definición. Para obtener más información, vea Usar IntelliSense. Los parámetros y los tipos de valor devuelto suelen usar anotaciones SAL para ayudarle a detectar errores de programación. Para obtener más información, vea Usar anotaciones SAL para reducir defectos de código de C/C++.Windows programas de escritorio requieren < windows.h > . <tchar.h define la macro , que se resuelve en última instancia en si el símbolo UNICODE está definido en el proyecto; de lo >
TCHARwchar_tcontrario, se resuelve enchar. Si siempre compila con UNICODE habilitado, no necesita TCHAR y solo puede usarwchar_tdirectamente.#include <windows.h> #include <tchar.h>Junto con la
WinMainfunción , todas las Windows de escritorio deben tener también una función de procedimiento de ventana. Esta función se denomina normalmenteWndProc, pero se puede denominar como quiera.WndProctiene la siguiente sintaxis.LRESULT CALLBACK WndProc( _In_ HWND hWnd, _In_ UINT message, _In_ WPARAM wParam, _In_ LPARAM lParam );En esta función, escribirá código para controlar los mensajes que la aplicación recibe de Windows cuando se producen eventos. Por ejemplo, si un usuario elige un botón Aceptar en la aplicación, Windows le enviará un mensaje y podrá escribir código dentro de la función que haga lo que sea
WndProcadecuado. Se denomina control de un evento. Solo se controlan los eventos que son relevantes para la aplicación.Para más información, vea Procedimientos de ventanas.
Para agregar funcionalidad a la función WinMain
En la
WinMainfunción , se rellena una estructura de tipoWinMain. La estructura contiene información sobre la ventana: el icono de aplicación, el color de fondo de la ventana, el nombre que se mostrará en la barra de título, entre otras cosas. Lo más importante es que contiene un puntero de función al procedimiento de ventana. El ejemplo siguiente muestra una estructura típica deWNDCLASSEX.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);Para obtener información sobre los campos de la estructura anterior, vea WNDCLASSEX.
Registre el
WNDCLASSEXcon Windows para que conozca la ventana y cómo enviarle mensajes. Use la función RegisterClassEx y pase la estructura de clase de ventana como argumento. La_Tmacro se usa porque usamos el tipoTCHAR.if (!RegisterClassEx(&wcex)) { MessageBox(NULL, _T("Call to RegisterClassEx failed!"), _T("Windows Desktop Guided Tour"), NULL); return 1; }Ahora puede crear una ventana. Use la función 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; }Esta función devuelve
HWNDun , que es un identificador de una ventana. Un identificador es algo parecido a un puntero que Windows para realizar un seguimiento de las ventanas abiertas. Para obtener más información, vea Tipos de datos de Windows.En este momento, se ha creado la ventana, pero todavía es necesario Windows para que sea visible. Esto es lo que hace este código:
// The parameters to ShowWindow explained: // hWnd: the value returned from CreateWindow // nCmdShow: the fourth parameter from WinMain ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd);La ventana mostrada no tiene mucho contenido porque aún no ha implementado la
WndProcfunción. En otras palabras, la aplicación aún no está controlando los mensajes que Windows envía ahora a ella.Para controlar los mensajes, primero agregamos un bucle de mensajes para escuchar los mensajes que Windows envía. Cuando la aplicación recibe un mensaje, este bucle lo envía a la
WndProcfunción que se va a controlar. El bucle de mensajes es similar al código siguiente.MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int) msg.wParam;Para más información sobre las estructuras y funciones que se usan en el bucle de mensajes, vea MSG, GetMessage, TranslateMessagey DispatchMessage.
En este punto, la función
WinMaindebe ser similar al código siguiente.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; }
Para agregar funcionalidad a la función WndProc
Para habilitar la función
WndProca fin de controlar los mensajes que recibe la aplicación, implemente una instrucción switch.Un mensaje importante que se debe controlar es WM_PAINT mensaje. La aplicación recibe el mensaje cuando se debe actualizar
WM_PAINTparte de su ventana mostrada. El evento puede producirse cuando un usuario mueve una ventana delante de la ventana y, a continuación, vuelve a moverla. La aplicación no sabe cuándo se producen estos eventos. Solo Windows sabe, por lo que notifica a la aplicación con unWM_PAINTmensaje. Cuando se muestra la ventana por primera vez, se debe actualizar todo.Para controlar un mensaje
WM_PAINT, primero llame aWM_PAINT, controle toda la lógica para mostrar el texto, los botones y otros controles de la ventana y luego llame a EndPaint. Para la aplicación, la lógica entre la llamada inicial y la llamada final muestra la cadena "Hello, Windows desktop!" en la ventana. En el código siguiente, se usa la función TextOut para mostrar la cadena.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; }HDCen el código es un identificador para un contexto de dispositivo, que se usa para dibujar en el área de cliente de la ventana. Use lasBeginPaintfunciones y para preparar y completar el dibujo en el área deEndPaintcliente.BeginPaintdevuelve un identificador al contexto del dispositivo de presentación que se usa para dibujar en el área de cliente;EndPaintfinaliza la solicitud de pintura y libera el contexto del dispositivo.Normalmente, una aplicación controla muchos otros mensajes. Por ejemplo, WM_CREATE la primera vez que se crea una ventana y WM_DESTROY cuando se cierra la ventana. El código siguiente muestra una función
WndProcbásica pero completa.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; }
Compilación del código
Como se ha comprometido, este es el código completo de la aplicación en funcionamiento.
Para compilar este ejemplo
Elimine cualquier código que haya escrito en HelloWindowsDesktop.cpp en el editor. Copie este código de ejemplo y péguelo en 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"); 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; }En el menú Compilar , elija Compilar solución. Los resultados de la compilación deben aparecer en la ventana Salida de Visual Studio.

Para ejecutar la aplicación, presione F5. Una ventana que contiene el texto "Hello, Windows desktop!" debe aparecer en la esquina superior izquierda de la pantalla.

¡Enhorabuena! Ha completado este tutorial y ha creado una aplicación Windows escritorio tradicional.