Windows 10下TabTip.exe(虚拟键盘)的尺寸与位置获取及适配问题
Hey, I’ve run into this exact problem with Windows 10’s TabTip.exe before—your old Windows 7/8 solution fails because Microsoft completely changed how the touch keyboard is implemented here. Let’s walk through how to get the keyboard’s position/size and adjust your panel correctly:
Why Your Old Code Doesn’t Work
Windows 10’s TabTip.exe is little more than a lightweight proxy window. The actual touch keyboard UI runs as a UWP component (a Windows.UI.Core.CoreWindow), which isn’t the direct child of TabTip’s main window. Your old code was probably targeting the TabTip.exe main handle, which doesn’t reflect the actual keyboard’s visible bounds.
Step 1: Get the Real Touch Keyboard Window Handle
You need to find the actual visible keyboard window, not just the TabTip proxy. Here’s how to do it in Delphi:
- First, get the TabTip.exe process ID via its main window.
- Enumerate all windows to find the one belonging to that PID and matching the UWP keyboard’s class name.
uses Winapi.Windows, Winapi.Messages, System.SysUtils; var TargetPID: DWORD; function EnumWindowsProc(hWnd: HWND; lParam: LPARAM): BOOL; stdcall; var CurrentPID: DWORD; ClassName: array[0..255] of Char; begin Result := True; GetWindowThreadProcessId(hWnd, @CurrentPID); if CurrentPID = TargetPID then begin GetClassName(hWnd, ClassName, SizeOf(ClassName)); // Check for the UWP keyboard's class name if StrComp(ClassName, 'Windows.UI.Core.CoreWindow') = 0 then begin Ptr(lParam)^ := hWnd; Result := False; // Stop enumeration once found end; end; end; function GetTouchKeyboardHandle: HWND; var hTabTip: HWND; begin Result := 0; // Find TabTip's main proxy window hTabTip := FindWindow('IPTip_Main_Window', nil); if hTabTip = 0 then Exit; // Get TabTip's process ID GetWindowThreadProcessId(hTabTip, @TargetPID); // Enumerate windows to find the actual keyboard window EnumWindows(@EnumWindowsProc, LPARAM(@Result)); end;
Step 2: Get the Keyboard’s Size & Position
Once you have the correct handle, use GetWindowRect to get its screen coordinates—this API still works reliably here:
var KeyboardRect: TRect; KeyboardWidth, KeyboardHeight: Integer; KeyboardLeft, KeyboardTop: Integer; hKeyboard: HWND; begin hKeyboard := GetTouchKeyboardHandle; if hKeyboard = 0 then Exit; if GetWindowRect(hKeyboard, KeyboardRect) then begin KeyboardWidth := KeyboardRect.Right - KeyboardRect.Left; KeyboardHeight := KeyboardRect.Bottom - KeyboardRect.Top; KeyboardLeft := KeyboardRect.Left; KeyboardTop := KeyboardRect.Top; // Now you have all the values you need end; end;
Step 3: Adjust Your Panel’s Position
To keep the focused input box above the keyboard:
- Get the input box’s screen position using
GetWindowRect. - Calculate the new position for your panel so the input box sits above the keyboard (add a small buffer like 10px for usability).
- Move the panel with
SetWindowPos(since your app runs as admin, this will work without permission issues):
procedure MovePanelAboveKeyboard(PanelHandle, InputBoxHandle: HWND); var KeyboardRect, InputBoxRect: TRect; NewPanelTop: Integer; hKeyboard: HWND; begin hKeyboard := GetTouchKeyboardHandle; if (hKeyboard = 0) or (InputBoxHandle = 0) or (PanelHandle = 0) then Exit; GetWindowRect(hKeyboard, KeyboardRect); GetWindowRect(InputBoxHandle, InputBoxRect); // Calculate new top position: make sure input box's bottom is above keyboard's top NewPanelTop := KeyboardRect.Top - (InputBoxRect.Bottom - InputBoxRect.Top) - 10; // Keep panel within screen bounds (optional but recommended) if NewPanelTop < 0 then NewPanelTop := 0; // Move the panel (keep its size, don't change z-order) SetWindowPos(PanelHandle, HWND_TOP, 0, NewPanelTop, 0, 0, SWP_NOSIZE or SWP_NOZORDER or SWP_NOMOVE); end;
Bonus: More Reliable UI Automation Method
If you want a more future-proof approach (since window class names can change), use Windows UI Automation to locate the touch keyboard. This is Microsoft’s official way to interact with UWP apps:
uses Winapi.Windows, System.SysUtils, UIAutomationClient, UIAutomationTypes; function GetKeyboardRectViaUIA: TRect; var UIA: IUIAutomation; Condition: IUIAutomationCondition; RootElement: IUIAutomationElement; KeyboardElement: IUIAutomationElement; BoundingRect: tagRECT; begin ZeroMemory(@Result, SizeOf(TRect)); UIA := CoCUIAutomation.Create; RootElement := UIA.GetRootElement; // Look for the touch keyboard by its control type and name Condition := UIA.CreateAndCondition( UIA.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_PaneControlTypeId), UIA.CreatePropertyCondition(UIA_NamePropertyId, 'Touch Keyboard') ); KeyboardElement := RootElement.FindFirst(TreeScope_Descendants, Condition); if Assigned(KeyboardElement) then begin KeyboardElement.Get_CurrentBoundingRectangle(BoundingRect); Result := BoundingRect; end; end;
This method doesn’t rely on window handles or class names, so it’s less likely to break with Windows 10 updates.
内容的提问来源于stack exchange,提问作者Den




