C#自定義控件:WinForm將其它應用程序窗體嵌入自己內部


大家可以到(https://github.com/bitzhuwei/AppContainer)找到最新的源碼下載。

這是最近在做的一個項目中提到的需求,把一個現有的窗體應用程序界面嵌入到自己開發的窗體中來,看起來就像自己開發的一樣(實際上……跟自己開發的還是有一點點區別的,就是內嵌程序和宿主程序的窗口激活狀態問題)。

在codeproject找到了一篇相關的文章(http://www.codeproject.com/Articles/9123/Hosting-EXE-Applications-in-a-WinForm-project),雖然可用,但是很不方便,於是重新設計編寫了一個類庫,用一個控件完成內嵌其它應用程序的功能。

直接上圖先:

 嵌入QQ影音 嵌入Windows Live Writer

嵌入photoshop 嵌入Adobe Reader

從打開Adobe Reader那張圖片可以看出來所謂的“內嵌程序和宿主程序的窗口激活狀態問題”。當內嵌程序窗口激活時,表面上將其包裹起來的宿主窗口卻處於非激活的狀態。想隱藏這一點的話,把窗口的FormBorderStyle屬性設為None吧,然后自己在窗口上畫關閉、最大化、最小化按鈕好了。

 

原作者的實現思路更能暴露本質,所以這里用原作者的代碼段解釋一下實現過程。

1、啟動要嵌入的應用程序進程

 1 Process p = null; 
 2 try 
 3 {
 4   // Start the process 
 5   p = System.Diagnostics.Process.Start(this.exeName); 
 6 
 7   // Wait for process to be created and enter idle condition 
 8   p.WaitForInputIdle(); 
 9 
10   // Get the main handle
11   appWin = p.MainWindowHandle; 
12 } 
13 catch (Exception ex) 
14 { 
15   MessageBox.Show(this, ex.Message, "Error"); 
16 }

2、調用Windows API將啟動的應用程序窗口嵌入自定義的控件(作者用的是Panel控件)

1 // Put it into this form
2 SetParent(appWin, this.Handle);//this在這里是Panel控件

3 4 // Remove border and whatnot 5 SetWindowLong(appWin, GWL_STYLE, WS_VISIBLE); 6 7 // Move the window to overlay it on this window 8 MoveWindow(appWin, 0, 0, this.Width, this.Height, true);

3、設置被嵌入的窗體大小隨宿主窗體改變

1 protected override void OnResize(EventArgs e)
2 {
3   if (this.appWin != IntPtr.Zero)
4   {
5     MoveWindow(appWin, 0, 0, this.Width, this.Height, true);
6   }
7   base.OnResize (e);
8 }

4、設置被嵌入的窗體應用程序在宿主程序關閉時也關閉

 1 protected override void OnHandleDestroyed(EventArgs e)
 2 {
 3   // Stop the application
 4   if (appWin != IntPtr.Zero)
 5   {
 6     // Post a colse message
 7     PostMessage(appWin, WM_CLOSE, 0, 0);
 8 
 9     // Delay for it to get the message
10     System.Threading.Thread.Sleep(1000);
11 
12     // Clear internal handle
13     appWin = IntPtr.Zero;
14   }
15   base.OnHandleDestroyed (e);
16 }

 

原作者的代碼實際用起來是很不方便的,具體大家試試就知道,不細說了(反正我只學了學上面的步驟,也不用他的庫)。

本人開發了一個比較實用的控件,使用起來也很簡單,只需三步。

首先,在窗體應用程序項目中引用類庫SmileWei.EmbeddedApp。

在窗體應用程序項目中引用類庫SmileWei.EmbeddedApp

然后,在宿主窗體上拖一個AppContainer控件,擺放好位置。(如果工具箱里沒有AppContainer,就F6生成解決方案一下,然后再看就有了。)

在宿主窗體上拖一個AppContainer控件,擺放好位置

最后,告訴AppContainer控件,要嵌入的應用程序(*.exe文件)的絕對路徑(本人以使用OpenFileDialog為例),命令AppContainer控件啟動之。

1 appContainer1.AppFilename = openEXE.FileName;
2 appContainer1.Start();

這個AppContainer控件有什么好處呢?

1、原作者想到的Resize和隨宿主程序關閉而關閉的問題,AppContainer都實現了。

2、AppContainer指定要嵌入的應用程序和啟動是分開的,這樣更靈活,開發過程中也不會看到如下的情況了:開發的時候原作者的控件就“情不自禁”地把內嵌程序加載進來了。

 開發的時候原作者的控件就“情不自禁”地把內嵌程序加載進來了

3、AppContainer防范了各種可能出錯的情形,例如禁止自己嵌入自己(死循環)、內嵌Console程序時提示不能嵌入、參數為null或無效的檢驗等。

4、其它。例如,AppContainer里面不會使用Thread.Sleep(1000);這樣低端的句子來保證程序正確地嵌入(而且對於類似photoshop這樣啟動很慢的程序也保證不了),而是通過Application.Ilde事件實現了在被嵌程序加載完畢后才將其窗體嵌入的技巧。

當然,有些應用程序是不能這么自動化地嵌入進來的。因為程序啟動窗體和主窗體句柄不一樣,AppContainer無法獲得主窗體句柄,所以無法自動嵌入。

為了解決這個問題,我在宿主窗體的狀態欄上設置了“句柄嵌入”標簽,點擊“句柄嵌入”,你可以填入想嵌入的應用程序主窗體句柄,然后宿主窗體就可以嵌入它了。

我在宿主窗體的狀態欄上設置了“句柄嵌入”標簽,點擊“句柄嵌入”,你可以填入想嵌入的應用程序主窗體句柄,然后宿主窗體就可以嵌入它了

然后有同學就問了,我怎么知道想要嵌入的窗體句柄是多少啊?方法很多啦,我這里也提供一個自己制作的小程序,大家可以在這里下載:WindowDetective(窗口偵探)0.20.rar

界面是這個樣子的:

我的WindowDetective界面是這個樣子的

里面“句柄:{1903014}”那一行就給出了本人正在用的Windows Live Writer的主窗體句柄。

用法很簡單,啟動這個程序后,它會自動檢測鼠標所在位置的窗體信息,顯示在窗口中。所以把鼠標放在你想了解的窗體菜單欄上就OK了。QQ TM版也可以這樣嵌進來滴。(QQ嵌不進來,不知道騰訊在搞什么)

QQ TM版也可以這樣嵌進來

大家還可以試試把QQ對話框嵌進來,很好玩哦~

 大家還可以試試把QQ對話框嵌進來,很好玩哦~

 

我的源代碼都給出了明確的注釋,類型、變量名也都規范易懂,在此不再多做解釋了,有疑問請留言吧O(∩_∩)O

本文所有源代碼、可執行程序均可在下面列出的鏈接中下載到。

示例宿主程序及類庫源代碼:SmileWei.EmbeddedApp.rar

窗口偵探(用於查看窗口句柄):WindowDetective(窗口偵探)0.20.rar

2015-05-04

大家可以到(https://github.com/bitzhuwei/AppContainer)找到最新的源碼下載。

我添加了各種FormBorderStyle的示例App,用於演示這個屬性對嵌入的影響。


轉載請注明出處,http://www.cnblogs.com/bitzhuwei/archive/2012/05/24/SmileWei_EmbeddedApp.htmlO(∩_∩)O謝謝


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM