29

我将为 OS X Mail.app 应用程序创建一个邮件插件以获得一些附加功能。

我不知道从哪里开始,因为没有插件的官方文档。

谁能帮助我,我该如何开始这个项目。有没有初始链接或教程,请建议?

4

2 回答 2

56

如前所述,编写 Apple Mail 插件并不简单,因为它只有一个私有插件 API,它完全没有文档记录,并且可以随着任何新版本的 Mail.app 更改。最好的代码示例是GPGMail,它是开源的并且仍然有效(已经在 Yosemite 支持上工作)。这是我成功地开始做的事情(完成后将把它放在 github 上):

如何构建一个最小的 Apple Mail 插件(从 Mavericks 和 Xcode 6.0.1 开始)

  1. 你需要在 XCode 中创建一个 OSX “Bundle”项目
  2. 包装器扩展是mailbundle(在项目构建设置中的打包下)
  3. 一个包需要存储在下面~/Library/Mail/Bundles(作为构建阶段添加一个复制文件操作,将其作为绝对路径目标,并将您的构建/文件夹中的 *.mailbundle 作为要复制的项目)
  4. 对于开发,我已在我的运行方案中设置/Applications/Mail.app为可执行文件,以便在 XCode 中运行将构建它,复制包并启动邮件;请注意,此时您将收到来自 Mail 的错误消息,指出您的插件无法启动并被禁用
  5. 你需要SupportedPluginCompatibilityUUIDs在 Info.plist 中提供一个列表,我从 GPGMail 偷了它,这些随着新的 Mail/OSX 版本而改变
  6. 使用class-dump从 Mail.app 的私有 API 生成头文件
  7. 起点是MVMailBundle,你必须从它继承,它有一种registerBundle方法可以吸引你
    • 我从生成的巨大头文件中提取了一个小的MVMailBundle.h头文件,以包含在需要的地方(由 GPGMail 完成)
  8. 创建一个新类MyMailBundle,继承自NSObject
    • 它需要一个initialize方法
    • 并将其设置为 Info.plist 中的“原理类”,以便在 Mail.app 加载捆绑包时运行它
#import <Cocoa/Cocoa.h>

@interface MyMailBundle : NSObject

+ (void)initialize;
@end
  1. initialize实现:以前,您可以使用简单的方法并直接继承,就像在 Letterbox 中所做的那样,但是,由于 Objective-C 的 64 位运行时,您必须使用GPGMail 所做的动态方法
    • 用于NSClassFromString动态获取MVMailBundle
    • class_setSuperclass<objc/runtime.h>让你自己的类继承它
    • 然后调用registerBundle它铸造为MVMailBundle(需要包括MVMailBundle.h
#import <objc/runtime.h>
#import "MVMailBundle.h"
#import "MyMailBundle.h"

@implementation MyMailBundle

+ (void)initialize
{
    NSLog(@"Loading MyMail plugin...");

    // since 64-bit objective-c runtimes, you apparently can't load
    // symbols directly (i.e. through class inheritance) and have to
    // resort to NSClassFromString
    Class mvMailBundleClass = NSClassFromString(@"MVMailBundle");
    
    // If this class is not available that means Mail.app
    // doesn't allow plugins anymore or has changed the API
    if (!mvMailBundleClass)
        return;
    
    // dynamically change super class hierarchy
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated"
    class_setSuperclass([self class], mvMailBundleClass);
#pragma GCC diagnostic pop
    
    // register our plugin bundle in mail
    [[((MyMailBundle *)self) class] registerBundle];
    
    NSLog(@"Done registering MyMail plugin.");
}
@end
  1. 添加一些NSLog日志调用以验证是否发生了正确的事情,当从 XCode 中运行/调试 Mail.app 或在 Console.app 的系统日志中时,它们将在 XCode 的控制台中可见
  2. 这应该成功地在 Mail 中运行插件,没有错误!
  3. 接下来的步骤涉及一些疯狂的事情,例如MethodSwizzlingClassPosing来修改 Mail 的行为,其中 GPGMail 可以是一个有用的示例。(我自己还没去过)

作为参考,这里有一些对我有帮助的资源:

于 2014-09-29T06:24:28.950 回答
4

没有官方支持的方式来构建这样的工具 - 你需要开始尝试在没有任何官方支持的情况下连接到 Mail.app。

如果你想坚持这种事情,那么你需要了解 Mail.app 内部是如何工作的,这是一堆使用调试器和类转储来检查其他应用程序中的库:

https://github.com/nygard/class-dump

您可能还需要一种将代码注入其他应用程序的方法,例如:

https://github.com/rentzsch/mach_inject

每次 Apple 更新 Mail.app 时,您都可能需要重做所有事情 :)

于 2012-10-04T07:49:20.743 回答