C # глобальное переопределение команды CTRL + V с использованием глобальной клавиатуры

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? Есть ли способ «принудительно» вставить пасту из буфера обмена (т.е. я постоянно очищаю буфер обмена, и когда вызывается ловушка, я принудительно помещаю данные в буфер обмена и вставляю их в курсор) или есть способ изменить свое текущее решение на быть более стойким?


person Medic3000    schedule 14.01.2016    source источник
comment
Нарушение ожидаемого поведения по умолчанию - это настолько зло, что ... ну, Дарт. Конечным пользователям это, вероятно, не понравится. В любом случае поведение буфера обмена не привязано к хукам клавиатуры, это основная операция, которая активируется множеством разных способов и, очевидно, имеет более высокий приоритет, чем любые хуки.   -  person Panagiotis Kanavos    schedule 14.01.2016
comment
Я намеренно пытаюсь удалить встроенные функции вырезания / копирования / вставки. приложение будет использоваться только мной в моих проектах графического дизайна. Конечным результатом является возможность иметь 2 или 3 «виртуальных буфера обмена», которыми я управляю, таким образом, я могу удерживать несколько изображений и просто CtrlV или CtrlVV, чтобы вставить одно или другое. Цель моего поста - узнать, КАК подключиться к этим операциям, если ключевые ловушки имеют слишком низкий приоритет. Вырезать / Копировать уже работают, это просто вставка вызывает у меня проблемы   -  person Medic3000    schedule 14.01.2016
comment
Попробуйте переопределить ProcessDialogKey. Если он нажимает CTRL-V, используйте свой код и верните Keys.Alt или что-то, что ничего не сделает. Если нет, верните base.ProcessDialogKey. Я делаю это для alt-f4 в своей программе, и это хорошо работает.   -  person Aaron    schedule 14.01.2016
comment
пожалуйста, предоставьте в качестве ответа пример небольшого кода / псевдокода, чтобы я мог дать вам ответы на вопросы   -  person Medic3000    schedule 14.01.2016
comment
Я пробовал это, в настоящее время это не сработает, так как это мешает моему переопределению для вырезания / копирования   -  person Medic3000    schedule 14.01.2016
comment
Зачем настаивать на использовании CTRL + V, которые Windows обрабатывает перед любыми вашими хуками? Что-то вроде CTRL + SHIFT + V сделало бы эту работу с гораздо меньшими усилиями.   -  person DLCross    schedule 14.01.2016
comment
Возможно, это вам поможет: ссылка   -  person    schedule 14.01.2016
comment
почему нет? как товарищ-программист вы должны знать, где есть воля, есть способ! Это забавный небольшой проект по усовершенствованию встроенной функциональности Windows. Конечно, я мог бы использовать другую комбинацию клавиш, но то же самое можно было сказать о Христофоре Колумбе! Конечно, он мог бы пересечь Атлантику, но ЛЕГЧЕ просто обогнуть Африку! если бы не его новаторство, мы жили бы в половине мира, в котором живем   -  person Medic3000    schedule 14.01.2016
comment
О, я, конечно, понимаю. Я понимаю желание подчинить Windows своей воле. Однако опыт подсказывает мне, что если что-то кажется намного сложнее, чем должно быть, вероятно, это сложно по какой-то причине. Я бы обошел эту стену, а не сквозь нее.   -  person DLCross    schedule 14.01.2016
comment
Если бы у Германии был такой менталитет, Запад и Восток никогда бы не объединились! где твоя дерзость!   -  person Medic3000    schedule 14.01.2016


Ответы (1)


Дайте этому шанс. Поскольку это привязка клавиш к вашему глобальному хуку, она должна работать. По сути, вы делаете это вместо нажатия / нажатия клавиши. Измените код, чтобы он смотрел на ctrl-v вместо alt-f4, и там, где у меня есть мое окно сообщений, добавьте свой собственный код ..

