1 手写第一个Win32窗口程序

news2024/9/29 9:20:10

1 基础概念

  1. 什么是窗口?
    答:窗口就是屏幕上的一片区域,接受用户的输入,显示程序的输出。可以包含标题栏、菜单栏、工具栏以及控件等。
  2. 什么是句柄?
    答: 作为一种管理和操作系统资源的机制,提供了对各种对象和资源的访问能力。通过使用句柄,程序可以与特定的资源进行交互和操作。(资源的编号、二级指针)
  3. 窗口类对象是啥?
    C++窗口类对象与窗口并不是一回事,它们之间惟一的关系是 C++窗口类对象内部定义了一个窗口句柄变量,保存了与这个 C++窗口类对象相关的那个窗口的句柄。窗口销毁时,与之对应的 C++窗口类对象销毁与否,要看其生命周期是否结束。但 C++窗口类对象销毁时,与之相关的窗口也将销毁
  4. 常用的结构体以及函数等
//1.用户提供的基于 Windows 的图形应用程序的入口点
int __clrcall WinMain(// __clrcall是一种调用约定,主要涉及函数参数传递方式、函数参数的压栈顺序等
  [in]           HINSTANCE hInstance,// 应用程序的当前实例的句柄
  [in, optional] HINSTANCE hPrevInstance,// 应用程序上一个实例的句柄。 此参数始终为NULL。
  [in]           LPSTR     lpCmdLine,// 应用程序的命令行,不包括程序名称。
  [in]           int       nShowCmd// 控制窗口的显示方式。
);

//2.窗口结构体
typedef struct tagWNDCLASSA {
  UINT      style;// 类样式
  WNDPROC   lpfnWndProc;// 指向窗口过程的指针
  int       cbClsExtra;// 要根据窗口类结构分配的额外字节数。 系统将字节初始化为零
  int       cbWndExtra;// 在窗口实例之后分配的额外字节数。 系统将字节初始化为零。
  HINSTANCE hInstance;// 实例的句柄,该实例包含类的窗口过程。
  HICON     hIcon;// 类图标的句柄。此成员必须是图标资源的句柄。如果此成员为NULL,则系统会提供默认图标。
  HCURSOR   hCursor;// 类游标的句柄。 此成员必须是游标资源的句柄。 
  HBRUSH    hbrBackground;// 类背景画笔的句柄。
  LPCSTR    lpszMenuName;// 类菜单的资源名称,该名称显示在资源文件中。
  LPCSTR    lpszClassName;// 指向以 null 结尾的字符串的指针或是原子。
} WNDCLASSA, *PWNDCLASSA, *NPWNDCLASSA, *LPWNDCLASSA;

//3.从与应用程序实例关联的可执行文件 (.EXE) 文件中加载指定的游标资源
// 如果函数成功,则返回值是新加载的游标的句柄。如果函数失败,则返回值为 NULL。
HCURSOR LoadCursorA(
  // DLL或可执行文件(.exe 模块的句柄)包含要加载的游标的文件。
  // 若要加载预定义的系统游标,请将此参数设置为 NULL。
  [in, optional] HINSTANCE hInstance,
  // 如果 hInstance 为非 NULL, 则 lpCursorName 按名称或序号指定游标资源
  //如果 hInstance 为 NULL, 则 lpCursorName 将指定标识符 (从要加载的预定义系统游标的 IDC_前缀) 开始 
  [in]           LPCSTR    lpCursorName
);
//4.从与应用程序实例关联的可执行 (.exe) 文件中加载指定的图标资源。
HICON LoadIconA(
// DLL 或可执行文件 (.exe 模块的句柄,) 包含要加载的图标的文件
// 若要加载预定义的系统图标,请将此参数设置为 NULL
  [in, optional] HINSTANCE hInstance,
 // 如果hInstance为非NULL,则 lpIconName 按名称或序号指定图标资源。
 // 如果hInstance为NULL,则 lpIconName 将指定标识符(从要加载的预定义系统图标的IDI_前缀)开始 
  [in]           LPCSTR    lpIconName
);
//5.检索其中一支股票笔、画笔、字体或调色板的句柄
HGDIOBJ GetStockObject(
  [in] int i
);
//6.注册一个窗口类
ATOM RegisterClassA(
  [in] const WNDCLASSA *lpWndClass// 指向 WNDCLASS 结构的指针
);

