You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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:

  1. First, get the TabTip.exe process ID via its main window.
  2. 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:

  1. Get the input box’s screen position using GetWindowRect.
  2. Calculate the new position for your panel so the input box sits above the keyboard (add a small buffer like 10px for usability).
  3. 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

火山引擎 最新活动