diff options
Diffstat (limited to 'src/mscorlib/src/System/Runtime/InteropServices/TypeLibConverter.cs')
-rw-r--r-- | src/mscorlib/src/System/Runtime/InteropServices/TypeLibConverter.cs | 595 |
1 files changed, 595 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/TypeLibConverter.cs b/src/mscorlib/src/System/Runtime/InteropServices/TypeLibConverter.cs new file mode 100644 index 0000000000..e6b148a0a5 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/TypeLibConverter.cs @@ -0,0 +1,595 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: Component that implements the ITypeLibConverter interface and +** does the actual work of converting a typelib to metadata and +** vice versa. +** +** +=============================================================================*/ +#if !FEATURE_CORECLR // current implementation requires reflection only load +namespace System.Runtime.InteropServices { + + using System; + using System.Diagnostics.Contracts; + using System.Collections; + using System.Collections.Generic; + using System.Threading; + using System.Runtime.InteropServices.TCEAdapterGen; + using System.IO; + using System.Reflection; + using System.Reflection.Emit; + using System.Configuration.Assemblies; + using Microsoft.Win32; + using System.Runtime.CompilerServices; + using System.Globalization; + using System.Security; + using System.Security.Permissions; + using System.Runtime.InteropServices.ComTypes; + using System.Runtime.Versioning; + using WORD = System.UInt16; + using DWORD = System.UInt32; + using _TYPELIBATTR = System.Runtime.InteropServices.ComTypes.TYPELIBATTR; + + [Guid("F1C3BF79-C3E4-11d3-88E7-00902754C43A")] + [ClassInterface(ClassInterfaceType.None)] +[System.Runtime.InteropServices.ComVisible(true)] + public sealed class TypeLibConverter : ITypeLibConverter + { + private const String s_strTypeLibAssemblyTitlePrefix = "TypeLib "; + private const String s_strTypeLibAssemblyDescPrefix = "Assembly generated from typelib "; + private const int MAX_NAMESPACE_LENGTH = 1024; + + + // + // ITypeLibConverter interface. + // + + [System.Security.SecuritySafeCritical] // auto-generated + [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)] + public AssemblyBuilder ConvertTypeLibToAssembly([MarshalAs(UnmanagedType.Interface)] Object typeLib, + String asmFileName, + int flags, + ITypeLibImporterNotifySink notifySink, + byte[] publicKey, + StrongNameKeyPair keyPair, + bool unsafeInterfaces) + { + return ConvertTypeLibToAssembly(typeLib, + asmFileName, + (unsafeInterfaces + ? TypeLibImporterFlags.UnsafeInterfaces + : 0), + notifySink, + publicKey, + keyPair, + null, + null); + } + + + + + [System.Security.SecuritySafeCritical] // auto-generated + [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)] + public AssemblyBuilder ConvertTypeLibToAssembly([MarshalAs(UnmanagedType.Interface)] Object typeLib, + String asmFileName, + TypeLibImporterFlags flags, + ITypeLibImporterNotifySink notifySink, + byte[] publicKey, + StrongNameKeyPair keyPair, + String asmNamespace, + Version asmVersion) + { + // Validate the arguments. + if (typeLib == null) + throw new ArgumentNullException("typeLib"); + if (asmFileName == null) + throw new ArgumentNullException("asmFileName"); + if (notifySink == null) + throw new ArgumentNullException("notifySink"); + if (String.Empty.Equals(asmFileName)) + throw new ArgumentException(Environment.GetResourceString("Arg_InvalidFileName"), "asmFileName"); + if (asmFileName.Length > Path.MaxPath) + throw new ArgumentException(Environment.GetResourceString("IO.PathTooLong"), asmFileName); + if ((flags & TypeLibImporterFlags.PrimaryInteropAssembly) != 0 && publicKey == null && keyPair == null) + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_PIAMustBeStrongNamed")); + Contract.EndContractBlock(); + + ArrayList eventItfInfoList = null; + + // Determine the AssemblyNameFlags + AssemblyNameFlags asmNameFlags = AssemblyNameFlags.None; + + // Retrieve the assembly name from the typelib. + AssemblyName asmName = GetAssemblyNameFromTypelib(typeLib, asmFileName, publicKey, keyPair, asmVersion, asmNameFlags); + + // Create the dynamic assembly that will contain the converted typelib types. + AssemblyBuilder asmBldr = CreateAssemblyForTypeLib(typeLib, asmFileName, asmName, + (flags & TypeLibImporterFlags.PrimaryInteropAssembly) != 0, + (flags & TypeLibImporterFlags.ReflectionOnlyLoading) != 0, + (flags & TypeLibImporterFlags.NoDefineVersionResource) != 0); + + // Define a dynamic module that will contain the contain the imported types. + String strNonQualifiedAsmFileName = Path.GetFileName(asmFileName); + ModuleBuilder modBldr = asmBldr.DefineDynamicModule(strNonQualifiedAsmFileName, strNonQualifiedAsmFileName); + + // If the namespace hasn't been specified, then use the assembly name. + if (asmNamespace == null) + asmNamespace = asmName.Name; + + // Create a type resolve handler that will also intercept resolve ref messages + // on the sink interface to build up a list of referenced assemblies. + TypeResolveHandler typeResolveHandler = new TypeResolveHandler(modBldr, notifySink); + + // Add a listener for the type resolve events. + AppDomain currentDomain = Thread.GetDomain(); + ResolveEventHandler resolveHandler = new ResolveEventHandler(typeResolveHandler.ResolveEvent); + ResolveEventHandler asmResolveHandler = new ResolveEventHandler(typeResolveHandler.ResolveAsmEvent); + ResolveEventHandler ROAsmResolveHandler = new ResolveEventHandler(typeResolveHandler.ResolveROAsmEvent); + currentDomain.TypeResolve += resolveHandler; + currentDomain.AssemblyResolve += asmResolveHandler; + currentDomain.ReflectionOnlyAssemblyResolve += ROAsmResolveHandler; + + // Convert the types contained in the typelib into metadata and add them to the assembly. + nConvertTypeLibToMetadata(typeLib, asmBldr.InternalAssembly, modBldr.InternalModule, asmNamespace, flags, typeResolveHandler, out eventItfInfoList); + + // Update the COM types in the assembly. + UpdateComTypesInAssembly(asmBldr, modBldr); + + // If there are any event sources then generate the TCE adapters. + if (eventItfInfoList.Count > 0) + new TCEAdapterGenerator().Process(modBldr, eventItfInfoList); + + // Remove the listener for the type resolve events. + currentDomain.TypeResolve -= resolveHandler; + currentDomain.AssemblyResolve -= asmResolveHandler; + currentDomain.ReflectionOnlyAssemblyResolve -= ROAsmResolveHandler; + + // We have finished converting the typelib and now have a fully formed assembly. + return asmBldr; + } + + [System.Security.SecuritySafeCritical] // auto-generated + [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)] + [return : MarshalAs(UnmanagedType.Interface)] + public Object ConvertAssemblyToTypeLib(Assembly assembly, String strTypeLibName, TypeLibExporterFlags flags, ITypeLibExporterNotifySink notifySink) + { + RuntimeAssembly rtAssembly; + AssemblyBuilder ab = assembly as AssemblyBuilder; + if (ab != null) + rtAssembly = ab.InternalAssembly; + else + rtAssembly = assembly as RuntimeAssembly; + + return nConvertAssemblyToTypeLib(rtAssembly, strTypeLibName, flags, notifySink); + } + + public bool GetPrimaryInteropAssembly(Guid g, Int32 major, Int32 minor, Int32 lcid, out String asmName, out String asmCodeBase) + { + String strTlbId = "{" + g.ToString().ToUpper(CultureInfo.InvariantCulture) + "}"; + String strVersion = major.ToString("x", CultureInfo.InvariantCulture) + "." + minor.ToString("x", CultureInfo.InvariantCulture); + + // Set the two out values to null before we start. + asmName = null; + asmCodeBase = null; + + // Try to open the HKEY_CLASS_ROOT\TypeLib key. + using (RegistryKey TypeLibKey = Registry.ClassesRoot.OpenSubKey("TypeLib", false)) + { + if (TypeLibKey != null) + { + // Try to open the HKEY_CLASS_ROOT\TypeLib\<TLBID> key. + using (RegistryKey TypeLibSubKey = TypeLibKey.OpenSubKey(strTlbId)) + { + if (TypeLibSubKey != null) + { + // Try to open the HKEY_CLASS_ROOT\TypeLib\<TLBID>\<Major.Minor> key. + using (RegistryKey VersionKey = TypeLibSubKey.OpenSubKey(strVersion, false)) + { + if (VersionKey != null) + { + // Attempt to retrieve the assembly name and codebase under the version key. + asmName = (String)VersionKey.GetValue("PrimaryInteropAssemblyName"); + asmCodeBase = (String)VersionKey.GetValue("PrimaryInteropAssemblyCodeBase"); + } + } + } + } + } + } + + // If the assembly name isn't null, then we found an PIA. + return asmName != null; + } + + + // + // Non native helper methods. + // + + [System.Security.SecurityCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable + private static AssemblyBuilder CreateAssemblyForTypeLib(Object typeLib, String asmFileName, AssemblyName asmName, bool bPrimaryInteropAssembly, bool bReflectionOnly, bool bNoDefineVersionResource) + { + // Retrieve the current app domain. + AppDomain currentDomain = Thread.GetDomain(); + + // Retrieve the directory from the assembly file name. + String dir = null; + if (asmFileName != null) + { + dir = Path.GetDirectoryName(asmFileName); + if (String.IsNullOrEmpty(dir)) + dir = null; + } + + AssemblyBuilderAccess aba; + if (bReflectionOnly) + { + aba = AssemblyBuilderAccess.ReflectionOnly; + } + else + { + aba = AssemblyBuilderAccess.RunAndSave; + } + + // Create the dynamic assembly itself. + AssemblyBuilder asmBldr; + + List<CustomAttributeBuilder> assemblyAttributes = new List<CustomAttributeBuilder>(); +#if !FEATURE_CORECLR + // mscorlib.dll must specify the security rules that assemblies it emits are to use, since by + // default all assemblies will follow security rule set level 2, and we want to make that an + // explicit decision. + ConstructorInfo securityRulesCtor = typeof(SecurityRulesAttribute).GetConstructor(new Type[] { typeof(SecurityRuleSet) }); + CustomAttributeBuilder securityRulesAttribute = + new CustomAttributeBuilder(securityRulesCtor, new object[] { SecurityRuleSet.Level2 }); + assemblyAttributes.Add(securityRulesAttribute); +#endif // !FEATURE_CORECLR + + asmBldr = currentDomain.DefineDynamicAssembly(asmName, aba, dir, false, assemblyAttributes); + + // Set the Guid custom attribute on the assembly. + SetGuidAttributeOnAssembly(asmBldr, typeLib); + + // Set the imported from COM attribute on the assembly and return it. + SetImportedFromTypeLibAttrOnAssembly(asmBldr, typeLib); + + // Set the version information on the typelib. + if (bNoDefineVersionResource) + { + SetTypeLibVersionAttribute(asmBldr, typeLib); + } + else + { + SetVersionInformation(asmBldr, typeLib, asmName); + } + + // If we are generating a PIA, then set the PIA custom attribute. + if (bPrimaryInteropAssembly) + SetPIAAttributeOnAssembly(asmBldr, typeLib); + + return asmBldr; + } + + [System.Security.SecurityCritical] // auto-generated + internal static AssemblyName GetAssemblyNameFromTypelib(Object typeLib, String asmFileName, byte[] publicKey, StrongNameKeyPair keyPair, Version asmVersion, AssemblyNameFlags asmNameFlags) + { + // Extract the name of the typelib. + String strTypeLibName = null; + String strDocString = null; + int dwHelpContext = 0; + String strHelpFile = null; + ITypeLib pTLB = (ITypeLib)typeLib; + pTLB.GetDocumentation(-1, out strTypeLibName, out strDocString, out dwHelpContext, out strHelpFile); + + // Retrieve the name to use for the assembly. + if (asmFileName == null) + { + asmFileName = strTypeLibName; + } + else + { + Contract.Assert((asmFileName != null) && (asmFileName.Length > 0), "The assembly file name cannot be an empty string!"); + + String strFileNameNoPath = Path.GetFileName(asmFileName); + String strExtension = Path.GetExtension(asmFileName); + + // Validate that the extension is valid. + bool bExtensionValid = ".dll".Equals(strExtension, StringComparison.OrdinalIgnoreCase); + + // If the extension is not valid then tell the user and quit. + if (!bExtensionValid) + throw new ArgumentException(Environment.GetResourceString("Arg_InvalidFileExtension")); + + // The assembly cannot contain the path nor the extension. + asmFileName = strFileNameNoPath.Substring(0, strFileNameNoPath.Length - ".dll".Length); + } + + // If the version information was not specified, then retrieve it from the typelib. + if (asmVersion == null) + { + int major; + int minor; + Marshal.GetTypeLibVersion(pTLB, out major, out minor); + asmVersion = new Version(major, minor, 0, 0); + } + + // Create the assembly name for the imported typelib's assembly. + AssemblyName AsmName = new AssemblyName(); + AsmName.Init( + asmFileName, + publicKey, + null, + asmVersion, + null, + AssemblyHashAlgorithm.None, + AssemblyVersionCompatibility.SameMachine, + null, + asmNameFlags, + keyPair); + + return AsmName; + } + + private static void UpdateComTypesInAssembly(AssemblyBuilder asmBldr, ModuleBuilder modBldr) + { + // Retrieve the AssemblyBuilderData associated with the assembly builder. + AssemblyBuilderData AsmBldrData = asmBldr.m_assemblyData; + + // Go through the types in the module and add them as public COM types. + Type[] aTypes = modBldr.GetTypes(); + int NumTypes = aTypes.Length; + for (int cTypes = 0; cTypes < NumTypes; cTypes++) + AsmBldrData.AddPublicComType(aTypes[cTypes]); + } + + + [System.Security.SecurityCritical] // auto-generated + private static void SetGuidAttributeOnAssembly(AssemblyBuilder asmBldr, Object typeLib) + { + // Retrieve the GuidAttribute constructor. + Type []aConsParams = new Type[1] {typeof(String)}; + ConstructorInfo GuidAttrCons = typeof(GuidAttribute).GetConstructor(aConsParams); + + // Create an instance of the custom attribute builder. + Object[] aArgs = new Object[1] {Marshal.GetTypeLibGuid((ITypeLib)typeLib).ToString()}; + CustomAttributeBuilder GuidCABuilder = new CustomAttributeBuilder(GuidAttrCons, aArgs); + + // Set the GuidAttribute on the assembly builder. + asmBldr.SetCustomAttribute(GuidCABuilder); + } + + [System.Security.SecurityCritical] // auto-generated + private static void SetImportedFromTypeLibAttrOnAssembly(AssemblyBuilder asmBldr, Object typeLib) + { + // Retrieve the ImportedFromTypeLibAttribute constructor. + Type []aConsParams = new Type[1] {typeof(String)}; + ConstructorInfo ImpFromComAttrCons = typeof(ImportedFromTypeLibAttribute).GetConstructor(aConsParams); + + // Retrieve the name of the typelib. + String strTypeLibName = Marshal.GetTypeLibName((ITypeLib)typeLib); + + // Create an instance of the custom attribute builder. + Object[] aArgs = new Object[1] {strTypeLibName}; + CustomAttributeBuilder ImpFromComCABuilder = new CustomAttributeBuilder(ImpFromComAttrCons, aArgs); + + // Set the ImportedFromTypeLibAttribute on the assembly builder. + asmBldr.SetCustomAttribute(ImpFromComCABuilder); + } + + [System.Security.SecurityCritical] // auto-generated + private static void SetTypeLibVersionAttribute(AssemblyBuilder asmBldr, Object typeLib) + { + Type []aConsParams = new Type[2] {typeof(int), typeof(int)}; + ConstructorInfo TypeLibVerCons = typeof(TypeLibVersionAttribute).GetConstructor(aConsParams); + + // Get the typelib version + int major; + int minor; + Marshal.GetTypeLibVersion((ITypeLib)typeLib, out major, out minor); + + // Create an instance of the custom attribute builder. + Object[] aArgs = new Object[2] {major, minor}; + CustomAttributeBuilder TypeLibVerBuilder = new CustomAttributeBuilder(TypeLibVerCons, aArgs); + + // Set the attribute on the assembly builder. + asmBldr.SetCustomAttribute(TypeLibVerBuilder); + } + + [System.Security.SecurityCritical] // auto-generated + private static void SetVersionInformation(AssemblyBuilder asmBldr, Object typeLib, AssemblyName asmName) + { + // Extract the name of the typelib. + String strTypeLibName = null; + String strDocString = null; + int dwHelpContext = 0; + String strHelpFile = null; + ITypeLib pTLB = (ITypeLib)typeLib; + pTLB.GetDocumentation(-1, out strTypeLibName, out strDocString, out dwHelpContext, out strHelpFile); + + // Generate the product name string from the named of the typelib. + String strProductName = String.Format(CultureInfo.InvariantCulture, Environment.GetResourceString("TypeLibConverter_ImportedTypeLibProductName"), strTypeLibName); + + // Set the OS version information. + asmBldr.DefineVersionInfoResource(strProductName, asmName.Version.ToString(), null, null, null); + + // Set the TypeLibVersion attribute + SetTypeLibVersionAttribute(asmBldr, typeLib); + } + + [System.Security.SecurityCritical] // auto-generated + private static void SetPIAAttributeOnAssembly(AssemblyBuilder asmBldr, Object typeLib) + { + IntPtr pAttr = IntPtr.Zero; + _TYPELIBATTR Attr; + ITypeLib pTLB = (ITypeLib)typeLib; + int Major = 0; + int Minor = 0; + + // Retrieve the PrimaryInteropAssemblyAttribute constructor. + Type []aConsParams = new Type[2] {typeof(int), typeof(int)}; + ConstructorInfo PIAAttrCons = typeof(PrimaryInteropAssemblyAttribute).GetConstructor(aConsParams); + + // Retrieve the major and minor version from the typelib. + try + { + pTLB.GetLibAttr(out pAttr); + Attr = (_TYPELIBATTR)Marshal.PtrToStructure(pAttr, typeof(_TYPELIBATTR)); + Major = Attr.wMajorVerNum; + Minor = Attr.wMinorVerNum; + } + finally + { + // Release the typelib attributes. + if (pAttr != IntPtr.Zero) + pTLB.ReleaseTLibAttr(pAttr); + } + + // Create an instance of the custom attribute builder. + Object[] aArgs = new Object[2] {Major, Minor}; + CustomAttributeBuilder PIACABuilder = new CustomAttributeBuilder(PIAAttrCons, aArgs); + + // Set the PrimaryInteropAssemblyAttribute on the assembly builder. + asmBldr.SetCustomAttribute(PIACABuilder); + } + + + // + // Native helper methods. + // + + [System.Security.SecurityCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern void nConvertTypeLibToMetadata(Object typeLib, RuntimeAssembly asmBldr, RuntimeModule modBldr, String nameSpace, TypeLibImporterFlags flags, ITypeLibImporterNotifySink notifySink, out ArrayList eventItfInfoList); + + // Must use assembly versioning or GuidAttribute to avoid collisions in typelib export or registration. + [System.Security.SecurityCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern Object nConvertAssemblyToTypeLib(RuntimeAssembly assembly, String strTypeLibName, TypeLibExporterFlags flags, ITypeLibExporterNotifySink notifySink); + + [System.Security.SecurityCritical] // auto-generated + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode), SuppressUnmanagedCodeSecurity] + internal extern static void LoadInMemoryTypeByName(RuntimeModule module, String className); + + // + // Helper class called when a resolve type event is fired. + // + + private class TypeResolveHandler : ITypeLibImporterNotifySink + { + public TypeResolveHandler(ModuleBuilder mod, ITypeLibImporterNotifySink userSink) + { + m_Module = mod; + m_UserSink = userSink; + } + + public void ReportEvent(ImporterEventKind eventKind, int eventCode, String eventMsg) + { + m_UserSink.ReportEvent(eventKind, eventCode, eventMsg); + } + + public Assembly ResolveRef(Object typeLib) + { + Contract.Ensures(Contract.Result<Assembly>() != null && Contract.Result<Assembly>() is RuntimeAssembly); + Contract.EndContractBlock(); + + // Call the user sink to resolve the reference. + Assembly asm = m_UserSink.ResolveRef(typeLib); + + if (asm == null) + throw new ArgumentNullException(); + + // Return the resolved assembly. We extract the internal assembly because we are called + // by the VM which accesses fields of the object directly and does not go via those + // delegating properties (the fields are empty if asm is an (external) AssemblyBuilder). + + RuntimeAssembly rtAssembly = asm as RuntimeAssembly; + if (rtAssembly == null) + { + AssemblyBuilder ab = asm as AssemblyBuilder; + if (ab != null) + rtAssembly = ab.InternalAssembly; + } + + if (rtAssembly == null) + throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeAssembly")); + + // Add the assembly to the list of assemblies. + m_AsmList.Add(rtAssembly); + + return rtAssembly; + } + + [System.Security.SecurityCritical] // auto-generated + public Assembly ResolveEvent(Object sender, ResolveEventArgs args) + { + // We need to load the type in the resolve event so that we will deal with + // cases where we are trying to load the CoClass before the interface has + // been loaded. + try + { + LoadInMemoryTypeByName(m_Module.GetNativeHandle(), args.Name); + return m_Module.Assembly; + } + catch (TypeLoadException e) + { + if (e.ResourceId != System.__HResults.COR_E_TYPELOAD) // type not found + throw; + } + + foreach (RuntimeAssembly asm in m_AsmList) + { + try + { + asm.GetType(args.Name, true, false); + return asm; + } + catch (TypeLoadException e) + { + if (e._HResult != System.__HResults.COR_E_TYPELOAD) // type not found + throw; + } + } + + return null; + } + + public Assembly ResolveAsmEvent(Object sender, ResolveEventArgs args) + { + foreach (RuntimeAssembly asm in m_AsmList) + { + if (String.Compare(asm.FullName, args.Name, StringComparison.OrdinalIgnoreCase) == 0) + return asm; + } + + return null; + } + + public Assembly ResolveROAsmEvent(Object sender, ResolveEventArgs args) + { + foreach (RuntimeAssembly asm in m_AsmList) + { + if (String.Compare(asm.FullName, args.Name, StringComparison.OrdinalIgnoreCase) == 0) + return asm; + } + + // We failed to find the referenced assembly in our pre-loaded assemblies, so try to load it based on policy. + string asmName = AppDomain.CurrentDomain.ApplyPolicy(args.Name); + return Assembly.ReflectionOnlyLoad(asmName); + } + + private ModuleBuilder m_Module; + private ITypeLibImporterNotifySink m_UserSink; + private List<RuntimeAssembly> m_AsmList = new List<RuntimeAssembly>(); + } + } +} +#endif // !FEATURE_CORECLR // current implementation requires reflection only load + |