Skip to content

Commit

Permalink
Event handler optimizations (#900)
Browse files Browse the repository at this point in the history
* Make event sources in WinRT.Runtime also optimized and add further optimizations.

* Make class abstract.

* Add tests and fix InvalidCastException for CanExecuteChanged
  • Loading branch information
manodasanW authored Jul 2, 2021
1 parent 46e26cd commit 58ede84
Show file tree
Hide file tree
Showing 16 changed files with 338 additions and 336 deletions.
22 changes: 22 additions & 0 deletions src/Tests/TestComponentCSharp/Class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1541,6 +1541,28 @@ namespace winrt::TestComponentCSharp::implementation
_dataErrorsChanged(*this, args);
}

// ICommand
winrt::event_token Class::CanExecuteChanged(winrt::Windows::Foundation::EventHandler<winrt::Windows::Foundation::IInspectable> const& handler)
{
return _canExecuteChanged.add(handler);
}
void Class::CanExecuteChanged(winrt::event_token const& token) noexcept
{
return _canExecuteChanged.remove(token);
}
bool Class::CanExecute(winrt::Windows::Foundation::IInspectable const& parameter)
{
return true;
}
void Class::Execute(winrt::Windows::Foundation::IInspectable const& parameter)
{
}
void Class::RaiseCanExecuteChanged()
{
_canExecuteChanged(*this, *this);
}


WF::IInspectable Class::BadRuntimeClassName()
{
struct bad_runtime_classname : winrt::implements<bad_runtime_classname, WF::IInspectable>
Expand Down
7 changes: 7 additions & 0 deletions src/Tests/TestComponentCSharp/Class.h
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,13 @@ namespace winrt::TestComponentCSharp::implementation
Windows::Foundation::Collections::IIterable<Windows::Foundation::IInspectable> GetErrors(hstring const& propertyName);
void RaiseDataErrorChanged();

winrt::event<Windows::Foundation::EventHandler<Windows::Foundation::IInspectable>> _canExecuteChanged;
winrt::event_token CanExecuteChanged(Windows::Foundation::EventHandler<Windows::Foundation::IInspectable> const& handler);
void CanExecuteChanged(winrt::event_token const& token) noexcept;
bool CanExecute(Windows::Foundation::IInspectable const& parameter);
void Execute(Windows::Foundation::IInspectable const& parameter);
void RaiseCanExecuteChanged();

static Windows::Foundation::IInspectable BadRuntimeClassName();
};
}
Expand Down
4 changes: 4 additions & 0 deletions src/Tests/TestComponentCSharp/TestComponentCSharp.idl
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ namespace TestComponentCSharp
, IProperties1
, IProperties2
, Microsoft.UI.Xaml.Data.INotifyDataErrorInfo
, Microsoft.UI.Xaml.Input.ICommand
//, Windows.Foundation.Collections.IVector<String>
//, Windows.Foundation.Collections.IMap<Int32, String>
{
Expand Down Expand Up @@ -391,6 +392,9 @@ namespace TestComponentCSharp
// INotifyDataErrorInfo
void RaiseDataErrorChanged();

// ICommand
void RaiseCanExecuteChanged();

static Object BadRuntimeClassName{ get; };
}

Expand Down
169 changes: 169 additions & 0 deletions src/WinRT.Runtime/Projections/EventHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,5 +139,174 @@ private static unsafe int Do_Abi_Invoke<TAbi>(void* thisPtr, IntPtr sender, TAbi
}
return 0;
}
}

