一、剪贴板
     1、数据发送
        BOOL OpenClipboard();         打开剪贴板。
        向剪贴板中放置数据。
        HANDLE SetClipboardData(UINT uFormat,HANDLE hMem);  
        参数:  uFormat:    指定剪贴板格式,这个格式可以是已注册的格式,
                             或者是任一种标志的剪贴板格式。
                hMem:       具有指定格式的数据的句柄。可以是NULL,指示
                             调用窗口直到有对剪贴板数据的请求时,才提供
                             指定剪贴板的数据。
        HGLOBAL GlobalAlloc(UINT uFlags, SIZE_T dwBytes);
        参数:uFlags:  指定分配内存的方式。
              dwBytes: 指定分配的字节数。
        LPVOID GlobalLock(HGLOBAL hMem);        给全局内存对象加锁。   
                                                GlobalUnLock 对全局
                                                内存对象解锁。
        已被加锁的内存不能被移动,或者被废除,废除调用了GlobalRealloc函数
        重新分配了该内存对象。 
       
        发送数据例子:
        if (OpenClipboard())   打开剪贴板
        {
           CString str;        保存发送的数据。
           HANDLE hClip;       保存调用GlobalAlloc函数后分配的内存对象的句柄
           char *pBuf;         保存调用GlobalLock函数后返回的内存地址
           EmptyClipboard();   清空剪贴板上的数据。
           str="aaaaaaa";
           hClip=GlobalAlloc(GMEM_MOVEABLE,str.GetLength()+1);
           pBuf=(char*)GlobalLock(hClip);
           strcpy(pBuf,str);
           GlobalUnlock(hClip);
           SetClipboardData(CF_TEXT,hClip);
           CloseClipBoard();  关闭剪贴板。
        }
        在数据放置到剪贴板之后,一定要记得调用CloseClipBoard函数关闭剪贴板,
        否则其他进程将无法打开剪贴板。
    
     2、接收数据
        if(OpenClipoard())
        {
           if(IsClipboardFormatAvailable(CF_TEXT)) 检查剪贴板中是否是我们                                  
           {                                       想要的数据格式。
              HANDLE hClip;
              char *pBuf;
              hClip=GetClipBoardData(CF_TEXT);     从剪贴板上获得指定格式的数据。
              pBuf=(char*)GlobalLock(hClip);       获得数据的内存首地址。
              GlobalUnlock(hClip);
           }
           CloseClipboard();
        }
         
         知识点: 当一个提供数据的进程创建了剪贴板数据之后,直到其他进程获取
                  剪贴板数据之前,这些数据都要占据内存空间。人工在剪贴板上放
                  置数据过大,就会浪费内存空间,降低对资源的利用率。为了避免
                  这种浪费,九可以采用延迟提交技术,也九是有数据提供先提供一
                  个指定格式的空剪贴板数据库,即把SetClipboardData函数的hMen
                  参数设置为NULL。当需要获取数据的进程想要从剪贴板上得到数据时,
                  操作系统会想数据提供者发送WM_RENDERFORMAT消息,而数据提供进
                  程可以响应这个消息,并在此消磁的响应函数中,再一次调用
                  SetClipboardData函数,将实际的数据放到剪贴板上,当再次调用
                  SetClipboardData函数时,就不再需要调用OpenClipboard函数,
                  也不再需要调用EmptyClipboard函数。也就是说,为了提高资源利
                  用率,避免浪费内存空间,可以采用延迟提交技术。第一次调用
                  SetClipboardData函数时,将其hMem参数设置为NULL,在剪贴板
                  上以指定的剪贴板格式放置一个空剪贴板数据块。然后直到有其他
                  进程需要数据或者自身进程需要终止运行时再次调用SetClipboardData
                  函数,这时才真正提交数据。
