1

我遇到了 WPF 4 中添加的 XBAP 脚本互操作功能的问题。它涉及以下组合:

  1. 从 .NET 访问脚本对象的成员
  2. 在从 JavaScript 调用的回调中运行 .NET 代码
  3. 在部分信任中运行

这似乎是一个“选择任何两个”的场景......如果我尝试做所有这三件事,我会得到一个SecurityException.

例如,组合 1 和 3 很容易。我可以将其放入托管网页的脚本中:

function ReturnSomething()
{
    return { Foo: "Hello", Bar: 42 };
}

然后在我的 WPF 代码后面的按钮单击处理程序中,我可以这样做:

dynamic script = BrowserInteropHelper.HostScript;
if (script != null)
{
    dynamic result = script.ReturnSomething();
    string foo = result.Foo;
    int bar = result.Bar;
    // go on to do something useful with foo and bar...
}

即使在部分信任部署中,它也能正常工作。(我在 Visual Studio 2010 中使用 WPF 浏览器应用程序模板提供的默认 ClickOnce 安全设置,它可以像在 Internet 区域中运行一样调试 XBAP。)到目前为止,一切都很好。

我也可以将 2 和 3 结合起来。为了使我的 .NET 方法可以从 JavaScript 调用,遗憾的是我们不能只传递一个委托,我们必须这样做:

[ComVisible(true)]
public class CallbackClass
{
    public string MyMethod(int arg)
    {
        return "Value: " + arg;
    }
}

然后我可以声明一个如下所示的 JavaScript 方法:

function CallMethod(obj)
{
    var result = obj.MyMethod(42);
    var myElement = document.getElementById("myElement");
    myElement.innerText = "Result: " + result;
}

现在在一个 WPF 按钮单击处理程序中,我可以这样做:

script.CallMethod(new CallbackClass());

因此,我的 WPF 代码调用(通过BrowserInteropHelper.HostScript)我的 JavaScriptCallMethod函数,该函数又调用了我的 .NET 代码——具体来说,它调用了MyMethod我的 CallbackClass 公开的方法。(或者我可以将回调方法标记为具有[DispId(0)]属性的默认方法,这可以让我简化 JavaScript 代码 - 脚本可以将参数本身视为方法。任何一种方法都会产生相同的结果。)

MyMethod成功调用回调。我可以在调试器中看到从 JavaScript (42) 传递的参数正确通过(已正确强制为 int)。当我的方法返回时,由于CallMethod函数的其余部分,它返回的字符串最终会出现在我的 HTML UI 中。

太好了 - 所以我们可以做 2 和 3。

但是将这三者结合起来呢?我想修改我的回调类,以便它可以与脚本对象一起使用,就像我的第一个片段返回的那个ReturnSomething函数一样。我们知道使用这样的对象是完全可能的,因为第一个示例成功了。所以你会认为我可以这样做:

[ComVisible(true)]
public class CallbackClass
{
    public string MyMethod(dynamic arg)
    {
        return "Foo: " + arg.Foo + ", Bar: " + arg.Bar;
    }
}

然后修改我的 JavaScript 看起来像这样:

function CallMethod(obj)
{
    var result = obj.MyMethod({ Foo: "Hello", Bar: 42 });
    var myElement = document.getElementById("myElement");
    myElement.innerText = "Result: " + result;
}

然后像以前一样从我的 WPF 按钮单击处理程序中调用该方法:

script.CallMethod(new CallbackClass());

这成功调用了 JavaScriptCallMethod函数,该函数成功回调了MyMethodC# 方法,但是当该方法尝试检索arg.Foo属性时,我得到一个SecurityException带有RequestFailed. 这是调用堆栈:

at System.Security.CodeAccessSecurityEngine.Check(Object demand, StackCrawlMark& stackMark, Boolean isPermSet)
at System.Security.CodeAccessSecurityEngine.Check(PermissionSet permSet, StackCrawlMark& stackMark)
at System.Security.PermissionSet.Demand()
at System.Dynamic.ComBinder.TryBindGetMember(GetMemberBinder binder, DynamicMetaObject instance, DynamicMetaObject& result, Boolean delayInvocation)
at Microsoft.CSharp.RuntimeBinder.CSharpGetMemberBinder.FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion)
at System.Dynamic.DynamicMetaObject.BindGetMember(GetMemberBinder binder)
at System.Dynamic.GetMemberBinder.Bind(DynamicMetaObject target, DynamicMetaObject[] args)
at System.Dynamic.DynamicMetaObjectBinder.Bind(Object[] args, ReadOnlyCollection`1 parameters, LabelTarget returnLabel)
at System.Runtime.CompilerServices.CallSiteBinder.BindCore[T](CallSite`1 site, Object[] args)
at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
at XBapDemo.CallbackClass.MyMethod(Object arg)

这就是异常报告的整个跟踪。在上面CallbackClass.MyMethod,Visual Studio 显示了两个 [Native to Managed Transition] 和一个 [AppDomain Transition] - 这就是整个堆栈。(显然我们现在在另一个线程上。这个回调发生在线程面板描述为工作线程的地方 - 我可以看到主线程仍然位于我的 WPF 按钮单击处理程序中,等待对 JavaScript 的调用CallMethod函数返回。)

显然,问题在于 DLR 最终将 JavaScript 对象包装在ComBinder需要完全信任的对象中。但是在前面的例子中,我调用了一个 JavaScript 方法HostScript,它返回给我一个对象,为我HostScript将它包装在 aSystem.Windows.Interop.DynamicScriptObject中。

该类DynamicScriptObject特定于 WPFs XBAP 脚本互操作 - 它不是通常 DLR 类型的一部分,它在PresentationFramework.dll. 据我所知,它所做的工作之一是使使用 C# 的dynamic关键字访问 JavaScript 属性而不需要完全信任成为可能,即使这些属性是通过 COM 互操作(通常需要完全信任)在盖子。

据我所知,问题在于您只能获取从其他实例(例如)DynamicScriptObject返回的对象的这些包装器。使用回调,这种包装似乎不会发生。在我的回调中,我得到了 C# 通常在普通的旧 COM 互操作场景中给我的那种动态包装器,此时,它要求我完全信任。DynamicScriptObjectHostScript

在完全信任的情况下运行它可以正常工作 - 这将是上面列表中的“1 和 2”组合。但我不想完全信任。(我想要 1、23。)在回调情况之外,我可以很好地访问 JavaScript 对象成员。大多数时候我都可以很好地访问 JavaScript 对象,但在回调中访问相同的对象是被禁止的,这似乎是不一致的。

有没有解决的办法?或者如果我想在回调中做任何有趣的事情,我是否注定要完全信任地运行我的代码?

4

1 回答 1

0

我有一段时间没有做过 XBAP,但我很好奇它是否是导致问题的动态类型。尝试将动态参数更改为类型对象,看看它是否会起作用。

[ComVisible(true)] 
public class CallbackClass 
{     
    public string MyMethod(object arg)     
    {         
          return "Arg is: " + arg.ToString();  
    } 
} 
于 2011-01-13T14:48:01.270 回答