C++ 進程和匿名管道使用學習


平台

Windows10 + VS2015

學習內容

  • 進程的創建使用(CreateProcess方式)
  • 父子進程間匿名管道通信

相關函數及參數介紹

  • CreatePipe函數:該的原型為
CreatePipe(_Out_ PHANDLE hReadPipe,
           _Out_ PHANDLE hWritePipe,
           _In_opt_ LPSECURITY_ATTRIBUTES lpPipeAttributes,
           _In_ DWORD nSize);

hReadPipe:返回一個可用於讀管道數據的文件句柄;hWritePipe:返回一個可用於寫管道數據的文件句柄;lpPipeAttributes:傳入一個SECURITY_ATTRIBUTES結構的指針,該結構用於決定該函數返回的句柄是否可被子進程繼承;nSize:管道的緩沖區大小,但是這僅僅只是一個理想值,系統根據這個值創建大小相近的緩沖區。如果傳入0 ,那么系統將使用一個默認的緩沖區大小。需要注意讀寫管道數據的句柄參數位置

  • LPSECURITY_ATTRIBUTES類型:相關定義內容為
typedef struct _SECURITY_ATTRIBUTES {
    DWORD nLength;
    LPVOID lpSecurityDescriptor;
    BOOL bInheritHandle;
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;

nLength:結構體的大小,可用sizeof取得;lpSecurityDescriptor:安全描述符;bInheritHandle:安全描述的對象能否被新創建的進程繼承。

  • CreateProcess函數:WIN32API函數CreateProcess用來創建一個新的進程和它的主線程,這個新進程運行指定的可執行文件。該函數原型有兩種可能,當前環境已定義UNICODE,只參考CreateProcessW
#ifdef UNICODE
#define CreateProcess  CreateProcessW
#else
#define CreateProcess  CreateProcessA
#endif // !UNICODE

WINBASEAPI
BOOL
WINAPI
CreateProcessW(
    _In_opt_ LPCWSTR lpApplicationName,
    _Inout_opt_ LPWSTR lpCommandLine,
    _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
    _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
    _In_ BOOL bInheritHandles,
    _In_ DWORD dwCreationFlags,
    _In_opt_ LPVOID lpEnvironment,
    _In_opt_ LPCWSTR lpCurrentDirectory,
    _In_ LPSTARTUPINFOW lpStartupInfo,
    _Out_ LPPROCESS_INFORMATION lpProcessInformation
    );

lpApplicationName:指向一個NULL結尾的、用來指定可執行模塊的字符串,這個參數可以被設為NULL,在這種情況下,可執行模塊的名字必須處於 lpCommandLine 參數最前面並由空格符與后面的字符分開;lpCommandLine:指向一個以NULL結尾的字符串,該字符串指定要執行的命令行,這個參數可以為空,那么函數將使用lpApplicationName參數指定的字符串當做要運行的程序的命令行;lpProcessAttributes:指向一個SECURITY_ATTRIBUTES結構體,這個結構體決定是否返回的句柄可以被子進程繼承;lpThreadAttributes:同lpProcessAttribute,不過這個參數決定的是線程是否被繼承.通常置為NULL;bInheritHandles:指示新進程是否從調用進程處繼承了句柄,如果參數的值為真,調用進程中的每一個可繼承的打開句柄都將被子進程繼承,被繼承的句柄與原進程擁有完全相同的值和訪問權限;dwCreationFlags:指定附加的、用來控制優先類和進程的創建的標志;lpEnvironment:指向一個新進程的環境塊,如果此參數為空,新進程使用調用進程的環境;lpCurrentDirectory:指向一個以NULL結尾的字符串,這個字符串用來指定子進程的工作路徑,這個字符串必須是一個包含驅動器名的絕對路徑;lpStartupInfo指向一個用於決定新進程的主窗體如何顯示的STARTUPINFO結構體;lpProcessInformation:指向一個用來接收新進程的識別信息的PROCESS_INFORMATION結構體。

  • dwCreationFlags參數:dwCreationFlags可選類型列表如下
參數值 描述
CREATE_DEFAULT_ERROR_MODE 新的進程不繼承調用進程的錯誤模式
CREATE_NEW_CONSOLE 新的進程將使用一個新的控制台,而不是繼承父進程的控制台。這個標志不能與DETACHED_PROCESS標志一起使用
CREATE_NEW_PROCESS_GROUP 新進程將是一個進程樹的根進程
CREATE_SEPARATE_WOW_VDM 如果被設置,新進程將會在一個私有的虛擬DOS機(VDM)中運行
CREATE_SHARED_WOW_VDM 如果WIN.INI中的Windows段的DefaultSeparateVDM選項被設置為真,這個標識使得CreateProcess函數越過這個選項並在共享的虛擬DOS機中運行新進程
CREATE_SUSPENDED 新進程的主線程會以暫停的狀態被創建,直到調用ResumeThread函數被調用時才運行
CREATE_UNICODE_ENVIRONMENT 如果被設置,由lpEnvironment參數指定的環境塊使用Unicode字符,如果為空,環境塊使用ANSI字符
DEBUG_PROCESS 如果這個標志被設置,調用進程將被當做一個調試程序,並且新進程會被當做被調試的進程。系統把被調試程序發生的所有調試事件通知給調試器,如果使用這個標志創建進程,只有調用進程(調用CreateProcess函數的進程)可以調用WaitForDebugEvent函數
DEBUG_ONLY_THIS_PROCESS 如果此標志沒有被設置且調用進程正在被調試,新進程將成為調試調用進程的調試器的另一個調試對象。如果調用進程沒有被調試,有關調試的行為就不會產生
DETACHED_PROCESS 對於控制台進程,新進程沒有訪問父進程控制台的權限。新進程可以通過AllocConsole函數自己創建一個新的控制台。這個標志不可以與CREATE_NEW_CONSOLE標志一起使用
CREATE_NO_WINDOW 系統不為新進程創建CUI窗口,使用該標志可以創建不含窗口的CUI程序
HIGH_PRIORITY_CLASS 指示這個進程將執行時間臨界的任務,所以它必須被立即運行以保證正確。這個優先級的程序優先於正常優先級或空閑優先級的程序
IDLE_PRIORITY_CLASS 指示這個進程的線程只有在系統空閑時才會運行並且可以被任何高優先級的任務打斷
NORMAL_PRIORITY_CLASS 指示這個進程沒有特殊的任務調度要求
REALTIME_PRIORITY_CLASS 指示這個進程擁有可用的最高優先級。一個擁有實時優先級的進程的線程可以打斷所有其他進程線程的執行,包括正在執行重要任務的系統進程
  • STARTUPINFO參數:該參數原型有兩種可能,當前環境已定義UNICODE,只參考STARTUPINFOW
#ifdef UNICODE
typedef STARTUPINFOW STARTUPINFO;
typedef LPSTARTUPINFOW LPSTARTUPINFO;
#else
typedef STARTUPINFOA STARTUPINFO;
typedef LPSTARTUPINFOA LPSTARTUPINFO;
#endif // UNICODE

typedef struct _STARTUPINFOW {
    DWORD   cb;
    LPWSTR  lpReserved;
    LPWSTR  lpDesktop;
    LPWSTR  lpTitle;
    DWORD   dwX;
    DWORD   dwY;
    DWORD   dwXSize;
    DWORD   dwYSize;
    DWORD   dwXCountChars;
    DWORD   dwYCountChars;
    DWORD   dwFillAttribute;
    DWORD   dwFlags;
    WORD    wShowWindow;
    WORD    cbReserved2;
    LPBYTE  lpReserved2;
    HANDLE  hStdInput;
    HANDLE  hStdOutput;
    HANDLE  hStdError;
} STARTUPINFOW, *LPSTARTUPINFOW;

結構體參數內容較多,練習使用時只用到了cb``dwFlags``wShowWindow``hStdInput``hStdOutput``hStdError,具體參數解釋如下表

參數 描述
cb 包含STARTUPINFO結構中的字節數,應用程序必須將cb初始化為sizeof(STARTUPINFO)
lpReserved 保留,必須初始化為NULL
lpDesktop 用於標識啟動應用程序所在的桌面的名字。如果該桌面存在,新進程便與指定的桌面相關聯。如果桌面不存在,便創建一個帶有默認屬性的桌面,並使用為新進程指定的名字。如果lpDesktop是NULL(這是最常見的情況),那么該進程將與當前桌面相關聯
lpTitle 用於設定控制台窗口的名稱。如果lpTitle是NULL,則可執行文件的名字將用作窗口名
dwX 用於設定應用程序窗口在屏幕上應該放置的位置的x坐標(以像素為單位)
dwY 用於設定應用程序窗口在屏幕上應該放置的位置的y坐標(以像素為單位),只有當子進程用CW_USEDEFAULT作為CreateWindow的x參數來創建它的第一個重疊窗口時,才使用這兩個坐標。若是創建控制台窗口的應用程序,這些成員用於指明控制台窗口的左上角
dwXSize 用於設定應用程序窗口的寬度(以像素為單位)當子進程將CW_USEDEFAULT用作CreateWindow的nWidth參數來創建它的第一個重疊窗口時,才使用
dwYSize 用於設定應用程序窗口的長度(以像素為單位)當子進程將CW_USEDEFAULT用作CreateWindow的nWidth參數來創建它的第一個重疊窗口時,才使用
dwXCountChars 用於設定子應用程序的控制台窗口的寬度(以字符為單位)
dwYCountChars 用於設定子應用程序的控制台窗口的高度(以字符為單位)
dwFillAttribute 用於設定子應用程序的控制台窗口使用的文本和背景顏色
dwFlags 子進程窗口標志
wShowWindow 用於設定如果子應用程序初次調用的ShowWindow將SW_SHOWDEFAULT作為nCmdShow參數傳遞時,該應用程序的第一個重疊窗口應該如何出現
cbReserved2 保留,必須被初始化為0
lpReserved2 保留,必須被初始化為NULL
hStdInput 用於設定供控制台輸入用的緩存的句柄
hStdOutput 用於設定供控制台輸出用的緩存的句柄
hStdError 用於設定供控制台輸出用的緩存的句柄
  • dwFlags參數:該參數可選類型如下
參數 描述
STARTF_USESIZE 使用dwXSize和dwYSize成員
STARTF_USESHOWWINDOW 使用wShowWindow成員
STARTF_USEPOSITION 使用dwX和dwY成員
STARTF_USECOUNTCHARS 使用dwXCountChars和dwYCountChars成員
STARTF_USEFILLATTRIBUTE 使用dwFillAttribute成員
STARTF_USESTDHANDLES 使用hStdInput、hStdOutput和hStdError成員
STARTF_RUN_FULLSCREEN 強制在x86計算機上運行的控制台應用程序以全屏幕方式啟動運行

父進程代碼

1 .父進程的功能是創建管道和創建進程
2 .創建成功后等待控制台輸入信息並通過管道發送給子進程
3 .等待一段時間后從管道讀取數據
4 .將管道數據輸出到控制台

#include <iostream>
#include <windows.h>
using namespace std;

char rxbuff[100] = { 0 };
char txbuff[100] = { 0 };
DWORD txcount = 0, rxcount = 0;

void main(void)
{
	SECURITY_ATTRIBUTES sa;
	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
	sa.lpSecurityDescriptor = NULL;
	sa.bInheritHandle = TRUE;

	//管道創建
	HANDLE hwrite, hread;
	if (CreatePipe(&hread,&hwrite,&sa,0)==NULL)
	{
		cout << "pPipe Create error!" << endl;
		return;
	}

	//進程創建
	STARTUPINFO si;
	ZeroMemory(&si,sizeof(STARTUPINFO));
	si.cb = sizeof(STARTUPINFO);
	si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
	si.wShowWindow = SW_SHOW;
	si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
	si.hStdOutput = hwrite;
	si.hStdInput = hread;

	PROCESS_INFORMATION pi;	
	TCHAR exe[] = TEXT("E:\\Soft_Pro\\VC_work\\MD5_Check\\Debug\\MD5_Check.exe");
	//創建子進程
	if (CreateProcess(NULL,
					  exe,
					  NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)==NULL)
	{
		cout << "進程創建錯誤" << endl;
		CloseHandle(hread);
		CloseHandle(hwrite);
		hread = NULL;
		hwrite = NULL;
		return;
	}
	else
	{
		CloseHandle(pi.hThread);
		CloseHandle(pi.hProcess);
	}

	cin >> txbuff;
	//父進程往管道寫數據
	if (!WriteFile(hwrite, txbuff, sizeof(txbuff), &txcount, NULL))
	{
		cout << "父進程寫數據失敗!" << endl;
		return;
	}
	else
	{
		cout << "父進程寫數據成功!" << endl;
	}

	Sleep(300);

	//父進程從管道讀取數據
	if (!ReadFile(hread,rxbuff,100,&rxcount,NULL))
	{
		cout << "父進程讀取失敗!" << endl;
		return;
	}
	else
	{
		cout << "父進程管道讀取數據:" << rxbuff << endl;
	}

	system("pause");
}

子進程代碼

1 .子進程獲取管道數據的文件句柄
2 .子進程從輸入端讀取管道數據
3 .子進程將讀取的數據從輸出端發送至管道

#include <iostream>
#include <windows.h>
using namespace std;

char rxbuff[100] = {0};
char txbuff[100] = {0};
DWORD txcount = 0, rxcount = 0;
HANDLE hRead, hWrite;

int main(void)
{
	hRead = GetStdHandle(STD_INPUT_HANDLE);
	hWrite = GetStdHandle(STD_OUTPUT_HANDLE);

        //讀管道數據
	if (!ReadFile(hRead,rxbuff,100,&rxcount,NULL))
	{
		cout << "子進程讀管道失敗" << endl;
		return 0;
	}
	sprintf_s(txbuff, "子進程應答:%s\n", rxbuff);

	Sleep(200);

        //寫管道數據
	if (!WriteFile(hWrite,txbuff,sizeof(txbuff),&txcount,NULL))
	{
		cout << "子進程寫失敗!" << endl;
		return 0;
	}

	return 0;
}

父進程運行結果

額外說明

  • CreateProcess的lpCommandLine參數使用TEXT("path")方式創建進程時總是報錯,后來在一篇博客里看到了解決方案
  • 初次學習還有部分問題,后續繼續補充


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM