如何获取最近打开的文件列表?

时间:2013-01-01 02:38:02

标签: objective-c macos applescript recent-file-list

我目前正在构建AppleScript应用程序。 (请注意,这是一个简单的AppleScript“应用程序”,使用AppleScript编辑器创建AppleScript,然后将该脚本另存为应用程序)。对于这个应用程序,我需要知道当前用户最近打开的文件的文件路径。到目前为止,我尝试了很多不同的方法,但似乎都没有给我提供我需要的信息。

我尝试了以下内容:

  • 我已经尝试了com.apple.recentitems.plist文件,但是这个文件中包含的文件路径信息似乎是十六进制格式,当转换成ASCII时,它充满了很多mumbo-jumbo,以及mumbo-jumbo似乎会针对不同的文件和不同的计算机进行更改。
  • 我尝试使用unix find命令来过滤过去一天打开的文件,但是在这里我只能选择最近修改过的和最近访问过的文件,而不是最近打开过的文件。最近修改过的文件不包括未修改的打开文件(如打开但未编辑的图像),最近访问的文件似乎显示所有在Finder中以缩略图视图显示的文件,即使它们没有用户打开了。
  • 我尝试过使用Objective-C和LaunchServices/LSSharedFileList.h文件获取kLSSharedFileListRecentDocumentItems(类似于this answer),但是之前我从未使用过Objective-C我们无法正常使用它。

任何人都可以提供帮助我获取当前用户最近打开的文件列表的任何帮助将非常感谢。此外,任何能够将最近更改的文件列表写入文本文件的帮助都会很棒。

3 个答案:

答案 0 :(得分:1)

的AppleScript

那个afaik没有AppleScript API(相当于LSSharedFileList)..你要么调用objC代码(参见下面的objC代码段),要么就是..你只是读了一个有点hackish的plist:)

目标c

对于使用LSSharedFileList API的非沙盒应用,这是最恰当的方式。

@implementation DDAppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    LSSharedFileListRef list = LSSharedFileListCreate(NULL, kLSSharedFileListRecentDocumentItems, 0);
    CFArrayRef items = LSSharedFileListCopySnapshot(list, NULL);

    for(int i=0; i<CFArrayGetCount(items); i++) {
        LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(items, i);

        //get items properties... for demo I just get name
        NSString *name = (__bridge_transfer NSString*)LSSharedFileListItemCopyDisplayName(item);

        NSLog(@"%@", name);
    }
}

@end

它不是在沙盒中工作 - 并且没有真正的解决方法(正如你所说,你可以直接读取plist但是......那是一个有点hackish:D PLUS苹果可能不会让你这样做!)

答案 1 :(得分:1)

你的解决方案可能有效,但也许我想出的可能更有用。

在您提出的3个选项中,确实LSSharedFile*命令是最佳解决方案。

随着AppleScript脚本包(.scptd)和可以保存为应用程序包(.app)的脚本的出现,您可以非常轻松地在捆绑包中包含自定义可执行文件。因此,如果您无法单独使用AppleScript或使用默认的BSD子系统工具,则可以使用这些可执行文件来完成您需要执行的操作。

所以,在Xcode中我创建了一个新的“Foundation Tool”项目,它使用了以下代码:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        UInt32 resolveFlags = 0;
        NSArray *arguments = [[NSProcessInfo processInfo] arguments];
        if (arguments.count > 1) {
            NSArray *revisedArguments = [arguments
                        subarrayWithRange:NSMakeRange(1, arguments.count - 1)];
            for (NSString *arg in revisedArguments) {
                if ([arg isEqualToString:@"--noUserInteraction"]) {
                    resolveFlags |= kLSSharedFileListNoUserInteraction;
                } else if ([arg isEqualToString:@"--doNotMountVolumes"]) {
                    resolveFlags |= kLSSharedFileListDoNotMountVolumes;
                }
            }
        }
        LSSharedFileListRef sharedFileListRef = LSSharedFileListCreate(NULL,
                              kLSSharedFileListRecentDocumentItems, NULL);
        NSArray *sharedFileListItemRefs =
           (NSArray *)LSSharedFileListCopySnapshot(sharedFileListRef, NULL);
        for (id itemRef in sharedFileListItemRefs) {
            NSURL *resolvedURL = nil;
            LSSharedFileListItemResolve((LSSharedFileListItemRef)itemRef,
                 resolveFlags, (CFURLRef *)&resolvedURL, NULL);
            if (resolvedURL) {
                printf("%s\n", resolvedURL.path.fileSystemRepresentation);
                [resolvedURL release];
            }
        }
        if (sharedFileListRef) CFRelease(sharedFileListRef);
        [sharedFileListItemRefs release];
        return EXIT_SUCCESS;
    }
}

