能否在C/D语言中通过Apple Events与应用通信(无需嵌入脚本)?
Absolutely possible! You don’t need to rely on AppleScript or JSX wrappers—Apple Events are built on a low-level C API part of macOS’s CoreServices framework. Since D has first-class C interoperability, you can bind directly to these APIs and interact with other apps natively. Here’s how to do it:
Step 1: Understand the Apple Events C API
The core functionality lives in headers like AE.h, AEConstants.h, and AEDesc.h (part of the CoreServices framework). Key functions and types you’ll work with include:
AEDesc: The fundamental data type for passing data between apps via Apple Events.AECreateAppleEvent: Builds a new Apple Event targeting a specific app.AESend: Sends the event and optionally waits for a response.AEBuildDesc: Helper to constructAEDescobjects with data like file paths or strings.AEDisposeDesc: Cleans upAEDescinstances to avoid memory leaks.
Step 2: Bind the C API in D
D can directly interface with C code using extern(C) blocks. You can either:
- Manually declare the required functions, types, and constants (matching the C signatures), or
- Use tools like
dstepto auto-generate D bindings from the CoreServices headers.
For example, here’s a minimal declaration set for key AE functions:
extern(C): import core.sys.darwin.CoreFoundation; OSStatus AECreateAppleEvent(OSType eventClass, OSType eventID, const AEDesc* target, AESendMode sendMode, long transactionID, AEDesc* result); OSStatus AESend(const AEDesc* theEvent, AEDesc* reply, AESendMode sendMode, long timeOut); void AEDisposeDesc(AEDesc* theDesc); OSStatus AEBuildDesc(AEDesc* result, const char* format, ...); // Common constants (from AEConstants.h) enum OSType kCoreEventClass = 'aevt'; enum OSType kAEOpenDocuments = 'odoc'; enum OSType typeApplicationBundleID = 'bund'; enum OSType keyDirectObject = '----'; enum AESendMode kAENoReply = 1; const long kAEDefaultTimeout = -1;
Step 3: Build and Send an Apple Event
Let’s walk through a practical example: sending an event to Finder to open a specific file.
import std.stdio; import std.string; // Include the extern(C) declarations from Step 2 here void main() { AEDesc targetApp; // Target Finder using its bundle ID auto status = AEBuildDesc(&targetApp, "ssss", typeApplicationBundleID, "com.apple.finder"); if (status != noErr) { writeln("Failed to create target app descriptor: ", status); return; } AEDesc openEvent; // Create the "Open Documents" event status = AECreateAppleEvent(kCoreEventClass, kAEOpenDocuments, &targetApp, kAENoReply, 0, &openEvent); if (status != noErr) { writeln("Failed to create Apple Event: ", status); AEDisposeDesc(&targetApp); return; } AEDisposeDesc(&targetApp); // Build a descriptor for the file path AEDesc fileDesc; const char* filePath = "/Users/yourusername/Documents/test.txt"; status = AEBuildDesc(&fileDesc, "(@)", typeFileURL, filePath); if (status != noErr) { writeln("Failed to create file descriptor: ", status); AEDisposeDesc(&openEvent); return; } // Attach the file as the event's direct object status = AEBuildDesc(&openEvent, "obj{prop:}", keyDirectObject, &fileDesc); if (status != noErr) { writeln("Failed to attach file to event: ", status); AEDisposeDesc(&fileDesc); AEDisposeDesc(&openEvent); return; } AEDisposeDesc(&fileDesc); // Send the event (we don't wait for a reply here) AEDesc reply; status = AESend(&openEvent, &reply, kAENoReply, kAEDefaultTimeout); if (status != noErr) { writeln("Failed to send Apple Event: ", status); } // Clean up all descriptors AEDisposeDesc(&openEvent); AEDisposeDesc(&reply); }
Critical Notes
- Privacy Permissions: On macOS 10.14+, your app needs explicit permission to send Apple Events. Add the
NSAppleEventsUsageDescriptionkey to your app’sInfo.plistwith a clear explanation of why you need this access (e.g., "This app needs to open files in Finder"). - Four-Character Codes: Apple Events use
OSType(4-byte character constants) to identify event classes, IDs, and properties. You can look these up in Apple’s documentation or headers likeAEConstants.h. - Memory Management: Always call
AEDisposeDescon anyAEDescyou create—failure to do so will cause memory leaks. - Debugging: Use the Console app to view Apple Events logs (filter for "appleevents") or the
AEDebugcommand-line tool to trace event flow.
内容的提问来源于stack exchange,提问作者Ontonator

