diff --git a/src/Tests/AuthoringConsumptionTest/AuthoringConsumptionTest.exe.manifest b/src/Tests/AuthoringConsumptionTest/AuthoringConsumptionTest.exe.manifest index bf43d8acd..ce6faf775 100644 --- a/src/Tests/AuthoringConsumptionTest/AuthoringConsumptionTest.exe.manifest +++ b/src/Tests/AuthoringConsumptionTest/AuthoringConsumptionTest.exe.manifest @@ -98,5 +98,9 @@ name="AuthoringTest.TypeOnlyActivatableViaItsOwnFactory" threadingModel="both" xmlns="urn:schemas-microsoft-com:winrt.v1" /> + diff --git a/src/Tests/AuthoringConsumptionTest/pch.h b/src/Tests/AuthoringConsumptionTest/pch.h index 92295c74b..00ed7367d 100644 --- a/src/Tests/AuthoringConsumptionTest/pch.h +++ b/src/Tests/AuthoringConsumptionTest/pch.h @@ -17,6 +17,7 @@ #pragma pop_macro("X86") #include +#include #include #include #include diff --git a/src/Tests/AuthoringConsumptionTest/test.cpp b/src/Tests/AuthoringConsumptionTest/test.cpp index a7c5db33a..cffdc9a8b 100644 --- a/src/Tests/AuthoringConsumptionTest/test.cpp +++ b/src/Tests/AuthoringConsumptionTest/test.cpp @@ -745,4 +745,30 @@ TEST(AuthoringTest, NonActivatableFactory) TEST(AuthoringTest, TypeOnlyActivatableViaItsOwnFactory) { EXPECT_EQ(TypeOnlyActivatableViaItsOwnFactory::Create().GetText(), L"Hello!"); +} + +TEST(AuthoringTest, ExplicitlyImplementedICustomPropertyProvider) +{ + CustomPropertyProviderWithExplicitImplementation userObject; + + // We should be able to cast to 'ICustomPropertyProvider' + auto propertyProvider = userObject.as(); + + auto providerType = propertyProvider.Type(); + EXPECT_EQ(providerType.Kind, Windows::UI::Xaml::Interop::TypeKind::Metadata); + EXPECT_EQ(providerType.Name, L"AuthoringTest.CustomPropertyProviderWithExplicitImplementation"); + + auto customProperty = propertyProvider.GetCustomProperty(L"TestCustomProperty"); + + EXPECT_NE(customProperty, nullptr); + EXPECT_TRUE(customProperty.CanRead()); + EXPECT_FALSE(customProperty.CanWrite()); + EXPECT_EQ(customProperty.Name(), L"TestCustomProperty"); + + auto propertyType = customProperty.Type(); + EXPECT_EQ(propertyType.Kind, Windows::UI::Xaml::Interop::TypeKind::Metadata); + EXPECT_EQ(propertyType.Name, L"AuthoringTest.CustomPropertyWithExplicitImplementation"); + + auto propertyValue = customProperty.GetValue(nullptr); + EXPECT_EQ(winrt::unbox_value(propertyValue), L"TestPropertyValue"); } \ No newline at end of file diff --git a/src/Tests/AuthoringTest/Program.cs b/src/Tests/AuthoringTest/Program.cs index db0eb5963..183d65228 100644 --- a/src/Tests/AuthoringTest/Program.cs +++ b/src/Tests/AuthoringTest/Program.cs @@ -1,5 +1,6 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Data; using Microsoft.UI.Xaml.Markup; using System; using System.Collections; @@ -206,6 +207,70 @@ public sealed partial class CustomProperty public string Value => "CsWinRT"; } + public sealed partial class CustomPropertyProviderWithExplicitImplementation : ICustomPropertyProvider + { + public Type Type => typeof(CustomPropertyProviderWithExplicitImplementation); + + public ICustomProperty GetCustomProperty(string name) + { + if (name == "TestCustomProperty") + { + return new CustomPropertyWithExplicitImplementation(); + } + + return null; + } + + public ICustomProperty GetIndexedProperty(string name, Type type) + { + return null; + } + + public string GetStringRepresentation() + { + return string.Empty; + } + } + + public sealed partial class CustomPropertyWithExplicitImplementation : ICustomProperty + { + internal CustomPropertyWithExplicitImplementation() + { + } + + public bool CanRead => true; + + public bool CanWrite => false; + + public string Name => "TestCustomProperty"; + + public Type Type => typeof(CustomPropertyWithExplicitImplementation); + + /// + public object GetIndexedValue(object target, object index) + { + throw new NotSupportedException(); + } + + /// + public object GetValue(object target) + { + return "TestPropertyValue"; + } + + /// + public void SetIndexedValue(object target, object value, object index) + { + throw new NotSupportedException(); + } + + /// + public void SetValue(object target, object value) + { + throw new NotSupportedException(); + } + } + [Version(3u)] public interface IDouble { diff --git a/src/WinRT.Runtime/ComWrappersSupport.cs b/src/WinRT.Runtime/ComWrappersSupport.cs index fec935337..7ea4e9029 100644 --- a/src/WinRT.Runtime/ComWrappersSupport.cs +++ b/src/WinRT.Runtime/ComWrappersSupport.cs @@ -189,7 +189,8 @@ public static void RegisterHelperType( internal static List GetInterfaceTableEntries(Type type) { var entries = new List(); - bool hasCustomIMarshalInterface = false; + bool hasUserImplementedIMarshalInterface = false; + bool hasUserImplementedICustomPropertyProviderInterface = false; bool hasWinrtExposedClassAttribute = false; #if NET @@ -229,7 +230,23 @@ static bool GetHasCustomIMarshalInterface(List entries) return false; } - hasCustomIMarshalInterface = GetHasCustomIMarshalInterface(entries); + // Same as above, for 'ICustomPropertyProvider' (separate method for a small perf boost). + // The method is very tiny, so the code duplication is not really a concern here. + static bool GetHasICustomPropertyProviderInterface(List entries) + { + foreach (ref readonly ComInterfaceEntry entry in CollectionsMarshal.AsSpan(entries)) + { + if (entry.IID == IID.IID_ICustomPropertyProvider) + { + return true; + } + } + + return false; + } + + hasUserImplementedIMarshalInterface = GetHasCustomIMarshalInterface(entries); + hasUserImplementedICustomPropertyProviderInterface = GetHasICustomPropertyProviderInterface(entries); } } else if (type == typeof(global::System.EventHandler)) @@ -260,7 +277,11 @@ static bool GetHasCustomIMarshalInterface(List entries) else if (RuntimeFeature.IsDynamicCodeCompiled) #endif { - static void AddInterfaceToVtable(Type iface, List entries, bool hasCustomIMarshalInterface) + static void AddInterfaceToVtable( + Type iface, + List entries, + ref bool hasUserImplementedIMarshalInterface, + ref bool hasUserImplementedICustomPropertyProviderInterface) { var interfaceHelperType = iface.FindHelperType(); Guid iid = GuidGenerator.GetIID(interfaceHelperType); @@ -270,9 +291,14 @@ static void AddInterfaceToVtable(Type iface, List entries, bo Vtable = interfaceHelperType.GetAbiToProjectionVftblPtr() }); - if (!hasCustomIMarshalInterface && iid == IID.IID_IMarshal) + if (!hasUserImplementedIMarshalInterface && iid == IID.IID_IMarshal) + { + hasUserImplementedIMarshalInterface = true; + } + + if (!hasUserImplementedICustomPropertyProviderInterface && iid == IID.IID_ICustomPropertyProvider) { - hasCustomIMarshalInterface = true; + hasUserImplementedICustomPropertyProviderInterface = true; } } @@ -283,7 +309,11 @@ static void AddInterfaceToVtable(Type iface, List entries, bo [UnconditionalSuppressMessage("Trimming", "IL2075", Justification = "Fallback method for JIT environments that is not trim-safe by design.")] [MethodImpl(MethodImplOptions.NoInlining)] #endif - static void GetInterfaceTableEntriesForJitEnvironment(Type type, List entries, bool hasCustomIMarshalInterface) + static void GetInterfaceTableEntriesForJitEnvironment( + Type type, + List entries, + ref bool hasUserImplementedIMarshalInterface, + ref bool hasUserImplementedICustomPropertyProviderInterface) { if (type.IsDelegate()) { @@ -312,7 +342,7 @@ static void GetInterfaceTableEntriesForJitEnvironment(Type type, List