二、匿名管道
     1、基础知识
          匿名管道是ygie未命名的,单向管道,通常用来在一个父进程和一个子进程之间
          传输数据。匿名管道只能实现本地机器上两个进程间的通信,而不能实现跨网
          络的通信。
          BOOL CreatePipe(PHANDLE hReadPipe,PHANDLE hWritePipe,
          LPSECURITY_ATTRIBUTES lpPipeAttibutes,DWORD nSize);
          参数:hReadPipe和 hWritePipe:   这两个参数都是out类型,即作为返回值来使用。
                                           前者返回管道的读取句柄,后者接收管道的写入
                                           句柄。  
                lpPipeAttibutes:          一个指向SECURITY_ATTRIBUTES结构体的指针,
                                           检测返回的句柄是否能被子进程继承。   
                nSize:                    指定管道的缓冲区大小,该大小仅仅是一个
                                           建议值,系统将使用这个值来计算一个适当
                                           的缓冲区大小,人工此参数是0,系统则使用
                                           默认的缓冲区大小。
     2、进程的创建
          BOOL CreateProcess(
               LPCTSTR lpApplicationName,
               LPTSTR lpCommandLine,
               LPSECURITY_ATTRIBUTES lpProcessAttributes,
               LPSECURITY_ATTRIBUTES lpThreadAttributes,
               BOOL bINheritHandles,
               DWORD dwCreationFlags,
               LPVOID lpEnvironment,
               LPCTSTR lpCurrentDirectory,
               LPSTARTUPINFO lpStartupInfo,
               LPPROCESS_INFORMATION lpProcessInformation);      
          参数: lpApplicationName: 一个指向NULL终止的字符串,用来指定可执行程序
                                     的名称,是要运行程序的路径。
                 lpCommandLine:     一个指向NULL终止的字符串,用来指定传递给新进
                                     程的命令行字符串。我们可以在
                                     lpApplicationName参数中传递可执行文件的名称,
                                     在lpCommandLine参数中传递命令行的参数。
                 lpProcessAttributes和lpThreadAttributes:
                                     系统将为新进程创建一个进程内核对象和一个线程
                                     内核对象,后者用于进程的主线程。
                 bINheritHandles:   该参数用来指定父进程随后创建的子进程是否能够
                                     继承父进程的对象句柄。
                 dwCreationFlags:   指定控件优先级类和进程创建的附加标记。
                 lpEnvironment:     一个指向环境块的指针,如果此参数是NULL,那么
                                     新进程使用调用进程的环境。
                 lpCurrentDirectory:一个指向空终止的字符串,用来指定子进程当前的
                                     路径,这个字符串必须是一个完整的路径名。
                 lpStartupInfo:     一个指向STARTUPINFO结构体的指针,用来指定新
                                     进程的主窗口将如何显式。
                 lpProcessInformation:返回值,是一个指向PROCESS_INFORMATION结构
                                       体的指针,用来接收关于新进程的标识信息。
     3、父进程的实现
          private:
               HANDLE hWrite;
               HANDLE hRead;
              
          hRead=NULL;
          hWrite=NULL;
          /**************************创建匿名管道******************************/
          SECURITY_ATTRIBUTES sa;
          sa.bInheritHandle=TRUE;
          sa.lpSecurityDescriptor=NULL;
          sa.nLength=sizeof(SECURITY_ATTRIBUTES);
          if(!CreatePipe(&hRead,&hWrite,&sa,0))
          {
             MessageBox("创建匿名管道失败!");
             return;
          }
          /************************创建结束***********************************/
          /************************创建子进程*********************************/
          STARTUPINFO sui;
          PROCESS_INFORMATION pi;
          ZeroMemory(&sui,sizeof(STARTUPINFO));
          sui.cb=sizeof(STARTUPINFO);
          sui.dwFlags=STARTF_USESTDHANDLES;
          sui.hStdInput=hRead;
          sui.hStdOutput=hWrite;
          sui.hStdError=GetStdHandle(STD_ERROR_HANDLE);
         
          if(!CreateProcess("..\\child\\debug\\child.exe",NULL,NULL,NULL,
                    TRUE,0,NULL,NULL,&sui,&pi))
          {
             CloseHandle(hRead);
             CloseHandle(hWrite);
             hRead=NULL;
             hWrite=NULL;
             MessageBox("创建子进程失败!");
             return;
          }
          else
          {
            CloseHandle(pi.hProcess);     一定不能丢,为的是内核计数器减1.
            CloseHandle(pi.hThread);
          }
          /*********************子进程结束************************************/
          /********************管道读数据************************************/
          char buf[100];                  存放读取的数据
          DWORD dwRead;                   保存实际读取的字节数。
          if(!ReadFile(hRead,buf,100,&dwRead,NULL))
          {
              MessageBox("读取数据失败!");
              return;
          }
          MessageBox(buf);
          /*****************管道读取结束**********************************/
          /******************写入数据*************************************/
          char buf[]="http://www.sunxin.org";  //用于写入的数据
          DWORD dwWrite;                       写入数据的字节数。
          if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
          {
             MessageBox("写入数据失败!");
             return;
          }
          /*****************写入数据结束*******************************/
          if(hRead)
             CloseHandle(hRead);
          if(hWrite)
             CloseHandle(hWrite);
     4、子进程的实现
          private:
               HANDLE hWrite;
               HANDLE hRead;
              
          hRead=NULL;
          hWrite=NULL;
          /****************获取管道的读取和写入句柄************************/
          hRead=GetStdHandle(STD_INPUT_HANDLE);
          hWrite=GetStdHandle(STD_OUTPUT_HANDLE);
          /****************获取管道的读取和写入句柄结束********************/
          /******************管道读数据************************************/
          char buf[100];                  存放读取的数据
          DWORD dwRead;                   保存实际读取的字节数。
          if(!ReadFile(hRead,buf,100,&dwRead,NULL))
          {
              MessageBox("读取数据失败!");
              return;
          }
          MessageBox(buf);
          /*****************管道读取结束******************************/
          /*****************写入数据*******************************/
          char buf[]="http://www.sunxin.org";  //用于写入的数据
          DWORD dwWrite;                       写入数据的字节数。
          if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
          {
             MessageBox("写入数据失败!");
             return;
          }
          /********************写入数据结束*************************/
          if(hRead)
             CloseHandle(hRead);
          if(hWrite)
             CloseHandle(hWrite);
            
          注意: 匿名管道必须是父子进程间通信。
                 匿名管道也可以是同一个进程间读写数据。
