Windows系统编程之进程间通信

关于windows异步命名管道通信的一些记录 要注意CreateEvent的第二个参数bManualReset:是否手动重置事件,一般手动设置的事件需要在事件触发后自行reset,是event处于无信号状态,如果是自动设置的事件,事件触发后,所有的wait结束后会自行变为无信号状态。客户端使用CreateFile打开命名管道,使用ReadFileEx和WriteFileEx进行管道的读写,经过测试,可以不用给overlap的hevent初始化,只需要有wait函数,读写完成后就能触发完成回调函数。分为服务端和客户端,参考。 阅读详情
作者:北极星2003
来源:看雪论坛(www.pediy.com)

附件:windowipc.rar

Windows 的IPC(进程间通信)机制主要是异步管道和命名管道。(至于其他的IPC方式,例如内存映射、邮槽等这里就不介绍了)
管道(pipe)是用于进程间通信的共享内存区域。创建管道的进程称为管道服务器,而连接到这个管道的进程称为管道客户端。一个进程向管道写入信息,而另外一个进程从管道读取信息。
异步管道是基于字符和半双工的(即单向),一般用于程序输入输出的重定向;命名管道则强大地多,它们是面向消息和全双工的,同时还允许网络通信,用于创建客户端/服务器系统。
一、异步管道(实现比较简单,直接通过实例来讲解)
实验目标:当前有sample.cpp, sample.exe, sample.in这三个文件,sample.exe为sample.cpp的执行程序,sample.cpp只是一个简单的程序示例(简单求和),如下:

代码:
    
#include <iostream.h> int main() {   int a, b ;   while ( cin >> a >> b && ( a || b ) )     cout << a + b << endl ;   return 0; }

 


Sample.in文件是输入文件,内容:
32 433
542 657
0 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的数据写入输入管道(管道A)
5)。操作B:子进程从输入管道中读取数据,作为该进程的加工原料。通常,程序的输入数据由标准的输入设备输入,这里实现输入重定向,即把输入管道作为输入设备。
6)。操作C:子进程把加工后的成品(输出数据)输出到输出管道。通常,程序的输出数据会输出到标准的输出设备,一般为屏幕,这里实现输出重定向,即把输出管道作为输出设备。
7)。操作D:把输出管道的数据写入输出文件
需要注意的是,管道的本质只是一个共享的内存区域。这个实验中,管道区域处于父进程的地址空间中,父进程的作用是提供环境和资源,并协调子进程进行加工。
程序源码:

代码:
    
#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 process's 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 process's 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,然后发送到服务器并计算a+b,然后把计算结果发送到客户端。可以多个客户端与同一个服务器并行通信。
界面设计:
  
难点所在:
实现的过程比较简单,但有一个难点。原本当服务端使用ConnectNamedPipe函数后,如果有客户端连接,就可以直接进行交互。原来我在实现过程中,当管道空闲时,管道的线程函数会无限(INFINITE)阻塞。若现在需要停止服务,就必须结束所有的线程,TernimateThread可以作为一个结束线程的方法,但我基本不用这个函数。一旦使用这个函数之后,目标线程就会立即结束,但如果此时的目标线程正在操作互斥资源、内核调用、或者是操作共享DLL的全局变量,可能会出现互斥资源无法释放、内核异常等现象。这里我用重叠I/0来解决这个问题,在创建PIPE时使用FILE_FLAG_OVERLAPPED标志,这样使用ConnectNamedPipe后会立即返回,但线程的阻塞由等待函数WaitForSingleObject来实现,等待OVERLAPPED结构的事件对象被设置。
客户端主要代码:

代码:
    
void 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 ) ; }

 


服务端主要代码:

代码:
    
// 启动服务 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 ; }
 
Windows 命名管道通信 什么是命名管道? 命名管道是一种简单的进程间通信机制, 命名管道的命名规范 \\server\Pipe\[path]name 第一部分server,指定服务器名字 第二部分Pipe,硬编码即原样照录 第三部分 唯一命名+路径 例如 \myserver\Pipe\mypipe \Testserver\pipe\coldirectory\funtest\jim \.\Pipe\Easyname... 阅读详情

相关推荐

Linux进程间通信(管道)

本篇文章将给大家讲解进程间通信中的管道使用方法和概念。本篇文章就讲解到这里,下篇文章讲解fifo有名管道。

m0_49476241的博客 1185

NamedPipe,利用IOCP实现命名管道异步通信

微软提供了利用回调函数来实现有名管道的示例:Named Pipe Server Using Completion Routines. 该示例使用了ReadFileEx和WriteFileEx函数来进行有名管道的异步读写操作。我们在该示例的基础上进行改写来实现通过IOCP来进行异步读写。主要改动在哪下几个方面: 1.删去 CreateEvent操作,改为CreateIoCompletion

alpbrook的专栏 5236

双管道与命令提示符进程之间的通信演示

本文将演示如何使用双管道实现与cmd.exe进程之间的通信,并提供相应的源代码。现在,我们可以运行上述两部分的代码,演示双管道与cmd.exe进程之间的通信。最后,服务端将读取客户端发送的消息,并向客户端发送响应。双管道与cmd.exe进程之间的通信为不同的进程提供了一种简单且有效的IPC方式。接下来,我们需要编写一个cmd.exe进程的脚本,用于连接到我们创建的双管道,并进行通信。函数创建了一个双管道,并指定了管道的名称、访问权限、管道类型等参数。在上述脚本中,我们首先定义了双管道的名称,并使用。

IunScala的博客 180

进程间通信

摘 要 随着人们对应用程序的要求越来越高,单进程应用在许多场合已不能满足人们的要求。编写多进程/多线程程序成为现代程序设计的一个重要特点,在多进程程序设计中,进程间的通信是不可避免的。Microsoft Win32 API提供了多种进程间通信的方法,全面地阐述了这些方法的特点,并加以比较和分析,希望能给读者选择通信方法提供参考。1 进程与进程通信   进程是装入内存并准备执行的程序,每个进

xgbing 8408

Windows系统编程 - 进程间通信

介绍了常用的进程通信方式:WM_COPYDATA、DLL共享段、文件映射、管道的相关知识

weixin_58038441的博客 1324

异步命名管道

异步命名管道命名管道可以用于进程间通信。 client 端: // 打开管道 HANDLE hPipe = CreateFile("\\\\.\\Pipe\\NamedPipe", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hPipe == I

yangyang031213的博客 2536

进程间通信(IPC):无名管道(pipe)和有名管道(named pipe)

无名管道和有名管道无名管道(pipe)<3 <3 <3无名管道的特点:pipe函数父进程——|pipe|——>子进程示例有名管道(FIFO)<3 <3 <3mkfifo示例代码 无名管道(pipe)❤️ ❤️ ❤️ 管道也叫无名管道,它是是 UNIX 系统 IPC(进程间通信) 的最古老形式,所有的 UNIX 系统都支持这种通信机制。 父进程调用pipe函数的时候,内核在执行pipe函数的时候就会在内核空间创建一个缓存,这个缓存就是无名管道 无名管道的特点:

Rain土的博客 1105

一个最简单的C#管道(NamePipe)全双工通信实例

       命名管道NamedPipe,可以用于进程间通信。本文创建两个exe工程,做为两个进程。一个为管道客户端,一个为管道服务端。互为双向通信。例子摘自MSDN。用控制台或者winForm,启动时,调用对应的函数,并打好断点就可以做测试了。         首先需要引用命名空间,using System.IO;   using System.IO.Pipes;         注:下面读...

FHZZWZ的专栏 9125

C/C++开发,win下命名管道通信压力测试实现(含c++示例代码)

win下命名管道通信压力测试实现,利用win c++标准函数CreateNamedPipe、ConnectNamedPipe、WriteFile、ReadFile、CloseHandle等创建、连接、使用、关闭的管道操作函数,本测试中,同时有300百个线程对管道进行数据写入及读取操作,测试完成10万次数据写入及读取操作,以实现本地系统进程间通信压力测试。

技术需要分享 1721

Windows平台创建控制台程序并实现双向管道(Pipe)

Windows平台创建控制台程序并实现双向管道(Pipe)语言:ObjectPascal,DELPHI11 3. 读取控制台程序输出 *** OVER *** 上张图 需要源码参考到 yxydev.com 找找有时间发上去。

Ti-蜗牛的博客 1077

匿名管道和命名管道

进程间通信(IPC)每个进程有各自不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到。所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间中拷贝到缓冲区,进程2再从缓冲区把数据读走。内核提供的这种机制就是进程间通信。 通信需要媒介,两个进程间通信的媒介就是内存。通信的原理就是让两个或多个进程能够看到同一块共同的资源,这块资源一般都是由内存提供。匿名管道(

YANG 2万+

进程通信之二 管道技术第三篇 命名管道

上一篇《进程通信之二管道技术第二篇匿名管道》中讲解了匿名管道,匿名管道有读取端和写入端,在创建匿名管道(CreatePipe)后就可以像读写文件一样的对管道中进行读写(ReadFile与WriteFile,注意读写顺序)。在关闭匿名管道两端后会由系统负责销毁并回收资源。文章中还示范了父进程如何使用匿名管道来改变子进程的输入输出。 本篇将讲解管道技术中的命名管道(Named Pipes),顾名思义,

MoreWindows Blog 3万+

使用异步命名管道通信的实例

记录一个使用异步命名管道通信的实例。。

溡漪幽博客 521

Windows 命名管道 + 异步I/O模型

一、管道 + 异步I/O 本地通讯模型 1、客户端操作 发送请求                                                              (1) 互斥上锁 CreateMutex, WaitForSingleObject                     (2) 打开命名管道 OpenNamedPipe              

phymat.nico的专栏 1600

C#中异步命名管道通信的实践指南

在C#中,命名管道是一种进程间通信(IPC)机制,它允许在不同应用或服务之间进行数据交换。命名管道比匿名管道具有更强的灵活性和功能性,因为它允许更复杂的通信场景,例如网络通信。异步通信是现代软件开发中不可或缺的一部分,特别是在多线程应用中。异步通信能够提高应用性能,改善用户体验。本章将概览命名管道和异步通信的概念,并讨论它们在C#开发中的重要性和基本用法。// 示例代码:命名管道服务端的基本结构// 示例代码:异步通信的常见模式// 异步操作的代码实现。

weixin_34064233的博客 983
上一篇: WinEggDrop源代码
下一篇: BCB中调用DOS程序
iiprogram
博客等级 码龄25年 1066粉丝 1515原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值