利用匿名管道在父子進(jìn)程間傳遞數據
進(jìn)程間傳遞數據有很多種方法,常用到的有命令行、共享內存、內存映射文件、剪貼板、windows消息、socket等。
命令行的缺點(diǎn)是數據長(cháng)度限制。Windows2000只能傳遞256個(gè)字節,內存映射文件、共享內存都需要一些進(jìn)程同步才能很好的配合讀寫(xiě)數據,剪貼板可能會(huì )被其他進(jìn)程擦數數據。當多開(kāi)的時(shí)候很難控制進(jìn)程間的對應關(guān)系。
一種解決方案是生成隨機命名的同步控制對象,然后利用命令行傳遞名字。同步控制對象可以通過(guò)唯一的名字再另一個(gè)進(jìn)程中獲取其引用。
相對來(lái)說(shuō)用同步對象會(huì )稍有些麻煩,匿名管道可以很好的解決這些問(wèn)題。子進(jìn)程可以繼承父進(jìn)程中創(chuàng )建的句柄,父子進(jìn)程一一對應的關(guān)系不會(huì )被打亂。而且使用簡(jiǎn)單,父子進(jìn)程可以雙向通信。
用法:
1. 首先創(chuàng )建兩條匿名管道,一條用于發(fā)送數據給子進(jìn)程,一條用于從子進(jìn)程接收數據,安全描述符中指定可繼承性 saAttr.bInheritHandle = TRUE;
2. 每條匿名管道包括兩個(gè)句柄,一個(gè)讀一個(gè)寫(xiě),可以理解為管道的兩端,從寫(xiě)端寫(xiě)出的數據可以從讀端讀取。所以父進(jìn)程需要一條管道來(lái)接收數據,一條發(fā)送數據。
3. windows的IO操作都可以用WriteFile 和ReadFile來(lái)完成,默認模式下數據發(fā)送和接收是阻塞的,管道的數據發(fā)送與接收也可以用重疊模式來(lái)進(jìn)行。
4. CreateProcess子進(jìn)程時(shí)參數.bInheritHandle需要傳真,保證句柄的可繼承性。
5. 利用STARTUPINFO傳遞管道端口給子進(jìn)程,父窗口發(fā)送數據的管道的讀端口,和父窗口等待接收數據的管道的寫(xiě)端口,利用STDHANDLES來(lái)傳遞。
STARTUPINFO starinfo ={0};
starinfo.cb = sizeof(starinfo);
starinfo.hStdInput = hSendReadPipe;
starinfo.hStdOutput= hRecvWritePipe;
starinfo.hStdError = hRecvWritePipe;
starinfo.dwFlags |= STARTF_USESTDHANDLES;
6. 子進(jìn)程從STDHANDLES獲得兩個(gè)句柄用來(lái)讀寫(xiě)。
HANDLE hRead = GetStdHandle(STD_INPUT_HANDLE);
HANDLE hWrite = GetStdHandle(STD_OUTPUT_HANDLE);
下面是完整代碼:
BOOL CreatePipe()
{
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
/**//*'創(chuàng )建匿名管道*/
if (!CreatePipe(&hSendReadPipe,&hSendWritePipe, &saAttr, 0))
{
::LogMsg("CreatePipe failed!");
return FALSE;
}
/**//*'構造寫(xiě)句柄的復制體*/
if(!DuplicateHandle(GetCurrentProcess(), hSendWritePipe,
GetCurrentProcess(), &hSendWritePipeDup, 0,FALSE,DUPLICATE_SAME_ACCESS))
{
::LogMsg("DuplicateHandle Handle!");
return FALSE;
}
CloseHandle(hSendWritePipe);
//////////////////////////////////////////////////////////////////////////
if (!CreatePipe(&hRecvReadPipe,&hRecvWritePipe, &saAttr, 0))
{
::LogMsg("CreatePipe failed!");
return FALSE;
}
/**//*'構造寫(xiě)句柄的復制體*/
if(!DuplicateHandle(GetCurrentProcess(), hRecvReadPipe,
GetCurrentProcess(), &hRecvReadPipeDup, 0,FALSE,DUPLICATE_SAME_ACCESS))
{
::LogMsg("DuplicateHandle Handle!");
return FALSE;
}
CloseHandle(hRecvReadPipe);
return TRUE;
}
BOOL ReadFromPipe()
{
DWORD dwReaded =0;
char szBuf[255];
return ReadFile(hRecvReadPipeDup, szBuf, sizeof(szBuf), &dwReaded, NULL); ;
}
void WaitForReply()
{
while ( !ReadFromPipe()){};
}
BOOL WriteToPipe()
{
char szData[1024];
int nSize = sizeof(g_cmdData);
CompressData((char*)&g_cmdData,nSize,szData);
/**//*'對管道進(jìn)行寫(xiě)操作*/
DWORD dwWrited =0;
BOOL bSuccess = TRUE;
bSuccess &= WriteFile(hSendWritePipeDup, (LPCVOID)szData, nSize, &dwWrited, NULL);
CloseHandle(hSendWritePipeDup);
if ( !bSuccess )
{
::LogMsg("WriteFile failed!");
return FALSE;
}
return TRUE;
}
BOOL CreateGameProcess()
{
char strDir[MAX_PATH] ={0};
char strPath[MAX_PATH]={0};
GetCurrentDirectory(MAX_PATH,strDir);
strcpy(strPath,strDir);
#ifdef _DEBUG
const char* pszFileName = "\\main_debug.exe";
#else
const char* pszFileName = "\\main.exe";
#endif
strcat(strPath,pszFileName);
if ( !CreatePipe() )
{
::LogMsg("CreatePipe failed!");
return FALSE;
}
STARTUPINFO starinfo ={0};
starinfo.cb = sizeof(starinfo);
starinfo.hStdInput = hSendReadPipe;
starinfo.hStdOutput= hRecvWritePipe;
starinfo.hStdError = hRecvWritePipe;
starinfo.dwFlags |= STARTF_USESTDHANDLES;
PROCESS_INFORMATION processinfo ={0};
BOOL bRet =::CreateProcess(strPath, " fromlogin", NULL,NULL,TRUE,NULL,NULL,strDir,&starinfo,&processinfo);
if(bRet)
{
// WaitForInputIdle(processinfo.hProcess,INFINITE);
dwGameProcessID = processinfo.dwProcessId;
CloseHandle(processinfo.hProcess);
CloseHandle(processinfo.hThread);
return TRUE;
}
::LogMsg("CreateProcess failed!");
return FALSE;
}
子進(jìn)程中接收數據:
DWORD dwReaded =0;
HANDLE hRead = GetStdHandle(STD_INPUT_HANDLE);
HANDLE hWrite = GetStdHandle(STD_OUTPUT_HANDLE);
if( hRead )
{
/**//*'從管道接收數據*/
const int nBufSize = 2048;
char szBuf[nBufSize];
if(ReadFile(hRead, &szBuf,nBufSize, &dwReaded, NULL))
{
}
}