如何创建一个最简单的Windows桌面应用程序 (C++)


如何创建一个最简单的Windows桌面应用程序 (C++)


     最近刚开始学习C/C++开发Windows应用程序,这里将会以零基础的视角把学习过程完全记录下来。如果你也刚刚起步,那本文一定非常适合你。

     进入正题,本文讨论如何使用Visual Studio生成一个最简单的C窗体应用程序,并向用户显示Hello~

     下面我们一步步来介绍,对于涉及代码的地方,我们只介绍大体的框架,完整的代码会在文章最后给出。   

     创建基于 Win32 的项目

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

     2.在“新建项目”对话框的左窗格中,依次单击“已安装模板”和“Visual C++”,然后选择“Win32”。在中间窗格中,选择“Win32 项目”。在“名称”框中,键入项目名称,例如HelloApp。单击“确定”。

     3.在“Win32 应用程序向导”的欢迎页面中,单击“下一步”。在“应用程序设置”页的“应用程序类型”下,选择“Windows 应用程序”。 在“附加选项”下,选择“空项目”。 单击“完成”以创建项目。

     4.在“解决方案资源管理器”中,右键单击 HelloApp项目,然后依次单击“添加”和“新建项”。 在“添加新项”对话框中选择“C++ 文件(.cpp)”。 在“名称”框中,键入文件名,例如GT_HelloWorldWin32.cpp。单击“添加”。

     添加引用

     我们的应用程序需要使用许多现有定义才能完成所需功能,针对我们的需求,添加引用如下(其中前两个是必须的):

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

     WinMain函数     

     正如每个 C/C++控制台应用程序在起始点必须具有 main 函数,每个基于 Win32 的应用程序的函数也必须具有 WinMain 函数。WinMain就相当于是入口函数,并且具有固定的语法:

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

     实现WinMain函数时内部逻辑大体相同,主要有以下几部分:

     1.创建描述窗体信息的窗口类结构WNDCLASSEX 

     如何创建一个 WNDCLASSEX 类型的窗口类结构?下面的代码演示了一个典型的窗口类结构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)); 

     对于初学者,我们不用过分纠结代码的细节,暂时从宏观上把控。我们需要知道,此结构包含关于窗口的信息,例如应用程序图标、窗口背景色、标题栏中显示的名称、窗口过程函数的名称等。
     2.对窗口类进行注册

     现在已创建了窗口类,必须进行注册。

     使用 RegisterClassEx 函数,并将窗口类结构作为参数传递。

   RegisterClassEx(&wcex);

     3.创建并显示窗口

     现在需要使用CreateWindow函数创建窗口

     使用ShowWindow函数显示窗口

     这部分也很好理解,详见文末的代码

     4.侦听消息

     添加用于侦听操作系统所发送消息的消息循环。

     当应用程序收到一条消息时,此循环将该消息调度到 WndProc 函数。WndProc 函数用于对接收的消息进行处理,我们下面会介绍到。 

     该消息循环类似于以下代码:

// Main message loop: // 添加用于侦听操作系统所发送消息的消息循环。 // 当应用程序收到一条消息时,此循环将该消息调度到 WndProc 函数以进行处理。 MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); //翻译消息DispatchMessage(&msg);  //派遣消息} 

     WndProc 函数

     WndProc 函数用以处理应用程序收到的消息。

     首先需要判断收到的消息类型进而做出不同的处理,这需要使用 switch 语句。
     系统提供了众多的消息命令,例如 WM_PAINT 代表收到了绘图消息,而收到鼠标点击消息的标识是WM_LBUTTONDOWN...

     这里以处理 WM_PAINT 消息为例进行说明。

     要处理 WM_PAINT 消息,首先应调用 BeginPaint,然后处理所有的绘图逻辑,例如在窗口中布局文本、按钮和其他控件,然后调用 EndPaint。 

     对于此应用程序,开始调用和结束调用之间的逻辑是在窗口中显示字符串 “Hello,World!”。 在以下代码中,TextOut 函数用于显示字符串。

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: //要处理 WM_PAINT 消息,首先应调用 BeginPaint//然后处理所有的逻辑以及在窗口中布局文本、按钮和其他控件等//然后调用 EndPaint。 hdc = BeginPaint(hWnd, &ps); // -----------------在这段代码对应用程序进行布局------------------------ // 对于此应用程序,开始调用和结束调用之间的逻辑是在窗口中显示字符串 “Hello,World!”。// 请注意 TextOut 函数用于显示字符串。TextOut(hdc, 50, 5, greeting, _tcslen(greeting)); // -----------------------布局模块结束----------------------------------EndPaint(hWnd, &ps);break; case WM_DESTROY: PostQuitMessage(0); break;default: //DefWindowProc缺省窗口处理函数//这个函数是默认的窗口处理函数,我们可以把不关心的消息都丢给它来处理return DefWindowProc(hWnd, message, wParam, lParam); break; } return 0; 
} 

     程序运行的结果为:


     完整代码

     到此我已经理清了写一个Windows应用程序的主要逻辑

     我是按照Microsoft官方文档进行的学习,详见创建Windows桌面应用程序

     对于更多的细节,代码中给出了详细注释。

