宁波市城乡和建设网站,自建站费用,微信小程序界面设计模板,做app网站的软件有哪些Windows系统编程之进程间通信作者#xff1a;北极星2003来源#xff1a;看雪论坛#xff08;www.pediy.com#xff09;Windows 的IPC#xff08;进程间通信#xff09;机制主要是异步管道和命名管道。#xff08;至于其他的IPC方式#xff0c;例如内存映射、邮槽等这里…Windows系统编程之进程间通信作者北极星2003来源看雪论坛www.pediy.comWindows 的IPC进程间通信机制主要是异步管道和命名管道。至于其他的IPC方式例如内存映射、邮槽等这里就不介绍了管道pipe是用于进程间通信的共享内存区域。创建管道的进程称为管道服务器而连接到这个管道的进程称为管道客户端。一个进程向管道写入信息而另外一个进程从管道读取信息。异步管道是基于字符和半双工的即单向一般用于程序输入输出的重定向命名管道则强大地多它们是面向消息和全双工的同时还允许网络通信用于创建客户端/服务器系统。一、异步管道(实现比较简单直接通过实例来讲解)实验目标当前有sample.cpp, sample.exe, sample.in这三个文件sample.exe为sample.cpp的执行程序sample.cpp只是一个简单的程序示例简单求和如下
代码:[cpp] view plain copy#include iostream.h int main() { int a, b ; while ( cin a b ( a || b ) ) cout a b endl ; return 0; } Sample.in文件是输入文件内容32 433542 6570 0要求根据sample.exe和它的输入数据把输出数据重定向到sample.out流程分析实际这个实验中包含两个部分把输入数据重定向到sample.exe 和把输出数据重定向到sample.out。在命令行下可以很简单的实现这个功能“sample sample.in sample.out”这个命令也是利用管道特性实现的现在我们就根据异步管道的实现原理自己来实现这个功能。管道是基于半双工单向的这里有两个重定向的过程显然需要创建两个管道下面给出流程图异步管道实现的流程图说明1。父进程是我们需要实现的其中需要创建管道A管道B和子进程整个实现流程分为4个操作。2。管道A输入管道3。管道B输出管道4。操作A把输入文件sample.in的数据写入输入管道管道A5。操作B子进程从输入管道中读取数据作为该进程的加工原料。通常程序的输入数据由标准的输入设备输入这里实现输入重定向即把输入管道作为输入设备。6。操作C子进程把加工后的成品输出数据输出到输出管道。通常程序的输出数据会输出到标准的输出设备一般为屏幕这里实现输出重定向即把输出管道作为输出设备。7。操作D把输出管道的数据写入输出文件需要注意的是管道的本质只是一个共享的内存区域。这个实验中管道区域处于父进程的地址空间中父进程的作用是提供环境和资源并协调子进程进行加工。程序源码代码:[cpp] view plain copy#include windows.h #include iostream.h const int BUFSIZE 4096 ; HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup, hChildStdoutRd,hChildStdoutWr,hChildStdoutRdDup, hSaveStdin, hSaveStdout; BOOL CreateChildProcess(LPTSTR); VOID WriteToPipe(LPTSTR); VOID ReadFromPipe(LPTSTR); VOID ErrorExit(LPTSTR); VOID ErrMsg(LPTSTR, BOOL); void main( int argc, char *argv[] ) { // 处理输入参数 if ( argc ! 4 ) return ; // 分别用来保存命令行输入文件名CPP/C输出文件名保存编译信息 LPTSTR lpProgram new char[ strlen(argv[1]) ] ; strcpy ( lpProgram, argv[1] ) ; LPTSTR lpInputFile new char[ strlen(argv[2]) ]; strcpy ( lpInputFile, argv[2] ) ; LPTSTR lpOutputFile new char[ strlen(argv[3]) ] ; strcpy ( lpOutputFile, argv[3] ) ; SECURITY_ATTRIBUTES saAttr; saAttr.nLength sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle TRUE; saAttr.lpSecurityDescriptor NULL; /************************************************ * redirecting child processs STDOUT * ************************************************/ hSaveStdout GetStdHandle(STD_OUTPUT_HANDLE); if (! CreatePipe(hChildStdoutRd, hChildStdoutWr, saAttr, 0)) ErrorExit(Stdout pipe creation failed/n); if (! SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr)) ErrorExit(Redirecting STDOUT failed); BOOL fSuccess DuplicateHandle( GetCurrentProcess(), hChildStdoutRd, GetCurrentProcess(), hChildStdoutRdDup , 0, FALSE, DUPLICATE_SAME_ACCESS); if( !fSuccess ) ErrorExit(DuplicateHandle failed); CloseHandle(hChildStdoutRd); /************************************************ * redirecting child processs STDIN * ************************************************/ hSaveStdin GetStdHandle(STD_INPUT_HANDLE); if (! CreatePipe(hChildStdinRd, hChildStdinWr, saAttr, 0)) ErrorExit(Stdin pipe creation failed/n); if (! SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd)) ErrorExit(Redirecting Stdin failed); fSuccess DuplicateHandle( GetCurrentProcess(), hChildStdinWr, GetCurrentProcess(), hChildStdinWrDup, 0, FALSE, DUPLICATE_SAME_ACCESS); if (! fSuccess) ErrorExit(DuplicateHandle failed); CloseHandle(hChildStdinWr); /************************************************ * 创建子进程即启动SAMPLE.EXE * ************************************************/ fSuccess CreateChildProcess( lpProgram ); if ( !fSuccess ) ErrorExit(Create process failed); // 父进程输入输出流的还原设置 if (! SetStdHandle(STD_INPUT_HANDLE, hSaveStdin)) ErrorExit(Re-redirecting Stdin failed/n); if (! SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout)) ErrorExit(Re-redirecting Stdout failed/n); WriteToPipe( lpInputFile ) ; ReadFromPipe( lpOutputFile ); delete lpProgram ; delete lpInputFile ; delete lpOutputFile ; } BOOL CreateChildProcess( LPTSTR lpProgram ) { PROCESS_INFORMATION piProcInfo; STARTUPINFO siStartInfo; BOOL bFuncRetn FALSE; ZeroMemory( piProcInfo, sizeof(PROCESS_INFORMATION) ); ZeroMemory( siStartInfo, sizeof(STARTUPINFO) ); siStartInfo.cb sizeof(STARTUPINFO); bFuncRetn CreateProcess ( NULL, lpProgram, NULL, NULL, TRUE, / 0, NULL, NULL, siStartInfo, piProcInfo); if (bFuncRetn 0) { ErrorExit(CreateProcess failed/n); return 0; } else { CloseHandle(piProcInfo.hProcess); CloseHandle(piProcInfo.hThread); return bFuncRetn; } } VOID WriteToPipe( LPTSTR lpInputFile ) { HANDLE hInputFile CreateFile(lpInputFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); if (hInputFile INVALID_HANDLE_VALUE) return ; BOOL fSuccess ; DWORD dwRead, dwWritten; CHAR chBuf[BUFSIZE] {0} ; for (;;) { fSuccess ReadFile( hInputFile, chBuf, BUFSIZE, dwRead, NULL) ; if ( !fSuccess || dwRead 0) break; fSuccess WriteFile( hChildStdinWrDup, chBuf, dwRead, dwWritten, NULL) ; if ( !fSuccess ) break; } if (! CloseHandle(hChildStdinWrDup)) ErrorExit(Close pipe failed/n); CloseHandle ( hInputFile ) ; } VOID ReadFromPipe( LPTSTR lpOutputFile ) { HANDLE hOutputFile CreateFile( lpOutputFile, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hOutputFile INVALID_HANDLE_VALUE) return ; BOOL fSuccess ; DWORD dwRead, dwWritten; CHAR chBuf[BUFSIZE] { 0 }; if (!CloseHandle(hChildStdoutWr)) ErrorExit(Closing handle failed); for (;;) { fSuccess ReadFile( hChildStdoutRdDup, chBuf, BUFSIZE, dwRead, NULL) ; if( !fSuccess || dwRead 0) { break; } fSuccess WriteFile( hOutputFile, chBuf, dwRead, dwWritten, NULL) ; if ( !fSuccess ) break; } CloseHandle ( hOutputFile ) ; } VOID ErrorExit (LPTSTR lpszMessage) { MessageBox( 0, lpszMessage, 0, 0 ); } 二、命名管道命名管道具有以下几个特征1命名管道是双向的所以两个进程可以通过同一管道进行交互。2命名管道不但可以面向字节流还可以面向消息所以读取进程可以读取写进程发送的不同长度的消息。3多个独立的管道实例可以用一个名称来命名。例如几个客户端可以使用名称相同的管道与同一个服务器进行并发通信。4命名管道可以用于网络间两个进程的通信而其实现的过程与本地进程通信完全一致。实验目标在客户端输入数据a和b然后发送到服务器并计算ab,然后把计算结果发送到客户端。可以多个客户端与同一个服务器并行通信。界面设计难点所在实现的过程比较简单但有一个难点。原本当服务端使用ConnectNamedPipe函数后如果有客户端连接就可以直接进行交互。原来我在实现过程中当管道空闲时管道的线程函数会无限INFINITE阻塞。若现在需要停止服务就必须结束所有的线程TernimateThread可以作为一个结束线程的方法但我基本不用这个函数。一旦使用这个函数之后目标线程就会立即结束但如果此时的目标线程正在操作互斥资源、内核调用、或者是操作共享DLL的全局变量可能会出现互斥资源无法释放、内核异常等现象。这里我用重叠I/0来解决这个问题在创建PIPE时使用FILE_FLAG_OVERLAPPED标志这样使用ConnectNamedPipe后会立即返回但线程的阻塞由等待函数WaitForSingleObject来实现等待OVERLAPPED结构的事件对象被设置。客户端主要代码代码:[cpp] view plain copyvoid CMyDlg::OnSubmit() { // 打开管道 HANDLE hPipe CreateFile(.//Pipe//NamedPipe, GENERIC_READ | GENERIC_WRITE, / 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL) ; if ( hPipe INVALID_HANDLE_VALUE ) { this-MessageBox ( 打开管道失败服务器尚未启动,或者客户端数量过多 ) ; return ; } DWORD nReadByte, nWriteByte ; char szBuf[1024] {0} ; // 把两个整数(a,b)格式化为字符串 sprintf ( szBuf, %d %d, this-nFirst, this-nSecond ) ; // 把数据写入管道 WriteFile ( hPipe, szBuf, strlen(szBuf), nWriteByte, NULL ) ; memset ( szBuf, 0, sizeof(szBuf) ) ; // 读取服务器的反馈信息 ReadFile ( hPipe, szBuf, 1024, nReadByte, NULL ) ; // 把返回信息格式化为整数 sscanf ( szBuf, %d, (this-nResValue) ) ; this-UpdateData ( false ) ; CloseHandle ( hPipe ) ; } 服务端主要代码代码:[cpp] view plain copy// 启动服务 void CMyDlg::OnStart() { CString lpPipeName .//Pipe//NamedPipe ; for ( UINT i 0; i nMaxConn; i ) { // 创建管道实例 PipeInst[i].hPipe CreateNamedPipe ( lpPipeName, PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED, / PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT, nMaxConn, 0, 0, 1000, NULL ) ; if ( PipeInst[i].hPipe INVALID_HANDLE_VALUE ) { DWORD dwErrorCode GetLastError () ; this-MessageBox ( 创建管道错误 ) ; return ; } // 为每个管道实例创建一个事件对象用于实现重叠IO PipeInst[i].hEvent CreateEvent ( NULL, false, false, false ) ; // 为每个管道实例分配一个线程用于响应客户端的请求 PipeInst[i].hTread AfxBeginThread ( ServerThread, PipeInst[i], THREAD_PRIORITY_NORMAL ) ; } this-SetWindowText ( 命名管道实例之服务器(运行) ) ; this-MessageBox ( 服务启动成功 ) ; } // 停止服务 void CMyDlg::OnStop() { DWORD dwNewMode PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_NOWAIT ; for ( UINT i 0; i nMaxConn; i ) { SetEvent ( PipeInst[i].hEvent ) ; CloseHandle ( PipeInst[i].hTread ) ; CloseHandle ( PipeInst[i].hPipe ) ; } this-SetWindowText ( 命名管道实例之服务器 ) ; this-MessageBox ( 停止启动成功 ) ; } // 线程服务函数 UINT ServerThread ( LPVOID lpParameter ) { DWORD nReadByte 0, nWriteByte 0, dwByte 0 ; char szBuf[MAX_BUFFER_SIZE] {0} ; PIPE_INSTRUCT CurPipeInst *(PIPE_INSTRUCT*)lpParameter ; OVERLAPPED OverLapStruct { 0, 0, 0, 0, CurPipeInst.hEvent } ; while ( true ) { memset ( szBuf, 0, sizeof(szBuf) ) ; // 命名管道的连接函数等待客户端的连接只针对NT ConnectNamedPipe ( CurPipeInst.hPipe, OverLapStruct ) ; // 实现重叠I/0等待OVERLAPPED结构的事件对象 WaitForSingleObject ( CurPipeInst.hEvent, INFINITE ) ; // 检测I/0是否已经完成如果未完成意味着该事件对象是人工设置即服务需要停止 if ( !GetOverlappedResult ( CurPipeInst.hPipe, OverLapStruct, dwByte, true ) ) break ; // 从管道中读取客户端的请求信息 if ( !ReadFile ( CurPipeInst.hPipe, szBuf, MAX_BUFFER_SIZE, nReadByte, NULL ) ) { MessageBox ( 0, 读取管道错误, 0, 0 ) ; break ; } int a, b ; sscanf ( szBuf, %d %d, a, b ) ; pMyDlg-nFirst a ; pMyDlg-nSecond b ; pMyDlg-nResValue a b ; memset ( szBuf, 0, sizeof(szBuf) ) ; sprintf ( szBuf, %d, pMyDlg-nResValue ) ; // 把反馈信息写入管道 WriteFile ( CurPipeInst.hPipe, szBuf, strlen(szBuf), nWriteByte, NULL ) ; pMyDlg-SetDlgItemInt ( IDC_FIRST, a, true ) ; pMyDlg-SetDlgItemInt ( IDC_SECOND, b, true ) ; pMyDlg-SetDlgItemInt ( IDC_RESULT, pMyDlg-nResValue, true ) ; // 断开客户端的连接以便等待下一客户的到来 DisconnectNamedPipe ( CurPipeInst.hPipe ) ; } return 0 ; } 最后特别说明一下此文章是看雪WIN32安全编程板块的斑竹北极星的大家可以多多关注一下他的技术文章看了几篇都认为很不错的。链 接: http://bbs.pediy.com/showthread.php?t26252http://blog.csdn.net/yiruirui0507/article/details/6457806