Cocoa 获取 SIMBL 插件的当前 运行 路径

Cocoa get current running path of SIMBL Plugin

我正在开发由 macOS 上的第 3 方主机应用程序加载的 SIMBL 插件。它几乎完全是用 C++ 编写的,只有极少的 objective-c 组件。 (UI 主要由 API 调用主机应用程序提供。)其中一个要求是插件包可以从不同的子目录加载多次。它是一个 Lua 解释器,目标是为每个实例托管不同配置的 lua 脚本,这些脚本出现在主机应用程序的单独菜单中。第三方可以将此插件与其脚本的自定义配置捆绑在一起,它们将在应用程序的插件菜单中显示为单独的项目。

我遇到的问题是:我需要找出我的插件在哪个目录中执行。我可以创建一个名为 MY_BUNDLE_ID_CLASS 的特殊 class 并使用:

[NSBundle bundleForClass:[MY_BUNDLE_ID_CLASS class]];

一旦我有了正确的 NSBundle,获取文件路径就很简单了。

问题是如果我的包的多个实例被加载(从不同的文件夹),Cocoa 抱怨 class MY_BUNDLE_ID_CLASS 在多个位置定义并且不会保证我使用了哪一个。对于其他类似的 classes 这对我的插件来说没问题,因为我唯一的 class 名称是宏,等同于包含版本号的错位名称,但在这种情况下是不行的。它可能是同一版本的多个实例。有没有其他方法可以找出我的插件代码正在执行的文件夹?这似乎是一个简单的请求,但我一无所获。欢迎提出建议。

给定可执行文件中的地址,dladdr 函数可用于查询动态链接器有关包含该地址的 dynamically-linked 映像;即,给定对插件中符号的引用,dladdr 可以为您提供有关插件的动态链接信息。

运行时查找如下所示:

// Sample: BundleClass.m, the principal class for the plugin

#import "BundleClass.h"
#import <dlfcn.h>

// We'll be using a reference to this variable compiled into the plugin,
// but we can just as easily use a function pointer or similar -- anything
// that will be statically compiled into the plugin.
int someVariable = 0;

@implementation BundleClass

+ (void)load {
    Dl_info info;
    if (dladdr(&someVariable, &info) != 0) {
        NSLog(@"Plugin loaded from %s", info.dli_fname);
    } else {
        // Handle lookup failure.
    }
}

@end

除了 &someSymbol,您还可以使用对函数的引用(例如 &someFunctionDefinedInThePlugin),但您应该注意不要传入可以动态分配的指针——因为那可能会失败,或者将您指向主机进程的内存 space。

在我的机器上,使用简单的 macOS 主机应用程序设置,以下加载代码:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    NSURL *bundleURL = [[NSBundle mainBundle] URLForResource:@"DynamicBundle" withExtension:@"bundle"];
    if (!bundleURL) {
        NSLog(@"Failed to find bundle!");
        return;
    }

    NSLog(@"Bundle class before loading: %@", NSClassFromString(@"BundleClass"));

    NSBundle *bundle = [NSBundle bundleWithURL:bundleURL];
    NSError *error = nil;
    if (![bundle loadAndReturnError:&error]) {
        NSLog(@"Failed to load bundle: %@", error);
        return;
    }

    NSLog(@"Bundle class after loading: %@", NSClassFromString(@"BundleClass"));
}

成功生产

Bundle class before loading: (null)
Loaded plugin from /Volumes/ExtSSD/Developer/Xcode/DerivedData/HostApp/Build/Products/Debug/HostApp.app/Contents/Resources/DynamicBundle.bundle/Contents/MacOS/DynamicBundle
Bundle class after loading: BundleClass

这确实是磁盘上插件的路径。