本博客主要總結MFC中匿名管道的原理和具體調用實例,以及調用匿名管道三個核心函數各個參數用法詳解,具體的如下所述。
博主在做項目時,遇到一個問題。用程序調用一個進程,然後讀取進程輸出信息。但是,博主用Qt的QProcess無法讀取標準輸出,所以只能考慮管道技術。
由於博主的開發環境是Windows10 64位,Qt的QProcess並沒有找到類似的功能(可能博主對Qt研究不夠深入,希望知道的大神告知一下),所以打算用MFC的匿名管道。
經過資料的查找和驗證,博主發現,MFC的匿名管道技術可以實現讀取CMD進程輸出的內容,所以特定將MFC匿名管道用法記錄下來,學習和總結。
匿名管道概念解釋:
匿名管道主要用於進程間通信,進程的關係爲父進程和子進程。具體原理如下圖所示:
父進程和子進程都有讀端口和寫端口。通信方式可以從父進程寫數據,子進程讀數據。或者子進程寫數據,父進程讀數據。
其中,本文下面的例子是用圖二的從子進程寫數據,從父進程讀取數據。即子進程通過cmd命令執行程序,然後程序輸入的內容通過寫句柄hWritePipe,寫入內核(直接調用CrateProcess()函數,設置對應參數就可以寫數據到內核。而不用調用WriteFile()函數)。父進程根據子進程管道的讀句柄hReadPipe,調用ReadFile()函數讀取內核數據。
一、MFC創建管道主要步驟
用MFC編寫匿名管道的核心函數有三個,分別是CreatePipe(),CreateProcess(),ReadFile()三個核心函數。其中,函數CreatePipe()主要功能是創建一個管道通信,函數CreateProcess()主要功能是創建一個進程,函數ReadFile()讀取進程輸出的內容。下面是對創建管道通信步驟的總結:
1.1先創建調用函數CreatePipe()創建一個管道通信。關鍵代碼爲:
CreatePipe(&hReadPipe, &hWritePipe, &safety, 0);
1.2調用函數CreateProcess()創建一個進程,該進程輸出的內容作爲管道的寫端口,向管道寫數據。關鍵代碼爲:(其中寫的句柄hWritePipe在結構體startInfo裏的參數設置)
CreateProcess(NULL, cmdStr, NULL, NULL, TRUE, NULL, NULL, NULL, &startupInfo, &pinfo);
1.3調用函數ReadFile()作爲管道的讀端口,讀取進程寫入管道的內容。關鍵代碼爲:(其中讀取內存存在緩衝區buffer)
ReadFile(hReadPipe, buffer, 4095, &byteRead, NULL);
1.4下面是博主封裝的一個函數。該函數的功能是輸入cmd命令,返回cmd輸出內容。
具體代碼如下所示:
CString executeCmd(CString command)
{
//創建匿名管道
HANDLE hReadPipe, hWritePipe;
SECURITY_ATTRIBUTES safety; //安全屬性
safety.nLength = sizeof(SECURITY_ATTRIBUTES); //結構體大小
safety.lpSecurityDescriptor = NULL; //安全描述符,NULL;使用默認的
safety.bInheritHandle = TRUE; //安全描述符的對象能否被子進程繼承
if (!CreatePipe(&hReadPipe, &hWritePipe, &safety, 0))
{
//創建管道錯誤
return _T("創建管道錯誤!");
}
//創建進程
TCHAR *cmdStr = StringToChar(command);
STARTUPINFO startupInfo = { sizeof(startupInfo) }; //進程信息
startupInfo.hStdError = hWritePipe; //標誌控制檯窗口緩存
startupInfo.hStdOutput = hWritePipe; //標誌控制檯窗口緩存
startupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; //使用wSHOWWIndows成員
startupInfo.wShowWindow = SW_HIDE;
PROCESS_INFORMATION pinfo;
if (!CreateProcess(NULL, cmdStr, NULL, NULL, TRUE, NULL, NULL, NULL, &startupInfo, &pinfo))
{
//創建進程錯誤
return _T("創建進程錯誤!");
}
CloseHandle(hWritePipe);
//獲取管道信息
char buffer[4096];
memset(buffer, 0, 4096);
DWORD byteRead;
CString output; //返回值
while (true)
{
if (ReadFile(hReadPipe, buffer, 4095, &byteRead, NULL) == NULL)
{
break;
}
output += buffer;
}
CloseHandle(hReadPipe);
return output;
}
TCHAR* StringToChar(CString& str)
{
int len = str.GetLength();
TCHAR* tr = str.GetBuffer(len);
str.ReleaseBuffer();
return tr;
}
1.5函數調用的一個實例
CString cmdString = _T("ipconfig.exe /?");
CString output = executeCmd(cmdString);
AfxMessageBox(output);
1.6運行結果如下圖所示:
二、關鍵函數參數講解
2.1函數CreatePipe()各個參數如下所示:
BOOL CreatePipe(
_Out_ PHANDLE hReadPipe, //管道讀端口句柄
_Out_ PHANDLE hWritePipe, //管道寫端口句柄
_In_opt_ LPSECURITY_ATTRIBUTES lpPipeAttributes, //安全描述符,通過設置參數,可以設置能否被支線程繼承
_In_ DWORD nSize //0表示管道緩衝設置爲系統默認值
);
2.2函數CreateProcess()各個參數如下所示:
BOOL CreateProcess(
_In_opt_ LPCWSTR lpApplicationName, //指向要調用的程序路徑
_Inout_opt_ LPWSTR lpCommandLine, //輸入的命令行TCHAR*字符串
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, //結構體SECURTY_ATTRIBUTE,指向進程安全描述符
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, //結構體SECURITY_ATTRIBUTE,指向線程安全描述符
_In_ BOOL bInheritHandles, //新進程是否從調用進程處繼承了句柄
_In_ DWORD dwCreationFlags, //附加的、用來控制優先類和進程的創建標誌。設置爲CREATE_NEW_CONSOLE可顯示子窗口。
_In_opt_ LPVOID lpEnvironment, //指向使用父類的環境
_In_opt_ LPCWSTR lpCurrentDirectory, //使用父類的當前目錄
_In_ LPSTARTUPINFOW lpStartupInfo, //結構體STARTUPINFO,指向一個用於決定新進程的主窗體如何顯示
_Out_ LPPROCESS_INFORMATION lpProcessInformation //結構體PROCESS_INFORMATION,指向一個用來接收新進程的識別信息
);
2.3函數ReadFile()各個參數如下所示:
BOOL ReadFile(
_In_ HANDLE hFile, //文件句柄
_Out_writes_bytes_to_opt_(nNumberOfBytesToRead, *lpNumberOfBytesRead) __out_data_source(FILE) LPVOID lpBuffer, //讀緩衝區
_In_ DWORD nNumberOfBytesToRead, //要讀取的字節數
_Out_opt_ LPDWORD lpNumberOfBytesRead, //實際讀取到的字節數
_Inout_opt_ LPOVERLAPPED lpOverlapped //指向結構體OVERLAPPED,一般設爲NULL
);
參考內容:
https://blog.csdn.net/it2153534/article/details/79064643(參考:重點函數各個參數講解)
https://blog.csdn.net/qq61394323/article/details/39829631(參考:MFC讀取CMD輸出內容代碼)
https://blog.csdn.net/qq61394323/article/details/39253193?utm_source=blogxgwz2(參考:匿名管道讀寫句柄用法)
https://blog.csdn.net/whynottrythis/article/details/39828395(參考:STARTUPINFO結構體各個參數講解)
https://blog.csdn.net/jeanphorn/article/details/44982273(參考:函數ReadFile()參數講解和調用實例,主要參考)
https://blog.csdn.net/virtualdesk/article/details/4379965(參考:函數ReadFile()參數講解和調用實例)