0

我无法向大量使用 Silverlight - JavaScript 互操作性的现有代码添加适当的异常处理。在这种情况下,我的 JavaScript 可能会引发我想在 Silverlight 中有意义地处理的异常。

在 Silverlight 中,我创建了一个 JavaScript 对象的实例,然后我在该对象上调用了一个方法:

public class MyWrapper
{
    dynamic _myJSObject;

    public MyWrapper()
    {
        _myJSObject = HtmlPage.Window.CreateInstance("MyJSObject");
    }

    public int MyMethod()
    {
        try
        {
            int result = (int)_myJSObject.MyMethod();
        }
        catch (Exception ex)
        {
            // I want to add meaningful exception handling here
        }      
    }
}

每当MyJSObject.MyMethod抛出异常时,都会出现两个问题:

  • 浏览器显示发生异常的消息。
  • 有关异常的信息不会传递给我的托管代码。相反,我得到一个RuntimeBinderException只是说“不能调用非委托类型”并且不包含任何其他信息。这似乎与这里描述的不符;我期待一个InvalidOperationException.

我试图避免强制转换方法的返回值:

object tmp= _myJSObject.MyMethod();

这没什么区别。更改 JavaScript 端抛出的异常类型也没有效果。

MyJSObject.prototype.MyMethod = function ()
                                {
                                    throw "Hello Silverlight!";
                                }

我现在能想到的唯一解决方案是滥用函数的返回值来传递有关异常的信息,但这会使我的代码变得更加丑陋......所以:

为什么我看到的行为与文档中描述的不同?它与我使用dynamic某种方式有关吗?如何在我的托管代码中正确处理 JavaScript 中发生的异常?

4

1 回答 1

0

经过相当多的实验,我得出结论,没有办法直接处理 Silverlight 中的 JavaScript 异常。为了能够处理异常,需要稍微更改 JavaScript 代码。

我没有抛出错误,而是返回它:

function MyMethod()
{
    try
    {
        // Possible exception here
    }
    catch (ex)
    {
        return new Error(ex);
    }
}

然后在 Silverlight 方面,我使用包装器ScriptObject将返回值再次转换为异常。这里的关键是TryInvokeMember方法:

public class ScriptObjectWrapper : DynamicObject
{
    private ScriptObject _scriptObject;

    public ScriptObjectWrapper(ScriptObject scriptObject)
    {
        _scriptObject = scriptObject;
    }

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        result = _scriptObject.Invoke(binder.Name, args);

        ScriptObject s = result as ScriptObject;
        if (s != null)
        {
            // The JavaScript Error object defines name and message properties.
            string name = s.GetProperty("name") as string;
            string message = s.GetProperty("message") as string;
            if (name != null && message != null && name.EndsWith("Error"))
            {
                // Customize this to throw a more specific exception type
                // that also exposed the name property.
                throw new Exception(message);
            }
        }

        return true;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        try
        {
            _scriptObject.SetProperty(binder.Name, value);
            return true;
        }
        catch
        {
            return false;
        }
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        try
        {
            result = _scriptObject.GetProperty(binder.Name);
            return true;
        }
        catch
        {
            result = null;
            return false;
        }
    }

}

潜在地你可以改进这个包装器,使它实际上透明地注入 JavaScript try-catch 机制,但是在我的例子中,我可以直接控制 JavaScript 源代码,所以没有必要这样做。

只要属性Error以.nameError

要使用包装器,原始代码将更改为:

public MyWrapper()
{
    _myJSObject = new ScriptObjectWrapper(
                  HtmlPage.Window.CreateInstance("MyJSObject"));
}
于 2012-08-27T14:28:22.520 回答