/// <summary> /// Writes the entries needed to register a COM-imported type (e.g. a type /// with the <see cref="System.Runtime.InteropServices.ComImportAttribute"/>) /// to the registry file. /// </summary> /// <param name="type">Type to write.</param> /// <param name="codebase">Optional codebase, if it needs to be written.</param> /// <param name="rootKeyName">Name of root key where to add types.</param> /// <param name="writer"><see cref="RegistryFileWriter"/> used to write to the /// registry file.</param> private void WriteComImportInRegistryFile(Type type, string codebase, string rootKeyName, RegistryFileWriter writer) { Debug.Assert(type != null); Debug.Assert(!String.IsNullOrEmpty(rootKeyName)); Debug.Assert(writer != null); // For COM imports, we write the CLSID, but no ProgID. string keyPath = String.Format(@"{0}\CLSID\{1}\InprocServer32", rootKeyName, type.GUID.ToString("B").ToUpper(CultureInfo.InvariantCulture)); using (RegistryKeyWriter keyWriter = writer.AddKey(keyPath)) { keyWriter.AddValue("Class", type.FullName); keyWriter.AddValue("Assembly", assembly.FullName); keyWriter.AddValue("RuntimeVersion", assembly.ImageRuntimeVersion); if (!String.IsNullOrEmpty(codebase)) { keyWriter.AddValue("CodeBase", codebase); } } // Also write versioned key. keyPath += String.Format(@"\{0}", assembly.GetName().Version); using (RegistryKeyWriter keyWriter = writer.AddKey(keyPath)) { keyWriter.AddValue("Class", type.FullName); keyWriter.AddValue("Assembly", assembly.FullName); keyWriter.AddValue("RuntimeVersion", assembly.ImageRuntimeVersion); if (!String.IsNullOrEmpty(codebase)) { keyWriter.AddValue("CodeBase", codebase); } } }
/// <summary> /// Writes the entries needed to register a value type (e.g. structs) to /// the registry file. /// </summary> /// <param name="type">Type to write.</param> /// <param name="codebase">Optional codebase, if it needs to be written.</param> /// <param name="rootKeyName">Name of root key where to add types.</param> /// <param name="writer"><see cref="RegistryKeyWriter"/> used to write /// to the registry file.</param> private void WriteValueTypeInRegistryFile(Type type, string codebase, string rootKeyName, RegistryFileWriter writer) { Debug.Assert(type != null); Debug.Assert(!String.IsNullOrEmpty(rootKeyName)); Debug.Assert(writer != null); // Value types are written to the Classes\Record key, using the type's GUID and its version. string keyPath = String.Format(@"{0}\Record\{1}\{2}", rootKeyName, type.GUID.ToString("B").ToUpper(CultureInfo.InvariantCulture), assembly.GetName().Version); using (RegistryKeyWriter keyWriter = writer.AddKey(keyPath)) { keyWriter.AddValue("Class", type.FullName); keyWriter.AddValue("Assembly", assembly.FullName); keyWriter.AddValue("RuntimeVersion", assembly.ImageRuntimeVersion); if (!String.IsNullOrEmpty(codebase)) { keyWriter.AddValue("CodeBase", codebase); } } }
/// <summary> /// Writes the entries needed to register our assembly as a primary /// interop assembly to the registry file. /// </summary> /// <param name="attrib"><see cref="System.Reflection.CustomAttributeData"/> /// for the assembly's <see cref="System.Runtime.InteropServices.PrimaryInteropAssemblyAttribute"/>.</param> /// <param name="codebase">Optional codebase, if it needs to be written.</param> /// <param name="rootKeyName">Name of root key where to add types.</param> /// <param name="writer"><see cref="RegistryFileWriter"/> used to write to the /// registry file.</param> /// <seealso cref="System.Runtime.InteropServices.PrimaryInteropAssemblyAttribute"/> private void WritePrimaryInteropAssemblyInRegistryFile(CustomAttributeData attrib, string codebase, string rootKeyName, RegistryFileWriter writer) { Debug.Assert(attrib != null); Debug.Assert(!String.IsNullOrEmpty(rootKeyName)); Debug.Assert(writer != null); // For this, we add data to the Classes\TypeLib key, using TypeLib ID and version, // pulled from the PrimaryInteropAssemblyAttribute (and inexplicably expressed as hexadecimal values). string keyPath = String.Format(@"{0}\TypeLib\{1}\{2}.{3}", rootKeyName, Marshal.GetTypeLibGuidForAssembly(assembly).ToString("B").ToUpper(CultureInfo.InvariantCulture), ((int)attrib.ConstructorArguments[0].Value).ToString("x", CultureInfo.InvariantCulture), ((int)attrib.ConstructorArguments[1].Value).ToString("x", CultureInfo.InvariantCulture)); using (RegistryKeyWriter keyWriter = writer.AddKey(keyPath)) { keyWriter.AddValue("PrimaryInteropAssemblyName", assembly.FullName); if (!String.IsNullOrEmpty(codebase)) { keyWriter.AddValue("PrimaryInteropAssemblyCodeBase", codebase); } } }
/// <summary> /// Writes the entries needed to register a class type to the registry file. /// </summary> /// <param name="type">Type to write.</param> /// <param name="codebase">Optional codebase, if it needs to be written.</param> /// <param name="rootKeyName">Name of root key where to add types.</param> /// <param name="writer"><see cref="RegistryFileWriter"/> used to write to the /// registry file.</param> private void WriteClassInRegistryFile(Type type, string codebase, string rootKeyName, RegistryFileWriter writer) { Debug.Assert(type != null); Debug.Assert(!String.IsNullOrEmpty(rootKeyName)); Debug.Assert(writer != null); // For class types, we add ProgID entries AND CLSID entries. string clsid = type.GUID.ToString("B").ToUpper(CultureInfo.InvariantCulture); string progId = regServices.GetProgIdForType(type); string keyPath; // First ProgID if class has one. Link it to CLSID. if (!String.IsNullOrEmpty(progId)) { keyPath = String.Format(@"{0}\{1}", rootKeyName, progId); using (RegistryKeyWriter keyWriter = writer.AddKey(keyPath)) { keyWriter.AddDefaultValue(type.FullName); } keyPath += @"\CLSID"; using (RegistryKeyWriter keyWriter = writer.AddKey(keyPath)) { keyWriter.AddDefaultValue(clsid); } } // Now CLSID. First write class name in root CLSID key. keyPath = String.Format(@"{0}\CLSID\{1}", rootKeyName, clsid); using (RegistryKeyWriter keyWriter = writer.AddKey(keyPath)) { keyWriter.AddDefaultValue(type.FullName); } // Now write all infos in InprocServer32. keyPath += @"\InprocServer32"; using (RegistryKeyWriter keyWriter = writer.AddKey(keyPath)) { keyWriter.AddDefaultValue("mscoree.dll"); keyWriter.AddValue("ThreadingModel", "Both"); keyWriter.AddValue("Class", type.FullName); keyWriter.AddValue("Assembly", assembly.FullName); keyWriter.AddValue("RuntimeVersion", assembly.ImageRuntimeVersion); if (codebase != null) { keyWriter.AddValue("CodeBase", codebase); } } // Also write versioned key. keyPath += String.Format(@"\{0}", assembly.GetName().Version); using (RegistryKeyWriter keyWriter = writer.AddKey(keyPath)) { keyWriter.AddValue("Class", type.FullName); keyWriter.AddValue("Assembly", assembly.FullName); keyWriter.AddValue("RuntimeVersion", assembly.ImageRuntimeVersion); if (codebase != null) { keyWriter.AddValue("CodeBase", codebase); } } // If class has a ProgID, link the CLSID to it. if (!String.IsNullOrEmpty(progId)) { keyPath = String.Format(@"{0}\CLSID\{1}\ProgId", rootKeyName, clsid); using (RegistryKeyWriter keyWriter = writer.AddKey(keyPath)) { keyWriter.AddDefaultValue(progId); } } // Finally, add implemented categories. keyPath = String.Format(@"{0}\CLSID\{1}\Implemented Categories\{2}", rootKeyName, clsid, regServices.GetManagedCategoryGuid().ToString("B").ToUpper(CultureInfo.InvariantCulture)); writer.AddEmptyKey(keyPath); }
/// <summary> /// Generates the registry file (.reg) asked for by the user via the /// <c>/regfile</c> command-line option. /// </summary> /// <returns>Exit code to return from the CLRegAsm tool.</returns> /// <exception cref="CoreException">User asked for codebase but provided assembly /// has no codebase.</exception> private int GenerateRegistryFile() { // This will be changed if an error occurs int exitCode = 0; // Get list of registrable types in the assembly. Type[] registrableTypes = regServices.GetRegistrableTypesInAssembly(assembly); // Check if assembly is a primary interop assembly. IList <CustomAttributeData> attribs = CustomAttributeData.GetCustomAttributes(assembly); bool assemblyIsPrimaryInterop = false; foreach (CustomAttributeData attrib in attribs) { if (attrib.Constructor.DeclaringType == typeof(PrimaryInteropAssemblyAttribute)) { assemblyIsPrimaryInterop = true; break; } } if (registrableTypes.Length != 0 || assemblyIsPrimaryInterop) { // Get codebase if needed. string codebase = null; if (prms.setCodeBase) { codebase = assembly.CodeBase; if (String.IsNullOrEmpty(codebase)) { throw new CoreException(String.Format("Assembly \"{0}\" has no codebase; " + "cannot include codebase in registry file.", prms.assemblyName)); } } // Get root key name depending on whether this is per-user or per-machine. string rootKeyName; if (prms.perUser) { rootKeyName = String.Format(@"{0}\Software\Classes", Registry.CurrentUser.Name); } else { rootKeyName = Registry.ClassesRoot.Name; } // Create regfile writer. Debug.Assert(!String.IsNullOrEmpty(prms.registryFile)); using (RegistryFileWriter writer = new RegistryFileWriter(prms.registryFile)) { // Write each registrable type to the file. foreach (Type type in registrableTypes) { if (type.IsValueType) { // Value type, like struct. WriteValueTypeInRegistryFile(type, codebase, rootKeyName, writer); } else if (regServices.TypeRepresentsComType(type)) { // Com-imported type. WriteComImportInRegistryFile(type, codebase, rootKeyName, writer); } else { // Class type. WriteClassInRegistryFile(type, codebase, rootKeyName, writer); } } // Write primary interop assembly info. foreach (CustomAttributeData attrib in attribs) { if (attrib.Constructor.DeclaringType == typeof(PrimaryInteropAssemblyAttribute)) { WritePrimaryInteropAssemblyInRegistryFile(attrib, codebase, rootKeyName, writer); break; } } } // We're done, tell the user. console.WriteLine("Registry file \"{0}\" generated successfully.", prms.registryFile); } else if (!prms.silent) { // No registrable types and assembly isn't a primary interop assembly; can't continue. console.Error.WriteLine("Assembly \"{0}\" has no registrable types and is not a " + "primary interop assembly: cannot generate registry file.", prms.assemblyName); exitCode = 500; } return(exitCode); }