6

WinRT (Windows 8) アプリで MVVM を使用したいのですが、私の要件の 1 つは、イベントをコマンド ( ICommand) にフックできるようにすることです。これは、ハンドラーを WinRT イベントに動的に追加する必要があることを意味します。here でそれを行う方法についての素晴らしい説明がありますが、私の問題は、コンパイル時にハンドラーのタイプが不明であることです(つまり、常にRoutedEventHandlerその例のようになるとは限りません)。

そのコードの一般的な実装を書き始め、式ツリーを使用してデリゲートを構築しました。その部分は機能します。私の問題は、動的に呼び出すことWindowsRuntimeMarshal.AddEventHandlerが失敗することです:

var rtMarshalType = typeof (WindowsRuntimeMarshal);
var eventHandlerMethod = rtMarshalType.GetRuntimeMethods().Single(x => x.IsStatic && x.Name == "AddEventHandler");
MethodInfo closedAddMethod = eventHandlerMethod.MakeGenericMethod(handlerType);
closedAddMethod.Invoke(null, new object[] {add, remove, handler});

これは呼び出しに失敗し、Invoke次のメッセージとともに InvalidOperationException をスローします。

API 'System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.AddEventHandler[ItemClickEventHandler](System.Func 2[Windows.UI.Xaml.Controls.ItemClickEventHandler,System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken], System.Action1[System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken], Windows.UI.Xaml.Controls.ItemClickEventHandler)' は、現在のプラットフォーム。詳細については、http: //go.microsoft.com/fwlink/?LinkId=248273を参照 してください。

上記の 4 行のコードをこれに置き換えると (イベント ハンドラーの型をハードコーディングしますが、これは私が望んでいるものではありません)、コードが機能し、イベントが期待どおりにアタッチされるため、適切な型があることはわかっています。

WindowsRuntimeMarshal.AddEventHandler<ItemClickEventHandler>(add, remove, handler);

参考までに、3 つのパラメーターは次のように定義されています (確かに、これは私の式ツリー構築コードの問題ではありません。私は現在、これらのデリゲートの明示的な定義を動的に呼び出しようAddEventHandlerとしてもまだ失敗しています):

Func<ItemClickEventHandler, EventRegistrationToken> add =
    a => (EventRegistrationToken) eventInfo.AddMethod.Invoke(instance, new object[] {a});
Action<EventRegistrationToken> remove = a => eventInfo.RemoveMethod.Invoke(instance, new object[] {a});
ItemClickEventHandler handler = (s, args) => command.Execute(args);

直接呼び出したときではなく、リフレクションを介して呼び出されたときに呼び出しが失敗するのはなぜですか?

コンパイル時にイベント ハンドラーのタイプがわからない場合、WinRT イベントを動的にアタッチするための代替ソリューションはありますか?

4

3 に答える 3

5

直接呼び出したときではなく、リフレクションを介して呼び出されたときに呼び出しが失敗するのはなぜですか?

回答:属性WindowsRuntimeMarshal.AddEventHandlerでマークされています[SecurityCritical]

リフレクションのセキュリティに関する考慮事項から: http://msdn.microsoft.com/en-us/library/stfy7tfc.aspx

必要なアクセス許可を条件として、コードはリフレクションを使用して次の種類のアクセスを実行できます 。 security-critical ではないパブリックメンバーにアクセスします。

AddEventHandlerそのため、パブリックであっても、リフレクションのセキュリティ クリティカルなメソッドを使用することはできません。このようなメソッドは、リフレクションを使用せずに自由に呼び出すことができ、独自のメソッドを自由に反映できます。これが、ソリューションが機能する理由です。

于 2012-10-16T02:31:58.253 に答える
0

いくつかいじくり回した後、これを行う方法を見つけました:

まず、このメソッドを宣言しました。

    private static void AddEventHandler<T>(Func<T,EventRegistrationToken> add, Action<EventRegistrationToken> remove, T handler)
    {
        WindowsRuntimeMarshal.AddEventHandler(add, remove, handler);            
    }

そして、リフレクションを介してそれを呼び出すことができます:

    var closedAddMethod = GetAddEventHandlerMethodInfo(handlerType);
    closedAddMethod.Invoke(null, new[] {add, remove, actualDelegate});

どこGetAddEventHandlerMethodInfoにある:

    private static MethodInfo GetAddEventHandlerMethodInfo(Type handlerType)
    {
        var rtMarshalType = typeof (AttachedCommand);
        var eventHandlerMethod = rtMarshalType.GetRuntimeMethods().Single(x => x.IsStatic && x.Name == "AddEventHandler");
        MethodInfo closedAddMethod = eventHandlerMethod.MakeGenericMethod(handlerType);
        return closedAddMethod;
    }

これは機能します。質問で述べたように、これは自分のクラスで中間メソッドを使用しないと機能しませんMethodInfo(つまり、 WindowsRunTimeMarshal.AddEventHandler を の直接のターゲットとして使用します)。なぜそうなのかは説明できませんが、これは実行可能な回避策であるため、他の誰かが同じ問題を抱えている場合はここに残します.

これが発生するのは、Microsoft が意図しない使用を防ごうとしているからなのか、それとも何か別のことなのかはわかりません。答えを残してください。

于 2012-10-10T17:04:48.513 に答える
0

編集: Silverlight がアプリケーションをサンドボックス内で実行し、.net コア フレームワークに影響を与えたり、フレームワークを悪用して導入されたセキュリティ対策を克服したりしないようにするのと同じように、プラットフォーム セキュリティのために反射された方法は機能しません。残念ながら、これは機能の実際の使用など、他のことに影響を与えます。Microsoft は、悪用される可能性のあるプラットフォーム クラスおよびメソッドに反映させません。

残念ながら、Silverlight のように WinRT の一部はアプリケーションをサンドボックスに保持するためにロックされていますが、それがすべての理由ではありませんが、一部の API はまだ開発されていません。

おっしゃる通り、最善の策は、AddEventHandler をカプセル化するラッパー メソッドを作成することです。

以下に実際の例を示します。AssignEvent への書き込みリフレクション呼び出しを作成するのは簡単です。この例では、画面上に「hello」というテキストブロックと「button」というボタンがあると想定しています。

public MainPage()
{
    this.InitializeComponent();

    RoutedEventHandler handler = (a, b) => this.Hellow.Text = "MUAHAHAH";
    // you can quite easily reflect the method below.
    this.AssignEvent<RoutedEventHandler>(this.button, "Click", handler);
}

private void AssignEvent<T1>(object instance, string eventName, T1 handler)
{
    var runtimeEvent = instance.GetType().GetRuntimeEvent(eventName);
    var handlerType = runtimeEvent.EventHandlerType;
    Func<T1, EventRegistrationToken> add = (a) => { return (EventRegistrationToken)runtimeEvent.AddMethod.Invoke(instance, new object[] { a }); };
    Action<EventRegistrationToken> remove = (a) => { runtimeEvent.RemoveMethod.Invoke(runtimeEvent, new object[] { a }); };

    WindowsRuntimeMarshal.AddEventHandler<T1>(add, remove, handler);
}

メソッドがSecurity Critical Attributeで装飾されているため、通常の方法では反映できません。アークくんの答えは正しいです。これもプラットフォームを保護するためです。

彼が言ったように、リフレクションのセキュリティに関する考慮事項がすべてを説明しています。

于 2012-10-11T15:14:51.653 に答える