// 7. 该函数创建一个重叠式窗口、弹出式窗口或子窗口。
// 它指定窗口类,窗口标题,窗口风格,以及窗口的初始位置及大小(可选的)。
// 函数也指该窗口的父窗口或所属窗口(如果存在的话),及窗口的菜单。
HWND WINAPI CreateWindow(
  _In_opt_  LPCTSTR lpClassName,   // 窗口类名称
  _In_opt_  LPCTSTR lpWindowName,  // 窗口标题
  _In_      DWORD dwStyle,         // 窗口风格,或称窗口格式
  _In_      int x,                 // 初始 x 坐标
  _In_      int y,                 // 初始 y 坐标
  _In_      int nWidth,            // 初始 x 方向尺寸
  _In_      int nHeight,           // 初始 y 方向尺寸
  _In_opt_  HWND hWndParent,       // 父窗口句柄
  _In_opt_  HMENU hMenu,           // 窗口菜单句柄
  _In_opt_  HINSTANCE hInstance,   // 程序实例句柄
  _In_opt_  LPVOID lpParam         // 创建参数
);

//8.设置指定窗口的显示状态
BOOL ShowWindow(// 如果窗口以前可见,则返回值为非零值,若隐藏则为0
  [in] HWND hWnd,// 窗口的句柄
  [in] int  nCmdShow// 控制窗口的显示方式
);
// 9。如果窗口的更新区域不为空, UpdateWindow 函数通过向窗口发送 WM_PAINT 消息来更新指定窗口的工作区。 函数绕过应用程序队列,将 WM_PAINT 消息直接发送到指定窗口的窗口过程。 如果更新区域为空,则不发送任何消息。
BOOL UpdateWindow(
  [in] HWND hWnd
);

//10.从调用线程的消息队列中检索消息。 函数调度传入的已发送消息,直到已发布的消息可供检索。
BOOL GetMessage(
  [out]          LPMSG lpMsg,// 指向 MSG 结构的指针,该结构从线程的消息队列接收消息信息。
  [in, optional] HWND  hWnd,// 要检索其消息的窗口的句柄。 窗口必须属于当前线程
  [in]           UINT  wMsgFilterMin,// 要检索的最低消息值的整数值。
  [in]           UINT  wMsgFilterMax// 要检索的最高消息值的整数值。
);

//11.将虚拟密钥消息转换为字符消息。
BOOL TranslateMessage(
  [in] const MSG *lpMsg// 指向 MSG 结构的指针
);
//12.将消息调度到窗口过程
LRESULT DispatchMessage(
  [in] const MSG *lpMsg// 指向包含消息的结构的指针
);

//12.包含来自线程的消息队列的消息信息
typedef struct tagMSG {
  HWND   hwnd;// 其窗口过程接收消息的窗口的句柄
  UINT   message;// 消息的标识符
  WPARAM wParam;// 关于消息的附加信息
  LPARAM lParam;// 关于消息的附加信息
  DWORD  time;// 消息的发布时间
  POINT  pt;// 发布消息时的光标位置
  DWORD  lPrivate;
} MSG, *PMSG, *NPMSG, *LPMSG;
  1. 消息循环
    a.消息是由事件产生的。
    b.事件:由输入设备触发比如鼠标、键盘等;由窗体控件触发比如button,file菜单;由Windows内部的事件。
    c.消息是事件翻译过来的
    d.消息队列:系统消息队列以及应用程序消息队列。产生的消息首先由Windows系统捕获,放在系统消息队列里,再拷贝到对应的应用程序消息队列。
    e.消息循环:系统为每个应用程序维护一个消息循环,消息循环会不断检索自身的消息队列。来一个消息,就用GetMessage()取出消息。
    在这里插入图片描述

