TL; DR: как мне переопределить функциональность горячих клавиш Windows или, по крайней мере, выполнить действие ДО того, как это произойдет? Кажется, что глобальные перехваты не настолько быстры, чтобы происходить раньше.
У меня возникла небольшая проблема с некоторым кодом, над которым я работаю ... Я пытаюсь переопределить естественное поведение Windows при нажатии CTRL + V вместо вставки из буфера обмена, я бы хотел, чтобы содержимое буфера обмена должно быть заменено содержимым моей программы.
ОБРАТИТЕ ВНИМАНИЕ, ЧТО ЭТО НЕОБХОДИМО ПРИМЕНИТЬ ДЛЯ ВСТАВКИ ДЕЙСТВИЙ ВНЕ МОЕЙ ПРОГРАММЫ, ТАКЖЕ, ПОСЛЕ ИСПОЛЬЗОВАНИЯ GLOBAL HOOKS
ниже приведен фрагмент того, как я в настоящее время пытаюсь «перехватить» горячие клавиши вставки, и объяснение, почему это не работает:
Здесь я определяю свои глобальные хуки:
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);
public struct keyboardHookStruct {
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
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;
#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;
#endregion
#region Events
/// <summary>
/// Occurs when one of the hooked keys is held
/// </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() {
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();
Dispose();
}
#endregion
#region Public Methods
/// <summary>
/// Installs the global hook
/// </summary>
public void hook() {
IntPtr hInstance = LoadLibrary("User32");
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0);
}
public void Dispose()
{
try { unhook(); }
catch (Exception e)
{ }
}
/// <summary>
/// Uninstalls the global hook
/// </summary>
public void unhook() {
UnhookWindowsHookEx(hhook);
}
/// <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) {
Keys key = (Keys)lParam.vkCode;
if (HookedKeys.Contains(key)) {
KeyEventArgs kea = new KeyEventArgs(key);
if ((wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && (KeyDown != null)) {
KeyDown(this, kea) ;
} else if ((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) && (KeyUp != null)) {
KeyUp(this, kea);
}
if (kea.Handled)
return 1;
}
}
return CallNextHookEx(hhook, code, wParam, ref lParam);
}
#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
}
И вот основной код, о котором идет речь:
globalKeyboardHook gkh = new globalKeyboardHook();
bool Vpressed;
bool controlIsUp;
void gkh_KeyUp(object sender, KeyEventArgs e)
{
//Control was released, handle any hotkeys that need handling
if(e.KeyCode.ToString()=="LControlKey" || e.KeyCode.ToString()=="RControlKey")
{
//scan paste
if(Vpressed && !controlIsUp)
{
Clipboard.SetDataObject(myTextLine);
Vpressed=false;
controlIsUp = true;
}
}
if (e.KeyCode.ToString() == "V")
{
if(controlIsUp)Vpressed=false;
else Vpressed=true;
}
}
void gkh_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode.ToString()=="LControlKey" || e.KeyCode.ToString()=="RControlKey")
{
controlIsUp=false;
}
}
Итак, что я вижу, это то, что система вставляет то, что находится в буфере обмена, ПЕРЕД вызовом «Clipboard.setDataObjec (...)», когда я хочу, чтобы содержимое буфера обмена было УСТАНОВЛЕНО до того, как система их вставит.
Кроме того, моим следующим шагом было бы определить количество нажатий V до того, как CTRL будет отпущен, и вставить другое сообщение, основанное на этом (например, CTL + V вставляет «HI FRIEND», CTRL + V + V вставляет «TEST TEST 2» и CTRL + V + V + V вставляет 'вот третий фрагмент')
Итак, в заключение мой вопрос: как я могу реструктурировать это, чтобы оно работало так, как ожидалось? есть ли что-то более эффективное / имеющее более высокий приоритет для хуков, которые я могу использовать, которые срабатывают до CTRL + V? Есть ли способ «принудительно» вставить пасту из буфера обмена (т.е. я постоянно очищаю буфер обмена, и когда вызывается ловушка, я принудительно помещаю данные в буфер обмена и вставляю их в курсор) или есть способ изменить свое текущее решение на быть более стойким?