在 WPF 中无缝托管 EXE
Hosting an EXE in WPF seamlessly
我目前的任务是在 WPF 中托管一堆旧的 VB6 exe,同时正在进行看似无限的任务或重构。
托管 EXE 一点也不难,但让这个过程变得无缝。
目前流程是:
1)创建视图
2)注入虚拟机
3) 启动进程并启动 EXE。
4)等待输入空闲
5) 设置父级
6) 设置窗口长
7) 设置窗口位置
我遇到的问题是,据我所知,使用此方法时,进程必须先自然加载,然后才能调用 SetParent。这意味着应用程序在停靠在 WPF 控件中之前会出现闪烁。
我想找到一种方法来摆脱这个,有没有一种方法可以打开纯粹在内存中或隐藏的进程(我将 UseShellExecute 设置为 true 并且 WindowStyle = ProcessWindowStyle.Minimized)。
可能有一种不同的方法可以完全打开我不知道的过程。
ViewModel 构造函数
public ShellViewModel()
{
WindowWidth = 600;
WindowHeight = 500;
MainTitle = "Main Title";
BreadCrumb = "NA";
IsLoading = true;
Task.Factory.StartNew(() =>
{
//Process p = OpenProcess(@"C:\Windows\System32\notepad.exe");
Process p = OpenProcess(@"C:\Program Files\WinRAR\WinRar.exe");
return p;
}).ContinueWith(r =>
{
try
{
Process p = r.Result;
_handler = p.MainWindowHandle;
_host.Child = _panel;
Content = _host;
int dwStyle = GetWindowLong(_handler, GWL_STYLE);
SetParent(_handler, _panel.Handle);
SetWindowLong(_handler, GWL_STYLE, new IntPtr(dwStyle & ~WS_CAPTION & ~WS_THICKFRAME));
SetWindowPos(_handler, IntPtr.Zero, 0, 0, (int) Math.Round(WindowWidth), (int) Math.Round(WindowHeight) - 106, SWP_FRAMECHANGED);
BreadCrumb += " Host: " + p.Id.ToString(CultureInfo.InvariantCulture);
IsLoading = false;
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}, UiTaskSchedulerHelper.Instance.UiTaskScheduler);
}
打开进程
private Process OpenProcess(string path)
{
Process p = null;
lock (locked)
{
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = path;
psi.UseShellExecute = true;
psi.WindowStyle = ProcessWindowStyle.Minimized;
p = Process.Start(psi);
BreadCrumb += " OpenProcess: " + p.Id;
p.EnableRaisingEvents = true;
p.WaitForInputIdle();
}
return p;
}
已将示例上传至github,随时更新。
https://github.com/OliDow/FormHostPoc/tree/master/FormHostPoc
经过一天的结对编程,我们按预期加载了它。回购工作正常。我们还必须在配置文件中将加载的 VB6 应用程序设置为最小化。
我目前的任务是在 WPF 中托管一堆旧的 VB6 exe,同时正在进行看似无限的任务或重构。
托管 EXE 一点也不难,但让这个过程变得无缝。
目前流程是: 1)创建视图 2)注入虚拟机 3) 启动进程并启动 EXE。 4)等待输入空闲 5) 设置父级 6) 设置窗口长 7) 设置窗口位置
我遇到的问题是,据我所知,使用此方法时,进程必须先自然加载,然后才能调用 SetParent。这意味着应用程序在停靠在 WPF 控件中之前会出现闪烁。
我想找到一种方法来摆脱这个,有没有一种方法可以打开纯粹在内存中或隐藏的进程(我将 UseShellExecute 设置为 true 并且 WindowStyle = ProcessWindowStyle.Minimized)。
可能有一种不同的方法可以完全打开我不知道的过程。
ViewModel 构造函数
public ShellViewModel()
{
WindowWidth = 600;
WindowHeight = 500;
MainTitle = "Main Title";
BreadCrumb = "NA";
IsLoading = true;
Task.Factory.StartNew(() =>
{
//Process p = OpenProcess(@"C:\Windows\System32\notepad.exe");
Process p = OpenProcess(@"C:\Program Files\WinRAR\WinRar.exe");
return p;
}).ContinueWith(r =>
{
try
{
Process p = r.Result;
_handler = p.MainWindowHandle;
_host.Child = _panel;
Content = _host;
int dwStyle = GetWindowLong(_handler, GWL_STYLE);
SetParent(_handler, _panel.Handle);
SetWindowLong(_handler, GWL_STYLE, new IntPtr(dwStyle & ~WS_CAPTION & ~WS_THICKFRAME));
SetWindowPos(_handler, IntPtr.Zero, 0, 0, (int) Math.Round(WindowWidth), (int) Math.Round(WindowHeight) - 106, SWP_FRAMECHANGED);
BreadCrumb += " Host: " + p.Id.ToString(CultureInfo.InvariantCulture);
IsLoading = false;
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}, UiTaskSchedulerHelper.Instance.UiTaskScheduler);
}
打开进程
private Process OpenProcess(string path)
{
Process p = null;
lock (locked)
{
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = path;
psi.UseShellExecute = true;
psi.WindowStyle = ProcessWindowStyle.Minimized;
p = Process.Start(psi);
BreadCrumb += " OpenProcess: " + p.Id;
p.EnableRaisingEvents = true;
p.WaitForInputIdle();
}
return p;
}
已将示例上传至github,随时更新。
https://github.com/OliDow/FormHostPoc/tree/master/FormHostPoc
经过一天的结对编程,我们按预期加载了它。回购工作正常。我们还必须在配置文件中将加载的 VB6 应用程序设置为最小化。