[Guid("c50898f6-c536-5f47-8583-8b2c2438a13b")]
internal static class EventHandler
{
private delegate int Abi_Invoke(IntPtr thisPtr, IntPtr sender, IntPtr args);

private static readonly global::WinRT.Interop.IDelegateVftbl AbiToProjectionVftable;
public static readonly IntPtr AbiToProjectionVftablePtr;

static EventHandler()
{
AbiInvokeDelegate = (Abi_Invoke)Do_Abi_Invoke;
AbiToProjectionVftable = new global::WinRT.Interop.IDelegateVftbl
{
IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl,
Invoke = Marshal.GetFunctionPointerForDelegate(AbiInvokeDelegate)
};
var nativeVftbl = ComWrappersSupport.AllocateVtableMemory(typeof(EventHandler), Marshal.SizeOf<global::WinRT.Interop.IDelegateVftbl>());
Marshal.StructureToPtr(AbiToProjectionVftable, nativeVftbl, false);
AbiToProjectionVftablePtr = nativeVftbl;
}

public static global::System.Delegate AbiInvokeDelegate { get; }

public static unsafe IObjectReference CreateMarshaler(global::System.EventHandler managedDelegate) =>
managedDelegate is null ? null : ComWrappersSupport.CreateCCWForObject(managedDelegate).As<global::WinRT.Interop.IDelegateVftbl>(GuidGenerator.GetIID(typeof(EventHandler)));

public static IntPtr GetAbi(IObjectReference value) =>
value is null ? IntPtr.Zero : MarshalInterfaceHelper<global::System.EventHandler<object>>.GetAbi(value);

public static unsafe global::System.EventHandler FromAbi(IntPtr nativeDelegate)
{
var abiDelegate = ObjectReference<IDelegateVftbl>.FromAbi(nativeDelegate);
return (global::System.EventHandler)ComWrappersSupport.TryRegisterObjectForInterface(new global::System.EventHandler(new NativeDelegateWrapper(abiDelegate).Invoke), nativeDelegate);
}

[global::WinRT.ObjectReferenceWrapper(nameof(_nativeDelegate))]
#if NETSTANDARD2_0
private class NativeDelegateWrapper
#else
private class NativeDelegateWrapper : IWinRTObject
#endif
{
private readonly ObjectReference<global::WinRT.Interop.IDelegateVftbl> _nativeDelegate;
#if NETSTANDARD2_0
private readonly AgileReference _agileReference = default;
#endif
public NativeDelegateWrapper(ObjectReference<global::WinRT.Interop.IDelegateVftbl> nativeDelegate)
{
_nativeDelegate = nativeDelegate;
if (_nativeDelegate.TryAs<ABI.WinRT.Interop.IAgileObject.Vftbl>(out var objRef) < 0)
{
var agileReference = new AgileReference(_nativeDelegate);
#if NETSTANDARD2_0
_agileReference = agileReference;
#else
((IWinRTObject)this).AdditionalTypeData.TryAdd(typeof(AgileReference).TypeHandle, agileReference);
#endif
}
else
{
objRef.Dispose();
}
}

#if !NETSTANDARD2_0
IObjectReference IWinRTObject.NativeObject => _nativeDelegate;
bool IWinRTObject.HasUnwrappableNativeObject => true;
ConcurrentDictionary<RuntimeTypeHandle, IObjectReference> IWinRTObject.QueryInterfaceCache { get; } = new();
ConcurrentDictionary<RuntimeTypeHandle, object> IWinRTObject.AdditionalTypeData { get; } = new();
#endif

public void Invoke(object sender, EventArgs args)
{
#if NETSTANDARD2_0
var agileReference = _agileReference;
#else
var agileReference = ((IWinRTObject)this).AdditionalTypeData.TryGetValue(typeof(AgileReference).TypeHandle, out var agileObj) ?
(AgileReference)agileObj : null;
#endif
using var agileDelegate = agileReference?.Get()?.As<global::WinRT.Interop.IDelegateVftbl>(GuidGenerator.GetIID(typeof(EventHandler)));
var delegateToInvoke = agileDelegate ?? _nativeDelegate;
IntPtr ThisPtr = delegateToInvoke.ThisPtr;
var abiInvoke = Marshal.GetDelegateForFunctionPointer<Abi_Invoke>(delegateToInvoke.Vftbl.Invoke);
IObjectReference __sender = default;
IObjectReference __args = default;
var __params = new object[] { ThisPtr, null, null };
try
{
__sender = MarshalInspectable<object>.CreateMarshaler(sender);
__params[1] = MarshalInspectable<object>.GetAbi(__sender);
__args = MarshalInspectable<EventArgs>.CreateMarshaler(args);
__params[2] = MarshalInspectable<EventArgs>.GetAbi(__args);
abiInvoke.DynamicInvokeAbi(__params);
}
finally
{
MarshalInspectable<object>.DisposeMarshaler(__sender);
MarshalInspectable<EventArgs>.DisposeMarshaler(__args);
}

}
}

public static IntPtr FromManaged(global::System.EventHandler managedDelegate) =>
CreateMarshaler(managedDelegate)?.GetRef() ?? IntPtr.Zero;

public static void DisposeMarshaler(IObjectReference value) => MarshalInterfaceHelper<global::System.EventHandler<object>>.DisposeMarshaler(value);

public static void DisposeAbi(IntPtr abi) => MarshalInterfaceHelper<global::System.EventHandler<object>>.DisposeAbi(abi);

private static unsafe int Do_Abi_Invoke(IntPtr thisPtr, IntPtr sender, IntPtr args)
{
try
{
global::WinRT.ComWrappersSupport.MarshalDelegateInvoke(thisPtr, (global::System.Delegate invoke) =>
{
invoke.DynamicInvoke(
MarshalInspectable<object>.FromAbi(sender),
MarshalInspectable<object>.FromAbi(args) as EventArgs ?? EventArgs.Empty);
});
}
catch (global::System.Exception __exception__)
{
global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
}
return 0;
}
}

