如何在控制外部程式的視窗元件(FindWindow, SendMessage)

                  .Introduction
        
        在開發程式中, 當碰到底層功能沒開放出來的時候, 往往最後只能在外部控制介面來處理,舉個例子, 像是碰到比較特殊的硬體裝置, 而廠商沒提供driver的原始碼, sdk的功能不完整等等問題之類的, 在只有官方的控制用應用程式的情況下, 此時也就只能寫外部程式直接來控制,以下論述實作方式.



        首先, 如下圖, 拿一個列印介面來當例子, 主要步驟有兩個部分, 首先要先取得視窗控制元件,在來世更進一步的做控制.

列印程式介面



I. Get Window HWND
        
        要取得一個windows的控制權, 首先要取得該視窗的HWND, 實作上可以使用FindWindow函式, 詳細可以看msdn.

FindWindow:
        http://msdn.microsoft.com/en-us/library/windows/desktop/ms633499%28v=vs.85%29.aspx

        由函式的定義來看, 可以指定的參數有兩個, 一個是class名稱, 指定控制元件的類別,此部分不給也可以, 但是要防止碰到名稱一樣的控制元件,第二個是控制元件名稱, 以這個例子就是"列印", 程式碼如下
?
1
2
3
4
5
HWND wnd = FindWindow( NULL, "列印");
if(wnd)
{
    //處理    
}


II.對視窗做控制
     
        在視窗的控制方面, 有兩種方式, 以下個別做說明
如果可以確定元件id的話, 可以Send WM_COMMAND做處理
抓到最底層目標的原件, 直接送訊息

1.WM_COMMAND
        在這邊特別說明一下 WM_COMMAND, 當我們在做視窗操作時, 像是按下按鈕, 下拉選單等等, 都會觸發WM_COMMAND, 如果知道操作對象的話,某種程度直接使用WM_COMMAND就可以處理了, 詳細可以看msdn


WM_COMMAND:       
        http://msdn.microsoft.com/en-us/library/windows/desktop/ms647591%28v=vs.85%29.aspx


WM_COMMAND的訊息有三種方式
Menu
Accelerator
Control

其中Accelerator不討論,這邊看menu以及Control的方式

.WM_COMMAND - Menu
       
        實際測試了一下, 按鈕的話照menu方式直接送ID也有BN_CLICKED的效果,非按鈕的我就不確定了, 程式碼如下.

?
1
SendMessage(wnd, WM_COMMAND, IDOK, NULL);

        參考程式碼頁面,照定義送的是menu元件的話, 則wParam的高位元會是0, 低位元是id, 其實等同IDOK(送高位元放0, 低位元IDOK), 如果怕判斷會出問題的話, 可以用MAKEWPARAM即可

?
1
2
3
4
5
6
7
WPARAM MAKEWPARAM(
  WORD wLow,
  WORD wHigh
);
 
WPARAM wParam = MAKEWPARAM( IDC_OK, 0);
SendMessage(wnd, WM_COMMAND, MAKEWPARAM(IDOK,0),0);

.WM_COMMAND - Control

        Control的方式較複雜, 以下是msdn的定義
wParam
        高位元放Control-defined notification code       
        低位元放Control identifier
lParam
        Handle to the control window
      
        此例已知控制視窗的wnd, 所以處理wParam即可

?
1
SendMessage(wnd, WM_COMMAND, MAKEWPARAM(IDOK,BN_CLICKED),0);

2.抓最底層目標元件

        目前已取得目標視窗的HWND, 要在更進一步找視窗裡面的元件時, 需要使用FindWindowEX來處理, 使用方式一樣是由名稱找, 這邊範例指定class名稱, 名稱定義詳細可以看msdn, 這邊就不詳述了, 程式碼如下


?
1
2
3
HWND wndButton = FindWindowEx(wnd, NULL,"Button", "取消");;
 
SendMessage(wndButton, BM_CLICK, 0, 0);


III.實作程式碼


?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void main()
{
    HWND wnd = FindWindow( NULL, "列印");
    if(wnd)
    {    
 
        //方式1..知道元件ID的話可以用, 
        //物件是按鈕的話,直接送ID也可以達到Click的效果
        //另外, 如果真的不確定id,
        //IDOK(1)通常會是windows預設介面的"確定",IDCANCEL同
     
        //menu方式
        SendMessage(wnd, WM_COMMAND, IDOK, NULL);
        SendMessage(wnd, WM_COMMAND, MAKEWPARAM(IDOK,0),0);
     
        //Control方式
        SendMessage(wnd, WM_COMMAND, MAKEWPARAM(IDOK,BN_CLICKED),0);
     
     
        //方式2, 進一步取得按鈕, 送出按下訊息
        HWND wndButton = FindWindowEx(wnd, NULL,"Button", "取消");//"列印(&P)");
        if(wndButton)
        {
            SendMessage(wndButton, BM_CLICK, 0, 0);
        }
     
     }
}


http://arkkk.blogspot.tw/2011/11/findwindow-sendmessage.html