C# 启动与文件关联的外部进程并将此应用程序发送到后台

C# 启动与文件关联的外部进程并将此应用程序发送到后台,c#,winforms,process,C#,Winforms,Process,我见过大量通过文件启动外部应用程序的代码,但这不是问题所在。为了明确我想要的行为: 对于给定的文件名,启动正确的进程 如果没有关联的进程,则适当的shell对话框应提示用户关联一个进程 当应用程序启动时,此应用程序需要转到Z顺序的后面(或正在启动的应用程序的后面)并停留在那里 第三步是我没有做对的。我通过psd文件启动Photoshop,但当屏幕显示aplash时,它会在我的应用程序争夺焦点时闪烁。一旦它启动正常,一切都很好,但我不喜欢闪烁,而闪光屏幕显示 以下是我迄今为止最好的尝试: usin

我见过大量通过文件启动外部应用程序的代码,但这不是问题所在。为了明确我想要的行为:

  • 对于给定的文件名,启动正确的进程
  • 如果没有关联的进程,则适当的shell对话框应提示用户关联一个进程
  • 当应用程序启动时,此应用程序需要转到Z顺序的后面(或正在启动的应用程序的后面)并停留在那里
  • 第三步是我没有做对的。我通过psd文件启动Photoshop,但当屏幕显示aplash时,它会在我的应用程序争夺焦点时闪烁。一旦它启动正常,一切都很好,但我不喜欢闪烁,而闪光屏幕显示

    以下是我迄今为止最好的尝试:

    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using System.Security;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    namespace Romy.Core
    {
        internal static class Example
        {
            public const int SW_RESTORE = 9;
    
            private static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
    
            private const uint SWP_NOACTIVATE = 0x0010;
    
            private const uint SWP_NOMOVE = 0x0002;
    
            private const uint SWP_NOSIZE = 0x0001;
    
            public static void SendWindowBack(IntPtr handle)
            {
                NativeMethods.SetWindowPos(handle, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
            }
    
            public static async void ShellExecuteFile(this IWin32Window window, string filename)
            {
                var p = Process.Start(new ProcessStartInfo()
                {
                    FileName = filename,
                    Verb = "open",
                    UseShellExecute = true,
                    ErrorDialog = true
                });
    
                SendWindowBack(window.Handle);
    
                try
                {
                    await Task.Run(async () =>
                    {
                        try
                        {
                            p.WaitForInputIdle();
                            IntPtr handle = p.MainWindowHandle;
    
                            while (handle == IntPtr.Zero)
                            {
                                await Task.Delay(TimeSpan.FromMilliseconds(250D));
                                handle = p.MainWindowHandle;
                            }
    
                            if (handle != IntPtr.Zero)
                            {
                                if (NativeMethods.IsIconic(handle))
                                    NativeMethods.ShowWindowAsync(handle, SW_RESTORE);
    
                                if (NativeMethods.SetForegroundWindow(handle))
                                    NativeMethods.SetActiveWindow(handle);
                            }
                        }
                        catch (InvalidOperationException) { }
                        catch (PlatformNotSupportedException) { }
                        catch (NotSupportedException) { }
                        catch (Exception ex) { ex.Log(); }
                    }).TimeoutAfter(TimeSpan.FromSeconds(3D));
                }
                catch (TimeoutException) { }
            }
    
            [SuppressUnmanagedCodeSecurity]
            internal static class NativeMethods
            {
                [DllImport("user32.dll")]
                [return: MarshalAs(UnmanagedType.Bool)]
                internal static extern bool IsIconic(System.IntPtr hWnd);
    
                [DllImport("user32.dll")]
                [return: MarshalAs(UnmanagedType.Bool)]
                internal static extern bool SetForegroundWindow(System.IntPtr hWnd);
    
                [DllImport("user32.dll")]
                internal static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter,
                    int X, int Y, int cx, int cy, uint uFlags);
    
                [DllImport("user32.dll")]
                [return: MarshalAs(UnmanagedType.Bool)]
                internal static extern bool ShowWindowAsync(System.IntPtr hWnd, int nCmdShow);
    
                [DllImport("user32.dll")]
                internal static extern System.IntPtr SetActiveWindow(System.IntPtr hWnd);
            }
        }
    }
    

    尝试取消对SendWindowBack的呼叫,并将SetForegroundWindow替换为。这应该满足您的要求:

    …(或就在正在启动的应用程序后面)并留在那里


    它与您的问题无关,但您几乎不应该使用
    async void
    。除了fire and forget。请不要发表无关的无稽之谈。哦,对不起。。。除了火灾和遗忘事件,我没有看到“几乎”。在任何其他情况下,这都是一个错误。在任何情况下,这个问题都与异步executionEvent处理程序无关。。。开火和遗忘的方法和事件不一定是一回事。否则+1表示在评论之后立即重复我的评论。也许我应该在我的问题中编辑代码以避免更多不相关的评论?我使用异步任务只是为了能够在方法内部异步等待进程启动。这种方法是fire and forget(我不想在进程启动后等待它),但我真的不想讨论async void的优点。@Panagiotis我刚刚意识到我的评论语气可能是冒犯性的。这不是我的本意。您是对的,问题中的代码虽然碰巧是异步的,但与异步执行无关。谢谢您的回答。太久了,我忘了我问过这个问题。我有时间的时候会去看看的。
    const int GWL_HWNDPARENT = (-8);
    
    [DllImport("user32.dll", SetLastError = true)]
    static extern int SetWindowLong(IntPtr childHandle, int nIndex, IntPtr parentHandle);
    
    if (handle != IntPtr.Zero)
    {
        if (NativeMethods.IsIconic(handle))
            NativeMethods.ShowWindowAsync(handle, SW_RESTORE);
    
        SetWindowLong(handle, GWL_HWNDPARENT, window.Handle)
        NativeMethods.SetActiveWindow(handle);
    }