显示任何过程,如模态窗口

时间:2014-05-12 08:31:59

标签: c# .net vb.net

MessageBox.Show显示一个窗口,在MessageBox窗口未关闭之前,点击主应用仍会让MessageBox窗口具有焦点并进入MessageBox窗口闪。与TaskDialog API,Form.ShowDialog(IWin32Window)Window.ShowDialog()相同的行为。

如何针对Process.Start("notepad.exe")等其他流程执行此操作?

4 个答案:

答案 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方法之间仍然有明显的闪烁。

Form1代码

private void button4_Click(object sender, EventArgs e)
{
    using (var f2 = new Form2())
    {
        f2.ShowDialog(this);
    }
}

Form2代码

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)

请看这里:Wait till a process ends

简而言之,您可以使用“process.WaitForExit();”暂停执行直到进程退出,但我认为这不会闪烁窗口或自动对焦到该进程。

如果你想要更高级的UI东西,你将不得不做这样的事情:

while (!process.HasExited)
{
    //update UI
}

要将焦点切换到另一个流程,this应该让您入门。

编辑: Here's有关闪烁窗口的更多信息。

答案 3 :(得分:-2)

好的,这是一个可行的解决方案:

使用以下代码启动该过程:

Process p = new Process("cmd");
p.Start();
p.WaitForExit();

现在,在用于激活应用程序的活动处理程序(活动)中,再次将焦点设置在流程上。您可以使用process.HasExited来验证您的进程是否仍在运行。

注意:我没有测试过,但我认为它应该会让你接近。