[C++]兩個程式之間,使用自己定義的結構來溝通-COPYDATA

這篇稍微複雜一點,呼應先前說得架構,必須讓兩個執行程式之間要溝通,使用Message的話大約會在10多個毫秒(Millisecond)內完成,是最快又普遍的方式。程序上說起來很簡單,假設有A.exe和B.exe要溝通,先說單向的部份,那A.exe必須先找到B.exe,然後再丟訊息過去,B.exe收到訊息後處理,整個過程大概就這樣而已。後面詳細說明如何做。一般簡單方式會用FindWindow(),我用另一個方法比較準確。

會用到以下的元件與方法:
- Process32First()/Process32Next() - 找出執行程式(*.exe)
- OpenProcess() - 取得ProcessID
- EnumWindows(), CALLBACK function - 比對GetWindowThreadProcessId()找出正確的HWND
- COPYDATASTRUCT - 可攜帶自訂的任意結構
- SendMessage - 送出WM_COPYDATA訊息
- ON_MESSAGE(), ON_WM_COPYDATA() - 處理Message訊息

說明:

Step 1. 
首先透過以下程序,尋找工作管理員列表中所有的程式,找出叫做「B.exe」的ProcessID,這是每個程式特有的ID,不會重複。一般使用的是FindWindow()的方法,但這方法局限於程式的標頭顯示名稱,如果是NULL或是亂取就很麻煩,所以檔名查找是比較好的方式。
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (Process32First(snapshot, &entry) == TRUE)
{
 while (Process32Next(snapshot, &entry) == TRUE)
 {
  if (wcscmp(entry.szExeFile, L"B.exe") == 0)
  {
   HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID);
   //Step 2. 找出HWND
   //Step 3. 填入CopyData,並傳送訊息
   CloseHandle(hProcess);
   }
  }
}
CloseHandle(snapshot);

Step 2. 
找到程式之後,因為傳送訊息是透過SendMessage的方式,他需要的參數是「視窗Handle(HWND)」,而找出的Process是「HANDLE」,兩者不同,所以我們需要透過剛剛找出的ProcessID反找視窗的Handle。反找方式是透過「EnumWindows(CallbackFun, lparam)」,第一個參數是帶所謂的CALLBACK function,意思是說,找到視窗之後,會去回呼CallbackFun得知。比較重要的地方是GetWindowLong這一個,因為一支程式會有好多thread,不知找到哪一個,所以要找的是看得到的視窗(WS_VISIBLE)的thread才可以。
EnumWindows(EnumWindowCallBack, dwProcessId);
BOOL CALLBACK EnumWindowCallBack(HWND hwnd, LPARAM lParam)
 if ((GetWindowLong(hwnd,GWL_STYLE) & WS_VISIBLE)) {
  DWORD  dwProcessId;
  GetWindowThreadProcessId(hwnd, &dwProcessId);   
 if( dwProcessId == lParam)//id一樣
  {
    this->hWnd = hwnd;//把對應的HWND回傳
    return FALSE;//找到就跳出,不用一直找下去
   }
 }
 return TRUE;//一直列舉直到所有窗口找完
}

Step 3.
COPYDATA是Windows內建的結構,然後假定我們有一個自定義的結構體,我先宣告裡頭只有兩個欄位,分別是dID和pID,如下:
typedef struct
{
 unsigned int dID;
 unsigned int pID;
}HandshakeMsg;
HandshakeMsg msgItem;
COPYDATASTRUCT copydata;

然後在程式裡頭去設定這些值,再利用SendMessage,將copydata利用WM_COPYDATA送出。
msgItem.pID = GetCurrentProcessId();//取的目前執行視窗的ProcessID
msgItem.dID = 0;
copydata.dwData = 0;//Specifies data to be passed to the receiving application.

copydata.cbData = sizeof(HandshakeMsg);
copydata.lpData = &msgItem;//自定義結構體利用這個參數
::SendMessage(hwnd, WM_COPYDATA, (WPARAM)GetSafeHwnd(), (LPARAM)&copydata);

Step 4.
至此以上算是完成傳送端的部分,再來檢視接收端。接收端Demo比較簡單,只要接收WM_COPYDATA消息。所以在BEGIN_MESSAGE_MAP去響應ON_WM_COPYDATA(),對應的函式名稱宣告如下:
OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct);
所以我們在函式裡頭就這樣寫,就可以接收到訊息了。
CString tmpS;
memcpy(&msgItem,(HandshakeMsg*)pCopyDataStruct->lpData,sizeof(HandshakeMsg));  //複製資料
tmpS.Format(L"dID = %d, pID = %d",msgItem.dID, msgItem.pID);

--程式執行圖--

--完整Source Code(VC++ 2008 vaildated,Win8.1 32bit執行OK)
TestMessageProgram(發送訊息端)
DebugLog(接收訊息端)




2 意見:

豆花 提到...

感謝!!

Aaron 提到...

範例連結下載點,目前沒法下載了,可以請您更新一下嗎?謝謝!

搜尋此網誌

總網頁瀏覽量

TK呱呱

Made with by TK