用WM_COPYDATA消息來實現兩個進程之間傳遞數據


文着重講述了如果用WM_COPYDATA消息來實現兩個進程之間傳遞數據.

進程之間通訊的幾種方法:
在Windows程序中,各個進程之間常常需要交換數據,進行數據通訊。常用的方法有


  1、使用內存映射文件 
  2、通過共享內存DLL共享內存 
  3、使用SendMessage向另一進程發送WM_COPYDATA消息

比起前兩種的復雜實現來,WM_COPYDATA消息無疑是一種經濟實惠的一中方法.
WM_COPYDATA消息的主要目的是允許在進程間傳遞只讀數據。Windows在通過WM_COPYDATA消息傳遞期間,不提供繼承同步方式。SDK文檔推薦用戶使用SendMessage函數,接受方在數
據拷貝完成前不返回,這樣發送方就不可能刪除和修改數據:
這個函數的原型及其要用到的結構如下:
SendMessage(hwnd,WM_COPYDATA,wParam,lParam); 
其中,WM_COPYDATA對應的十六進制數為0x004A
wParam設置為包含數據的窗口的句柄。lParam指向一個COPYDATASTRUCT的結構: 
typedef struct tagCOPYDATASTRUCT{ 
    DWORD dwData;//用戶定義數據 
    DWORD cbData;//數據大小 
    PVOID lpData;//指向數據的指針 
}COPYDATASTRUCT; 
該結構用來定義用戶數據。
具體過程如下:
首先,在發送方,用FindWindow找到接受方的句柄,然后向接受方發送WM_COPYDATA消息.
接受方在DefWndProc事件中,來處理這條消息.由於中文編碼是兩個字節,所以傳遞中文時候字節長度要搞清楚.
代碼中有適量的解釋,大家請自己看吧.
用WM_COPYDATA的前提:


1,知道接收消息進程的句柄。

2,接收消息進程重載了WM_COPYDATA消息映射,能對其做出反應(否則不是發送端自作多情了?)

看過前提,的出結論:在自己寫的兩個進程間用WM_COPYDATA再好不過。

下面CODE幾行就說明了一切。

獲得句柄的方法,最簡單的方法就是使用FindWindow,找窗口類,或者名,如果你覺得這樣不把握,那就利用SetProp個窗口做個記號....(不說這些,跑踢兒了都)

OK,開始寫發送端代碼:

HWND hWnd = FindWindow(NULL,"MyApp");

if(hWnd!=NULL)
{
      COPYDATASTRUCT cpd; /*給COPYDATASTRUCT結構賦值*/
      cpd.dwData = 0;
      cpd.cbData = strlen("字符串");
      cpd.lpData = (void*)"字符串";
      ::SendMessage(hWnd,WM_COPYDATA,NULL,(LPARAM)&cpd);//發送!
      /*完事兒了!!*/
}

接收端重載ON_WM_COPYDATA消息映射函數(下面是手工所要加的,你最好還是用ClassWizard)

afx_msg BOOL OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct);
ON_WM_COPYDATA()/*消息映射*/
BOOL CMainFrame::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct) 
{
        AfxMessageBox((LPCSTR)(pCopyDataStruct->lpData));/*利用對話框表示收到消息*/
        return CWnd::OnCopyData(pWnd, pCopyDataStruct);
}

進程通信還有其他一些手段,相對來說比較麻煩,但局限性要比WM_COPYDATA小。當然你也可以兩端都注冊一個消息來通信。
使用WM_COPYDATA進行進程間通信的一個問題
開發中有時需要進程間傳遞數據,比如對於只允許單實例運行的程序,當已有實例運行時,再次打開程序,可能需要向當前運行的實例傳遞信息進行特殊處理。對於傳遞少量數據

的情況,最簡單的就是用SendMessage發送WM_COPYDATA消息,所帶參數wParam和lParam可以攜帶相關數據。由於SendMessage是阻塞的,在接收數據進程處理完數據之前不會返回,

發送方不會刪除或修改數據,因此這種方法是簡單且安全的,不過數據量不能太大,否則會由於處理時間過長造成阻塞假死。

    用SendMessage發送WM_COPYDATA的方法如下:

    lResult = SendMessage(    // returns LRESULT in lResult
       (HWND) hWndControl,    // handle to destination control
       (UINT) WM_COPYDATA,    // message ID
       (WPARAM) wParam,    // = (WPARAM) () wParam;
       (LPARAM) lParam    // = (LPARAM) () lParam;
    );

    其中,wParam為發送數據方的窗口句柄,lParam為指向一個COPYDATASTRUCT類型結構體的指針,該結構體中包含了傳遞的數據信息。COPYDATASTRUCT定義如下:

    typedef struct tagCOPYDATASTRUCT {
        ULONG_PTR dwData;
        DWORD cbData;
        PVOID lpData;
    } COPYDATASTRUCT, *PCOPYDATASTRUCT;

    其中,dwData為自定義的數據,cbData指定lpData指向數據的大小,lpData為指向數據的指針。按照前面所說,在使用WM_COPYDATA時要保證數據的只讀屬性,即不能有發送方
的其他線程對傳遞數據進行改寫。(這也解釋了為什么不允許用PostMessage發送WM_COPYDATA,因為PostMessage函數是異步的。還有一點需要注意的是由於SendMessage是阻塞的
,所以容易引起死鎖,可以考慮用SendMessageTimeout代替。)另外,如果傳遞數據中涉及到對象或系統資源,必須確保接收方可以對其進行處理,比如HDC、HBITMAP之類的資源
是無效的,他們屬於不同的進程。
    在使用的時候,要用FindWindow等API找到接收方的窗口句柄;接收方的程序中要添加對WM_COPYDATA消息的響應。

    前幾天寫程序用到WM_COPYDATA進行進程間通信,但是接收方怎么也收不到消息。調試發現找到的窗口句柄是沒有問題的,查看MSDN也沒有什么提示,百思不得其解。
    后來看了一些示例代碼,發現不同之處是我的SendMessage調用中wParam和lParam參數都是0,因為我只是需要通過WM_COPYDATA消息通知一下接收程序即可,不用傳遞任何數據。
試着將這兩個參數改為非空,接收方就可以收到消息了。總結結論為:wParam參數是否為0沒有影響,但是lParam參數必須為非空,即必須指向一個有效的COPYDATASTRUCT結構體。
    原因是什么呢?查了一些資料發現,SendMessage(WM_COPYDATA)底層是通過文件映射(File Mapping)完成的,大概流程是發送方線程根據COPYDATASTRUCT結構體中的傳遞

數據信息,在共享內存中進行數據復制,接收方線程則會到共享內存中讀取數據進行處理。因此如果指向COPYDATASTRUCT結構的指針為空的話,流程是無法進行的,所以接收方也

理所當然收不到消息。
  進程間通信的方法有多種,其中,對於少量數據可以用WM_COPYDATA方便的實現通信(如果對於大量數據的話,由於SendMessage是阻塞的,只有接收方響應了消息,SendMessage才
能返回,否則則一直阻塞,所以,對於大量數據來說,用SendMessage就容易造成窗口假死) 。


MSDN幫助里面有該消息的例子,說的也很清楚。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM