【发布时间】:2019-06-11 08:35:28
【问题描述】:
我的任务是创建一个应用程序启动器,它同时托管 Winforms 和 WPF 应用程序,并通过稍微不同的方法 Web 应用程序。向用户显示他们可以启动的应用程序列表,这些应用程序在启动时被“捕获”,并通过使用 SetParent 使面板成为进程 MainWindowHandle 的父级,将其保存在表单的面板内。该位似乎运行良好,启动时的应用程序被捕获并显示在给定面板中。
我遇到的一个特殊问题是,并非所有被捕获的应用程序都乐于在面板中开始绘制自己。它似乎与那些基于 WPF 的应用程序是隔离的,但这并不能保证。
实际上发生的情况是,如果 WPF 应用程序启动,它会被捕获并移动到面板,并且面板将保持空白,直到我单击面板,此时应用程序将愉快地重新绘制自己。从现在开始,应用程序似乎很乐意根据需要重新绘制自己而无需干预。
基本上我现在束手无策,尝试了以下 User32 本机和 .NET 方法; 无效(在表单上、面板上、承载这两者的选项卡控件上) 更新 刷新 带有过多参数的 SendMessage,包括尝试“你不应该这样做”WM_PAINT。 带有 UpdateNow 和 Invalidate 标志的 RedrawWindow。
以上都没有任何明显的区别,只有当我物理单击面板或移动窗口时,包含的应用程序才会运行并重新绘制自身。
有没有其他人制作过类似的东西并且有解决重绘/重绘问题的方法?我搜索了整个 Google/Bing/Duck Duck Go 领域,试图寻找答案,但无济于事。
希望你们当中有人能给出答案。
以下代码代表了大部分功能,它启动一个进程并捕获进程主窗口的句柄,并将其父级设置为沼泽标准 WinForms 窗口上的面板控件。我可能应该指出,沼泽标准 WinForms 窗口本身是“托管”在使用 (EasyTabs) 库的应用程序中。我相信这不会造成任何问题。
例子:
// Try to acquire a the process.
ProcessStartInfo startInfo = new ProcessStartInfo(path);
try
{
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
startInfo.WindowStyle = ProcessWindowStyle.Normal;
Process.StartInfo = startInfo;
Process.EnableRaisingEvents = true;
Process.Exited += TabManagerContainerForm_ProcessExited;
Process.Start();
if (Process != null)
{
// Wait until the process has created a main window or exited.
while (Process.MainWindowHandle == IntPtr.Zero && !Process.HasExited)
{
Thread.Sleep(100);
Process.Refresh();
}
if (!Process.HasExited) // We have acquired a MainWindowHandle
{
// Capture the Process's main window and show it inside the applicationPanel panel control.
SetParent(Process.MainWindowHandle, applicationPanel.Handle);
// Change the captured Process's window to one without the standard chrome. Itʼs provided by our tabbed application.
SetWindowLong(Process.MainWindowHandle, (int)WindowLongFlags.GWL_STYLE, (int)WindowStyles.WS_VISIBLE);
}
else // Process has exited.
{
if (Process.MainWindowHandle == IntPtr.Zero) Log.Information("{0} failed to execute.", Process.ProcessName);
throw new FailedProcessException(string.Format("{0} failed to execute.", path));
}
}
else
{
throw new Exception(string.Format("Invalid path: {0}", path));
}
}
catch (Exception ex) when (!(ex is FailedProcessException)) // Catch everything but FailedProcessExceptions. FPEs are simply passed up the chain.
{
Log.Error(ex.Message);
throw;
}
【问题讨论】:
-
如果您给出一个代码示例来说明您所做的事情,将更容易理解您究竟在哪里卡住并需要帮助。没有具体示例的长问题不会引起太多关注(也许也缩短了答案)。
-
我会看看我能做什么。代码方面,它分布在多个文件中,并且不容易拆卸。我看看能不能做一个更简单的 POC 例子。
-
仅供参考:我尝试了另一个使用标准 WinForms 选项卡控件和选项卡页的示例,并得到了相同的结果。因此,试图捕获进程的主窗口并将其嵌入选项卡似乎是徒劳的。可能会有答案,但目前我还没有找到答案。
-
我更新了我的答案。它可能会解决您的标签问题