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);
}
}
}