第一个Windows窗口

  • 效果展示
  • 详细过程
    • 设计窗口类
    • 注册窗口类
    • 创建窗口
    • 显示窗口
    • 更新窗口
    • 消息循环
  • 源代码
  • 实用工具
    • 图标制作软件
    • 图标库

效果展示

第一个Windows窗口

详细过程

设计窗口类

首先,自定义窗口类型名和窗口标题,后面要用到,一定不能缺少。

  wchar_t szAppClassName[] = L"ChengZiEDU_GUI";             //窗口类型名wchar_t szWindowName[] = L"这是我的第一个Windows程序";       //窗口标题

接着,是设计窗口类的常规操作,按步骤操作即可。

  WNDCLASS wc;     //W : Unicodewc.style = CS_HREDRAW | CS_VREDRAW;                                   //窗口类风格wc.lpfnWndProc = WindowProc;                                       //窗口处理函数wc.cbClsExtra = 0;                                                   //窗口类的额外扩展空间大小(字节)wc.cbWndExtra = 0;                                                   //窗口的额外扩展空间大小(字节)wc.hInstance = hInstance;                                           //当前应用程序实例句柄// NULL 表示使用默认窗口图标和光标图标wc.hIcon = NULL;                                                 //窗口图标句柄wc.hCursor = NULL;                                                 //光标图标句柄(鼠标)//使用自定义图标//wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON));//wc.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_CURSOR));wc.hbrBackground = CreateSolidBrush(RGB(255, 255, 255));           //背景画刷句柄wc.lpszMenuName = NULL;                                                //菜单名wc.lpszClassName = szAppClassName;                                    //窗口类型名

上述程序先设计了一个窗口类 wc,接着对这个窗口的所有参数进行初始化。
我们来看看WNDCLASS的参数:

typedef struct tagWNDCLASSA {
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCSTR lpszMenuName;
LPCSTR lpszClassName;
} WNDCLASSA, *PWNDCLASSA, *NPWNDCLASSA, *LPWNDCLASSA;

下面我们来仔细说一下每个参数

1、窗口类样式(style)有,可自行选择,多个样式之间用 “|” 连接
一般设置为: CS_HREDRAW | CS_VREDRAW (水平和竖直发生变化时重绘 )

常量/值 说明
CS_BYTEALIGNCLIENT 在 (x 方向) 的字节边界上对齐窗口的工作区 。 此样式会影响窗口的宽度及其在显示器上的水平位置。
CS_BYTEALIGNWINDOW 在 (x 方向) 的字节边界上对齐窗口 。 此样式会影响窗口的宽度及其在显示器上的水平位置。
CS_CLASSDC 分配一个设备上下文,供类中的所有窗口共享。 由于窗口类是特定于进程的,因此应用程序多个线程可以创建同一类的窗口。 线程还可以尝试同时使用设备上下文。 发生这种情况时,系统只允许一个线程成功完成其绘图操作。
CS_DBLCLKS 当用户双击鼠标位于属于该类的窗口中时,将双击消息发送到窗口过程。
CS_DROPSHADOW 对窗口启用阴影效果。 通过 SPISETDROPSHADOW_ 打开和关闭效果。 通常,这为小型生存期窗口(例如菜单)启用,以强调它们与其他窗口的 Z 顺序关系。 从具有此样式的类创建的Windows必须是顶级窗口;它们可能不是子窗口。
CS_GLOBALCLASS 指示窗口类是应用程序全局类。
CS_HREDRAW 如果移动或大小调整更改了工作区的宽度,则重新绘制整个窗口。
CS_NOCLOSE 禁用窗口菜单上的 "关闭 "。
CS_OWNDC 为类中的每个窗口分配唯一的设备上下文。
CS_PARENTDC 将子窗口的剪裁矩形设置为父窗口的剪裁矩形,以便子窗口可以绘制父窗口。 具有 CSPARENTDC_ 样式位的窗口从系统的设备上下文缓存接收常规设备上下文。 它不会为子级提供父级的设备上下文或设备上下文设置。 指定 CSPARENTDC_ 可增强应用程序的性能。
CS_SAVEBITS 以位图形式保存此类窗口遮盖的屏幕图像部分。 删除窗口后,系统使用保存的位图还原屏幕图像,包括遮盖的其他窗口。 因此,如果位图使用的内存尚未丢弃,并且其他屏幕操作未使存储的图像失效,则系统不会将 WMPAINT_ 消息发送到隐藏的窗口。 此样式适用于小型窗口 (例如,菜单或对话框) 短暂显示,然后在其他屏幕活动发生前删除。 此样式增加了显示窗口所需的时间,因为系统必须首先分配内存来存储位图。
CS_VREDRAW 如果移动或大小调整更改工作区的高度,则重新绘制整个窗口。

