詳解C# wpf如何嵌入外部程序
前言
實(shí)現(xiàn)嵌入各種窗口控件后,其實(shí)還會(huì)有一種需求:嵌入外部程序,我們有時(shí)可能需要嵌入一個(gè)瀏覽器或者或者播放器等一些已有的程序,其嵌入原理也和前面差不多,只要能獲取進(jìn)程的主窗口句柄,然后將窗口嵌入。
一、如何實(shí)現(xiàn)
1、定義屬性
定義一個(gè)依賴屬性,提供給xaml設(shè)置進(jìn)程運(yùn)行的命令行
public class AppHost : HwndHost { /// <summary> /// 進(jìn)程運(yùn)行的命令行 /// </summary> public string Cmdline { get { return (string)GetValue(CmdlineProperty); } set { SetValue(CmdlineProperty, value); } } // Using a DependencyProperty as the backing store for Cmdline. This enables animation, styling, binding, etc... public static readonly DependencyProperty CmdlineProperty = DependencyProperty.Register("Cmdline", typeof(string), typeof(AppHost), new PropertyMetadata("")); }
2、進(jìn)程嵌入
在下列方法中進(jìn)行進(jìn)程嵌入,具體操作如下列步驟。
protected override HandleRef BuildWindowCore(HandleRef hwndParent)
(1)啟動(dòng)進(jìn)程
var cmds = Cmdline.Split(" ", 2); Process? _process; _process.StartInfo.FileName = cmds.First(); _process.StartInfo.Arguments = cmds.Last(); _process.StartInfo.UseShellExecute = false; _process.StartInfo.CreateNoWindow = true; _process.StartInfo.WindowStyle = ProcessWindowStyle.Minimized; _process.Start();
(2)進(jìn)程加入作業(yè)對(duì)象
這個(gè)步驟是用于管理進(jìn)程,確保《子進(jìn)程跟隨主進(jìn)程關(guān)閉》。
static Job _job = new Job();
_job.AddProcess(_process.Handle);
(3)獲取主窗口句柄
下列提供的是簡單獲取主窗口句柄的方法。通過延時(shí)等待的方式獲取。需要精確時(shí)間獲取主窗口句柄則可以使用鉤子,在子進(jìn)程窗口創(chuàng)建事件中獲取句柄。
for (int i = 0; i < 200 && _process.MainWindowHandle == 0; i++) Thread.Sleep(5); if (_process.MainWindowHandle == 0) { throw new Exception("process no window"); } return new HandleRef(this, Handle);
3、銷毀進(jìn)程
protected override void DestroyWindowCore(HandleRef hwnd) { _process?.Kill(); _process?.Dispose(); _process = null; }
二、完整代碼
其中Job對(duì)象在《子進(jìn)程跟隨主進(jìn)程關(guān)閉》中。
AppHost.cs
using JobManagement; using System.ComponentModel; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Interop; using Process = System.Diagnostics.Process; using TextBox = System.Windows.Controls.TextBox; using Thread = System.Threading.Thread; namespace WpfHwndElement { /// <summary> /// 需要手動(dòng)dispose此控件。 /// </summary> public class AppHost : HwndHost { static Job _job = new Job(); Process? _process; /// <summary> /// 進(jìn)程運(yùn)行的命令行 /// </summary> public string Cmdline { get { return (string)GetValue(CmdlineProperty); } set { SetValue(CmdlineProperty, value); } } // Using a DependencyProperty as the backing store for Cmdline. This enables animation, styling, binding, etc... public static readonly DependencyProperty CmdlineProperty = DependencyProperty.Register("Cmdline", typeof(string), typeof(AppHost), new PropertyMetadata("")); new public IntPtr Handle { get { return (IntPtr)GetValue(HandleProperty); } private set { SetValue(HandleProperty, value); } } // Using a DependencyProperty as the backing store for Hwnd. This enables animation, styling, binding, etc... public static readonly DependencyProperty HandleProperty = DependencyProperty.Register("Handle", typeof(IntPtr), typeof(NativeHost), new PropertyMetadata(IntPtr.Zero)); protected override HandleRef BuildWindowCore(HandleRef hwndParent) { try { if (DesignerProperties.GetIsInDesignMode(this)) throw new Exception("design mode won't show app"); var cmds = Cmdline.Split(" ", 2); _process = new Process(); _process.StartInfo.FileName = cmds.First(); _process.StartInfo.Arguments = cmds.Length > 1 ? cmds.Last() : ""; _process.StartInfo.UseShellExecute = false; _process.StartInfo.CreateNoWindow = true; _process.StartInfo.WindowStyle = ProcessWindowStyle.Minimized; _process.Start(); _job.AddProcess(_process.Handle); for (int i = 0; i < 200 && _process.MainWindowHandle == 0; i++) Thread.Sleep(5); if (_process.MainWindowHandle == 0) { throw new Exception("process no window"); } Handle = _process.MainWindowHandle; var wndStyle = GetWindowLong(Handle, GWL_STYLE); wndStyle &= ~WS_THICKFRAME; wndStyle &= ~WS_CAPTION; SetWindowLong(Handle, GWL_STYLE, wndStyle | WS_CHILD); SetParent(Handle, hwndParent.Handle); } catch (Exception ex) { var window = new Window() { Width = 0, Height = 0, ResizeMode = ResizeMode.NoResize, WindowStyle = WindowStyle.None, Content = new TextBox() { IsReadOnly = true, Text = ex.Message + " " + ex.StackTrace, TextWrapping = TextWrapping.Wrap } }; var hwnd = new WindowInteropHelper(window).EnsureHandle(); window.Show(); SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) | WS_CHILD); SetParent(hwnd, hwndParent.Handle); Handle = hwnd; } return new HandleRef(this, Handle); } protected override void DestroyWindowCore(HandleRef hwnd) { var window = HwndSource.FromHwnd(hwnd.Handle)?.RootVisual as Window; window?.Close(); _process?.Kill(); _process?.Dispose(); _process = null; } const int WS_CAPTION = 0x00C00000; const int WS_THICKFRAME = 0x00040000; const int WS_CHILD = 0x40000000; const int GWL_STYLE = (-16); [DllImport("user32.dll", EntryPoint = "GetWindowLongW")] static extern int GetWindowLong(IntPtr hwnd, int nIndex); [DllImport("user32.dll", EntryPoint = "SetWindowLongW")] static extern int SetWindowLong(IntPtr hwnd, int nIndex, int dwNewLong); [DllImport("user32.dll")] public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent); } }
三、使用示例
嵌入ffplay.exe
MainWindow.xaml
<Window x:Class="WpfHwndElement.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfHwndElement" mc:Ignorable="d" Title="MainWindow" Height="360" Width="640" > <Grid> <local:AppHost Cmdline="ffplay" Width="200" Height="200"></local:AppHost> </Grid> </Window>
效果預(yù)覽
總結(jié)
嵌入外部程序還是相對(duì)比較容易實(shí)現(xiàn)的,而且也有一定的使用場(chǎng)景。創(chuàng)建進(jìn)程,并能獲取到進(jìn)程的主窗口句柄即可。另外要注意的是管理子進(jìn)程的退出,其他都問題不大。
以上就是詳解C# wpf如何嵌入外部程序的詳細(xì)內(nèi)容,更多關(guān)于C# wpf嵌入外部程序的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#窗體編程不顯示最小化、最大化、關(guān)閉按鈕的方法
這篇文章主要介紹了C#窗體編程不顯示最小化、最大化、關(guān)閉按鈕的方法,即windows forms編程中取消最小化、最大化、關(guān)閉按鈕,需要的朋友可以參考下2014-08-08C#之HttpClient設(shè)置cookies的兩種方式
這篇文章主要介紹了C#之HttpClient設(shè)置cookies的兩種方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11Unity Shader實(shí)現(xiàn)素描風(fēng)格的渲染
這篇文章主要為大家詳細(xì)介紹了Unity Shader實(shí)現(xiàn)素描風(fēng)格的渲染,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-04-04visio二次開發(fā)--判斷文檔是否已發(fā)生變化(變化就加星號(hào)*)
最近做一個(gè)故障樹診斷的項(xiàng)目,用visio二次開發(fā),可以同時(shí)打開多個(gè)繪制的故障樹圖形文檔。項(xiàng)目中需要實(shí)現(xiàn)判斷文檔是否發(fā)生變化,這是很多編輯軟件的基本功能,變化了就加個(gè)星號(hào)*2013-04-04C#啟動(dòng)和停止windows服務(wù)的實(shí)例代碼
這篇文章介紹了C#啟動(dòng)和停止windows服務(wù)的實(shí)例代碼,有需要的朋友可以參考一下2013-09-09