詳解C# wpf如何嵌入外部程序
前言
實現(xiàn)嵌入各種窗口控件后,其實還會有一種需求:嵌入外部程序,我們有時可能需要嵌入一個瀏覽器或者或者播放器等一些已有的程序,其嵌入原理也和前面差不多,只要能獲取進(jìn)程的主窗口句柄,然后將窗口嵌入。
一、如何實現(xiàn)
1、定義屬性
定義一個依賴屬性,提供給xaml設(shè)置進(jìn)程運行的命令行
public class AppHost : HwndHost
{
/// <summary>
/// 進(jì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)啟動進(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è)對象
這個步驟是用于管理進(jìn)程,確保《子進(jìn)程跟隨主進(jìn)程關(guān)閉》。
static Job _job = new Job();
_job.AddProcess(_process.Handle);
(3)獲取主窗口句柄
下列提供的是簡單獲取主窗口句柄的方法。通過延時等待的方式獲取。需要精確時間獲取主窗口句柄則可以使用鉤子,在子進(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對象在《子進(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>
/// 需要手動dispose此控件。
/// </summary>
public class AppHost : HwndHost
{
static Job _job = new Job();
Process? _process;
/// <summary>
/// 進(jì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é)
嵌入外部程序還是相對比較容易實現(xiàn)的,而且也有一定的使用場景。創(chuàng)建進(jìn)程,并能獲取到進(jìn)程的主窗口句柄即可。另外要注意的是管理子進(jìn)程的退出,其他都問題不大。
以上就是詳解C# wpf如何嵌入外部程序的詳細(xì)內(nèi)容,更多關(guān)于C# wpf嵌入外部程序的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C# 通過同步和異步實現(xiàn)優(yōu)化做早餐的時間
本文以一個簡單的小例子—如何做一頓早餐及如何優(yōu)化做早餐的時間來讓大家具體了解一下同步和異步方法的區(qū)別,需要的朋友可以參考一下2021-12-12
C#?PictureBox控件方法參數(shù)及圖片刪除重命名上傳詳解
這篇文章主要為大家介紹了C#?PictureBox控件方法參數(shù)及圖片刪除重命名上傳示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
C# 委托的三種調(diào)用示例(同步調(diào)用 異步調(diào)用 異步回調(diào))
本文將主要通過同步調(diào)用、異步調(diào)用、異步回調(diào)三個示例來講解在用委托執(zhí)行同一個加法類的時候的的區(qū)別和利弊2013-12-12

