Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
326 views
in Technique[技术] by (71.8m points)

c# - Excel VSTO Key hook

I want to change cell color when Enter key is pressed. Everything is fine except, that code doesn't work in active Excel 2013, only when it is in background. How can I solve this problem?

using MouseKeyboardActivityMonitor;
using MouseKeyboardActivityMonitor.WinApi;

        public KeyboardHookListener k_keyListener;
        private void ThisAddIn_Startup(object sender, System.EventArgs e)
        {
            k_keyListener = new KeyboardHookListener(new GlobalHooker());
            k_keyListener.Enabled = true;
            k_keyListener.KeyDown += new KeyEventHandler(k_keyListener_KeyDown);
        }

        void k_keyListener_KeyDown(object sender, KeyEventArgs e)
        {   
            if(e.KeyValue == 13)
            { 
            Excel.Range rng2 = this.Application.get_Range("A1");
            rng2.Font.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Red);
            }
        }

Also I have tried to do like this, but same, works only if Excel 2013 is in background....

 public partial class ThisAddIn
{
    private const int WH_KEYBOARD_LL = 13;
    private const int WM_KEYDOWN = 0x0100;

    private static IntPtr hookId = IntPtr.Zero;
    private delegate IntPtr HookProcedure(int nCode, IntPtr wParam, IntPtr lParam);
    private static HookProcedure procedure = HookCallback;

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr GetModuleHandle(string lpModuleName);

    [DllImport("user32.dll", SetLastError = true)]
    private static extern bool UnhookWindowsHookEx(IntPtr hhk);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr SetWindowsHookEx(int idHook, HookProcedure lpfn, IntPtr hMod, uint dwThreadId);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);



    private void ThisAddIn_Startup(object sender, System.EventArgs e)
    {
        hookId = SetHook(procedure);

    }



    private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
    {
        UnhookWindowsHookEx(hookId);

    }
    private static IntPtr SetHook(HookProcedure procedure)
    {
        using (Process process = Process.GetCurrentProcess())
        using (ProcessModule module = process.MainModule)
            return SetWindowsHookEx(WH_KEYBOARD_LL, procedure, GetModuleHandle(module.ModuleName), 0);
    }

    private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
        {
            int pointerCode = Marshal.ReadInt32(lParam);
            string pressedKey = ((Keys)pointerCode).ToString();

            //Do some sort of processing on key press
            var thread = new Thread(() => { MessageBox.Show(pressedKey); });
            thread.Start();
        }
        return CallNextHookEx(hookId, nCode, wParam, lParam);
    }
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The main mistake you made in your code is using WH_KEYBOARD_LL. It will not work well in Excel, use WH_KEYBOARD instead. The code below allows you to catch any key pressed, check modifiers and invoke some action.

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Microsoft.Office.Interop.Excel;

namespace SimpleExcelAddIn {
    static class ShortcutManager {
        delegate int LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
        static readonly LowLevelKeyboardProc _proc = HookCallback;
        static IntPtr _hookID = IntPtr.Zero;
        const int WH_KEYBOARD = 2;
        const int HC_ACTION = 0;
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        static extern bool UnhookWindowsHookEx(IntPtr idHook);
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
        [DllImport("user32.dll")]
        static extern short GetKeyState(int nVirtKey);

        static bool _keyHookingStarted;
        public static void Start() {
            if (!_keyHookingStarted) {
#pragma warning disable 0618
                _hookID = SetWindowsHookEx(WH_KEYBOARD, _proc, IntPtr.Zero, (uint) AppDomain.GetCurrentThreadId());
#pragma warning restore 0618
                _keyHookingStarted = true;
            }
        }
        public static void Stop() {
            if (_keyHookingStarted) {
                UnhookWindowsHookEx(_hookID);
                _hookID = IntPtr.Zero;
                _keyHookingStarted = false;
            }
        }
        static void OnKeyPress(uint keys) {
            Func<Keys, bool> checkKey = key => keys == (uint) key && IsKeyDown(key);

            //checks that shift, alt, ctrl and win keys are not pressed
            Func<bool> checkModifiers = () => !IsKeyDown(Keys.ShiftKey)
                && !IsKeyDown(Keys.Menu) // Keys.Menu is Alt button code
                && !IsKeyDown(Keys.LWin) && !IsKeyDown(Keys.RWin);

            if (checkModifiers() && (checkKey(Keys.Enter) || checkKey(Keys.Return))) {
                // Make you actions here. If it is some long action, do it in background thread
                // this code is just and example
                Worksheet ws = Globals.ThisAddIn.Application.ActiveSheet;
                Range cell = ws.Cells[1, 1];
                cell.Interior.Color = 0xFF0000;
            }
        }
        static bool IsKeyDown(Keys keys) {
            return (GetKeyState((int) keys) & 0x8000) == 0x8000;
        }
        static int HookCallback(int nCode, IntPtr wParam, IntPtr lParam) {
            if (nCode < 0) {
                return (int) CallNextHookEx(_hookID, nCode, wParam, lParam);
            }
            if (nCode == HC_ACTION) {
                OnKeyPress((uint) wParam);
            }
            return (int) CallNextHookEx(_hookID, nCode, wParam, lParam);
        }
    }
}

To use this class, call Start() and Stop() methods.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...