Skip to content

Commit da80937

Browse files
jkoritzinskyjkotasAaronRobinsonMSFT
authored
Use UnsafeAccessorType in System.Private.CoreLib and the BCL (#115583)
Co-authored-by: Jan Kotas <[email protected]> Co-authored-by: Aaron Robinson <[email protected]>
1 parent e16eeab commit da80937

File tree

21 files changed

+613
-534
lines changed

21 files changed

+613
-534
lines changed

src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivator.cs

Lines changed: 85 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Diagnostics.CodeAnalysis;
88
using System.IO;
99
using System.Reflection;
10+
using System.Runtime.CompilerServices;
1011
using System.Runtime.InteropServices;
1112
using System.Runtime.Loader;
1213
using System.Runtime.Versioning;
@@ -616,7 +617,6 @@ public void LockServer([MarshalAs(UnmanagedType.Bool)] bool fLock)
616617
[RequiresUnreferencedCode("Built-in COM support is not trim compatible", Url = "https://aka.ms/dotnet-illink/com")]
617618
private sealed class LicenseClassFactory : IClassFactory2
618619
{
619-
private readonly LicenseInteropProxy _licenseProxy = new LicenseInteropProxy();
620620
private readonly Guid _classId;
621621

622622
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicConstructors)]
@@ -643,7 +643,7 @@ public void LockServer([MarshalAs(UnmanagedType.Bool)] bool fLock)
643643

