c# - 如何从 hWnd 获取 "Application Name"for Windows 10 Store Apps (e.g. Edge)

标签 c# windows windows-10 kernel32

我正在尝试为 Windows 10 应用获取一个易于理解的“进程名称”。目前,它们都使用 ApplicationFrameHost,所以我想我可以使用 ModelIdPackageName,但似乎是 Windows 10 Store Apps (我尝试使用 MailStoreEdge)无法与 Package query API 一起使用

使用 kernel32.dllGetApplicationUserModelId 返回 APPMODEL_ERROR_NO_APPLICATIONGetPackageId 返回 APPMODEL_ERROR_NO_PACKAGE .

如何获取 Windows 10 应用商店应用程序的标识符,以便我可以唯一地标识,例如 Edge还有任何其他 Windows 10 应用商店应用程序吗?


更新

我从 hWnd(窗口句柄)获取进程 ID,所以我认为我的问题实际上是如何从窗口句柄获取“真实”进程 ID。从那里开始,使用这些方法可能会奏效。

最佳答案

UWP应用程序被包装到另一个应用程序/进程中。如果它有焦点,则尝试找到子 UWP 进程。

您将需要一些 P/Invoke 方法。看看这个类,它提供了完成这项工作所需的所有代码:

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);
        /// <summary>
        /// Delegate for the EnumChildWindows method
        /// </summary>
        /// <param name="hWnd">Window handle</param>
        /// <param name="parameter">Caller-defined variable; we use it for a pointer to our list</param>
        /// <returns>True to continue enumerating, false to bail.</returns>
        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

        /// <summary>
        /// Find child process for uwp apps, edge, mail, etc.
        /// </summary>
        /// <param name="hWnd">hWnd</param>
        /// <param name="pID">pID</param>
        /// <returns>The application name of the UWP.</returns>
        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);
        }

        /// <summary>
        /// Callback for enumerating the child windows.
        /// </summary>
        /// <param name="hWnd">hWnd</param>
        /// <param name="lParam">lParam</param>
        /// <returns>always <c>true</c>.</returns>
        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
    }
}

现在,使用另一个 P/Invoke 方法获取当前前台窗口的句柄

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

使用返回值并调用上面代码中的 GetProcessName 方法。您应该收到正确的流程名称/路径。

下面是一个简单的表单来测试代码:

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

您可以下载完整代码,包括 GitHub 上的示例/测试.

关于c# - 如何从 hWnd 获取 "Application Name"for Windows 10 Store Apps (e.g. Edge),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32001621/

相关文章:

c# - 基于功能区的 GUI - Winforms 与 WPF

c# - Itextsharp PDFPTable 如何在整个表格周围制作边框

c - 找不到头文件 "glade.h": Code::Blocks - Windows 7

python - Python2 的 sys.path 在 Windows 中究竟是如何设置的?

c# - 当它在 splittercontainer 中时,查找相对于父窗体的控件位置

c# - 如何将所有文件扩展名从 Gif 更改为 gif?

c# - 如何在 WPF C# 中对一行进行部分着色(来自代码隐藏)?

regex - 从 perl 5.8(32 位)升级到 5.16(64 位)- 正则表达式性能受到影响

windows-10 - 在windows10中开发dx应用还需要DirectX SDK吗?

c++ - 如何在 Visual Studio 2017 中启动 HLSL 调试器?