C# 是否有可能使插入符号在Word中的位置更新得更快?

C# 是否有可能使插入符号在Word中的位置更新得更快?,c#,ms-word,keyboard-hook,C#,Ms Word,Keyboard Hook,我正在使用一个低级键盘挂钩和GUITHREADINFO在Windows应用程序中获得插入符号在每个键盘上的位置 适用于除Word以外的几乎所有应用程序。由于某些原因,Word仅在经过任意延迟后更新插入符号的位置(这意味着位置将始终关闭一个或多个字符) 如果在获取位置之前等待几百毫秒(Thread.Sleep(400)),我就可以获得正确的位置,但这是我正在处理的应用程序的可用解决方案 有什么方法可以更快地获得正确的插入符号位置吗?通过强制Word呈现插入符号、使用特定于Word的功能、订阅Wor

我正在使用一个低级键盘挂钩和
GUITHREADINFO
在Windows应用程序中获得插入符号在每个键盘上的位置

适用于除Word以外的几乎所有应用程序。由于某些原因,Word仅在经过任意延迟后更新插入符号的位置(这意味着位置将始终关闭一个或多个字符)

如果在获取位置之前等待几百毫秒(
Thread.Sleep(400)
),我就可以获得正确的位置,但这是我正在处理的应用程序的可用解决方案

有什么方法可以更快地获得正确的插入符号位置吗?通过强制Word呈现插入符号、使用特定于Word的功能、订阅Word事件或其他完全不同的方式?

键盘挂钩类别:

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.ComponentModel;


namespace CS_test
{
    /// <summary>
    /// A class that manages a global low level keyboard hook
    /// </summary>
    public class GlobalKeyboardHook
    {
        #region Constant, Structure and Delegate Definitions
        /// <summary>
        /// defines the callback type for the hook
        /// </summary>
        public delegate int KeyboardHookProc(int code, int wParam, ref KeyboardHookStruct lParam);

        private static KeyboardHookProc _callbackDelegate;

        public struct KeyboardHookStruct // C++ KBDLLHOOKSTRUCT 
        {
            public int vkCode;
            public int scanCode;
            public int flags;
            public int time;
            public int dwExtraInfo;
        }

        const int WH_KEYBOARD = 2;
        const int WH_KEYBOARD_LL = 13;
        const int WM_KEYDOWN = 0x100;
        const int WM_KEYUP = 0x101;
        const int WM_SYSKEYDOWN = 0x104;
        const int WM_SYSKEYUP = 0x105;
        const int LLKHF_INJECTED = 0x10;
        #endregion

        #region Instance Variables
        /// <summary>
        /// The collections of keys to watch for
        /// </summary>
        public List<Keys> HookedKeys = new List<Keys>();
        /// <summary>
        /// Handle to the hook, need this to unhook and call the next hook
        /// </summary>
        IntPtr hhook = IntPtr.Zero;

        private bool _ignoreInjected = false;
        private bool _shiftHeld = false;
        private bool _ctrlHeld = false;
        private bool _altHeld = false;

        #endregion

        #region Events
        /// <summary>
        /// Occurs when one of the hooked keys is pressed
        /// </summary>
        public event KeyEventHandler KeyDown;
        /// <summary>
        /// Occurs when one of the hooked keys is released
        /// </summary>
        public event KeyEventHandler KeyUp;
        #endregion

        #region Constructors and Destructors
        /// <summary>
        /// Initializes a new instance of the <see cref="globalKeyboardHook"/> class and installs the keyboard hook.
        /// </summary>
        public GlobalKeyboardHook(bool ignoreInjected)
        {
            _ignoreInjected = ignoreInjected;
            Hook();
        }

        /// <summary>
        /// Releases unmanaged resources and performs other cleanup operations before the
        /// <see cref="globalKeyboardHook"/> is reclaimed by garbage collection and uninstalls the keyboard hook.
        /// </summary>
        ~GlobalKeyboardHook()
        {
            Unhook();
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Installs the global hook
        /// </summary>
        /// 

        private void Hook()
        {
            _callbackDelegate = new KeyboardHookProc(HookProc);
            //IntPtr hInstance = LoadLibrary("User32");
            //hhook = SetWindowsHookEx(WH_KEYBOARD_LL, callbackDelegate, hInstance, 0);
            hhook = SetWindowsHookEx(WH_KEYBOARD_LL, _callbackDelegate, IntPtr.Zero, 0);
            if (hhook == IntPtr.Zero) throw new Win32Exception();
        }

        /// <summary>
        /// Uninstalls the global hook
        /// </summary>
        public bool Unhook()
        {
            try
            {
                bool ok = UnhookWindowsHookEx(hhook);
                if (!ok) throw new Win32Exception();
                _callbackDelegate = null;
                return true;
            }
            catch (Exception) { }

            return false;
        }

        /// <summary>
        /// The callback for the keyboard hook
        /// </summary>
        /// <param name="code">The hook code, if it isn't >= 0, the function shouldn't do anyting</param>
        /// <param name="wParam">The event type</param>
        /// <param name="lParam">The keyhook event information</param>
        /// <returns></returns>
        /// 
        public int HookProc(int code, int wParam, ref KeyboardHookStruct lParam)
        {
            if (code >= 0)
            {
                // Continue only if injected keys are allowed or key is not injected
                if (!_ignoreInjected || (lParam.flags & LLKHF_INJECTED) == 0)
                {
                    // Determine modifiers
                    if (lParam.vkCode == 160 || lParam.vkCode == 161) // LShiftKey / RShiftKey
                    {
                        _shiftHeld = (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN);
                    }
                    else if (lParam.vkCode == 162 || lParam.vkCode == 163) // LControlKey / RControlKey
                    {
                        _ctrlHeld = (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN);
                    }
                    else if (lParam.vkCode == 164 || lParam.vkCode == 165) // LMenu / RMenu aka LAlt / RAlt
                    {
                        // We can also determine if Alt is held by checking if bit 5 (0-indexed) of lParam.flags is set
                        _altHeld = (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN);
                    }

                    Keys key = (Keys)lParam.vkCode;
                    if (HookedKeys.Contains(key))
                    {
                        Keys keyModified = key;
                        if (_shiftHeld) keyModified |= Keys.Shift;
                        if (_ctrlHeld) keyModified |= Keys.Control;
                        if (_altHeld) keyModified |= Keys.Alt;
                        KeyEventArgs kea = new KeyEventArgs(keyModified);

                        // Call keydown event handlers
                        if ((wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && (KeyDown != null))
                        {
                            KeyDown(this, kea);
                        }

                        // Call keyup event handlers
                        else if ((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) && (KeyUp != null))
                        {
                            KeyUp(this, kea);
                        }

                        // Check if any of the handlers have handled the event
                        if (kea.Handled)
                        {
                            return 1;
                        }
                    }
                }
            }

            try
            {
                // This can crash if Windows has decided to unhook the proc during this function
                return CallNextHookEx(hhook, code, wParam, ref lParam);
            }
            catch (Exception ex)
            {
                Console.WriteLine("GlobalKeyboardHook exception: " + ex.Message);
                return 0;
            }

        }
        #endregion

        #region DLL imports
        /// <summary>
        /// Sets the windows hook, do the desired event, one of hInstance or threadId must be non-null
        /// </summary>
        /// <param name="idHook">The id of the event you want to hook</param>
        /// <param name="callback">The callback.</param>
        /// <param name="hInstance">The handle you want to attach the event to, can be null</param>
        /// <param name="threadId">The thread you want to attach the event to, can be null</param>
        /// <returns>a handle to the desired hook</returns>
        [DllImport("user32.dll")]
        static extern IntPtr SetWindowsHookEx(int idHook, KeyboardHookProc callback, IntPtr hInstance, uint threadId);

        /// <summary>
        /// Unhooks the windows hook.
        /// </summary>
        /// <param name="hInstance">The hook handle that was returned from SetWindowsHookEx</param>
        /// <returns>True if successful, false otherwise</returns>
        [DllImport("user32.dll")]
        static extern bool UnhookWindowsHookEx(IntPtr hInstance);

        /// <summary>
        /// Calls the next hook.
        /// </summary>
        /// <param name="idHook">The hook id</param>
        /// <param name="nCode">The hook code</param>
        /// <param name="wParam">The wparam.</param>
        /// <param name="lParam">The lparam.</param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref KeyboardHookStruct lParam);

        /// <summary>
        /// Loads the library.
        /// </summary>
        /// <param name="lpFileName">Name of the library</param>
        /// <returns>A handle to the library</returns>
        [DllImport("kernel32.dll")]
        static extern IntPtr LoadLibrary(string lpFileName);
        #endregion
    }

    public class KeyboardHookLogEventArgs : EventArgs
    {
        public string Message;
        public KeyboardHookLogEventArgs(string message)
        {
            Message = message;
        }
    }
}
WPF主窗口:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace CS_test
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            GlobalKeyboardHook hook = new GlobalKeyboardHook(true);
            foreach (Keys key in Enum.GetValues(typeof(Keys))) // Add keys that KeyboardHook should listen for
            {
                if (key != Keys.Control && key != Keys.ControlKey)
                {
                    hook.HookedKeys.Add(key);
                }
            }
            hook.KeyUp += hook_KeyUp;
        }

