From e798f3aa66b97cbd6a2fe0ff59b9ca52898fe9ff Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 9 Apr 2020 12:36:49 -0700 Subject: [PATCH 01/11] Implement ICustomQueryInterface and limited interface inheritance to support WinUI's assumptions around COM Aggregation and interface exclusivity. --- .../ComWrappersSupport.netstandard2.0.cs | 7 + cswinrt/code_writers.h | 183 ++++++++++-------- 2 files changed, 110 insertions(+), 80 deletions(-) diff --git a/WinRT.Runtime/ComWrappersSupport.netstandard2.0.cs b/WinRT.Runtime/ComWrappersSupport.netstandard2.0.cs index 2900687f8..4d92b9401 100644 --- a/WinRT.Runtime/ComWrappersSupport.netstandard2.0.cs +++ b/WinRT.Runtime/ComWrappersSupport.netstandard2.0.cs @@ -286,6 +286,13 @@ internal uint Release() internal int QueryInterface(Guid iid, out IntPtr ptr) { const int E_NOINTERFACE = unchecked((int)0x80004002); + if (ManagedObject is ICustomQueryInterface customQI) + { + if (customQI.GetInterface(ref iid, out ptr) == CustomQueryInterfaceResult.Handled) + { + return 0; + } + } if (_managedQITable.TryGetValue(iid, out ptr)) { AddRef(); diff --git a/cswinrt/code_writers.h b/cswinrt/code_writers.h index 9c897463e..2566a3253 100644 --- a/cswinrt/code_writers.h +++ b/cswinrt/code_writers.h @@ -662,7 +662,7 @@ namespace cswinrt return; } - bool write_explicit_implementation = is_protected || is_overridable; + bool write_explicit_implementation = is_overridable || !is_exclusive_to(method.Parent()); auto access_spec = is_protected ? "protected " : "public "; std::string method_spec = ""; @@ -826,7 +826,7 @@ remove => %.% -= value; } write_event(w, event.Name(), event, interface_member, visibility); - if (is_protected || is_overridable) + if (is_overridable || !is_exclusive_to(event.Parent())) { write_event(w, w.write_temp("%.%", bind(event.Parent(), false, false), event.Name()), event, "this"); } @@ -1291,6 +1291,61 @@ target); } } + std::pair find_property_interface(writer& w, TypeDef const& setter_iface, std::string_view prop_name) + { + std::string getter_iface; + + auto search_interface = [&](TypeDef const& type) + { + for (auto&& prop : type.PropertyList()) + { + if (prop.Name() == prop_name) + { + getter_iface = write_type_name_temp(w, type, "%", true); + return true; + } + } + return false; + }; + + std::function search_interfaces = [&](TypeDef const& type) + { + for (auto&& iface : type.InterfaceImpl()) + { + auto semantics = get_type_semantics(iface.Interface()); + if (for_typedef(w, semantics, [&](auto&& type) + { + return (setter_iface != type) && (search_interface(type) || search_interfaces(type)); + })) { + return true; + } + } + return false; + }; + + // first search base interfaces for property getter + if (search_interfaces(setter_iface)) + { + return { getter_iface, true }; + } + + // then search peer exclusive-to interfaces and their bases + if (auto exclusive_to_attr = get_attribute(setter_iface, "Windows.Foundation.Metadata", "ExclusiveToAttribute")) + { + auto sig = exclusive_to_attr.Value(); + auto const& fixed_args = sig.FixedArgs(); + XLANG_ASSERT(fixed_args.size() == 1); + auto sys_type = std::get(std::get(fixed_args[0].value).value); + auto exclusive_to_type = setter_iface.get_cache().find_required(sys_type.name); + if (search_interfaces(exclusive_to_type)) + { + return { getter_iface, false }; + } + } + + throw_invalid("Could not find property getter interface"); + } + void write_class_members(writer& w, TypeDef const& type) { std::map> properties; @@ -1368,8 +1423,8 @@ private % AsInternal(InterfaceTag<%> _) => new %(_default.ObjRef); XLANG_ASSERT(!getter_target.empty() || !setter_target.empty()); } - // If this interface is overridable or protected then we need to emit an explicit implementation of the property for that interface. - if (is_overridable_interface || is_protected_interface) + // If this interface is overridable then we need to emit an explicit implementation of the property for that interface. + if (is_overridable_interface || !is_exclusive_to(interface_type)) { w.write("% %.% {%%}", prop_type, @@ -1377,7 +1432,7 @@ private % AsInternal(InterfaceTag<%> _) => new %(_default.ObjRef); prop.Name(), bind([&](writer& w) { - if (getter) + if (getter || find_property_interface(w, interface_type, prop.Name()).second) { w.write("get => %; ", prop.Name()); } @@ -1448,61 +1503,6 @@ private EventSource<%> _%;)", } } - std::pair find_property_interface(writer& w, TypeDef const& setter_iface, std::string_view prop_name) - { - std::string getter_iface; - - auto search_interface = [&](TypeDef const& type) - { - for (auto&& prop : type.PropertyList()) - { - if (prop.Name() == prop_name) - { - getter_iface = write_type_name_temp(w, type, "%", true); - return true; - } - } - return false; - }; - - std::function search_interfaces = [&](TypeDef const& type) - { - for (auto&& iface : type.InterfaceImpl()) - { - auto semantics = get_type_semantics(iface.Interface()); - if (for_typedef(w, semantics, [&](auto&& type) - { - return (setter_iface != type) && (search_interface(type) || search_interfaces(type)); - })) { - return true; - } - } - return false; - }; - - // first search base interfaces for property getter - if (search_interfaces(setter_iface)) - { - return { getter_iface, true }; - } - - // then search peer exclusive-to interfaces and their bases - if (auto exclusive_to_attr = get_attribute(setter_iface, "Windows.Foundation.Metadata", "ExclusiveToAttribute")) - { - auto sig = exclusive_to_attr.Value(); - auto const& fixed_args = sig.FixedArgs(); - XLANG_ASSERT(fixed_args.size() == 1); - auto sys_type = std::get(std::get(fixed_args[0].value).value); - auto exclusive_to_type = setter_iface.get_cache().find_required(sys_type.name); - if (search_interfaces(exclusive_to_type)) - { - return { getter_iface, false }; - } - } - - throw_invalid("Could not find property getter interface"); - } - void write_interface_member_signatures(writer& w, TypeDef const& type) { for (auto&& method : type.MethodList()) @@ -2486,7 +2486,7 @@ remove => _%.Unsubscribe(value); get(get_arg(10))); } - void write_type_inheritance(writer& w, TypeDef const& type, type_semantics base_semantics) + void write_type_inheritance(writer& w, TypeDef const& type, type_semantics base_semantics, bool add_custom_qi) { auto delimiter{ " : " }; auto write_delimiter = [&]() @@ -2505,10 +2505,19 @@ remove => _%.Unsubscribe(value); { for_typedef(w, get_type_semantics(iface.Interface()), [&](auto type) { - write_delimiter(); - w.write("%", bind(type, false, false)); + if (has_attribute(iface, "Windows.Foundation.Metadata", "OverridableAttribute") || !is_exclusive_to(type)) + { + write_delimiter(); + w.write("%", bind(type, false, false)); + } }); } + + if (add_custom_qi) + { + write_delimiter(); + w.write("global::System.Runtime.InteropServices.ICustomQueryInterface"); + } } std::string get_vmethod_delegate_type(writer& w, MethodDef const& method, std::string vmethod_name) @@ -3428,7 +3437,7 @@ AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; bind(type), is_exclusive_to(type) ? "internal" : "public", type_name, - bind(type, object_type{}), + bind(type, object_type{}, false), bind(type) ); } @@ -3576,12 +3585,12 @@ _defaultLazy = new Lazy<%>(() => ifc); private struct InterfaceTag{}; private % AsInternal(InterfaceTag<%> _) => _default; -% +%% } )", bind(type), type_name, - bind(type, base_semantics), + bind(type, base_semantics, true), derived_new, default_interface_abi_name, default_interface_abi_name, @@ -3630,7 +3639,26 @@ default_interface_abi_name); }), default_interface_name, default_interface_name, - bind(type)); + bind(type), + bind([&](writer& w) + { + w.write(R"( +global::System.Runtime.InteropServices.CustomQueryInterfaceResult global::System.Runtime.InteropServices.ICustomQueryInterface.GetInterface(ref Guid iid, out IntPtr ppv) +{ +ppv = IntPtr.Zero; +_ = ComWrappersSupport.TryUnwrapObject(_default, out var reference); +try +{ +using IObjectReference objRef = reference.As(iid); +ppv = objRef.GetRef(); +return global::System.Runtime.InteropServices.CustomQueryInterfaceResult.Handled; +} +catch (InvalidCastException) +{ +return global::System.Runtime.InteropServices.CustomQueryInterfaceResult.NotHandled; +} +})"); + })); } void write_abi_class(writer& w, TypeDef const& type) @@ -3642,33 +3670,28 @@ default_interface_abi_name); auto abi_type_name = write_type_name_temp(w, type, "%", true); auto projected_type_name = write_type_name_temp(w, type); - auto default_interface_name = get_default_interface_name(w, type, false); auto default_interface_abi_name = get_default_interface_name(w, type, true); w.write(R"([global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] public struct % { -public static IObjectReference CreateMarshaler(% obj) => MarshalInterface<%>.CreateMarshaler(obj); -public static IntPtr GetAbi(IObjectReference value) => MarshalInterfaceHelper<%>.GetAbi(value); +public static IObjectReference CreateMarshaler(% obj) => MarshalInspectable.CreateMarshaler(obj).As<%.Vftbl>(); +public static IntPtr GetAbi(IObjectReference value) => MarshalInterfaceHelper.GetAbi(value); public static % FromAbi(IntPtr thisPtr) => (%)MarshalInspectable.FromAbi(thisPtr); -public static IntPtr FromManaged(% obj) => MarshalInterface<%>.FromManaged(obj); -public static (int length, IntPtr data) FromManagedArray(%[] obj) => MarshalInterface<%>.FromManagedArray(obj); -public static void DisposeMarshaler(IObjectReference value) => MarshalInterfaceHelper<%>.DisposeMarshaler(value); -public static void DisposeAbi(IntPtr abi) => MarshalInterfaceHelper<%>.DisposeAbi(abi); +public static IntPtr FromManaged(% obj) => obj is null ? IntPtr.Zero : CreateMarshaler(obj).GetRef(); +public static (int length, IntPtr data) FromManagedArray(%[] array) => MarshalInterfaceHelper<%>.FromManagedArray(array, (o) => FromManaged(o)); +public static void DisposeMarshaler(IObjectReference value) => MarshalInspectable.DisposeMarshaler(value); +public static void DisposeAbi(IntPtr abi) => MarshalInspectable.DisposeAbi(abi); } )", abi_type_name, projected_type_name, - default_interface_name, - default_interface_name, + default_interface_abi_name, projected_type_name, projected_type_name, projected_type_name, - default_interface_name, projected_type_name, - default_interface_name, - default_interface_name, - default_interface_name); + projected_type_name); } void write_delegate(writer& w, TypeDef const& type) From 035bff08fa4b9e61a2b3877a331bb3e2b5201c4a Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 10 Apr 2020 15:13:41 -0700 Subject: [PATCH 02/11] Streamline ICustomQI.GetInterface implementation. --- cswinrt/code_writers.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cswinrt/code_writers.h b/cswinrt/code_writers.h index 2566a3253..3e5a74980 100644 --- a/cswinrt/code_writers.h +++ b/cswinrt/code_writers.h @@ -3646,10 +3646,9 @@ default_interface_abi_name); global::System.Runtime.InteropServices.CustomQueryInterfaceResult global::System.Runtime.InteropServices.ICustomQueryInterface.GetInterface(ref Guid iid, out IntPtr ppv) { ppv = IntPtr.Zero; -_ = ComWrappersSupport.TryUnwrapObject(_default, out var reference); try { -using IObjectReference objRef = reference.As(iid); +using IObjectReference objRef = _default.ObjRef.As(iid); ppv = objRef.GetRef(); return global::System.Runtime.InteropServices.CustomQueryInterfaceResult.Handled; } From 3907e7cbf6d0bd62c31c66f1e24e1734702ca333 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 14 Apr 2020 11:06:46 -0700 Subject: [PATCH 03/11] Fix interface ABI types with new limited code-gen inheritance. Fixed code-gen for overriding interfaces in sealed types. --- cswinrt/code_writers.h | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/cswinrt/code_writers.h b/cswinrt/code_writers.h index 3adb4987f..9ed371a43 100644 --- a/cswinrt/code_writers.h +++ b/cswinrt/code_writers.h @@ -655,18 +655,19 @@ namespace cswinrt ); } - void write_class_method(writer& w, MethodDef const& method, bool is_overridable, bool is_protected, std::string_view interface_member) + void write_class_method(writer& w, MethodDef const& method, TypeDef const& class_type, bool is_overridable, bool is_protected, std::string_view interface_member) { if (method.SpecialName()) { return; } - bool write_explicit_implementation = is_overridable || !is_exclusive_to(method.Parent()); - auto access_spec = is_protected ? "protected " : "public "; + auto access_spec = is_protected || is_overridable ? "protected " : "public "; std::string method_spec = ""; - if (is_overridable) + // If this interface is overridable but the type is sealed, don't mark the member as virtual. + // The C# compiler errors out about declaring a virtual member in a sealed class. + if (is_overridable && !class_type.Flags().Sealed()) { // All overridable methods in the WinRT type system have protected visibility. access_spec = "protected "; @@ -701,7 +702,7 @@ namespace cswinrt write_method(w, signature, method.Name(), return_type, interface_member, access_spec, method_spec); - if (write_explicit_implementation) + if (is_overridable || !is_exclusive_to(method.Parent())) { w.write(R"( % %.%(%) => %(%);)", @@ -1446,15 +1447,7 @@ private % AsInternal(InterfaceTag<%> _) => new %(_default.ObjRef); auto is_overridable_interface = has_attribute(ii, "Windows.Foundation.Metadata", "OverridableAttribute"); auto is_protected_interface = has_attribute(ii, "Windows.Foundation.Metadata", "ProtectedAttribute"); - // If this interface is overridable but the type is sealed, make the interface act as though it is protected. - // If we don't do this, then the C# compiler errors out about declaring a virtual member in a sealed class. - if (is_overridable_interface && type.Flags().Sealed()) - { - is_overridable_interface = false; - is_protected_interface = true; - } - - w.write_each(interface_type.MethodList(), is_overridable_interface, is_protected_interface, target); + w.write_each(interface_type.MethodList(), type, is_overridable_interface, is_protected_interface, target); w.write_each(interface_type.EventList(), is_overridable_interface, is_protected_interface, target); // Merge property getters/setters, since such may be defined across interfaces @@ -2538,8 +2531,11 @@ remove => _%.Unsubscribe(value); { for_typedef(w, get_type_semantics(iface.Interface()), [&](auto type) { - write_required_interface(type); - write_required_interface_members_for_abi_type(w, type, required_interfaces); + if (has_attribute(iface, "Windows.Foundation.Metadata", "OverridableAttribute") || !is_exclusive_to(type)) + { + write_required_interface(type); + write_required_interface_members_for_abi_type(w, type, required_interfaces); + } }); } } From a5f0e83404d75023c1fd016de68ca4df19c33b23 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 14 Apr 2020 12:08:01 -0700 Subject: [PATCH 04/11] Fix ICustomQI handling of overrride interfaces. --- cswinrt/code_writers.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/cswinrt/code_writers.h b/cswinrt/code_writers.h index 9ed371a43..89d37e04f 100644 --- a/cswinrt/code_writers.h +++ b/cswinrt/code_writers.h @@ -3731,6 +3731,7 @@ default_interface_abi_name); global::System.Runtime.InteropServices.CustomQueryInterfaceResult global::System.Runtime.InteropServices.ICustomQueryInterface.GetInterface(ref Guid iid, out IntPtr ppv) { ppv = IntPtr.Zero; +% try { using IObjectReference objRef = _default.ObjRef.As(iid); @@ -3741,7 +3742,19 @@ catch (InvalidCastException) { return global::System.Runtime.InteropServices.CustomQueryInterfaceResult.NotHandled; } -})"); +})", + bind_each([&](writer& w, InterfaceImpl const& iface) + { + if (has_attribute(iface, "Windows.Foundation.Metadata", "OverridableAttribute")) + { + w.write(R"( +if (GuidGenerator.GetIID(typeof(%)) == iid) +{ +return global::System.Runtime.InteropServices.CustomQueryInterfaceResult.NotHandled; +})", + bind(get_type_semantics(iface.Interface()), false, false)); + } + }, type.InterfaceImpl())); })); } From b9b48832dc62c099a955833bf4e4bb205999cfd0 Mon Sep 17 00:00:00 2001 From: Steven Kirbach Date: Tue, 14 Apr 2020 13:03:12 -0700 Subject: [PATCH 05/11] fix sample project --- .../NoBuild.Targets | 8 ++++++ .../WinUIDesktopSample.Package.wapproj | 2 +- WinUI/WinUIDesktopSample/App.xaml.cs | 26 ++++++++++++------- WinUI/WinUIDesktopSample/MainPage.xaml.cs | 1 + .../WinUIDesktopSample.csproj | 1 - 5 files changed, 26 insertions(+), 12 deletions(-) create mode 100644 WinUI/WinUIDesktopSample.Package/NoBuild.Targets diff --git a/WinUI/WinUIDesktopSample.Package/NoBuild.Targets b/WinUI/WinUIDesktopSample.Package/NoBuild.Targets new file mode 100644 index 000000000..35c6b94d9 --- /dev/null +++ b/WinUI/WinUIDesktopSample.Package/NoBuild.Targets @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/WinUI/WinUIDesktopSample.Package/WinUIDesktopSample.Package.wapproj b/WinUI/WinUIDesktopSample.Package/WinUIDesktopSample.Package.wapproj index 1245e8c1c..c09c19468 100644 --- a/WinUI/WinUIDesktopSample.Package/WinUIDesktopSample.Package.wapproj +++ b/WinUI/WinUIDesktopSample.Package/WinUIDesktopSample.Package.wapproj @@ -85,5 +85,5 @@ - + \ No newline at end of file diff --git a/WinUI/WinUIDesktopSample/App.xaml.cs b/WinUI/WinUIDesktopSample/App.xaml.cs index 23c160b57..2f4623f6d 100644 --- a/WinUI/WinUIDesktopSample/App.xaml.cs +++ b/WinUI/WinUIDesktopSample/App.xaml.cs @@ -19,23 +19,29 @@ public App() { } + Window myWindow; protected override void OnLaunched(LaunchActivatedEventArgs args) { + var button = new Button + { + Content = "Click me to load MainPage", + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center + }; + button.Click += Button_Click; var window = new Microsoft.UI.Xaml.Window { - // This will cause the InvalidCast exception. Once that is fixed, this can - // be removed and just use the new MainPage() line below for setting content - Content = new Microsoft.UI.Xaml.Shapes.Rectangle() - { - Fill = new SolidColorBrush(Windows.UI.Colors.Red), - Width = 200, - Height = 200 - } + Content = button }; - window.Content = new MainPage(); - window.Activate(); + + myWindow = window; + } + + private void Button_Click(object sender, RoutedEventArgs e) + { + myWindow.Content = new MainPage(); } } diff --git a/WinUI/WinUIDesktopSample/MainPage.xaml.cs b/WinUI/WinUIDesktopSample/MainPage.xaml.cs index 4272f85e1..cb7a1b423 100644 --- a/WinUI/WinUIDesktopSample/MainPage.xaml.cs +++ b/WinUI/WinUIDesktopSample/MainPage.xaml.cs @@ -15,6 +15,7 @@ public partial class MainPage : Page { public MainPage() { + Application.LoadComponent(this, new System.Uri("ms-resource:///MainPage.xaml")); } } } diff --git a/WinUI/WinUIDesktopSample/WinUIDesktopSample.csproj b/WinUI/WinUIDesktopSample/WinUIDesktopSample.csproj index ca51dedc9..cf59fcaef 100644 --- a/WinUI/WinUIDesktopSample/WinUIDesktopSample.csproj +++ b/WinUI/WinUIDesktopSample/WinUIDesktopSample.csproj @@ -5,7 +5,6 @@ netcoreapp5.0 10.0.18362.0 DISABLE_XAML_GENERATED_MAIN - true WinUIDesktopSample.exe.manifest