开发者

Partial Trust JavaScript Object Access in XBAP via HostScript: SecurityException in Callbacks

I've encountered a problem with the XBAP Script Interop feature that was added in WPF 4. It involves a combination of the following:

  1. Accessing members of a script object from .NET
  2. Running .NET code in a callback invoked from JavaScript
  3. Running in Partial trust

This seems to be a "pick any two" scenario... If I try and do all three of those things, I get a SecurityException.

For example, combining 1 and 3 is easy. I can put this into my hosting web page's script:

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

And then in, say, a button click handler in my WPF code behind, I c开发者_StackOverflow中文版an do this:

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...
}

That works fine, even in a partial trust deployment. (I'm using the default ClickOnce security settings offered by the WPF Browser Application template in Visual Studio 2010, which debugs the XBAP as though it were running in the Internet zone.) So far, so good.

I can also combine 2 and 3. To make my .NET method callable from JavaScript, sadly we can't just pass a delegate, we have to do this:

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

and then I can declare a JavaScript method that looks like this:

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

and now in, say, a WPF button click handler, I can do this:

script.CallMethod(new CallbackClass());

So my WPF code calls (via BrowserInteropHelper.HostScript) my JavaScript CallMethod function, which in turn calls my .NET code back - specifically, it calls the MyMethod method exposed by my CallbackClass. (Or I could mark the callback method as a default method with a [DispId(0)] attribute, which would let me simplify the JavaScript code - the script could treat the argument itself as a method. Either approach yields the same results.)

The MyMethod callback is successfully called. I can see in the debugger that the argument passed from JavaScript (42) is getting through correctly (having been properly coerced to an int). And when my method returns, the string that it returns ends up in my HTML UI thanks to the rest of the CallMethod function.

Great - so we can do 2 and 3.

But what about combining all three? I want to modify my callback class so that it can work with script objects just like the one returned by my first snippet, the ReturnSomething function. We know that it's perfectly possible to work with such objects because that first example succeded. So you'd think I could do this:

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

and then modify my JavaScript to look like this:

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

and then call the method from my WPF button click handler as before:

script.CallMethod(new CallbackClass());

this successfully calls the JavaScript CallMethod function, which successfully calls back the MyMethod C# method, but when that method attempts to retrieve the arg.Foo property, I get a SecurityException with a message of RequestFailed. Here's the call stack:

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)

That's the whole trace as reported by the exception. And above CallbackClass.MyMethod, Visual Studio is showing two lots of [Native to Managed Transition] and an [AppDomain Transition] - so that's the whole of the stack. (Apparently we're on a different thread now. This callback is happening on what the Threads panel describes as a Worker Thread - I can see that the Main Thread is still sat inside my WPF button click handler, waiting for the call to the JavaScript CallMethod function to return.)

Apparently the problem is that the DLR has ended up wrapping the JavaScript object in the ComBinder which demands full trust. But in the earlier case where I called a JavaScript method via HostScript and it returned me an object, the HostScript wrapped it in a System.Windows.Interop.DynamicScriptObject for me.

The DynamicScriptObject class is specific to WPFs XBAP script interop - it's not part of the usual DLR types, and it's defined in PresentationFramework.dll. As far as I can tell, one of the jobs it does is to make it possible to use C#'s dynamic keyword to access JavaScript properties without needing full trust, even though those properties are being accessed through COM interop (which usually requires full trust) under the covers.

As far as I can tell, the problem is that you only get these DynamicScriptObject wrappers for objects that are returned from other DynamicScriptObject instances (such as HostScript). With callbacks, that wrapping doesn't seem to occur. In my callback, I'm getting the sort of dynamic wrapper C# would normally give me in plain old COM interop scenarios, at which point, it demands that I have full trust.

Running it with full trust works fine - that would be the "1 and 2" combination from the list above. But I don't want to have full trust. (I want 1, 2, and 3.) And outside of callback situations, I can access JavaScript object members just fine. It seems inconsistent that I can access a JavaScript object just fine most of the time, but accessing an identical object in a callback is forbidden.

Is there a way around this? Or am I doomed to run my code in full trust if I want to do anything interesting in a callback?


I haven't done XBAP in a while, but I am curious if it is the dynamic type that could be causing the issue. Try changing the dynamic parameter to type object and see if it will work.

[ComVisible(true)] 
public class CallbackClass 
{     
    public string MyMethod(object arg)     
    {         
          return "Arg is: " + arg.ToString();  
    } 
} 
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