Site icon Embarcadero RAD Studio, Delphi, & C++Builder Blogs

Using a .NET Assembly via COM in Delphi

This is mostly a reprise of an article I wrote back in 2004, but that I still refer people to. That linked version may eventually go away. I have not tested any of this recently, but will update based on feedback, and revisit as necessary. I found a recent utility that may be useful in this process: NirSoft DLL Export Viewer

Abstract: Using .NET Assemblies as COM objects in Win32 programs is simple, once you are familiar with the necessary hoops. This article outlines the steps necessary and provides a few tips to improve your experience.

This paper operates on the premise that you are familiar with .NET development and know-how to call a COM object in traditional Win32 development. This paper fills in the gap by explaining how you can treat a .NET assembly as a COM object.  Most of the links provided point to articles in the MSDN unless otherwise specified. All the documentation links were broken (MSDN was deprecated) so I’ve done my best to update them.

Introduction

Using a .NET assembly as a COM object typically requires the following steps:

  1. Giving the assembly a Strong Name.
  2. Placing the assembly in the Global Assembly Cache (GAC).
  3. Registering the assembly as a COM object.
  4. Calling your COM object.

An assembly is not required to be in the GAC if it is in the system or application path, and the strong name is only required if it is in the GAC, but this is the preferred method of using an assembly as a COM object. In the following sections, we will take a closer look at each step as well as some other related steps.

All the command-line utilities I will mention in this article are part of the .NET Framework SDK, but they may not be in the path when you are at a command prompt. You will either need to explicitly specify the path, copy them to your path, or add the path to your path environment variable (Control Panel / System / Advanced / Environment Variables for 2000 and XP). The usual location for the .NET version 1.1 is C:WINDOWSMicrosoft.NETFrameworkv1.1.4322 (or WinNT instead of Windows)

Step 1: Strong Names

Strong names provide assemblies with a digital signature and public key to identify it by. This is in addition to the simple name and version number. Because strong names rely on unique, cryptographically secure strong key pairs. No one else can generate the same key pair you generate, and no one else knows your private key, so no one can generate the same strong name. “Assemblies with the same strong name are expected to be identical.” Because strong names guarantee uniqueness and equality they allow for side-by-side operation and help to eliminate DLL Hell.

Well, that is all fine and dandy, but I am sure you would like to know how one gives their assembly a strong name. The steps to give an assembly a strong name include:

  1. Obtain a key pair.
  2. Assign the key to the assembly.

Lets look at these steps.

Obtain a Key Pair

Your organization may already have a key pair. If this is the case, and the private key is not available to you as a developer for security reasons then you will need to perform a Delayed Signing. If you do have a key pair then you can skip this step.

To obtain a key pair use the Strong Name Tool (Sn.exe) that is provided with the .NET Framework.

To generate a new key pair type the following:

[crayon-673ffd6a00eec110356030/]

Where MyKey is the name of the key you are creating. Key files typically have an snk extension, but this is not required. If you don’t want to deal with a key file, you can place the key in the strong name Cryptographic Service Provider (CSP) with the following command:

[crayon-673ffd6a00efd958318968/]

Now the key from the file MyKey.snk has been placed in the MyContainer container. When you want to remove a container use the following:

[crayon-673ffd6a00eff199203984/]

We will talk more about containers in the section on assigning a key to an assembly (next).

Form more information see the tutorial on strong names.

Assign the Key to the Assembly

Once you have a key that you wish to assign to your assembly you need to decide how to assign it to your assembly. As mentioned earlier, if you don’t have access to the private key you will need to resort to Delayed Signing.

There are essentially 4 ways to sign an assembly.

  1. Using the Assembly Linker (AL.exe) tool
  2. Using the AssemblyKeyFile attribute
  3. Using the AssemblyKeyName attribute
  4. Using the AssemblyDelaySign attribute

Using the Assembly Linker (AL.exe) tool

The method suggested in most of the documentation on strong names is to use the Assembly Linker (AL.exe) tool. AL has many uses beyond strong names. It allows the generation of a file with an assembly manifest from one or more module or resource files. For now, we will just look at using it to sign an assembly.

To sign an assembly, use the following command:

[crayon-673ffd6a00f01359484209/]

This signs the assembly MyAssembly.dll with the code module MyModule.netmodule using the key found in MyKey.snk. If instead, you are using a key stored in a container you would type:

[crayon-673ffd6a00f08271139868/]

Which uses the key found in MyContainer. For those of you who hate typing, you can shorten keyname and keyfile to keyn and keyf respectively, saving 3 keystrokes each.

One thing to note about using the AL tool is that rebuilding the assembly will require a re-signing of the assembly.

Using the AssemblyKeyFile attribute

If you are actually able to edit the source code of the assembly then the AssemblyKeyFile attribute is probably a preferred method over using AL since the assembly will be resigned every time you rebuild it.

The syntax for AssemblyKeyFile is simply

[crayon-673ffd6a00f09540103017/]

