首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

VC++ 匿名管道程式設計完整指南:從原理到實戰

     編輯:VC 教學

什麼是匿名管道?

管道是進程間通訊 (IPC) 的一種共享記憶體機制。建立管道的進程稱為「管道伺服器」,連接管道的進程為「管道客戶端」。資料由一端寫入,另一端讀取,實現單向流動。

匿名管道 特點:

  • 無名稱,無法透過名稱開啟
  • 僅限父子進程間使用(需透過句柄繼承)
  • 本機通訊,不支援跨網路
  • 單向傳輸:一個讀端、一個寫端

核心 API 詳解

CreatePipe —— 建立管道

BOOL CreatePipe(
    PHANDLE hReadPipe,        // 輸出:讀端句柄
    PHANDLE hWritePipe,       // 輸出:寫端句柄
    LPSECURITY_ATTRIBUTES lpPipeAttributes, // 安全屬性(控制繼承性)
    DWORD nSize               // 緩衝區大小建議值(0 為預設)
);

關鍵點:lpPipeAttributes->bInheritHandle = TRUE 才能讓子進程繼承句柄。

句柄傳遞與標準流重導向

父進程需將管道一端句柄傳給子進程,常用方式:

  1. 建立管道時設定 bInheritHandle = TRUE
  2. 呼叫 SetStdHandle(STD_OUTPUT_HANDLE, hWrite) 將標準輸出重導向至管道寫端
  3. 啟動子進程 CreateProcess(..., TRUE, ...) 參數 bInheritHandles = TRUE
  4. 子進程透過 GetStdHandle(STD_INPUT_HANDLE) 取得管道讀端
  5. 父進程恢復自身標準輸出 SetStdHandle(STD_OUTPUT_HANDLE, hTemp)

完整範例:父進程讀取子進程輸出

父進程(管道伺服器)

#include <windows.h>
#include <stdio.h>

int main() {
    STARTUPINFO si = { sizeof(si) };
    PROCESS_INFORMATION pi;
    HANDLE hRead, hWrite;
    char buf[1024];
    DWORD bytesRead;

    // 1. 建立可繼承的匿名管道
    SECURITY_ATTRIBUTES sa = { sizeof(sa), NULL, TRUE };
    if (!CreatePipe(&hRead, &hWrite, &sa, 0)) {
        printf("建立管道失敗,錯誤碼: %d\n", GetLastError());
        return 1;
    }

    // 2. 設定子進程標準輸出 = 管道寫端
    HANDLE hOldOut = GetStdHandle(STD_OUTPUT_HANDLE);
    SetStdHandle(STD_OUTPUT_HANDLE, hWrite);

    // 3. 啟動子進程
    GetStartupInfo(&si);
    si.hStdOutput = hWrite;
    si.hStdError = hWrite;
    si.dwFlags |= STARTF_USESTDHANDLES;

    if (!CreateProcess(NULL, "Child.exe", NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
        printf("建立子進程失敗: %d\n", GetLastError());
        return 1;
    }

    // 4. 父進程不需要寫端,關閉避免阻塞
    CloseHandle(hWrite);
    SetStdHandle(STD_OUTPUT_HANDLE, hOldOut); // 恢復父進程標準輸出

    // 5. 讀取管道資料直到 EOF
    while (ReadFile(hRead, buf, sizeof(buf) - 1, &bytesRead, NULL) && bytesRead > 0) {
        buf[bytesRead] = '\0';
        printf("[父進程讀取] %s", buf);
    }

    DWORD err = GetLastError();
    if (err == ERROR_BROKEN_PIPE) printf("管道已由子進程關閉\n");
    else if (err != ERROR_SUCCESS) printf("讀取錯誤: %d\n", err);

    CloseHandle(hRead);
    WaitForSingleObject(pi.hProcess, INFINITE);
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    return 0;
}

子進程

#include <windows.h>
#include <stdio.h>
#include <iostream>

int main() {
    for (int i = 0; i < 10; ++i) {
        printf("stdout: 第 %d 行輸出\n", i);   // 導向管道
        fprintf(stderr, "stderr: 錯誤訊息 %d\n", i); // 導向管道(因 hStdError = hWrite)
        Sleep(100);
    }
    return 0; // 進程結束自動關閉句柄,父進程 ReadFile 返回 FALSE
}

關鍵注意事項

項目說明
同步阻塞WriteFile 在緩衝區滿時阻塞;ReadFile 在無資料時阻塞
非支援異步匿名管道不支援 ReadFileEx/WriteFileEx,lpOverlapped 會被忽略
關閉條件所有寫端句柄關閉後,讀端 ReadFile 返回 0(EOF)
緩衝區大小建議值 4KB~64KB,過大浪費記憶體,過小增加切換開銷
錯誤處理務必檢查 GetLastError(),區分 ERROR_BROKEN_PIPE(正常結束)與其他錯誤

進階變體

  • 雙向通訊:建立兩組管道(父讀/子寫、父寫/子讀)
  • 非父子進程:透過 DuplicateHandle 跨進程傳遞句柄,或使用命名管道
  • 高效能場景:考慮 I/O 完成埠 (IOCP) + 命名管道替代

匿名管道雖簡單,但掌握「句柄繼承 + 標準流重導向」的核心模式,是 Windows IPC 開發的必修課。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved