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.