我正在 .NET 7.0 中开发一个 macOS 应用程序,它利用辅助功能框架(ApplicationServices -> HIServices)来访问辅助功能。当尝试调用
AXUIElementCopyAttributValues()
函数时,应用程序崩溃并出现 EXC_BAD_ACCESS (SIGSEGV)
错误。
这是我的代码和我的测试存储库的链接:
public partial class ProgramTest
{
public void MainTest()
{
AccessibilityHelper.RequestAccessibilityPermission();
string bundleIdentifier = "com.apple.Safari";
NSRunningApplication app = NSRunningApplication.GetRunningApplications(bundleIdentifier)?.First();
if (app != null)
{
int processId = app.ProcessIdentifier;
IntPtr appElement = AXUIElementCreateApplication((uint)processId);
IntPtr mainWindow;
if (AXUIElementCopyAttributeValue(appElement, kAXMainWindowAttribute, out mainWindow) == 0)
{
Console.WriteLine("Main Window Handle: " + mainWindow);
}
else
{
Console.WriteLine("Could not retrieve the main window.");
}
}
else
{
Console.WriteLine("Application not found.");
}
}
const string accessibilityFramework = "/System/Library/Frameworks/ApplicationServices.framework/Versions/Current/ApplicationServices";
[DllImport(accessibilityFramework)]
static extern IntPtr AXUIElementCreateApplication(uint pid);
[DllImport(accessibilityFramework)]
static extern int AXUIElementCopyAttributeValue(IntPtr element, string attribute, out IntPtr value);
const string kAXMainWindowAttribute = "kAXTitleAttribute";
}
public static class AccessibilityHelper
{
public static void RequestAccessibilityPermission()
{
NSString key = new NSString(AXTrustedCheckOptionPrompt);
NSDictionary options = NSDictionary.FromObjectAndKey(NSNumber.FromBoolean(true), key);
IntPtr optionsPtr = options.Handle;
bool isTrusted = AXIsProcessTrustedWithOptions(optionsPtr);
if (!isTrusted)
{
Console.WriteLine("User denied accessibility permission.");
}
}
const string accessibilityFramework = "/System/Library/Frameworks/ApplicationServices.framework/Versions/Current/ApplicationServices";
[DllImport(accessibilityFramework)]
static extern bool AXIsProcessTrustedWithOptions(IntPtr options);
const string AXTrustedCheckOptionPrompt = "AXTrustedCheckOptionPrompt";
}
以下是我为解决该问题所采取的步骤:
我已在 Info.plist 文件中添加了辅助权限所需的密钥:
<key>NSAppleEventsUsageDescription</key>
<string>We need accessibility permissions to interact with other applications.</string>
<key>NSAccessibilityUsageDescription</key>
<string>We need accessibility permissions to interact with other applications.</string>
我实现了 RequestAccessibilityPermission() 方法来在运行时请求可访问性权限。
public static void RequestAccessibilityPermission()
{
NSString key = new NSString(AXTrustedCheckOptionPrompt);
NSDictionary options = NSDictionary.FromObjectAndKey(NSNumber.FromBoolean(true), key);
IntPtr optionsPtr = options.Handle;
bool isTrusted = AXIsProcessTrustedWithOptions(optionsPtr);
if (!isTrusted)
{
Console.WriteLine("User denied accessibility permission.");
}
}
我还使用 Xcode 使用方法
AXUIElementCreateApplication(_:)
和 AXUIElementCopyAttributeValue(_:_:_:)
创建了一个自定义 Cocoa 库。我打算在我的 .NET 7.0 macOS 应用程序中通过 P/Invoke 使用这些方法。然而,即使使用这个自定义 Cocoa 库,我也会遇到同样的 EXC_BAD_ACCESS (SIGSEGV)
错误。
此外,我已在权利中禁用沙盒。
<key>com.apple.security.app-sandbox</key>
<false/>
这是错误报告
-------------------------------------
Translated Report (Full Report Below)
-------------------------------------
Process: netMacOSTest [70764]
Path: /Users/USER/*/netMacOSTest.app/Contents/MacOS/netMacOSTest
Identifier: com.companyname.netMacOSTest
Version: 1.0 (1)
Code Type: X86-64 (Translated)
Parent Process: vsdbg-ui [70763]
Responsible: VisualStudio [491]
User ID: 501
Date/Time: 2023-12-07 15:44:18.6174 +0100
OS Version: macOS 14.0 (23A344)
Report Version: 12
Anonymous UUID: E66ED467-F839-D06D-428B-00BCDEEAD568
Time Awake Since Boot: 610000 seconds
System Integrity Protection: enabled
Notes:
PC register does not match crashing frame (0x0 vs 0x10344B8E8)
Crashed Thread: 0 Dispatch queue: com.apple.main-thread
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x0000746954584180
Exception Codes: 0x0000000000000001, 0x0000746954584180
Termination Reason: Namespace SIGNAL, Code 11 Segmentation fault: 11
Terminating Process: exc handler [70764]
VM Region Info: 0x746954584180 is not in any region. Bytes after previous region: 22442082320769 Bytes before following region: 12514185690752
REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL
MALLOC_NANO 600000000000-600020000000 [512.0M] rw-/rwx SM=PRV
---> GAP OF 0x1fcae3f00000 BYTES
MALLOC_TINY 7fcb03f00000-7fcb04000000 [ 1024K] rw-/rwx SM=PRV
Error Formulating Crash Report:
PC register does not match crashing frame (0x0 vs 0x10344B8E8)
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 <translation info unavailable> 0x10344b8e8 ???
1 HIServices 0x7ff81c30f1e0 AXUIElementCopyAttributeValue + 95
2 ??? 0x116ecfdbe ???
3 ??? 0x116ecafd2 ???
4 ??? 0x116ecae4a ???
5 libcoreclr.dylib 0x10d478d89 CallDescrWorkerInternal + 124
6 libcoreclr.dylib 0x10d478d89 CallDescrWorkerInternal + 124
有谁有解决此问题的经验并可以提供如何解决该问题的建议吗?我是否应该采取其他步骤来识别和解决错误的根本原因?
您的方法定义似乎并不完全正确。
我刚刚使用以下导入定义进行了一些测试,结果是成功的。 (注意 - 由于指针的使用,您需要允许您的代码执行不安全的代码)
[DllImport("/System/Library/Frameworks/ApplicationServices.framework/ApplicationServices")]
static extern IntPtr AXUIElementCreateSystemWide();
[DllImport("/System/Library/Frameworks/ApplicationServices.framework/ApplicationServices")]
unsafe static extern int AXUIElementCopyAttributeNames(IntPtr element, IntPtr* names);
[DllImport("/System/Library/Frameworks/ApplicationServices.framework/ApplicationServices")]
unsafe static extern int AXUIElementCopyAttributeValue(IntPtr element, IntPtr attribute, IntPtr* value);
这些看起来对我有用,请参阅下面的示例。请注意,您应该使用的属性名称与头文件中的确切 const 定义不匹配,但使用 CopyAttributeNames,您可以验证存在哪些可能的键。
更改方法定义后,我开始获取正确的错误代码或结果,而不是因内存故障而崩溃。
// get accessibility element
var element = AXUIElementCreateSystemWide();
// fetch the available attributes
IntPtr ptrAttributeNames;
var attributeNamesError = AXUIElementCopyAttributeNames(element, &ptrAttributeNames);
// convert response to a readable array
var attributeNames = CFArray.ArrayFromHandleFunc(ptrAttributeNames, CFString.FromHandle);
// fetch the value for an attribute
IntPtr ptrAttributeValue;
var attributeValueError = AXUIElementCopyAttributeValue(element, CFString.CreateNative("AXSelectedText"), &ptrAttributeValue);
var attributeValue = CFString.FromHandle(ptrAttributeValue);