How to get the “Application Name” from hWnd for Windows 10 Store Apps (e.g. Edge)

前端 未结 5 1689
清歌不尽
清歌不尽 2020-12-31 13:28

I\'m trying to get an understandable \"Process Name\" for Windows 10 apps. Currently, all of them use ApplicationFrameHost, so I thought I could use either the

5条回答
  •  一整个雨季
    2020-12-31 13:41

    UWP apps are wrapped into an other app/process. If this has focus, then try and find the child UWP process.

    You will need some P/Invoke methods. Take a look at this class, which provide all the code you need to do the job:

    using System;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Text;
    
    namespace Stackoverflow
    {
        internal struct WINDOWINFO
        {
            public uint ownerpid;
            public uint childpid;
        }
    
        public class UwpUtils
        {
            #region User32
            [DllImport("user32.dll")]
            public static extern IntPtr GetForegroundWindow();
            [DllImport("user32.dll", SetLastError = true)]
            public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
            // When you don't want the ProcessId, use this overload and pass IntPtr.Zero for the second parameter
            [DllImport("user32.dll", SetLastError = true)]
            public static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
            /// 
            /// Delegate for the EnumChildWindows method
            /// 
            /// Window handle
            /// Caller-defined variable; we use it for a pointer to our list
            /// True to continue enumerating, false to bail.
            public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);
    
            [DllImport("user32", SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool EnumChildWindows(IntPtr hWndParent, EnumWindowProc lpEnumFunc, IntPtr lParam);
            #endregion
    
            #region Kernel32
            public const UInt32 PROCESS_QUERY_INFORMATION = 0x400;
            public const UInt32 PROCESS_VM_READ = 0x010;
    
            [DllImport("kernel32.dll", SetLastError = true)]
            public static extern bool QueryFullProcessImageName([In]IntPtr hProcess, [In]int dwFlags, [Out]StringBuilder lpExeName, ref int lpdwSize);
    
            [DllImport("kernel32.dll", SetLastError = true)]
            public static extern IntPtr OpenProcess(
                UInt32 dwDesiredAccess,
                [MarshalAs(UnmanagedType.Bool)]
                Boolean bInheritHandle,
                Int32 dwProcessId
            );
            #endregion
    
            public static string GetProcessName(IntPtr hWnd)
            {
                string processName = null;
    
                hWnd = GetForegroundWindow();
    
                if (hWnd == IntPtr.Zero)
                    return null;
    
                uint pID;
                GetWindowThreadProcessId(hWnd, out pID);
    
                IntPtr proc;
                if ((proc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, (int)pID)) == IntPtr.Zero)
                    return null;
    
                int capacity = 2000;
                StringBuilder sb = new StringBuilder(capacity);
                QueryFullProcessImageName(proc, 0, sb, ref capacity);
    
                processName = sb.ToString(0, capacity);
    
                // UWP apps are wrapped in another app called, if this has focus then try and find the child UWP process
                if (Path.GetFileName(processName).Equals("ApplicationFrameHost.exe"))
                {
                    processName = UWP_AppName(hWnd, pID);
                }
    
                return processName;
            }
    
            #region Get UWP Application Name
    
            /// 
            /// Find child process for uwp apps, edge, mail, etc.
            /// 
            /// hWnd
            /// pID
            /// The application name of the UWP.
            private static string UWP_AppName(IntPtr hWnd, uint pID)
            {
                WINDOWINFO windowinfo = new WINDOWINFO();
                windowinfo.ownerpid = pID;
                windowinfo.childpid = windowinfo.ownerpid;
    
                IntPtr pWindowinfo = Marshal.AllocHGlobal(Marshal.SizeOf(windowinfo));
    
                Marshal.StructureToPtr(windowinfo, pWindowinfo, false);
    
                EnumWindowProc lpEnumFunc = new EnumWindowProc(EnumChildWindowsCallback);
                EnumChildWindows(hWnd, lpEnumFunc, pWindowinfo);
    
                windowinfo = (WINDOWINFO)Marshal.PtrToStructure(pWindowinfo, typeof(WINDOWINFO));
    
                IntPtr proc;
                if ((proc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, (int)windowinfo.childpid)) == IntPtr.Zero)
                    return null;
    
                int capacity = 2000;
                StringBuilder sb = new StringBuilder(capacity);
                QueryFullProcessImageName(proc, 0, sb, ref capacity);
    
                Marshal.FreeHGlobal(pWindowinfo);
    
                return sb.ToString(0, capacity);
            }
    
            /// 
            /// Callback for enumerating the child windows.
            /// 
            /// hWnd
            /// lParam
            /// always true.
            private static bool EnumChildWindowsCallback(IntPtr hWnd, IntPtr lParam)
            {
                WINDOWINFO info = (WINDOWINFO)Marshal.PtrToStructure(lParam, typeof(WINDOWINFO));
    
                uint pID;
                GetWindowThreadProcessId(hWnd, out pID);
    
                if (pID != info.ownerpid)
                    info.childpid = pID;
    
                Marshal.StructureToPtr(info, lParam, true);
    
                return true;
            }
            #endregion
        }
    }
    

    Now, get a handle to the current foreground window using another P/Invoke method

    [DllImport("user32.dll")]
    public static extern IntPtr GetForegroundWindow();
    

    Use the return value and call the GetProcessName method from the code above. You should receive the correct name/path to the process.

    Here is a simple Form to test the code:

    using System;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    using StackOverflow;
    
    namespace Stackoverflow.Test
    {
        public partial class TestForm : Form
        {
            WinEventDelegate dele = null;
            delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
    
            [DllImport("user32.dll")]
            static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
    
            private const uint WINEVENT_OUTOFCONTEXT = 0;
            private const uint EVENT_SYSTEM_FOREGROUND = 3;
    
            [DllImport("user32.dll")]
            public static extern IntPtr GetForegroundWindow();
            public TestForm()
            {
                InitializeComponent();
    
                dele = new WinEventDelegate(WinEventProc);
                IntPtr m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, dele, 0, 0, WINEVENT_OUTOFCONTEXT);
            }
    
            public void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
            {
                textBox1.AppendText(GetActiveWindowTitle() + "\n");
            }
    
            private string GetActiveWindowTitle()
            {
                return UwpUtils.GetProcessName(GetForegroundWindow());
            }
        }
    }
    

    You can download the full code, including the example/test on GitHub.

提交回复
热议问题