我遇到了WPF 4中添加的XBAP Script Interop功能的问题。它涉及以下各项的组合:
这似乎是“挑选任何两个”场景......如果我尝试做所有这三件事,我会得到一个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中,其调试的XBAP,就好像是在Internet区域运行WPF浏览器应用程序模板提供的默认的ClickOnce安全设置)。到目前为止,一切都很好。
我也可以将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
)我的JavaScript CallMethod
功能,后者又调用我的.NET代码背面 - 即,它调用被我CallbackClass露出的MyMethod
方法。 (或者我可以将回调方法标记为具有[DispId(0)]
属性的默认方法,这可以让我简化JavaScript代码 - 脚本可以将参数本身视为一种方法。这两种方法都会产生相同的结果。)< / p>
成功调用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());
这成功调用了JavaScript CallMethod
函数,该函数成功回调了MyMethod
C#方法,但当该方法尝试检索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
中。但是在之前的情况下,我通过HostScript
调用了JavaScript方法并且它返回了一个对象,HostScript
将它包装在System.Windows.Interop.DynamicScriptObject
中。
DynamicScriptObject
类特定于WPF XBAP脚本互操作 - 它不是通常的DLR类型的一部分,它在PresentationFramework.dll
中定义。至于我可以告诉大家,它的工作之一就是使人们有可能使用的C#dynamic
关键词,而不需要完全信任来访问JavaScript属性,即使这些属性正在通过COM互操作访问(这通常需要完全信任)。
据我所知,问题是你只能从其他DynamicScriptObject
实例(例如DynamicScriptObject
)返回的对象获得这些HostScript
包装器。使用回调,似乎不会发生包装。在我的回调中,我得到了一种动态包装器C#通常会在普通的COM互操作场景中给我,此时它要求我完全信任。
以完全信任方式运行它可以正常工作 - 这将是上面列表中的“1和2”组合。但我不想完全信任。 (我想要1,2,和 3。)在回调情况之外,我可以很好地访问JavaScript对象成员。似乎不一致的是我可以在大多数时间访问JavaScript对象,但禁止在回调中访问相同的对象。
有解决方法吗?或者我注定要完全信任地运行我的代码,如果我想在回调中做任何有趣的事情?
答案 0 :(得分:0)
我有一段时间没有做过XBAP,但我很好奇它是否是导致问题的动态类型。尝试将动态参数更改为类型对象,看它是否有效。
[ComVisible(true)]
public class CallbackClass
{
public string MyMethod(object arg)
{
return "Arg is: " + arg.ToString();
}
}