// GT_HelloWorldWin32.cpp 
// compile with: /D_UNICODE /DUNICODE /DWIN32 /D_WINDOWS /c 
// 这是一个最简单的Win32程序,亦可作为开发桌面应用程序的模板#include <windows.h> 
#include <stdlib.h> 
#include <string.h> 
#include <tchar.h> // ------------------------Global variables---------------------------------
// 主窗体类名
static TCHAR szWindowClass[] = _T("win32app"); 
// 应用程序标题栏处出现的字符串
static TCHAR szTitle[] = _T("Win32 Guided Tour Application"); //HINSTANCE 是Windows里的一中数据类型,是用于标示(记录)一个程序的实例。
//它与HMODULE是一样的(通用的,这两种类型最终就是32位无符号长整形)。
//HINSTANCE,分开看就是 HANDLE(句柄) + INSTANCE(实例)
//实例就是一个程序。用qq来举例:你可以开同时开2个qq号,但是你电脑里的qq软件只有一份。
//这2个qq号就是qq的2个实例
HINSTANCE hInst; // -------------------------需要预定义的函数放置在此代码块种:---------------------------- //每个 Windows 桌面应用程序必须具有一个窗口过程函数
//此函数处理应用程序从操作系统中接收的大量消息。 
//例如,如果应用程序的对话框中有“确定”按钮,那么用户单击该按钮时,
//操作系统会向应用程序发送一条消息,通知按钮已被单击。WndProc 负责对该事件作出响应。
//在本例中,相应的响应可能是关闭对话框。
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //-------------------------------------正式内容-----------------------------------------//主窗体函数(入口过程)
//每个基于 Win32 的应用程序的函数必须具有 WinMain 函数
//正如每个 C 应用程序和 C++ 控制台应用程序在起始点必须具有 main 函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { //创建 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)); //对已创建的窗口类进行注册。 使用 RegisterClassEx 函数,并将窗口类结构作为参数传递。if (!RegisterClassEx(&wcex)) { MessageBox(NULL, _T("Call to RegisterClassEx failed!"), _T("Win32 Guided Tour"), NULL); return 1; } // Store instance handle in our global variable // 将句柄实例存储于全局变量中hInst = hInstance; // CreateWindow 函数的参数解释: // 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 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; } // ShowWindow 函数的参数解释: // hWnd: CreateWindow函数返回的窗口句柄 // nCmdShow: the fourth parameter from WinMain ShowWindow(hWnd, nCmdShow);// UpdateWindow函数用于更新窗口指定的区域// 如果窗口更新的区域不为空,UpdateWindow函数就发送一个WM_PAINT消息来更新指定窗口的客户区。// 函数绕过应用程序的消息队列,直接发送WM_PAINT消息给指定窗口的窗口过程// 如果更新区域为空,则不发送消息。UpdateWindow(hWnd); // Main message loop: // 添加用于侦听操作系统所发送消息的消息循环。 // 当应用程序收到一条消息时,此循环将该消息调度到 WndProc 函数以进行处理。 MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); //翻译消息DispatchMessage(&msg);  //派遣消息} return (int) msg.wParam; 
} // //  函数: WndProc(HWND, UINT, WPARAM, LPARAM) 
// //  目标:  处理主窗体产生的信息
// //  WM_PAINT消息代表绘制主窗体 
// //  WM_DESTROY消息代表投递一个退出消息并返回 
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: //要处理 WM_PAINT 消息,首先应调用 BeginPaint//然后处理所有的逻辑以及在窗口中布局文本、按钮和其他控件等//然后调用 EndPaint。 hdc = BeginPaint(hWnd, &ps); // -----------------在这段代码对应用程序进行布局------------------------ // 对于此应用程序,开始调用和结束调用之间的逻辑是在窗口中显示字符串 “Hello,World!”。// 请注意 TextOut 函数用于显示字符串。TextOut(hdc, 50, 5, greeting, _tcslen(greeting)); // -----------------------布局模块结束----------------------------------EndPaint(hWnd, &ps);break; case WM_DESTROY: PostQuitMessage(0); break;default: //DefWindowProc缺省窗口处理函数//这个函数是默认的窗口处理函数,我们可以把不关心的消息都丢给它来处理return DefWindowProc(hWnd, message, wParam, lParam); break; } return 0; 
} 



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/499524.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Win32窗体应用程序如何添加资源文件?

Win32窗体应用程序如何添加资源文件&#xff1f; 上一篇文章介绍了&#xff1a;如何创建一个最简单的Windows窗体应用程序。 按照上一篇文章的介绍&#xff0c;我们的HelloApp项目对应如下的窗体应用程序&#xff1a; 这一篇文章中&#xff0c;我们将在这个程序的基础上&#x…

【论文党福利】如何提取图像中的数据

【论文党福利】如何提取图像中的数据 从事科研的老师和同学们在撰写论文时&#xff0c;经常需要将文献中的曲线与自己的结果进行对比&#xff0c;为获取原始数据&#xff0c;最靠谱的方法当然是找原作者要。如果没有要到呢&#xff1f;本文将成为论文党的福利&#xff0c;为你…

添加串口和虚拟终端输出帮助调试

&#xfeff;&#xfeff;在使用IAR开发STM32项目时&#xff0c;使用串口或者是虚拟终端来输出我们想看的信息是一个非常好而且简便的方式。 首先来看看串口怎么实现信息输出。简单来说串口输出信息就是将标准输出重定向到串口&#xff0c;在上位机的超级终端或者串口助手等工…

1.1股票数据预处理练习

第一阶段、一个简单策略入门量化投资 1.1股票数据预处理练习 无论我们要对股票市场进行何种探索&#xff0c;在开始前&#xff0c;研究如何获取数据&#xff0c;并进行对应的预处理都是必要的。 本节以美股为例&#xff0c;进行股票数据预处理的练习。正文如下&#xff1a; …

1-2 移动均线交叉策略1

第一阶段、一个简单策略入门量化投资 1-2 移动均线交叉策略1 第一阶段一个简单策略入门量化投资1-2 移动均线交叉策略1前言获取数据移动均线交叉策略数据可视化绘制折线图绘制K线图绘制移动均线 移动均线交叉策略回测什么是回测回溯买卖信号计算收益 未完待续完整代码 前言 …

STM32F412应用开发笔记之一:初识NUCLEO-F412ZG

&#xfeff;&#xfeff;今天终于收到了期待已久的NUCLEO-F412ZG&#xff0c;感谢电子发烧友论坛&#xff01; 近几年来基本都是在STM32平台上做一些设计开发工作。STM32F103、STM32F107、STM32F429等都应用过&#xff0c;但却从没有申请过试用。没想到这次申请居然能被选中&a…

STM32F412应用开发笔记之二:基本GPIO控制

&#xfeff;&#xfeff;NUCLEO-F412ZG板子上的元器件并没有完全焊接&#xff0c;除去ST-LINK部分和电源部分后&#xff0c;还有用一个USB主机接口&#xff0c;三个LED灯和两个按钮&#xff0c;不过很多功能引脚都已经引到了插针。查看原理图可发现&#xff0c;由原理图模块的…

1-3移动均线交叉策略2

第一阶段、一个简单策略入门量化投资 1-3移动均线交叉策略2 上一篇文章1-2 移动均线交叉策略1中我们最后提到&#xff1a; 如果我们从第一天买入股票&#xff0c;一直持有股票&#xff0c;最后一天卖出&#xff0c;获得的收益是每股124.02美元&#xff0c;收益率为412% 如果…

1-4移动均线交叉策略3

第一阶段、一个简单策略入门量化投资 1-4移动均线交叉策略3 上一文1-3移动均线交叉策略2中&#xff0c;我们得到的结果是令人失望的。但我们的探索还要继续。 我们知道&#xff0c;使用投资组合的方式进行分散投资是降低风险的好办法。尽管移动均线交叉策略的表现并不理想&a…

STM32学习及应用笔记一:SysTick定时器学习及应用

