详解C# wpf如何嵌入外部程序

 更新时间:2024年04月09日 11:25:10   作者:CodeOfCC  
实现嵌入各种窗口控件后,其实还会有一种需求:嵌入外部程序,我们有时可能需要嵌入一个浏览器或者或者播放器等一些已有的程序,下面我们就来看看具体操作吧

前言

实现嵌入各种窗口控件后,其实还会有一种需求:嵌入外部程序,我们有时可能需要嵌入一个浏览器或者或者播放器等一些已有的程序,其嵌入原理也和前面差不多,只要能获取进程的主窗口句柄,然后将窗口嵌入。

一、如何实现

1、定义属性

定义一个依赖属性,提供给xaml设置进程运行的命令行

public class AppHost : HwndHost
{
    /// <summary>
    /// 进程运行的命令行
    /// </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、进程嵌入

在下列方法中进行进程嵌入,具体操作如下列步骤。

protected override HandleRef BuildWindowCore(HandleRef hwndParent)

(1)启动进程

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)进程加入作业对象

这个步骤是用于管理进程,确保《子进程跟随主进程关闭》

static Job _job = new Job();
_job.AddProcess(_process.Handle);

(3)获取主窗口句柄

下列提供的是简单获取主窗口句柄的方法。通过延时等待的方式获取。需要精确时间获取主窗口句柄则可以使用钩子,在子进程窗口创建事件中获取句柄。

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、销毁进程

protected override void DestroyWindowCore(HandleRef hwnd)
{
    _process?.Kill();
    _process?.Dispose();
    _process = null;
}

二、完整代码

其中Job对象在《子进程跟随主进程关闭》中。

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>
        /// 进程运行的命令行
        /// </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>

效果预览

在这里插入图片描述

总结

嵌入外部程序还是相对比较容易实现的,而且也有一定的使用场景。创建进程,并能获取到进程的主窗口句柄即可。另外要注意的是管理子进程的退出,其他都问题不大。

以上就是详解C# wpf如何嵌入外部程序的详细内容,更多关于C# wpf嵌入外部程序的资料请关注脚本之家其它相关文章!

相关文章

  • C#窗体编程不显示最小化、最大化、关闭按钮的方法

    C#窗体编程不显示最小化、最大化、关闭按钮的方法

    这篇文章主要介绍了C#窗体编程不显示最小化、最大化、关闭按钮的方法,即windows forms编程中取消最小化、最大化、关闭按钮,需要的朋友可以参考下
    2014-08-08
  • C#之HttpClient设置cookies的两种方式

    C#之HttpClient设置cookies的两种方式

    这篇文章主要介绍了C#之HttpClient设置cookies的两种方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • Unity实现全屏截图以及QQ截图

    Unity实现全屏截图以及QQ截图

    这篇文章主要为大家详细介绍了Unity实现全屏截图以及QQ截图,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-04-04
  • 基于WPF实现裁剪图像功能

    基于WPF实现裁剪图像功能

    这篇文章主要为大家详细介绍了如何基于WPF实现裁剪图像功能,文中的示例代码讲解详细,对我们学习或工作有一定帮助,感兴趣的小伙伴可以了解一下
    2023-06-06
  • C#实现TCP和UDP通信的示例详解

    C#实现TCP和UDP通信的示例详解

    这篇文章主要为大家详细介绍了C#实现TCP和UDP通信的相关知识,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的小伙伴可以了解一下
    2023-03-03
  • Unity Shader实现素描风格的渲染

    Unity Shader实现素描风格的渲染

    这篇文章主要为大家详细介绍了Unity Shader实现素描风格的渲染,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-04-04
  • C# 日历类功能的实例

    C# 日历类功能的实例

    本文通过实例代码给大家介绍了C#日历类的相关知识,非常不错,具有参考借鉴价值,需要的朋友参考下吧
    2017-06-06
  • visio二次开发--判断文档是否已发生变化(变化就加星号*)

    visio二次开发--判断文档是否已发生变化(变化就加星号*)

    最近做一个故障树诊断的项目,用visio二次开发,可以同时打开多个绘制的故障树图形文档。项目中需要实现判断文档是否发生变化,这是很多编辑软件的基本功能,变化了就加个星号*
    2013-04-04
  • 编辑IL文件 修改DLL文件的方法

    编辑IL文件 修改DLL文件的方法

    下面小编就为大家带来一篇编辑IL文件 修改DLL文件的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-02-02
  • C#启动和停止windows服务的实例代码

    C#启动和停止windows服务的实例代码

    这篇文章介绍了C#启动和停止windows服务的实例代码,有需要的朋友可以参考一下
    2013-09-09

最新评论