1

I am trying to get the HWND of the On-Screen/Touch keyboard - the one that appears on the bottom of the screen. For that I first look for the Process ID according to the name, and then do EnumWindows to look through all windows and find one whose Process ID corresponds to the one I know:

doTheThing(int pid)
{
    EnumWindows(new EnumWindowsProc(Report), (IntPtr)pid);
}

protected delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);

[DllImport("user32.dll")]
protected static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);

protected static bool Report(IntPtr hwnd, IntPtr lParam)
{
    IntPtr lpdwProcessId;
    GetWindowThreadProcessId(hwnd, out lpdwProcessId);
    if (lpdwProcessId == lParam)
    {
        MessageBox.Show("True: " + hwnd.ToString());
        return false;
    }
    return true;
}

Now Report is called several times, but the thing never actually matches. If I take another process, it works, but it seems like the OnScreenkeyboard "System.Diagnostics.Process (WindowsInternal.ComposableShell.Experiences.TextInput.InputApp)" does not show up with EnumWindows.

Using FindWindow does not work as the window does not have a Title and its class is the generic ApplicationFrameHost.

In AHK I was able to get the HWND by hovering over the keyboard with MouseGetPos,,, WinUMID, so a HWND definitely exists.

Is there a possibility that some windows are ignored by the EnumWindows? If so, how can I prevent that? What other solutions are there?

As a sidenote, it also does not show up in the UI Automation verify tool.

Note: Windows now has two keyboards apparently. I mean the one you can open via the taskbar.

To clarify why I need this:

I have the following AHK-Script that can make the window I point my mouse over window semi-transparent:

MouseGetPos,,, WinUMID
WinSet, Transparent, 100, ahk_id %WinUMID%

I noticed that when I point it over the Touch-Keyboard, it will make it transparent and that effect stays even if the keyboard disappears, until I restart my system. If I save the WinUMID variable I can even change the transparency while the keyboard is not used.

Now I want to make the Keyboard semi-transparent in C#, and that works as long as I provide the HWND. But I can't figure out a way to get the HWND inside of C#, without the help of AHK.

process.MainWindowHandle returns 0.

I have also tried

ProcessThreadCollection threads = process.Threads;
foreach (ProcessThread thread in threads)
{
    EnumThreadWindows((uint)thread.Id, new EnumThreadDelegate(EnumThreadCallback), IntPtr.Zero);
}

[DllImport("user32.dll")]
static extern bool EnumThreadWindows(uint dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);

protected delegate bool EnumThreadDelegate(IntPtr hwnd, IntPtr lParam);

protected static bool EnumThreadCallback(IntPtr hwnd, IntPtr lParam)
{
    MessageBox.Show("Try");
    IntPtr lpdwProcessId;
    GetWindowThreadProcessId(hwnd, out lpdwProcessId);
    if (lpdwProcessId == lParam)
    {
        MessageBox.Show("True: " + hwnd.ToString());
        makeTransparent(lParam);
        return false;
    } else
        MessageBox.Show("False: " + hwnd.ToString());
    return true;
}

EnumThreadCallback was never called though, even though it works for any other process.

Edit:

I found out that the Keyboard process was not actually creating the Keyboard. Instead, it seems like the keyboard-UI is created by explorer.

The following code does work:

Process[] processes = Process.GetProcesses();

IntPtr hWnd = IntPtr.Zero;
while ((hWnd = FindWindowEx(IntPtr.Zero, hWnd, "ApplicationFrameWindow", null)) != IntPtr.Zero)
{
    IntPtr lpdwProcessId;
    GetWindowThreadProcessId(hWnd, out lpdwProcessId);
    foreach (Process process in processes)
    {
        if (process.ToString() == "System.Diagnostics.Process (explorer)")
        {
            if (process.Id == (int)lpdwProcessId)
            {
                doThing(hWnd);
            }
        }
    }
}

However, multiple hWnds are used, and only one of them belongs to the keyboard. Now I need to find out how to filter out this specific one. There do not seem to be downsides to this method right now, but I don't feel comfortable releasing this without knowing if it can have any adverse effects because of this. Victim 1: The new Paste window.

Dodeius
  • 125
  • 1
  • 10
  • Use UI Automation verify tool (https://learn.microsoft.com/en-us/windows/desktop/winauto/ui-automation-verify) to get the HWND and then find the matching process. – Karel Frajták Jan 06 '19 at 18:05
  • Maybe this helps? (window is found by the class name) https://stackoverflow.com/a/24890595/7034621 – orhtej2 Jan 06 '19 at 18:05

1 Answers1

0

Try this alternate solution:

var process = Process.GetProcessById(pid);
IntPtr hwnd = process.MainWindowHandle;
Ilian
  • 5,113
  • 1
  • 32
  • 41
  • `process.ToString()` returns the right name (`System.Diagnostics.Process (WindowsInternal.ComposableShell.Experiences.TextInput.InputApp)`), but the hwnd is `0` – Dodeius Jan 07 '19 at 23:23
  • It might be an owned window. See https://stackoverflow.com/a/22177830/324260 on how to query for it. – Ilian Jan 07 '19 at 23:26
  • @ `EnumThreadWindows` doesn't call anything. I will update my question with that information. – Dodeius Jan 08 '19 at 23:49