Частичное доверие доступа к объектам JavaScript в XBAP через HostScript: исключение SecurityEx в обратных вызовах - PullRequest
1 голос
/ 13 января 2011

Я столкнулся с проблемой с функцией взаимодействия сценариев XBAP, которая была добавлена ​​в WPF 4. Она включает в себя комбинацию следующего:

  1. Доступ к членам объекта сценария из .NET
  2. Запуск .NET-кода в обратном вызове, вызванном из JavaScript
  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...
}

Это прекрасно работает, даже при частичном развертывании доверия. (Я использую настройки безопасности ClickOnce по умолчанию, предлагаемые шаблоном приложения браузера WPF в Visual Studio 2010, который отлаживает 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) мою функцию JavaScript CallMethod, которая, в свою очередь, вызывает мой код .NET, в частности, вызывает метод MyMethod, предоставляемый моим CallbackClass. (Или я мог бы пометить метод обратного вызова как метод по умолчанию с атрибутом [DispId(0)], что позволило бы мне упростить код JavaScript - сценарий мог обрабатывать сам аргумент как метод. Любой подход дает одинаковые результаты.)

Обратный вызов MyMethod успешно вызван. В отладчике я вижу, что аргумент, переданный из JavaScript (42), проходит правильно (будучи правильно приведен к int). И когда мой метод возвращается, возвращаемая им строка заканчивается в моем пользовательском интерфейсе HTML благодаря остальной части функции CallMethod.

Отлично - мы можем сделать 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 показывает два лота [Нативный в управляемый переход] и [Переход домена приложения] - так что это весь стек. (Очевидно, мы сейчас находимся в другом потоке. Этот обратный вызов происходит на том, что панель «Потоки» описывает как рабочий поток - я вижу, что основной поток все еще находится внутри моего обработчика нажатия кнопок WPF, ожидая вызова JavaScript CallMethod функция для возврата.)

Очевидно, проблема в том, что DLR обернул объект JavaScript в ComBinder, что требует полного доверия. Но в более раннем случае, когда я вызывал метод JavaScript через HostScript и он возвращал мне объект, HostScript обернул его в System.Windows.Interop.DynamicScriptObject для меня.

Класс DynamicScriptObject специфичен для взаимодействия сценариев XBAP WPF - он не является частью обычных типов DLR и определен в PresentationFramework.dll. Насколько я могу судить, одно из заданий, которые он выполняет, - сделать возможным использование ключевого слова C # dynamic для доступа к свойствам JavaScript без необходимости полного доверия, даже если эти свойства доступны через взаимодействие COM (которое обычно требует полного доверия). ) под одеялом.

Насколько я могу судить, проблема в том, что вы получаете эти DynamicScriptObject оболочки только для объектов, которые возвращаются из других DynamicScriptObject экземпляров (таких как HostScript). С обратными вызовами такое обертывание не происходит. В моем обратном вызове я получаю динамическую оболочку, которую C # обычно дает мне в старых сценариях взаимодействия COM, и в этот момент мне требуется полное доверие.

Работа с полным доверием работает нормально - это будет комбинация «1 и 2» из списка выше. Но я не хочу иметь полное доверие. (Я хочу 1, 2, и 3). И вне ситуаций обратного вызова я могу получить доступ к элементам объекта JavaScript просто отлично. Кажется непоследовательным, что я могу получить доступ к объекту JavaScript большую часть времени, но доступ к идентичному объекту в обратном вызове запрещен.

Есть ли способ обойти это? Или я обречен на выполнение моего кода с полным доверием, если я хочу сделать что-нибудь интересное в обратном вызове?

1 Ответ

0 голосов
/ 13 января 2011

Я давно не делал XBAP, но мне любопытно, может ли проблема вызывать динамический тип. Попробуйте изменить динамический параметр на тип объекта и посмотрите, будет ли он работать.

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