2 基本流程

  1. 设计一个窗口类
// 1 定义和配置窗口信息
WNDCLASS wndcls;
wndcls.cbClsExtra = NULL;
wndcls.cbWndExtra = NULL;
wndcls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndcls.hCursor = LoadCursor(NULL, IDC_ARROW);
wndcls.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndcls.hInstance = hInstance;
// 2 定义交互响应
wndcls.lpfnWndProc = MyWinProc;//回调
// 3 定义窗口代号
wndcls.lpszClassName = (LPCTSTR)"My"; 
wndcls.lpszMenuName = NULL; 
wndcls.style = CS_HREDRAW | CS_VREDRAW;// 每当窗口更改大小时,让应用程序重新绘制工作区的整个内容
  1. 注册窗口类
	// 注册窗口类
	RegisterClass(&wndcls);
  1. 创建窗口
HWND hwnd; // HWND是唯一标识和操作窗口对象
hwnd = CreateWindow(clsName, msgName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
	CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
  1. 显示以及更新窗口
ShowWindow(hwnd, SW_SHOWNORMAL);
UpdateWindow(hwnd);
  1. 消息循环
MSG msg; 
while (GetMessage(&msg, NULL, NULL, NULL)) 
{ 
	TranslateMessage(&msg); 
	DispatchMessage(&msg); 
}
  1. 回调函数
LRESULT CALLBACK MyWinProc(
	HWND hwnd, // 窗口的句柄
	UINT uMsg, // 消息的标识符 
	WPARAM wParam, // first message parameter word 
	LPARAM lParam // second message parameter long 
) {

	//uMsg 消息类型
    int ret; 
	HDC hdc; 
	switch (uMsg) {
	case WM_CHAR: char szChar[20];
		sprintf_s(szChar, "您刚才按下了: %c", wParam);
		MessageBox(hwnd, szChar, "char", NULL);
		break;
	case WM_LBUTTONDOWN:
		MessageBox(hwnd, "检测鼠标左键按下", "msg", NULL);
		break;
	case WM_PAINT: PAINTSTRUCT ps;
		hdc = BeginPaint(hwnd, &ps);
		TextOut(hdc, 0, 0, "www.baidu.com", strlen("www.baidu.com"));
		EndPaint(hwnd, &ps); MessageBox(hwnd, "重绘", "msg", NULL);
		break;
	case WM_CLOSE: ret = MessageBox(hwnd, "是否真的结束?", "msg", MB_YESNO);
		if (ret == IDYES) { DestroyWindow(hwnd); }
		break;
	case WM_DESTROY: PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hwnd, uMsg, wParam, lParam);
	}
	return 0;
}

3 整体代码以及效果

// 创建第一个win32窗口程序
#include<Windows.h>
#include<stdio.h>

LPCTSTR clsName = (LPCTSTR)"My";
LPCTSTR msgName = (LPCTSTR)"欢迎学习";

