Connecting UWP apps hosted by ApplicationFrameHost to their real processes Connecting UWP apps hosted by ApplicationFrameHost to their real processes windows windows

Connecting UWP apps hosted by ApplicationFrameHost to their real processes


I eventually found a way to do this, so I am going answer my own question so maybe someone in the future with the same problem could find it useful.

This is the class with the WinApiFunctions:

public class WinAPIFunctions{    //Used to get Handle for Foreground Window    [DllImport("user32.dll", CharSet = CharSet.Auto)]    private static extern IntPtr GetForegroundWindow();    //Used to get ID of any Window    [DllImport("user32.dll", CharSet = CharSet.Auto)]    private static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);    public delegate bool WindowEnumProc(IntPtr hwnd, IntPtr lparam);    [DllImport("user32.dll")]    [return: MarshalAs(UnmanagedType.Bool)]    public static extern bool EnumChildWindows(IntPtr hwnd, WindowEnumProc callback, IntPtr lParam);    public static int GetWindowProcessId(IntPtr hwnd)    {        int pid;        GetWindowThreadProcessId(hwnd, out pid);        return pid;    }    public static IntPtr GetforegroundWindow()    {        return GetForegroundWindow();    }}

And this is the class I used to test if it would work. I used it in a simple console program that just writes out the name of the process that has current focus:

class FindHostedProcess{    public Timer MyTimer { get; set; }    private Process _realProcess;    public FindHostedProcess()    {        MyTimer = new Timer(TimerCallback, null, 0, 1000);        Console.ReadKey();    }    private void TimerCallback(object state)    {        var foregroundProcess = Process.GetProcessById(WinAPIFunctions.GetWindowProcessId(WinAPIFunctions.GetforegroundWindow()));        if (foregroundProcess.ProcessName == "ApplicationFrameHost")        {            foregroundProcess = GetRealProcess(foregroundProcess);        }        Console.WriteLine(foregroundProcess.ProcessName);    }    private Process GetRealProcess(Process foregroundProcess)    {        WinAPIFunctions.EnumChildWindows(foregroundProcess.MainWindowHandle, ChildWindowCallback, IntPtr.Zero);        return _realProcess;    }    private bool ChildWindowCallback(IntPtr hwnd, IntPtr lparam)    {        var process = Process.GetProcessById(WinAPIFunctions.GetWindowProcessId(hwnd));        if (process.ProcessName != "ApplicationFrameHost")        {            _realProcess = process;        }        return true;    }}


Chris, there is the alternative way which I have discovered trying to apply your solution to a related problem. While trying to analyse how the ApplicationFrameHost.exe-related stuff worked, I have stumbled upon the documented way of getting the true foreground window / thread and its process by passing 0 instead of the actual thread ID to GetGUIThreadInfo.

It might not fully work for the edge-case scenarios of your problem, but I felt that this might be a useful contribution for people of the future who might face the same problems ;-)

Here is the example of how this can be applied (pseudo C++'ish code):

GUITHREADINFO gti = { sizeof(GUITHREADINFO) };GetGUIThreadInfo(0, &gti); // <- note the `0`DWORD processId = 0;GetWindowThreadProcessId(gti.hwndFocus, &processId);const auto procName = Util::GetProcessName(processId);

It solved my problem (obtaining the actual keyboard layout + finding the real foreground window) for all more-or-less common apps I have tested it against.


Basically, the answer you provided will also fail if the active window is in full screen mode,

for example if you have Skype app opened along with Microsoft Remote Desktop, which is the active one and on full screen mode, EnumChildWindows would return SkypeApp not RDPClient.

and to get this fixed, you should follow the workaround suggested by Ivanmoskalev,

check this out:

[DllImport("user32.dll")][return: MarshalAs(UnmanagedType.Bool)]public static extern bool GetGUIThreadInfo(uint idThread, ref GUITHREADINFO lpgui);public static IntPtr getThreadWindowHandle(uint dwThreadId){    IntPtr hWnd;    // Get Window Handle and title from Thread    var guiThreadInfo = new GUITHREADINFO();    guiThreadInfo.cbSize = Marshal.SizeOf(guiThreadInfo);    GetGUIThreadInfo(dwThreadId, ref guiThreadInfo);    hWnd = guiThreadInfo.hwndFocus;    //some times while changing the focus between different windows, it returns Zero so we would return the Active window in that case    if (hWnd == IntPtr.Zero)    {        hWnd = guiThreadInfo.hwndActive;    }    return hWnd;}[DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]public static extern int GetWindowThreadProcessId(IntPtr windowHandle, out int processId);static void Main(string[] args){    var current = getThreadWindowHandle(0);    int processId = 0;    GetWindowThreadProcessId(current, out processId);    var foregroundProcess = GetActiveProcess(processId);}private static Process GetActiveProcess(int activeWindowProcessId){    Process foregroundProcess = null;    try    {        foregroundProcess = Process.GetProcessById(activeWindowProcessId);    }    catch (Exception ex)    {        Console.WriteLine(ex);    }    if (string.IsNullOrWhiteSpace(GetProcessNameSafe(foregroundProcess)))    {        var msg = "Process name is empty.";        Console.WriteLine(msg);    }    return foregroundProcess;}