2

我已经看到了大量通过文件启动外部应用程序的代码,但这不是问题。为了准确澄清我想要的行为:

  1. 对于给定的文件名,启动正确的进程。
  2. 如果没有关联的进程,正确的 shell 对话框应该提示用户关联一个。
  3. 启动应用程序时,此应用程序需要转到 Z 顺序的后面(或就在正在启动的应用程序后面)并留在那里。

第3步是我没有做对的。我正在通过 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);
        }
    }
}
4

1 回答 1

0

尝试删除您对SendWindowBack的调用并将SetForegroundWindow替换为SetWindowLong。这应该满足您的要求:

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

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);
}     
于 2014-05-04T21:10:15.640 回答