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.
Introduction
Using a .NET assembly as a COM object typically requires the
following steps:
- Giving the assembly a Strong Name.
- Placing the assembly in the Global Assembly Cache
(GAC).
-
Registering the assembly as a COM object.
-
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:\WINDOWS\Microsoft.NET\Framework\v1.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:
- Obtain a key pair.
- 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:
sn -k MyKey.snk
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:
sn -i MyKey.snk MyContainer
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:
sn -d MyContainer
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.
- Using the Assembly Linker (AL.exe) tool
- Using the AssemblyKeyFile attribute
- Using the AssemblyKeyName attribute
- 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:
al /out:MyAssembly.dll MyModule.netmodule /keyfile:MyKey.snk
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:
al /out:MyAssembly.dll MyModule.netmodule /keyname:MyContainer
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 key strokes 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
[assembly:AssemblyKeyFile( "MyKey.snk" )] // C#
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.
[assembly:AssemblyKeyFile( 'MyContainer')] // Delphi
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:
[assembly: AssemblyDelaySign( true )]
For more information please see the following:
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:
- Drag and drop the assembly into the GAC
- Use an installer that supports placing assemblies in the
GAC (preferred for deployment)
- 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:
gacutil -i MyAssembly.dll
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:
gacutil -u MyAssembly
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:
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:
regasm MyAssembly.dll
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:
regasm -u MyAssembly.dll
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::
@echo off
echo Installing %1. . .
gacutil /nologo /i %1.dll
regasm /nologo %1.dll
echo.
echo Press any key to uninstall. . .
pause >nul
echo.
regasm /nologo /u /nologo %1.dll
gacutil /nologo /u %1
echo.
echo %1 uninstalled.
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 then the Assembly. So if your namespace is
MyNamespace and your class name is MyClass then your ProgID
would be:
MyNamespace.MyClass
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:
[DispId(1)]
For more information see:
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:
[ComVisible(false)]
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.
About the Author
Jim McKeeth is
president of the Boise
Software Developers Group and a Senior Software Engineer
for Washington Group
International. He co-contributed a chapter on consuming
.NET XML web services on Linux using Kylix for the book
"Building Web Applications with ADO.NET and XML Web
Services". He is named inventor on seven patents, trains
and mentors other developers, enjoys writing when he gets the
chance and presented at BorCon2003. When not exploring
software development he enjoys spending time with his family.
For other papers, components and programs by Jim McKeeth please visit
DaVinciUnltd.com. Thanks!
Copyright ) 2004 by
Jim McKeeth - Some Rights Reserved. This work is available
for license under an Attribution-NonCommercial-ShareAlike
license from Creative
Commons Licenses.
Connect with Us