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:訊號量的名稱,型別為寬字元指標。