// 声明回调函数
LRESULT CALLBACK MyWinProc(
	HWND hwnd, // 窗口的句柄
	UINT uMsg, // 消息的标识符 
	WPARAM wParam, // first message parameter word 
	LPARAM lParam // second message parameter long 
);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
	// a 设计一个窗口类
	// 1 定义和配置窗口信息
	WNDCLASS wndcls;
	wndcls.cbClsExtra = NULL;
	wndcls.cbWndExtra = NULL;
	wndcls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndcls.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndcls.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndcls.hInstance = hInstance;
	// 2 定义交互响应
	wndcls.lpfnWndProc = MyWinProc;//回调
	// 3 定义窗口代号
    wndcls.lpszClassName = clsName; 
	wndcls.lpszMenuName = NULL; 
	wndcls.style = CS_HREDRAW | CS_VREDRAW;

	// b 注册窗口类
	RegisterClass(&wndcls);

	// c 创建窗口
	HWND hwnd; 
	hwnd = CreateWindow(clsName, msgName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);

	// d 显示和刷新窗口
	ShowWindow(hwnd, SW_SHOWNORMAL);
	UpdateWindow(hwnd);
	//e 消息循环 GetMessage 只有在接收到 WM_QUIT 才会返回 0 
	//TranslateMessage 翻译消息 WM_KEYDOWN 和 WM_KEYUP 合并为 WM_CAHR 
	MSG msg; 
	while (GetMessage(&msg, NULL, NULL, NULL)) 
	{ 
		TranslateMessage(&msg); 
		DispatchMessage(&msg); 
	}
	return msg.wParam;
}

LRESULT CALLBACK MyWinProc(
	HWND hwnd, // 窗口的句柄
	UINT uMsg, // 消息的标识符 
	WPARAM wParam, // first message parameter word 
	LPARAM lParam // second message parameter long 
) {

	//uMsg 消息类型
    int ret; 
	HDC hdc; 
	switch (uMsg) {
	case WM_CHAR: char szChar[20];
		sprintf_s(szChar, "您刚才按下了: %c", wParam);
		MessageBox(hwnd, szChar, "char", NULL);
		break;
	case WM_LBUTTONDOWN:
		MessageBox(hwnd, "检测鼠标左键按下", "msg", NULL);
		break;
	case WM_PAINT: PAINTSTRUCT ps;
		hdc = BeginPaint(hwnd, &ps);
		TextOut(hdc, 0, 0, "www.baidu.com", strlen("www.baidu.com"));
		EndPaint(hwnd, &ps); MessageBox(hwnd, "重绘", "msg", NULL);
		break;
	case WM_CLOSE: ret = MessageBox(hwnd, "是否真的结束?", "msg", MB_YESNO);
		if (ret == IDYES) { DestroyWindow(hwnd); }
		break;
	case WM_DESTROY: PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hwnd, uMsg, wParam, lParam);
	}
}


效果如下:
在这里插入图片描述

4 Windows数据类型

WORD: 16位无符号整形数据

DWORD: 32位无符号整型数据(DWORD32)

DWORD64: 64位无符号整型数据

INT: 32位有符号整型数据类型

INT_PTR: 指向INT数据类型的指针类型

INT32: 32位符号整型

INT64: 64位符号整型

UINT: 无符号INT

LONG: 32位符号整型(LONG32)

ULONG: 无符号LONG

LONGLONG: 64位符号整型(LONG64)

SHORT: 无符号短整型(16位)

LPARAM: 消息的L参数

WPARAM: 消息的W参数

HANDLE: 对象的句柄,最基本的句柄类型

HICON: 图标的句柄

HINSTANCE: 程序实例的句柄

HKEY: 注册表键的句柄

HMODULE: 模块的句柄

HWND: 窗口的句柄

LPSTR: 字符指针,也就是字符串变量

LPCSTR: 字符串常量

LPCTSTR: 根据环境配置,如果定义了UNICODE宏,则是LPCWSTR类型,否则则为LPCSTR类型

LPCWSTR: UNICODE字符串常量

LPDWORD: 指向DWORD类型数据的指针

CHAR: 8比特字节

TCHAR: 如果定义了UNICODE,则为WCHAR,否则为CHAR

UCHAR: 无符号CHAR

WCHAR: 16位Unicode字符

BOOL: 布尔型变量

BYTE: 字节类型(8位)

CONST: 常量

FLOAT: 浮点数据类型

SIZE_T: 表示内存大小,以字节为单位,其最大值是CPU最大寻址范围

VOID: 无类型,相当于标准C语言中的void

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

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

相关文章

【AI提示词故事】雪的诗意:静谧与活力的奇妙交织

