1. Introduction.

1.1 After publishing Programmatically Register COM Dlls in
C#
 back in 2011, I received a number of requests for
advise on how to register managed assemblies as COM servers.

1.2 In this blog, I will present the reader with a
sample C# program that will perform such registration.

1.3 The sample assembly COM-registration source codes
can be used to target 32-bit and 64-bit assemblies.

1.4 I will also provide 2 sets of programs (one 32-bit
and the other 64-bit) for testing.

2. The Assembly Registration
Program.

2.1 The full source codes for this article can be found
here in CodePlex.

2.1 Shown below is a partial listing of the source codes
for the program :

// Program.cs
// Main control source codes.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Collections;
using System.Collections.Specialized;
using System.Reflection;
using System.IO; namespace RegisterAssembly
{     // The managed definition of the ICreateTypeLib interface.     [ComImport()]     [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]     [Guid("00020406-0000-0000-C000-000000000046")]     public interface ICreateTypeLib     {         IntPtr CreateTypeInfo(string szName, System.Runtime.InteropServices.ComTypes.TYPEKIND tkind);         void SetName(string szName);         void SetVersion(short wMajorVerNum, short wMinorVerNum);         void SetGuid(ref Guid guid);         void SetDocString(string szDoc);         void SetHelpFileName(string szHelpFileName);         void SetHelpContext(int dwHelpContext);         void SetLcid(int lcid);         void SetLibFlags(uint uLibFlags);         void SaveAllChanges();     }     partial class Program     {         // Command line options.         const string TYPELIB = "TYPELIB";         const string CODE_BASE = "CODE_BASE";         const string VERBOSE = "VERBOSE";         const string UNREGISTER = "UNREGISTER";         // Windows API to register a COM type library.         [DllImport("Oleaut32.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]         public extern static UInt32 RegisterTypeLib(ITypeLib tlib, string szFullPath, string szHelpDir);         [DllImport("Oleaut32.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]         public extern static UInt32 UnRegisterTypeLib(ref Guid libID, UInt16 wVerMajor, UInt16 wVerMinor,
int lcid, System.Runtime.InteropServices.ComTypes.SYSKIND syskind);         public static bool Is32Bits()         {             if (IntPtr.Size == 4)             {                 // 32-bit                 return true;             }             return false;         }         public static bool Is64Bits()         {             if (IntPtr.Size == 8)             {                 // 64-bit                 return true;             }             return false;         }         static void DisplayUsage()         {             Console.WriteLine("Usage :");             Console.WriteLine("RegisterAssembly [CODE_BASE] [CREATE_TYPELIB] [VERBOSE] [UNREGISTER] <path to managed assembly file>");             Console.WriteLine("where CODE_BASE, CREATE_TYPELIB, VERBOSE, UNREGISTER are optional parameters");             Console.WriteLine("Note that the UNREGISTER parameter cannot be used with the CODE_BASE or the CREATE_TYPELIB parameters.");             Console.WriteLine();         }         static bool ProcessArguments(string[] args)         {             const int iCountMinimumParametersRequired = 1;             if (args == null)             {                 Console.WriteLine(string.Format("Invalid number of parameters."));                 return false;             }             if (args.Length < iCountMinimumParametersRequired)             {                 Console.WriteLine(string.Format("Invalid number of parameters (minimum parameters : [{0:D}]).",                     iCountMinimumParametersRequired));                 return false;             }             for (int i = 0; i < args.Length; i++)             {                 switch (args[i].Trim())                 {                     case TYPELIB:                         {                             m_bTypeLib = true;                             break;                         }                     case CODE_BASE:                         {                             m_bCodeBase = true;                             break;                         }                     case VERBOSE:                         {                             m_bVerbose = true;                             break;                         }                     case UNREGISTER:                         {                             m_bUnregister = true;                             break;                         }                     default:                         {                             if (string.IsNullOrEmpty(m_strTargetAssemblyFilePath))                             {                                 m_strTargetAssemblyFilePath = args[i].Trim();                                 break;                             }                             else                             {                                 Console.WriteLine(string.Format("Invalid parameter : [{0:S}].", args[i]));                                 return false;                             }                         }                 }             }             if (m_bUnregister)             {                 if (m_bCodeBase)                 {                     Console.WriteLine(string.Format("UNEGISTER flag cannot be used with the CODE_BASE flag."));                     return false;                 }             }             return true;         }                 static bool DoWork()         {             try             {                 if (m_bVerbose)                 {                     Console.WriteLine(string.Format("Target Assembly File : [{0:S}].", m_strTargetAssemblyFilePath));                 }                 if (m_bUnregister)                 {                     if (PerformAssemblyUnregistration(m_strTargetAssemblyFilePath) == false)                     {                         return false;                     }                                          if (m_bTypeLib == true)                     {                         return PerformTypeLibUnRegistration(m_strTargetAssemblyFilePath);                     }                     else                     {                         return true;                     }                 }                 else                 {                     if (PerformAssemblyRegistration(m_strTargetAssemblyFilePath, m_bCodeBase) == false)                     {                         return false;                     }                     if (m_bTypeLib == true)                     {                         return PerformTypeLibCreationAndRegistration(m_strTargetAssemblyFilePath);                     }                     else                     {                         return true;                     }                 }             }             catch (Exception ex)             {                 Console.WriteLine(string.Format("An exception occurred. Exception description : [{0:S}].", ex.Message));                 return false;             }         }         static void Main(string[] args)         {             if (ProcessArguments(args) == false)             {                 DisplayUsage();                 return;             }             if (ReadConfigSettings() == false)             {                 return;             }             DoWork();         }         public static bool m_bVerbose = false;         private static bool m_bTypeLib = false;         private static bool m_bCodeBase = false;         private static bool m_bUnregister = false;         private static string m_strTargetAssemblyFilePath = null;         private static ITypeLibExporterNotifySink m_pITypeLibExporterNotifySink = null;     }
}
  • The program begins by inspecting its arguments (in
    ProcessArguments()).
  • Member variables are set according to the
    arguments.
  • If there is any error discovered inside
    ProcessArguments(), the program will display instructions on the program
    arguments and then the program will stop.
  • Next, via ReadConfigSettings(), the configuration file
    of the program is examined to check for the type to be created for the
    ITypeLibExporterNotifySink object (more on this later).
  • If ReadConfigSettings() encounters an error, a false
    will be returned and the program will end.
  • Otherwise,the DoWork() method will be called to perform
    the registration task.

The sections that follow will provide more details on
the running process of the program.

2. The Arguments to the Program.

2.1 The program takes at minimum one parameter (i.e. the
path to the assembly to be registered).

2.2 VERBOSE , UNREGISTER, CODE_BASE and TYPELIB are
optional parameters which can be used in combination.

2.3 The VERBOSE option indicates to the program to
display additional useful information at runtime.

2.4 Unless the UNREGISTER parameter is used, it is
assumed that the program is to perform assembly un-registration.

2.3 The CODE_BASE parameter indicates that the assembly
registration includes the assembly’s full path in the registry.

  • Note that this option cannot be used together with the
    UNREGISTER argument.

2.4 The presence of the TYPELIB argument indicates the
following :

  • For assembly registration, an associated type library
    will be created and registered.
  • For assembly un-registration, the type library
    associated with the assembly will be un-registered.

3. PerformAssemblyRegistration().

3.1 The source codes for the
PerformAssemblyRegistration() method is found in AssemblyRegistration.cs :

        static bool PerformAssemblyRegistration(string strTargetAssemblyFilePath, bool bCodeBase)         {             try             {                 RegistrationServices registration_services = new RegistrationServices();                 Assembly assembly = Assembly.LoadFrom(strTargetAssemblyFilePath);                 AssemblyRegistrationFlags flags;                 bool bRet = false;                 if (bCodeBase == true)                 {                     flags = AssemblyRegistrationFlags.SetCodeBase;                 }                 else                 {                     flags = AssemblyRegistrationFlags.None;                 }                 bRet = registration_services.RegisterAssembly(assembly, flags);                 if (bRet)                 {                     Console.WriteLine(string.Format("Successfully registered assembly [{0:S}].", strTargetAssemblyFilePath));                     if (m_bVerbose)                     {                         Type[] types = registration_services.GetRegistrableTypesInAssembly(assembly);                         Console.WriteLine(string.Format("Types Registered :"));                         foreach (Type type in types)                         {                             Console.WriteLine(string.Format("GUID : [{0:S}] [{1:S}].", type.GUID.ToString(), type.FullName));                         }                     }                 }                 else                 {                     Console.WriteLine(string.Format("Failed to register assembly [{0:S}].", strTargetAssemblyFilePath));                 }                 return bRet;             }             catch (Exception ex)             {                 Console.WriteLine(string.Format("An exception occurred. Exception description : [{0:S}].", ex.Message));                 return false;             }         }

3.2 PerformAssemblyRegistration() takes 2 parameters
:

  • A path to the assembly file to be
    registered.
  • A boolean flag that indicates whether the path to the
    assembly (i.e. the code base) is to be  written in the
    registry.

The assembly registration work is done via
the RegistrationServices class, specifically,
the RegisterAssembly() method.

3.3 If all went well with the assembly registration
process, the types which have been registered will be displayed if the “VERBOSE”
flag was used.

3.4 If the registration process was successful,
PerformAssemblyRegistration() will return true.

3.5 If the registration process failed, either because
the RegisterAssembly() returned false or because an exception was
thrown,  PerformAssemblyRegistration() will return false and the program
will stop.

4.
PerformTypeLibCreationAndRegistration().

4.1 The source codes for the
PerformTypeLibCreationAndRegistration() method is also listed
in AssemblyRegistration.cs :

        static bool PerformTypeLibCreationAndRegistration(string strTargetAssemblyFilePath)         {             try             {                 string strTargetAssemblyDirectory = Path.GetDirectoryName(strTargetAssemblyFilePath);                 string strTargetAssemblyFileNameWithoutExtension = Path.GetFileNameWithoutExtension(strTargetAssemblyFilePath);                 string strTargetTypeLibFullPath = strTargetAssemblyDirectory + "\\" + strTargetAssemblyFileNameWithoutExtension + ".tlb";                 TypeLibConverter converter = new TypeLibConverter();                 Assembly assembly = Assembly.LoadFrom(strTargetAssemblyFilePath);                 TypeLibExporterFlags flags;                 if (Is32Bits())                 {                     flags = TypeLibExporterFlags.ExportAs32Bit;                 }                 else if (Is64Bits())                 {                     flags = TypeLibExporterFlags.ExportAs64Bit;                 }                 else                 {                     Console.WriteLine(string.Format("Unknown bit-ness."));                     return false;                 }                 ICreateTypeLib create_typeLib = (ICreateTypeLib)(converter.ConvertAssemblyToTypeLib                     (assembly, strTargetTypeLibFullPath, flags, m_pITypeLibExporterNotifySink));                 // SaveAllChanges() will create the TypeLib physical file                 // based on strTargetTypeLibFullPath.                 create_typeLib.SaveAllChanges();                 ITypeLib typelib = (ITypeLib)create_typeLib;                 UInt32 uiRetTemp = RegisterTypeLib(typelib, strTargetTypeLibFullPath, null);                 if (uiRetTemp == 0)                 {                     Console.WriteLine(string.Format("TypeLib File [{0:S}] registered.", strTargetTypeLibFullPath));                 }                 else                 {                     Console.WriteLine(string.Format("Failed to register TypeLib File [{0:S}]. Error code : [{1:D}]",                         strTargetTypeLibFullPath, uiRetTemp));                     return false;                 }                 return true;             }             catch (Exception ex)             {                 Console.WriteLine(string.Format("An exception occurred. Exception description : [{0:S}].", ex.Message));                 return false;             }         }

4.2 This method will perform the creation of a COM type
library from the target assembly. After the type library has been created, it
will be registered.

4.3 The TypeLibConverter class is used to perform
the type library creation. Specifically the ConvertAssemblyToTypeLib() method.

4.4 An important part of calling
ConvertAssemblyToTypeLib() has to do with the bit-ness of the type library to be
created for the assembly :

  • If the RegisterAssembly program is compiled as a 32-bit
    program, it can only register a 32-bit assembly.
  • Hence the TypeLibExporterFlags flag parameter for
    ConvertAssemblyToTypeLib() must be set to
    TypeLibExporterFlags.ExportAs32Bit.
  • In the same way, if it is compiled as a 64-bit program,
    TypeLibExporterFlags.ExportAs64Bit must be used.

4.5 A simple but effective way of determining whether
RegisterAssembly is running as a 32-bit or 64-bit program is via testing the
size of IntPtr as can be seen in the Is32Bits() and Is64Bits()
methods.

4.6 Another point to note is the
ITypeLibExporterNotifySink parameter for ConvertAssemblyToTypeLib().

4.7 This parameter must be supplied with an instance of
a class that implements the ITypeLibExporterNotifySink interface. This object
can be important in some circumstances. This interface contains 2 methods
:

  • The ReportEvent() method is purely for information
    purposes only.
  • The ResolveRef() method requires some explanation (see
    below).

4.8 In a nutshell, the ResolveRef() method will be
called when the assembly to be registered references a COM Type that is exported
from another assembly instead of from an unmanaged type library. E.g. the COM
type being defined as a managed type and is exposed to COM via
COMVisibleAttribute. More will be explained in the next section
(ConversionEventHandler).

4.9 When ResolveRef() is called, the referenced assembly
is passed as parameter and ResolveRef() is expected to return an object which,
at minimum, implements the ITypeLib interface. See ITypeLibExporterNotifySink.ResolveRef Method
(Assembly)
.

4.10 I will be writing separate blogs in the future
expounding ITypeLibExporterNotifySink implementations in greater detail. For the
current blog and source codes, a default adequate one (ConversionEventHandler)
is supplied and used (see next section).

4.11 The output of the ConvertAssemblyToTypeLib() method
will be an object that implements the ICreateTypeLib and the ITypeLib interfaces
:

  • We then cast the returned object to ICreateTypeLib and
    call the SaveAllChanges() method.
  • This will save the information in the ITypeLib object
    into an external file.
  • The type library file will be saved in the same
    directory and using the same name as the target assembly.
  • We then cast the returned object into the ITypeLib
    interface and register the saved type library by calling the RegisterTypeLib()
    API.

5. The ConversionEventHandler
class.

5.1 The source codes for the ConversionEventHandler
class is listed in ConversionEventHandler.cs :

// ConversionEventHandler.cs
// Source codes for handling assembly reference resolution.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Collections;
using System.Collections.Specialized;
using System.Reflection;
using System.IO; namespace RegisterAssembly
{     public class ConversionEventHandler : ITypeLibExporterNotifySink     {         public ConversionEventHandler(bool bVerbose)         {             m_bVerbose = bVerbose;         }         public void ReportEvent(ExporterEventKind eventKind, int eventCode, string eventMsg)         {             // Handle the warning event here.             if (m_bVerbose)             {                 Console.WriteLine("ConversionEventHandler.ReportEvent() [eventKind : {0:S}] [eventCode : {1:D}] [eventMsg : {2:S}]",
eventKind.ToString(), eventCode, eventMsg);             }         }         public Object ResolveRef(Assembly asm)         {             try             {                 // Resolve the reference here and return a correct type library.                 if (m_bVerbose)                 {                     Console.WriteLine("ConversionEventHandler.ResolveRef() [assembly : {0:S}]", asm.FullName);                 }                 string strAssemblyDirectory = Path.GetDirectoryName(asm.Location);                 string strAssemblyFileNameWithoutExtension = Path.GetFileNameWithoutExtension(asm.Location);                 string strTypeLibFullPath = strAssemblyDirectory + "\\" + strAssemblyFileNameWithoutExtension + ".tlb";                 TypeLibConverter converter = new TypeLibConverter();                 ConversionEventHandler eventHandler = new ConversionEventHandler(m_bVerbose);                 TypeLibExporterFlags flags;                 if (Program.Is32Bits())                 {                     flags = TypeLibExporterFlags.ExportAs32Bit;                 }                 else if (Program.Is64Bits())                 {                     flags = TypeLibExporterFlags.ExportAs64Bit;                 }                 else                 {                     Console.WriteLine(string.Format("Unknown bit-ness."));                     return null;                 }                 ICreateTypeLib create_typeLib = null;                 try                 {                     create_typeLib = (ICreateTypeLib)(converter.ConvertAssemblyToTypeLib                         (asm, strTypeLibFullPath, flags, eventHandler));                 }                 catch(Exception ex)                 {                     Console.WriteLine(string.Format("Unable to convert assembly [{0:S}] into a Type Lib. Exception description : [{1:S}]",                         strAssemblyFileNameWithoutExtension, ex.Message));                     return null;                 }                 try                 {                     // SaveAllChanges() will create the TypeLib physical file                     // based on strTargetTypeLibFullPath.                     create_typeLib.SaveAllChanges();                 }                 catch (Exception ex)                 {                     Console.WriteLine(string.Format("Unable to save TypeLib File [{0:S}] registered. Exception description : [{1:S}]",
strTypeLibFullPath, ex.Message));                     return null;                 }                 ITypeLib typelib = (ITypeLib)create_typeLib;                 UInt32 uiRetTemp = Program.RegisterTypeLib(typelib, strTypeLibFullPath, null);                 if (uiRetTemp == 0)                 {                     Console.WriteLine(string.Format("TypeLib File [{0:S}] registered.", strTypeLibFullPath));                 }                 else                 {                     Console.WriteLine(string.Format("Failed to register TypeLib File [{0:S}]. Error code : [{1:D}]",                         strTypeLibFullPath, uiRetTemp));                     return null;                 }                 return typelib;             }             catch(Exception ex)             {                 Console.WriteLine(string.Format("An exception occurred. Exception description : [{0:S}].", ex.Message));                 return null;             }         }         private bool m_bVerbose = false;     }
}

The following are the pertinent points concerning this
class (some points have been mentioned previously and are repeated) :

  • It implements the ITypeLibExporterNotifySink
    interface.
  • As mentioned previously, the ReportEvent() method is not
    very important and it serves only to provide information during the conversion
    process.
  • The implementation for ResolveRef(), however, is not
    trivial.
  • It will be called when the following conditions hold
    :

    • If the assembly to be registered references COM-visible
      types from another assembly.
    • The assembly which contains the COM-visible types has
      not been registered.
    • A type library for the assembly which contains the
      COM-visible types has not been created nor registered.
  • Hence if the target assembly does not reference
    COM-visible types from other assemblies, ResolveRef() will not be
    invoked.
  • Also, if the target assembly does reference COM-visible
    types from another assembly but that assembly has been registered and its type
    library created and registered, then likewise, ResolveRef() will not be
    invoked.
  • If a type library is to be created, it is expected to be
    saved to disk and then registered.

5.3 The ConversionEventHandler.ResolveRef() method is
very similar to the PerformTypeLibCreationAndRegistration() method in that the
TypeLibConverter.ConvertAssemblyToTypeLib() method is used to process an
assembly :

  • In the case of ResolveRef(), the assembly to be
    processed is a reference assembly which contain COM-visible types.
  • In the case of the ConversionEventHandler class, its
    ResolveRef() method uses TypeLibConverter.ConvertAssemblyToTypeLib() to generate
    an ITypeLib object.
  • This ITypeLib object is then saved to disk and
    registered.
  • This ITypeLib object is then returned.

