2

我们的应用程序托管 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 组件在回调到提供的脚本函数时遇到异常???

4

1 回答 1

2

哇,这被严重低估了。

答案是:

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

  1. QI在要调用的脚本函数上获取IDispatchEx指针。
  2. 构造一个同时实现IServiceProviderICanHandleException的对象;例如CScriptErrorCapturer
    • IServiceProvider::QueryService可以返回 E_NOINTERFACE
    • 如果脚本回调函数在 InvokEx'd 时抛出但未捕获异常(见下文),则ICanHandleException::CanHandleException将获得 EXCEPINFO 和 VARIANT*(在 MSDN 上查看文档)。
    • 变体将包含抛出的对象,它可能是一个错误对象。
    • 尝试从 IDispatch 获取此Error对象的“数字”和“消息”属性,其中“数字”表示实际的脚本错误 (HRESULT)。
    • 这些值可以/应该用于更新 EXCEPINFO 代码(可选)bstrDescription,以便将错误传播到调用脚本。如果您不更新scode,那么引擎将抛出一个“抛出但未捕获的异常”(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。

于 2010-07-02T17:04:55.527 回答