进程间通信的四种方式:

1.     剪贴板:

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

 

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

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

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

c.        为即将拷贝的内容分配内存空间:GlobalAlloc,第一个参数指示分配内存的类型,重要的有两类,GMEM_FIXED:Allocates fixed memory. The return value is a pointer;GMEM_MOVEABLE:Allocates 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.

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,则这样不需要这样的转换。该语句将增长Lock数

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

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

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

           CloseClipboard();

}

 

从剪贴板上提取数据:

具体代码如下:

if(OpenClipboard())

      {

           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:如果创建管道成功,则创建子进程,并将管道的读写句柄传递给子进程。

 

创建匿名管道具体代码:

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的hProcess和hThread中,当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);

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

 

4.     命名管道:

命名管道是通过网络来完成进程间的通信,它屏蔽了底层的网络协议细节。我们在不了解网络协议的情况下,也可以利用命名管道来实现进程间的通信。而上述的匿名管道只能在本地机器上,且连个父子进程间通信。命名管道也具有匿名管道的功能。

命名管道服务器和客户机的区别在于:服务器是唯一一个有权创建命名管道的进程,也只有它才能接受管道客户机的连接请求。而客户机只能同一个现成的命名管道服务器建立连接。

命名管道提供了两种基本通信模式:字节模式和消息模式。在字节模式中,数据以一个连续的字节流的形式,在客户机和服务器之间流动。而在消息模式中,客户机和服务器则通过一系列不连续的数据单位,进行数据的收发,每次在管道上发出了一条消息后,它必须作为一条完整的消息读入。

 

命名管道服务器端代码(核心为命名管道的创建与等待客户端的连接):

server:

   static DWORD dwNumberOfByteWritten;
    static DWORD dwNumberOfByteRead;
    //----------------------创建管道-------------------------------
    HANDLE hNamedPipe = CreateNamedPipe(szPipeName, PIPE_ACCESS_DUPLEX,
        PIPE_TYPE_BYTE|PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, BUFFERSIZE,
        BUFFERSIZE, NMPWAIT_USE_DEFAULT_WAIT, NULL);
    if(hNamedPipe == INVALID_HANDLE_VALUE)
    {
        MessageBox(hWinMain, TEXT("命名管道创建失败"), NULL, MB_OK);
        return 1;
    }

    //-----------------------连接管道-------------------------------
    if(ConnectNamedPipe(hNamedPipe, NULL))
    {
        SetDlgItemText(hWinMain, IDC_STATE, TEXT("已连接"));
    }
    else
    {
        SetDlgItemText(hWinMain, IDC_STATE, TEXT("连接失败,请关闭重试"));
        return 1;
    }

    //-----------------------使用管道----------------------------------
    //写入服务器身份信息
    WriteFile(hNamedPipe, szPipeIdentity, lstrlen(szPipeIdentity) + 1,
        &dwNumberOfByteWritten, NULL);
    //读出客户端传来的文件名
    ReadFile(hNamedPipe, szFileName, MAX_PATH,
        &dwNumberOfByteRead, NULL);

    //读取文件内容并写入管道
 struct Msg
 {
  int n;
  char sz[10];
 };
 Msg msg;
 msg.n = 0;
 lstrcpy(msg.sz, "123");
 int nCount = 0;
    while(nCount<10)
    {
  nCount++;
        WriteFile(hNamedPipe, &msg, sizeof(msg), &dwNumberOfByteWritten, NULL);
  Sleep(1000);
    }
 


    CloseHandle(hNamedPipe);

 

client:

   static DWORD dwNumberOfByteWritten;
    static DWORD dwNumberOfByteRead;
    //---------------打开并连接管道-----------------------------
    HANDLE hNamedPipe = CreateFile(szPipeName, GENERIC_WRITE|GENERIC_READ,
        FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if(hNamedPipe == INVALID_HANDLE_VALUE)
    {
        SetDlgItemText(hWinMain, IDC_STATE, TEXT("打开、连接管道失败,请关闭重试"));
        return FALSE;
    }
 
    //-----------------获取服务器身份信息--------------------------------
    ReadFile(hNamedPipe, szBuffer, BUFFERSIZE, &dwNumberOfByteRead, NULL);
    if(lstrcmp(szBuffer, szPipeIdentity) == 0)
    {
        SetDlgItemText(hWinMain, IDC_STATE, TEXT("已连接"));
    }
    else
    {

       
        SetDlgItemText(hWinMain, IDC_STATE, TEXT("未成功连接服务器"));
        return FALSE;
    }
 
    //-----------------向管道写入文件名------------------------------
    WriteFile(hNamedPipe, szFileName, lstrlen(szFileName) + 1, &dwNumberOfByteWritten, NULL);
 
    SetDlgItemText(hWinMain, IDC_FILENAME, szFileName);
    EnableWindow(GetDlgItem(hWinMain, IDC_FILETEXT), TRUE);
 struct Msg
 {
  int n;
  char sz[10];
 };
 Msg msg;
    while(ReadFile(hNamedPipe, &msg, sizeof(msg), &dwNumberOfByteRead, NULL) && dwNumberOfByteRead)
    {//server在就等待read
        SendDlgItemMessage(hWinMain, IDC_FILETEXT, EM_SETSEL, -1, -1);
        SendDlgItemMessage(hWinMain, IDC_FILETEXT, EM_REPLACESEL, FALSE, (LPARAM)msg.sz);
    }
 
 

 

 

 

5.     邮槽:

Ø         邮槽是基于广播通信体系设计出来的,它采用无连接的不可靠的数据传输。

Ø         邮槽是一种单向通信机制,创建邮槽的服务器进程读取数据,打开邮槽的客户机进程写入数据。

Ø         为保证邮槽在各种Windows平台下都能够正常工作,我们传输消息的时候,应将消息的长度限制在424字节以下。

服务器端的代码实现:

HANDLE hMailslot;

hMailslot=CreateMailslot(".//mailslot//MyMailSlot",0,MAILSLOT_WAIT_FOREVER,NULL);

      if(INVALID_HANDLE_VALUE==hMailslot)

      {

           MessageBox("创建邮槽失败!");

           return;

      }

      char buf[100];

      DWORD dwRead;

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

      {

           MessageBox("读取数据失败!");

           CloseHandle(hMailslot);

           hMailslot=NULL;

           return;

      }

      MessageBox(buf);

CloseHandle(hMailslot);

 

客户端的代码实现:

HANDLE hMailslot;

//因为邮槽的客户是负责写入数据,所以访问方式只需要是写,但对于服务器端而言,是读取数据,所以客户端共享方式设置为读FILE_SHARE_READ

hMailslot=CreateFile(".//mailslot//MyMailSlot",GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);

      if(INVALID_HANDLE_VALUE==hMailslot)

      {

           MessageBox("打开邮槽失败!");

           return;

      }

 

      char buf[]="this is test!";

      DWORD dwWrite;

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

      {

           MessageBox("写入数据失败");

           CloseHandle(hMailslot);

           return;

      }

     

CloseHandle(hMailslot);

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值