简体   繁体   中英

Launch external process associated with a file and send this application to the back

I've seen loads of code to launch an external application via a file, but that's not the problem. To clarify exactly the behaviour I want:

  1. For a given filename, launch the correct process.
  2. If there is no associated process, the proper shell dialog should prompt the user to associate one.
  3. While the application is launched, this application needs to go to the back of the Z-order (or just behind the app that is launching) and STAY THERE.

Step 3 is what I haven't got right. I am launching Photoshop via a psd file, but while the aplash screen is shown, it flashes as my app fights for the focus. Once it starts up properly, all is well, but I don't like the flickering while the flash screen is displayed.

Here is my best attempt so far:

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);
        }
    }
}

Try removing your call to SendWindowBack and replace SetForegroundWindow with SetWindowLong . This should meet your requierment:

...(or just behind the app that is launching) and STAY THERE..

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);
}     

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM