如鵬網(wǎng) 大學(xué)生計(jì)算機(jī)學(xué)習(xí)社區(qū)

          CowNew開(kāi)源團(tuán)隊(duì)

          http://www.cownew.com 郵件請(qǐng)聯(lián)系 about521 at 163.com

            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            363 隨筆 :: 2 文章 :: 808 評(píng)論 :: 0 Trackbacks

          公司現(xiàn)在在研發(fā)基于.Net中WPF技術(shù)的產(chǎn)品,由于要兼容舊有產(chǎn)品,比如一些舊有的Win32程序、第三方的Win32程序等等,還要實(shí)現(xiàn)自動(dòng)登錄這些外部Win32程序,因此必須能夠?qū)⑦@些程序整合到我們的系統(tǒng)中來(lái),讓使用者看起來(lái)它們好像是一個(gè)程序。

          在MSDN中有專(zhuān)門(mén)的章節(jié)提到了在WPF中嵌入Win32控件的辦法,那就是使用 HwndHost ,只要把 Win32控件的句柄傳遞給 HwndHost 就可以了。MSDN中的例子演示的都是在同一個(gè)進(jìn)程內(nèi)創(chuàng)建的 Win32控件,我一開(kāi)始認(rèn)為只要通過(guò)FindWindow等Win32API得到外部Win32程序的窗口句柄,然后將窗口句柄交給 HwndHost 就可以了。實(shí)現(xiàn)核心代碼如下:

                   protected   override   HandleRef  BuildWindowCore( HandleRef  hwndParent)

                  {

                      appProc =  new   Process ();

                      appProc.StartInfo.WindowStyle =  ProcessWindowStyle .Hidden;

                      appProc.StartInfo.FileName =  @"D:\greeninst\netterm\netterm.exe" ;

                      appProc.Start();

                       //等待初始化完成,實(shí)現(xiàn)有點(diǎn)土

                       Thread .Sleep(1000);

                      hwndHost =  Win32Native .FindWindow( "NetTermClass" null );

                       // 嵌入在HwnHost中的窗口必須要 設(shè)置為WS_CHILD風(fēng)格

                       uint  oldStyle =  Win32Native .GetWindowLong(hwndHost,  Win32Native .GWL_STYLE);

                         Win32Native .SetWindowLong(hwndHost,  Win32Native .GWL_STYLE, (oldStyle |  Win32Native .WS_CHILD));

                       //將netterm的父窗口設(shè)置為HwndHost

                       Win32Native .SetParent(hwndHost, hwndParent.Handle);

                       return   new   HandleRef ( this , hwndHost);

                  }

          這里啟動(dòng)的是NetTerm這個(gè)外部程序。實(shí)踐證明我這種想法是可行的,但是唯一的問(wèn)題就是雖然 外部Win32程序顯示到WPF程序中來(lái)了,但是很奇怪的是嵌入的Win32程序再也無(wú)法點(diǎn)擊了,點(diǎn)擊按鈕、輸入按鍵都不起作用,程序好像死了一樣。經(jīng)過(guò)分析,我認(rèn)為由于通過(guò) SetParent 這個(gè) Win32API 將NetTerm的父窗口設(shè)置為了 HwndHost ,這樣 NetTerm就不再有自己獨(dú)立的窗口消息循環(huán),而是眼巴巴等著 HwndHost 這個(gè)爹給他發(fā) 消息。可能由于WPF對(duì)于消息循環(huán)的處理 不同于以前的Win32程序,導(dǎo)致所有的鼠標(biāo)點(diǎn)擊、按鍵 消息都不能被傳遞給NetTerm這個(gè)兒子,這樣NetTerm就得不到任何消息,所以就像死了一樣。

          解決這個(gè)問(wèn)題的思路是截獲WPF的窗口消息,然后把它通過(guò) SendMessage 這個(gè)Win32API 轉(zhuǎn)發(fā)給NetTerm。但是找了半天也沒(méi)找到WPF的消息處理的地方,請(qǐng)教同事以后得知WPF根本不像傳統(tǒng)的Win32程序那樣有窗口消息循環(huán),而是自己搞了一套。郁悶了一會(huì)兒,突然靈光一現(xiàn):管它什么WPF不WPF,它本質(zhì)上還是Win32程序,只不過(guò)是一個(gè)內(nèi)部使用了DirectX技術(shù)的Win32程序而已,只要是Win32程序一定有辦法拿到它的窗口消息循環(huán)。啥辦法呢?對(duì)!就是窗口鉤子。使用 SetWindowsHookEx 這個(gè)Win32API可以截獲一個(gè)窗口所有的 消息循環(huán),這樣只要挑出來(lái)發(fā)給 HwndHost 的消息,然后把它轉(zhuǎn)發(fā)給 NetTerm窗口就ok了。經(jīng)過(guò)改造以后NetTerm終于活過(guò)來(lái)了!!!

          解決了最核心的問(wèn)題就該處理普通問(wèn)題了,主要問(wèn)題及對(duì)策如下:

          1、隱藏NetTerm的窗口邊框,這樣看起來(lái)就感覺(jué)不出來(lái)NetTerm是一個(gè)外部程序了。思路很簡(jiǎn)單使用 GetWindowLong 得到窗口原來(lái)的風(fēng)格,然后再附加一個(gè) WS_BORDER 風(fēng)格就ok了。

          //設(shè)置為WS_CHILD風(fēng)格

                       uint  oldStyle =  Win32Native .GetWindowLong(hwndHost,  Win32Native .GWL_STYLE);

                       //&~WS_BORDER去掉邊框,這樣看起來(lái)更像一個(gè)內(nèi)嵌的程序,注意()的作用,改變默認(rèn)的優(yōu)先級(jí)

                       Win32Native .SetWindowLong(hwndHost,  Win32Native .GWL_STYLE, (oldStyle |  Win32Native .WS_CHILD)&~ Win32Native .WS_BORDER);

          2、隱藏NetTerm在任務(wù)欄上的按鈕

          只要找到任務(wù)欄的句柄,然后首先向它發(fā)送TB_BUTTONCOUNT得到它上邊按鈕的個(gè)數(shù),由于NetTerm是剛剛啟動(dòng)的,可以認(rèn)為最后一個(gè)按鈕就是NetTerm的按鈕,只要向任務(wù)欄的句柄發(fā)送TB_DELETEBUTTON消息將最后一個(gè)按鈕刪掉就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);

                      //得到任務(wù)欄中按鈕的數(shù)目

                      int vCount = Win32Native.SendMessage(new HandleRef(this, vHandle), 

          (uint)Win32Native.TB_BUTTONCOUNT, IntPtr.Zero, IntPtr.Zero).ToInt32();

                      

                      //認(rèn)為最后一個(gè)按鈕就是被嵌套程序的按鈕,刪除它

                      Win32Native.SendMessage(new HandleRef(this, vHandle), 

          Win32Native.TB_DELETEBUTTON, new IntPtr(vCount - 1), IntPtr.Zero);

                  }

          這是在WinXP下的處理。好像Win2000、Vista的任務(wù)欄的結(jié)構(gòu)是不同的,如果需要運(yùn)行在這些OS下需要做進(jìn)一步的改進(jìn)。

          3、 自動(dòng)登錄。在NetTerm啟動(dòng)以后自動(dòng)登錄到服務(wù)器,并且自動(dòng)輸入用戶(hù)名、密碼,并且啟動(dòng)指定的程序。NetTerm支持在啟動(dòng)參數(shù)中指定要連接的服務(wù)器地址,這樣可以解決自動(dòng)登錄到服務(wù)器的問(wèn)題;使用 SendMessage( handle , Win32Native.WM_CHAR,  ch , IntPtr.Zero) 向NetTerm窗口發(fā)送模擬按鍵就可以實(shí)現(xiàn)自動(dòng)鍵入Linux指令的效果。由于Linux指令需要一定的處理的時(shí)間,所以每發(fā)完一條指令就要Sleep一會(huì)兒以防止鍵入指令速度過(guò)快。

          運(yùn)行效果如下:
          主要代碼如下,
          Win32Native .cs是我們寫(xiě)的一個(gè)對(duì)Win32API的調(diào)用聲明,都是簡(jiǎn)單的PInvoke聲明,由于尺寸比較大這里就不貼出來(lái)了,大家可以查MSDN自己來(lái)聲明。

          =======================================NetTermHost.cs============================

          namespace Client.Pages

          {

              class NetTermHost : HwndHost

              {

                  public IntPtr hwndHost;

                  private IntPtr hookId = new IntPtr(3);

                  private HookProc hookProc;

                  private Process appProc;

                  protected override HandleRef BuildWindowCore(HandleRef hwndParent)

                  {

                      appProc = new Process();

                      appProc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;

                      appProc.StartInfo.FileName = @"D:\greeninst\netterm\netterm.exe";

                      //設(shè)置要連接的主機(jī)名,這樣啟動(dòng)以后就立即連接了

                      appProc.StartInfo.Arguments = "192.168.88.128";

                      appProc.Start();

                      //等待初始化完成,實(shí)現(xiàn)有點(diǎn)土

                      Thread.Sleep(1000);

                      hwndHost = Win32Native.FindWindow("NetTermClass", null);

                      //設(shè)置為WS_CHILD風(fēng)格

                      uint oldStyle = Win32Native.GetWindowLong(hwndHost, Win32Native.GWL_STYLE);

                      //&~WS_BORDER去掉邊框,這樣看起來(lái)更像一個(gè)內(nèi)嵌的程序,注意()的作用,改變默認(rèn)的優(yōu)先級(jí)

                      Win32Native.SetWindowLong(hwndHost, Win32Native.GWL_STYLE, (oldStyle | Win32Native.WS_CHILD)&~Win32Native.WS_BORDER);

                      //將netterm的父窗口設(shè)置為HwndHost,爹地我來(lái)了

                      Win32Native.SetParent(hwndHost, hwndParent.Handle);

               

                //窗口最大化

                      Win32Native.ShowWindow(hwndHost.ToInt32(), Win32Native.SW_MAXIMIZE);

                //隱藏netterm在任務(wù)欄上的按鈕

                      HideTaskBarButton();

                //隱藏netterm的工具欄

                      HideNetTermToolBar();

                      //由于登錄過(guò)程非常長(zhǎng),所以不要在這里等太久,否則界面像死了一樣,所以啟動(dòng)線程來(lái)操作

                      ThreadStart ts = new ThreadStart(

                                      delegate()

                                      {

                          //自動(dòng)登錄telnet

                                          AutoLogin();

                                      }

                                  );

                      Thread thread = new Thread(ts);

                      thread.Start();       

                      hookProc = new HookProc(MyHookHandler);

                      //設(shè)置鉤子,截獲主窗口界面消息循環(huán)

                      //對(duì)當(dāng)前的窗口,使用IntPtr.Zero

                      HookApi.SetWindowsHookEx(hookId.ToInt32(), hookProc, IntPtr.Zero, 

                          HookApi.GetCurrentThreadId());

                      return new HandleRef(this, hwndHost);

                  }

                  private int MyHookHandler(int code, IntPtr wparam, ref MSG msg)

                  {

                      //如果是當(dāng)前Host的消息,則將其轉(zhuǎn)發(fā)給netterm程序

                      if (msg.hwnd == this.Handle)

                      {

                          HandleRef handleRef = new HandleRef(this, hwndHost);

                          Win32Native.SendMessage(handleRef, (uint)msg.message, msg.wParam, msg.lParam);

                      }

                      int nextHook = HookApi.CallNextHookEx(hookId, code, wparam, ref msg);

                      return nextHook;

                  }

                  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);

                      //得到任務(wù)欄中按鈕的數(shù)目

                      int vCount = Win32Native.SendMessage(new HandleRef(this, vHandle), (uint)Win32Native.TB_BUTTONCOUNT, IntPtr.Zero, IntPtr.Zero).ToInt32();

                      

                      //認(rèn)為最后一個(gè)按鈕就是被嵌套程序的按鈕,刪除它

                      Win32Native.SendMessage(new HandleRef(this, vHandle), Win32Native.TB_DELETEBUTTON, new IntPtr(vCount - 1), IntPtr.Zero);

                  }

                  private void HideNetTermToolBar()

                  {

                      IntPtr toolBarWin = Win32Native.FindWindowEx(hwndHost, IntPtr.Zero, "ToolbarWindow32", IntPtr.Zero);

                      Win32Native.ShowWindow(toolBarWin.ToInt32(), 0);

                  }

                  private void AutoLogin()

                  {      

                      Thread.Sleep(10000);

                //輸用戶(hù)名

                      SendString("yzk\n");

                      Thread.Sleep(1000);

                //輸密碼

                      SendString("123456\n");

                      Thread.Sleep(1000);

                //進(jìn)入目錄

                      SendString("cd /mnt/hgfs/NAHA/src/\n");

                      Thread.Sleep(1000);

                //運(yùn)行字符終端

                      SendString("python FrontEnd.py\n");

                  }

              //模擬按鍵

                  private void SendString(String s)

                  {

                      foreach(char c in s)

                      {

                          Win32Native.SendMessage(new HandleRef(this, hwndHost), Win32Native.WM_CHAR, new IntPtr(c), IntPtr.Zero);

                      }            

                  }

                  protected override void DestroyWindowCore(HandleRef hwnd)

                  {

                      HandleRef handleRef = new HandleRef(this, hwndHost);

                      //關(guān)閉netterm窗口

                      //Win32Native.SendMessage(handleRef, Win32Native.WM_CLOSE, IntPtr.Zero, IntPtr.Zero);

                      //很黃很暴力,直接殺死

                      appProc.Kill();

                      //有bug,如果netterm已經(jīng)連上遠(yuǎn)程主機(jī),那么如果不退出就close的話會(huì)彈出對(duì)話框,這就會(huì)造成主程序無(wú)法退出

                      //幾種策略:殺死netterm、發(fā)送模擬鍵點(diǎn)擊“是”按鈕、把netterm釋放出來(lái)讓用戶(hù)決定、只是demo而已不管它

                      //沒(méi)有主菜單的bug

                      //由于是攔截消息循環(huán)搞的,所以有可能有潛在的bug

                      Win32Native.DestroyWindow(hwnd.Handle); 

                      HookApi.UnhookWindowsHookEx(hookId); 

                  }

              }

          }

          =======================TradeNetTermHost.cs===========================================

          public partial class TradeNetTermHost : UserControl

          {

            private NetTermHost ch;

            public TradeNetTermHost()

            {

              Win32Native.InitCommonControls();

              InitializeComponent();

              ch = new NetTermHost();

              this.Win32HosterBorder.Child = ch;

              Loaded += new RoutedEventHandler(TradeNetTermHost_Loaded);

            }

            void TradeNetTermHost_Loaded(object sender, RoutedEventArgs e)

            {

              //設(shè)置netterm容器為焦點(diǎn),否則消息不會(huì)發(fā)給它

              Win32Native.SetFocus(ch.Handle);

            }

          }

          posted on 2008-04-24 12:48 CowNew開(kāi)源團(tuán)隊(duì) 閱讀(6172) 評(píng)論(0)  編輯  收藏

          只有注冊(cè)用戶(hù)登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 阿坝县| 开封市| 视频| 平湖市| 利辛县| 永年县| 琼结县| 印江| 清丰县| 沧州市| 延吉市| 剑川县| 黔西| 东丽区| 建德市| 翁牛特旗| 察哈| 日喀则市| 盈江县| 桂东县| 鄄城县| 仙居县| 沽源县| 昌江| 蓬溪县| 桂东县| 哈尔滨市| 桃园县| 唐河县| 图片| 邹城市| 汾阳市| 桂林市| 辽宁省| 彝良县| 大兴区| 太和县| 台北县| 孝义市| 瑞安市| 泰兴市|