AppleScript 和崩溃

发布于 2024-11-10 11:20:40 字数 2803 浏览 1 评论 0原文

我正在尝试从我的 C++ 应用程序执行苹果脚本,但我仍然没有成功使其稳定。 我的第一种方法是使用 Carbon API:

  1. ::OSACompileExecute(Component, &ScriptTextDesc, kOSANullScript, kOSAModeNull, &ResultID); 我在每个脚本执行时创建组件并在同一范围内发布它。但我遇到了这个问题:有时尝试释放组件时会崩溃。所以看来脚本引擎中出现了一些问题,并且它以某种方式损坏了组件。我没有找到任何灵魂,所以我切换到另一种类似的方法:
  2. ::OSACompileExecute(m_Component, &ScriptTextDesc, kOSANullScript, kOSAModeNull, &ResultID); 其中m_Component是在ctor中创建并在应用程序执行期间生存的ComponentInstance。因此,由于应用程序工作时缺少释放步骤,释放组件时的崩溃就消失了。但还有另一个问题:当用户尝试注销时,会显示弹出窗口,其中显示“MyApp 阻止注销”(类似的内容)并且 MyApp 关闭。因此,当我关闭此弹出窗口时,我可以注销。所以看来组件释放的问题做了一些告诉系统它无法关闭的事情。我在这里没有找到任何解决方案,所以我决定采用第三种方法:
  3. Cocoa - NSAppleScript 解决方案非常简单且很小:

    QString ExecuteAppleScript(const QString& strScriptText)
    {
    QString strResult;
    
    NSAutoreleasePool* 池 = [[NSAutoreleasePool alloc] init];
    NSString* ScriptText = [[NSString alloc] initWithBytes:strScriptText.toUtf8().data() 长度:strScriptText.size() 
        编码:NSUTF8StringEncoding];
    NSAppleScript* 脚本 = [[NSAppleScript alloc] initWithSource:ScriptText];
    如果(脚本)
    {
        NSDictionary* ErrorInfo = nil;
        NSAppleEventDescriptor* 结果 = [脚本executeAndReturnError:&ErrorInfo];
        如果(结果)
        {
           strResult = QString::fromUtf8([[结果字符串值] UTF8String]);
        }
        否则如果(错误信息)
        {
           QString strErrorInfo = QString::fromUtf8([[ErrorInfo 描述] UTF8String]);
        }
    }
    [脚本文本发布];
    [矿池释放];
    
    返回字符串结果;
    

    }

这里有一些 Qt 类,但它们不相关。所以它可以工作,但它会在 NSAppleScript 内部的不同位置崩溃,日志之一:

Thread 5 Crashed:
0   com.apple.applescript           0x000000010ff0a4c9 ASCoerceToDesc(unsigned int, unsigned int, int, AEDesc*) + 84
1   ...ple.CoreServices.CarbonCore  0x00007fff86545e48 CallComponentFunction + 28
2   com.apple.applescript           0x000000010ff05cb1 AppleScriptComponent + 1728
3   com.apple.applescript           0x000000010ff1ebd0 AGenericCall::Delegate(ComponentInstanceRecord*) + 46
4   com.apple.applescript           0x000000010ff1e524 AGenericManager::HandleOSACall(ComponentParameters*) + 54
5   com.apple.applescript           0x000000010ff1e4b4 GenericComponent + 219
6   com.apple.openscripting         0x00007fff8716ae34 OSACoerceToDesc + 65
7   com.apple.Foundation            0x00007fff87c61a83 -[NSAppleScript(NSPrivate) _executeWithMode:andReturnError:] + 170

所以现在我不知道我还能做些什么来克服这些 AppleScript 崩溃...

附加信息:

  1. AppleScript 至少经常执行每秒 1 个脚本。
  2. 脚本是(只是从 C++ 代码中获取):

    执行 shell 脚本 \"ioreg -c IOHIDSystem" "| perl -ane 'if (/Idle/) {$idle=(pop @F)/1000000000; print $idle,\\"\\";last}'\"

  3. AppleScipt 不是从主线程执行的。我听说 10.6.6 之前的 MacOS X 版本上 NSAppleScript 存在问题,但我在 10.6.7 上测试了它。无论如何,方法 1 和方法 2 不应受到线程的困扰。
  4. 崩溃是随机的。因此它可以全天工作,也可能在启动后立即崩溃。

有人可以帮我吗? 先感谢您!

