如何从hWnd获取Windows 10商店应用程序的"应用程序名称"(例如Edge)

Chr*_*eau 7 c# windows kernel32 windows-10

我正试图为Windows 10应用程序获得一个可理解的"进程名称".目前,所有的人使用ApplicationFrameHost,所以我想我可以请使用ModelIdPackageName,但它似乎视窗10店应用程序(我试过Mail,StoreEdge)将不会与工作包查询API

使用kernel32.dll,GetApplicationUserModelId返回APPMODEL_ERROR_NO_APPLICATIONGetPackageId返回APPMODEL_ERROR_NO_PACKAGE.

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


更新

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

Chr*_*unk 7

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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
Run Code Online (Sandbox Code Playgroud)

使用返回值并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());
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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


kie*_*wic 1

您可以使用GetPackageId()然后使用PackageFullNameFromId()

例如:

HANDLE hProcess = OpenProcess(
    PROCESS_QUERY_LIMITED_INFORMATION,
    false,
    pe32.th32ProcessID);

UINT32 bufferLength = 0;

LONG result = GetPackageId(hProcess, &bufferLength, nullptr);

BYTE* buffer = (PBYTE) malloc(bufferLength);
result = GetPackageId(hProcess, &bufferLength, buffer);

PACKAGE_ID* packageId = reinterpret_cast<PACKAGE_ID*>(buffer);
wprintf(L"Name: %s\n", packageId->name);
Run Code Online (Sandbox Code Playgroud)

  • @Andy,即使你在技术上是正确的,我也不认为@kiewic的答案值得“-1”,因为我的问题与“kernel32.dll”有关,因此本机调用......我会建议改进而不是惩罚答案。 (2认同)