现在的位置: 首页 > 综合 > 正文

鑫MFC笔记教程(17)–进程间通信(总)

2013年10月26日 ⁄ 综合 ⁄ 共 6208字 ⁄ 字号 评论关闭

进程间通信的四种方式:

Ø 剪贴板

Ø 匿名管道

Ø 命名管道

Ø 邮槽

 

1.     剪贴板:

剪贴板其实是系统管理的一个内存区域,当一个程序发生拷贝的时候,将是该内存区域得到填充,使用粘贴的时候是重该区域取出数据,然后显示的对应窗口上。

 

将指定内容赋值到剪贴板上:

a.        打开剪贴板:OpenClipboard注意:一旦打开了剪贴版,其它运用程序将无法修改剪贴板,直到调用了CloseClipboard

b.       清空剪贴板EmptyClipboard,清空剪切板,并将所有权交付给打开剪贴板的运用程序

c.        为即将拷贝的内容分配内存空间:GlobalAlloc,第一个参数指示分配内存的类型,重要的有两类,GMEM_FIXEDAllocates fixed memory. The return value is a pointerGMEM_MOVEABLEAllocates movable memory. In Win32, memory blocks are never moved in physical memory, but they can be moved within the default heap. The return value is a handle to the memory object. To translate the handle into a pointer, use the GlobalLock function. This flag cannot be combined with the GMEM_FIXED flag.

本例中采用GMEM_MOVEABLE,其返回值是一个指向内存对象的句柄。

d.       将句柄转换为指针:GlobalLock,将指定内存块锁定。

The internal data structures for each memory object include a lock count that is initially zero. For movable memory objects, GlobalLock increments the count by one, and the GlobalUnlock function decrements the count by one. For each call that a process makes to GlobalLock for an object, it must eventually call GlobalUnlock. Locked memory will not be moved or discarded, unless the memory object is reallocated by using the GlobalReAlloc function. The memory block of a locked memory object remains locked until its lock count is decremented to zero, at which time it can be moved or discarded.  18:26有翻译

e.        将字符串的内容拷贝到可移动堆中:strcpy

f.         释放内存块锁定:GlobalUnlock

g.       放置数据:SetClipboardData, The SetClipboardData function places data on the clipboard in a specified clipboard format. The window must be the current clipboard owner, and the application must have called the OpenClipboard function.

(本例程序没有采用)SetClipboardData的第一个参数可以是指定的格式或NULL,如果是NULL,则采用的是延迟提交的技术,所谓延迟提交表示的是为了避免下面这种情况:当一个拷贝数据到剪贴板的动作发生时,直到下一个从剪贴板上取出数据的过程中,数据一直占用着内存空间,造成了资源浪费。为了改善这种情况,延迟提交技术采用SetClipboardData调用一个空的内存区块,当下一个从剪贴板取出数据的动作发生时,自动发送一个WM_RENERFORMAT消息,剪贴板的所有者程序再次调用具有实际内存区块参数的SetClipboardData方法,发生实际剪贴动作。第二次调用前不用再调用OpenClipboard方法。

h.       关闭剪贴板:CloseClipboard

实现代码如下:

if(OpenClipboard()) //打开剪贴板

      {

           CString str;

           HANDLE hClip; //剪贴板句柄

           char* pBuf;

           EmptyClipboard();

           GetDlgItemText(IDC_EDIT_SEND,str);

 

//分配内存的长度一般是字符串的长度加1用来存放空字符,否则系统将自动覆盖掉现有字符串的最后一位用来存放空字符,空字符作为结尾标识

           hClip=GlobalAlloc(GMEM_MOVEABLE,str.GetLength()+1);

           pBuf=(char*)GlobalLock(hClip); //将句柄转换为指针,返回内存对象地址并加锁,如果GlobalAlloc参数是GMEM_FIXED,则这样不需要这样的转换。 GMEM_FIXED锁计数总为零,该语句将增长Lock

           strcpy(pBuf,str); //为分配好的内存空间填充想赋的值

           GlobalUnlock(hClip); //如果GlobalAlloc参数是GMEM_FIXED,则不起作用。该语句将减少Lock数,如果Lock数为0,则指定动态内存区域将可被移动和抛弃

           SetClipboardData(CF_TEXT,hClip); //以指定格式存放数据,不完成指定格式转换,不能完成粘贴

           CloseClipboard();

}

 

从剪贴板上提取数据:

具体代码如下:

if(OpenClipboard())

      { GMEM_FIXED

           if(IsClipboardFormatAvailable(CF_TEXT)) //指定格式数据在接贴板上是否存在

           {

HANDLE hClip=GetClipboardData(CF_TEXT); //从剪贴板上得到数据,且拿到了数据块的句柄

                 char* pBuf;

                 pBuf=(char*)GlobalLock(hClip);

                 GlobalUnlock(hClip);

                 SetDlgItemText(IDC_EDIT_RECV,pBuf);

           }

            CloseClipboard();

}

 

 

2.     what is the pipes

A pipe is a section of shared memory that processes use for communication. The process that creates a pipe is the pipe server. A process that connects to a pipe is a pipe client. One process writes information to the pipe, then the other process reads the information from the pipe. This overview describes how to create, manage, and use pipes.

3.     匿名管道

创建父进程:

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具见 00:51:30

倒数第三个参数 [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,&pi))

      {

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

           CloseHandle(hRead);

           CloseHandle(hWrite);

 

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

           //if(hRead) CloseHandle(hRead)

           hRead=NULL;

           hWrite=NULL;

           return;

      }

      else

      {

           //创建一个新的进程的时候,系统会创建一个进程内核对象和一个线程内核对象,内核对象都有一个使用基数,初始调用的时候,都设置为1。在CreateProcess返回之前,该函数打开进程和线程的内核对象,,并将进程相关的句柄放置到结构体PROCESS_INFORMATION

抱歉!评论已关闭.