protected override bool ProcessDialogKey(Keys keyData)
{

if (keyData == Keys.Alt + Keys.F4) {
    string s = HelixMessageBox.ShowInptDlg("Yeahhhhh, right.  You can shut me down like a normal person, tyvm.", CustomMessageBoxButtons.OK);
    return Keys.Alt;
    }
return base.ProcessDialogKey(keyData);
}
person Aaron    schedule 14.01.2016
comment
вы настоящий MVP, НО будет ли это работать с нажатиями клавиш вне моей программы? т.е. как в фотошопе или в текстовом редакторе? твой где-нибудь работает? - person Medic3000; 14.01.2016
comment
Рад, что смог помочь. Убедитесь, что вы протестировали это вне вашей программы ... Сосредоточьтесь на другой программе и убедитесь. В противном случае хорошо :) - person Aaron; 14.01.2016
comment
похоже, что это не сработает, если в моем приложении не нажимаются клавиши. какие-либо предложения? Похоже, ProcessDialogKey () - это метод Winforms ... - person Medic3000; 14.01.2016
comment
Хм. Возможно, переместите его в класс хуков. Я никогда не делал этого снаружи - я просто догадываюсь ... - person Aaron; 14.01.2016
comment
не может быть в классе перехвата, поскольку это метод класса форм Windows, поэтому он работает только в приложении winForm (при условии, что он называется ProcessDialougKey, поскольку он смотрит только на нажатия клавиш в этом диалоговом окне процесса) - person Medic3000; 14.01.2016
comment
Вероятно, он все еще понадобится вам снаружи. Но, возможно, вы могли бы остановить перехватчик окон и запустить событие в главном приложении? Опять же, просто мозговой штурм с вами - никаких гарантий. - person Aaron; 14.01.2016
comment
Я думаю, что мне может быть лучше вручную манипулировать буфером обмена, поэтому, когда нажимается CTRL, я очищаю буфер обмена, таким образом, если нажать V, он ничего не сделает, тогда после освобождения управления я могу найти контр / булевский флаг, указывающий V был нажат, и если это так, поместите мои данные в буфер обмена и заставьте систему вставить туда, где находится курсор ... единственная проблема заключается в том, как я могу `` принудительно вставить '' - person Medic3000; 14.01.2016
comment
Я думаю, что KeyCode тоже из Windows.Forms ... Он использует KeyCode в классе? - person Aaron; 14.01.2016
comment
но у меня есть глобальные перехватчики в этих ключах, тогда он вставляет указанные перехватчики в коды клавиш: private void Form1_Load (object sender, EventArgs e) {gkh.HookedKeys.Add (Keys.LControlKey); gkh.HookedKeys.Add (Keys.RControlKey); gkh.HookedKeys.Add (Keys.C); gkh.HookedKeys.Add (Keys.X); gkh.HookedKeys.Add (Keys.V); gkh.KeyDown + = новый обработчик событий KeyEventHandler (gkh_KeyDown); gkh.KeyUp + = новый KeyEventHandler (gkh_KeyUp); } - person Medic3000; 14.01.2016
comment
Попался. Возможно возврат 1; после вызова событий «вверх» и «вниз»? Я считаю, что возвращение 1 говорит о том, что вы его обработали. Возможно, он все еще обрабатывается, потому что в этих блоках нет возврата? Нет гарантии. Или какие настройки обрабатываются? Это установлено снаружи? Это проблема, если это так ... запуск / отключение огня после того, как Windows уже имеет ключи .. Возможно, вам нужно просто обработать их как обработанные. Он в любом случае ударит по этим блокам, только если это ключ интереса, верно? - person Aaron; 14.01.2016
comment
Разъяснение. Проверьте свои ключи на предмет ключей, представляющих интерес в классе. Если это не интересующие клавиши, нажмите клавишу вверх / вниз, как сейчас. Если это интересующие ключи, тот же код, но возвращает 1 после событий нажатия / нажатия. - person Aaron; 14.01.2016