進程間通信

進程間通信的四種方式?
一、剪貼板
BOOL OpenClipboard( );---》打開剪切板,,假如其他程序要打開剪切板,必須調用CloseClipboard
BOOL CloseClipboard(VOID);

BOOL EmptyClipboard(VOID);---》清空剪切板並且釋放裏面的數據,交給當前打開剪切板的所有者

HANDLE SetClipboardData(
  UINT uFormat, // clipboard format
  HANDLE hMem   // data handle---》如果設置爲NULL,則爲延遲方式,暫時不提交,避免提交的數據量過大,浪費空間,直到進程需要,才提交數據
);

剪切板加載數據:
 if(OpenClipboard())
 {
  CString str;
  HANDLE hClip;
  char *pBuf;
  EmptyClipboard();
  GetDlgItemText(IDC_EDIT_SEND,str);
  hClip=GlobalAlloc(GMEM_MOVEABLE,str.GetLength()+1);
  pBuf=(char*)GlobalLock(hClip);
  strcpy(pBuf,str);
  GlobalUnlock(hClip);
  SetClipboardData(CF_TEXT,hClip);
  CloseClipboard();
 } 

 

HANDLE GetClipboardData(
  UINT uFormat   // clipboard format
);

BOOL IsClipboardFormatAvailable(-----》檢測剪切板的數據格式
  UINT format   // clipboard format
);


剪切板接收數據:
 if(OpenClipboard())
 {
  if(IsClipboardFormatAvailable(CF_TEXT))
  {
   HANDLE hClip;
   char *pBuf;
   hClip=GetClipboardData(CF_TEXT);
   pBuf=(char*)GlobalLock(hClip);
   GlobalUnlock(hClip);
   SetDlgItemText(IDC_EDIT_RECV,pBuf);
  }
  CloseClipboard();
 }

二、匿名管道---->只能在本地實現
BOOL CreatePipe(
  PHANDLE hReadPipe,                       // read handle
  PHANDLE hWritePipe,                      // write handle
  LPSECURITY_ATTRIBUTES lpPipeAttributes,  // security attributes
  DWORD nSize                              // pipe size
);
BOOL CreateProcess(
  LPCTSTR lpApplicationName,                 // name of executable module
  LPTSTR lpCommandLine,                      // command line string
  LPSECURITY_ATTRIBUTES lpProcessAttributes, // SD
  LPSECURITY_ATTRIBUTES lpThreadAttributes,  // SD
  BOOL bInheritHandles,                      // handle inheritance option
  DWORD dwCreationFlags,                     // creation flags
  LPVOID lpEnvironment,                      // new environment block
  LPCTSTR lpCurrentDirectory,                // current directory name
  LPSTARTUPINFO lpStartupInfo,               // startup information
  LPPROCESS_INFORMATION lpProcessInformation // process information
);

HANDLE GetStdHandle(-----》從標準輸入或者標準輸出或標準錯誤設備上獲取句柄
  DWORD nStdHandle   // input, output, or error device
);

創建管道:
 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))----》Child.exe爲需要啓動的子進程
 {
  CloseHandle(hRead);
  CloseHandle(hWrite);
  hRead=NULL;
  hWrite=NULL;
  MessageBox("創建子進程失敗!");
  return;
 }
 else
 {
  CloseHandle(pi.hProcess);
  CloseHandle(pi.hThread);
 } 

CloseHandle有什麼用處?
1,線程和線程句柄(Handle)不是一個東西,線程是在cpu上運行的.....(說不清楚了),線程句柄是一個內核對象。我們可以通過句柄來操作線程,但是線程的生命週期和線程句柄的生命週期不一樣的。線程的生命週期就是線程函數從開始執行到return,線程句柄的生命週期是從CreateThread返回到你CloseHandle()。

2,所有的內核對象(包括線程Handle)都是系統資源,用了要還的,也就是說用完後一定要closehandle關閉之,如果不這麼做,你係統的句柄資源很快就用光了。

3,如果你CreateThread以後需要對這個線程做一些操作,比如改變優先級,被其他線程等待,強制TermateThread等,就要保存這個句柄,使用完了在CloseHandle。如果你開了一個線程,而不需要對它進行如何幹預,CreateThread後直接CloseHandle就行了。


所以
CloseHandel(ThreadHandle );
只是關閉了一個線程句柄對象,表示我不再使用該句柄,即不對這個句柄對應的線程做任何干預了。並沒有結束線程。

 

BOOL ReadFile(
  HANDLE hFile,                // handle to file
  LPVOID lpBuffer,             // data buffer
  DWORD nNumberOfBytesToRead,  // number of bytes to read
  LPDWORD lpNumberOfBytesRead, // number of bytes read
  LPOVERLAPPED lpOverlapped    // overlapped buffer
);

讀取信息:
 // TODO: Add your command handler code here
 char buf[100];
 DWORD dwRead;
 if(!ReadFile(hRead,buf,100,&dwRead,NULL))
 {
  MessageBox("讀取數據失敗!");
  return;
 }
 MessageBox(buf);

寫入數據:
 char buf[]="主進程管道測試";
 DWORD dwWrite;
 if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
 {
  MessageBox("寫入數據失敗!");
  return;
 }


