WPF呼叫Win32程式的方法
在MSDN中有專門的章節提到了在WPF中嵌入Win32控制元件的辦法,那就是使用 HwndHost ,只要把 Win32控制元件的控制代碼傳遞給 HwndHost 就可以了。MSDN中的例子演示的都是在同一個程序內建立的 Win32控制元件,我一開始認為只要通過FindWindow等Win32API得到外部Win32程式的視窗控制代碼,然後將視窗控制代碼交給 HwndHost 就可以了。實現核心程式碼如下:
protected override HandleRef BuildWindowCore( HandleRef hwndParent)
{ appProc = new appProc.StartInfo.WindowStyle = ProcessWindowStyle .Hidden; appProc.StartInfo.FileName = @"D:/greeninst/netterm/netterm.exe" ; appProc.Start(); //等待初始化完成,實現有點土 Thread .Sleep(1000); hwndHost = Win32Native .FindWindow( "NetTermClass" , null ); // 嵌入在HwnHost中的視窗必須要 設定為WS_CHILD風格 uint oldStyle = Win32Native .GetWindowLong(hwndHost, Win32Native .GWL_STYLE); Win32Native .SetWindowLong(hwndHost, Win32Native .GWL_STYLE, (oldStyle | Win32Native .WS_CHILD)); //將netterm的父視窗設定為HwndHost Win32Native .SetParent(hwndHost, hwndParent.Handle); return new HandleRef ( this , hwndHost); } |
解決這個問題的思路是截獲WPF的視窗訊息,然後把它通過 SendMessage 這個Win32API 轉發給NetTerm。但是找了半天也沒找到WPF的訊息處理的地方,請教同事以後得知WPF根本不像傳統的Win32程式那樣有視窗訊息迴圈,而是自己搞了一套。鬱悶了一會兒,突然靈光一現:管它什麼WPF不WPF,它本質上還是Win32程式,只不過是一個內部使用了DirectX技術的Win32程式而已,只要是Win32程式一定有辦法拿到它的視窗訊息迴圈。啥辦法呢?對!就是視窗鉤子。使用 SetWindowsHookEx 這個Win32API可以截獲一個視窗所有的 訊息迴圈,這樣只要挑出來發給 HwndHost 的訊息,然後把它轉發給 NetTerm視窗就ok了。經過改造以後NetTerm終於活過來了!!!
解決了最核心的問題就該處理普通問題了,主要問題及對策如下:
1、隱藏NetTerm的視窗邊框,這樣看起來就感覺不出來NetTerm是一個外部程式了。思路很簡單使用 GetWindowLong 得到視窗原來的風格,然後再附加一個 WS_BORDER 風格就ok了。
//設定為WS_CHILD風格
uint oldStyle = Win32Native .GetWindowLong(hwndHost, Win32Native .GWL_STYLE);
//&~WS_BORDER去掉邊框,這樣看起來更像一個內嵌的程式,注意()的作用,改變預設的優先順序
Win32Native .SetWindowLong(hwndHost, Win32Native .GWL_STYLE, (oldStyle | Win32Native .WS_CHILD)&~ Win32Native .WS_BORDER);
2、隱藏NetTerm在工作列上的按鈕
只要找到工作列的控制代碼,然後首先向它傳送TB_BUTTONCOUNT得到它上邊按鈕的個數,由於NetTerm是剛剛啟動的,可以認為最後一個按鈕就是NetTerm的按鈕,只要向工作列的控制代碼傳送TB_DELETEBUTTON訊息將最後一個按鈕刪掉就ok了。
private void HideTaskBarButton()
{ IntPtr vHandle = Win32Native.FindWindow("Shell_TrayWnd", null); vHandle = Win32Native.FindWindowEx(vHandle, IntPtr.Zero, "ReBarWindow32", IntPtr.Zero); vHandle = Win32Native.FindWindowEx(vHandle, IntPtr.Zero, "MSTaskSwWClass", IntPtr.Zero); vHandle = Win32Native.FindWindowEx(vHandle, IntPtr.Zero, "ToolbarWindow32", IntPtr.Zero); //得到工作列中按鈕的數目 int vCount = Win32Native.SendMessage(new HandleRef(this, vHandle), (uint)Win32Native.TB_BUTTONCOUNT, IntPtr.Zero, IntPtr.Zero).ToInt32(); //認為最後一個按鈕就是被巢狀程式的按鈕,刪除它 Win32Native.SendMessage(new HandleRef(this, vHandle), Win32Native.TB_DELETEBUTTON, new IntPtr(vCount - 1), IntPtr.Zero); } |
3、 自動登入。在NetTerm啟動以後自動登入到伺服器,並且自動輸入使用者名稱、密碼,並且啟動指定的程式。NetTerm支援在啟動引數中指定要連線的伺服器地址,這樣可以解決自動登入到伺服器的問題;使用 SendMessage( handle , Win32Native.WM_CHAR, ch , IntPtr.Zero) 向NetTerm視窗傳送模擬按鍵就可以實現自動鍵入Linux指令的效果。由於Linux指令需要一定的處理的時間,所以每發完一條指令就要Sleep一會兒以防止鍵入指令速度過快。