如何在MacOS的Electron应用中添加辅助功能启用代码?
Hey there, integrating that Objective-C accessibility code into your macOS Electron app isn't straightforward out of the box—since Electron runs on Node.js, it can't execute Objective-C directly. You'll need to wrap that code into a Node.js native addon, which acts as a bridge between Electron and macOS's native APIs. Let's walk through this step by step:
Step 1: Set up the native addon project structure
First, create a dedicated folder in your Electron project root (name it something like native-accessibility) to hold all the native code files. Inside it, you'll need:
- An Objective-C++ source file (
.mmextension, since we need to bridge Node.js's C API to Objective-C) - A
binding.gypfile to configure the build process - Dev dependencies to compile the addon against Electron's specific Node.js version
Step 2: Write the Objective-C++ wrapper code
Create a file named accessibility.mm with the following code. This wraps your original accessibility logic into a function that Node.js (and thus Electron) can call directly:
#include <node.h> #include <CoreFoundation/CoreFoundation.h> #include <ApplicationServices/ApplicationServices.h> using namespace v8; CFStringRef kAXManualAccessibility = CFSTR("AXManualAccessibility"); void EnableAccessibility(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = args.GetIsolate(); // Validate input arguments: we need a boolean (enable/disable) and a process ID if (args.Length() != 2 || !args[0]->IsBoolean() || !args[1]->IsNumber()) { isolate->ThrowException(Exception::TypeError( String::NewFromUtf8(isolate, "Expected arguments: (boolean enable, number processId)").ToLocalChecked())); return; } bool enable = args[0]->BooleanValue(isolate); pid_t processId = static_cast<pid_t>(args[1]->IntegerValue(isolate)); AXUIElementRef appRef = AXUIElementCreateApplication(processId); if (appRef == nil) { args.GetReturnValue().Set(False(isolate)); return; } CFBooleanRef value = enable ? kCFBooleanTrue : kCFBooleanFalse; AXError error = AXUIElementSetAttributeValue(appRef, kAXManualAccessibility, value); CFRelease(appRef); // Return whether the operation succeeded args.GetReturnValue().Set(error == kAXErrorSuccess ? True(isolate) : False(isolate)); } void Initialize(Local<Object> exports) { NODE_SET_METHOD(exports, "enableAccessibility", EnableAccessibility); } NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
This code takes the core logic you provided, adapts it to accept inputs from Node.js, and returns a success/failure status back to your Electron code.
Step 3: Configure the build with binding.gyp
Create a binding.gyp file in the native-accessibility folder. This tells node-gyp how to compile your code, including linking against the required macOS frameworks:
{ "targets": [ { "target_name": "accessibility", "sources": ["accessibility.mm"], "libraries": [ "-framework CoreFoundation", "-framework ApplicationServices" ], "mac_settings": { "OTHER_CFLAGS": ["-std=c++17", "-ObjC++"] } } ] }
Step 4: Compile the addon for your Electron version
Electron uses a custom Node.js build, so you can't compile the addon with regular node-gyp. Instead, use electron-rebuild to ensure compatibility:
- First, install the required tools as dev dependencies in your project:
npm install --save-dev node-gyp electron-rebuild - Run
electron-rebuildto compile the addon:npx electron-rebuild -f -w accessibility
This will generate a compiled module file at native-accessibility/build/Release/accessibility.node.
Step 5: Call the addon from your Electron main process
Now you can import the compiled module into your Electron main process (e.g., main.js) and use it to enable accessibility. Here's how to enable it for your own Electron app:
const { app } = require('electron'); const accessibility = require('./native-accessibility/build/Release/accessibility'); // Wait until the app is ready before accessing process IDs app.whenReady().then(() => { const appPid = app.getProcessId(); const success = accessibility.enableAccessibility(true, appPid); if (success) { console.log('Accessibility enabled successfully for this app'); } else { console.error('Failed to enable accessibility—check permissions!'); } });
If you need to target another running macOS app, you can find its process ID using Electron's app.getRunningApplications():
const { app } = require('electron'); app.whenReady().then(() => { // Replace "Target App Name" with the name of the app you want to target const targetApp = app.getRunningApplications().find(installedApp => installedApp.name === "Target App Name"); if (targetApp) { const success = accessibility.enableAccessibility(true, targetApp.processId); console.log(success ? "Accessibility enabled for target app" : "Failed to enable accessibility"); } else { console.error("Target app not found"); } });
Critical Things to Keep in Mind
- Accessibility Permissions: Your Electron app must be granted accessibility permissions in macOS System Settings > Privacy & Security > Accessibility. Without this, the API call will fail with an authorization error.
- Recompile on Electron Updates: Every time you update your Electron version, you'll need to rerun
electron-rebuildto recompile the addon (since Electron's Node.js ABI changes between versions). - Architecture Compatibility: Ensure your app is built for the same architecture (arm64 for Apple Silicon, x86_64 for Intel) as the compiled addon—
electron-rebuildhandles this automatically if you're building on the same machine you'll run the app on.
内容的提问来源于stack exchange,提问作者P. James




