RAD Studio
ContentsIndex
PreviousUpNext
Accessing User-defined .NET Components

When you examine a type library for a .NET component, you might - depending on how the component was designed and built - find only an empty class interface. The class interface will not contain any information about the parameters expected by the methods implemented by the class. Also notably absent, are the dispids for the methods of the class. The reason for this are the problems that can arise when a new version of the component is created. 

In COM, inheriting via interface is the only option. In the .NET Framework, inheriting via interface or inheriting via implementation is a design decision. .NET component writers can choose to add a new method or property at any time. If changes are made to the .NET component, any COM client that depends on the layout of the interface (e.g. by caching dispids) will break. 

A .NET component writer must choose to expose type information in an exported type library; it is not the default behavior. This is done through the use of the ClassInterfaceAttribute custom attribute. ClassInterfaceAttribute is found in the System.Runtime.InteropServices namespace. It can take on the values of the ClassInterfaceType enumeration, which are, AutoDispatch (the default), AutoDual, and None

The AutoDispatch value is what causes the empty class interface to be generated. Clients are restricted to late binding when accessing such a class. The AutoDual value causes all type information (including dispids) to be included for a class so marked. When a class is marked with the AutoDual value, type information is also included for all inherited classes. This is the most convenient approach, and it can work well when the .NET components are developed in a controlled environment. However, this approach is also the one most prone to the versioning problems mentioned earlier. 

The ClassInterfaceType value None inhibits the generation of a class interface. When a .NET class is marked this way, only the methods implemented in inherited interfaces can be invoked. For .NET components that are intended to be used by an unmanaged COM client, inheritance via interface is the preferred method of interoperating between managed and unmanaged code. This way, the COM client is less susceptible to changes in the .NET class. It also reinforces a tried-and-true COM design principle, the immutability of interfaces. 

The following example demonstrates this approach. We start out with a C# interface called MyInterface, and a class called MyClass.

   using System;
   using System.Reflection;
   using System.Runtime.InteropServices;
   using System.Windows.Forms;

   [assembly:AssemblyKeyFile("KeyFile.snk")]
   namespace InteropTest1 {
      public interface MyInterface {
         void myMethod1();
         void myMethod2(string msg);
      }

   // Restrict clients to using only implemented interfaces.
   [ClassInterface(ClassInterfaceType.None)]
   public class MyClass : MyInterface {

      // The class must have a parameterless constructor for COM interoperability
      public MyClass() {
      }

      // Implement MyInterface methods
      public void myMethod1() {
         MessageBox.Show("In C# Method!");
      }

      public void myMethod2(string msg) {
         MessageBox.Show(msg);
      }
   }
}

The assembly is marked with the AssemblyKeyFile attribute. This is required if the component is to be deployed in the Global Assembly Cache. If you deploy your component in the same directory as the unmanaged executable client, the strong key is not required. This example component will be deployed in the GAC, so we first generate the keyfile using the Strong Name Utility of the .NET Framework SDK:

sn -k KeyFile.snk

Execute this command from the same directory where the C# source file is located. 

The next step is to compile this code using the C# compiler. Assuming the C# code is in a file called interoptest1.cs:

csc /t:library interoptest1.cs

The result of this command is the creation of an assembly called interoptest1.dll. The assembly must now be registered, using the regasm utility. Regasm is similar in concept to tregsvr; it creates entries in the Windows registry that allow the component to be exposed to unmanaged COM clients.

regasm /tlb interoptest1.dll

The use of the /tlb option causes regasm to do two things: First, the registry entries for the assembly are created. Second, the types in the assembly will be exported to a type library, and the type library will also be registered. 

Finally, the component is deployed to the GAC using the gacutil command:

gacutil -i interoptest1.dll

The -i option indicates the assembly is being installed into the GAC. The gacutil command must be executed each time you build a new version of the .NET component. Later, if you wish to remove the component from the GAC, execute the gacutil command again, this time with the -u option:

gacutil -u interoptest1

Note: When uninstalling a component, do not include the '.dll' extension on the assembly name.
Once the .NET component has been built, registered, and installed into the GAC (or, copied to the directory of the unmanaged executable), accessing it in Delphi is the same as for any other COM object. Open or create your project, and then select ComponentImport type library from the menu. Scroll through the list of registered type libraries until you find the one for your component. You can create a package for the component and install it on the Tool Palette by selecting the Install check box. The type library importer will create a _TLB file to wrap the component, making it accessible to unmanaged Delphi code through vtable binding. 

The Add button of the type library import dialog box will not correctly register a type library exported for a .NET assembly. Instead, you must always use the regasm utility on the command line. 

The type library importer will automatically create _TLB files (and their corresponding .dcr and .dcu files) for any .NET assemblies that are referenced in the imported type library. Importing the type library for the example C# component above would cause the creation of _TLB, .dcr, and .dcu files for the mscorlib and System.Windows.Forms assemblies. 

The example below demonstrates calling methods on the .NET component, after its type library has been imported into Delphi. The class and method names come from the earlier C# example, and the variable MyClass1 is assumed to be previously declared (e.g. as a member variable of a class, or a local variable of a procedure or function).

MyClass1 := TMyClass.Create(self);
MyClass1.myMethod1;
MyClass1.myMethod2('Display this message');
MyClass1.Free;
Copyright(C) 2008 CodeGear(TM). All Rights Reserved.
What do you think about this topic? Send feedback!