Other implementations of ITypeLibExporterNotifySink may
opt to search for an existing type library file, dynamically load it into
memory, extract an ITypeLib object and then return it.

5.4 Note
well
 that referenced assemblies which contain
COM-visible types should be pre-registered (either via RegisterAssembly.exe or RegAsm.exe).
Likewise, their associated type libraries should have been generated and
registered before hand. Generally speaking a call to ResolveRef() can and should
be avoided if possible.

6.
PerformAssemblyUnregistration().

6.1 If the RegisterAssembly program is required to
unregister an assembly, PerformAssemblyUnregistration() will be used. The
listing for this method can be found in AssemblyUnregistration.cs :

        static bool PerformAssemblyUnregistration(string strTargetAssemblyFilePath)         {             try             {                 RegistrationServices registration_services = new RegistrationServices();                 Assembly assembly = Assembly.LoadFrom(strTargetAssemblyFilePath);                 bool bRet = false;                 bRet = registration_services.UnregisterAssembly(assembly);                 if (bRet)                 {                     Console.WriteLine(string.Format("Successfully unregistered assembly [{0:S}].", strTargetAssemblyFilePath));                 }                 else                 {                     Console.WriteLine(string.Format("Failed to unregister assembly [{0:S}].", strTargetAssemblyFilePath));                 }                 return bRet;             }             catch (Exception ex)             {                 Console.WriteLine(string.Format("An exception occurred. Exception description : [{0:S}].", ex.Message));                 return false;             }         }

6.2 It is simple. Just a call to the
RegistrationServices.UnregisterAssembly() will do.

7. PerformTypeLibUnRegistration().

7.1 If an associated type library for the assembly exist
and was registered, PerformTypeLibUnRegistration() is used to perform
unregistration of this type library.

7.2 The source codes for PerformTypeLibUnRegistration()
can be found in AssemblyUnregistration.cs :

        static bool PerformTypeLibUnRegistration(string strTargetAssemblyFilePath)         {             try             {                 Assembly assembly = Assembly.LoadFrom(strTargetAssemblyFilePath);                 Version version = assembly.GetName().Version;                 GuidAttribute guid_attribute = (GuidAttribute)assembly.GetCustomAttributes(typeof(GuidAttribute), false)[0];                 Guid guid = new Guid(guid_attribute.Value);                 System.Runtime.InteropServices.ComTypes.SYSKIND syskind;                 if (Is32Bits())                 {                     syskind = System.Runtime.InteropServices.ComTypes.SYSKIND.SYS_WIN32;                 }                 else if (Is64Bits())                 {                     syskind = System.Runtime.InteropServices.ComTypes.SYSKIND.SYS_WIN64;                 }                 else                 {                     Console.WriteLine(string.Format("Unknown bit-ness."));                     return false;                 }                 UInt32 uiRetTemp = UnRegisterTypeLib(ref guid, (UInt16)(version.Major), (UInt16)(version.Minor), 0, syskind);                 if (uiRetTemp == 0)                 {                     Console.WriteLine(string.Format("TypeLib File for assembly [{0:S}] unregistered.", strTargetAssemblyFilePath));                 }                 else                 {                     Console.WriteLine(string.Format("Failed to unregister TypeLib File for assembly [{0:S}]. Error code : [{1:D}]",                         strTargetAssemblyFilePath, uiRetTemp));                     return false;                 }                 return true;             }             catch (Exception ex)             {                 Console.WriteLine(string.Format("An exception occurred. Exception description : [{0:S}].", ex.Message));                 return false;             }         }

