記念日更新前のWindows 8およびWindows 10では、起動することでタッチキーボードを表示できました。
C:\Program Files\Common Files\Microsoft shared\ink\TabTip.exe
Windows 10 Anniversaryアップデートでは動作しなくなりました。 TabTip.exe
プロセスは実行中ですが、キーボードは表示されません。
プログラムで表示する方法はありますか?
[〜#〜] update [〜#〜]
回避策が見つかりました-システムトレイのタッチキーボードアイコンを偽のマウスクリックします。これはDelphiのコードです
// Find tray icon window
function FindTrayButtonWindow: THandle;
var
ShellTrayWnd: THandle;
TrayNotifyWnd: THandle;
begin
Result := 0;
ShellTrayWnd := FindWindow('Shell_TrayWnd', nil);
if ShellTrayWnd > 0 then
begin
TrayNotifyWnd := FindWindowEx(ShellTrayWnd, 0, 'TrayNotifyWnd', nil);
if TrayNotifyWnd > 0 then
begin
Result := FindWindowEx(TrayNotifyWnd, 0, 'TIPBand', nil);
end;
end;
end;
// Post mouse click messages to it
TrayButtonWindow := FindTrayButtonWindow;
if TrayButtonWindow > 0 then
begin
PostMessage(TrayButtonWindow, WM_LBUTTONDOWN, MK_LBUTTON, $00010001);
PostMessage(TrayButtonWindow, WM_LBUTTONUP, 0, $00010001);
end;
更新2
私が見つけたもう1つのことは、TabTip.exeを起動するとタッチキーボードが表示されるときにこのレジストリキーを設定すると古い機能が復元されることです
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\TabletTip\1.7\EnableDesktopModeAutoInvoke=1
OK、ユーザーがシステムトレイでそのボタンを押したときにExplorerが行うことをリバースエンジニアリングしました。
基本的に、ドキュメント化されていないインターフェイスITipInvocation
のインスタンスを作成し、Toggle(HWND)
メソッドを呼び出して、デスクトップウィンドウを引数として渡します。名前が示すように、メソッドは現在の状態に応じてキーボードを表示または非表示にします。
注意してください ExplorerはボタンがクリックされるたびにITipInvocation
のインスタンスを作成します。したがって、インスタンスをキャッシュすべきではないと考えています。また、Explorerは取得したインスタンスでRelease()
を呼び出さないことに気付きました。私はCOMにあまり詳しくありませんが、これはバグのように見えます。
これをWindows 8.1、Windows 10およびWindows 10 Anniversary Editionでテストしましたが、完全に機能します。これは明らかに、いくつかのエラーチェックが欠けているCの最小限の例です。
#include <initguid.h>
#include <Objbase.h>
#pragma hdrstop
// 4ce576fa-83dc-4F88-951c-9d0782b4e376
DEFINE_GUID(CLSID_UIHostNoLaunch,
0x4CE576FA, 0x83DC, 0x4f88, 0x95, 0x1C, 0x9D, 0x07, 0x82, 0xB4, 0xE3, 0x76);
// 37c994e7_432b_4834_a2f7_dce1f13b834b
DEFINE_GUID(IID_ITipInvocation,
0x37c994e7, 0x432b, 0x4834, 0xa2, 0xf7, 0xdc, 0xe1, 0xf1, 0x3b, 0x83, 0x4b);
struct ITipInvocation : IUnknown
{
virtual HRESULT STDMETHODCALLTYPE Toggle(HWND wnd) = 0;
};
int WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
HRESULT hr;
hr = CoInitialize(0);
ITipInvocation* tip;
hr = CoCreateInstance(CLSID_UIHostNoLaunch, 0, CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER, IID_ITipInvocation, (void**)&tip);
tip->Toggle(GetDesktopWindow());
tip->Release();
return 0;
}
C#バージョンも同様です。
class Program
{
static void Main(string[] args)
{
var uiHostNoLaunch = new UIHostNoLaunch();
var tipInvocation = (ITipInvocation)uiHostNoLaunch;
tipInvocation.Toggle(GetDesktopWindow());
Marshal.ReleaseComObject(uiHostNoLaunch);
}
[ComImport, Guid("4ce576fa-83dc-4F88-951c-9d0782b4e376")]
class UIHostNoLaunch
{
}
[ComImport, Guid("37c994e7-432b-4834-a2f7-dce1f13b834b")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface ITipInvocation
{
void Toggle(IntPtr hwnd);
}
[DllImport("user32.dll", SetLastError = false)]
static extern IntPtr GetDesktopWindow();
}
更新: @EugeneKコメントごとに、tabtip.exe
は問題のCOMコンポーネントのCOMサーバーであるため、コードがREGDB_E_CLASSNOTREG
、おそらくtabtip.exe
そしてさらに試みる。
私も同じ問題を抱えていました。かなりの時間と頭痛がかかりましたが、AlexeiとTorvinのおかげで、ついにWin 10 1709で動作するようになりました。可視性チェックは困難でした。 OSKlib Nugetが更新される可能性があります。完全なサロチオンを要約しましょう(私のコードには今、不必要な行があります)。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.ComponentModel;
using Osklib.Interop;
using System.Runtime.InteropServices;
using System.Threading;
namespace OSK
{
public static class OnScreenKeyboard
{
static OnScreenKeyboard()
{
var version = Environment.OSVersion.Version;
switch (version.Major)
{
case 6:
switch (version.Minor)
{
case 2:
// Windows 10 (ok)
break;
}
break;
default:
break;
}
}
private static void StartTabTip()
{
var p = Process.Start(@"C:\Program Files\Common Files\Microsoft Shared\ink\TabTip.exe");
int handle = 0;
while ((handle = NativeMethods.FindWindow("IPTIP_Main_Window", "")) <= 0)
{
Thread.Sleep(100);
}
}
public static void ToggleVisibility()
{
var type = Type.GetTypeFromCLSID(Guid.Parse("4ce576fa-83dc-4F88-951c-9d0782b4e376"));
var instance = (ITipInvocation)Activator.CreateInstance(type);
instance.Toggle(NativeMethods.GetDesktopWindow());
Marshal.ReleaseComObject(instance);
}
public static void Show()
{
int handle = NativeMethods.FindWindow("IPTIP_Main_Window", "");
if (handle <= 0) // nothing found
{
StartTabTip();
Thread.Sleep(100);
}
// on some devices starting TabTip don't show keyboard, on some does ¯\_(ツ)_/¯
if (!IsOpen())
{
ToggleVisibility();
}
}
public static void Hide()
{
if (IsOpen())
{
ToggleVisibility();
}
}
public static bool Close()
{
// find it
int handle = NativeMethods.FindWindow("IPTIP_Main_Window", "");
bool active = handle > 0;
if (active)
{
// don't check style - just close
NativeMethods.SendMessage(handle, NativeMethods.WM_SYSCOMMAND, NativeMethods.SC_CLOSE, 0);
}
return active;
}
public static bool IsOpen()
{
return GetIsOpen1709() ?? GetIsOpenLegacy();
}
[DllImport("user32.dll", SetLastError = false)]
private static extern IntPtr FindWindowEx(IntPtr parent, IntPtr after, string className, string title = null);
[DllImport("user32.dll", SetLastError = false)]
private static extern uint GetWindowLong(IntPtr wnd, int index);
private static bool? GetIsOpen1709()
{
// if there is a top-level window - the keyboard is closed
var wnd = FindWindowEx(IntPtr.Zero, IntPtr.Zero, WindowClass1709, WindowCaption1709);
if (wnd != IntPtr.Zero)
return false;
var parent = IntPtr.Zero;
for (;;)
{
parent = FindWindowEx(IntPtr.Zero, parent, WindowParentClass1709);
if (parent == IntPtr.Zero)
return null; // no more windows, keyboard state is unknown
// if it's a child of a WindowParentClass1709 window - the keyboard is open
wnd = FindWindowEx(parent, IntPtr.Zero, WindowClass1709, WindowCaption1709);
if (wnd != IntPtr.Zero)
return true;
}
}
private static bool GetIsOpenLegacy()
{
var wnd = FindWindowEx(IntPtr.Zero, IntPtr.Zero, WindowClass);
if (wnd == IntPtr.Zero)
return false;
var style = GetWindowStyle(wnd);
return style.HasFlag(WindowStyle.Visible)
&& !style.HasFlag(WindowStyle.Disabled);
}
private const string WindowClass = "IPTip_Main_Window";
private const string WindowParentClass1709 = "ApplicationFrameWindow";
private const string WindowClass1709 = "Windows.UI.Core.CoreWindow";
private const string WindowCaption1709 = "Microsoft Text Input Application";
private enum WindowStyle : uint
{
Disabled = 0x08000000,
Visible = 0x10000000,
}
private static WindowStyle GetWindowStyle(IntPtr wnd)
{
return (WindowStyle)GetWindowLong(wnd, -16);
}
}
[ComImport]
[Guid("37c994e7-432b-4834-a2f7-dce1f13b834b")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface ITipInvocation
{
void Toggle(IntPtr hwnd);
}
internal static class NativeMethods
{
[DllImport("user32.dll", EntryPoint = "FindWindow")]
internal static extern int FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", EntryPoint = "SendMessage")]
internal static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);
[DllImport("user32.dll", EntryPoint = "GetDesktopWindow", SetLastError = false)]
internal static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll", EntryPoint = "GetWindowLong")]
internal static extern int GetWindowLong(int hWnd, int nIndex);
internal const int GWL_STYLE = -16;
internal const int GWL_EXSTYLE = -20;
internal const int WM_SYSCOMMAND = 0x0112;
internal const int SC_CLOSE = 0xF060;
internal const int WS_DISABLED = 0x08000000;
internal const int WS_VISIBLE = 0x10000000;
}
}
私が働いていることがわかった唯一の解決策は、回答1で述べたようにPostMessageを送信することです。誰かがそれを必要とする場合のC#バージョンです。
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr FindWindow(string sClassName, string sAppName);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string lclassName, string windowTitle);
[DllImport("User32.Dll", EntryPoint = "PostMessageA")]
static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
var trayWnd = FindWindow("Shell_TrayWnd", null);
var nullIntPtr = new IntPtr(0);
if (trayWnd != nullIntPtr)
{
var trayNotifyWnd = FindWindowEx(trayWnd, nullIntPtr, "TrayNotifyWnd", null);
if (trayNotifyWnd != nullIntPtr)
{
var tIPBandWnd = FindWindowEx(trayNotifyWnd, nullIntPtr, "TIPBand", null);
if (tIPBandWnd != nullIntPtr)
{
PostMessage(tIPBandWnd, (UInt32)WMessages.WM_LBUTTONDOWN, 1, 65537);
PostMessage(tIPBandWnd, (UInt32)WMessages.WM_LBUTTONUP, 1, 65537);
}
}
}
public enum WMessages : int
{
WM_LBUTTONDOWN = 0x201,
WM_LBUTTONUP = 0x202,
WM_KEYDOWN = 0x100,
WM_KEYUP = 0x101,
WH_KEYBOARD_LL = 13,
WH_MOUSE_LL = 14,
}
Windows 10 Anniversary Updateでタッチキーボードを開こうとすると、4つの状況が検出されます
1-何もしない
2 + 3-COMを介したアクティベーション
4-最も興味深いシナリオ。一部のデバイスでは、TabTipプロセスを開始するとタッチキーボードが開きます。そのため、TabTipプロセスを開始し、ウィンドウ「IPTIP_Main_Window」が表示されるのを待ち、可視性を確認し、必要に応じてCOMを介してアクティブ化する必要があります。
私は自分のプロジェクト用の小さなライブラリを作成します。それを使用できます- osklib
Windows 10 Anniversary Updateでタッチキーボードがどのように表示されるかについては、まだ謎が残っています。私は実際にまったく同じ問題を抱えていますが、ここに私が見つけた最新の情報があります:
Windows 10 1607は、デスクトップとタブレットの2つのモードで動作します。デスクトップモードでは、TabTip.exeを呼び出すことができますが、表示されません。タブレットモードでは、すべてが正常に動作します。TabTip.exeは呼び出されると表示されます。 100%の回避策は、コンピューターをタブレットモードに設定することですが、デスクトップ/ラップトップをタブレットモードで動作させたい人はいますか?とにかく私ではありません!
「EnableDesktopModeAutoInvoke
」キー(HKCU、DWORDを1に設定)を使用できます。また、1607を実行している一部のコンピューターでは、デスクトップモードでうまく機能しました。しかし、いくつかの未知の理由により、HPタッチパッドで動作しません。
このレジストリ値は、Windowsパラメーターの[接続されたキーボードがない場合、デスクトップモードでタッチキーボードを表示する]オプションであることに注意してください
これまでのところ、4台の異なるコンピューターでテストしましたが、すべてで正常に機能するものを取得することはできません...
IValueProvider/ITextProviderをコントロールに実装することは、ここで説明するように、これを達成する正しい方法です。 https://stackoverflow.com/a/43886052/118495
問題は、Windows OSの設定にあるようです。開発中のアプリで同じ問題に直面しました。 Windows 8および10(更新前)では、キーボードを呼び出すコードは正常に機能しましたが、更新後は機能しませんでした。 この記事 を読んだ後、次のことを行いました。
Win + Iを押して設定アプリを開きます
[デバイス]> [入力]をクリックしました
「デバイスにキーボードが接続されていないときにウィンドウアプリでタッチキーボードを自動的に表示する」をオンにしました。
そのキーボードがWindows 10でも表示され始めた直後。
次のコードは最新のMS Apiを使用するため、常に機能します
私はそれをdll(Delphiプロジェクトに必要)に入れましたが、それはプレーンC
キーボードサイズの取得とアプリケーションレイアウトの調整にも役立ちます
//*******************************************************************
//
// RETURNS KEYBOARD RECTANGLE OR EMPTY ONE IF KEYBOARD IS NOT VISIBLE
//
//*******************************************************************
RECT __stdcall GetKeyboardRect()
{
IFrameworkInputPane *inputPane = NULL;
RECT prcInputPaneScreenLocation = { 0,0,0,0 };
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (SUCCEEDED(hr))
{
hr = CoCreateInstance(CLSID_FrameworkInputPane, NULL, CLSCTX_INPROC_SERVER, IID_IFrameworkInputPane, (LPVOID*)&inputPane);
if (SUCCEEDED(hr))
{
hr=inputPane->Location(&prcInputPaneScreenLocation);
if (!SUCCEEDED(hr))
{
}
inputPane->Release();
}
}
CoUninitialize();
return prcInputPaneScreenLocation;
}
この方法を使用します。
Osk.batファイルを作成し、プログラムフォルダーの下に保存します。 _C:\My Software\osk.bat
_
このosk.batに次のコマンドを入力します。
_"C:\Program Files\Common Files\Microsoft Shared\Ink\Tabtip.exe"
_
Windowsスクリプトを使用して、このbatファイルを実行します
oWSH = CREATEOBJECT("wscript.Shell")
oWSH.Run("osk.bat", 0, .T.)