You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在MacOS的Electron应用中添加辅助功能启用代码?

如何在macOS Electron应用中集成辅助功能启用的Objective-C代码

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 (.mm extension, since we need to bridge Node.js's C API to Objective-C)
  • A binding.gyp file 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:

  1. First, install the required tools as dev dependencies in your project:
    npm install --save-dev node-gyp electron-rebuild
    
  2. Run electron-rebuild to 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-rebuild to 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-rebuild handles this automatically if you're building on the same machine you'll run the app on.

内容的提问来源于stack exchange,提问作者P. James

火山引擎 最新活动