644644
public void GetLicInfo(ref LICINFO licInfo)
645645
{
646-
_licenseProxy.GetLicInfo(_classType, out bool runtimeKeyAvail, out bool licVerified);
646+
LicenseInteropProxy.GetLicInfo(_classType, out bool runtimeKeyAvail, out bool licVerified);
647647

648648
// The LICINFO is a struct with a DWORD size field and two BOOL fields. Each BOOL
649649
// is typedef'd from a DWORD, therefore the size is manually computed as below.
@@ -654,7 +654,7 @@ public void GetLicInfo(ref LICINFO licInfo)
654654

655655
public void RequestLicKey(int dwReserved, [MarshalAs(UnmanagedType.BStr)] out string pBstrKey)
656656
{
657-
pBstrKey = _licenseProxy.RequestLicKey(_classType);
657+
pBstrKey = LicenseInteropProxy.RequestLicKey(_classType);
658658
}
659659

660660
public void CreateInstanceLic(
@@ -677,7 +677,7 @@ private void CreateInstanceInner(
677677
{
678678
var interfaceType = BasicClassFactory.CreateValidatedInterfaceType(_classType, ref riid, pUnkOuter);
679679

680-
object obj = _licenseProxy.AllocateAndValidateLicense(_classType, key, isDesignTime);
680+
object obj = LicenseInteropProxy.AllocateAndValidateLicense(_classType, key, isDesignTime);
681681
if (pUnkOuter != null)
682682
{
683683
obj = BasicClassFactory.CreateAggregatedObject(pUnkOuter, obj);
@@ -699,51 +699,68 @@ internal sealed class LicenseInteropProxy
699699
private static readonly Type? s_licenseAttrType = Type.GetType("System.ComponentModel.LicenseProviderAttribute, System.ComponentModel.TypeConverter", throwOnError: false);
700700
private static readonly Type? s_licenseExceptionType = Type.GetType("System.ComponentModel.LicenseException, System.ComponentModel.TypeConverter", throwOnError: false);
701701

702-
// LicenseManager
703-
private readonly MethodInfo _createWithContext;
704-
705-
// LicenseInteropHelper
706-
private readonly MethodInfo _validateTypeAndReturnDetails;
707-
private readonly MethodInfo _getCurrentContextInfo;
708-
709-
// CLRLicenseContext
710-
private readonly MethodInfo _createDesignContext;
711-
private readonly MethodInfo _createRuntimeContext;
712-
713-
// LicenseContext
714-
private readonly MethodInfo _setSavedLicenseKey;
715-
716-
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
717-
private readonly Type _licInfoHelper;
718-
719-
private readonly MethodInfo _licInfoHelperContains;
702+
private const string LicenseManagerTypeName = "System.ComponentModel.LicenseManager, System.ComponentModel.TypeConverter";
703+
private const string LicenseContextTypeName = "System.ComponentModel.LicenseContext, System.ComponentModel.TypeConverter";
704+
private const string LicenseInteropHelperTypeName = "System.ComponentModel.LicenseManager+LicenseInteropHelper, System.ComponentModel.TypeConverter";
705+
private const string CLRLicenseContextTypeName = "System.ComponentModel.LicenseManager+CLRLicenseContext, System.ComponentModel.TypeConverter";
706+
private const string LicenseRefTypeName = "System.ComponentModel.License&, System.ComponentModel.TypeConverter";
707+
private const string LicInfoHelperLicenseContextTypeName = "System.ComponentModel.LicenseManager+LicInfoHelperLicenseContext, System.ComponentModel.TypeConverter";
720708

721709
// RCW Activation
722710
private object? _licContext;
723711
private Type? _targetRcwType;
724712

725-
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2111:ReflectionToDynamicallyAccessedMembers",
726-
Justification = "The type parameter to LicenseManager.CreateWithContext method has PublicConstructors annotation. We only invoke this method" +
727-
"from AllocateAndValidateLicense which annotates the value passed in with the same annotation.")]
728-
public LicenseInteropProxy()
729-
{
730-
Type licManager = Type.GetType("System.ComponentModel.LicenseManager, System.ComponentModel.TypeConverter", throwOnError: true)!;
731-
732-
Type licContext = Type.GetType("System.ComponentModel.LicenseContext, System.ComponentModel.TypeConverter", throwOnError: true)!;
733-
_setSavedLicenseKey = licContext.GetMethod("SetSavedLicenseKey", BindingFlags.Instance | BindingFlags.Public)!;
734-
_createWithContext = licManager.GetMethod("CreateWithContext", [typeof(Type), licContext])!;
735-
736-
Type interopHelper = licManager.GetNestedType("LicenseInteropHelper", BindingFlags.NonPublic)!;
737-
_validateTypeAndReturnDetails = interopHelper.GetMethod("ValidateAndRetrieveLicenseDetails", BindingFlags.Static | BindingFlags.Public)!;
738-
_getCurrentContextInfo = interopHelper.GetMethod("GetCurrentContextInfo", BindingFlags.Static | BindingFlags.Public)!;
739-
740-
Type clrLicContext = licManager.GetNestedType("CLRLicenseContext", BindingFlags.NonPublic)!;
741-
_createDesignContext = clrLicContext.GetMethod("CreateDesignContext", BindingFlags.Static | BindingFlags.Public)!;
742-
_createRuntimeContext = clrLicContext.GetMethod("CreateRuntimeContext", BindingFlags.Static | BindingFlags.Public)!;
743-
744-
_licInfoHelper = licManager.GetNestedType("LicInfoHelperLicenseContext", BindingFlags.NonPublic)!;
745-
_licInfoHelperContains = _licInfoHelper.GetMethod("Contains", BindingFlags.Instance | BindingFlags.Public)!;
746-
}
713+
[UnsafeAccessor(UnsafeAccessorKind.Method)]
714+
private static extern void SetSavedLicenseKey(
715+
[UnsafeAccessorType(LicenseContextTypeName)] object licContext,
716+
Type type,
717+
string key);
718+
719+
[UnconditionalSuppressMessage("Trimming", "IL2111", Justification = "Manually validated that the annotations are kept in sync.")]
720+
[UnsafeAccessor(UnsafeAccessorKind.StaticMethod)]
721+
private static extern object? CreateWithContext(
722+
[UnsafeAccessorType(LicenseManagerTypeName)] object? licManager,
723+
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type,
724+
[UnsafeAccessorType(LicenseContextTypeName)] object licContext
725+
);
726+
727+
[UnsafeAccessor(UnsafeAccessorKind.StaticMethod)]
728+
private static extern bool ValidateAndRetrieveLicenseDetails(
729+
[UnsafeAccessorType(LicenseInteropHelperTypeName)] object? licInteropHelper,
730+
[UnsafeAccessorType(LicenseContextTypeName)] object? licContext,
731+
Type type,
732+
[UnsafeAccessorType(LicenseRefTypeName)] out object? license,
733+
out string? licenseKey);
734+
735+
[UnsafeAccessor(UnsafeAccessorKind.StaticMethod)]
736+
[return: UnsafeAccessorType(LicenseContextTypeName)]
737+
private static extern object? GetCurrentContextInfo(
738+
[UnsafeAccessorType(LicenseInteropHelperTypeName)] object? licInteropHelper,
739+
Type type,
740+
out bool isDesignTime,
741+
out string? key);
742+
743+
[UnsafeAccessor(UnsafeAccessorKind.StaticMethod)]
744+
[return: UnsafeAccessorType(CLRLicenseContextTypeName)]
745+
private static extern object CreateDesignContext(
746+
[UnsafeAccessorType(CLRLicenseContextTypeName)] object? context,
747+
Type type);
748+
749+
[UnsafeAccessor(UnsafeAccessorKind.StaticMethod)]
750+
[return: UnsafeAccessorType(CLRLicenseContextTypeName)]
751+
private static extern object CreateRuntimeContext(
752+
[UnsafeAccessorType(CLRLicenseContextTypeName)] object? context,
753+
Type type,
754+
string? key);
755+
756+
[UnsafeAccessor(UnsafeAccessorKind.Constructor)]
757+
[return:UnsafeAccessorType(LicInfoHelperLicenseContextTypeName)]
758+
private static extern object CreateLicInfoHelperLicenseContext();
759+
760+
[UnsafeAccessor(UnsafeAccessorKind.Method)]
761+
private static extern bool Contains(
762+
[UnsafeAccessorType(LicInfoHelperLicenseContextTypeName)] object? licInfoHelperContext,
763+
string assemblyName);
747764

748765
// Helper function to create an object from the native side
749766
public static object Create()
@@ -769,35 +786,35 @@ public static bool HasLicense(Type type)
769786
//
770787
// COM normally doesn't expect this function to fail so this method
771788
// should only throw in the case of a catastrophic error (stack, memory, etc.)
772-
public void GetLicInfo(Type type, out bool runtimeKeyAvail, out bool licVerified)
789+
public static void GetLicInfo(Type type, out bool runtimeKeyAvail, out bool licVerified)
773790
{
774791
runtimeKeyAvail = false;
775792
licVerified = false;
776793

777-
// Types are as follows:
778-
// LicenseContext, Type, out License, out string
779-
object licContext = Activator.CreateInstance(_licInfoHelper)!;
780-
var parameters = new object?[] { licContext, type, /* out */ null, /* out */ null };
781-
bool isValid = (bool)_validateTypeAndReturnDetails.Invoke(null, BindingFlags.DoNotWrapExceptions, binder: null, parameters: parameters, culture: null)!;
794+
object licContext = CreateLicInfoHelperLicenseContext();
795+
bool isValid = ValidateAndRetrieveLicenseDetails(null, licContext, type, out object? license, out _);
782796
if (!isValid)
783797
{
784798
return;
785799
}
786800

787-
var license = (IDisposable?)parameters[2];
788-
if (license != null)
801+
if (license is IDisposable disp)
789802
{
790-
license.Dispose();
803+
// Dispose of the license if it implements IDisposable
804+
// and we are not in design mode. This is a bit of a hack
805+
// but we need to do this to avoid leaking the license.
806+
// The license will be disposed of when the context is
807+
// disposed of.
808+
disp.Dispose();
791809
licVerified = true;
792810
}
793811

794-
parameters = [type.AssemblyQualifiedName];
795-
runtimeKeyAvail = (bool)_licInfoHelperContains.Invoke(licContext, BindingFlags.DoNotWrapExceptions, binder: null, parameters: parameters, culture: null)!;
812+
runtimeKeyAvail = Contains(licContext, type.AssemblyQualifiedName!);
796813
}
797814

798815
// The CLR invokes this whenever a COM client invokes
799816
// IClassFactory2::RequestLicKey on a managed class.
800-
public string RequestLicKey(Type type)
817+
public static string RequestLicKey(Type type)
801818
{
802819
// License will be null, since we passed no instance,
803820
// however we can still retrieve the "first" license
@@ -806,20 +823,14 @@ public string RequestLicKey(Type type)
806823
// like LicFileLicenseProvider that don't require the
807824
// instance to grant a key.
808825

809-
// Types are as follows:
810-
// LicenseContext, Type, out License, out string
811-
var parameters = new object?[] { /* use global LicenseContext */ null, type, /* out */ null, /* out */ null };
812-
bool isValid = (bool)_validateTypeAndReturnDetails.Invoke(null, BindingFlags.DoNotWrapExceptions, binder: null, parameters: parameters, culture: null)!;
813-
if (!isValid)
826+
if (!ValidateAndRetrieveLicenseDetails(null, null, type, out object? license, out string? licenseKey))
814827
{
815828
throw new COMException(); // E_FAIL
816829
}
817830

818-
((IDisposable?)parameters[2])?.Dispose();
819-
820-
var licenseKey = (string?)parameters[3] ?? throw new COMException(); // E_FAIL
831+
((IDisposable?)license)?.Dispose();
821832

822-
return licenseKey;
833+
return licenseKey ?? throw new COMException(); // E_FAIL
823834
}
824835

825836
// The CLR invokes this whenever a COM client invokes
@@ -832,25 +843,21 @@ public string RequestLicKey(Type type)
832843
// If we are being entered because of a call to ICF::CreateInstanceLic(),
833844
// "isDesignTime" will be "false" and "key" will point to a non-null
834845
// license key.
835-
public object AllocateAndValidateLicense([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type, string? key, bool isDesignTime)
846+
public static object AllocateAndValidateLicense([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type, string? key, bool isDesignTime)
836847
{
837-
object?[] parameters;
838-
object? licContext;
848+
object licContext;
839849
if (isDesignTime)
840850
{
841-
parameters = [type];
842-
licContext = _createDesignContext.Invoke(null, BindingFlags.DoNotWrapExceptions, binder: null, parameters: parameters, culture: null);
851+
licContext = CreateDesignContext(null, type);
843852
}
844853
else
845854
{
846-
parameters = [type, key];
847-
licContext = _createRuntimeContext.Invoke(null, BindingFlags.DoNotWrapExceptions, binder: null, parameters: parameters, culture: null);
855+
licContext = CreateRuntimeContext(null, type, key);
848856
}
849857

850858
try
851859
{
852-
parameters = [type, licContext];
853-
return _createWithContext.Invoke(null, BindingFlags.DoNotWrapExceptions, binder: null, parameters: parameters, culture: null)!;
860+
return CreateWithContext(null, type, licContext)!;
854861
}
855862
catch (Exception exception) when (exception.GetType() == s_licenseExceptionType)
856863
{
@@ -864,14 +871,10 @@ public void GetCurrentContextInfo(RuntimeTypeHandle rth, out bool isDesignTime,
864871
{
865872
Type targetRcwTypeMaybe = Type.GetTypeFromHandle(rth)!;
866873

867-
// Types are as follows:
868-
// Type, out bool, out string -> LicenseContext
869-
var parameters = new object?[] { targetRcwTypeMaybe, /* out */ null, /* out */ null };
870-
_licContext = _getCurrentContextInfo.Invoke(null, BindingFlags.DoNotWrapExceptions, binder: null, parameters: parameters, culture: null);
874+
_licContext = GetCurrentContextInfo(null, targetRcwTypeMaybe, out isDesignTime, out string? key);
871875

872876
_targetRcwType = targetRcwTypeMaybe;
873-
isDesignTime = (bool)parameters[1]!;
874-
bstrKey = Marshal.StringToBSTR((string)parameters[2]!);
877+
bstrKey = Marshal.StringToBSTR((string)key!);
875878
}
876879

877880
// The CLR invokes this when instantiating a licensed COM
@@ -886,8 +889,8 @@ public void SaveKeyInCurrentContext(IntPtr bstrKey)
886889
}
887890

888891
string key = Marshal.PtrToStringBSTR(bstrKey);
889-
var parameters = new object?[] { _targetRcwType, key };
890-
_setSavedLicenseKey.Invoke(_licContext, BindingFlags.DoNotWrapExceptions, binder: null, parameters: parameters, culture: null);
892+
893+
SetSavedLicenseKey(_licContext!, _targetRcwType!, key);
891894
}
892895
}
893896
}

0 commit comments

Comments
 (0)