以下將介紹如何將其他程式 (.exe) 內嵌到 WPF 程式內
1. 在 WPF 專案內新增一個名為 [HostControl] 的使用者控制項
2. 在 HostControl.xaml.cs 內使用 DllImport 引用必要的函數
using System;
using System.Runtime.InteropServices;
using System.Windows.Controls;
namespace HostDemo
{
public partial class HostControl : UserControl, IDisposable
{
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct HWND__
{
/// int
public int unused;
}
[DllImport("user32.dll", EntryPoint = "GetWindowThreadProcessId", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern long GetWindowThreadProcessId(long hWnd, long lpdwProcessId);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
private static extern long SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", EntryPoint = "GetWindowLongA", SetLastError = true)]
private static extern long GetWindowLong(IntPtr hwnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "SetWindowLongA", SetLastError = true)]
public static extern int SetWindowLongA([System.Runtime.InteropServices.InAttribute()] System.IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll", SetLastError = true)]
private static extern long SetWindowPos(IntPtr hwnd, long hWndInsertAfter, long x, long y, long cx, long cy, long wFlags);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);
internal delegate int WindowEnumProc(IntPtr hwnd, IntPtr lparam);
[DllImport("user32.dll")]
internal static extern bool EnumChildWindows(IntPtr hwnd, WindowEnumProc func, IntPtr lParam);
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
[DllImport("Shcore.dll")]
private static extern int SetProcessDpiAwareness(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
private const int SWP_NOOWNERZORDER = 0x200;
private const int SWP_NOREDRAW = 0x8;
private const int SWP_NOZORDER = 0x4;
private const int SWP_SHOWWINDOW = 0x0040;
private const int WS_EX_MDICHILD = 0x40;
private const int SWP_FRAMECHANGED = 0x20;
private const int SWP_NOACTIVATE = 0x10;
private const int SWP_ASYNCWINDOWPOS = 0x4000;
private const int SWP_NOMOVE = 0x2;
private const int SWP_NOSIZE = 0x1;
private const int GWL_STYLE = (-16);
private const int WS_VISIBLE = 0x10000000;
private const int WS_CHILD = 0x40000000;
private const int WM_ACTIVATE = 0x0006;
private readonly IntPtr WA_ACTIVE = new IntPtr(1);
private readonly IntPtr WA_INACTIVE = new IntPtr(0);
}
}
3. 在 HostControl.xaml.cs 內加入 [呼叫其他程式] 與 [內嵌其他程式] 的邏輯
namespace HostDemo
{
public partial class HostControl : UserControl, IDisposable
{
public event EventHandler HostProcessStarted;
private void OnHostProcessStarted()
{
HostProcessStarted?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Track if the application has been created
/// </summary>
public bool IsCreated { get; private set; } = false;
public string ExeName { get; set; }
/// <summary>
/// Track if the control is disposed
/// </summary>
private bool isDisposed = false;
/// <summary>
/// Handle to the application Window
/// </summary>
private IntPtr appWin;
private Process childp;
public HostControl()
{
InitializeComponent();
this.Loaded += HostControl_Loaded;
this.Unloaded += HostControl_Unloaded;
this.SizeChanged += HostControl_SizeChanged;
}
~HostControl()
{
this.Dispose();
}
private void HostControl_Loaded(object sender, RoutedEventArgs e)
{
if (IsCreated)
{
return;
}
if (string.IsNullOrEmpty(ExeName))
{
return;
}
Application.Current.Exit -= Current_Exit;
Application.Current.Exit += Current_Exit;
appWin = IntPtr.Zero;
try
{
var procInfo = new ProcessStartInfo(this.ExeName);
procInfo.WorkingDirectory = System.IO.Path.GetDirectoryName(this.ExeName);
childp = Process.Start(procInfo);
IsCreated = true;
childp.WaitForInputIdle();
this.appWin = childp.MainWindowHandle;
var helper = new WindowInteropHelper(Window.GetWindow(this));
SetParent(appWin, helper.Handle);
SetWindowLongA(appWin, GWL_STYLE, WS_VISIBLE);
if (childp != null && childp.HasExited == false)
{
OnHostProcessStarted();
}
UpdateSize();
}
catch (Exception ex)
{
Debug.Print(ex.Message + "Error");
// 出錯了,把自己隱藏起來
this.Visibility = Visibility.Collapsed;
}
}
private void HostControl_Unloaded(object sender, RoutedEventArgs e)
{
Application.Current.Exit -= Current_Exit;
this.Dispose();
}
private void Current_Exit(object sender, ExitEventArgs e)
{
this.Dispose();
}
private void HostControl_SizeChanged(object sender, SizeChangedEventArgs e)
{
UpdateSize();
}
private void UpdateSize()
{
if (this.appWin != IntPtr.Zero)
{
PresentationSource source = PresentationSource.FromVisual(this);
var scaleX = 1D;
var scaleY = 1D;
if (source != null)
{
scaleX = source.CompositionTarget.TransformToDevice.M11;
scaleY = source.CompositionTarget.TransformToDevice.M22;
}
var width = (int)(this.ActualWidth * scaleX);
var height = (int)(this.ActualHeight * scaleY);
MoveWindow(appWin, 0, 0, width, height, true);
}
}
protected void Dispose(bool disposing)
{
if (!isDisposed)
{
if (disposing)
{
if (IsCreated && childp != null && !childp.HasExited)
{
childp.Kill();
}
if (appWin != IntPtr.Zero)
{
appWin = IntPtr.Zero;
}
}
isDisposed = true;
}
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
}
}
至此便完成了 HostControl 控制項的建置,以下介紹如何使用此控制項
在任一頁面中加入 HostControl,並將 HostControl.ExeName 填入指定程式的檔名,即可將該程式內嵌進來
<Page x:Class="HostDemo.Page1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:HostDemo"
mc:Ignorable="d"
Title="Page1">
<Grid>
<local:HostControl ExeName="notepad.exe" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</Grid>
</Page>
完整原始碼
https://github.com/renewal-wu/EmbeddedExeInWPF
參考資料
https://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=673701