使用 WM_COPYDATA 在進程間共享數據


   開發中有時需要進程間傳遞數據,比如對於只允許單實例運行的程序,當已有實例運行時,再次打開程序,可能需要向當前運行的實例傳遞信息進行特殊處理。對於傳遞少量數據的情況,最簡單的就是用SendMessage發送WM_COPYDATA消息,所帶參數wParam和lParam可以攜帶相關數據。由於SendMessage是阻塞的,在接收數據進程處理完數據之前不會返回,發送方不會刪除或修改數據,因此這種方法是簡單且安全的,不過數據量不能太大,否則會由於處理時間過長造成阻塞假死。

    用SendMessage發送WM_COPYDATA的方法如下:

      lResult = SendMessage((HWND) hWndControl,
                    (UINT) WM_COPYDATA, // message ID
                   (WPARAM) wParam, // = (WPARAM) () wParam;
                   (LPARAM) lParam // = (LPARAM) () lParam;
                  );

          其中,hWndControl為接收數據方的窗口句柄,wParam為發送數據方的窗口句柄,lParam為指向一個COPYDATASTRUCT類型結構體的指針

 
         
在用MFC AppWizard(exe)創建接收數據的對話框程序后,生成對話框類CDataRecvDlg。在這個類中,首先要定義接收WM_COPYDATA消息的映射,可以用ClassWizard工具來增加,也可以手動增加,但手動增加需要修改三個地方:①在消息映射表中增加ON_WM_COPYDATA();②增加成員函數BOOL CDataRecvDlg::OnCopyData();③在CDataRecvDlg類中增加WM_COPYDATA消息映射函數的定義。

消息作用: 在進程間共享數據(內部通過創建內存映射文件) 消息介紹: 需要用到的數據結構
/類型: typedef struct tagCOPYDATASTRUCT { ULONG_PTR dwData; DWORD cbData; PVOID lpData; } COPYDATASTRUCT, *PCOPYDATASTRUCT; 結構體參數說明: dwData(ULONG) 保存一個數值, 可以用來作標志等 lpData(void*) 待發送的數據的起始地址(可以為NULL) cbData(DWORD) 待發送的數據的長度 消息的參數: hWnd: 接收數據的窗口的句柄 wParam: 傳送該數據的窗口句柄(NULL也無所謂) lParam: COPYDATASTRUCT類型變量的地址 使用示例: COPYDATASTRUCT cds; char msg[] = "女孩不哭"; cds.dwData = 0; cds.lpData = msg; cds.cbData = strlen(msg)+1; //字符串請記得把'\0'加上, 不然就錯了, 這里是ANSI字符串 SendMessage(FindWindow("nbsg_class", NULL), WM_COPYDATA, 0, (LPARAM)&cds); 接收端對該消息的一種可能處理: case WM_COPYDATA: { //這里的消息應該是以 '\0' 結尾的字符串 COPYDATASTRUCT* pCDS = (COPYDATASTRUCT*)lParam; MessageBox(hWnd, pCDS->lpData, "", MB_OK); return TRUE; } 說明:       發送的數據可以是任意的, 我上面只是為了用MessageBox做測試, 所以發送的是以'\0'的字符串.     如果接收消息的應用程序處理了該消息, 它應該返回 TRUE , 否則返回 FALSE. lpData 指向的內存應該是一段"數據", 就是說里面不應該有指向該程序某數據的指針. 因為 SendMessage 在處理 WM_COPYDATA 時, 只是把 lpData 指向的 cbData 個字節復制到共享內存中. 當前進程私有的指針就算是被發送到接收程序, 其也是無法訪問的. 當該消息正當發送時, 該進程的其它線程不能修改其中的數據. 接收端應用程序應該把這段共享內存作為只讀內存來訪問. 請不要嘗試修改其中的內容. lParam 指向的數據只有在該消息處理時有效, 消息返回后無效(共享內存已被釋放). 且接收端也不能釋放該內存. 如果要在消息返回后繼續取得數據, 可以把它復制到當前進程的某個位置.

另一種接收消息的相應方式:

WM_COPYDATA消息的映射如下:

BEGIN_MESSAGE_MAP(CDataRecvDlg, CDialog)

    //{{AFX_MSG_MAP(CDataRecvDlg)

    ON_WM_COPYDATA()

    //}}AFX_MSG_MAP

END_MESSAGE_MAP()

CDataRecvDlg::OnCopyData()函數的定義如下:

BOOL CDataRecvDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)

{

    m_strCopyData=(LPSTR)pCopyDataStruct->lpData;

    // 獲得實際長度的字符串

    m_strCopyData=m_strCopyData.Left(pCopyDataStruct->cbData);

    // 更新數據

    UpdateData(FALSE);

    return CDialog::OnCopyData(pWnd, pCopyDataStruct);

}

  其中m_strCopyData為接收到的字符串,pCopyDataStruct為COPYDATASTRUCT結構指針。注意由pCopyDataStruct直接得到的m_strCopyData字符串長度可能不是實際發送的字符串長度,需要用發送字符串時所給定的字符串長度來進一步確定,其長度由pCopyDataStruct ->cbData來得到。

 
         

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

     原因是什么呢?查了一些資料發現,SendMessage(WM_COPYDATA)底層是通過文件映射(File Mapping)完成的,大概流程是發送方線程根據COPYDATASTRUCT結構體中的傳遞數據信息,在共享內存中進行數據復制,接收方線程則會到共享內存中讀取數據進行處理。因此如果指向COPYDATASTRUCT結構的指針為空的話,流程是無法進行的,所以接收方也理所當然收不到消息。

SendMessage中接收信息的窗口句柄如何獲取???

 


免責聲明!

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



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