diff --git a/src/WinRT.Runtime/ActivationFactory.cs b/src/WinRT.Runtime/ActivationFactory.cs index e18d5f570..2babaf241 100644 --- a/src/WinRT.Runtime/ActivationFactory.cs +++ b/src/WinRT.Runtime/ActivationFactory.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using WinRT.Interop; @@ -227,26 +228,9 @@ public static IObjectReference Get(string typeName) return factory; } - var moduleName = typeName; - while (true) - { - var lastSegment = moduleName.LastIndexOf(".", StringComparison.Ordinal); - if (lastSegment <= 0) - { - Marshal.ThrowExceptionForHR(hr); - } - moduleName = moduleName.Remove(lastSegment); + ThrowIfClassNotRegisteredAndManifestFreeActivationDisabled(typeName, hr); - DllModule module = null; - if (DllModule.TryLoad(moduleName + ".dll", out module)) - { - (factory, hr) = module.GetActivationFactory(typeName); - if (factory != null) - { - return factory; - } - } - } + return ManifestFreeGet(typeName, hr); } #if NET @@ -276,6 +260,10 @@ public static ObjectReference Get(string typeName, Guid iid) { return factory; } + + ThrowIfClassNotRegisteredAndManifestFreeActivationDisabled(typeName, hr); + + return ManifestFreeGet(typeName, iid, hr); #else ObjectReference factory; (factory, hr) = WinRTModule.GetActivationFactory(typeName, iid); @@ -283,8 +271,16 @@ public static ObjectReference Get(string typeName, Guid iid) { return factory; } + + ThrowIfClassNotRegisteredAndManifestFreeActivationDisabled(typeName, hr); + + return ManifestFreeGet(typeName, iid, hr); #endif + } + [MethodImpl(MethodImplOptions.NoInlining)] + private static IObjectReference ManifestFreeGet(string typeName, int hr) + { var moduleName = typeName; while (true) { @@ -297,6 +293,34 @@ public static ObjectReference Get(string typeName, Guid iid) DllModule module = null; if (DllModule.TryLoad(moduleName + ".dll", out module)) + { + (ObjectReference factory, hr) = module.GetActivationFactory(typeName); + if (factory != null) + { + return factory; + } + } + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] +#if NET + public static IObjectReference ManifestFreeGet(string typeName, Guid iid, int hr) +#else + private static ObjectReference ManifestFreeGet(string typeName, Guid iid, int hr) +#endif + { + var moduleName = typeName; + while (true) + { + var lastSegment = moduleName.LastIndexOf(".", StringComparison.Ordinal); + if (lastSegment <= 0) + { + Marshal.ThrowExceptionForHR(hr); + } + moduleName = moduleName.Remove(lastSegment); + + if (DllModule.TryLoad(moduleName + ".dll", out DllModule module)) { ObjectReference activationFactory; (activationFactory, hr) = module.GetActivationFactory(typeName); @@ -315,6 +339,35 @@ public static ObjectReference Get(string typeName, Guid iid) } } + private static void ThrowIfClassNotRegisteredAndManifestFreeActivationDisabled(string typeName, int hr) + { + // If manifest free activation is enabled, we never throw here. + // Callers will always try the fallback path if we didn't succeed. + if (FeatureSwitches.EnableManifestFreeActivation) + { + return; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static Exception GetException(string typeName, int hr) + { + Exception exception = Marshal.GetExceptionForHR(hr); + + if (hr == ExceptionHelpers.REGDB_E_CLASSNOTREG) + { + throw new NotSupportedException( + $"Failed to activate type with runtime class name '{typeName}' with 'RoGetActivationFactory' (it returned 0x80040154, ie. 'REGDB_E_CLASSNOTREG'). Make sure to add the activatable class id for the type " + + "to the APPX manifest, or enable the manifest free activation fallback path by disabling the 'CsWinRTEnableManifestFreeActivation' property (note: the fallback path incurs a performance hit).", exception); + } + + return exception; + } + + // Explicit throw so the JIT can see it and correctly optimize for always throwing paths. + // The exception instantiation is in a separate method so that codegen remains separate. + throw GetException(typeName, hr); + } + #if NET private static IObjectReference GetFromActivationHandler(string typeName, Guid iid) { diff --git a/src/WinRT.Runtime/ExceptionHelpers.cs b/src/WinRT.Runtime/ExceptionHelpers.cs index a8121db58..d90a827a6 100644 --- a/src/WinRT.Runtime/ExceptionHelpers.cs +++ b/src/WinRT.Runtime/ExceptionHelpers.cs @@ -53,6 +53,7 @@ static unsafe class ExceptionHelpers private const int ERROR_BAD_FORMAT = unchecked((int)0x8007000b); private const int ERROR_CANCELLED = unchecked((int)0x800704c7); private const int ERROR_TIMEOUT = unchecked((int)0x800705b4); + internal const int REGDB_E_CLASSNOTREG = unchecked((int)0x80040154); private static delegate* unmanaged[Stdcall] getRestrictedErrorInfo; private static delegate* unmanaged[Stdcall] setRestrictedErrorInfo; diff --git a/src/WinRT.Runtime/Projections/ICustomPropertyProvider.net5.cs b/src/WinRT.Runtime/Projections/ICustomPropertyProvider.net5.cs index 469a5e6b3..a2bfb9301 100644 --- a/src/WinRT.Runtime/Projections/ICustomPropertyProvider.net5.cs +++ b/src/WinRT.Runtime/Projections/ICustomPropertyProvider.net5.cs @@ -409,7 +409,6 @@ private static unsafe int Do_Abi_GetCustomProperty_0(IntPtr thisPtr, IntPtr name { throw new NotSupportedException( $"ICustomProperty support used by XAML binding for type '{target.GetType()}' (property '{_name}') requires the type to marked with 'WinRT.GeneratedBindableCustomPropertyAttribute'. " + - $"If this is a built-in type or a type that can't be marked, a wrapper type should be used around it that is marked to enable this support."); }