虽然此代码与您的代码类似,但它不必将结果写入中间文件,而只是将文件路径打印到标准输出。这应该允许在AppleScript结束时大大简化编码。构建此Xcode项目的结果是一个名为recentItemsFinagler的单个“Unix可执行文件”,而不是应用程序包。

要在AppleScript中使用此构建的可执行文件,首先需要确保脚本保存为脚本包或应用程序,然后应启用Bundle Contents工具栏项,如下图所示:< / p>

enter image description here

单击该工具栏项会显示抽屉,其中显示了脚本包或应用程序包的内容。要添加自定义可执行文件,只需将其从Finder中拖放即可,如下图所示:

enter image description here

这会将其复制到脚本包中,如下所示:

enter image description here

要在运行时找到此可执行文件,您可以使用path to resource AppleScript命令,该命令是StandardAdditions.osax的一部分。 (请注意,根据您尝试在脚本中使用path to resource命令的方式,当您从AppleScript编辑器中运行脚本而不是通过双击运行脚本时,可能会遇到“找不到资源”错误如果你遇到这种类型的错误,首先要确保你已经保存了对脚本的所有更改,然后退出AppleScript编辑器,然后通过双击它来启动脚本应用程序。 Finder,在脚本运行完毕后,重新打开AppleScript Editor,然后重新打开脚本应用程序,然后尝试在AppleScript编辑器中运行并查看它是否有效)。

因此,要在AppleScript中使用此recentItemsFinagler,您可以执行以下操作:

property recentItemsFinagler : missing value

if (recentItemsFinagler = missing value) then
    set recentItemsFinagler to (path to resource "recentItemsFinagler")
end if

set toolPath to quoted form of POSIX path of recentItemsFinagler

set recentItemsString to (do shell script toolPath & " --noUserInteraction --doNotMountVolumes")

set recentItemsPaths to paragraphs of recentItemsString

我将逐行浏览这个AppleScript来解释它正在做什么。我们首先创建一个全局属性recentItemsFinagler,并将其初始值设置为missing value。 (missing value是与Objective-C的nil)等效的AppleScript。

如果您不了解它,AppleScript中的全局属性就像其他语言中的全局变量一样重要:当您运行脚本并为属性赋值时,该值实际上保存在脚本中文件本身,并将在下次运行脚本时保留。

该脚本首先检查recentItemsFinagler是否等于missing value,如果是,则将其设置为(path to resource "recentItemsFinagler")的结果,这是AppleScript alias参考。这类似于Finder中的别名,因为它能够成功跟踪重命名等更改,以及从一个文件夹移动到另一个文件夹。如果我将其存储为一个简单的字符串,然后将AppleScript应用程序包从我的Documents文件夹移动到我的桌面,则该路径将不再有效,并且每次运行该脚本时都必须更新该属性。 / p>

无论如何,我们会将recentItemsString设置为AppleScript do shell script recentItemsFinagler工具的结果,该工具是一个字符串。要将其转换为表示路径的AppleScript字符串列表,请使用paragraphs of命令。

因此,通过在脚本或AppleScript应用程序的包中包含可执行文件,您可以在不到10行代码中获得所需的结果。

示例项目:RecentItemsFinagler.zip

答案 2 :(得分:0)

我知道回答我自己的问题有点讽刺,但多亏了Daij-Djan和很多谷歌搜索,我找到了答案。毫无疑问,这不是最干净的方式,但它可以满足我的需要。我希望这可以帮助将来的某个人!

#import "AppDelegate.h"

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    LSSharedFileListRef list = LSSharedFileListCreate(NULL, kLSSharedFileListRecentDocumentItems, 0);
    CFArrayRef items = LSSharedFileListCopySnapshot(list, NULL);


    NSArray *paths = NSSearchPathForDirectoriesInDomains
    (NSLibraryDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];

    NSString *fileName = [NSString stringWithFormat:@"%@/Application Support/MyAppName/RecentFiles.txt",documentsDirectory];

    NSString *content = @"";
    [content writeToFile:fileName
              atomically:NO
                encoding:NSStringEncodingConversionAllowLossy
                   error:nil];

    for(int i=0; i<CFArrayGetCount(items); i++) {
        LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(items, i);

        CFURLRef itemURL = NULL;
        LSSharedFileListItemResolve(item, kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes, &itemURL, NULL);

       if (itemURL != (NULL)) {

        NSString *str = [NSString stringWithFormat:@"%@\n", itemURL]; //get text from textField

            NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:fileName];

            // move to the end of the file
            [fileHandle seekToEndOfFile];

            // convert the string to an NSData object
            NSData *textData = [str dataUsingEncoding:NSUTF8StringEncoding];

            // write the data to the end of the file
            [fileHandle writeData:textData];

            // clean up
            [fileHandle closeFile];


             }

    }
    exit(0);
}

@end