internal sealed unsafe class EventHandlerEventSource : EventSource<global::System.EventHandler>
{
private global::System.EventHandler handler;

internal EventHandlerEventSource(IObjectReference obj,
delegate* unmanaged[Stdcall]<global::System.IntPtr, global::System.IntPtr, out global::WinRT.EventRegistrationToken, int> addHandler,
delegate* unmanaged[Stdcall]<global::System.IntPtr, global::WinRT.EventRegistrationToken, int> removeHandler)
: base(obj, addHandler, removeHandler)
{
}

protected override IObjectReference CreateMarshaler(global::System.EventHandler del) =>
del is null ? null : EventHandler.CreateMarshaler(del);

protected override void DisposeMarshaler(IObjectReference marshaler) =>
EventHandler.DisposeMarshaler(marshaler);

protected override IntPtr GetAbi(IObjectReference marshaler) =>
marshaler is null ? IntPtr.Zero : EventHandler.GetAbi(marshaler);

protected override global::System.Delegate EventInvoke
{
// This is synchronized from the base class
get
{
if (handler == null)
{
handler = (global::System.Object obj, global::System.EventArgs e) =>
{
var localDel = _event;
if (localDel != null)
localDel.Invoke(obj, e);
};
}
return handler;
}
}
}
}
140 changes: 4 additions & 136 deletions src/WinRT.Runtime/Projections/ICommand.net5.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,138 +12,6 @@

