Generating Runtime Callable Wrappers

team lib

An RCW is an assembly generated using Visual Studio .NET or the .NET Framework that allows a COM component to be used in .NET code. As I mentioned in the section Interop Assemblies, you can choose from three ways to generate an RCW: by using Visual Studio .NET, by using the TlbImp.exe tool, and by using the TypeLibConverter class from code. Each method uses the type library importer provided by the .NET Framework in a different way.

In the following sections, Ive listed the techniques for generating RCWs in order of increasing flexibility. Visual Studio .NET, the least flexible, provides a simple way to add a reference to a COM component to a project, but it does not support all the options that TlbImp.exe gives you. Using TypeLibConverter from code gives you the most flexibility, at the expense of increased complexity.


This section covers the generation and use of RCWs for non- UI COM components . The use of ActiveX controls in the Windows Forms designer is covered toward the end of the chapter in the section Using ActiveX Controls with .NET.

Using Visual Studio .NET

Visual Studio .NET 2003 will create an RCW for COM components for Visual Basic .NET, Visual C#, and managed C++ projects.


Visual Studio .NET 2002 wont create RCWs for managed C++ projects. If youre using the older version, youll have to use the TlbImp.exe tool, as described in the following section.

You can create an RCW using the following steps:

  1. Open Solution Explorer.

  2. Right-click on either the solution name or the References folder, and select Add Reference from the context menu.

  3. This will display the Add Reference dialog, through which you can add references to both .NET and COM components.

  4. Select the COM tab, highlight the component you want to use in the listbox, and then click Select. If the component isnt listed, use the Browse button to search for the type library. You can select more than one component at a time.

  5. When youve chosen the components you require, click OK.

The RCW will be generated, and an entry will be added to the project References folder. If you look at the properties for the new reference, you will see that the assembly name starts with Interop. and contains a namespace with the same name as the type library.

Details of what is placed in the interop assembly, and how COM types are represented, are covered in the section How COM Entities Are Converted later in the chapter.

Using the TlbImp.exe Tool

The command line Type Library Importer tool, TlbImp.exe, is provided as part of the .NET Framework and is not dependent on Visual Studio .NET. Because TlbImp.exe provides more options for generating interop assemblies than Visual Studio .NET, it can be used if you need more control over generating RCWs.


You need to have your path correctly set up to run .NET Framework tools from a console window. If you are running Visual Studio .NET, you can open a console with the path correctly set by going to the Visual Studio .NET or Visual Studio .NET 2003 entry on the Start menu and selecting the Visual Studio .NET Command Prompt item from the Visual Studio .NET Tools menu.

TlbImp.exe converts type libraries to interop assemblies, and it always converts a whole type library at a time, which means you cannot use TlbImp.exe to selectively convert types from within a library.

Simple Use of TlbImp.exe

The simplest way to use TlbImp is to provide the name of the type library file:


This action will produce an RCW with the same name as the type library and with a DLL suffix. If youre using type information that is merged into a COM DLL, TlbImp will detect that the output file would overwrite the input file and will stop with an error message.

Use the /out option to specify the name of the RCW:


Using TlbImp.exe Options

Table 3-1 summarizes the options that can be used with TlbImp.exe. Several options are concerned with signing the generated RCW. Ill discuss these options later in the chapter.

Table 3-1: Options Used with the TlbImp.exe Tool



/asmversion: number

Specifies the version number to be given to the assembly. A version number of will be used by default if this argument is not specified.


Signs the resulting assembly using delayed signing, in which the assembly isnt properly signed until late in the development process. See the Delayed Signing section later in the chapter for more details.

/help or /?

Displays help for the command.

/keycontainer: containername

Signs the assembly with the key pair found in the specified key container. See the Signing Assemblies and Installing Assemblies into the GAC sections later in the chapter for more details.

/ keyfile : filename

Signs the assembly with the key pair found in the specified key file. See the Signing Assemblies and Installing Assemblies into the GAC sections later in the chapter for more details.

/namespace: name

Specifies the namespace for the assembly.


Runs the command without any startup banner.

/out: file

Specifies the name for the output file. By default, the name is the type library name given in the IDL.


