如何在Windows中嵌入tabtip.exe

Mc *_*vin 8 c# wpf window on-screen-keyboard

我试图将osk嵌入到wpf窗口或用户控件中,我发现下面的代码并且它适用于记事本但是对于tabtip.exe,它说它没有图形界面?

WaitForInputIdle失败.这可能是因为该过程没有图形界面.

我尝试让它休眠一段时间而不是调用waitForInputIdle方法,但它抛出了另一个异常:

进程已退出,因此无法获取所请求的信息.

但在我的任务管理器中,我仍然可以看到TabTip.exe正在运行.

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private System.Windows.Forms.Panel _panel;
        private Process _process;

        public MainWindow()
        {
            InitializeComponent();
            _panel = new System.Windows.Forms.Panel();
            windowsFormsHost1.Child = _panel;
        }

        [DllImport("user32.dll")]
        private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

        [DllImport("user32.dll", SetLastError = true)]
        private static extern int GetWindowLong(IntPtr hWnd, int nIndex);

        [DllImport("user32")]
        private static extern IntPtr SetParent(IntPtr hWnd, IntPtr hWndParent);

        [DllImport("user32")]
        private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);

        private const int SWP_NOZORDER = 0x0004;
        private const int SWP_NOACTIVATE = 0x0010;
        private const int GWL_STYLE = -16;
        private const int WS_CAPTION = 0x00C00000;
        private const int WS_THICKFRAME = 0x00040000;


        protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
        {
            base.OnClosing(e);
            if (_process != null)
            {
                _process.Refresh();
                _process.Close();
            }
        }

        private void ResizeEmbeddedApp()
        {
            if (_process == null)
                return;

            SetWindowPos(_process.MainWindowHandle, IntPtr.Zero, 0, 0, (int)_panel.ClientSize.Width, (int)_panel.ClientSize.Height, SWP_NOZORDER | SWP_NOACTIVATE);
        }

        protected override Size MeasureOverride(Size availableSize)
        {
            Size size = base.MeasureOverride(availableSize);
            ResizeEmbeddedApp();
            return size;
        }

        private void button1_Click_1(object sender, RoutedEventArgs e)
        {
            button1.Visibility = Visibility.Hidden;
            ProcessStartInfo psi = new ProcessStartInfo("C:\\Program Files\\Common Files\\microsoft shared\\ink\\TabTip.exe");
            _process = Process.Start(psi);

            Thread.Sleep(500);
            //_process.WaitForInputIdle();

            SetParent(_process.MainWindowHandle, _panel.Handle);

            // remove control box
            int style = GetWindowLong(_process.MainWindowHandle, GWL_STYLE);
            style = style & ~WS_CAPTION & ~WS_THICKFRAME;
            SetWindowLong(_process.MainWindowHandle, GWL_STYLE, style);

            // resize embedded application & refresh
            ResizeEmbeddedApp();
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:灵感来自rene的评论,我试图获得如下窗口ptr并使用spy ++来验证FindWindow提供的地址是否指向正确的窗口,但它仍然没有移动:

 IntPtr KeyboardWnd = FindWindow("IPTip_Main_Window", null);


 int style = GetWindowLong(KeyboardWnd, GWL_STYLE);
 style = style & ~WS_CAPTION & ~WS_THICKFRAME;
 SetWindowLong(KeyboardWnd, GWL_STYLE, style);
 SetWindowPos(KeyboardWnd, IntPtr.Zero, 0, 0, (int)_panel.ClientSize.Width, (int)_panel.ClientSize.Height, SWP_NOZORDER | SWP_NOACTIVATE);
Run Code Online (Sandbox Code Playgroud)

编辑2:我的第一个想法是标签提示无法调整大小,但是当我尝试将窗口移动到两个不同的屏幕时,我注意到了一种行为,它会调整大小以适应屏幕大小,所以我肯定在那里必须是一种调整大小的方法,所以我开始使用spy ++(x64)来检查:

窗口日志

编辑3:在使用user32 api修改abit并且没有进展之后,我尝试使用内存扫描程序扫描tabtip的x和y位置并更改它,但是,在重新启动触发之前它不会令人耳目一新,我是想知道沿着这条路走下去的可行性.

Ank*_*jay 1

您可以尝试在 STA 线程中运行您的句柄代码吗?我在本机窗口中遇到了类似的问题,我已使用 STA 线程解决了该问题。

var thread = new Thread(() => {
          // Your code here
});
thread.TrySetApartmentState(ApartmentState.STA);
thread.Start();
Run Code Online (Sandbox Code Playgroud)