雪的诗意&#xff1a;沉浸在雪景的浪漫氛围中 冬日的清晨&#xff0c;窗外的世界被一层洁白的雪覆盖着&#xff0c;仿佛是大自然为我们准备的一幅美丽画卷。 我走出房门&#xff0c;踏上雪地&#xff0c; 那柔软的雪粒在脚下发出轻柔的咯吱声&#xff0c;仿佛是在诉说着冬日的…

SQL进阶:子查询

一般情况下,我们都是直接对表进行查询,但有时候,想要的数据可能通过一次select 获取不到,需要嵌套select,这样就形成了子查询。 子查询可以位于查询语句的任意位置,主要的注意点在于用于不同的位置,和不同的关键字一起使用时,需要注意返回的列的数量和行的数量。 位于…

Kubernetes(K8S)快速入门

概述 在本门课程中&#xff0c;我们将会学习K8S一些非常重要和核心概念&#xff0c;已经操作这些核心概念对应组件的相关命令和方式。比如Deploy部署&#xff0c;Pod容器&#xff0c;调度器&#xff0c;Service服务&#xff0c;Node集群节点&#xff0c;Helm包管理器等等。 在…

第十四章 集合(List)

一、集合框架体系 集合&#xff1a; &#xff08;1&#xff09;可以动态保存任意多个对象。 &#xff08;2&#xff09;提供了一系列方便的操作对象的方法&#xff1a;add、remove、set、get等。 二、Collection 1. Collection 接口常用方法 &#xff08;1&#xff09;add&a…

华为OD机试 - 多段线数据压缩(Java JS Python C)