Produces a primary interop assembly, indicating the assembly was produced by the publisher of the COM component. Primary interop assemblies must by signed with a strong name. See the Generating and Installing Primary Interop Assemblies section later in the chapter for more details.

/ publickey : file

Signs the assembly with the specified public key. This option is used for testing and delayed signing. See the Delayed Signing section later in the chapter for more details.

/reference: file

Gives the name of a file to be used to resolve
references to types defined outside the current type library.


Suppresses the display of success messages.


Does not import a type library if the tool cannot resolve all the references within it.


Imports COM-style SafeArrays as .NET System.Array types.

/transform: name

Transforms metadata as specified by the name parameter. At present, the only name supported is dispret .


Produces interfaces without .NET security checks. This should be used only where it is known to be necessary, as it introduces security risks.


Displays additional information about the conversion process.

To generate an RCW in the same way that Visual Studio .NET does, use TlbImp with the following options:


Visual Studio .NET will produce an assembly with the same base name as the type library, containing a namespace with the same name as the library. The /sysarray option causes any references to SAFEARRAY s in COM interface methods to be marshaled as .NET System.Array types.


The interop assembly will define a namespace, which will be named according to the following rules:

  • If neither the /out nor /namespace option is specified, the namespace name will be constructed from the input filename, without any extension.

  • If the /out option is specified, the namespace will be constructed from the output filename, without any extension.

  • The /namespace option can be used to override the default namespace generation.

Note that PIAs can contain the custom IDL attribute that specifies the namespace to be used in the interop code. This attribute will prevent the namespace from being overridden from the command line. See the section Importing Libraries later in the chapter for more details.

Resolving References

COM type libraries can make references to other libraries using the importlib IDL statement. By default, TlbImp.exe will try to locate a PIA representing the component. If it cant, it will try to find the location of the type library from the Windows registry and generate an interop assembly for the library. This is a recursive process that is continued until all references have been satisfied.


This process, of course, requires that dependent type libraries have been registered. If the dependent type libraries dont have the correct registry entries, the importer will be unable to find them.

If you already know the location of interop assemblies for dependent type libraries, you can specify them using the /reference option. In this example, mytype.tlb contains a reference to a component represented by the interop assembly Interop.SomeComponent.dll :


You can specify as many /reference options as necessary on the command line.

The /strictref option can be used to prevent TlbImp.exe from looking in the registry for type library information. If this option is specified, the build will fail if the tool cannot reference components by finding a PIA in the GAC or from a /reference option on the command line.


The /transform:dispret Option

COM interfaces commonly use the [out, retval] attributes to denote an argument that can be used as a function return value. The /transform:dispret option is used to determine how such arguments are represented in the RCW. If /transform:dispret is not specified, [out, retval] arguments will be marshaled as [out] parameters in the RCW; if it is specified, such arguments will be converted to return values.


This option is necessary only for pure dispinterfaces . The conversion of [out, retval] arguments to return values is automatically carried out for dual and custom interfaces.

As an example, consider the following dispinterface definition:

 [ uuid(DF8107E9-D9F0-45B6-989B-9226407D8B3B) ] dispinterfaceIJAuto{ properties: methods: [id(1)]voidSquare([in]shortval,[out,retval]long*pRet); }; 

If /transform:dispret is not specified, the generated RCW contains the following function:

 //ILfunctionsignatureinRCW,without/transform:dispret voidSquare(int16val,outint32&pRet); 

If the option is specified, the function signature looks like this:

 //ILfunctionsignatureinRCW,with/transform:dispret int32Square(int16val); 

Using the TypeLibConverter Class

You can use the System.Runtime.InteropServices.TypeLibConverter class to create interop assemblies in code in the same way that TlbImp.exe does from the command line.

The TypeLibConverter class has three major methods, shown in Table 3-2.

Table 3-2: Members of the TypeLibConverter Class




Takes a .NET assembly and converts it to a COM type library


Takes a COM type library and converts it to a .NET interop assembly


Returns the name and code base of the primary interop assembly that represents a specified type library

This section will use the second method, ConvertTypeLibToAssembly , which is the one used by both Visual Studio .NET and TlbImp.exe to generate interop assemblies. The function converts an in-memory type library into an in- memory interop assembly.