        void hook_KeyUp(object sender, System.Windows.Forms.KeyEventArgs e)
        {
            CaretInfo info = CaretHelper.GetCaretPosition();
            Console.WriteLine(info.Left + ", " + info.Top);
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
使用System.Threading.Tasks;
使用System.Windows;
使用System.Windows.Controls;
使用System.Windows.Data;
使用System.Windows.Documents;
使用System.Windows.Forms;
使用System.Windows.Input;
使用System.Windows.Media;
使用System.Windows.Media.Imaging;
使用System.Windows.Navigation;
使用System.Windows.Shapes;
命名空间CS_测试
{
/// 
///MainWindow.xaml的交互逻辑
/// 
公共部分类主窗口:窗口
{
公共主窗口()
{
初始化组件();
GlobalKeyboardHook hook=新的GlobalKeyboardHook(true);
foreach(key-key-in-Enum.GetValues(typeof(Keys))//添加KeyboardHook应该侦听的键
{
if(key!=Keys.Control&&key!=Keys.ControlKey)
{
hook.HookedKeys.Add(键);
}
}
hook.KeyUp+=hook_KeyUp;
}
void hook_KeyUp(对象发送方,System.Windows.Forms.KeyEventArgs e)
{
CaretInfo info=CaretHelper.GetCaretPosition();
控制台写入线(info.Left+,“+info.Top);
}
}
}

试着连续快速输入3个字母-应该会生成一个所有三个键都具有相同坐标的日志。

这真是一个奇迹,因为微软已经很长时间没有在Office程序中使用GDI了。让他们的代码与试图这样破解的程序兼容一定是一场噩梦。损失是不可避免的,特别是2013年的流畅动画效果必然会导致这种问题。别这么做,这到底是为什么?另外,你能指出其他的替代方案吗?这是一个奇迹,因为微软已经很长时间没有在Office程序中使用GDI了。让他们的代码与试图这样破解的程序兼容一定是一场噩梦。损失是不可避免的,特别是2013年的流畅动画效果必然会导致这种问题。别这么做,这到底是为什么?另外,你能指出其他选择吗?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace CS_test
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            GlobalKeyboardHook hook = new GlobalKeyboardHook(true);
            foreach (Keys key in Enum.GetValues(typeof(Keys))) // Add keys that KeyboardHook should listen for
            {
                if (key != Keys.Control && key != Keys.ControlKey)
                {
                    hook.HookedKeys.Add(key);
                }
            }
            hook.KeyUp += hook_KeyUp;
        }

        void hook_KeyUp(object sender, System.Windows.Forms.KeyEventArgs e)
        {
            CaretInfo info = CaretHelper.GetCaretPosition();
            Console.WriteLine(info.Left + ", " + info.Top);
        }
    }
}