Note the following :

  • The UnRegisterTypeLib() API is used to perform the type
    library un-registration.
  • Instead of specifying a path to the registered type
    library, the LIBID is used, plus the type library’s major and minor version
    numbers.
  • The original assembly’s GUID attribute is extracted and
    is used to create a Guid instance.
  • The original assembly’s version number is also extracted
    and is used to determine the type library’s major and minor version
    numbers.

8. Selecting the ITypeLibExporterNotifySink
Object.

8.1 RegisterAssembly has been developed to allow for
custom ITypeLibExporterNotifySink implementation objects to be used in the call
to TypeLibConverter.ConvertAssemblyToTypeLib().

8.2 This is controlled in the RegisterAssembly.exe.config file
:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>     <startup>         <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />     </startup>     <appSettings>       <add key="TypeLibExporterNotifySink" value="RegisterAssembly.ConversionEventHandler, RegisterAssembly" />     </appSettings>
</configuration>

8.3 The default implementation is the
ConversionEventHandler class which is supplied in ConversionEventHandler.cs.

8.4 Readers can write custom ITypeLibExporterNotifySink
implementations and specify its use in the config file.

8.5 In upcoming blogs, I will be expounding more on
ITypeLibExporterNotifySink and will provide a sample custom
implementation.

8.6 The code that reads the config file and controls the
program’s ITypeLibExporterNotifySink implementation can be found
in ConfigSettings.cs :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Configuration;
using System.Collections.Specialized; namespace RegisterAssembly
{     partial class Program     {         const string DEFAULT_TYPELIB_EXPORTER_NOTIFY_SINK = "RegisterAssembly.ConversionEventHandler, RegisterAssembly";         static bool ReadConfigSettings()         {             NameValueCollection appSettings = ConfigurationManager.AppSettings;             string strTypeLibExporterNotifySink = appSettings["TypeLibExporterNotifySink"];             if (string.IsNullOrEmpty(strTypeLibExporterNotifySink) == true)             {                 strTypeLibExporterNotifySink = DEFAULT_TYPELIB_EXPORTER_NOTIFY_SINK;             }             Type type = Type.GetType(strTypeLibExporterNotifySink);             if (type == null)             {                 return false;             }             m_pITypeLibExporterNotifySink = (ITypeLibExporterNotifySink)(Activator.CreateInstance(type, new object[] { m_bVerbose }));             if (m_pITypeLibExporterNotifySink == null)             {                 return false;             }             return true;         }     }
}

9. The Test Programs.

9.1 Two sets of test programs are supplied in the
companion CodePlex site.

9.2 One is a pair of 32-bit code and the other
64-bit.

9.3 Remember that in order to process 64-bit
code, RegisterAssembly.exe must
be compiled as a 64-bit program.

9.4 Set this in the project properties under the “Build”
section :

  • Set the Platform Target to “x64”.
  • Set the Output path to a path separate from the one for
    32-bits, e.g. “bin\Debug64\”

9.5 The 32-bit set includes the following projects
:

  • A 32-bit managed assembly (ManagedClassLib32) which
    contains a class (ManagedClass).
  • A 32-bit C++ client application (ConsoleClient32) which
    references ManagedClass as a COM object.

To use this demonstration set together
with RegisterAssembly.exe perform
the following :

  • Compile ManagedClassLib32 to produce
    ManagedClassLib32.dll.
  • Run RegisterAssembly.exe using
    the following command line :
RegisterAssembly.exe CODE_BASE TYPELIB "<path>\ManagedClassLib32.dll"
  • Compile ConsoleClient32 to produce
    ConsoleClient32.exe.
  • Run ConsoleClient32.exe to test for
    success.

Then perform an unregistration of ManagedClassLib32.dll
with the following command line :

RegisterAssembly.exe UNREGISTER TYPELIB "<path>\ManagedClassLib32.dll"
  • The unregistration process will not delete away the
    original type library (ManagedClassLib32.tlb) created earlier.
  • Hence it is possible to re-compile ConsoleClient32 even
    though the assembly and its associated type library has been
    un-registered.
  • Run ConsoleClient32.exe. This time. the program will not
    be able to create an instance of ManagedClass.

9.6 Analogous results will be observed when the 64-bit
test set is used.

10. In Summary.

10.1 RegisterAssembly.exe is
an on-going project, a work in progress.

10.2 I consider it a basic version and more features can
certainly be added to it.

10.3 I hope that the reader will comment on how to
improve it further.

[转]Programmatically Register Assemblies in C#.的更多相关文章

  1. 4: 模块化应用程序开发 Modular Application Development Using Prism Library 5.0 for WPF (英汉对照版)

    A modular application is an application that is divided into a set of loosely coupled functional uni ...

  2. .net core高性能通讯开源组件BeetleX

    BeetleX beetleX是基于dotnet core实现的轻量级高性能的TCP通讯组件,使用方便.性能高效和安全可靠是组件设计的出发点!开发人员可以在Beetlx组件的支持下快带地构建高性能的T ...

  3. ABP框架系列之二十:(Dependency-Injection-依赖注入)

    What is Dependency Injection If you already know Dependency Injection concept, Constructor and Prope ...

  4. BeetleX高性能通讯开源组件

    net core高性能通讯开源组件BeetleX https://www.cnblogs.com/smark/p/9617682.html BeetleX beetleX是基于dotnet core实 ...

  5. 微软企业库的&nbsp;注入和依赖&amp;nbs…

    Working with ObjectBuilder This topic has not yet been rated - Rate this topic Retired Content This ...

  6. Exploring Micro-frameworks: Spring Boot--转载

    原文地址:http://www.infoq.com/articles/microframeworks1-spring-boot Spring Boot is a brand new framework ...

  7. easyhook报错The given 64-Bit library does not exist

    在调用 RemoteHooking.Inject 时,报错 查看easyhook源代码,出错位置如下 if(!RtlFileExists(UserLibrary)) { #ifdef _M_X64 T ...

  8. springcloud starter(一)

    Spring Cloud - Getting Started Example, 转载自:https://www.logicbig.com/tutorials/spring-framework/spri ...

  9. .NET:CLR via C# Shared Assemblies and Strongly Named Assemblies

    Two Kinds of Assemblies, Two Kinds of Deployment A strongly named assembly consists of four attribut ...

随机推荐

  1. PhantomJS 一个隐形的浏览器

    下载地址: http://phantomjs.org/download.html 使用方法: 下载压缩包解压出来找到phantomjs.exe 放到python的根目录下

  2. UEditor使用有感(红色),保存内容时,会自动添加p标签

    UEditor 介绍 UEditor 是由百度「FEX前端研发团队」开发的所见即所得富文本web编辑器,具有轻量,可定制,注重用户体验等特点,开源基于MIT协议,允许自由使用和修改代码. 1 入门部署 ...

  3. composer 发布自己的开源软件

    首先创建一个github项目. 在项目中,创建一个composer.json文件. { "name": "jiqing9006/valid", "de ...

  4. python学习(十四) 网络编程

    14.1 少数几个网络设计模块 14.1.1 socket模块 套接字包括:服务器套接字和客户机套接字. 在创建一个服务器套架字后,让它等待连接,这样它就在摸个网络地址处(IP地址和一个端口号的组合) ...

  5. 搭建httpd服务

    实验环境:CentOS7 实验步骤: 安装httpd服务:yum -y install httpd 关闭SELinux:setenforce 0 禁用防火墙策略:iptables -F 启动httpd ...

  6. CVE-2017-11882复现配合koadic

    项目地址:https://github.com/iBearcat/CVE-2017-11882 首先开启koadic,然后配置一下 复制这句代码 mshta http://192.168.220.13 ...

  7. java成神之——集合框架之ArrayList,Lists,Sets

    集合 集合种类 ArrayList 声明 增删改查元素 遍历几种方式 空集合 子集合 不可变集合 LinkedList Lists 排序 类型转换 取交集 移动元素 删除交集元素 Sets 集合特点 ...

  8. SEO网站title应该怎么写

    第一:具有独特性 在你的网站中,也许有成千上万的页面,首页-分类-无数的文章页面,这些都有固定的标题,他们的标题最好不要相同.有的时候也许不是`站长们故意的,但是在使用编辑软件的时候,经常 会出现很多 ...

  9. 虚拟机之 LNMP

    LNMP就是Linux nginx mysql php 一.mysql 下载安装mysql转至 LAMP (点击“LAMP”即可跳转) 也可以从快照跳转至mysql安装ok 二.php 下载同上, 1 ...

  10. cacti启动有图无数据

    cactiEZ服务器重启后,获取不到图形的解决办法 cd /var/www/html/cli/ php -q rebuild_poller_cache.php -d myisamchk --safe- ...