application:openFile:尝试打开自己的文件类型时报告错误

时间:2016-01-23 16:00:19

标签: objective-c xcode macos cocoa chromium-embedded

我的应用程序拥有自己独特的文件类型。当用户双击文件并且应用程序未运行时,应用程序启动但报告以下错误:

Error dialog on dbl-click

我的申请基于Chromium(CEF)。我既没有使用application:applicationWillFinishLaunching也没有application:applicationDidFinishLaunching用于任何设置部分,但似乎应用程序:openFile在应用程序完全启动之前就被调用了。

问题

  • 有没有办法等到应用程序完全初始化后才能正确打开/处理文件
  • 是否有其他方法可以在应用程序完全初始化后调用

我的AppDelegate实现如下:

// ****************************************************************************
// application:openFile
// ****************************************************************************

- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
{
  return [self processFile:filename];
}

// ****************************************************************************
// processFile
// ****************************************************************************

- (BOOL)processFile:(NSString *)file
{
  std::string fileName([file UTF8String]);
  au::arcwork::Handler* handler = au::arcwork::Handler::GetInstance();
  handler->OnOpenFile(fileName);
  return YES;
}

2 个答案:

答案 0 :(得分:0)

不要直接在true方法中调用[self processFile:filename],而是尝试将其存储到ivar中,并在应用程序完全初始化时调用application:openFile:

processFile:

答案 1 :(得分:0)

经过大量的研究和调试后,我想出了一个解决方案,如何在OS X上为基于Chromium的应用程序实现application:openFile。首先,有3个部分/层需要解决

  1. 可可部分application:openFile
  2. 使用浏览器初始化代码的Chromium部分
  3. JavaScript部分及其初始化部分
  4. 1 开始:

    application:openFile的Apple文档已在讨论部分中描述,application:openFile之前调用了applicationDidFinishLaunching。这意味着,如果你依赖一个完整的初始化客户端(我无法想象它是怎么回事?),你必须在某处存储文件的URL,例如application:openFile中的ivar/property或我的std::vector Chromium handler中我的示例中的the "Azure Let's Encrypt" site extension。只有当应用程序完全初始化并显示浏览器窗口时,您才能直接调用Chromium handler method,然后调用相应的JavaScript函数!

    // ***
    // application:openFile
    // ***
    
    - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename {
      AUApplication * clientApp = (AUApplication *)theApplication;
      NSWindow* targetWindow = [clientApp findTargetWindow];
      //Check if browser window is up and running
      if (targetWindow) {
        [self processFile:filename];
      }
      else {
        //This method saves the file URL to open the file 
        //when the application+JavaScript is fully initialized
        au::test::Handler* handler = au::test::Handler::GetInstance();
        handler->AddPendingFile([filename UTF8String]); 
      }
      return YES;
    }
    
    // ***
    // processFile
    // ***
    
    - (BOOL)processFile:(NSString *)file
    {
      //This method calls the JavaScript function to open the file
      std::string fileName([file UTF8String]);
      au::test::Handler* handler = au::test::Handler::GetInstance();
      handler->OnOpenFile(fileName); 
      return YES;
    }
    

    2 部分:

    Chromium side上,您必须将文件URL存储在适当的结构中。我正在使用std::vector。文件网址保存在Chromium方法OnLoadEnd中。这里Chromium已经在浏览器中加载了您的HTML + JavaScript部分。不过要小心。 JavaScript的初始化还没有完成!在下面的示例中,我必须指定一个JavaScript属性来存储文件URL。

    // ***
    // Handler::OnLoadEnd
    // ***
    
    void Handler::OnLoadEnd(CefRefPtr<CefBrowser> browser,
                   CefRefPtr<CefFrame> frame,
                   int httpStatusCode) {
      if (!m_pendingOpenFiles.empty()) {
        std::string file(m_pendingOpenFiles[0]);
        std::cout << "Pending file for later opening: " << file << std::endl;
        //This method below is calling a JavaScript function to assign
        //a property `pendingFile`
        OnPendingFile(file);
        //Don't forget to pop the file URL afterwards 
        //or use another store container instead of `std::vector`
        //if you don't plan to implement `application:openFiles` as well!!!
        m_pendingOpenFiles.pop_back();
      }
    }
    

    3 部分:

    在JavaScript方面,如果分配了属性pendingFile,则必须在应用程序初始化后进行检查,以便正确打开文件。