深奥的 JScript 托管问题:IDispatch::Invoke 返回 SCRIPT_E_PROPAGATE 时的错误代码在哪里?

发布于 2024-09-07 13:25:48 字数 1269 浏览 0 评论 0原文

我们的应用程序托管 Windows Scripting Host JScript 引擎,并公开多个可以从脚本代码调用的域对象。

其中一个域对象是一个 COM 组件,它实现了 IDispatch(实际上是 IDispatchEx),并且具有一个将脚本函数作为回调参数(IDispatch* 作为参数)的方法。该 COM 组件由脚本调用,执行一些操作,然后通过提供的 IDispatch 参数回调到脚本,然后返回到调用脚本。

如果回调脚本碰巧抛出异常(例如,调用另一个 COM 组件,该组件返回 S_OK 以外的内容),则回调脚本上对 IDispatch::Invoke 的调用将返回 SCRIPT_E_PROPAGATE 而不是 HRESULT来自其他 COM 组件;不是来自其他 COM 对象的预期 HRESULT。如果我将该 HRESULT (SCRIPT_E_PROPAGATE) 返回给第一个 COM 组件的调用者(例如,调用脚本),则脚本引擎会正确地抛出来自其他 COM 对象的预期 HRESULT 的错误。

然而,实际错误却无处可寻。它不会从 Invoke 调用返回(返回值为 SCRIPT_E_PROPAGATE)。它不会通过提供给 Invoke 的 EXCEPINFO 返回(结构保持为空)。并且,它无法通过 GetErrorInfo 获得(调用返回 S_FALSE)!

Script
    Defines ScriptCallback = function() { return ComComponentB.doSomething(); }
    Invokes ComComponentA.execute(ScriptCallback)
        Invokes ScriptCallback()
            Invokes ComComponentB.doSomething()
                Returns E_FAIL (or some other HRESULT)
            Throws returned HRESULT
        Receives SCRIPT_E_PROPAGATE <--- WHERE IS THE ACTUAL ERROR?
        Returns SCRIPT_E_PROPAGATE
    Throws E_FAIL (or whatever HRESULT was returned from ComComponentB)

我真的很想得到这个错误,因为缓存它并在后续调用中返回相同的错误会很有用(获取错误通常涉及一个昂贵的操作,该操作由脚本函数作为参数传递,但我确实知道如何缓存错误)。有没有办法让脚本化的 COM 组件在回调提供的脚本函数期间抛出异常?

Our application hosts the Windows Scripting Host JScript engine and exposes several domain objects that can be called from script code.

One of the domain objects is a COM component that implements IDispatch (actually, IDispatchEx) and which has a method that takes a script-function as a call-back parameter (an IDispatch* as a parameter). This COM component is called by script, does some things, and then calls back into script via that supplied IDispatch parameter before returning to the calling script.

If the call-back script happens to throw an exception (e.g., makes a call to another COM component which returns something other than S_OK), then the call to IDispatch::Invoke on the call-back script will return SCRIPT_E_PROPAGATE instead of the HRESULT from the other COM component; not the expected HRESULT from the other COM object. If I return that HRESULT (SCRIPT_E_PROPAGATE) back to the caller of the first COM component (e.g., to the calling script), then the script engine correctly throws an error with the expected HRESULT from the other COM object.

However, the ACTUAL ERROR is nowhere to be found. It's not returned from the Invoke call (the return value is SCRIPT_E_PROPAGATE). It's not returned via the EXCEPINFO supplied to Invoke (the structure remains empty). AND, it's not available via GetErrorInfo (the call returns S_FALSE)!

Script
    Defines ScriptCallback = function() { return ComComponentB.doSomething(); }
    Invokes ComComponentA.execute(ScriptCallback)
        Invokes ScriptCallback()
            Invokes ComComponentB.doSomething()
                Returns E_FAIL (or some other HRESULT)
            Throws returned HRESULT
        Receives SCRIPT_E_PROPAGATE <--- WHERE IS THE ACTUAL ERROR?
        Returns SCRIPT_E_PROPAGATE
    Throws E_FAIL (or whatever HRESULT was returned from ComComponentB)

I'd really like to get my hands on that error, because it would be useful to cache it and return the same error on subsequent calls (getting to the error often involves an expensive operation that is defined by the script-function passed as a parameter, but I do know how to cache the error). Is there a way for a scripted COM component to get to an exception thrown during a call-back into a supplied script-function???

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

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

发布评论

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

评论(1

坐在坟头思考人生 2024-09-14 13:25:49

哇,这记录严重不足。

答案是:

在 COM 组件中对脚本进行回调...

  1. QI 获取要调用的脚本函数上的 IDispatchEx 指针。
  2. 构造一个同时实现 IServiceProvider 和 IServiceProvider 的对象。 ICanHandleException;例如CScriptErrorCapturer
    • IServiceProvider::QueryService 可以返回 E_NOINTERFACE
    • 如果脚本回调函数在 InvokEx 时抛出但未捕获异常(见下文),则 ICanHandleException::CanHandleException 将获得 EXCEPINFO 和 VARIANT*(请在 MSDN 上查找文档)。
    • 该变体将包含抛出的对象,该对象可能是一个 Error 对象。
    • 尝试从该 Error 对象的 IDispatch 中获取“number”和“message”属性,其中“number”代表实际的脚本错误 (HRESULT)。
    • 这些值可以/应该用于更新 EXCEPINFO scode 和(可选)bstrDescription,以便将错误传播到调用脚本。如果您不更新代码,那么引擎将抛出“异常已抛出但未捕获”(0x800A139E),这是您修改 EXCEPINFO 之前包含的内容。
    • 不确定是否应清除 pfnDeferredFillIn,但不执行此操作也能正常工作。
    • 在我的代码中,我在 CScriptErrorCapturer 中捕获了错误。
    • 返回S_OK。此处返回 E_FAIL 将中止整个脚本运行,并且不允许将异常抛出回原始调用脚本。
  3. 调用 IDispatchEx::InvokeEx 并将 CScriptErrorCapturer 作为 IServiceProvider 参数传递。
  4. 从 InvokeEx 返回后,查询 CScriptErrorCapturer 以查看它是否捕获了错误。根据 GoogleWebKit 中的 代码,有时 InvokeEx 可能会返回 S_OK,即使抛出错误。
  5. 不要触及 InvokeEx 的返回值,尤其是 SCRIPT_E_PROPAGATE (0x80020102)

注意:此链接包含上述一些未记录的 JScript HRESULTS。

Wow, this was seriously underdocumented.

The answer is to:

In the COM component making a callback into script...

  1. QI to get an IDispatchEx pointer on the script function to be called.
  2. Construct an object implementing both IServiceProvider & ICanHandleException; e.g. CScriptErrorCapturer.
    • IServiceProvider::QueryService can return E_NOINTERFACE
    • If the script callback function throws, but does not catch, an exception when InvokEx'd (see below), then ICanHandleException::CanHandleException will get an EXCEPINFO and VARIANT* (look on MSDN for documentation).
    • The variant will contain the object thrown, which might be an Error object.
    • Try to get the "number" and "message" properties from the IDispatch on this Error object, where "number" represents the actual script error (HRESULT).
    • These values can/should be used to update the EXCEPINFO scode and (optionally) bstrDescription in order to propagate the error up to the calling script. If you don't update the scode, then then engine will throw an "Exception thrown but not caught" (0x800A139E), which is what the EXCEPINFO contains before you modify it.
    • Not sure if pfnDeferredFillIn should be cleared, but it works without doing this.
    • In my code, I capture the error here in my CScriptErrorCapturer.
    • Return S_OK. Returning E_FAIL here will abort the entire script run, and not allow the exception to be thrown back up to the original calling script.
  3. Call IDispatchEx::InvokeEx and pass your CScriptErrorCapturer as the IServiceProvider parameter.
  4. Upon return from InvokeEx, query your CScriptErrorCapturer to see if it caught an error. According to code in the GoogleWebKit, sometimes InvokeEx may return S_OK, even if an error is thrown.
  5. Don't touch the return value from InvokeEx, especially if it is SCRIPT_E_PROPAGATE (0x80020102)

Note: this link contains some of the undocumented JScript HRESULTS described above.

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