ConvertTypeLibToAssembly has two overloads. I recommend you use the overload that takes a Version as the final parameter because the other one is primarily provided for backward compatibility with previous versions of .NET.

Listing 3-1 contains a complete C# sample program that shows how to convert a type library using ConvertTypeLibToAssembly . You can find this sample in the Chapter03\ConvertTlb folder in the books companion content.

Listing 3-1: ConvertTlb.cs
start example
 usingSystem; usingSystem.Reflection; usingSystem.Reflection.Emit; usingSystem.Runtime.InteropServices; usingSystem.IO; namespaceConvertTlb { classClass1 { //DefineRegKindenumforuseinPInvokedeclaration privateenumRegKind{ REGKIND_DEFAULT=0,REGKIND_REGISTER=1,REGKIND_NONE=2 } //PInvokedeclarationtoaccessfunctioninoleaut32 [DllImport("oleaut32.dll",CharSet=CharSet.Unicode, PreserveSig=false)] privatestaticexternvoidLoadTypeLibEx(StringstrTypeLibName, RegKindregKind, [MarshalAs(UnmanagedType.Interface)]outObjecttypeLib); [STAThread] staticvoidMain(string[]args) { //Inputandoutputfilenames stringinfile,outfile; //Processcommandlinearguments if(args.Length==1) { if(File.Exists(args[0])) { infile=args[0]; intextPos=infile.IndexOf('.'); if(extPos!=-1) { if(infile.ToLower().EndsWith(".dll")) { Console.WriteLine ("Error:outputfilewouldoverwriteinputfile"); return; } outfile=infile.Substring(0,extPos)+ ".dll"; } else outfile=infile+ ".dll"; } else { Console.WriteLine("File{0}notfound",args[0]); return; } } elseif(args.Length==2) { if(File.Exists(args[0])) { infile=args[0]; outfile=args[1]; Console.WriteLine("Infile:{0},outfile:{1}",infile,outfile); } else { Console.WriteLine("File{0}notfound",args[0]); return; } } else { Console.WriteLine("Usage:ConvertTlb<typelib><outfile>"); return; } //CallLoadTypeLibExtoloadthetypelibrary.RegKind_None //meansthatthetypelibrarywillnotberegisteredin //theWindowsregistry ObjecttypeLib; LoadTypeLibEx(infile,RegKind.REGKIND_NONE,outtypeLib); //Checkthecallworked if(typeLib==null) { Console.WriteLine("LoadTypeLibExfailed."); return; } //CreateaTypeLibConverterandaneventhandlerforevents //raisedduringtheconversion TypeLibConverterconverter=newTypeLibConverter(); ConversionEventHandlereventHandler= newConversionEventHandler(); //CallConvertTypeLibToAssemblyontheloadedlibrary AssemblyBuilderasm=converter.ConvertTypeLibToAssembly(typeLib,outfile,0,eventHandler, null,null,null,null); //Savetheinteropassembly asm.Save(outfile); } } //Defineaneventhandlerclassforusebytheconverter publicclassConversionEventHandler:ITypeLibImporterNotifySink { publicvoidReportEvent(ImporterEventKindeventKind, inteventCode,stringeventMsg) { Console.WriteLine("Eventmsg: " +eventMsg); } publicAssemblyResolveRef(objecttypeLib) { Console.WriteLine("ResolveRefcalled"); returnnull; } } } 
end example

The program first imports the extra namespaces used by the program:

  • System.Reflection (needed for the definition of the Assembly type)

  • System.Reflection.Emit (needed for the definition of AssemblyBuilder )

  • System.Runtime.InteropServices (needed for TypeLibConverter, DllImportAttribute , and ITypeLibImporterNotifySink )

  • System.IO (needed for the File class)

The ConvertTlb class itself starts by defining the Reg_Kind enum; Ill discuss the purpose of this enumeration shortly. This is followed by a prototype for the LoadTypeLibEx function, which is needed so that the Platform Invoke mechanism can be used to execute it.


Platform Invoke is a mechanism for using functions in Win32 DLLs from managed code. Using Platform Invoke is covered in Chapter 12, Interacting with Unmanaged Code.

The Main function in the class starts by building input and output filenames from the command line arguments, as follows :

  1. If there are two arguments, use them as the input and output filenames.

  2. If there is only one argument, use it as the input filename. For the output filename, use the input filename with any extension replaced with . dll .

Note that when a single argument is given, the code checks that the output file wont overwrite the input file. If the user gives two filenames explicitly, the program assumes they know what theyre doing!

Once the filenames have been constructed, the next task is to call the Win32 LoadTypeLibEx to load the type library into memory. This function takes three arguments: the path to the type library, a RegKind value, and an Object reference that LoadTypeLibEx hooks up to the opened type library. Note the use of the out modifier to show that the final argument is passed by reference.

  • RegKind is an enumeration used in the Win32 LoadTypeLibEx function, and it is defined here for ease of use. This enum is used to govern whether a type library is registered when it is loaded by LoadTypeLibEx . The three members are as follows:

  • REGKIND_DEFAULT , which causes the default LoadTypeLib rules to be followed

  • REGKIND_REGISTER , which causes the type library to be registered when it is loaded

  • REGKIND_NONE , which prevents LoadTypelibEx from registering the type library


    You should use LoadTypeLibEx rather than the older LoadTypeLib because LoadTypeLib always registers the libraries it loads if a path is not specified for the library.

If the call to LoadTypeLibEx works, the Object reference will act as a reference to the loaded library. Assuming all is OK, the next task is to create a TypeLibConverter object. When importing type libraries using ConvertTypeLibToAssembly , you have to provide a reference to an event handler object, an instance of a class that implements the ITypeLibImporterNotifySink interface. Youll find you need to do this even if you dont want to act on any notifications: passing a null reference isnt acceptable in this case. The sink interface specifies two methods: ReportEvent , which is called when events occur during conversion, and ResolveRef , which is called when a reference to another type library needs to be resolved.

In this simple program, ResolveRef isnt implemented and ReportEvent simply prints the message associated with any report it receives. In a real application, you would need to implement ResolveRef to returnor create and then returnan interop assembly for any type libraries referenced from the one being converted. You might want to recursively call ConvertTypeLibToAssembly on the value passed in, or use the GetPrimaryInteropAssembly method to see whether a PIA exists for the type.

Once the event handler has been created, ConvertTypeLibToAssembly can be called to convert the type library. Lets look at the arguments to the function: The first is the reference to the type library loaded into memory, while the second is the filename of the assembly that will be produced. Note that this file isnt written to disk by the call to ConvertTypeLibToAssembly; it must be written to disk as a separate step.

The third parameter indicates any flags that are to be specified for the conversion process. The possible values in the TypeLibImporterFlags enumeration are shown in Table 3-3.

Table 3-3: Members of the TypeLibImporterFlags Enumeration




Creates a PIA


Causes SAFEARRAY s to be imported as System.Array references


Imports [out,retval] arguments as function returns


Suppresses security checks on imported interfaces

In this case, a value of zero is passed because no flags are being specified.

The fourth argument is a reference to the ITypeLibImporterNotifySink event handler object, which cannot be null. The fifth and sixth arguments are used to specify key information if you want to sign the generated assembly. If these parameters are null, the assembly will not be signed. The seventh parameter is a String that will be used as the namespace for the generated assembly. If this is null, the namespace used will be derived from the name of the type library. The final parameter can be used to specify the version for the generated assembly. If this is null, the version of the type library is used.

The conversion process produces a reference to an AssemblyBuilder , a type that represents an assembly constructed in memory. The Save method on the AssemblyBuilder is then used to write the assembly to disk.

Assemblies and the GAC

To recap briefly , there are two kinds of assembly: private assemblies that are used by a single application, and shared assemblies that can be used by any application. Private assemblies are installed in the same directory as the application using them, and there is no requirement for them to be digitally signed. Shared assemblies must be installed in the GAC, and they must be digitally signed.

You might want to install assemblies in the GAC for several reasons:

  • Security: As part of the operating system directory structure, the GAC directories usually have restricted access.

  • Location: The GAC is the first place that .NET looks when resolving references to assemblies, so it is the most efficient place to locate them. It is also the one location for assemblies that can be shared by all applications on a machine.

  • Versioning: Different versions of the same assembly can be installed side-by-side in the GAC.

The GAC is located under the assembly\GAC subdirectory of your Windows installation directory (for example, C:\Windows\assembly\GAC). The GAC is structured into a number of subdirectories, whose names are generated by a special algorithm.

Note that Windows Explorer knows about the structure of the GAC, so youll simply see details of assemblies listed directly under the assembly subdirectory, as shown in Figure 3-1. Youll need to look at the assembly directory using some other toolsuch as the dir command in a console windowto see the finer structure.

click to expand
Figure 3-1: The Global Assembly Cache (GAC) viewed in Windows Explorer

Note the two versions of cscompmgd in Figure 3-1. You can right-click on an entry in the list and bring up the property pages from the context menu, as shown in Figure 3-2.

click to expand
Figure 3-2: Properties of an assembly in the GAC as viewed in Windows Explorer

The properties show that the two versions of this assembly have different version numbers even though they share the same name and public key.

You should never manually copy files into the GAC. Instead, you should use one of the following methods:

  • Run the Gacutil.exe tool from the command line. The use of this tool is covered in the Signing Assemblies and Installing Assemblies into the GAC sections later in the chapter.

  • Use an installer that knows about assemblies and the GAC, such as Microsoft Windows Installer (MSI) version 2.0.

  • Use Windows Explorer to drop assembly files into the assembly subdirectory.

The Gacutil.exe utility isnt included in the .NET redistributable, so in production scenarios, you are advised to use a suitable installer such as the Microsoft Windows Installer. The Gacutil and drag and drop methods are suitable for use in development and testing.

Strong Names

Assemblies in the GAC are required to have a strong name . Private assemblies, which are not required to have a strong name, are identified by three characteristics:

  • The simple text name of the assembly

  • The version number

  • Culture information, if any

This information is sufficient to enable different versions of assemblies to live side-by-side and to maintain culture-specific versions. A strong name adds a public key and a digital signature to these three items. A key can be used as a unique identifier for a company or developer, so it is possible to determine the originator of a signed assembly. The signature is generated from the assembly file using a private key supplied by the originator of the assembly. Strong names bring several advantages to assemblies:

  • They guarantee uniqueness for shared assemblies because the key they are signed with will be unique.

  • Nobody else can produce a new version of a strong-named assembly unless they have the private key used to sign it.

  • They provide an integrity check, with the signature guaranteeing the origin of the assembly and that the assembly has not been tampered with.

Private assemblies can also have strong names, but it is not a requirement. It is a requirement that shared assemblies installed in the GAC have strong names.


Giving private assemblies strong names will incur overhead because the assembly integrity will be validated by the .NET security mechanism every time it is loaded. Shared assemblies are only validated once, when they are installed into the GAC.

When an application references a strong-named assembly, the referenced assemblys public key is included in the manifest for the application. At run time, this key can be used to verify the integrity of the referenced assembly so that the client code can be sure its getting the right assembly and that the assembly hasnt been tampered with.

Signing Assemblies

To sign an assembly, first create a key pair using the Strong Name tool, sn.exe , which ships with the .NET Framework SDK. The following command will create a private and public key pair, which can be used to sign the assembly:


When youre using Visual Studio .NET, creating a signed assembly is simple. Every project, whether it is Visual Basic .NET, Visual C#, or managed C++, contains a source-code file called AssemblyInfo (with the appropriate extension). This file contains assembly- related attributes used to edit the metadata that the compiler builds into the assembly upon compilation. One of the entries lets you specify the path to a key file. Heres an example in C#:


One slightly confusing aspect is that different languages handle this in different ways. Visual C# projects include the AssemblyKeyFileAttribute in AssemblyInfo.cs as standard. You should specify a path relative to the output directory. In the preceding example, the file would be located in the project directory two levels above the output directory.

Visual Basic .NET does not include the attribute, but you can add it to AssemblyInfo.vb like this:


Once again, the path needs to be relative to the output directory, which by default will be obj/Debug .

Managed C++ does include the attribute in AssemblyInfo.cpp, so you only need to edit the path.


Note, though, that for C++ projects the key file path is relative to the project directory, not the output directory.

If youre building an assembly from the command line using the Assembly Linker tool, AL.exe, you can specify the /keyfile option. The tool expects a keyfile pathname relative to the directory containing the input files. The following example command line will build an assembly named NewAssembly.dll from the module MyModule.mod and sign it with the keypair stored in mykeys.snk:


Installing Assemblies into the GAC

The simplest way to install an assembly is to run the Gacutil.exe utility with the /i flag:


There are a few important things to note about using Gacutil:

  • Gacutil.exe can be run only by members of the Administrators group .

  • If you try to install an assembly that doesnt have a strong name, Gacutil will give the following error message: Failure adding assembly to cache: Attempt to install an assembly without a strong name.

  • The installation fails if the assembly is already in the cache. If you want to overwrite the existing assembly, use the /if flag instead.

You can uninstall an assembly from the GAC using the /u flag:


Installation of an assembly using the /i flag is simple, but there is nothing to prevent someone from removing the assembly when other applications are still depending on it. The /ir flag can be used to install an assembly and add a reference to its count. This flag needs several parameters to be specified, as shown in the following command line:

 gacutil/irmyassembly.dllFILEPATHc:\installer.exe "Myassembly" 

This command installs myassembly.dll using the installation application installer.exe. The final string is a description that can be displayed when the contents of the GAC are displayed using the /l or /lr flag. See the help for Gacutil for more details of the /ir option.

An assembly cannot be removed from the GAC using the /u option if it has outstanding references. The /uf option can be used to force the removal of an assembly, even if it has outstanding references, unless the assembly was installed using Windows Installer.

Delayed Signing

The keys used to sign real-world production assemblies should be guarded carefully because the private key identifies the assemblies produced by a particular vendor. If someone gains a copy of the private key, he or she can create counterfeit components that will apparently belong to the vendor. For this reason, developers will not usually have access to private keys during the development process. Once development has finished and the assembly is ready to be released, a release build will be made that uses the private key.

Delayed signing (also known as partial signing ) lets a developer test-sign assemblies, install them in the GAC, and use them, without needing access to the private key. The assembly is signed with the public key only, which lets the assembly use the GAC but doesnt generate a digital signature. Partially signed assemblies cannot be checked for tampering, but that should not be a problem when developing.

To extract the public key from a keypair generated by the SN.exe tool, run it again, specifying the -p option:


To use delayed signing in Visual Studio .NET projects, use the public key filename in the AssemblyKeyFile attribute and specify true for the value of the AssemblyDelaySign attribute. Heres an example from a managed C++ project:

 [assembly:AssemblyDelaySignAttribute(true)]; [assembly:AssemblyKeyFileAttribute("mypublickey.snk")]; 

If youre using the Assembly Linker, specify the public key file for the /keyfile option and also specify the /delaysign option.

Generating and Installing Primary Interop Assemblies

From the point of view of .NET, a PIA is simply an assembly marked with the PrimaryInteropAssembly attribute. You can generate one by specifying the /primary option with TlbImp.exe.

You need to ensure when choosing the interop assembly name and the namespace name that they dont conflict with names of existing PIAs. Remember that you can use a custom IDL attribute to specify the namespace for the interop assembly, as described in the section Importing Libraries later in the chapter.

Note also that PIAs can only reference other interop assemblies that are also PIAs. If your type library references other components that do not have PIAs, you will have to work around the problem. If the other components are under your control, you can create PIAs for them. If they come from third parties, you might be able to obtain a PIA from the third party. If you cant, you might need to restructure your type library to avoid references to the component. Remember that .NET clients can still QueryInterface your component to get interface pointers even if those interfaces are not mentioned in the type library.

PIAs dont have to live in the GAC, so Visual Studio .NET and the .NET Framework use registry entries to locate them. Use the Assembly Registration tool, RegAsm.exe, to install the registry entries needed by PIAs. At its simplest, the command would look like this:


RegAsm adds the following entry to the registry:


Here libid is the GUID of the type library, and major.minor represents the version information. This key will have the full name of the assembly as its value, for example

team lib

COM Programming with Microsoft .NET
COM Programming with Microsoft .NET
ISBN: 0735618755
EAN: 2147483647
Year: 2006
Pages: 140

Similar book on Amazon © 2008-2017.
If you may any questions please contact us: