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 ModelId
or the PackageName
, but it seems Windows 10 Store Apps (I tried with Mail
, Store
and Edge
) won't work with the Package query API
Using kernel32.dll
, GetApplicationUserModelId
returns APPMODEL_ERROR_NO_APPLICATION
and GetPackageId
returns APPMODEL_ERROR_NO_PACKAGE
.
How can I get an identifier for a Windows 10 Store App, so that I can uniquely identify, say, Edge but also any other Windows 10 Store Apps?
I'm getting the process ID from the hWnd
(the window handle), so I think my problem is actually how to get the "real" process ID from a window handle. From there, using those methods would probably work.
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);
/// <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
}
}
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.