我的目标是使用 IOHID 阻止按键到达操作系统(由于其他原因不能使用 CGEvent)。根据 kIOHIDOptionsTypeSeizeDevice 的文档:用于打开独占通信...
我的目标是使用 IOHID 阻止按键到达操作系统(由于其他原因不能使用 CGEvent)。根据 的文档 kIOHIDOptionsTypeSeizeDevice
:
用于打开与设备的独占通信。这将阻止系统和其他客户端接收来自设备的事件。
#import "TestKeys.h"
#import <IOKit/hid/IOHIDManager.h>
#import <IOKit/hid/IOHIDUsageTables.h>
@implementation TestKeys
#define KEYS 2
static void Handle_InputCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDValueRef value)
{
IOHIDElementRef elem = IOHIDValueGetElement(value);
uint16_t scancode = IOHIDElementGetUsage(elem);
if (scancode < 4 || scancode > 231) {
return;
}
NSLog(@"Key event received: %d", scancode);
}
static void Handle_DeviceMatchingCallback(void * inContext, IOReturn inResult, void * inSender, IOHIDDeviceRef inIOHIDDeviceRef)
{
NSLog(@"Connected");
}
static void Handle_RemovalCallback(void * inContext, IOReturn inResult, void * inSender, IOHIDDeviceRef inIOHIDDeviceRef)
{
NSLog(@"Removed");
}
-(void)start
{
IOHIDManagerRef manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDManagerOptionNone);
if (CFGetTypeID(manager) != IOHIDManagerGetTypeID()) {
exit(1);
}
int usagePage = kHIDPage_GenericDesktop;
int usage = kHIDUsage_GD_Keyboard;
CFStringRef keys[KEYS] = {
CFSTR(kIOHIDDeviceUsagePageKey),
CFSTR(kIOHIDDeviceUsageKey),
};
CFNumberRef values[KEYS] = {
CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &usagePage),
CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &usage),
};
CFDictionaryRef matchingDict = CFDictionaryCreate(kCFAllocatorDefault,
(const void **) keys, (const void **) values, KEYS,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
for (int i=0; i<KEYS; i++) {
CFRelease(keys[i]);
CFRelease(values[i]);
}
IOHIDManagerSetDeviceMatching(manager, matchingDict);
CFRelease(matchingDict);
IOHIDManagerRegisterDeviceMatchingCallback(manager, Handle_DeviceMatchingCallback, NULL);
IOHIDManagerRegisterDeviceRemovalCallback(manager, Handle_RemovalCallback, NULL);
IOHIDManagerRegisterInputValueCallback(manager, Handle_InputCallback, NULL);
IOHIDManagerOpen(manager, kIOHIDOptionsTypeSeizeDevice);
IOHIDManagerScheduleWithRunLoop(manager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
}
@end
该代码运行并设法从操作系统全局查看和打印所有击键,但它似乎 kIOHIDOptionsTypeSeizeDevice
被忽略了,因为击键仍然被传递给 macOS。
编辑: 添加 IOReturn result =
到代码中会暴露错误 -536870207
,该错误转换为 kIOReturnNotPrivileged
。然后我将 Xcode 方案更改为 root 并能够阻止键盘按键。
这就引出了我的下一个问题,我怎样才能将此代码添加到显然不以 root 权限运行的开发者 ID 应用程序中?
很高兴我们在评论中追踪到您的 HID 问题是由于权限问题造成的。
要在生产/部署中以 root 身份运行代码,您需要设置一个单独的工具作为 启动守护进程 ,并将其配置为在主应用程序向其发送 IPC(通常是 XPC)消息时按需启动。
首先,您有两个主要选项可以设置启动守护程序:
SMJobBless
从您的应用代码中调用以将其安装在系统中。这将要求用户输入管理员密码。
/Library/Application Support/YourAppName/
),并将相应的 launchd plist 在 /Library/LaunchDaemons
.
,设置启动守护程序时 需要 非常 本系列文章 是一份关于应该做什么和应该避免什么的深入指南。
还要注意的是, SMJobBless
App Store 不允许这样做,因此在那里分发的应用程序根本无法实现这种功能。