MessageBox.Show
显示一个窗口,在MessageBox
窗口未关闭之前,点击主应用仍会让MessageBox
窗口具有焦点并进入MessageBox
窗口闪。与TaskDialog
API,Form.ShowDialog(IWin32Window)和Window.ShowDialog()相同的行为。
如何针对Process.Start("notepad.exe")
等其他流程执行此操作?
答案 0 :(得分:4)
这个黑客可能会成功。但在使用之前一定要问自己两次。
private void DoEvil()
{
var windowField = typeof(Control).GetField("window", BindingFlags.Instance |
BindingFlags.NonPublic);
Form notepad = new Form();
NativeWindow window = (NativeWindow)windowField.GetValue(notepad);
var process = Process.Start("notepad.exe");
process.EnableRaisingEvents = true;
while (process.MainWindowHandle == IntPtr.Zero)
{
Thread.Sleep(1);
}
window.AssignHandle(process.MainWindowHandle);
Control.CheckForIllegalCrossThreadCalls = false;
EventHandler handler = (s, ev) => notepad.DialogResult = DialogResult.OK;
process.Exited += handler;
notepad.ShowDialog(this);
process.Exited -= handler;
Control.CheckForIllegalCrossThreadCalls = true;
}
警告:不要在家,办公室或任何地方尝试:p
答案 1 :(得分:1)
好的,所以这就是我提出的,不是最好或最优雅的方式,它也不包含任何错误处理,但我认为它有效,
我在代码中所做的是使用Panel控件创建一个虚拟表单(Form2),并将EXE的父表分配给面板的句柄(InPtr)。我已经移除到窗体的边框,使其看起来尽可能齐全,但EXE加载和调用的SetParent方法之间仍然有明显的闪烁。
private void button4_Click(object sender, EventArgs e)
{
using (var f2 = new Form2())
{
f2.ShowDialog(this);
}
}
public partial class Form2 : Form
{
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll")]
static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, SetWindowPosFlags uFlags);
private readonly BackgroundWorker worker = new BackgroundWorker();
private IntPtr mainHandle;
private IntPtr processHandle;
private Panel panel;
public Form2()
{
InitializeComponent();
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
FormBorderStyle = FormBorderStyle.None;
StartPosition = FormStartPosition.CenterParent;
AddPanel();
}
private void AddPanel()
{
panel = new Panel {Dock = DockStyle.Fill};
mainHandle = panel.Handle;
Controls.Add(panel);
}
private void Form2_Load(object sender, EventArgs e)
{
worker.RunWorkerAsync();
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Invoke((MethodInvoker) Close);
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
var process = Process.Start("cmd.exe", "/k echo echo");
if (process != null)
{
while (process.MainWindowHandle == IntPtr.Zero)
{
// Add some sort of timeout here, infintite loops are bad!!!
}
processHandle = process.MainWindowHandle;
// Get the size of the EXE window and apply it to this form.
var size = GetSize(processHandle);
Invoke((MethodInvoker) delegate { Size = new Size(size.Width, size.Height);});
// Hook the parent of the EXE window to this form
SetHandle(processHandle);
// Make sure the windows is positions at location x = 0, y = 0 of this form
SetWindowPos(processHandle, IntPtr.Zero, 0, 0, size.Width, size.Height, SetWindowPosFlags.SWP_ASYNCWINDOWPOS);
// wait for the EXE to terminate
process.WaitForExit();
// Unhook the closed process window
SetParent(processHandle, IntPtr.Zero);
}
}
private void SetHandle(IntPtr ptr)
{
if (ptr != IntPtr.Zero)
SetParent(processHandle, mainHandle);
}
private static Size GetSize(IntPtr hWnd)
{
RECT pRect;
var size = new Size();
GetWindowRect(hWnd, out pRect);
size.Width = pRect.Right - pRect.Left;
size.Height = pRect.Bottom - pRect.Top;
return size;
}
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[Flags]
private enum SetWindowPosFlags : uint
{
SWP_ASYNCWINDOWPOS = 0x4000,
SWP_DEFERERASE = 0x2000,
SWP_DRAWFRAME = 0x0020,
SWP_FRAMECHANGED = 0x0020,
SWP_HIDEWINDOW = 0x0080,
SWP_NOACTIVATE = 0x0010,
SWP_NOCOPYBITS = 0x0100,
SWP_NOMOVE = 0x0002,
SWP_NOOWNERZORDER = 0x0200,
SWP_NOREDRAW = 0x0008,
SWP_NOREPOSITION = 0x0200,
SWP_NOSENDCHANGING = 0x0400,
SWP_NOSIZE = 0x0001,
SWP_NOZORDER = 0x0004,
SWP_SHOWWINDOW = 0x0040,
}
}
答案 2 :(得分:0)
简而言之,您可以使用“process.WaitForExit();”暂停执行直到进程退出,但我认为这不会闪烁窗口或自动对焦到该进程。
如果你想要更高级的UI东西,你将不得不做这样的事情:
while (!process.HasExited)
{
//update UI
}
要将焦点切换到另一个流程,this应该让您入门。
编辑: Here's有关闪烁窗口的更多信息。
答案 3 :(得分:-2)
好的,这是一个可行的解决方案:
使用以下代码启动该过程:
Process p = new Process("cmd");
p.Start();
p.WaitForExit();
现在,在用于激活应用程序的活动处理程序(活动)中,再次将焦点设置在流程上。您可以使用process.HasExited来验证您的进程是否仍在运行。
注意:我没有测试过,但我认为它应该会让你接近。