&#xfeff;&#xfeff; 这几年一直使用STM32的MCU&#xff0c;对ARM内核的SysTick计时器也经常使用&#xff0c;但几乎没有仔细了解过。最近正好要在移植一个新的操作系统时接触到了这块&#xff0c;据比较深入的了解了一下。 1、SysTick究竟是什么&#xff1f; 关于SysT…

使用Atom快速打造好用的Markdown编辑器

使用Atom快速打造好用的Markdown编辑器 Atom当前主流的跨平台的三大编辑器(Atom,sublime,vscode)之一 今天尝试了使用Atom来打造Markdown编辑器&#xff0c;快速上手且易用&#xff0c;墙裂推荐&#xff01; 下面直接进入正题&#xff0c;一步步介绍如何使用Atom快速打造好用…

PID控制器开发笔记之一:PID算法原理及基本实现

&#xfeff;&#xfeff; 在自动控制中&#xff0c;PID及其衍生出来的算法是应用最广的算法之一。各个做自动控制的厂家基本都有会实现这一经典算法。我们在做项目的过程中&#xff0c;也时常会遇到类似的需求&#xff0c;所以就想实现这一算法以适用于更多的应用场景。…

十分钟能学会的简单python爬虫

简单爬虫三步走&#xff0c;So easy~ 本文介绍一个使用python实现爬虫的超简单方法&#xff0c;精通爬虫挺难&#xff0c;但学会实现一个能满足简单需求的爬虫&#xff0c;只需10分钟&#xff0c;往下读吧~ 该方法不能用于带有反爬机制的页面&#xff0c;但对于我这样的非专业…

PID控制器开发笔记之二:积分分离PID控制器的实现

前面的文章中&#xff0c;我们已经讲述了PID控制器的实现&#xff0c;包括位置型PID控制器和增量型PID控制器。但这个实现只是最基本的实现&#xff0c;并没有考虑任何的干扰情况。在本节及后续的一些章节&#xff0c;我们就来讨论一下经典PID控制器的优化与改进。这一节我们首…

利用python实现短信和电话提醒功能

有时候&#xff0c;我们需要程序帮我们自动检测某些事件的发生 这个需求是广泛存在的 因此&#xff0c;这里整理了利用python实现短信和电话提醒功能的方法 主要需要完成以下4个步骤&#xff1a; - 安装核心库&#xff1a;twilio - 注册账号及配置 - 发送短信示例 - 电话…

PID控制器开发笔记之三:抗积分饱和PID控制器的实现

积分作用的引入是为了消除系统的静差&#xff0c;提高控制精度。但是如果一个系统总是存在统一个方向的偏差&#xff0c;就可能无限累加而进而饱和&#xff0c;极大影响系统性能。抗积分饱和就是用以解决这一问题的方法之一。这一节我们就来实现抗积分饱和的PID算法。 1、抗积…

如何获取STM32 MCU的唯一ID

前段时间由于应用需要对产品授权进行限制&#xff0c;所以研究了一下有关STM32 MCU的唯一ID的资料&#xff0c;并最终利用它实现了我们的目标。 1、基本描述 在STM32的全系列MCU中均有一个96位的唯一设备标识符。在ST的相关资料中&#xff0c;对其功能的描述有3各方面&#x…

SHA256算法原理详解

1. SHA256简介 SHA256是SHA-2下细分出的一种算法 SHA-2&#xff0c;名称来自于安全散列算法2&#xff08;英语&#xff1a;Secure Hash Algorithm 2&#xff09;的缩写&#xff0c;一种密码散列函数算法标准&#xff0c;由美国国家安全局研发&#xff0c;属于SHA算法之一&…

学习笔记:区块链概念入门

本文是100天区块链学习计划的第二篇学习笔记&#xff0c;其实就是按照阮一峰的网络日志-区块链入门教程的讲解进行的简单梳理。也是时间有点紧张的原因&#xff0c;相比于上一篇SHA256算法原理详解&#xff0c;个人感觉质量和原创程度明显下降。待对区块链有了更深的理解后&…

PID控制器开发笔记之四:梯形积分PID控制器的实现

从微积分的基本原理看&#xff0c;积分的实现是在无限细分的情况下进行的矩形加和计算。但是在离散状态下&#xff0c;时间间隔已经足够大&#xff0c;矩形积分在某些时候显得精度要低了一些&#xff0c;于是梯形积分被提出来以提升积分精度。 1、梯形积分基本思路 在PID控制…