You need to be aware of how the relative path is calculated since that varies based on Language. Both Borland Developer Studio (Delphi or C#) and Microsoft Visual C# base the path on the binary location, while Microsoft VB.NET bases the path on the source code location. Also in C# you will either need to proceed the string with the @ sign or use double backslashes if specifying a relative path.

One word of caution: When using AssemblyKeyFileAttribute the path and file name persist into the distributed assembly, so you will need to be sure that it does not contain any sensitive information.

Using the AssemblyKeyName attribute

If instead of a key file, you stored your key within a container in the CSP then you want to use the AssemblyKeyName attribute. With this attribute, instead of specifying the name of the key file you use the name of the container.

[crayon-673ffd6a00f0b126097055/]

This has the advantage of not needing to worry about relative paths. The disadvantage is that it requires the key be in the container, so could make it harder to share code between machines.

Notice: You should specify a value for AssemblyKeyFile OR AssemblyKeyName. If you specify a value for both then you will receive an error at compilation.

Using the AssemblyDelaySign attribute

As mentioned earlier, if you do not have access to the private key then you need to use the AssemblyDelaySign attribute. This attribute reserves space in your assembly for later signing. The syntax is simply:

[crayon-673ffd6a00f0d195390666/]

For more information please see the following: Understanding and Using Assemblies and Namespaces in .NET

Step 2: Global Assembly Cache (GAC)

The Global Assembly Cache (GAC) provides a machine-wide code depository. This is present on all machines with the Common Language Runtime (CLR) installed. Whenever there is an assembly that is intended to be shared by several applications on the computer then they should be placed in the GAC. As mentioned earlier, the less desirable alternative is to simply place the assembly in the system or application path. There are three ways to place an assembly in the GAC:

  1. Drag and drop the assembly into the GAC
  2. Use an installer that supports placing assemblies in the GAC (preferred for deployment)
  3. Use Global Assembly Cache tool (Gacutil.exe)

The first two options are for systems without the SDK since Gacutil is only available with the SDK. When installing on a system without the SDK you will either need to manually drag it in to the Assembly folder (e.g. C:WinntAssembly) or use an installer that supports the GAC. For now we are going to look at using Gacutil.exe.

Global Assembly Cache tool (Gacutil.exe)

The Global Assembly Cache tool (Gacutil.exe) allows viewing and manipulation of the assemblies in the GAC. For now, we are going to look at installing and removing assemblies from the cache. Before an assembly can be installed into the GAC it must have a strong name as covered in step 1.

The command to install an assembly in the GAC is:

[crayon-673ffd6a00f11235833281/]

This installs the assembly MyAssembly.dll into the GAC. If you change the assembly you will need to reinstall the new version. Before reinstalling you may need to remove the old one. To remove an assembly, use the following command:

[crayon-673ffd6a00f12644036743/]

Notice that instead of using the file name MyAssembly.dll you use the assembly name MyAssembly. Using the file name results in an error.

For more information see the following: Working with Assemblies and the Global Assembly Cache

Step 3: Registering

Prior to .NET we registered out COM objects with RegSvr32. In order to register a .NET assembly, we need to use the new Assembly Registration Tool (Regasm.exe). The usage of RegAsm is just as simple as RegSvr32 was. To register use the following command:

[crayon-673ffd6a00f14303045831/]

If you make a change to the interface you will need to re-register your assembly. If the change is significant enough you will want to unregister the old one first. Generally, I have a batch file to unregister with each change. The command to unregister is:

[crayon-673ffd6a00f18474772586/]

Automating

As mentioned earlier, I use a batch file to automate the installation into the GAC and registering. Here is a sample batch file that assumes the assembly already has a strong name:

[crayon-673ffd6a00f19635009622/]

Then I create a batch file that calls this batch file with the name of the assembly I am working with. Notice that this batch file wants the assembly name, not the file name, and assumes that the file name is the assembly name with a .dll extension. If this is not the case for your project then you will need to adapt this batch file as necessary.

Step 4: Calling your COM object.

Now that you have gone to all the work of registering your assembly as a COM object you need to know how to call it. You call it just like you call any other COM object, but you might be curious what it’s Dispatch Identifier (DispID) and the Programmatic Identifier (ProgID). You also might be curious how to prevent some classes and members from not being visible via COM.

ProgID

The ProgID is simply the namespace dot class name. Notice that the Namespace may be named different than the Assembly. So if your namespace is MyNamespace and your class name is MyClass then your ProgID would be:

[crayon-673ffd6a00f1b902113891/]

DispID

A Dispatch ID is a unique integer assigned to a method of a COM object. Depending on how you are making the calls you might alternatively use the method name. You can specify the Dispatch ID in your assembly’s source code with the DispId Attribute. If you do not specify one then the Common Language Runtime (CLR) automatically assigns a DispID. For example to set the Dispatch ID to 1 for a method you would use the following:

[crayon-673ffd6a00f1c330064401/]

For more information see: Exposing .NET Framework Components to COM

Visibility

By default, all public members are visible to COM when an assembly is registered. If you wish to hide a public member then you can use the ComVisible Attribute. The attribute takes a Boolean as a parameter, and it defaults to true. This seems kind of odd since a member defaults to visible without the attribute. So if you want to hide a member you would use the following:

[crayon-673ffd6a00f20071798588/]

Additional Reading

Another good source of information on this topic, from a slightly different point of view, is Brian Long’s .NET Interoperability: COM Interop paper from BorCon 2004. You can also pick up Adam Nathan’s book .NET and COM: The Complete Interoperability Guide which is also available in PDF format ebook.

Know more about .NET Core framework and how it compares to its alternatives.

Let me know if this is still usable or too outdated . . .

Exit mobile version