三、命名管道               
     1、基础知识
          命名管道通过网络来完成进程间的通信,它屏蔽了底层的网络协议细节。
          命名管道不仅可以在本机子上实现两个进程间的通信,还可以跨网络实现
          两个进程间的通信。对同一个命名管道的实例来说,在某一时刻,它只能
          和一个客户端进行通信。
          HANDLE CreateNamedPipe(
               LPCTSTR lpName,
               DWORD dwOpenMode,
               DWORD dwPipeMode,
               DWORD nMaxInstances,
               DWORD nOutBufferSize,
               DWORD nInBufferSize,
               DWORD nDefaultTimeOut,
               LPSECURITY_ATTRIBUTES lpSecurityAttributes);
          参数:lpName:   指向空终止的字符串,该字符串的格式必须是:
                           "\\.\pipe\pipename".其中该字符串开始是两个
                           连续的反斜杠,其后的圆点标识是本地机器,如果
                           想要与远程的服务器建立连接,那么这个圆点位置
                           处应指定这个远程服务器的名称。接下来是"pipe"
                           这个固定的字符串,也就是说这个字符串的内容不
                           能修改,但其大小写是无所谓的。最后是创建的命
                           名管道的名称。
                dwOpenMode:指定管道的访问方式,重叠方式,写直通方式,
                            还是管道句柄的安全访问方式。
                dwPipeMode:指定管道句柄的类型,读取和等待方式。
                nMaxInstances:指定管道能够创建实例的最大数目。
                nOutBufferSize:指定输出缓冲区所保留的字节数。
                nInBufferSize: 指定为输入缓冲区所保留的字节数。
                nDefaultTimeOut:指定默认的超时值,单位是ms,同一个管
                                 道的不同实例必须指定同样的超时值。
                lpSecurityAttributes:指定命名管道的安全描述符,并确定
                                      子进程是否可以继承这个函数返回的
                                      管道句柄。
     2、服务器端程序
          private:
               HANDLE hPipe;
               hPipe=NULL;
               /******************创建命名管道****************************/
               hPipe=CreateNamePipe("\\\\.\\pipe\\MyPipe",
                     PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,   
                                              //PIPE_ACCESS_DUPLEX双向模式
                     0,1,1024,1024,0,NULL);
               if(INVALID_HANDLE_VALUE==hPipe)
               {
                  MessageBox("创建命名管道失败!");
                  hPipe=NULL;
                  return;
               }
               //创建匿名的人工重置事件对象
               HANDLE hEvent;
               hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
               if(!hEvent)
               {
                  MessageBox("创建事件对象失败!");
                  CloseHandle(hPipe);
                  hPipe=NULL;
                  return;
               }
               OVERLAPPED ovlap;
               ZeroMemory(&ovlap,sizeof(OVERLAPPED));
               ovlap.hEvent=hEvent;
               if(!ConnectNamedPipe(hPipe,&ovlap))    等待客户端请求的到来
               {                                      允许一个服务器进程等待一个客户端
                  if(ERROR_IO_PENDING!=GetLastError())
                  {
                     MessageBox("等待客户端连接失败!");
                     CloseHandle(hPipe);
                     CloseHandle(hEvent);
                     hPipe=NULL;
                     return;
                  }
               }
               if(WAIT_FAILED==WaitForSingleObject(hEvent,INFINITE))
               {
                  MessageBox("等待对象失败!");
                  CloseHandle(hPipe);
                  CloseHandle(hEvent);
                  hPipe=NULL;
                  return;
               }
               CloseHandle(hEvent);
               /**************创建命名管道结束****************************/
               /***************读取数据***********************************/
               char buf[100];
               DWORD dwRead;
               if(!ReadFile(hPipe,buf,100,&dwRead,NULL))
               {
                  MessageBox("读取数据失败!");
                  return;
               }
               MessageBox(buf);
               /******************读取数据结束************************/
               /*****************写入数据*****************************/
               char buf[]="http://www.sunxin.org";
               DWORD dwWrite;
               if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL))
               {
                 MessageBox("写入数据失败!");
                 return;
               }
               /****************写入数据结束**********************/
               if(hPipe)
                  CloseHandle(hPipe);
     3、客户端程序
          private:
               HANDLE hPipe;
               hPipe=NULL;
              
               /***************连接命名管道******************************/
               //判断是否有可以利用的命名管道
               if(!WaitNamedPipe("\\\\.\\pipe\\MyPipe",
                   NMPEAIT_WAIT_FOREVER))  第一个参数是命名管道名称,第二个超时时间
               {                                   同一个命名管道超时间隔必须一样。
                  MessageBox("当前没有可利用的命名管道实例!");
                  return;
               }
               //打开可用的命名管道,并与服务器端进程进行通信
               hPipe=CreateFile("\\\\.\\pipe\\MyPipe",GENERIC_READ|GENERIC_WRITE,
                    0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
               if(INVALID_HANDLE_VALUE==hPipe)
               {
                  MessageBox("打开命名管道失败!");
                  hPipe=NULL;
                  return;
               }
               /****************连接命名管道结束*********************/
               /******************读取数据*************************/
               char buf[100];
               DWORD dwRead;
               if(!ReadFile(hPipe,buf,100,&dwRead,NULL))
               {
                  MessageBox("读取数据失败!");
                  return;
               }
               MessageBox(buf);
               /**************读取数据结束************************/
               /************写入数据******************************/
               char buf[]="命名管道测试程序";
               DWORD dwWrite;
               if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL))
               {
                  MessageBox("写入数据失败!");
                  return;
               }
               /***************写入数据结束*********************/
               if(hPipe)
                  CloseHandle(hPipe);  
