3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 /*=============================================================================
8 ** Class: TypeLibConverter
11 ** Purpose: Component that implements the ITypeLibConverter interface and
12 ** does the actual work of converting a typelib to metadata and
16 =============================================================================*/
17 #if !FEATURE_CORECLR // current implementation requires reflection only load
18 namespace System.Runtime.InteropServices {
21 using System.Diagnostics.Contracts;
22 using System.Collections;
23 using System.Collections.Generic;
24 using System.Threading;
25 using System.Runtime.InteropServices.TCEAdapterGen;
27 using System.Reflection;
28 using System.Reflection.Emit;
29 using System.Configuration.Assemblies;
30 using Microsoft.Win32;
31 using System.Runtime.CompilerServices;
32 using System.Globalization;
33 using System.Security;
34 using System.Security.Permissions;
35 using System.Runtime.InteropServices.ComTypes;
36 using System.Runtime.Versioning;
37 using WORD = System.UInt16;
38 using DWORD = System.UInt32;
39 using _TYPELIBATTR = System.Runtime.InteropServices.ComTypes.TYPELIBATTR;
41 [Guid("F1C3BF79-C3E4-11d3-88E7-00902754C43A")]
42 [ClassInterface(ClassInterfaceType.None)]
43 [System.Runtime.InteropServices.ComVisible(true)]
44 public sealed class TypeLibConverter : ITypeLibConverter
46 private const String s_strTypeLibAssemblyTitlePrefix = "TypeLib ";
47 private const String s_strTypeLibAssemblyDescPrefix = "Assembly generated from typelib ";
48 private const int MAX_NAMESPACE_LENGTH = 1024;
52 // ITypeLibConverter interface.
55 [System.Security.SecuritySafeCritical] // auto-generated
56 [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)]
57 [ResourceExposure(ResourceScope.Machine)]
58 [ResourceConsumption(ResourceScope.Machine)]
59 public AssemblyBuilder ConvertTypeLibToAssembly([MarshalAs(UnmanagedType.Interface)] Object typeLib,
62 ITypeLibImporterNotifySink notifySink,
64 StrongNameKeyPair keyPair,
65 bool unsafeInterfaces)
67 return ConvertTypeLibToAssembly(typeLib,
70 ? TypeLibImporterFlags.UnsafeInterfaces
82 [System.Security.SecuritySafeCritical] // auto-generated
83 [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)]
84 [ResourceExposure(ResourceScope.Machine)]
85 [ResourceConsumption(ResourceScope.Machine)]
86 public AssemblyBuilder ConvertTypeLibToAssembly([MarshalAs(UnmanagedType.Interface)] Object typeLib,
88 TypeLibImporterFlags flags,
89 ITypeLibImporterNotifySink notifySink,
91 StrongNameKeyPair keyPair,
95 // Validate the arguments.
97 throw new ArgumentNullException("typeLib");
98 if (asmFileName == null)
99 throw new ArgumentNullException("asmFileName");
100 if (notifySink == null)
101 throw new ArgumentNullException("notifySink");
102 if (String.Empty.Equals(asmFileName))
103 throw new ArgumentException(Environment.GetResourceString("Arg_InvalidFileName"), "asmFileName");
104 if (asmFileName.Length > Path.MAX_PATH)
105 throw new ArgumentException(Environment.GetResourceString("IO.PathTooLong"), asmFileName);
106 if ((flags & TypeLibImporterFlags.PrimaryInteropAssembly) != 0 && publicKey == null && keyPair == null)
107 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_PIAMustBeStrongNamed"));
108 Contract.EndContractBlock();
110 ArrayList eventItfInfoList = null;
112 // Determine the AssemblyNameFlags
113 AssemblyNameFlags asmNameFlags = AssemblyNameFlags.None;
115 // Retrieve the assembly name from the typelib.
116 AssemblyName asmName = GetAssemblyNameFromTypelib(typeLib, asmFileName, publicKey, keyPair, asmVersion, asmNameFlags);
118 // Create the dynamic assembly that will contain the converted typelib types.
119 AssemblyBuilder asmBldr = CreateAssemblyForTypeLib(typeLib, asmFileName, asmName,
120 (flags & TypeLibImporterFlags.PrimaryInteropAssembly) != 0,
121 (flags & TypeLibImporterFlags.ReflectionOnlyLoading) != 0,
122 (flags & TypeLibImporterFlags.NoDefineVersionResource) != 0);
124 // Define a dynamic module that will contain the contain the imported types.
125 String strNonQualifiedAsmFileName = Path.GetFileName(asmFileName);
126 ModuleBuilder modBldr = asmBldr.DefineDynamicModule(strNonQualifiedAsmFileName, strNonQualifiedAsmFileName);
128 // If the namespace hasn't been specified, then use the assembly name.
129 if (asmNamespace == null)
130 asmNamespace = asmName.Name;
132 // Create a type resolve handler that will also intercept resolve ref messages
133 // on the sink interface to build up a list of referenced assemblies.
134 TypeResolveHandler typeResolveHandler = new TypeResolveHandler(modBldr, notifySink);
136 // Add a listener for the type resolve events.
137 AppDomain currentDomain = Thread.GetDomain();
138 ResolveEventHandler resolveHandler = new ResolveEventHandler(typeResolveHandler.ResolveEvent);
139 ResolveEventHandler asmResolveHandler = new ResolveEventHandler(typeResolveHandler.ResolveAsmEvent);
140 ResolveEventHandler ROAsmResolveHandler = new ResolveEventHandler(typeResolveHandler.ResolveROAsmEvent);
141 currentDomain.TypeResolve += resolveHandler;
142 currentDomain.AssemblyResolve += asmResolveHandler;
143 currentDomain.ReflectionOnlyAssemblyResolve += ROAsmResolveHandler;
145 // Convert the types contained in the typelib into metadata and add them to the assembly.
146 nConvertTypeLibToMetadata(typeLib, asmBldr.InternalAssembly, modBldr.InternalModule, asmNamespace, flags, typeResolveHandler, out eventItfInfoList);
148 // Update the COM types in the assembly.
149 UpdateComTypesInAssembly(asmBldr, modBldr);
151 // If there are any event sources then generate the TCE adapters.
152 if (eventItfInfoList.Count > 0)
153 new TCEAdapterGenerator().Process(modBldr, eventItfInfoList);
155 // Remove the listener for the type resolve events.
156 currentDomain.TypeResolve -= resolveHandler;
157 currentDomain.AssemblyResolve -= asmResolveHandler;
158 currentDomain.ReflectionOnlyAssemblyResolve -= ROAsmResolveHandler;
160 // We have finished converting the typelib and now have a fully formed assembly.
164 [System.Security.SecuritySafeCritical] // auto-generated
165 [ResourceExposure(ResourceScope.Machine)]
166 [ResourceConsumption(ResourceScope.Machine)]
167 [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)]
168 [return : MarshalAs(UnmanagedType.Interface)]
169 public Object ConvertAssemblyToTypeLib(Assembly assembly, String strTypeLibName, TypeLibExporterFlags flags, ITypeLibExporterNotifySink notifySink)
171 RuntimeAssembly rtAssembly;
172 AssemblyBuilder ab = assembly as AssemblyBuilder;
174 rtAssembly = ab.InternalAssembly;
176 rtAssembly = assembly as RuntimeAssembly;
178 return nConvertAssemblyToTypeLib(rtAssembly, strTypeLibName, flags, notifySink);
181 [ResourceExposure(ResourceScope.Machine)]
182 [ResourceConsumption(ResourceScope.Machine)]
183 public bool GetPrimaryInteropAssembly(Guid g, Int32 major, Int32 minor, Int32 lcid, out String asmName, out String asmCodeBase)
185 String strTlbId = "{" + g.ToString().ToUpper(CultureInfo.InvariantCulture) + "}";
186 String strVersion = major.ToString("x", CultureInfo.InvariantCulture) + "." + minor.ToString("x", CultureInfo.InvariantCulture);
188 // Set the two out values to null before we start.
192 // Try to open the HKEY_CLASS_ROOT\TypeLib key.
193 using (RegistryKey TypeLibKey = Registry.ClassesRoot.OpenSubKey("TypeLib", false))
195 if (TypeLibKey != null)
197 // Try to open the HKEY_CLASS_ROOT\TypeLib\<TLBID> key.
198 using (RegistryKey TypeLibSubKey = TypeLibKey.OpenSubKey(strTlbId))
200 if (TypeLibSubKey != null)
202 // Try to open the HKEY_CLASS_ROOT\TypeLib\<TLBID>\<Major.Minor> key.
203 using (RegistryKey VersionKey = TypeLibSubKey.OpenSubKey(strVersion, false))
205 if (VersionKey != null)
207 // Attempt to retrieve the assembly name and codebase under the version key.
208 asmName = (String)VersionKey.GetValue("PrimaryInteropAssemblyName");
209 asmCodeBase = (String)VersionKey.GetValue("PrimaryInteropAssemblyCodeBase");
217 // If the assembly name isn't null, then we found an PIA.
218 return asmName != null;
223 // Non native helper methods.
226 [System.Security.SecurityCritical] // auto-generated
227 [ResourceExposure(ResourceScope.Machine)]
228 [ResourceConsumption(ResourceScope.Machine)]
229 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
230 private static AssemblyBuilder CreateAssemblyForTypeLib(Object typeLib, String asmFileName, AssemblyName asmName, bool bPrimaryInteropAssembly, bool bReflectionOnly, bool bNoDefineVersionResource)
232 // Retrieve the current app domain.
233 AppDomain currentDomain = Thread.GetDomain();
235 // Retrieve the directory from the assembly file name.
237 if (asmFileName != null)
239 dir = Path.GetDirectoryName(asmFileName);
240 if (String.IsNullOrEmpty(dir))
244 AssemblyBuilderAccess aba;
247 aba = AssemblyBuilderAccess.ReflectionOnly;
251 aba = AssemblyBuilderAccess.RunAndSave;
254 // Create the dynamic assembly itself.
255 AssemblyBuilder asmBldr;
257 List<CustomAttributeBuilder> assemblyAttributes = new List<CustomAttributeBuilder>();
259 // mscorlib.dll must specify the security rules that assemblies it emits are to use, since by
260 // default all assemblies will follow security rule set level 2, and we want to make that an
261 // explicit decision.
262 ConstructorInfo securityRulesCtor = typeof(SecurityRulesAttribute).GetConstructor(new Type[] { typeof(SecurityRuleSet) });
263 CustomAttributeBuilder securityRulesAttribute =
264 new CustomAttributeBuilder(securityRulesCtor, new object[] { SecurityRuleSet.Level2 });
265 assemblyAttributes.Add(securityRulesAttribute);
266 #endif // !FEATURE_CORECLR
268 asmBldr = currentDomain.DefineDynamicAssembly(asmName, aba, dir, false, assemblyAttributes);
270 // Set the Guid custom attribute on the assembly.
271 SetGuidAttributeOnAssembly(asmBldr, typeLib);
273 // Set the imported from COM attribute on the assembly and return it.
274 SetImportedFromTypeLibAttrOnAssembly(asmBldr, typeLib);
276 // Set the version information on the typelib.
277 if (bNoDefineVersionResource)
279 SetTypeLibVersionAttribute(asmBldr, typeLib);
283 SetVersionInformation(asmBldr, typeLib, asmName);
286 // If we are generating a PIA, then set the PIA custom attribute.
287 if (bPrimaryInteropAssembly)
288 SetPIAAttributeOnAssembly(asmBldr, typeLib);
293 [System.Security.SecurityCritical] // auto-generated
294 internal static AssemblyName GetAssemblyNameFromTypelib(Object typeLib, String asmFileName, byte[] publicKey, StrongNameKeyPair keyPair, Version asmVersion, AssemblyNameFlags asmNameFlags)
296 // Extract the name of the typelib.
297 String strTypeLibName = null;
298 String strDocString = null;
299 int dwHelpContext = 0;
300 String strHelpFile = null;
301 ITypeLib pTLB = (ITypeLib)typeLib;
302 pTLB.GetDocumentation(-1, out strTypeLibName, out strDocString, out dwHelpContext, out strHelpFile);
304 // Retrieve the name to use for the assembly.
305 if (asmFileName == null)
307 asmFileName = strTypeLibName;
311 Contract.Assert((asmFileName != null) && (asmFileName.Length > 0), "The assembly file name cannot be an empty string!");
313 String strFileNameNoPath = Path.GetFileName(asmFileName);
314 String strExtension = Path.GetExtension(asmFileName);
316 // Validate that the extension is valid.
317 bool bExtensionValid = ".dll".Equals(strExtension, StringComparison.OrdinalIgnoreCase);
319 // If the extension is not valid then tell the user and quit.
320 if (!bExtensionValid)
321 throw new ArgumentException(Environment.GetResourceString("Arg_InvalidFileExtension"));
323 // The assembly cannot contain the path nor the extension.
324 asmFileName = strFileNameNoPath.Substring(0, strFileNameNoPath.Length - ".dll".Length);
327 // If the version information was not specified, then retrieve it from the typelib.
328 if (asmVersion == null)
332 Marshal.GetTypeLibVersion(pTLB, out major, out minor);
333 asmVersion = new Version(major, minor, 0, 0);
336 // Create the assembly name for the imported typelib's assembly.
337 AssemblyName AsmName = new AssemblyName();
344 AssemblyHashAlgorithm.None,
345 AssemblyVersionCompatibility.SameMachine,
353 private static void UpdateComTypesInAssembly(AssemblyBuilder asmBldr, ModuleBuilder modBldr)
355 // Retrieve the AssemblyBuilderData associated with the assembly builder.
356 AssemblyBuilderData AsmBldrData = asmBldr.m_assemblyData;
358 // Go through the types in the module and add them as public COM types.
359 Type[] aTypes = modBldr.GetTypes();
360 int NumTypes = aTypes.Length;
361 for (int cTypes = 0; cTypes < NumTypes; cTypes++)
362 AsmBldrData.AddPublicComType(aTypes[cTypes]);
366 [System.Security.SecurityCritical] // auto-generated
367 private static void SetGuidAttributeOnAssembly(AssemblyBuilder asmBldr, Object typeLib)
369 // Retrieve the GuidAttribute constructor.
370 Type []aConsParams = new Type[1] {typeof(String)};
371 ConstructorInfo GuidAttrCons = typeof(GuidAttribute).GetConstructor(aConsParams);
373 // Create an instance of the custom attribute builder.
374 Object[] aArgs = new Object[1] {Marshal.GetTypeLibGuid((ITypeLib)typeLib).ToString()};
375 CustomAttributeBuilder GuidCABuilder = new CustomAttributeBuilder(GuidAttrCons, aArgs);
377 // Set the GuidAttribute on the assembly builder.
378 asmBldr.SetCustomAttribute(GuidCABuilder);
381 [System.Security.SecurityCritical] // auto-generated
382 private static void SetImportedFromTypeLibAttrOnAssembly(AssemblyBuilder asmBldr, Object typeLib)
384 // Retrieve the ImportedFromTypeLibAttribute constructor.
385 Type []aConsParams = new Type[1] {typeof(String)};
386 ConstructorInfo ImpFromComAttrCons = typeof(ImportedFromTypeLibAttribute).GetConstructor(aConsParams);
388 // Retrieve the name of the typelib.
389 String strTypeLibName = Marshal.GetTypeLibName((ITypeLib)typeLib);
391 // Create an instance of the custom attribute builder.
392 Object[] aArgs = new Object[1] {strTypeLibName};
393 CustomAttributeBuilder ImpFromComCABuilder = new CustomAttributeBuilder(ImpFromComAttrCons, aArgs);
395 // Set the ImportedFromTypeLibAttribute on the assembly builder.
396 asmBldr.SetCustomAttribute(ImpFromComCABuilder);
399 [System.Security.SecurityCritical] // auto-generated
400 private static void SetTypeLibVersionAttribute(AssemblyBuilder asmBldr, Object typeLib)
402 Type []aConsParams = new Type[2] {typeof(int), typeof(int)};
403 ConstructorInfo TypeLibVerCons = typeof(TypeLibVersionAttribute).GetConstructor(aConsParams);
405 // Get the typelib version
408 Marshal.GetTypeLibVersion((ITypeLib)typeLib, out major, out minor);
410 // Create an instance of the custom attribute builder.
411 Object[] aArgs = new Object[2] {major, minor};
412 CustomAttributeBuilder TypeLibVerBuilder = new CustomAttributeBuilder(TypeLibVerCons, aArgs);
414 // Set the attribute on the assembly builder.
415 asmBldr.SetCustomAttribute(TypeLibVerBuilder);
418 [System.Security.SecurityCritical] // auto-generated
419 private static void SetVersionInformation(AssemblyBuilder asmBldr, Object typeLib, AssemblyName asmName)
421 // Extract the name of the typelib.
422 String strTypeLibName = null;
423 String strDocString = null;
424 int dwHelpContext = 0;
425 String strHelpFile = null;
426 ITypeLib pTLB = (ITypeLib)typeLib;
427 pTLB.GetDocumentation(-1, out strTypeLibName, out strDocString, out dwHelpContext, out strHelpFile);
429 // Generate the product name string from the named of the typelib.
430 String strProductName = String.Format(CultureInfo.InvariantCulture, Environment.GetResourceString("TypeLibConverter_ImportedTypeLibProductName"), strTypeLibName);
432 // Set the OS version information.
433 asmBldr.DefineVersionInfoResource(strProductName, asmName.Version.ToString(), null, null, null);
435 // Set the TypeLibVersion attribute
436 SetTypeLibVersionAttribute(asmBldr, typeLib);
439 [System.Security.SecurityCritical] // auto-generated
440 private static void SetPIAAttributeOnAssembly(AssemblyBuilder asmBldr, Object typeLib)
442 IntPtr pAttr = IntPtr.Zero;
444 ITypeLib pTLB = (ITypeLib)typeLib;
448 // Retrieve the PrimaryInteropAssemblyAttribute constructor.
449 Type []aConsParams = new Type[2] {typeof(int), typeof(int)};
450 ConstructorInfo PIAAttrCons = typeof(PrimaryInteropAssemblyAttribute).GetConstructor(aConsParams);
452 // Retrieve the major and minor version from the typelib.
455 pTLB.GetLibAttr(out pAttr);
456 Attr = (_TYPELIBATTR)Marshal.PtrToStructure(pAttr, typeof(_TYPELIBATTR));
457 Major = Attr.wMajorVerNum;
458 Minor = Attr.wMinorVerNum;
462 // Release the typelib attributes.
463 if (pAttr != IntPtr.Zero)
464 pTLB.ReleaseTLibAttr(pAttr);
467 // Create an instance of the custom attribute builder.
468 Object[] aArgs = new Object[2] {Major, Minor};
469 CustomAttributeBuilder PIACABuilder = new CustomAttributeBuilder(PIAAttrCons, aArgs);
471 // Set the PrimaryInteropAssemblyAttribute on the assembly builder.
472 asmBldr.SetCustomAttribute(PIACABuilder);
477 // Native helper methods.
480 [System.Security.SecurityCritical] // auto-generated
481 [ResourceExposure(ResourceScope.None)]
482 [MethodImplAttribute(MethodImplOptions.InternalCall)]
483 private static extern void nConvertTypeLibToMetadata(Object typeLib, RuntimeAssembly asmBldr, RuntimeModule modBldr, String nameSpace, TypeLibImporterFlags flags, ITypeLibImporterNotifySink notifySink, out ArrayList eventItfInfoList);
485 // Must use assembly versioning or GuidAttribute to avoid collisions in typelib export or registration.
486 [System.Security.SecurityCritical] // auto-generated
487 [ResourceExposure(ResourceScope.Machine)]
488 [MethodImplAttribute(MethodImplOptions.InternalCall)]
489 private static extern Object nConvertAssemblyToTypeLib(RuntimeAssembly assembly, String strTypeLibName, TypeLibExporterFlags flags, ITypeLibExporterNotifySink notifySink);
491 [System.Security.SecurityCritical] // auto-generated
492 [ResourceExposure(ResourceScope.None)]
493 [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode), SuppressUnmanagedCodeSecurity]
494 internal extern static void LoadInMemoryTypeByName(RuntimeModule module, String className);
497 // Helper class called when a resolve type event is fired.
500 private class TypeResolveHandler : ITypeLibImporterNotifySink
502 public TypeResolveHandler(ModuleBuilder mod, ITypeLibImporterNotifySink userSink)
505 m_UserSink = userSink;
508 public void ReportEvent(ImporterEventKind eventKind, int eventCode, String eventMsg)
510 m_UserSink.ReportEvent(eventKind, eventCode, eventMsg);
513 public Assembly ResolveRef(Object typeLib)
515 Contract.Ensures(Contract.Result<Assembly>() != null && Contract.Result<Assembly>() is RuntimeAssembly);
516 Contract.EndContractBlock();
518 // Call the user sink to resolve the reference.
519 Assembly asm = m_UserSink.ResolveRef(typeLib);
522 throw new ArgumentNullException();
524 // Return the resolved assembly. We extract the internal assembly because we are called
525 // by the VM which accesses fields of the object directly and does not go via those
526 // delegating properties (the fields are empty if asm is an (external) AssemblyBuilder).
528 RuntimeAssembly rtAssembly = asm as RuntimeAssembly;
529 if (rtAssembly == null)
531 AssemblyBuilder ab = asm as AssemblyBuilder;
533 rtAssembly = ab.InternalAssembly;
536 if (rtAssembly == null)
537 throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeAssembly"));
539 // Add the assembly to the list of assemblies.
540 m_AsmList.Add(rtAssembly);
545 [System.Security.SecurityCritical] // auto-generated
546 public Assembly ResolveEvent(Object sender, ResolveEventArgs args)
548 // We need to load the type in the resolve event so that we will deal with
549 // cases where we are trying to load the CoClass before the interface has
553 LoadInMemoryTypeByName(m_Module.GetNativeHandle(), args.Name);
554 return m_Module.Assembly;
556 catch (TypeLoadException e)
558 if (e.ResourceId != System.__HResults.COR_E_TYPELOAD) // type not found
562 foreach (RuntimeAssembly asm in m_AsmList)
566 asm.GetType(args.Name, true, false);
569 catch (TypeLoadException e)
571 if (e._HResult != System.__HResults.COR_E_TYPELOAD) // type not found
579 public Assembly ResolveAsmEvent(Object sender, ResolveEventArgs args)
581 foreach (RuntimeAssembly asm in m_AsmList)
583 if (String.Compare(asm.FullName, args.Name, StringComparison.OrdinalIgnoreCase) == 0)
590 public Assembly ResolveROAsmEvent(Object sender, ResolveEventArgs args)
592 foreach (RuntimeAssembly asm in m_AsmList)
594 if (String.Compare(asm.FullName, args.Name, StringComparison.OrdinalIgnoreCase) == 0)
598 // We failed to find the referenced assembly in our pre-loaded assemblies, so try to load it based on policy.
599 string asmName = AppDomain.CurrentDomain.ApplyPolicy(args.Name);
600 return Assembly.ReflectionOnlyLoad(asmName);
603 private ModuleBuilder m_Module;
604 private ITypeLibImporterNotifySink m_UserSink;
605 private List<RuntimeAssembly> m_AsmList = new List<RuntimeAssembly>();
609 #endif // !FEATURE_CORECLR // current implementation requires reflection only load