WPF内嵌其他进程的窗口

发布于:2025-05-10 ⋅ 阅读:(14) ⋅ 点赞:(0)

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;
     }
 }

效果示例
在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到