【问题标题】:Show any Process like Modal Window显示任何进程,如模态窗口
【发布时间】:2014-06-29 12:55:52
【问题描述】:

MessageBox.Show 显示一个窗口,直到MessageBox 窗口没有关闭,单击主应用程序仍然会让MessageBox 窗口具有焦点,并使MessageBox 窗口闪烁。 TaskDialog API、Form.ShowDialog(IWin32Window)Window.ShowDialog() 的行为相同。

如何为 Process.Start("notepad.exe") 等不同的进程执行此操作?

【问题讨论】:

  • 据我所知,这是不可能的,因为您启动的进程可以是任何东西,并不总是代表一个窗口。但是,您可以使用 Process.WaitForExit 和 Process.HasExited 来确定进程是否仍在运行(并且您可以阻止您的应用执行任何操作,直到被调用的进程停止为止)。
  • social.msdn.microsoft.com/Forums/en-US/…你试过这样的事情吗?
  • @MPatel 这会阻塞 GUI 线程,因为WaitForExit() 只是让线程休眠。不是 MessageBox 提供的行为。单击主应用程序不会发生任何事情。
  • 确实如此,但它并没有模仿 flash 的行为。
  • 我希望我终于明白了,检查我的新答案。

标签: c# .net vb.net


【解决方案1】:

这个 hack 可以解决问题。但请确保您在使用此之前两次质疑自己。

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

【讨论】:

  • 我在任何代码投入生产之前对其提出三次质疑。 =p
  • 顺便说一句,即使在我关闭它们之后,邪恶的记事本也会神秘地弹出。记事本启示录!不过说真的,我建议大家不要使用这段代码。
  • 对我来说,它的行为并非如此,也许你应该检查一下你只调用了这个邪恶的代码一次。无论如何,我玩得很开心,很喜欢你的问题 +1。如果我问为什么会有这个奇怪的要求,你介意吗?
  • 我没有使用这个运行记事本,一些 CMD /K 命令。我正在考虑为此制作一个表单,以便将 CMD 输出重定向到该表单。不过我没有完全理解你的代码。
【解决方案2】:

好的,这就是我想出的,不是最好的或最优雅的方法,也不包含任何错误处理,但我认为它有效,

我在代码中所做的是创建一个带有面板控件的虚拟表单 (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,
    }
}

【讨论】:

  • 感谢您的努力,但这太疯狂了:)(尝试调整/移动子窗口的大小)。一定有一些简单的东西......类似于 ShowDialog 的工作原理
  • @Zuck,同意,但我不知道它在哪里,在哪里。当我找到它的那一刻,我会更新代码
  • 我正在尝试理解 dotPeek/Reflector/JustDecompile 中的 ShowDialog 函数。
【解决方案3】:

看这里:Wait till a process ends

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

如果您想要更精美的 UI 内容,您将不得不执行以下操作:

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

要将焦点切换到另一个进程,this 应该可以帮助您开始。

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

【讨论】:

    【解决方案4】:

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

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

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

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

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

    【讨论】:

    • 您应该在发布答案之前阅读原帖下方的 cmets ......
    猜你喜欢
    • 2023-03-10
    • 1970-01-01
    • 1970-01-01
    • 2012-02-21
    • 2018-11-04
    • 1970-01-01
    • 1970-01-01
    • 2013-01-11
    相关资源
    最近更新 更多