如何为快捷键实现键盘处理程序 cefSharp
Posted
技术标签:
【中文标题】如何为快捷键实现键盘处理程序 cefSharp【英文标题】:How to implement keyboard handler cefSharp for shortcut keys 【发布时间】:2016-06-15 10:34:43 【问题描述】:我正在构建一个 Windows 应用程序,用于使用 cefSharp
浏览网页。
我需要在该应用程序中实现一些快捷键,谁能告诉我如何实现此功能。
例如
ctrl + tab = move to next tab
我可以跟踪用户是否按下了任何单个键,但无法跟踪多键按下。
IKeyboardHandler
public class KeyboardHandler : IKeyboardHandler
public bool OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey)
bool result = false;
Debug.WriteLine(String.Format("OnKeyEvent: KeyType: 0 0x1:X Modifiers: 2", type, windowsKeyCode, modifiers));
// TODO: Handle MessageNeeded cases here somehow.
return result;
public bool OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut)
const int WM_SYSKEYDOWN = 0x104;
const int WM_KEYDOWN = 0x100;
const int WM_KEYUP = 0x101;
const int WM_SYSKEYUP = 0x105;
const int WM_CHAR = 0x102;
const int WM_SYSCHAR = 0x106;
const int VK_TAB = 0x9;
bool result = false;
isKeyboardShortcut = false;
// Don't deal with TABs by default:
// TODO: Are there any additional ones we need to be careful of?
// i.e. Escape, Return, etc...?
if (windowsKeyCode == VK_TAB)
return result;
Control control = browserControl as Control;
int msgType = 0;
switch (type)
case KeyType.RawKeyDown:
if (isSystemKey)
msgType = WM_SYSKEYDOWN;
else
msgType = WM_KEYDOWN;
break;
case KeyType.KeyUp:
if (isSystemKey)
msgType = WM_SYSKEYUP;
else
msgType = WM_KEYUP;
break;
case KeyType.Char:
if (isSystemKey)
msgType = WM_SYSCHAR;
else
msgType = WM_CHAR;
break;
default:
Trace.Assert(false);
break;
// We have to adapt from CEF's UI thread message loop to our fronting WinForm control here.
// So, we have to make some calls that Application.Run usually ends up handling for us:
PreProcessControlState state = PreProcessControlState.MessageNotNeeded;
// We can't use BeginInvoke here, because we need the results for the return value
// and isKeyboardShortcut. In theory this shouldn't deadlock, because
// atm this is the only synchronous operation between the two threads.
control.Invoke(new Action(() =>
Message msg = new Message() HWnd = control.Handle, Msg = msgType, WParam = new IntPtr(windowsKeyCode), LParam = new IntPtr(nativeKeyCode) ;
// First comes Application.AddMessageFilter related processing:
// 99.9% of the time in WinForms this doesn't do anything interesting.
bool processed = Application.FilterMessage(ref msg);
if (processed)
state = PreProcessControlState.MessageProcessed;
else
// Next we see if our control (or one of its parents)
// wants first crack at the message via several possible Control methods.
// This includes things like Mnemonics/Accelerators/Menu Shortcuts/etc...
state = control.PreProcessControlMessage(ref msg);
));
if (state == PreProcessControlState.MessageNeeded)
// TODO: Determine how to track MessageNeeded for OnKeyEvent.
isKeyboardShortcut = true;
else if (state == PreProcessControlState.MessageProcessed)
// Most of the interesting cases get processed by PreProcessControlMessage.
result = true;
Debug.WriteLine(String.Format("OnPreKeyEvent: KeyType: 0 0x1:X Modifiers: 2", type, windowsKeyCode, modifiers));
Debug.WriteLine(String.Format("OnPreKeyEvent PreProcessControlState: 0", state));
return result;
【问题讨论】:
感谢这个 KeyboardHandler 类,它正是我要找的! OnPreviewKeyDown 函数是相关的。当我知道更多时,我会添加更多信息。 【参考方案1】:终于有了不实现IKeyboardHandler
的替代实现快捷功能。
这里我使用Global Keyboard Hook
来实现这个。
全局键盘钩子
public class GlobalKeyboardHook
#region Variables and dll import
#region For key capturing
[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr hhk, int code, int wParam, ref keyBoardHookStruct lParam);
[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int idHook, LLKeyboardHook callback, IntPtr hInstance, uint theardID);
[DllImport("user32.dll")]
static extern bool UnhookWindowsHookEx(IntPtr hInstance);
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
public delegate int LLKeyboardHook(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 = 0x0100;
const int WM_KEYUP = 0x0101;
const int WM_SYSKEYDOWN = 0x0104;
const int WM_SYSKEYUP = 0x0105;
LLKeyboardHook llkh;
public List<Keys> HookedKeys = new List<Keys>();
IntPtr Hook = IntPtr.Zero;
public event KeyEventHandler KeyDown;
public event KeyEventHandler KeyUp;
#endregion
#region For modifier capturing
/// <summary>
/// Gets the state of modifier keys for a given keycode.
/// </summary>
/// <param name="keyCode">The keyCode</param>
/// <returns></returns>
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
public static extern short GetKeyState(int keyCode);
//Modifier key vkCode constants
private const int VK_SHIFT = 0x10;
private const int VK_CONTROL = 0x11;
private const int VK_MENU = 0x12;
private const int VK_CAPITAL = 0x14;
#endregion
#endregion
#region Constructor
// This is the Constructor. This is the code that runs every time you create a new GlobalKeyboardHook object
public GlobalKeyboardHook()
llkh = new LLKeyboardHook(HookProc);
// This starts the hook. You can leave this as comment and you have to start it manually
// Or delete the comment mark and your hook will start automatically when your program starts (because a new GlobalKeyboardHook object is created)
// That's why there are duplicates, because you start it twice! I'm sorry, I haven't noticed this...
// hook(); <-- Choose!
~GlobalKeyboardHook()
unhook();
#endregion
#region Functions and implementation
/// <summary>
/// Hook (Start listening keybord events)
/// </summary>
public void hook()
IntPtr hInstance = LoadLibrary("User32");
Hook = SetWindowsHookEx(WH_KEYBOARD_LL, llkh, hInstance, 0);
/// <summary>
/// Unhook (Stop listening keybord events)
/// </summary>
public void unhook()
UnhookWindowsHookEx(Hook);
/// <summary>
/// Pass key into event
/// </summary>
/// <param name="Code">Key code</param>
/// <param name="wParam">int event type (keydown/keyup)</param>
/// <param name="lParam">keyBoardHookStruct enum for detecting key</param>
/// <returns>next hook call</returns>
public int HookProc(int Code, int wParam, ref keyBoardHookStruct lParam)
if (Code >= 0)
Keys key = (Keys)lParam.vkCode;
if (HookedKeys.Contains(key))
//Get modifiers
key = AddModifiers(key);
KeyEventArgs kArg = new KeyEventArgs(key);
if ((wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && (KeyDown != null))
KeyDown(this, kArg);
else if ((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) && (KeyUp != null))
KeyUp(this, kArg);
if (kArg.Handled)
return 1;
return CallNextHookEx(Hook, Code, wParam, ref lParam);
/// <summary>
/// Checks whether Alt, Shift, Control or CapsLock
/// is pressed at the same time as the hooked key.
/// Modifies the keyCode to include the pressed keys.
/// </summary>
private Keys AddModifiers(Keys key)
//CapsLock
if ((GetKeyState(VK_CAPITAL) & 0x0001) != 0) key = key | Keys.CapsLock;
//Shift
if ((GetKeyState(VK_SHIFT) & 0x8000) != 0) key = key | Keys.Shift;
//Ctrl
if ((GetKeyState(VK_CONTROL) & 0x8000) != 0) key = key | Keys.Control;
//Alt
if ((GetKeyState(VK_MENU) & 0x8000) != 0) key = key | Keys.Alt;
return key;
#endregion
开始钩子
To start hook add below code to form load or application start
//Start listening keybord events
gHook = new GlobalKeyboardHook();
gHook.KeyDown += new KeyEventHandler(gHook_KeyDown);
foreach (Keys key in Enum.GetValues(typeof(Keys)))
gHook.HookedKeys.Add(key);
gHook.hook();
停止钩子
Add this code on application exit(form closing event)
//Stop listening keybord events
gHook.unhook();
Handel 按键按下事件以实现快捷方式
public void gHook_KeyDown(object sender, KeyEventArgs e)
if (this.ContainsFocus)
if (e.KeyData.ToString().ToUpper().IndexOf("ALT".ToUpper()) >= 0
&& e.KeyValue == 66)//B = 66
//ALT + B
new Thread(() =>
// execute this on the gui thread. (winforms)
this.Invoke(new Action(delegate
tosBrowserBtnBack_Click(this, new EventArgs());
));
).Start();
else if (e.KeyData.ToString().ToUpper().IndexOf("ALT".ToUpper()) >= 0
&& e.KeyValue == 70)//F = 70
//ALT + F
new Thread(() =>
// execute this on the gui thread. (winforms)
this.Invoke(new Action(delegate
tosBrowserBtnFor_Click(this, new EventArgs());
));
).Start();
无论您的应用程序是否有焦点,全局挂钩都适用于每次击键,所以这里我使用this.ContainsFocus
来检查当前表单是否有焦点,如果有则处理快捷方式事件。如果您需要在孔应用程序上实现快捷方式,那么您需要检查应用程序是否具有焦点。
为此,您需要创建一个新类
应用焦点
public class ApplicationFocus
/// <summary>Returns true if the current application has focus, false otherwise</summary>
public static bool ApplicationIsActivated()
var activatedHandle = GetForegroundWindow();
if (activatedHandle == IntPtr.Zero)
return false; // No window is currently activated
var procId = Process.GetCurrentProcess().Id;
int activeProcId;
GetWindowThreadProcessId(activatedHandle, out activeProcId);
return activeProcId == procId;
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int GetWindowThreadProcessId(IntPtr handle, out int processId);
然后在gHook_KeyDown
事件中将if (this.ContainsFocus)
替换为ApplicationFocus.ApplicationIsActivated()
。
这对我有用。
【讨论】:
这会起作用,但不幸的是,当您将应用程序安装到客户端的 PC 上时,防病毒软件会发现您的应用程序是恶意软件。如果启用了防病毒软件。 通过在安装了不同杀毒软件的多个系统上运行检查,它运行良好,没有问题。以上是关于如何为快捷键实现键盘处理程序 cefSharp的主要内容,如果未能解决你的问题,请参考以下文章