在线OJ刷题 题目详情 - 多段线数据压缩 - Hydro 题目描述 下图中,每个方块代表一个像素,每个像素用其行号和列号表示。 为简化处理,多线段的走向只能是水平、竖直、斜向45度。 上图中的多线段可以用下面的坐标串表示:(2,8),(3,7),(3,6),(3,5),(4,4),(5,3),(6,2),(7,3),(…

C语言中常见的笔试题(二)

题目一&#xff1a; 问题&#xff1a; 在C语言中&#xff0c;const关键字有哪些用途&#xff1f;请列举出至少三种用途&#xff0c;并给出相应的代码示例。 答案&#xff1a; 定义常量&#xff1a;使用const关键字可以定义常量&#xff0c;它们的值在程序运行期间不能被修改…

React 路由跳转

1. push 与 replace 模式 默认情况下&#xff0c;开启的是 push 模式&#xff0c;也就是说&#xff0c;每次点击跳转&#xff0c;都会向栈中压入一个新的地址&#xff0c;在点击返回时&#xff0c;可以返回到上一个打开的地址&#xff0c; 就像上图一样&#xff0c;我们每次返…

U4_2 语法分析-自底向上分析-算符优先分析

文章目录 一、回顾1、推导 vs 规约2、句型、短语、简单短语、句柄 二、自底向上&#xff08;移进-规约&#xff09;分析方法1、基本思想2、分析过程3、移进—归约分析&#xff08;Shift-Reduce Parsing) 三、算符优先分析1、概念2、分析流程1&#xff09;先确定终结符之间的优先…

Codeforces Round 917 (Div. 2)(A~D)(又是数学题)

A - Least Product 题意&#xff1a; 思路&#xff1a;若有奇数个负数&#xff0c;则不需要任何操作。若存在0&#xff0c;也不需要任何操作。其余情况将任意一个数改为0即可。 #include <bits/stdc.h> using namespace std; void solve() {int n;cin >> n;int …

CUMT--Java复习--网络编程

目录 一、Java网络API 1、InetAddress类 2、URL类 3、URLConnection类 4、URLDecoder类和URLEncoder类 二、基于TCP的网络编程 1、Socket类 2、ServerSocket类 三、网络通信过程 一、Java网络API Java中有关网络方面的功能都定义在java.net中。 1、InetAddress类 Jav…

MySQL undo日志精讲3-从回滚段中申请 Undo 页面链表

回滚段-Rollback Segment Header 页面 回滚段的概念 我们现在知道一个事务在执行过程中最多可以分配4个 Undo 页面链表&#xff0c;在同一时刻不同事务拥有的 Undo 页面链表是不一样的&#xff0c;所以在同一时刻系统里其实可以有许许多多个 Undo 页面链表存在。为了更好的管…

工业互联网:数字化制造的未来

引言 在当今的数字化时代&#xff0c;制造业正经历着革命性的变革。工业互联网&#xff08;Industrial Internet of Things"&#xff0c;简称 IIoT&#xff09;作为这一变革的核心引擎&#xff0c;正在重新定义现代工业和制造。本文将探讨工业互联网的基础、关键技术、应…

论文润色的原理是什么 PaperBERT

大家好&#xff0c;今天来聊聊论文润色的原理是什么&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff0c;可以借助此类工具&#xff1a; 标题&#xff1a;论文润色的原理――探究论文润色背后的科学依据 一、…

第十五节TypeScript 接口

1、简介 接口是一系列抽象方法的声明&#xff0c;是一些方法特征的集合&#xff0c;这些方法都应该是抽象的&#xff0c;需要有由具体的类去实现&#xff0c;然后第三方就可以通过这组抽象方法调用&#xff0c;让具体的类执行具体的方法。 2、接口的定义 interface interface_…

利用STM32和可控硅控制220V加热电路

利用STM32和可控硅控制220V加热电路 Chapter1 利用STM32和可控硅控制220V加热电路一、错误原理图二、正确原理图 Chapter2 可控硅驱动芯片MOC3081/3061Chapter3 一个MOC3061的可控硅触发电路的分析Chapter4 可控硅的两种触发方式&#xff1a;移相触发和过零触发1、过零触发2、移…

C++面试宝典第9题:找出第K大元素

题目 给定一个整数数组a,同时给定它的大小N和要找的K(1 <= K <= N),请根据快速排序的思路,找出数组中第K大的数(保证答案存在)。比如:数组a为[50, 23, 66, 18, 72],数组大小N为5,K为3,则第K大的数为50。 解析 这道题主要考察应聘者对于快速排序的理解,以及实…

​【C语言】乘法表

题目要求&#xff1a; 实现一个函数&#xff0c;打印乘法口诀表&#xff0c;口诀表的行数和列数自己指定 如&#xff1a;输入9&#xff0c;输出9 * 9口诀表&#xff0c;输出12&#xff0c;输出12 * 12的乘法口诀表。 题目分析&#xff1a; 我们观察乘法口诀表可以发现&#x…

【重点!!!】【堆】215.数组中的第K个最大元素

题目 法1&#xff1a;小根堆 最大的K个元素 > 小根堆&#xff08;类似上窄下宽的梯形&#xff09; 最小的K个元素 > 大根堆&#xff08;类似倒三角形&#xff09; 必须掌握&#xff01;&#xff01;&#xff01; class Solution {public int findKthLargest(int[] nu…

4.3 媒资管理模块 - Minio系统上传图片与视频

文章目录 一、上传图片1.1 需求分析1.2 数据模型1.2.1 media_files 媒资信息表 1.3 准备Minio环境1.3.1 桶环境1.3.2 连接Minio参数1.3.3 Minio配置类 1.4 接口定义1.4.1 上传图片接口请求参数1.4.2 上传图片接口返回值1.4.3 接口代码 1.5 MediaFilesMapper1.6 MediaFileServic…

解决log4j多个日志都写到一个文件

之前客户端程序由于Websockt包依赖的log4j&#xff0c;就用log4j写日志了&#xff0c;Web用的log4j2没毛病。用log4j的多个logger的日志都写到一个文件里了&#xff0c;查了很多资料都没解决。今天闲了解决一下。 最后好使的配置 # 设置日志根 log4j.rootLogger INFO,Except…