I'm trying to execute an apple script from my C++ application but I still no have success to make it stable.
My first approach was with Carbon API:

  1. ::OSACompileExecute(Component, &ScriptTextDesc, kOSANullScript, kOSAModeNull, &ResultID);
    Where I created component every script execution and release it in the same scope. But I stuck with the problem: sometimes it crashes when trying to release Component. So it seems that some problem is occured in the script engine and it corrupts Component somehow. I didn't find any soultions so I switch to another but similar approach:
  2. ::OSACompileExecute(m_Component, &ScriptTextDesc, kOSANullScript, kOSAModeNull, &ResultID);
    Where m_Component is the ComponentInstance which is created in ctor and lives during the application execution. So the crashes on releasing Component is gone due to the absence that release step while application is working. But there is another problem: when user tries to log out then pop-up is shown which says "MyApp prevents log out"(something like that) and MyApp closes. So when I close this pop-up I can log out. So it seems that the problem with Component releasing do something that tells System that it can't be closed. I didn't find any solution here so I decided to sitch to the 3rd approach:
  3. Cocoa - NSAppleScript
    The solution is very simple and small:

    QString ExecuteAppleScript(const QString& strScriptText)
    {
    QString strResult;
    
    NSAutoreleasePool* Pool = [[NSAutoreleasePool alloc] init];
    NSString* ScriptText = [[NSString alloc] initWithBytes:strScriptText.toUtf8().data() length:strScriptText.size() 
        encoding:NSUTF8StringEncoding];
    NSAppleScript* Script = [[NSAppleScript alloc] initWithSource:ScriptText];
    if (Script)
    {
        NSDictionary* ErrorInfo = nil;
        NSAppleEventDescriptor* Result = [Script executeAndReturnError:&ErrorInfo];
        if (Result)
        {
           strResult = QString::fromUtf8([[Result stringValue] UTF8String]);
        }
        else if (ErrorInfo)
        {
           QString strErrorInfo = QString::fromUtf8([[ErrorInfo description] UTF8String]);
        }
    }
    [ScriptText release];
    [Pool release];
    
    return strResult;
    

    }

There are a bit of Qt classes here but they are not relevant. So it works but it crashes with different places inside the NSAppleScript guts, one of the logs:

Thread 5 Crashed:
0   com.apple.applescript           0x000000010ff0a4c9 ASCoerceToDesc(unsigned int, unsigned int, int, AEDesc*) + 84
1   ...ple.CoreServices.CarbonCore  0x00007fff86545e48 CallComponentFunction + 28
2   com.apple.applescript           0x000000010ff05cb1 AppleScriptComponent + 1728
3   com.apple.applescript           0x000000010ff1ebd0 AGenericCall::Delegate(ComponentInstanceRecord*) + 46
4   com.apple.applescript           0x000000010ff1e524 AGenericManager::HandleOSACall(ComponentParameters*) + 54
5   com.apple.applescript           0x000000010ff1e4b4 GenericComponent + 219
6   com.apple.openscripting         0x00007fff8716ae34 OSACoerceToDesc + 65
7   com.apple.Foundation            0x00007fff87c61a83 -[NSAppleScript(NSPrivate) _executeWithMode:andReturnError:] + 170

So for now I don't know what else I can do to overcome those AppleScript crashes...

Additional info:

  1. AppleScript executes very often, at least 1 script per second.
  2. Script is(just took it from the C++ code):

    do shell script \"ioreg -c IOHIDSystem"
    "| perl -ane 'if (/Idle/) {$idle=(pop @F)/1000000000; print $idle,\\"\\";last}'\"

  3. AppleScipt is executed not from the Main thread. I heard about problems with NSAppleScript on MacOS X versions before 10.6.6 but I test it on 10.6.7. Anyway 1 and 2 approaches should not be bothered by threads.
  4. Crashes are random. So it can work all the day or can crashe right after the start.

Could anyone help me with it?
Thank you in advance!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

阳光的暖冬 2024-11-17 11:20:40

您提到您正在从不同的线程运行 NSApplescript 。如果您查看 NSApplescript 的文档,在顶部它会这样说:

Important:  You should access NSAppleScript only from the main thread.

所以只要知道它不是线程安全的,这可能就是您出现问题的原因。

编辑:我在您的代码中发现了一些其他问题。首先,您没有释放使用 alloc/init 创建的“脚本”。接下来,这是主要问题......您的“ErrorInfo”是带有键/值对的字典。您没有使用键来获取值,这就是崩溃发生的地方。返回字典的“描述”时遇到问题。这是我用来创建该错误字典的 NSString 的示例代码,因此请执行类似的操作。

NSString* errorInfo = [NSString stringWithFormat:@"Error:%@ %@", [errorDict valueForKey:@"NSAppleScriptErrorNumber"], [errorDict valueForKey:@"NSAppleScriptErrorMessage"]];

最后,您是否使用“performSelectorOnMainThread”之类的方法来确保它在主线程上运行?

You mention that you're running NSApplescript from a different thread. If you look in the documentation for NSApplescript, right at the top it says this:

Important:  You should access NSAppleScript only from the main thread.

So just know that it's not thread safe and that's probably the reason for your problems.

EDIT: I see a couple other issues in your code. First, you are not releasing "Script" which you have created using alloc/init. Next, and this is the main issue... your "ErrorInfo" is a dictionary with key/value pairs. You are not using the keys to get the values and that's where it looks like your crash is occuring. It's having trouble returning the "description" of the dictionary. Here's example code I use to create an NSString of that error dictionary, so do something like this.

NSString* errorInfo = [NSString stringWithFormat:@"Error:%@ %@", [errorDict valueForKey:@"NSAppleScriptErrorNumber"], [errorDict valueForKey:@"NSAppleScriptErrorMessage"]];

Finally, are you using some method like "performSelectorOnMainThread" to ensure it's run on the main thread?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文