namespace ABI.System.Windows.Input
{
[Guid("c50898f6-c536-5f47-8583-8b2c2438a13b")]
internal static class CanExecuteChangedEventHandler
{
private delegate int Abi_Invoke(IntPtr thisPtr, IntPtr sender, IntPtr args);

private static readonly global::WinRT.Interop.IDelegateVftbl AbiToProjectionVftable;
public static readonly IntPtr AbiToProjectionVftablePtr;

static CanExecuteChangedEventHandler()
{
AbiInvokeDelegate = (Abi_Invoke)Do_Abi_Invoke;
AbiToProjectionVftable = new global::WinRT.Interop.IDelegateVftbl
{
IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl,
Invoke = Marshal.GetFunctionPointerForDelegate(AbiInvokeDelegate)
};
var nativeVftbl = ComWrappersSupport.AllocateVtableMemory(typeof(CanExecuteChangedEventHandler), Marshal.SizeOf<global::WinRT.Interop.IDelegateVftbl>());
Marshal.StructureToPtr(AbiToProjectionVftable, nativeVftbl, false);
AbiToProjectionVftablePtr = nativeVftbl;
}

public static global::System.Delegate AbiInvokeDelegate { get; }

public static unsafe IObjectReference CreateMarshaler(global::System.EventHandler managedDelegate) =>
managedDelegate is null ? null : ComWrappersSupport.CreateCCWForObject(managedDelegate).As<global::WinRT.Interop.IDelegateVftbl>(GuidGenerator.GetIID(typeof(CanExecuteChangedEventHandler)));

public static IntPtr GetAbi(IObjectReference value) =>
value is null ? IntPtr.Zero : MarshalInterfaceHelper<global::System.EventHandler<object>>.GetAbi(value);

public static unsafe global::System.EventHandler FromAbi(IntPtr nativeDelegate)
{
var abiDelegate = ObjectReference<IDelegateVftbl>.FromAbi(nativeDelegate);
return (global::System.EventHandler)ComWrappersSupport.TryRegisterObjectForInterface(new global::System.EventHandler(new NativeDelegateWrapper(abiDelegate).Invoke), nativeDelegate);
}

[global::WinRT.ObjectReferenceWrapper(nameof(_nativeDelegate))]
private class NativeDelegateWrapper : IWinRTObject
{
private readonly ObjectReference<global::WinRT.Interop.IDelegateVftbl> _nativeDelegate;

public NativeDelegateWrapper(ObjectReference<global::WinRT.Interop.IDelegateVftbl> nativeDelegate)
{
_nativeDelegate = nativeDelegate;
if (_nativeDelegate.TryAs<ABI.WinRT.Interop.IAgileObject.Vftbl>(out var objRef) < 0)
{
var agileReference = new AgileReference(_nativeDelegate);
((IWinRTObject)this).AdditionalTypeData.TryAdd(typeof(AgileReference).TypeHandle, agileReference);
}
else
{
objRef.Dispose();
}
}

IObjectReference IWinRTObject.NativeObject => _nativeDelegate;
bool IWinRTObject.HasUnwrappableNativeObject => true;
ConcurrentDictionary<RuntimeTypeHandle, IObjectReference> IWinRTObject.QueryInterfaceCache { get; } = new();
ConcurrentDictionary<RuntimeTypeHandle, object> IWinRTObject.AdditionalTypeData { get; } = new();

public void Invoke(object sender, EventArgs args)
{
var agileReference = ((IWinRTObject)this).AdditionalTypeData.TryGetValue(typeof(AgileReference).TypeHandle, out var agileObj) ? (AgileReference)agileObj : null;
using var agileDelegate = agileReference?.Get()?.As<global::WinRT.Interop.IDelegateVftbl>(GuidGenerator.GetIID(typeof(CanExecuteChangedEventHandler)));
var delegateToInvoke = agileDelegate ?? _nativeDelegate;
IntPtr ThisPtr = delegateToInvoke.ThisPtr;
var abiInvoke = Marshal.GetDelegateForFunctionPointer<Abi_Invoke>(delegateToInvoke.Vftbl.Invoke);
IObjectReference __sender = default;
IObjectReference __args = default;
var __params = new object[] { ThisPtr, null, null };
try
{
__sender = MarshalInspectable<object>.CreateMarshaler(sender);
__params[1] = MarshalInspectable<object>.GetAbi(__sender);
__args = MarshalInspectable<EventArgs>.CreateMarshaler(args);
__params[2] = MarshalInspectable<EventArgs>.GetAbi(__args);
abiInvoke.DynamicInvokeAbi(__params);
}
finally
{
MarshalInspectable<object>.DisposeMarshaler(__sender);
MarshalInspectable<EventArgs>.DisposeMarshaler(__args);
}

}
}

public static IntPtr FromManaged(global::System.EventHandler managedDelegate) =>
CreateMarshaler(managedDelegate)?.GetRef() ?? IntPtr.Zero;

public static void DisposeMarshaler(IObjectReference value) => MarshalInterfaceHelper<global::System.EventHandler<object>>.DisposeMarshaler(value);

public static void DisposeAbi(IntPtr abi) => MarshalInterfaceHelper<global::System.EventHandler<object>>.DisposeAbi(abi);

private static unsafe int Do_Abi_Invoke(IntPtr thisPtr, IntPtr sender, IntPtr args)
{
try
{
global::WinRT.ComWrappersSupport.MarshalDelegateInvoke(thisPtr, (global::System.Delegate invoke) =>
{
invoke.DynamicInvoke(
MarshalInspectable<object>.FromAbi(sender),
MarshalInspectable<EventArgs>.FromAbi(args) ?? EventArgs.Empty);
});
}
catch (global::System.Exception __exception__)
{
global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
}
return 0;
}
}

internal sealed unsafe class CanExecuteChangedEventSource : EventSource<global::System.EventHandler>
{
internal CanExecuteChangedEventSource(IObjectReference obj,
delegate* unmanaged[Stdcall]<global::System.IntPtr, global::System.IntPtr, out global::WinRT.EventRegistrationToken, int> addHandler,
delegate* unmanaged[Stdcall]<global::System.IntPtr, global::WinRT.EventRegistrationToken, int> removeHandler)
: base(obj, addHandler, removeHandler)
{
}

protected override IObjectReference CreateMarshaler(EventHandler del) =>
del is null ? null : CanExecuteChangedEventHandler.CreateMarshaler(del);

protected override void DisposeMarshaler(IObjectReference marshaler) =>
CanExecuteChangedEventHandler.DisposeMarshaler(marshaler);

protected override IntPtr GetAbi(IObjectReference marshaler) =>
marshaler is null ? IntPtr.Zero : CanExecuteChangedEventHandler.GetAbi(marshaler);
}

[EditorBrowsable(EditorBrowsableState.Never)]
[Guid("E5AF3542-CA67-4081-995B-709DD13792DF")]
[DynamicInterfaceCastableImplementation]
Expand Down Expand Up @@ -233,7 +101,7 @@ private static unsafe int Do_Abi_add_CanExecuteChanged_0(IntPtr thisPtr, IntPtr
try
{
var __this = global::WinRT.ComWrappersSupport.FindObject<global::System.Windows.Input.ICommand>(thisPtr);
var __handler = CanExecuteChangedEventHandler.FromAbi(handler);
var __handler = EventHandler.FromAbi(handler);
*token = _CanExecuteChanged_TokenTables.GetOrCreateValue(__this).AddEventHandler(__handler);
__this.CanExecuteChanged += __handler;
return 0;
Expand Down Expand Up @@ -266,12 +134,12 @@ private static unsafe int Do_Abi_remove_CanExecuteChanged_1(IntPtr thisPtr, glob
}
public static ObjectReference<Vftbl> FromAbi(IntPtr thisPtr) => ObjectReference<Vftbl>.FromAbi(thisPtr);

private static CanExecuteChangedEventSource _CanExecuteChanged(IWinRTObject _this)
private static EventHandlerEventSource _CanExecuteChanged(IWinRTObject _this)
{
var _obj = ((ObjectReference<Vftbl>)((IWinRTObject)_this).GetObjectReferenceForType(typeof(global::System.Windows.Input.ICommand).TypeHandle));

return (CanExecuteChangedEventSource)_this.GetOrCreateTypeHelperData(typeof(global::System.Windows.Input.ICommand).TypeHandle,
() => new CanExecuteChangedEventSource(_obj, _obj.Vftbl.add_CanExecuteChanged_0, _obj.Vftbl.remove_CanExecuteChanged_1));
return (EventHandlerEventSource)_this.GetOrCreateTypeHelperData(typeof(global::System.Windows.Input.ICommand).TypeHandle,
() => new EventHandlerEventSource(_obj, _obj.Vftbl.add_CanExecuteChanged_0, _obj.Vftbl.remove_CanExecuteChanged_1));
}

unsafe bool global::System.Windows.Input.ICommand.CanExecute(object parameter)
Expand Down
Loading

0 comments on commit 58ede84

Please sign in to comment.