2

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);
    }
Little Fox
  • 1,212
  • 13
  • 39
  • Why do you want to catch enter? You want to do change the color of cell then it's accepted? Or It is about Enter, and you are not interested if it is accepted? – Alex Butenko Jan 19 '16 at 10:34
  • @AlexButenko I want to change color off the cell - "A1". When I press "Enter", it changes color, but only when Excel is in background. If I press Enter, when Excel is active, color doesn't change. – Little Fox Jan 19 '16 at 10:45
  • But what is your purpose? I can answer your question, but I feel what it maybe wrong way to deal with your problem. So, most of the time enter accepts changes made in a cell (if it was edited) or move focus to the next row. So I'm asking, maybe you actually want to catch these events? If not, how do you prefer to deal with Alt+enter, shift+enter, etc? – Alex Butenko Jan 19 '16 at 10:48
  • @AlexButenko i just want to edit one cell by pressing any key, it can be "Esc" or some other, using Add in. – Little Fox Jan 19 '16 at 10:56

1 Answers1

3

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.

Alex Butenko
  • 3,664
  • 3
  • 35
  • 54