如何动态附加到WinRT事件?

时间:2012-09-30 10:12:18

标签: c# windows-runtime .net-4.5

我想在WinRT(Windows 8)应用程序中使用MVVM,我的一个要求是能够将事件挂钩到命令(ICommand)。这意味着我必须动态地向WinRT事件添加处理程序。有一个很好的explanation of how to do that 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.Action 1 [System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken]   Windows.UI.Xaml.Controls.ItemClickEventHandler)'不能用于   目前的平台。见http://go.microsoft.com/fwlink/?LinkId=248273   了解更多信息。

我知道我有正确的类型,因为当我用这个替换上面的4行代码(硬编码事件处理程序类型,这不是我想要的),然后代码工作并且事件被附加为预期:

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

作为参考,这三个参数定义如下(可以肯定的是,我的表达式树构建代码不是问题,我正在使用这些委托的显式定义,即使在尝试动态调用{{1}时也是如此},仍然失败):

AddEventHandler

为什么通过反射调用时调用失败,而不是直接调用它?

当在编译时不知道事件处理程序的类型时,是否存在动态附加WinRT事件的备用解决方案?

3 个答案:

答案 0 :(得分:5)

  

为什么通过反射调用时调用失败,而不是直接调用它?

答案:WindowsRuntimeMarshal.AddEventHandler标有[SecurityCritical]属性

来自反思的安全注意事项http://msdn.microsoft.com/en-us/library/stfy7tfc.aspx

  

根据必要的权限,代码可以使用反射来执行以下类型的访问:   访问非安全关键 公开成员。

因此,即使它们是公共的,也不能使用像AddEventHandler这样的反射安全关键方法。您可以在不使用反射的情况下自由调用此类方法,您可以自由地反映自己的方法。这就是您的解决方案有效的原因。

答案 1 :(得分: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;
    }

这很有效。正如问题所述,这个在我自己的类中没有中间方法(即使用WindowsRunTimeMarshal.AddEventHandler作为MethodInfo的直接目标)。我无法解释为什么会出现这种情况,但这是一个可行的解决方法,所以如果其他人遇到同样的问题,我会把它留在这里。

我不知道是否会发生这种情况是因为微软试图防止某些意外使用或其他事情 - 如果有人真正知道为什么原始问题中描述的前一种方法不起作用; 留下答案。

答案 2 :(得分:0)

编辑:由于平台安全性,反射方法不起作用,就像Silverlight确保应用程序在沙箱中运行并且不影响.net核心框架或利用框架来覆盖安全措施地点。不幸的是,这会影响其他功能,例如功能的真正使用。 Microsoft不会让您反思可能被利用的平台类和方法。

不幸的是,像Silverlight一样,WinRT的某些部分被锁定以将应用程序保留在沙盒中,但这并不是完整的原因,但是某些API仍然没有开发。

驱动你是对的,最好的办法是创建一个封装AddEventHandler的包装器方法。

您可以在下面看到一个工作示例,创建对AssignEvent的写入反射调用会很简单。此示例假设您在屏幕上有一个名为“hellow”的文本块和一个名为“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);
}

Ark-kun的回答是正确的,因为该方法用Security Critical Attribute修饰,不能用正常方法反映出来。这再次保护平台。

正如他所说,security considerations for reflection解释了一切。