0

The goal

I developed a keyboard in Unity3D (C#) and want it to pop up when the users click on "EDIT" type control such as a address bar or an input field. Therefore, I need to detect when an "EDIT" control is clicked.

What I've tried

Currently I use SetWinEventHook and listen to event EVENT_OBJECT_FOCUS to get the handle of the object which gets the focus. After that, I use GetClassName to see if the focused object is an "EDIT" control which displays a flashing caret when clicked. However, take Google Chrome as an example, I always get Chrome_WidgetWin_1 whether I click the address bar or the plain text of the page. After doing some googling I found this blog post What makes RealGetWindowClass so much more real than GetClassName? saying that RealGetWindowClass can get the base class which I think it will be something like "EDIT" or "COMBOBOX" listed here. Things were not going so well. I tried using RealGetWindowClass and still get the same result Chrome_WidgetWin_1.

The problem

Why do Get­Class­Name and Real­Get­Window­Class return the same value? How should I make Real­Get­Window­Class return the base class?

The code

[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetWinEventHook(int eventMin, int eventMax, IntPtr hmodWinEventProc, WinEventProc lpfnWinEventProc, int idProcess, int idThread, int dwflags);
[DllImport("user32.dll", SetLastError = true)]
private static extern int UnhookWinEvent(IntPtr hWinEventHook);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint RealGetWindowClass(IntPtr hwnd, [Out] StringBuilder pszType, uint cchType);

private delegate void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hWnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
private const int WINEVENT_SKIPOWNPROCESS = 2;
private IntPtr windowEventHook;
private const int EVENT_OBJECT_FOCUS = 0x8005;

private void Start()
{
    if (windowEventHook == IntPtr.Zero)
    {
        windowEventHook = SetWinEventHook(
            EVENT_OBJECT_FOCUS,
            EVENT_OBJECT_FOCUS,
            IntPtr.Zero,        
            WindowEventCallback,
            0,                  
            0,                  
            WINEVENT_SKIPOWNPROCESS);

        if (windowEventHook == IntPtr.Zero)
        {
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }
    }

}

private void OnDestroy()
{
    UnhookWinEvent(windowEventHook);
}

private void WindowEventCallback(IntPtr hWinEventHook, uint eventType, IntPtr hWnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
    UnityEngine.Debug.Log($"[EventType]: {(EventEnum)eventType} [Class Name]: {GetClassName(hWnd)} [Real Class]: {RealGetWindowClassM(hWnd)}");
    // Will print out the same log whether I click an address bar or plain text.
    // [EventType]: EVENT_OBJECT_FOCUS [Class Name]: Chrome_WidgetWin_1 [Real Class]: Chrome_WidgetWin_1
}

private string GetClassName(IntPtr hWnd)
{
    StringBuilder className = new StringBuilder(256);
    GetClassName(hWnd, className, className.Capacity);
    return className.ToString();
}

private string RealGetWindowClassM(IntPtr hWnd)
{
    StringBuilder className = new StringBuilder(256);
    RealGetWindowClass(hWnd, className, (UInt32)className.Capacity);
    return className.ToString();
}
  • 1
    It is merely telling you the truth, both ways, browsers (like Chrome) do not use edit or combobox controls. They draw pixels to make it look like such a control. Use the 64-bit version of Spy++ or WinSpy to see this for yourself. – Hans Passant Mar 07 '20 at 15:50
  • Thank you for the information! I will try Spy++ and see that. Do you know if there is any other way to achieve the detection of focus change on edit control like a textbox or an input field globally? – user3047913 Mar 08 '20 at 12:37

0 Answers0