1. 程式人生 > >C++進階(應用篇)—第2章 程序間的通訊(補)

C++進階(應用篇)—第2章 程序間的通訊(補)

2.3.2匿名管道

//匿名管道

//father.cpp

#include <windows.h>

#include <iostream>

 

using namespace std;

#define BUF_SIZE 4096

 

#define CHILD_ADDR L"C:\\Users\\gyrt\\Documents\\Visual Studio 2008\\Projects\\unnamed_pipe2\\Debug\\child.exe"

 

//父程序到子程序的管道,父程序一端寫入,子程序一端讀出

HANDLE g_hFtoCStd_Wr,g_hFtoCStd_Rd;

//子程序到父程序的管道,子程序一端寫入,父程序一端讀出

HANDLE g_hCtoFStd_Wr,g_hCtoFStd_Rd;

 

int main(int argc,char ** argv)

{

//步驟1:建立匿名管道

SECURITY_ATTRIBUTES saAttr;

saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);

saAttr.bInheritHandle = TRUE;

saAttr.lpSecurityDescriptor = NULL;

 

//CreatePipe:引數是對管道讀的HANDLE,引數是對管道寫的HANDLE

if (!CreatePipe(&g_hFtoCStd_Rd,&g_hFtoCStd_Wr, &saAttr, 0))

{

cout << "Create Unnamed_Pipe Failed..." << endl;

return 1;

}

if (!CreatePipe(&g_hCtoFStd_Rd, &g_hCtoFStd_Wr, &saAttr, 0))

{

cout << "Create Unnamed_Pipe Failed..." << endl;

return 1;

}

 

//步驟2:設定父程序端的管道口不可繼承

//設定控制代碼不可繼承(子程序)

if (!SetHandleInformation(g_hFtoCStd_Wr, HANDLE_FLAG_INHERIT, 0))

{

cout << "hFtoCStdinWr is not inherited by child process." << endl;

return 1;

}

//設定控制代碼不可繼承

if (!SetHandleInformation(g_hCtoFStd_Rd, HANDLE_FLAG_INHERIT, 0))

{

cout << "hCtoFStdoutRd is not inherited by child process." << endl;

return 1;

}

 

//步驟3:建立子程序

//TCHAR szCommandLINE[] = TEXT("child.exe");//TEXT("child")等價於L"child"

STARTUPINFO si;

ZeroMemory(&si, sizeof(STARTUPINFO));

si.cb = sizeof(STARTUPINFO);

si.hStdError = g_hCtoFStd_Wr;

si.hStdOutput = g_hCtoFStd_Wr; //將子程序的標準輸出重定向到匿名管道的寫段

si.hStdInput = g_hFtoCStd_Rd; //將子程序的標準輸入重定向到匿名管道的讀端

si.dwFlags |= STARTF_USESTDHANDLES;

PROCESS_INFORMATION pi; //pi 獲取子程序的資訊

ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));

//CreateProcess方法1:通過路徑來開啟子程序

int ret = CreateProcess(CHILD_ADDR, NULL,NULL,NULL,TRUE,0,NULL,NULL,&si,&pi);

//CreateProcess方法2:子程序的exe檔案必須放在父程序中,然後開啟

//int ret = CreateProcess(NULL, szCommandLINE,NULL,NULL,TRUE,0,NULL,NULL,&si,&pi);

if (ret)

{

printf("child process created.\n");

//父程序不需要子程序的系統資源,因此將子程序控制代碼和子程序的執行緒控制代碼關閉

//當然某些程式可能會保留這些控制代碼來監視子程序狀態

CloseHandle(pi.hProcess);

CloseHandle(pi.hThread);

}

 

//步驟4:讀寫匿名管道

//4.1:開啟my.txt檔案的控制代碼

HANDLE h_mytxt = CreateFile(

L"my.txt",

GENERIC_READ,

0,

NULL,

OPEN_EXISTING,

FILE_ATTRIBUTE_READONLY,

NULL);

 

//4.2:讀取檔案my.txt,輸出到匿名管道g_hFtoCStd_Wr,即向子程序寫資料

{

char szBuffer[BUF_SIZE] = { 0 };

DWORD wLen = 0;

DWORD rLen = 0;

int rRet = 0;

int wRet = 0;

 

for(;;)

{

//ReadFile讀檔案控制代碼:第一次讀取結束後,第二次讀取若檔案內容未改動,則不再讀取

//ReadFile讀管道控制代碼:第一次讀取結束後,清除管道緩衝區,因此第二次讀取管道若無資料,一直等待

rRet = ReadFile(h_mytxt, szBuffer, BUF_SIZE, &rLen, NULL);

if(!rRet || rLen == 0)

break;

wRet = WriteFile(g_hFtoCStd_Wr, szBuffer, strlen(szBuffer) + 1, &wLen, NULL);

if(!wRet)

break;

}

}

 

