C#讲外部窗口嵌入到WPF控件当中
本Markdown编辑器使用[StackEdit][6]修改而来,用它写博客,将会带来全新的体验哦:
- 可能会用到的API函数
public class Win32API
{
#region Win32 API
[DllImport("user32.dll", EntryPoint = "GetWindowThreadProcessId", SetLastError = true,
CharSet = CharSet.Unicode, ExactSpelling = true,
CallingConvention = CallingConvention.StdCall)]
public static extern long GetWindowThreadProcessId(long hWnd, long lpdwProcessId);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
public static extern int SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", EntryPoint = "GetWindowLongA", SetLastError = true)]
public static extern long GetWindowLong(IntPtr hwnd, int nIndex);
public static IntPtr SetWindowLong(HandleRef hWnd, int nIndex, int dwNewLong)
{
if (IntPtr.Size == 4)
{
return SetWindowLongPtr32(hWnd, nIndex, dwNewLong);
}
return SetWindowLongPtr64(hWnd, nIndex, dwNewLong);
}
[DllImport("user32.dll", EntryPoint = "SetWindowLong", CharSet = CharSet.Auto)]
public static extern IntPtr SetWindowLongPtr32(HandleRef hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", CharSet = CharSet.Auto)]
public static extern IntPtr SetWindowLongPtr64(HandleRef hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll", SetLastError = true)]
public static extern long SetWindowPos(IntPtr hwnd, long hWndInsertAfter, long x, long y, long cx, long cy, long wFlags);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);
[DllImport("user32.dll", EntryPoint = "PostMessageA", SetLastError = true)]
public static extern bool PostMessage(IntPtr hwnd, uint Msg, uint wParam, uint lParam);
/// <summary>
/// 获取系统错误信息描述
/// </summary>
/// <param name="errCode">系统错误码</param>
/// <returns></returns>
public static string GetLastError()
{
var errCode = Marshal.GetLastWin32Error();
IntPtr tempptr = IntPtr.Zero;
string msg = null;
FormatMessage(0x1300, ref tempptr, errCode, 0, ref msg, 255, ref tempptr);
return msg;
}
/// <summary>
/// 获取系统错误信息描述
/// </summary>
/// <param name="errCode">系统错误码</param>
/// <returns></returns>
public static string GetLastErrorString(int errCode)
{
IntPtr tempptr = IntPtr.Zero;
string msg = null;
FormatMessage(0x1300, ref tempptr, errCode, 0, ref msg, 255, ref tempptr);
return msg;
}
[System.Runtime.InteropServices.DllImport("Kernel32.dll")]
public extern static int FormatMessage(int flag, ref IntPtr source, int msgid, int langid, ref string buf, int size, ref IntPtr args);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetParent(IntPtr hwnd);
[DllImport("user32.dll", EntryPoint = "ShowWindow", SetLastError = true)]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
public const int SWP_NOOWNERZORDER = 0x200;
public const int SWP_NOREDRAW = 0x8;
public const int SWP_NOZORDER = 0x4;
public const int SWP_SHOWWINDOW = 0x0040;
public const int WS_EX_MDICHILD = 0x40;
public const int SWP_NOACTIVATE = 0x10;
public const int SWP_ASYNCWINDOWPOS = 0x4000;
public const int SWP_NOMOVE = 0x2;
public const int SWP_NOSIZE = 0x1;
public const int WM_CLOSE = 0x10;
public const int SW_HIDE = 0; //{隐藏, 并且任务栏也没有最小化图标}
public const int SW_SHOWNORMAL = 1; //{用最近的大小和位置显示, 激活}
public const int SW_NORMAL = 1; //{同 SW_SHOWNORMAL}
public const int SW_SHOWMINIMIZED = 2; //{最小化, 激活}
public const int SW_SHOWMAXIMIZED = 3; //{最大化, 激活}
public const int SW_MAXIMIZE = 3; //{同 SW_SHOWMAXIMIZED}
public const int SW_SHOWNOACTIVATE = 4; //{用最近的大小和位置显示, 不激活}
public const int SW_SHOW = 5; //{同 SW_SHOWNORMAL}
public const int SW_MINIMIZE = 6; //{最小化, 不激活}
public const int SW_SHOWMINNOACTIVE = 7; //{同 SW_MINIMIZE}
public const int SW_SHOWNA = 8; //{同 SW_SHOWNOACTIVATE}
public const int SW_RESTORE = 9; //{同 SW_SHOWNORMAL}
public const int SW_SHOWDEFAULT = 10; //{同 SW_SHOWNORMAL}
public const int SW_MAX = 10; //{同 SW_SHOWNORMAL}
//const int PROCESS_ALL_ACCESS = 0x1F0FFF;
//const int PROCESS_VM_READ = 0x0010;
//const int PROCESS_VM_WRITE = 0x0020;
internal const int
GWL_WNDPROC = (-4),
GWL_HINSTANCE = (-6),
GWL_HWNDPARENT = (-8),
GWL_STYLE = (-16),
GWL_EXSTYLE = (-20),
GWL_USERDATA = (-21),
GWL_ID = (-12);
internal const int
WS_CHILD = 0x40000000,
WS_VISIBLE = 0x10000000,
LBS_NOTIFY = 0x00000001,
HOST_ID = 0x00000002,
LISTBOX_ID = 0x00000001,
WS_VSCROLL = 0x00200000,
WS_BORDER = 0x00800000;
private const int HWND_TOP = 0x0;
private const int WM_COMMAND = 0x0112;
private const int WM_QT_PAINT = 0xC2DC;
private const int WM_PAINT = 0x000F;
private const int WM_SIZE = 0x0005;
private const int SWP_FRAMECHANGED = 0x0020;
#endregion Win32 API
}
- 给该窗口添加一个容器
public partial class PluginContainer : ContentControl
{
public PluginContainer(string appFileName,IntPtr hostHandle)
{
InitializeComponent();
_appFilename = appFileName;
_hostWinHandle = hostHandle;
}
private void OpenExternProcess(int width,int height)
{
try{
ProcessStartInfo info = new ProcessStartInfo(_appFilename) ;
info.UseShellExecute = true;
//info.WindowStyle = ProcessWindowStyle.Minimized;
info.WindowStyle = ProcessWindowStyle.Hidden;
AppProcess = System.Diagnostics.Process.Start(info);
// Wait for process to be created and enter idle condition
AppProcess.WaitForInputIdle();
while (AppProcess.MainWindowHandle == IntPtr.Zero){
Thread.Sleep(5);
}
}
catch (Exception ex){
throw ex;
}
}
public bool EmbedProcess(int width, int height)
{
OpenExternProcess(width, height);
try {
var pluginWinHandle = AppProcess.MainWindowHandle;//Get the handle of main window.
embedResult = Win32API.SetParent(pluginWinHandle, _hostWinHandle);//set parent window
Win32API.SetWindowLong(new HandleRef(this, pluginWinHandle), Win32API.GWL_STYLE, Win32API.WS_VISIBLE);//Set window style to "None".
var moveResult = Win32API.MoveWindow(pluginWinHandle, 0, 0, width, height, true);//Move window to fixed position(up-left is (0,0), and low-right is (width, height)).
//embed failed, and tries again
if (!moveResult || embedResult==0){
AppProcess.Kill();
if (MAXCOUNT-->0){
EmbedProcess(width, height);
}
}
else{
Win32API.ShowWindow(pluginWinHandle, (short)Win32API.SW_MAXIMIZE);
}
}
catch (Exception ex)
{
var errorString = Win32API.GetLastError();
MessageBox.Show(errorString+ex.Message);
}
return (embedResult != 0);
}
#region
public int embedResult = 0;
public Process AppProcess { get; set; }
private IntPtr _hostWinHandle { get; set; }
private string _appFilename = "";
private int MAXCOUNT=10;
#endregion
}
- 正式使用
//把句柄传进去,这个mainBorder是在MainWindow中一个Border控件的名字,下面这个获取句柄这个只能在MainWindow.xaml.cs文件中才能获取,我也不知道为什么。在其他的某个xaml.cs中却获取不到这个句柄,这个窗口句柄被当做外部窗口的父窗口了(API中的 SetParent函数来实现的)
var hostWinHandle = ((HwndSource)PresentationSource.FromVisual(mainBorder)).Handle;
var plugin = new PluginContainer(fileName, hostWinHandle);
mainBorder.Content = plugin;
plugin.EmbedProcess((int)mainBorder.Width,(int)mainBorder.Height);``
- *此外
获取窗口句柄还可以用API函数FindWindow来实现
IntPtr handle = FindWindow(null,"Window_Name");