WPF内嵌其他进程的窗口

WPF内嵌其他进程窗口的常见方法有 HwndHost + SetParentWindowsFormsHost + WinForms Panel + SetParent

推荐使用自定义HwndHost

两者的对比区别

示例代码

复制代码
public class MyWndHost : HwndHost
{
    const int WS_CHILD = 0x40000000;
    const int WS_VISIBLE = 0x10000000;
    const int WS_CLIPCHILDREN = 0x02000000;
    const int WS_CLIPSIBLINGS = 0x04000000;

    [DllImport("user32.dll")]
    internal static extern IntPtr CreateWindowEx(int exStyle, string className, string windowName, int style, int x, int y, int width, int height, IntPtr hwndParent, IntPtr hMenu, IntPtr hInstance, IntPtr pvParam);

    [DllImport("user32.dll")]
    internal static extern bool DestroyWindow(IntPtr hwnd);

    public static readonly DependencyProperty HandleProperty = DependencyProperty.Register("Handle", typeof(IntPtr), typeof(MyWndHost), new PropertyMetadata(IntPtr.Zero));

    public new IntPtr Handle
    {
        get => (IntPtr)GetValue(HandleProperty);
        set => SetValue(HandleProperty, value);
    }

    protected override HandleRef BuildWindowCore(HandleRef hwndParent)
    {
        Handle = CreateWindowEx(
            0, "static", "",
            WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
            0, 0,
            (int)Width, (int)Height,
            hwndParent.Handle,
            IntPtr.Zero,
            IntPtr.Zero,
            IntPtr.Zero);
        return new HandleRef(this, Handle);
    }

    protected override void DestroyWindowCore(HandleRef hwnd)
    {
        DestroyWindow(hwnd.Handle);
    }
}

<Grid>
    <!--WindowsFormsHost-->
    <Grid Margin="12,0,0,0" x:Name="web2Layout" Background="Black">
        <wfi:WindowsFormsHost Name="windowsFormsHost" >
            <wf:Panel x:Name="hostPanel"/>
        </wfi:WindowsFormsHost>
    </Grid>
    <!--自定义HwndHost-->
    <ui:MyWndHost x:Name="MyWnd"/>
</Grid>

 public partial class WebView : System.Windows.Controls.UserControl
 {
     private IntPtr m_hWndChild = IntPtr.Zero;
     private Process _electronProcess;

     [DllImport("user32.dll", SetLastError = true)]
     private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

     [DllImport("user32.dll")]
     private static extern int SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewLong);

     [DllImport("user32.dll")]
     private static extern uint GetWindowLong(IntPtr hWnd, int nIndex);

     [DllImport("user32.dll")]
     private static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);

     private const int GWL_STYLE = -16;
     private const uint WS_CHILD = 0x40000000;
     private const uint WS_BORDER = 0x00800000;

     public WebView()
     {
         InitializeComponent();
         Loaded += Web2Container_Loaded;
         SizeChanged += Web2Container_SizeChanged;
     }

     private void Web2Container_Loaded(object sender, RoutedEventArgs e)
     {
         StartElectronApp();
     }

     private void StartElectronApp()
     {
         var path = AppDomain.CurrentDomain.BaseDirectory + @"Exteral\MyElectronApp.exe";
         _electronProcess = new Process
         {
             StartInfo =
             {
                 FileName = path,
                 UseShellExecute = false
             }
         };
         _electronProcess.Start();

         Task.Run(() =>
         {
             while (_electronProcess.MainWindowHandle == IntPtr.Zero && !_electronProcess.HasExited)
             {
                 Thread.Sleep(100);
                 _electronProcess.Refresh();
             }
             if (!_electronProcess.HasExited) ToSetWndParent(_electronProcess.MainWindowHandle);
         });
     }

     private async void ToSetWndParent(IntPtr wnd)
     {
         await Dispatcher.InvokeAsync(() =>
         {
             if (this.FindName("MyWnd") is MyWndHost wfh && wfh.Handle != IntPtr.Zero)
             {
                 m_hWndChild = wnd;
                 SetParent(m_hWndChild, wfh.Handle);

                 uint style = GetWindowLong(m_hWndChild, GWL_STYLE);
                 style = (style & ~WS_BORDER) | WS_CHILD;
                 SetWindowLong(m_hWndChild, GWL_STYLE, style);

                 AdjustChildWindowSize();
             }
         });
     }

     private void Web2Container_SizeChanged(object sender, SizeChangedEventArgs e)
     {
         AdjustChildWindowSize();
     }

     private void AdjustChildWindowSize()
     {
         if (m_hWndChild != IntPtr.Zero)
         {
             var dpiScale = GetDpiFromVisual(this);
             MoveWindow(
                 m_hWndChild,
                 0,
                 0,
                 (int)(ActualWidth * dpiScale),
                 (int)(ActualHeight * dpiScale),
                 true
             );
         }
     }

     private double GetDpiFromVisual(System.Windows.Media.Visual visual)
     {
         var source = PresentationSource.FromVisual(visual);
         double dpiX = 96.0;
         if (source?.CompositionTarget != null)
         {
             dpiX = 96.0 * source.CompositionTarget.TransformToDevice.M11;
         }
         return dpiX / 96.0;
     }
 }

效果示例

相关推荐
almighty279 小时前
C# 阿里云 OSS 图片上传步骤及浏览器查看方法
c#·图片上传·c# 阿里云 oss·阿里云 oss
Python大数据分析@1 天前
Python 语言有什么奇技淫巧吗?
开发语言·python·c#
Envyᥫᩣ1 天前
C#中的设计模式:构建更加优雅的代码
开发语言·c#
没有bug.的程序员1 天前
Redis 数据结构全面解析:从底层编码到实战应用
java·数据结构·redis·wpf
CHHC18801 天前
C#工作流示例(WorkflowCore)
c#·工作流
somethingGoWay1 天前
wpf 自定义输入ip地址的文本框
wpf
oscar9991 天前
少儿编程C++快速教程之——2. 字符串处理
开发语言·c++·c#·字符串·少儿
时光追逐者1 天前
.NET 使用 CsvHelper 快速读取和写入 CSV 文件
c#·.net·.net core·csv
INSO1 天前
查漏补缺之Polly
c#
秋月的私语1 天前
Wpf程序屏幕居中问题修复全记录
wpf