Hook 程序中使用 WindowFromPoint 時如何讓其它程式暫時不接收滑鼠事件? |
答題得分者是:Zard
|
RedSnow
版主 發表:79 回覆:1322 積分:845 註冊:2003-12-15 發送簡訊給我 |
各位好,我在撰寫一隻擷取螢幕畫面 (抓圖) 的程式,與一般同類程式一樣,提供了三種擷取方式:整個畫面、自定區域、選擇視窗,原先我在撰寫第三種擷取視窗的方式時,是配合 Timer 來使用,但總覺得使用上不夠便利,因此也想仿效一般截圖程式,使用滑鼠來選擇欲擷取的視窗,我在 K.Top 參考了一些資料之後,試著以 SetWindowsHookEx(WH_JOURNALRECORD...) 來捕捉滑鼠的位置,然後透過 WindowFromPoint 來取得滑鼠所在位置的視窗 Handle,接著在滑鼠所在視窗的周邊畫框標示出來,當點擊滑鼠按鈕後,才將被點擊的視窗畫面截取下來,這些動作也都順利做出來了。 現在剩下一個問題,就是當我在移動滑鼠指標時,其它的程式仍會接收到滑鼠事件,例如:Flat 型態的按鈕項目會在滑鼠經過時接收到滑鼠移動的信號而變成凸起狀,以至於畫面會被破壞掉 (如下圖),以及點擊滑鼠告訴程式本身要擷取所在視窗畫面時,被點擊的視窗會被觸發,而跑到前景來。 我想請教各位的問題如下:
1. 截圖程式使用 SetWindowsHookEx(WH_JOURNALRECORD...) 來捕捉滑鼠事件,進而抓取滑鼠指向的視窗是否恰當?有無其它方式更為合適? 2. 我應該要如何做,才能在使用滑鼠配合 WindowFromPoint 來選擇視窗的過程中,讓其它程式暫時不接收滑鼠事件?
請各位先進賜教,謝謝!
|
Zard
尊榮會員 發表:24 回覆:396 積分:539 註冊:2003-11-26 發送簡訊給我 |
引言: 各位好,我在撰寫一隻擷取螢幕畫面 (抓圖) 的程式,與一般同類程式一樣,提供了三種擷取方式:整個畫面、自定區域、選擇視窗,原先我在撰寫第三種擷取視窗的方式時,是配合 Timer 來使用,但總覺得使用上不夠便利,因此也想仿效一般截圖程式,使用滑鼠來選擇欲擷取的視窗,我在 K.Top 參考了一些資料之後,試著以 SetWindowsHookEx(WH_JOURNALRECORD...) 來捕捉滑鼠的位置,然後透過 WindowFromPoint 來取得滑鼠所在位置的視窗 Handle,接著在滑鼠所在視窗的周邊畫框標示出來,當點擊滑鼠按鈕後,才將被點擊的視窗畫面截取下來,這些動作也都順利做出來了。 現在剩下一個問題,就是當我在移動滑鼠指標時,其它的程式仍會接收到滑鼠事件,例如:Flat 型態的按鈕項目會在滑鼠經過時接收到滑鼠移動的信號而變成凸起狀,以至於畫面會被破壞掉 (如下圖),以及點擊滑鼠告訴程式本身要擷取所在視窗畫面時,被點擊的視窗會被觸發,而跑到前景來。 我想請教各位的問題如下: 1. 截圖程式使用 SetWindowsHookEx(WH_JOURNALRECORD...) 來捕捉滑鼠事件,進而抓取滑鼠指向的視窗是否恰當?有無其它方式更為合適? 2. 我應該要如何做,才能在使用滑鼠配合 WindowFromPoint 來選擇視窗的過程中,讓其它程式暫時不接收滑鼠事件? 請各位先進賜教,謝謝!要攔截其它視窗的訊息可以用Message Hook, 可以攔滑鼠訊息, 又可以攔截其它程式的滑鼠訊息, 並且可以阻止其它程式收到滑鼠訊息. 請參考 http://delphi.ktop.com.tw/topic.php?topic_id=62441 http://delphi.ktop.com.tw/topic.php?topic_id=33007 |
RedSnow
版主 發表:79 回覆:1322 積分:845 註冊:2003-12-15 發送簡訊給我 |
多謝 Zard 提供參考資訊,我對於 Hook 的使用不太瞭解,因此又設法找了一些相關資料,然後試著改用 WH_GETMESSAGE 來做測試,我設定 Hook 的方式如下:
CaptureWindowFlag = true; OldWindowHandle = NULL; MainForm->Hide(); Delay(1000); if(NULL != hGetMsgProc){ UnhookWindowsHookEx(hGetMsgProc); hGetMsgProc = NULL; } hGetMsgProc=SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, HInstance, 0);Hook 程序的內容設定如下: LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam) { if(nCode < 0){ CallNextHookEx(hGetMsgProc, nCode, wParam, lParam); return 0; } if(CaptureWindowFlag){ LPMSG msg = (LPMSG)lParam; POINT P; P.x = msg->pt.x; P.y = msg->pt.y; HWND ObjHandle = MyWindowFromPoint(P); // 取得滑鼠指標所在的物件視窗 if(msg->message == WM_MOUSEMOVE){ if(ObjHandle != NULL && ObjHandle != OldWindowHandle){ if(OldWindowHandle != NULL){ HighLightWindowFrame(OldWindowHandle); // 清除標示框 } HighLightWindowFrame(ObjHandle); // 繪製標示框 OldWindowHandle = ObjHandle; } }else if(msg->message == WM_LBUTTONDOWN){ HighLightWindowFrame(OldWindowHandle); // 清除標示框 HDC dc = GetWindowDC(ObjHandle); Graphics::TCanvas *ScreenCanvas = new Graphics::TCanvas; ScreenCanvas->Handle = dc; TRect Rect = ScreenCanvas->ClipRect; Rect.right = Rect.right - Rect.left; Rect.bottom = Rect.bottom - Rect.top; Rect.top = 0; Rect.left = 0; MainForm->ImageBMP->Picture->Bitmap->Width = Rect.right; MainForm->ImageBMP->Picture->Bitmap->Height= Rect.bottom; MainForm->ImageBMP->Picture->Bitmap->Canvas->CopyRect(Rect, ScreenCanvas, ScreenCanvas->ClipRect); delete ScreenCanvas; ReleaseDC(ObjHandle, dc); MainForm->Show(); CaptureWindowFlag = false; } } return CallNextHookEx(hGetMsgProc, nCode, wParam, lParam); }但不知是何處設定有誤?Hook 程序未能生效 (未能進入 Hook 程序),因為程序未能生效,因此也尚未開始測試攔截訊息的那一段 (設定 WM_NULL 以便讓其它程式無法接收滑鼠事件訊息)。 |
Zard
尊榮會員 發表:24 回覆:396 積分:539 註冊:2003-11-26 發送簡訊給我 |
一般的Hook若是做用於整個系統, 則必須把Hook Procedure 寫在 DLL 裡, 這點要先了解, 不過這個規則也是有例外, 那就是WH_JOURNALRECORD, 所以你若是直接把之前的WH_JOURNALRECORD那份碼把SetWindowsHookEx(WH_JOURNALRECORD, ...)改為SetWindowsHookEx(WH_GETMESSAGE, ....); 是無法直接使用的. 你要先確定你的Message Hook Procedure 是在DLL中. 另一點要注意的是, 因為是作用於整個系統, 所以Windows會把你的DLL Inject到每個行程中, 各個行程中的DLL要如何互相通訊, 你可以使用共享記憶體的方式. 還有就是在系統裡的每一個行程都會被掛上你的Hook, 這當然也包含了你自己的行程(*.exe), 要注意在使用Message Hook時要避開自己的行程, 否則你的行程也收不到被攔截的message. 這點你可以用GetCurrentProcessId() 得到自己行程的Process ID, 並且在過濾message前先確認DLL並非作用在自己行程本身. 你的碼太長了我還沒有時間看, 抱歉..... 發表人 -
|
RedSnow
版主 發表:79 回覆:1322 積分:845 註冊:2003-12-15 發送簡訊給我 |
多謝 Zard 的指導,因為我對 Hook 的不瞭解,因此先前曾看過的資料也容易忘掉,經過您的說明之後,我想起來當初就是因為不想做成 DLL 檔,並且看到有人提到 WH_JOURNALRECORD 可以直接做成全域性處理,所以才會使用它的,當您提到 WH_GETMESSAGE 之後,我卻忘了它必須做成 DLL 才能正常運作。 我之所以不想將 Hook 做在 DLL 檔案裡,是因為我看過數支截圖 (抓圖) 程式,都未使用 DLL 檔來配合,但是也仍然能夠達到我在本篇開頭所述的那些動作,因此才想撇開 DLL 檔來試試看,照目前我所能瞭解的程度來看,仍然不適合使用 Hook 來做相關的動作,待進修一段時間後再考慮用它吧。 為了解決本篇標題的這個問題,我又想到一些有類似動作的程式,其中之一是 SPY++,它探測視窗的動作給了我一些啟發,後來我做了一些測試後,暫時決定將我要的功能改成不透過 Hook 來處理,我的心得與處理概念如下,供有興趣的網友們參考一下:
1. 在主 Form 之外,另外建立一個 Form,在此暫且以 cForm 稱之。 2. 當點選探測視窗的選項後,將主 Form 隱藏起來,並將 cForm 以 ShowModal 模式呼叫出來。 3. 在 cForm 內設定自身寬度與高度均為 1,Left 與 Top 直接設在滑鼠所在的位置。 4. 設定 SetCapture,以直接將高、寬均僅有一個點的 cForm 隨著滑鼠指標移動。 5. 透過 WndProc 來處理滑鼠移動與點擊的動作。 6. 因為滑鼠的 Hot Spot 直接 "黏" 在 cForm 上,所以可避過其它視窗物件接收滑鼠動作。 7. 移動滑鼠指標時,透過座標取得所在視窗或物件的 Handle 值,然後在該視窗或物件的周邊繪製標示框 (移開時再重繪一次以消除標示框)。 8. 點選視窗目標後,取得指標所在視窗或物件,並將內容繪至自己的 Image 物件上。 9. ReleaseCapture、退出 cForm、恢復主 Form 的顯示。
以上雖然沒有將細節部份描述出來 (其它諸如視窗系統 TrayBar 的處理、繪製邊框....等等的動作不在這裡多做說明了),但大致上就是這樣子了。 只是這個方法也仍有缺點,那就是無法直接使用真正的滑鼠座標來讓 WindowFromPoint 取得視窗或物件的 Handle 值,因為上述提到滑鼠的 Hot Spot 是 "黏" 在 cForm 上的,所以傳回的物件一定是 cForm,因此我在探測物件時,是將滑鼠座標值的 X 與 Y 值各減 1,這樣才能取得 cForm 以外的物件資訊,這麼做雖然不會對操作有什麼大的影響,但終究不是正途,因此這一點尚有待解決,或許 SPY 的作法也可以再一次的拿來當作參考。
|
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |