进程间通信 - 匿名管道实现
引子
前面的兩篇博文分別介紹了通過剪貼板和通過郵槽來實現(xiàn)進程之間的通信,
其中剪貼板呢,是用來實現(xiàn)本地兩個進程之間的通信,
而郵槽則既可以實現(xiàn)本地兩個進程之間的通信,也可以實現(xiàn)本地進程和服務器進程的通信,
當然,上面這兩種方式都是有局限性的,比如剪貼板就只能實現(xiàn)本地進程之間的通信,
而在郵槽中,雖然是既可以實現(xiàn)本地進程之間的通信,又能實現(xiàn)本地進程和遠程服務器進程的通信,
但是使用郵槽的話,服務端只能接收數(shù)據(jù),不能發(fā)送數(shù)據(jù),而客戶端只能發(fā)送數(shù)據(jù)而不能接收數(shù)據(jù)。
而本篇博文介紹的這個通過匿名管道來實現(xiàn)進程之間的通信的話,
局限性就顯得更加嚴重了,
第一:匿名管道只能實現(xiàn)本地進程之間的通信,不能實現(xiàn)跨網(wǎng)絡之間的進程間的通信。
第二:匿名管道只能實現(xiàn)父進程和子進程之間的通信,而不能實現(xiàn)任意兩個本地進程之間的通信。
???????????
????????????
匿名管道概述
既然是匿名管道的話,自然,就是沒有名字的管道了,還有一種管道呢,叫做命名管道,
命名管道的功能是很強大的,匿名管道在命名管道面前,功能那是簡陋的不行的,
至于命名管道的話,會留到下一篇博文中介紹的,
匿名管道正因為提供的功能很單一,所以它所需要的系統(tǒng)的開銷也就比命名管道小很多,
在本地機器上可以使用匿名管道來實現(xiàn)父進程和子進程之間的通信,
這里需要注意兩點,第一就是在本地機器上,這是因為匿名管道不支持跨網(wǎng)絡之間的兩個進程之間的通信,
第二就是實現(xiàn)的是父進程和子進程之間的通信,而不是任意的兩個進程。
然后得話還順便介紹匿名管道的另外一種功能,其通過匿名管道可以實現(xiàn)子進程輸出的重定向,
何為輸出重定向呢?還請聽下面詳解:
比如我現(xiàn)在建立一個 Win32 的 Console 程序,然后在其中使用如下代碼來輸出一些信息:
#include <iostream> using namespace std; ? int main(int argc, char * argv) { cout<<"Zachary XiaoZhen "<<endl<<endl; cout<<"Happy New Year"<<endl<<endl; system("pause"); }那么在默認下,編譯運行上面的代碼時,Windows 會彈出一個黑框框,并且在這個黑框框中顯示一些信息,
為什么一定要將輸出的信息顯示在這個黑框框中呢?有沒有辦法讓其顯示在我們自己定義的文本框中呢?
而后我們再看一幅截圖:
上面畫了很多紅線的這個區(qū)域中的信息來自那里呢?為什么會在這個文本框中輸出呢?
其實這就可以通過匿名管道來實現(xiàn),
在卸載 QQ 游戲這幅截圖中呢,其實運行了兩個進程,
一個就是我們看到的這個輸出了圖形界面的進程,我們稱之為卸載表象進程(父進程),
而另外一個用來執(zhí)行真正意義上的卸載的進程我們稱之為卸載實質(zhì)進程(子進程)。
其實該卸載表象進程在其執(zhí)行過程中創(chuàng)建了卸載實質(zhì)進程來執(zhí)行真正的卸載操作,
而后,卸載實質(zhì)進程會輸出上面用紅色矩形標記的區(qū)域中的信息,
如果我們使用默認的輸出的話,卸載實質(zhì)進程會將上面紅色區(qū)域標記中的信息輸出到默認的黑框框中,
但是我們可以使用匿名管道來更改卸載實質(zhì)進程的輸出,
讓其將輸出數(shù)據(jù)輸入到匿名管道中,而后卸載表象進程從匿名管道中讀取到這些輸出數(shù)據(jù),
然后再將這些數(shù)據(jù)顯示到卸載表象進程的文本框中就可以了。
而上面的這種用來更改卸載實質(zhì)進程的輸出的技術就稱之為輸出重定向。
當然與之相對的還有輸入重定向的。
我們可以讓一個進程的輸入來自于匿名管道,而不是我們在黑框框中輸入數(shù)據(jù)。
話說到這份上呢,也可以點出一點東東了,
上面的這個重定向不就是利用匿名管道實現(xiàn)的父進程和子進程之間的通信嘛。
?????????????
??????????
匿名管道的使用
匿名管道主要用于本地父進程和子進程之間的通信,
在父進程中的話,首先是要創(chuàng)建一個匿名管道,
在創(chuàng)建匿名管道成功后,可以獲取到對這個匿名管道的讀寫句柄,
然后父進程就可以向這個匿名管道中寫入數(shù)據(jù)和讀取數(shù)據(jù)了,
但是如果要實現(xiàn)的是父子進程通信的話,那么還必須在父進程中創(chuàng)建一個子進程,
同時,這個子進程必須能夠繼承和使用父進程的一些公開的句柄,
為什么呢?
因為在子進程中必須要使用父進程創(chuàng)建的匿名管道的讀寫句柄,
通過這個匿名管道才能實現(xiàn)父子進程的通信,所以必須繼承父進程的公開句柄。
同時在創(chuàng)建子進程的時候,
必須將子進程的標準輸入句柄設置為父進程中創(chuàng)建匿名管道時得到的讀管道句柄,
將子進程的標準輸出句柄設置為父進程中創(chuàng)建匿名管道時得到的寫管道句柄。
然后在子進程就可以讀寫匿名管道了。
???????????????
?????????????
匿名管道的創(chuàng)建
BOOL WINAPI CreatePipe( ????????? __out?? PHANDLE hReadPipe, ????????? __out?? PHANDLE hWritePipe, ????????? __in??? LPSECURITY_ATTRIBUTES lpPipeAttributes, ????????? __in??? DWORD nSize );
參數(shù)?hReadPipe?為輸出參數(shù),該句柄代表管道的讀取句柄。
參數(shù)?hWritePipe?為輸出參數(shù),該句柄代表管道的寫入句柄。
參數(shù)?lpPipeAttributes?為一個輸入?yún)?shù),指向一個?SECURITY_ATTRIBUTES?的結(jié)構(gòu)體指針,
其檢測返回的句柄是否能夠被子進程繼承,如果此參數(shù)為?NULL?,則表明句柄不能被繼承,
在匿名管道中,由于匿名管道要在父子進程之間進行通信,
而子進程如果想要獲得匿名管道的讀寫句柄,則其只能通過從父進程繼承獲得,
當一個子進程從其父進程處繼承了匿名管道的讀寫句柄以后,
子進程和父進程之間就可以通過這個匿名管道的讀寫句柄進行通信了。
所以在這里必須構(gòu)建一個?SECURITY_ATTRIBUTES?的結(jié)構(gòu)體,
并且該結(jié)構(gòu)體的第三個結(jié)構(gòu)成員變量?bInheritHandle?參數(shù)必須設置為?TRUE?,
從而讓子進程可以繼承父進程所創(chuàng)建的匿名管道的讀寫句柄。
typedef struct _SECURITY_ATTRIBUTES { ? DWORD nLength; ? LPVOID lpSecurityDescriptor; ? BOOL bInheritHandle; ? } SECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;參數(shù)?nSize?用來指定緩沖區(qū)的大小,
如果此參數(shù)設置為?0?,則表明系統(tǒng)將使用默認的緩沖區(qū)大小。一般將該參數(shù)設置為?0?即可。
?????????
??????????????
子進程的創(chuàng)建
BOOL? CreateProcess( ??????? LPCWSTR pszImageName, ?LPCWSTR pszCmdLine, ??????? LPSECURITY_ATTRIBUTES psaProcess, ??????? LPSECURITY_ATTRIBUTES psaThread, ??????? BOOL fInheritHandles, ?DWORD fdwCreate, ??????? LPVOID pvEnvironment, ?LPWSTR pszCurDir, ??????? LPSTARTUPINFOW psiStartInfo, ??????? LPPROCESS_INFORMATION pProcInfo );參數(shù)?pszImageName?是一個指向?NULL?終止的字符串,用來指定可執(zhí)行程序的名稱。
參數(shù)?pszCmdLine?用來指定傳遞給新進程的命令行字符串,一般做法是在?pszImageName?中傳遞可執(zhí)行文件的名稱,
在?pszCmdLine?中傳遞命令行參數(shù)。
參數(shù)?psaProcess?即代表當?CreateProcess?函數(shù)創(chuàng)建進程時,需要給進程對象設置一個安全性。
參數(shù)?psaThread?代表當?CreateProcess?函數(shù)創(chuàng)建新進程后,需要給該進程的主線程對象設置一個安全性。
參數(shù)?fInheritHandles?用來指定父進程隨后創(chuàng)建的子進程是否能夠繼承父進程的對象句柄,
如果該參數(shù)設置為?TRUE?,則父進程的每一個可繼承的打開句柄都將被子進程所繼承,
繼承的句柄與原始的句柄擁有同樣的訪問權(quán)。
在匿名管道的使用中,因為子進程需要使用父進程中創(chuàng)建的匿名管道的讀寫句柄,
所以應該將這個參數(shù)設置為?TRUE?,從而可以讓子進程繼承父進程創(chuàng)建的匿名管道的讀寫句柄。
參數(shù)?fdwCreate?用來指定控件優(yōu)先級類和進程創(chuàng)建的附加標記。
如果只是為了啟動子進程,則并不需要設置它創(chuàng)建的標記,可以將此參數(shù)設置為?0,
對于這個參數(shù)的具體取值列表可以參考?MSDN 。
參數(shù)?pvEnvironment?代表指向環(huán)境塊的指針,
如果該參數(shù)設置為?NULL?,則默認將使用父進程的環(huán)境。通常給該參數(shù)傳遞?NULL。
參數(shù)?pszCurDir?用來指定子進程當前的路徑,
這個字符串必須是一個完整的路徑名,其包括驅(qū)動器的標識符,
如果此參數(shù)設置為?NULL?,那么新的子進程將與父進程擁有相同的驅(qū)動器和目錄。
參數(shù)?psiStartInfo?指向一個?StartUpInfo?的結(jié)構(gòu)體的指針,用來指定新進程的主窗口如何顯示。
typedef struct _STARTUPINFOA { ? DWORD cb; ? LPSTR lpReserved; ? LPSTR lpDesktop; ? LPSTR 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; ? } STARTUPINFOA, *LPSTARTUPINFOA;
對于?dwFlags?參數(shù)來說,如果其設置為?STARTF_USESTDHANDLES?,
則將會使用該?STARTUPINFO?結(jié)構(gòu)體中的?hStdInput?,?hStdOutput?,?hStdError?成員,
來設置新創(chuàng)建的進程的標準輸入,標準輸出,標準錯誤句柄。
參數(shù)?pProcInfo?為一個輸出參數(shù),
指向一個?PROCESS_INFORMATION?結(jié)構(gòu)體的指針,用來接收關于新進程的標識信息。
typedef struct _PROCESS_INFORMATION { HANDLE hProcess; HANDLE hThread; DWORD dwProcessId; DWORD dwThreadId; ? }PROCESS_INFORMATION;其中?hProcess?和?hThread?分別用來標識新創(chuàng)建的進程句柄和新創(chuàng)建的進程的主線程句柄。
dwProcessId?和?dwThreadId?分別是全局進程標識符和全局線程標識符。
前者可以用來標識一個進程,后者用來標識一個線程。
????????
???????????????
示例:匿名管道實現(xiàn)父子進程間通信
父進程實現(xiàn):(簡單 MFC 程序)
項目結(jié)構(gòu):
消息以及成員函數(shù)和成員變量的聲明:
public: //創(chuàng)建匿名管道 afx_msg void OnBnClickedBtnCreatePipe(); //寫匿名管道 afx_msg void OnBnClickedBtnWritePipe(); //讀匿名管道 afx_msg void OnBnClickedBtnReadPipe(); ? //定義父進程讀匿名管道的成員函數(shù) void ParentReadPipe(void); //定義父進程寫匿名管道的成員函數(shù) void ParentWritePipe(void); //創(chuàng)建 SECURITY_ATTRIBUTES 結(jié)構(gòu)的成員函數(shù) void CreateSecurityAttributes(PSECURITY_ATTRIBUTES pSa); //創(chuàng)建 STARTUPINFO 結(jié)構(gòu)的成員函數(shù) void CreateStartUpInfo(LPSTARTUPINFO lpStartUpInfo); //創(chuàng)建匿名管道的成員函數(shù) void CreateNoNamedPipe(void); ? //分別代表要從匿名管道中讀的數(shù)據(jù)和要寫到匿名管道中的數(shù)據(jù) CString m_CStrReadPipe; CString m_CStrWritePipe; ? //保存創(chuàng)建匿名管道后所得到的對匿名管道的讀寫句柄 HANDLE hPipeRead; HANDLE hPipeWrite; //保證匿名管道只創(chuàng)建一次 BOOL m_PipeIsCreated;消息映射表定義:
const int dataLength = 100; ? CNoNamedPipeParentDlg::CNoNamedPipeParentDlg(CWnd* pParent /*=NULL*/) : CDialogEx(CNoNamedPipeParentDlg::IDD, pParent) , m_CStrReadPipe(_T("")) { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); ? m_PipeIsCreated = FALSE; } ? void CNoNamedPipeParentDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Text(pDX, IDC_EDIT_WRITE_PIPE, m_CStrWritePipe); DDX_Text(pDX, IDC_EDIT_READ_PIPE, m_CStrReadPipe); } ? BEGIN_MESSAGE_MAP(CNoNamedPipeParentDlg, CDialogEx) ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_BTN_CREATE_PIPE, &CNoNamedPipeParentDlg::OnBnClickedBtnCreatePipe) ON_BN_CLICKED(IDC_BTN_WRITE_PIPE, &CNoNamedPipeParentDlg::OnBnClickedBtnWritePipe) ON_BN_CLICKED(IDC_BTN_READ_PIPE, &CNoNamedPipeParentDlg::OnBnClickedBtnReadPipe) END_MESSAGE_MAP()消息處理函數(shù):
//創(chuàng)建匿名管道按鈕的消息處理函數(shù) void CNoNamedPipeParentDlg::OnBnClickedBtnCreatePipe() { if(m_PipeIsCreated == FALSE) { this->CreateNoNamedPipe(); } } ? ? //寫入數(shù)據(jù)到匿名管道中按鈕的消息處理函數(shù) void CNoNamedPipeParentDlg::OnBnClickedBtnWritePipe() { this->ParentWritePipe(); } ? ? //從匿名管道中讀取數(shù)據(jù)按鈕的消息處理函數(shù) void CNoNamedPipeParentDlg::OnBnClickedBtnReadPipe() { this->ParentReadPipe(); } ? ? //接收數(shù)據(jù) void CNoNamedPipeParentDlg::ParentReadPipe(void) { DWORD dwRead; char * pReadBuf; CString cStrRecvData; ? pReadBuf = new char[dataLength]; memset(pReadBuf, 0, dataLength); ? if(!ReadFile(hPipeRead, pReadBuf, dataLength, &dwRead, NULL)) { MessageBox(TEXT(" 從匿名管道接收數(shù)據(jù)失敗 ..."), TEXT("提示"), MB_ICONERROR); return; } ? cStrRecvData = " 從匿名管道接收數(shù)據(jù)成功: "; cStrRecvData += pReadBuf; ? this->m_CStrReadPipe.Empty(); ? this->m_CStrReadPipe = pReadBuf; UpdateData(FALSE); ? MessageBox(cStrRecvData, TEXT("提示"), MB_ICONINFORMATION); } ? ? //發(fā)送數(shù)據(jù) void CNoNamedPipeParentDlg::ParentWritePipe(void) { UpdateData(); ? if(!this->m_CStrWritePipe.IsEmpty()) { char * pSendData; DWORD dwWrite; CString cStrSendData; ? //在這里需要將 Unicode 字符集轉(zhuǎn)換為 ASCII 字符集 pSendData = new char[this->m_CStrWritePipe.GetLength() + 1]; memset(pSendData, 0, this->m_CStrWritePipe.GetLength() + 1); for(int i=0;i<this->m_CStrWritePipe.GetLength();i++) { pSendData[i] = (char)this->m_CStrWritePipe.GetAt(i); } ? if(!WriteFile(hPipeWrite, pSendData, this->m_CStrWritePipe.GetLength() + 1, &dwWrite, NULL)) { MessageBox(TEXT(" 給匿名管道發(fā)送數(shù)據(jù)失敗 ..."), TEXT("提示"), MB_ICONERROR); return; } ? cStrSendData = " 給匿名管道發(fā)送數(shù)據(jù)成功: "; cStrSendData += this->m_CStrWritePipe; ? this->m_CStrWritePipe.Empty(); UpdateData(FALSE); ? MessageBox(cStrSendData, TEXT("提示"), MB_ICONINFORMATION); } else { MessageBox(TEXT(" 請先輸入要發(fā)送給匿名管道的數(shù)據(jù) ..."), TEXT("提示"), MB_ICONERROR); } } ? ? //創(chuàng)建 SECURITY_ATTRIBUTES 結(jié)構(gòu) void CNoNamedPipeParentDlg::CreateSecurityAttributes(PSECURITY_ATTRIBUTES pSa) { //這里必須將 bInheritHandle 設置為 TRUE, //從而使得子進程可以繼承父進程創(chuàng)建的匿名管道的句柄 pSa->bInheritHandle = TRUE; pSa->lpSecurityDescriptor = NULL; pSa->nLength = sizeof(SECURITY_ATTRIBUTES); } ? ? //用來初始化新進程的 STARTUPINFO 成員 void CNoNamedPipeParentDlg::CreateStartUpInfo(LPSTARTUPINFO lpStartUpInfo) { memset(lpStartUpInfo, 0, sizeof(STARTUPINFO)); ? lpStartUpInfo->cb = sizeof(STARTUPINFO); lpStartUpInfo->dwFlags = STARTF_USESTDHANDLES; ? //子進程的標準輸入句柄為父進程管道的讀數(shù)據(jù)句柄 lpStartUpInfo->hStdInput = hPipeRead; ? //子進程的標準輸出句柄為父進程管道的寫數(shù)據(jù)句柄 lpStartUpInfo->hStdOutput = hPipeWrite; ? //子進程的標準錯誤處理句柄和父進程的標準錯誤處理句柄一致 lpStartUpInfo->hStdError = GetStdHandle(STD_ERROR_HANDLE); } ? ? //創(chuàng)建匿名管道 void CNoNamedPipeParentDlg::CreateNoNamedPipe(void) { SECURITY_ATTRIBUTES sa; PROCESS_INFORMATION processInfo; STARTUPINFO startUpInfo; ? CreateSecurityAttributes(&sa); if(!CreatePipe(&hPipeRead, &hPipeWrite, &sa, 0)) { MessageBox(TEXT(" 創(chuàng)建匿名管道失敗 ..."), TEXT("提示"), MB_ICONERROR); return; } ? CreateStartUpInfo(&startUpInfo); if(!CreateProcess(TEXT("NoNamedPipeChild.exe"), NULL, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &startUpInfo, &processInfo)) { CloseHandle(hPipeRead); CloseHandle(hPipeWrite); ? hPipeWrite = NULL; hPipeRead = NULL; ? MessageBox(TEXT(" 創(chuàng)建子進程失敗 ..."), TEXT("提示"), MB_ICONERROR); return; } else { m_PipeIsCreated = TRUE; ? //對于 processInfo.hProcess 和 processInfo.hThread //這兩個句柄不需要使用,所以釋放資源 CloseHandle(processInfo.hProcess); CloseHandle(processInfo.hThread); } }子進程實現(xiàn):(簡單 MFC 程序)
項目結(jié)構(gòu):
消息以及成員函數(shù)和成員變量的聲明:
// 實現(xiàn) protected: HICON m_hIcon; ? // 生成的消息映射函數(shù) virtual BOOL OnInitDialog(); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() public: afx_msg void OnBnClickedBtnWritePipe(); afx_msg void OnBnClickedBtnReadPipe(); ? //保存從父進程得到針對于匿名管道的讀寫句柄 HANDLE hPipeRead; HANDLE hPipeWrite; ? //分別代表要從匿名管道中讀的數(shù)據(jù)和要寫到匿名管道中的數(shù)據(jù) CString m_CStrWritePipe; CString m_CStrReadPipe; ? //子進程讀取匿名管道 void ChildReadPipe(void); //子進程寫匿名管道 void ChildWritePipe(void); //子進程獲取從父進程處繼承得到的關于匿名管道的讀寫句柄 void GetReadWriteHandleFromParent(void); ? //只需要獲取一次匿名管道的讀寫句柄即可 BOOL m_IsGettedParentHandle;消息映射表定義:
const int dataLength = 100; ? CNoNamedPipeChildDlg::CNoNamedPipeChildDlg(CWnd* pParent /*=NULL*/) : CDialogEx(CNoNamedPipeChildDlg::IDD, pParent) , m_CStrWritePipe(_T("")) , m_CStrReadPipe(_T("")) { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); ? this->m_IsGettedParentHandle = FALSE; } ? void CNoNamedPipeChildDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Text(pDX, IDC_EDIT_WRITE_PIPE, m_CStrWritePipe); DDX_Text(pDX, IDC_EDIT_READ_PIPE, m_CStrReadPipe); } ? BEGIN_MESSAGE_MAP(CNoNamedPipeChildDlg, CDialogEx) ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(ID_BTN_WRITE_PIPE, &CNoNamedPipeChildDlg::OnBnClickedBtnWritePipe) ON_BN_CLICKED(ID_BTN_READ_PIPE, &CNoNamedPipeChildDlg::OnBnClickedBtnReadPipe) END_MESSAGE_MAP()消息處理函數(shù):
//往匿名管道中寫入數(shù)據(jù)按鈕的消息處理函數(shù) void CNoNamedPipeChildDlg::OnBnClickedBtnWritePipe() { //如果子進程還沒有獲得對匿名管道的讀寫句柄的話需要先獲取句柄 this->GetReadWriteHandleFromParent(); ? ChildWritePipe(); } ? ? //從匿名管道中讀取數(shù)據(jù)按鈕的消息處理函數(shù) void CNoNamedPipeChildDlg::OnBnClickedBtnReadPipe() { //如果子進程還沒有獲得對匿名管道的讀寫句柄的話需要先獲取句柄 this->GetReadWriteHandleFromParent(); ? ChildReadPipe(); } ? //從匿名管道讀取數(shù)據(jù)成員函數(shù) void CNoNamedPipeChildDlg::ChildReadPipe(void) { DWORD dwRead; char * pReadBuf; CString cStrRecvData; ? pReadBuf = new char[dataLength]; memset(pReadBuf, 0, dataLength); ? //讀取數(shù)據(jù) if(!ReadFile(hPipeRead, pReadBuf, dataLength, &dwRead, NULL)) { MessageBox(TEXT(" 從匿名管道接收數(shù)據(jù)失敗 ..."), TEXT("提示"), MB_ICONERROR); return; } ? cStrRecvData = " 從匿名管道接收數(shù)據(jù)成功: "; cStrRecvData += pReadBuf; ? this->m_CStrReadPipe.Empty(); this->m_CStrReadPipe = pReadBuf; UpdateData(FALSE); ? MessageBox(cStrRecvData, TEXT("提示"), MB_ICONINFORMATION); } ? //往匿名管道中寫入數(shù)據(jù) void CNoNamedPipeChildDlg::ChildWritePipe(void) { UpdateData(); ? if(!this->m_CStrWritePipe.IsEmpty()) { char * pSendData; DWORD dwWrite; CString cStrSendData; ? //在這里需要將 Unicode 字符集轉(zhuǎn)換為 ASCII 字符集 pSendData = new char[this->m_CStrWritePipe.GetLength() + 1]; memset(pSendData, 0, this->m_CStrWritePipe.GetLength() + 1); for(int i=0;i<this->m_CStrWritePipe.GetLength();i++) { pSendData[i] = (char)this->m_CStrWritePipe.GetAt(i); } ? //寫入數(shù)據(jù) if(!WriteFile(hPipeWrite, pSendData, this->m_CStrWritePipe.GetLength(), &dwWrite, NULL)) { MessageBox(TEXT(" 給匿名管道發(fā)送數(shù)據(jù)失敗 ..."), TEXT("提示"), MB_ICONERROR); return; } ? cStrSendData = "給匿名管道發(fā)送數(shù)據(jù)成功: "; cStrSendData += this->m_CStrWritePipe; ? this->m_CStrWritePipe.Empty(); UpdateData(FALSE); ? MessageBox(cStrSendData, TEXT("提示"), MB_ICONINFORMATION); } else { MessageBox(TEXT(" 請先輸入要發(fā)送給匿名管道的數(shù)據(jù) ..."), TEXT("提示"), MB_ICONERROR); } } ? //需要獲取繼承自父進程的匿名管道讀寫句柄 void CNoNamedPipeChildDlg::GetReadWriteHandleFromParent(void) { if(this->m_IsGettedParentHandle == FALSE) { hPipeRead = GetStdHandle(STD_INPUT_HANDLE); hPipeWrite = GetStdHandle(STD_OUTPUT_HANDLE); ? this->m_IsGettedParentHandle = TRUE; } }效果展示:
首先需要將子進程的可執(zhí)行文件拷貝到父進程所在目錄下,否則創(chuàng)建進程時會找不到子進程的可執(zhí)行文件。?
啟動父進程可執(zhí)行文件,并單擊創(chuàng)建匿名管道按鈕,此時會彈出子進程窗口(新建了進程):
再在父進程的左邊文本框中輸入數(shù)據(jù),單擊寫入數(shù)據(jù)按鈕:
再在子進程窗口中單擊讀取數(shù)據(jù)按鈕:
再在子進程窗口左邊的文本框中輸入數(shù)據(jù),單擊寫入數(shù)據(jù)按鈕:
再在父進程窗口中單擊讀取數(shù)據(jù)按鈕:
?????????????
??????????????????
結(jié)束語
從上面的效果展示中就可以看出我們確實是實現(xiàn)了父子進程之間通過匿名管道來進行通信,
最后再來總結(jié)一下對于匿名管道的使用,
匿名管道一般用于實現(xiàn)本地父子進程之間的通信,其不能實現(xiàn)跨網(wǎng)絡進程之間的通信,
同時其也一般只用于實現(xiàn)父進程和子進程之間的通信。
像匿名管道的話,其和郵槽不同,
其可以實現(xiàn)父進程即可以向子進程發(fā)送數(shù)據(jù),同時父進程又可以從子進程接收到數(shù)據(jù)。
而且子進程可以接收來自父進程的數(shù)據(jù),并且也可以給父進程發(fā)送數(shù)據(jù)。
最后的話,那就是今天是 2011? 年的第一天,說實話,太冷了,實在不想寫博客的,
不過因為想寫完這個進程之間通信的幾篇博客,同時也對 2011 有個好的開始,
所以硬著頭皮寫下了這篇博客,在這里希望自己 2011 會更好,人會更成熟,更上進 !!!
同時也祝各位新年快樂,2011 會更好 !!!
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結(jié)
以上是生活随笔為你收集整理的进程间通信 - 匿名管道实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: gh0st去硬盘锁
- 下一篇: Windows 服务(附服务开发辅助工具