四、邮槽
     1、基本知识
          邮槽是基于广播通信体系设计出来的。它采用无连接的不可靠数据传输。
          邮槽是一种单向的通信机制,创建邮槽的服务器进程
          读取数据,打开邮槽的客户机进程写入数据。
          HANDLE CreateMailslot(
               LPCTSTR lpName,
               DWORD nMaxMessageSize,
               DWord lReadTimeout,
               LPSECURITY_ATTRIBUTES lpSecurityAttributes);
          参数:lpName:   指向一个空终止字符串的指针,该字符串指定了邮槽的名称,
                           该名称的格式必须是:"\\.\mailslot=[path]name"
                           其中前两个反斜杠之后字符标识服务器所在的机器名称,
                           圆点表示是本地主机,接着是硬编码的字符串:
                           "mailslot",这jgie字符不能改变,但大小写无所谓,
                           最后的字符串([path]name)就是程序员为邮槽取的名称。
                nMaxMessageSize:由来指定可用被写入到邮槽的单一消息的最大尺寸,
                                 网络可用发送任一大小的消息,可用设置为0。
                lReadTimeout:   指定读取操作的超时时间间隔。以ms为单位。 如果0,
                                 没有消息,函数立即返回。如果是
                                 MAILSLOT_WAIT_FOREVER,在该函数将一直等待。
                                 直到有消息可用。         
                lpSecurityAttributes:创建邮槽默认安全描述。
     2、服务器端程序
          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);
             return;
          }        
          MessageBox(buf);
          CloseHandle(hMailslot);
     3、客户端程序
          HANDLE hMailslot;
          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[]="http://www.sunxin.org";
          DWORD dwWrite;
          //向邮槽写入数据
          if(!WriteFile(hMailslot,buf,strlen(buf)+1,&dwWrite,NULL))
          {
             MessageBox("写入数据失败!");
             CloseHandle(hMailslot);
             return;
          }
          CloseHandle(hMailslot);