這篇稍微複雜一點,呼應先前說得架構,必須讓兩個執行程式之間要溝通,使用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或是亂取就很麻煩,所以檔名查找是比較好的方式。
首先透過以下程序,尋找工作管理員列表中所有的程式,找出叫做「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,如下:
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)©data);
Step 4.
至此以上算是完成傳送端的部分,再來檢視接收端。接收端Demo比較簡單,只要接收WM_COPYDATA消息。所以在BEGIN_MESSAGE_MAP去響應ON_WM_COPYDATA(),對應的函式名稱宣告如下:
--程式執行圖--
至此以上算是完成傳送端的部分,再來檢視接收端。接收端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);
--程式執行圖--
TestMessageProgram(發送訊息端)
DebugLog(接收訊息端)
2 意見:
感謝!!
範例連結下載點,目前沒法下載了,可以請您更新一下嗎?謝謝!
張貼留言