在類的析構函數中:
 if(hRead)
  CloseHandle(hRead);
 if(hWrite)
  CloseHandle(hWrite);


再另外創建一個項目,聲明爲Child
1.void CChildView::OnInitialUpdate()
{
 CView::OnInitialUpdate();
 
 hRead=GetStdHandle(STD_INPUT_HANDLE);
 hWrite=GetStdHandle(STD_OUTPUT_HANDLE);
}
2.讀取和寫入和以上一樣的

必須由主進程啓動子進程纔有父子的關係,否則通過兩個項目分別打開一個窗口,兩者並沒有關係


三、命名管道
  通過網絡來完成進程間的通信,它屏蔽了底層的網絡協議細節。
我們在不瞭解網絡協議的情況下,也可以利用命名管道來實現進程間的通信。
命名管道充分利用了Windows NT和Windows 2000內建的安全機制。
將命名管道作爲一種網絡編程方案時,
它實際上建立了一個客戶機/服務器通信體系,並在其中可靠地傳輸數據。

圍繞Windows文件系統設計的一種機制,採用“命名管道文件系統(Named Pipe File System,NPFS)”接口,
因此,客戶機和服務器可利用標準的Win32文件系統函數(例如:ReadFile和WriteFile)來進行數據的收發。

命名管道服務器和客戶機的區別?
 服務器是唯一一個有權創建命名管道的進程,也只有它才能接受管道客戶機的連接請求。

客戶機只能同一個現成的命名管道服務器建立連接。

命名管道服務器只能在Windows NT或Windows 2000上創建,
所以,我們無法在兩臺Windows 95或Windows 98計算機之間利用管道進行通信。
不過,客戶機可以是Windows 95或Windows 98計算機,與Windows NT或Windows 2000計算機進行連接通信。

命名管道提供了兩種基本通信模式:
字節模式---》數據以一個連續的字節流的形式,在客戶機和服務器之間流動。
 消息模式----》客戶機和服務器則通過一系列不連續的數據單位,進行數據的收發,每次在管道上發出了一條消息後,它必須作爲一條完整的消息讀入。
 
HANDLE CreateNamedPipe(
  LPCTSTR lpName,                             // pipe name
  DWORD dwOpenMode,                           // pipe open mode
  DWORD dwPipeMode,                           // pipe-specific modes
  DWORD nMaxInstances,                        // maximum number of instances
  DWORD nOutBufferSize,                       // output buffer size
  DWORD nInBufferSize,                        // input buffer size
  DWORD nDefaultTimeOut,                      // time-out interval
  LPSECURITY_ATTRIBUTES lpSecurityAttributes  // SD
);

BOOL ConnectNamedPipe(------》讓服務器端的管道等待客戶端的管道數據到來,而不是連接管道
  HANDLE hNamedPipe,          // handle to named pipe
  LPOVERLAPPED lpOverlapped   // overlapped structure
);

創建管道:
void CNamedPipeSrvView::OnPipeCreate()
{
 // TODO: Add your command handler code here
 hPipe=CreateNamedPipe("////.//pipe//MyPipe",
  PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
  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); 
}
讀取和寫入和匿名管道都是一樣的

BOOL WaitNamedPipe(
  LPCTSTR lpNamedPipeName,  // pipe name
  DWORD nTimeOut            // time-out interval
);
HANDLE CreateFile(
  LPCTSTR lpFileName,                         // file name
  DWORD dwDesiredAccess,                      // access mode
  DWORD dwShareMode,                          // share mode
  LPSECURITY_ATTRIBUTES lpSecurityAttributes, // SD
  DWORD dwCreationDisposition,                // how to create
  DWORD dwFlagsAndAttributes,                 // file attributes
  HANDLE hTemplateFile                        // handle to template file
);

客戶端連接版塊:
void CNamedPipeCltView::OnPipeConnect()
{
 // TODO: Add your command handler code here
 if(!WaitNamedPipe("////.//pipe//MyPipe",NMPWAIT_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;
 }
 
}
讀取和寫入和匿名管道都是一樣的

四、郵槽
基於廣播通信體系設計出來的,它採用無連接的不可靠的數據傳輸。

郵槽是一種單向通信機制,創建郵槽的服務器進程讀取數據,打開郵槽的客戶機進程寫入數據。

爲保證郵槽在各種Windows平臺下都能夠正常工作,我們傳輸消息的時候,應將消息的長度限制在424字節以下。

HANDLE CreateMailslot(
  LPCTSTR lpName,                            // mailslot name
  DWORD nMaxMessageSize,                     // maximum message size
  DWORD lReadTimeout,                        // read time-out interval
  LPSECURITY_ATTRIBUTES lpSecurityAttributes // inheritance option
);
讀取數據:
void CMailslotSrvView::OnMailslotRecv()
{
 // TODO: Add your command handler code here
 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); 
}

寫入數據:

void CMailslotCltView::OnMailslotSend()
{
 // TODO: Add your command handler code here
 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[]="郵槽測試數據";
 DWORD dwWrite;
 if(!WriteFile(hMailslot,buf,strlen(buf)+1,&dwWrite,NULL))
 {
  MessageBox("寫入數據失敗!");
  CloseHandle(hMailslot);
  return;
 }
 CloseHandle(hMailslot); 
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章