WPF 4 で追加された XBAP スクリプト相互運用機能で問題が発生しました。これには、次の組み合わせが含まれます。
- .NET からスクリプト オブジェクトのメンバーにアクセスする
- JavaScript から呼び出されたコールバックで .NET コードを実行する
- 部分信頼での実行
これは「任意の 2 つを選択する」シナリオのようです...これら 3 つすべてを実行しようとすると、SecurityException
.
たとえば、1 と 3 を組み合わせるのは簡単です。これをホスティング Web ページのスクリプトに入れることができます。
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 セキュリティ設定を使用しています。これは、インターネット ゾーンで実行されているかのように 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 を実行できます。
しかし、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
関数が正常に呼び出され、 C# メソッドが正常に呼び出されますが、そのメソッドがプロパティMyMethod
を取得しようとすると、. コールスタックは次のとおりです。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] の 2 つのロットを表示しています。これがスタック全体です。(明らかに、現在は別のスレッド上にいます。このコールバックは、[スレッド] パネルでワーカー スレッドとして記述されているもので発生しています。メイン スレッドがまだ WPF ボタン クリック ハンドラ内にあり、JavaScript への呼び出しを待っていることがわかります。CallMethod
返す関数。)
ComBinder
どうやら問題は、DLR が完全な信頼を要求する で JavaScript オブジェクトをラップしてしまったことです。しかし、JavaScript メソッドを経由HostScript
して呼び出した前のケースでは、オブジェクトが返され、 forHostScript
でラップされていSystem.Windows.Interop.DynamicScriptObject
ました。
このDynamicScriptObject
クラスは、WPF の XBAP スクリプトの相互運用性に固有のものです。通常の DLR タイプの一部ではなく、 で定義されていPresentationFramework.dll
ます。私が知る限り、それが行う仕事の 1 つは、C# のdynamic
キーワードを使用して、完全な信頼を必要とせずに JavaScript プロパティにアクセスできるようにすることです。カバーします。
私が知る限り、問題は、他のインスタンス ( など)DynamicScriptObject
から返されるオブジェクトに対してのみこれらのラッパーを取得することです。コールバックでは、そのラッピングは発生しないようです。私のコールバックでは、単純な古い COM 相互運用シナリオで C# が通常提供する一種の動的ラッパーを取得しています。この時点で、完全な信頼が要求されます。DynamicScriptObject
HostScript
完全な信頼で実行すると問題なく動作します。これは、上記のリストの「1 と 2」の組み合わせになります。しかし、私は完全に信頼したくありません。(1、2、3 が必要です。) また、コールバックの状況以外では、JavaScript オブジェクト メンバーに問題なくアクセスできます。ほとんどの場合、JavaScript オブジェクトに問題なくアクセスできることには一貫性がないように思えますが、コールバックで同一のオブジェクトにアクセスすることは禁止されています。
これを回避する方法はありますか?それとも、コールバックで何か面白いことをしたい場合、コードを完全に信頼して実行する運命にあるのでしょうか?