8、进程通信-匿名管道

匿名管道

一个单向,未命名的管道,通常用来在一个父进程和一个子进程间传输数据。只能实现本地机器上两个进程间的通信,而不能实现跨网络的通信。

BOOL CreatePipe(

  PHANDLE hReadPipe,                       // read handle

  PHANDLE hWritePipe,                      // write handle

  LPSECURITY_ATTRIBUTES lpPipeAttributes,  // security attributes

  DWORD nSize                              // pipe size

);

CreateProcess 创建进程

创建父进程:

a. CreatePipe:其中第三个参数代表安全属性结构体SECURITY_ATTRIBUTES的指针,在前几章的运用中,都是运用了NULL,代表返回的安全句柄不可以被子进程所继承。但在本运用中,涉及到的是匿名管道。匿名管道就是父子进程之间的通信,所以结构体必须设置相应的值。子进程要想获得匿名管道的读写句柄,只能从父进程继承而来。一旦子进程有了继承而来的读写句柄,就可以和父进程进行通信了。对于机构体SECURITY_ATTRIBUTES,最重要的是第三个参数bInheritHandle,表示Specifies whether the returned handle is inherited when a new process is created. If this member is TRUE, the new process inherits the handle.

b. CreateProcess:如果创建管道成功,则创建子进程,并将管道的读写句柄传递给子进程。

1.MFC单文档程序菜单中增加创建管道,读取数据,写入数据三项

2.View类中增加成员变量

3.  CreateProcess

倒数第三个参数 [in] Pointer to a STARTUPINFO structure that specifies how the main window for the new process should appear 指向STARTUPINFO结构体的一个指针,用来指定新的进程它的主窗口如何出现

最后一个参数指向PROCESS_INFORMATION结构体的一个指针的返回值,用来接收新的进程的标识信息

PROCESS_INFORMATION

The PROCESS_INFORMATION structure is filled in by the CreateProcess function with information

 about a newly created process and its primary thread.

typedef struct _PROCESS_INFORMATION {

    HANDLE hProcess;

    HANDLE hThread;

    DWORD dwProcessId;

    DWORD dwThreadId;

} PROCESS_INFORMATION;

Members

hProcess //新创建进程的句柄

A handle to the newly created process. The handle is used to specify the process in all

 functions that perform operations on the process object.

hThread //新创建进程主线程的句柄

A handle to the primary thread of the newly created process. The handle is used to specify

the thread in all functions that perform operations on the thread object.

dwProcessId //全局进程标识符,如果进程结束操作系统可能会将标识分配给其他进程,我们调用标识的时候要确保进程在运行

A global process identifier that can be used to identify a process. The value is valid from the time the process is created until the time the process is terminated.

dwThreadId //全局线程标识符

A global thread identifiers that can be used to identify a thread. The value is valid from the

time the thread is created until the time the thread is terminated.

创建匿名管道具体代码:

SECURITY_ATTRIBUTES sa;

      //总共就三个参数

      sa.bInheritHandle=TRUE; //表示可被子进程所继承

      sa.lpSecurityDescriptor=NULL; //安全描述符号一般都设置成NULL,即默认描述符

      sa.nLength=sizeof(SECURITY_ATTRIBUTES); //管道长度

      if(!CreatePipe(&hRead,&hWrite,&sa,0))

      {

           MessageBox("创建匿名函数失败!");

           return;

      }

      //管道创建成功后,接着创建子进程,并将读写句柄传递给子进程

      STARTUPINFO sui;

      PROCESS_INFORMATION pi;

      //调用ZeroMemory方法将该结构体中的所有成员都置为0,这是因为这个结构体的成员很多,如果开始的时候没有置为0的话,那它的值是随机的,将这样的结构体传给CreateProcess,可能会影响到执行的结果。

      ZeroMemory(&sui,sizeof(STARTUPINFO));

      sui.cb=sizeof(STARTUPINFO); //设置结构体的大小

      sui.dwFlags=STARTF_USESTDHANDLES; //该标识表示标准输入句柄,标准输出句柄和错误句柄是有用的

      sui.hStdInput=hRead; //将子进程的输入句柄设置成父进程的读句柄

      sui.hStdOutput=hWrite; //将子进程的输出句柄设置成父进程的写句柄

      sui.hStdError=GetStdHandle(STD_ERROR_HANDLE); //得到标准错误句柄,是父进程的错误句柄,该行代码在本程序中没有实际的用途意义

//因为是匿名管道,是没有名称的管道,只有通过CreateProcess由上而下的传递管道操作句柄。

if(!CreateProcess("..\\Child\\Debug\\Child.exe",NULL,NULL,NULL,

           TRUE,0,NULL,NULL,&sui,π))

      {

           MessageBox("创建子进程失败!");

           CloseHandle(hRead);

           CloseHandle(hWrite);

 

           //避免在析构函数中再次关闭,析构函数采用:

           //if(hRead) CloseHandle(hRead)

           hRead=NULL;

           hWrite=NULL;

           return;

      }

      else

      {

           //创建一个新的进程的时候,系统会创建一个进程内核对象和一个线程内核对象,内核对象都有一个使用基数,初始调用的时候,都设置为1。在CreateProcess返回之前,该函数打开进程和线程的内核对象,并将进程相关的句柄放置到结构体PROCESS_INFORMATIONhProcesshThread中,当Process在内部打开这些对象的时候,使得每个对象的使用基数增加到2了。如果在父进程中不需要使用这两个句柄,就将这个句柄进行关闭,使得使用基数减1。当子进程终结的时候,系统会在将使用基数减1,使得子进程的进程内核对象和线程内核对象的使用基数变为0,这样内核对象就可以被释放了。

           CloseHandle(pi.hProcess); //关闭子进程的句柄

           CloseHandle(pi.hThread); //关闭子进程中主线程的句柄

}

          

父进程写匿名管道:

char *buf="hello world";

      DWORD dwWrite;

      if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))

      {

           MessageBox("匿名管道写入数据失败!");

           return;

}

父进程读匿名管道:

char buf[100];

      DWORD dwRead;

      if(!ReadFile(hRead,buf,100,&dwRead,NULL))

      {

           MessageBox("匿名管道读取数据失败!");

           return;

      }

MessageBox(buf);

     

创建子进程程序:

可以将获取父进程的匿名管道的读写句柄操作放在CView类的OnInitialUpdate方法中实现,该方法是在CView完全构造后调用的第一个方法。代码如下:

hRead=GetStdHandle(STD_INPUT_HANDLE);

hWrite=GetStdHandle(STD_OUTPUT_HANDLE);

子进程的读写匿名管道的代码和父进程的一样,这里不再累述。

    为了让子进程从众多继承的句柄中区分出管道的读、写句柄,就必须将子进程的特殊句柄设置为管道的读写句柄。

参考

1MSDN

2]《VC++ 深入》

3http://blog.csdn.net/liufei_learning/archive/2009/12/17/5026410.aspx 

posted @ 2010-09-04 23:34  浪里飞  阅读(2456)  评论(0编辑  收藏  举报