用于全局捕获媒体中心远程特殊键的 C# API
Posted
技术标签:
【中文标题】用于全局捕获媒体中心远程特殊键的 C# API【英文标题】:C# API For Globally Capturing Media Center Remote Special Keys 【发布时间】:2011-12-17 05:09:35 【问题描述】:我正在编写一个媒体应用程序,我想让它与标准媒体中心遥控器一起使用。 箭头键、Next 和 Enter 可以正常工作(我确定还有其他的,但这就是我正在使用的),但 Play 和 Pause 不起作用。我正在使用 WH_KEYBOARD_LL 事件的全局挂钩捕获其他键。
当按下“播放”或“暂停”(不要与媒体键盘上的“播放/暂停”...
C# 中是否有标准方法来全局捕获这些按钮?
更新:
这是我正在使用的钩子代码:
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows.Input;
namespace Elpis.KeyboardHook
/// <summary>
/// Listens keyboard globally.
///
/// <remarks>Uses WH_KEYBOARD_LL.</remarks>
/// </summary>
public class KeyboardListener : IDisposable
/// <summary>
/// Creates global keyboard listener.
/// </summary>
public KeyboardListener()
// We have to store the HookCallback, so that it is not garbage collected runtime
hookedLowLevelKeyboardProc = LowLevelKeyboardProc;
// Set the hook
hookId = InterceptKeys.SetHook(hookedLowLevelKeyboardProc);
// Assign the asynchronous callback event
hookedKeyboardCallbackAsync = KeyboardListener_KeyboardCallbackAsync;
#region IDisposable Members
/// <summary>
/// Disposes the hook.
/// <remarks>This call is required as it calls the UnhookWindowsHookEx.</remarks>
/// </summary>
public void Dispose()
InterceptKeys.UnhookWindowsHookEx(hookId);
#endregion
/// <summary>
/// Destroys global keyboard listener.
/// </summary>
~KeyboardListener()
Dispose();
/// <summary>
/// Fired when any of the keys is pressed down.
/// </summary>
public event RawKeyEventHandler KeyDown;
/// <summary>
/// Fired when any of the keys is released.
/// </summary>
public event RawKeyEventHandler KeyUp;
#region Inner workings
/// <summary>
/// Hook ID
/// </summary>
private readonly IntPtr hookId = IntPtr.Zero;
/// <summary>
/// Event to be invoked asynchronously (BeginInvoke) each time key is pressed.
/// </summary>
private readonly KeyboardCallbackAsync hookedKeyboardCallbackAsync;
/// <summary>
/// Contains the hooked callback in runtime.
/// </summary>
private readonly InterceptKeys.LowLevelKeyboardProc hookedLowLevelKeyboardProc;
/// <summary>
/// Actual callback hook.
///
/// <remarks>Calls asynchronously the asyncCallback.</remarks>
/// </summary>
/// <param name="nCode"></param>
/// <param name="wParam"></param>
/// <param name="lParam"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.NoInlining)]
private IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam)
if (nCode >= 0)
if (wParam.ToUInt32() == (int) InterceptKeys.KeyEvent.WM_KEYDOWN ||
wParam.ToUInt32() == (int) InterceptKeys.KeyEvent.WM_KEYUP ||
wParam.ToUInt32() == (int) InterceptKeys.KeyEvent.WM_SYSKEYDOWN ||
wParam.ToUInt32() == (int) InterceptKeys.KeyEvent.WM_SYSKEYUP)
hookedKeyboardCallbackAsync.BeginInvoke((InterceptKeys.KeyEvent) wParam.ToUInt32(),
Marshal.ReadInt32(lParam), null, null);
return InterceptKeys.CallNextHookEx(hookId, nCode, wParam, lParam);
/// <summary>
/// HookCallbackAsync procedure that calls accordingly the KeyDown or KeyUp events.
/// </summary>
/// <param name="keyEvent">Keyboard event</param>
/// <param name="vkCode">VKCode</param>
private void KeyboardListener_KeyboardCallbackAsync(InterceptKeys.KeyEvent keyEvent, int vkCode)
switch (keyEvent)
// KeyDown events
case InterceptKeys.KeyEvent.WM_KEYDOWN:
if (KeyDown != null)
KeyDown(this, new RawKeyEventArgs(vkCode, false));
break;
case InterceptKeys.KeyEvent.WM_SYSKEYDOWN:
if (KeyDown != null)
KeyDown(this, new RawKeyEventArgs(vkCode, true));
break;
// KeyUp events
case InterceptKeys.KeyEvent.WM_KEYUP:
if (KeyUp != null)
KeyUp(this, new RawKeyEventArgs(vkCode, false));
break;
case InterceptKeys.KeyEvent.WM_SYSKEYUP:
if (KeyUp != null)
KeyUp(this, new RawKeyEventArgs(vkCode, true));
break;
private delegate void KeyboardCallbackAsync(InterceptKeys.KeyEvent keyEvent, int vkCode);
#endregion
/// <summary>
/// Raw KeyEvent arguments.
/// </summary>
public class RawKeyEventArgs : EventArgs
/// <summary>
/// Is the hitted key system key.
/// </summary>
public bool IsSysKey;
/// <summary>
/// WPF Key of the key.
/// </summary>
public Key Key;
/// <summary>
/// VKCode of the key.
/// </summary>
public int VKCode;
/// <summary>
/// Create raw keyevent arguments.
/// </summary>
/// <param name="VKCode"></param>
/// <param name="isSysKey"></param>
public RawKeyEventArgs(int VKCode, bool isSysKey)
this.VKCode = VKCode;
IsSysKey = isSysKey;
Key = KeyInterop.KeyFromVirtualKey(VKCode);
/// <summary>
/// Raw keyevent handler.
/// </summary>
/// <param name="sender">sender</param>
/// <param name="args">raw keyevent arguments</param>
public delegate void RawKeyEventHandler(object sender, RawKeyEventArgs args);
#region WINAPI Helper class
/// <summary>
/// Winapi Key interception helper class.
/// </summary>
internal static class InterceptKeys
#region Delegates
public delegate IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam);
#endregion
#region KeyEvent enum
public enum KeyEvent
WM_KEYDOWN = 256,
WM_KEYUP = 257,
WM_SYSKEYUP = 261,
WM_SYSKEYDOWN = 260
#endregion
public static int WH_KEYBOARD_LL = 13;
public static IntPtr SetHook(LowLevelKeyboardProc proc)
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
GetModuleHandle(curModule.ModuleName), 0);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, UIntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
#endregion
【问题讨论】:
【参考方案1】:编辑:在您发布的代码中,您似乎只过滤了按键向上和按键向下事件:
private IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam)
if (nCode >= 0)
if (wParam.ToUInt32() == (int) InterceptKeys.KeyEvent.WM_KEYDOWN ||
wParam.ToUInt32() == (int) InterceptKeys.KeyEvent.WM_KEYUP ||
wParam.ToUInt32() == (int) InterceptKeys.KeyEvent.WM_SYSKEYDOWN ||
wParam.ToUInt32() == (int) InterceptKeys.KeyEvent.WM_SYSKEYUP)
hookedKeyboardCallbackAsync.BeginInvoke((InterceptKeys.KeyEvent) wParam.ToUInt32(),
Marshal.ReadInt32(lParam), null, null);
当你删除你的内部 if 语句时会发生什么?
private IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam)
if (nCode >= 0)
hookedKeyboardCallbackAsync.BeginInvoke((InterceptKeys.KeyEvent) wParam.ToUInt32(),
Marshal.ReadInt32(lParam), null, null);
根据this article,Play相当于Ctrl+Shift+P,Pause相当于Ctrl+P。你能详细说明你所看到的吗?也许提供一些代码会有所帮助。
【讨论】:
@AdamHaile:根据您提供的代码查看我的更新回复。 遗憾的是,什么都没有......我认为,虽然 Ctrl+Shft+P 是等效的,但在这种情况下发送的实际代码并非如此。有了这种变化,什么都没有被拾起...... @AdamHaile:删除 if 语句不应影响其处理已在工作的内容的能力。另一种方法是使用:msdn.microsoft.com/en-us/library/ms996387.aspx 抱歉,澄清一下...之前所有可以正常工作的键。我的意思是播放/暂停遥控按钮不起作用。 您能否更新您的代码以显示您所做的更改?看起来您处理此问题的方式,您需要在几个地方进行更改以更改支持哪些键。以上是关于用于全局捕获媒体中心远程特殊键的 C# API的主要内容,如果未能解决你的问题,请参考以下文章
使用 Web Audio API 和 Wrtc 进行远程音频处理