2、窗口处理函数
我们要自定义一个窗口处理函数。
LRESULT 的处理结果 long

参数 描述 类型
hWnd 窗口句柄(当前窗口的窗口句柄) HWND (结构体指针)
uMsg 消息编号(比如鼠标点击、移动) UINT (usigned int)
wParam 窗口附加信息(比如鼠标坐标) WPARAM (usigned int)
lParam 窗口附加信息 LPARAM (long)

例如:

  //窗口处理函数LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){switch (uMsg){case WM_CLOSE:                //窗口关闭消息DestroyWindow(hWnd);    //销毁窗口 (调用下面的WM_DESTROY)break;case WM_DESTROY:           //窗口销毁消息PostQuitMessage(0);       //发出一个退出消息 :WM_QUITbreak;}return DefWindowProc(hWnd, uMsg, wParam, lParam);    //调用操作系统默认消息处理函数}

LRESULT CALLBACK 函数名(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 是固定格式,下面函数体要自己写,其中WM_CLOSEWM_DESTROY 是一定要有的,记得最后调用一下操作系统默认消息处理函数

3、窗口类的额外扩展空间大小(字节)
设置为 0

4、窗口的额外扩展空间大小(字节)
设置为 0

5、窗口句柄
设置为当前应用程序实例句柄,也就是 WinMain 函数的第一个参数 hInstance

6、窗口图标句柄
使用默认窗口图标句柄,例如:
wc.hIcon = NULL;

使用自定义窗口图标句柄,例如:
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON));

LoadIcon:加载一个图标文件
第一个参数:应用程序实例句柄
如果是系统资源,传递 NULL
如果是自定义资源,传递 hInstance
第二个参数:资源ID(要和 resource.h 头文件里面的一致),参数类型为 LPCWSTR
MAKEINTRESOURCE() 函数可以将参数类型转换为 LPCWSTR

LoadCursor:加载一个光标文件
第一个参数:应用程序实例句柄
如果是系统资源,传递NULL
如果是自定义资源,传递hInstance
第二个参数:资源ID(要和 resource.h 头文件里面的一致),参数类型为 LPCWSTR
MAKEINTRESOURCE() 函数可以将参数类型转换为 LPCWSTR

7、光标图标句柄
使用默认光标图标句柄,例如:
wc.hCursor = NULL;

使用自定义光标图标句柄,例如:
wc.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_CURSOR));

8、背景画刷句柄
功能:设置窗口背景颜色,颜色采用 RGB模式
例如:
wc.hbrBackground = CreateSolidBrush(RGB(255, 255, 255));

9、菜单名
wc.lpszMenuName = NULL;
此阶段设置为 NULL

10、窗口类型名
为一开始自定义的窗口类型名
例如:
wc.lpszClassName = szAppClassName;

tips: 很多API都是以 W 开头,这表示它的字符集是 Unicode

注册窗口类

注册窗口类使用的是 RegisterClass 函数,我们只需要将前面设计的窗口类名字取地址传进来,然后进行一个简单的条件判断是否注册成功即可。

例如:

  //注册窗口类if (0 == RegisterClass(&wc))     {MessageBox(NULL, L"此程序不能运行在Windows NT上", L"温馨提示", MB_OK);return 0;}

创建窗口

创建窗口使用的是 CreateWindow 函数,再简单初始化一下参数,最后判断一下窗口是否创建成功即可。

例如:

  HWND hWnd = CreateWindow(szAppClassName,                          //窗口类型名(一开始自定义的类型名)szWindowName,                            //窗口标题(一开始自定义的标题)WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, //窗口风格200,                                  //窗口放置坐标的横坐标200,                                    //窗口放置坐标的纵坐标800,                                    //窗口的宽度600,                                 //窗口的高度NULL,                                  //父窗口,没有的话写 NULLNULL,                                  //菜单句柄,没有的话写 NULLhInstance,                                //应用程序句柄NULL                                 //创建窗口需要传递的信息,没有的话写 NULL);if (hWnd == NULL){MessageBox(NULL, L"创建窗口失败", L"温馨提示", MB_OK);return 0;}

HWND hWnd 来接收创建的窗口,窗口名可自定义,不一定要为 hWnd

下面为系统中 CreateWindow 函数的定义

HWND WINAPI CreateWindow(
In_opt LPCTSTR lpClassName,
In_opt LPCTSTR lpWindowName,
In DWORD dwStyle,
In int x,
In int y,
In int nWidth,
In int nHeight,
In_opt HWND hWndParent,
In_opt HMENU hMenu,
In_opt HINSTANCE hInstance,
In_opt LPVOID lpParam
);

窗口常用风格

风格 描述
WS_CAPTION 该窗口有一个标题栏
WS_SYSMENU 窗口的标题栏上有一个窗口菜单
WS_MAXIMIZEBOX 窗口有一个最大化按钮,不能与 WS_EX_CONTEXTHELP 样式组合
WS_MINIMIZEBOX 窗口有一个最小化按钮,不能与 WS_EX_CONTEXTHELP 样式组合
WS_MAXIMIZE 窗口最初是最大化的
WS_MINIMIZE 窗口最初是最小化的
WS_CHILD 窗口是一个子窗口,具有这种样式的窗口不能有菜单栏
WS_DISABLED 窗口最初被禁用,禁用的窗口无法接收用户的输入。要在创建窗口后更改此设置,请使用EnableWindow函数。
WS_HSCROLL 窗口有一个水平滚动条
WS_VSCROLL 窗口有一个垂直滚动条
WS_ICONIC 窗口最初是最小化的

显示窗口

使用 ShowWindow 函数,将创建的窗口传进来

ShowWindow(hWnd, SW_SHOW);        //正常显示

一般设置为正常显示模式 :SW_SHOW

更新窗口

使用 UpdateWindow 函数,将创建的窗口传进来

UpdateWindow(hWnd);

消息循环

有小伙伴问为什么需要消息循环呢?
因为没有消息循环的话窗口就会一闪而过了

  //这里只要收到了 WM_QUIT 的消息,函数就会返回 0 ,窗口和进程都会销毁MSG msg;while (GetMessage(&msg, NULL, 0, 0))       //NULL获取当前窗口及其子窗口   0,0获取所有消息{//将虚拟键消息转为字符消息TranslateMessage(&msg);//将虚拟键消息分发给窗口处理函数DispatchMessage(&msg);}

这里,我们先定义一个接收窗口消息的变量,然后使用 GetMessageTranslateMessageDispatchMessage 函数对消息进行处理,它们缺一不可。

源代码

#include <Windows.h>        //头文件
#include "resource.h"       //资源头文件//窗口处理函数声明
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR lpCmdLine, int nCmdShow)
{//设计窗口类wchar_t szAppClassName[] = L"ChengZiEDU_GUI";wchar_t szWindowName[] = L"这是我的第一个Windows程序";WNDCLASS wc;     wc.style = CS_HREDRAW | CS_VREDRAW;                                   //窗口类风格 CS_HREDRAW | CS_VREDRAW 水平和竖直发生变化时重绘wc.lpfnWndProc = WindowProc;                                       //窗口处理函数wc.cbClsExtra = 0;                                                   //窗口类的额外扩展空间大小(字节)wc.cbWndExtra = 0;                                                   //窗口的额外扩展空间大小(字节)wc.hInstance = hInstance;                                           //当前应用程序实例句柄wc.hIcon = NULL;                                                   //窗口图标句柄wc.hCursor = NULL;                                                 //光标图标句柄(默认鼠标)//wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON));//wc.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_CURSOR));wc.hbrBackground = CreateSolidBrush(RGB(255, 255, 255));           //背景画刷句柄wc.lpszMenuName = NULL;                                                //菜单名wc.lpszClassName = szAppClassName;                                    //窗口类型名//注册窗口类if (0 == RegisterClass(&wc))       {MessageBox(NULL, L"此程序不能运行在Windows NT上", L"温馨提示", MB_OK);return 0;}//创建窗口HWND hWnd = CreateWindow(szAppClassName,                         //窗口类型名(一开始自定义的类型名)szWindowName,                            //窗口标题(一开始自定义的标题)WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, //窗口风格200,                                  //窗口放置坐标的横坐标200,                                    //窗口放置坐标的纵坐标800,                                    //窗口的宽度600,                                 //窗口的高度NULL,                                  //父窗口,没有的话写 NULLNULL,                                  //菜单句柄,没有的话写 NULLhInstance,                                //应用程序句柄NULL                                 //创建窗口需要传递的信息,没有的话写 NULL);if (hWnd == NULL){MessageBox(NULL, L"创建窗口失败", L"温馨提示", MB_OK);return 0;}//显示窗口ShowWindow(hWnd, SW_SHOW);      //正常显示//更新窗口UpdateWindow(hWnd);//消息循环//这里只要收到了 WM_QUIT 的消息,函数就会返回 0 ,窗口和进程都会销毁MSG msg;while (GetMessage(&msg, NULL, 0, 0))        //NULL获取当前窗口及其子窗口   0,0获取所有消息{//将虚拟键消息转为字符消息TranslateMessage(&msg);//将虚拟键消息分发给窗口处理函数DispatchMessage(&msg);}return 0;
}//窗口处理函数定义
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{switch (uMsg){case WM_CLOSE:             //窗口关闭消息DestroyWindow(hWnd);    //销毁窗口 (调用下面的WM_DESTROY)break;case WM_DESTROY:           //窗口销毁消息PostQuitMessage(0);       //发出一个退出消息 :WM_QUITbreak;}return DefWindowProc(hWnd, uMsg, wParam, lParam);    //调用操作系统默认消息处理函数
}

实用工具

可能有的小伙伴不知道怎么自己制作图标和光标,这可不是简单地将一张图片的后缀改为 .ico.cur 就可以做到的,下面我给大家分享一个图标制作软件和一个有着大量免费图标的网站。

图标制作软件

icofx
下面是这个软件的网盘链接
链接:https://pan.baidu.com/s/1GMt7uA1k9GbRkJOFJKQFmQ?pwd=czjy
提取码:czjy

下载完解压缩后,将箭头所指的快捷方式复制到桌面即可使用


使用方法:
将图片拖到 icofx 的界面中,然后选择创建图标或光标,接着 CTRL + S 保存即可


这里我们顺便讲一下怎么将资源添加到工程中

首先,将要添加的资源拷贝到工程的文件夹中,如下图


接着,在工程中找到资源文件,按下图操作

然后,选择导入图标(Icon) 或光标 (Cursor)


最后,找到工程文件夹中对应的资源,将其添加进来即可

图标库

https://www.iconfont.cn/

手把手教你写第一个Windows窗口相关推荐

  1. 手把手教你写第一个C语言程序

    C语言是所有编程语言的基础,历经50多年的发展依然被众多编程者使用,那么怎么写C语言程序呢?下面我将从项目的创建开始一步步交大家写第一个C语言程序. 一.C语言项目的创建: C语言是一门编译型语言.所 ...

  2. Vue3 Typescript + Axios 全栈开发教程:手把手教你写「待办清单」APP

    本文完整版:<Vue3 Typescript + Axios 全栈开发教程:手把手教你写「待办清单」APP> Vue3 Typescript + Axios 全栈开发教程 前端 Vue3 ...

  3. windows脚本编制引擎_手把手教你写脚本引擎(一)

    手把手教你写脚本引擎(一)--挑选语言的特性 陈梓瀚 华南理工大学软件本科05级 脚本引擎的作用在于增强程序的可配置性.从游戏到管理系统都需要脚本,甚至连工业级产品的Office.3DS Max以及A ...

  4. 手把手教你写一个生成对抗网络

    成对抗网络代码全解析, 详细代码解析(TensorFlow, numpy, matplotlib, scipy) 那么,什么是 GANs? 用 Ian Goodfellow 自己的话来说: " ...

  5. python k线合成_手把手教你写一个Python版的K线合成函数

    手把手教你写一个Python版的K线合成函数 在编写.使用策略时,经常会使用一些不常用的K线周期数据.然而交易所.数据源又没有提供这些周期的数据.只能通过使用已有周期的数据进行合成.合成算法已经有一个 ...

  6. 手把手教你写一个spring IOC容器

    本文分享自华为云社区<手把手教你写一个spring IOC容器>,原文作者:技术火炬手. spring框架的基础核心和起点毫无疑问就是IOC,IOC作为spring容器提供的核心技术,成功 ...

  7. 手把手教你写一个Matlab App(一)

    对于传统工科的学生用的最多的编程软件应该就是matlab,其集成度高,计算能力强,容易上手,颇受大众青睐.今天挖的这个新坑,主要是分享用matlab app designer设计GUI界面的一些方法和 ...

  8. 后端思维篇:手把手教你写一个并行调用模板

    前言 36个设计接口的锦囊中,也提到一个知识点:就是使用并行调用优化接口.所以接下来呢,就快马加鞭写第二篇:手把手教你写一个并行调用模板~ 一个串行调用的例子(App首页信息查询) Completio ...

  9. 从原理到实现丨手把手教你写一个线程池丨源码分析丨线程池内部组成及优化

    人人都能学会的线程池 手写完整版 1. 线程池的使用场景 2. 线程池的内部组成 3. 线程池优化 [项目实战]从原理到实现丨手把手教你写一个线程池丨源码分析丨线程池内部组成及优化 内容包括:C/C+ ...

最新文章

  1. python命令方式和关键字
  2. Session保存到指定数据库中
  3. 屏蔽武汉ip地址 php,wordpress如何限制屏蔽IP地址
  4. 【文末福利】聊天机器人的几种主要架构实现
  5. 京东数据分析工具(京东销售数据如何查询)
  6. MAC中SPSS无法打开数据
  7. 20155311《网络对抗》信息搜集与漏洞扫描
  8. Nagios基本介绍
  9. 【乐谱】摇篮曲(小宝宝要睡觉)五线谱-单音和声版
  10. C#远程启动、终止进程
  11. 物联网、无线模块和组合模块,具体是什么意思
  12. 小程序:下载图片文件(wx.downloadFile)并保存到手机相册(wx.saveImageToPhotosAlbum)
  13. 生存智慧——新的生活方式
  14. 解析压敏电阻与TVS管的性能对比,谁能更胜一筹?
  15. python xlwt安装linux_Linux安装Python xlrd、xlwt、xlutils模块
  16. php返回值乱码,php中文返回乱码怎么办
  17. Parameter index out of range (1 > number of parameters, which is 0)
  18. Dfinity(ICP)基础开发教程-5
  19. 511遇见易语言文本替换和子文本替换
  20. 清除数据库中所有数据

热门文章

  1. 读《Finger Vein Verification using Intrinsic and Extrinsic Features》
  2. 以汽车制造业为例,细说制造企业如何实施PLM系统项目?
  3. ClickHouse基于全局字典与物化视图的精确去重方案
  4. 网络编程知识预备(2) —— 三次握手与四次挥手、半连接状态、2MSL
  5. 个人站——作品集页面设计
  6. python就业前景不好_担心学习Python就业情况不好?来看看Python发展前景
  7. VC++电脑锁屏(附源码)
  8. 使用RT-Thread Studio DIY 迷你桌面时钟(三)| 获取NTP时间(at_device软件包 + netutils软件包)
  9. 2022年全球及中国鼻梁条行业头部企业市场占有率及排名调研报告
  10. 计算机组成原理ce和we是什么,EL-JY-Ⅱ 计算机组成原理第二套 实验三