这些是 VK_MEDIA_NEXT_TRACK 和 VK_MEDIA_PREV_TRACK 虚拟键。它们的处理非常复杂:
- 无论哪个程序拥有前台窗口,当它调用 GetMessage() 时,都会从其消息队列中检索他的击键
- 该程序消息循环中的 TranslateMessage() 调用将击键转换为WM_APPCOMMAND 消息 http://msdn.microsoft.com/en-us/library/windows/desktop/ms646275%28v=vs.85%29.aspx、 APPCOMMAND_MEDIA_NEXTTRACK 或 APPCOMMAND_MEDIA_PREVIOUSTRACK 并将其发送到具有焦点的子窗口
- 子窗口不会使用它并将消息传递给 DefWindowProc()
- 它将消息传递给子窗口的父窗口。子窗口嵌套时,此过程会重复多次,最终到达顶层窗口
- 当它调用 DefWindowProc 时,Windows 会调用 shell 挂钩来触发任何为 WH_SHELL 挂钩、HSHELL_APPCOMMAND 通知调用 SetWindowsHookEx() 的程序中的回调。像 Windows Media Player 这样的程序将使用它
- 如果没有钩子拦截它,它最终将作为最后一个钩子出现在资源管理器中,然后最终执行操作。
这里的解决方案是不发送击键,而是向上移动先前列出的处理链。如果可以直接调用 shell 挂钩但不公开该功能,那就太好了。下一步是发送 WM_APPCOMMAND 消息。
这需要选择一个窗口来发送消息。如今,由于 UAC,这是一个困难的问题,一项名为 UIPI(用户界面权限隔离)的功能可防止未提升的程序向提升的程序发送消息。因此,您不能只使用前台的窗口,它很可能是一个以管理员权限运行的应用程序。与 Visual Studio 一样,当您尝试使用 PostMessage 和 keybd_event() 时,它们可能失败的原因。
诀窍是将其发送到一个窗口you自己的。您使用的是控制台模式项目,这让您变得困难。这是可以解决的。项目 + 添加引用,选择 System.Windows.Forms。将新类添加到您的项目中并粘贴下面所示的代码。将您的 keybd_event() 调用替换为,例如
AppCommand.Send(AppCommands.MediaNext);
我把你可以发送的所有命令都放进去了。我提供了一个 Cleanup() 方法来释放资源,没有必要调用它。该代码与 .NET 版本 2.0 至 4.5.1 兼容
using System;
using System.Threading;
using System.Windows.Forms;
using System.Runtime.InteropServices;
public enum AppCommands {
BrowserBack = 1,
BrowserForward = 2,
BrowserRefresh = 3,
BrowserStop = 4,
BrowserSearch = 5,
BrowserFavorite = 6,
BrowserHome = 7,
VolumeMute = 8,
VolumeDown = 9,
VolumeUp = 10,
MediaNext = 11,
MediaPrevious = 12,
MediaStop = 13,
MediaPlayPause = 14,
LaunchMail = 15,
LaunchMediaSelect = 16,
LaunchApp1 = 17,
LaunchApp2 = 18,
BassDown = 19,
BassBoost = 20,
BassUp = 21,
TrebleUp = 22,
TrebleDown = 23,
MicrophoneMute = 24,
MicrophoneVolumeUp = 25,
MicrophoneVolumeDown = 26,
Help = 27,
Find = 28,
New = 29,
Open = 30,
Close = 31,
Save = 32,
Print = 33,
Undo = 34,
Redo = 35,
Copy = 36,
Cut = 37,
Paste = 38,
ReplyToMail = 39,
ForwardMail = 40,
SendMail = 41,
SpellCheck = 42,
Dictate = 43,
MicrophoneOnOff = 44,
CorrectionList = 45,
MediaPlay = 46,
MediaPause = 47,
MediaRecord = 48,
MediaFastForward = 49,
MediaRewind = 50,
MediaChannelUp = 51,
MediaChannelDown = 52,
Delete = 53,
Flip3D = 54
}
public static class AppCommand {
public static void Send(AppCommands cmd) {
if (frm == null) Initialize();
frm.Invoke(new MethodInvoker(() => SendMessage(frm.Handle, WM_APPCOMMAND, frm.Handle, (IntPtr)((int)cmd << 16))));
}
private static void Initialize() {
// Run the message loop on another thread so we're compatible with a console mode app
var t = new Thread(() => {
frm = new Form();
var dummy = frm.Handle;
frm.BeginInvoke(new MethodInvoker(() => mre.Set()));
Application.Run();
});
t.SetApartmentState(ApartmentState.STA);
t.IsBackground = true;
t.Start();
mre.WaitOne();
}
public static void Cleanup() {
if (frm != null) {
frm.BeginInvoke(new MethodInvoker(() => {
frm.Close();
Application.ExitThread();
mre.Set();
}));
mre.WaitOne();
frm = null;
}
}
private static ManualResetEvent mre = new ManualResetEvent(false);
private static Form frm;
// Pinvoke
private const int WM_APPCOMMAND = 0x319;
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
}