//4.3:輸出重定向到控制代碼hStdout

HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

 

//4.4:從子程序讀取資料,再通過hStdout輸出

{

char szBuffer[BUF_SIZE] = { 0 };

DWORD wLen = 0;

DWORD rLen = 0;

int rRet = 0;

int wRet = 0;

for(;;)

{

rRet = ReadFile(g_hCtoFStd_Rd, szBuffer, BUF_SIZE, &rLen, NULL);

if(!rRet || rLen == 0)

break;

else

cout << "Read Successed..." << endl;

wRet = WriteFile(hStdout, szBuffer, strlen(szBuffer) + 1, &wLen, NULL);

if(!wRet)

break;

else

cout <<"Write Successed..." << endl;

}

}

 

//步驟5:關閉控制代碼

CloseHandle(g_hFtoCStd_Wr);

CloseHandle(g_hFtoCStd_Rd);

CloseHandle(g_hCtoFStd_Wr);

CloseHandle(g_hCtoFStd_Rd);

CloseHandle(h_mytxt);

CloseHandle(hStdout);

return 0;

}

//匿名管道

//child.cpp

#include <windows.h>

#include <iostream>

 

using namespace std;

#define BUF_SIZE 4096

HANDLE h_Stdin,h_Stdout;

int main(int argc,char ** argv)

{

//步驟1:子程序輸出輸入重定向

h_Stdin = GetStdHandle(STD_INPUT_HANDLE);

h_Stdout = GetStdHandle(STD_OUTPUT_HANDLE);

 

if (h_Stdin == INVALID_HANDLE_VALUE

|| h_Stdout == INVALID_HANDLE_VALUE)

{

cout << "Get Std_Handle Failed..." << endl;

ExitProcess(1);

}

 

//相當於執行WriteFile(h_Stdout,...)

cout<<"*This is a message from the child process.*"<<endl;

_sleep(500);

cout<<"***This is a message from the child process.***"<<endl;

 

char szBuffer[BUF_SIZE] = { 0 };

DWORD wLen = 0;

DWORD rLen = 0;

int rRet = 0;

int wRet = 0;

 

//步驟2:讀寫匿名管道

for(;;)

{

rRet = ReadFile(h_Stdin, szBuffer, BUF_SIZE, &rLen, NULL);

if(!rRet || rLen == 0)

break;

wRet = WriteFile(h_Stdout, szBuffer, strlen(szBuffer) + 1, &wLen, NULL);

if(!wRet)

break;

}

 

//步驟3:關閉控制代碼

CloseHandle(h_Stdin);

CloseHandle(h_Stdout);

return 0;

}

執行結果:

child process created.

Read Successed...

*This is a message from the child process.*

 Write Successed...

Read Successed...

***This is a message from the child process.***

 Write Successed...

Read Successed...

hello world

hello C++

你好,中國! Write Successed...

父程序從my.txt中讀取資料,然後輸出到匿名管道,傳送給子程序;子程序讀取到資料後,再發送會父程序;父程序讀取子程序中傳來的資料,然後通過標準輸出重定向的控制代碼來輸出資料。

BOOL WINAPI CreatePipe(引數1,引數2,引數3,引數4):建立匿名管道,並得到讀寫管道。

引數1:對匿名管道讀的控制代碼;

引數2:對匿名管道寫的控制代碼;

引數3:指向SECURITY_ATTRIBUTES結構的指標,SECURITY_ATTRIBUTES是用來設定管道的訪問許可權。SECURITY_ATTRIBUTES成員bInheritHandle是設定控制代碼是否被子程序繼承,若為ture則可以被繼承;SECURITY_ATTRIBUTES成員lpSecurityDescriptor表示指向安全描述符(Security_Descriptor)的指標,若為NULL,則表示管道設定為預設安全屬性。

引數4:管道緩衝區的大小,若設定0,則使用系統預設的緩衝區大小。

BOOL WINAPI SetHandleInformation(引數1,引數2,引數3):控制子程序獲得物件控制代碼的繼承權。

引數1:標識一個控制代碼;

引數2:若為HANDLE_FLAG_INHERIT表示建立的子程序可以獲得物件控制代碼。

HANDLE GetStdHandle( DWORD nStdHandle ):用於從一個特定的標準裝置(標準輸入、標準輸出或標準錯誤)中取得一個控制代碼。

BOOL CreateProcess

(

LPCTSTR lpApplicationName,

LPTSTR lpCommandLine,

LPSECURITY_ATTRIBUTES lpProcessAttributes,

LPSECURITY_ATTRIBUTES lpThreadAttributes,

BOOL bInheritHandles,

DWORD dwCreationFlags,

LPVOID lpEnvironment,

LPCTSTR lpCurrentDirectory,

LPSTARTUPINFO lpStartupInfo,

LPPROCESS_INFORMATION lpProcessInformation

)

函式作用:建立子程序。在寬字元版本中實際呼叫的是CreateProcessW,窄字元版本中呼叫的則是CreateProcessA。

引數1:lpApplicationName,指向可執行模組的字串。這個字串可以是可執行模組的絕對路徑或相對路徑。注:若設為NULL,則引數2必須要設定。

引數2:lpCommandLine,指定要執行的命令列。此命令列是子程序的可執行模組,並且需要放在父程序程式碼下。

引數3:lpProcessAttributes,指向SECURITY_ATTRIBUTES結構的指標,這裡設為為NULL。

引數4:lpThreadAttributes,指向SECURITY_ATTRIBUTES結構的指標,這裡設為為NULL。

引數5:bInheritHandles,指示子程序是否從父程序處繼承控制代碼,這裡設為TRUE。

引數6:dwCreationFlags,這裡設為0。

引數7:lpEnvironment,指示子程序的環境塊,若設為NULL,則使用父程序的環境。

引數8:lpCurrentDirectory,指定子程序的工作路徑,若設為NULL,則子程序和父程序使用相同的驅動器和目錄。

引數9:lpStartupInfo,指向StartupInfo結構的指標。StartupInfo結構體用於指定新程序的主視窗特性:設定了標準錯誤/輸出/輸入;dwFlags設定為STARTF_USESTDHANDLES表示使用hStdInput、hStdOutput和hStdError成員。

引數10:lpProcessInformation,指向PROCESS_INFORMATION結構的指標。PROCESS_INFORMATION結構體用於存放程序資訊。

2.4訊號量

//程序1

#include <iostream>

#include <windows.h>

#include <process.h>

using namespace std;

 

int g_var;

//步驟1:建立控制代碼

HANDLE H_Semaphore = NULL;

UINT WINAPI ThreadOne(LPVOID lpParameter)

{

 

printf("程序正在執行...\n");

//步驟3:釋放訊號量

ReleaseSemaphore(H_Semaphore, 1, NULL);

return 1;

}

 

 

int main(int argc, char **argv)

{

//步驟2:使用CreateSemaphore建立訊號量

H_Semaphore = CreateSemaphore(NULL,0,1,L"pSemaphore");

if(H_Semaphore == NULL)

printf("建立失敗\n");

 

unsigned int thread_id = 0;

 

HANDLE HOne =(HANDLE)_beginthreadex(NULL, 0, ThreadOne, NULL, 0, &thread_id);

 

WaitForSingleObject(HOne, INFINITE);

while(1)

{

 

}

 

CloseHandle(HOne);

//步驟4:使用CloseHandle關閉訊號量控制代碼

CloseHandle(H_Semaphore);

 

return 0;

}

//程序2

#include <iostream>

#include <windows.h>

#include <process.h>

using namespace std;

 

int g_var;

//步驟1:建立控制代碼

HANDLE H_Semaphore2 = NULL;

UINT WINAPI ThreadOne(LPVOID lpParameter)

{

//步驟3:等待訊號量釋放

WaitForSingleObject(H_Semaphore2, INFINITE);

printf("獲得訊號量,程序開始執行\n...\n");

printf("程序執行結束!\n");

return 1;

}

 

int main(int argc, char **argv)

{

//步驟2:使用OpenSemaphore開啟訊號量

H_Semaphore2 = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE,L"pSemaphore");

if(H_Semaphore2 == NULL)

{

printf("開啟訊號量失敗\n");

exit(1);

}

else

printf("開啟訊號量成功\n");

 

unsigned int thread_id = 0;

 

HANDLE HOne = (HANDLE)_beginthreadex(NULL, 0, ThreadOne, NULL, 0, &thread_id);

 

WaitForSingleObject(HOne, INFINITE);

 

CloseHandle(HOne);

//步驟4:使用CloseHandle關閉訊號量控制代碼

CloseHandle(H_Semaphore2);

 

return 0;

}

程序1執行結果:

程序1正在執行...

程序2執行結果:

開啟訊號量成功

獲得訊號量,程序2開始執行

...

程序2執行結束!

必須程序1開始執行後,再執行程序2。

OpenSemaphore(引數1,引數2,引數3):開啟建立的訊號量。

引數1:若是SEMAPHORE_ALL_ACCESS,表示對訊號量的完全訪問。

引數2:訊號量控制代碼是否被子程序繼承。

引數3:訊號量的名稱,型別為寬字元指標。