HomeDigital EditionSearch Dotnet Cd
ASP.NET C# Certification Exams The CLI Data Access Editorials Extending .NET Fundamentals Interoperability Interviews Migrate Mobile .NET Mono .NET Interface Object-Oriented Programming Open Source Optimization Product/Book Reviews Security Source Code UML Visual Studio .NET

COM has been the standard for application interoperability on Windows platforms for some time now. You probably have a great deal of code written and packaged as COM components that you don't want to recreate. But you want to start taking advantage of .NET's capabilities. So you have to rewrite all your functionality in .NET, right? Luckily, you don't. Microsoft had the foresight to build in some very powerful and easy-to-use COM interoperability features in the Common Language Runtime (CLR) and the .NET Framework Class Library.

In this article, the first of a two-part series, I will show you how to harness those features to use your existing COM components in a .NET application. I'll discuss some migration scenarios and how you might use .NET's capabilities for different types of applications and migration scenarios.

This article assumes you are familiar with COM type libraries and method calling, at least at the conceptual level, and know the basics of C# programming.

.NET Framework COM Support
Most of the heavy lifting in COM interop gets done behind the scenes by the CLR itself. The CLR takes care of bridging the two worlds and acts as a universal translator between calls made in the COM world and calls made in the .NET world.

When you use a COM component in a .NET application, you need a .NET assembly that contains metadata describing the types in your COM server. You get this assembly from the COM type library by using the tlbimp.exe tool in the .NET Framework SDK or if you use late binding, by having the runtime generate one on the fly. When you instantiate a COM component from .NET, the CLR uses the assembly metadata generated from the type library to create a Runtime Callable Wrapper (RCW) that handles marshaling the calls from the .NET code to the unmanaged COM code (see Figure 1). That RCW appears to your .NET code to just be another .NET type, but the incoming calls on the COM server side appear to the server as an unmanaged client.

Figure 1

In a similar fashion, when you expose a .NET class as a COM component, you need to create a type library that COM can use to marshal the calls from an unmanaged client into the .NET code using COM calling conventions. Again, you get this type library using a .NET Framework SDK tool, this time called tlbexp.exe. When the COM client application goes to instantiate the .NET class, the CLR steps in and creates a COM Callable Wrapper (CCW) to handle the message passing between the unmanaged and managed worlds (see Figure 1). In addition to a type library, you will also have to register information about the component in the registry so that the COM runtime can figure out where to find the server and how to instantiate it. Another .NET SDK tool, regasm.exe, lets you easily put the needed information in the registry. If you look at what it puts there, it actually refers to the CLR mscorlib.dll server as the InprocServer32 entry, and uses a different entry to tell the runtime which .NET class to instantiate and use to provide the desired services.

Using COM Components in .NET
Let's cover the most common scenario first: consuming existing COM components in a .NET application. There are many situations in which you might want to consume a COM component in a .NET application. It is likely that you have a lot of business logic or other nonvisual components that do various types of processing for your applications, whether you built them yourself or bought them as third-party components. You would not want to have to recode all of those from scratch or acquire new components just to move into the .NET world. You may also have ActiveX controls that provide certain UI capabilities that you don't want to have to recreate in .NET. Again, you can simply host these ActiveX controls as client controls in a Windows Form application, and you are on the road to migration.

You can easily use COM components in your applications using a few tools in the .NET Framework SDK, but you can do it even easier using VS.NET. I'll cover the command-line approach first so you can understand what VS.NET is doing for you under the covers. Then I'll tell you the simple steps to do the same thing in VS.NET.

I will use a simple ATL COM component to demonstrate the concept so that you can see how artifacts in the COM world get translated into artifacts in the .NET world. If you are not a C++ or ATL developer, don't worry. It is not required that you understand the internals of the COM component; that is the whole point of the binary reuse that COM provides. You can get all the concepts you need from knowing what interfaces and types are described in the type library for the component, which I'll cover shortly. But if you are comfortable with ATL, you can look at the specific methods and parameters and see how they manifest themselves in the .NET world as compared to how they are implemented in the C++ COM world.

The COM coclass that we will consume is called (what else?) MyComponent, and resides in an ATL COM DLL server named MyComSvr. As a result, its ProgID is MyComSvr.MyComponent. It implements two interfaces, IMyComponent and IAnother. This architecture is shown in Figure 2, and the interface methods are described briefly in Table 1.

Figure 2

Table 1

1.   The first step to making this component and its functionality accessible in .NET is to create a .NET assembly that acts as a wrapper for the type library information of the component. To do this from the command line, use the tlbimp.exe utility. The download code includes the source and the compiled DLL so if Visual C++ isn't accessible to you, all is not lost. ATL creates the type library info both in a .tlb file and as a resource embedded in the DLL itself. We will work against the DLL since that is all you would have if you were dealing with a VB COM component.

2.   To generate a .NET assembly for this type library, use the tlbimp.exe tool as shown below. This tool is in the VS.NET \FrameworkSDK\bin folder, along with many of the other .NET tools. If this folder is not already part of your system path, it is a good idea to add it so you can get to these tools from a command prompt.

tlbimp MyComSvr.dll

This will generate a .NET assembly named MYCOMSVRLib.dll. You can use the /out switch to make sure you don't have a name conflict, but for an ATL component, there is usually a Lib tacked on to the library name in the type library (i.e., MYCOMSVRLib), so you won't have a name-conflict problem here.

3.   You now have a .NET assembly that can be added to a .NET application as a Reference. To compile a C# client application contained in MyCOMClient.cs that uses MyComponent in its code, you would use the following command:

csc /r:MYCOMSVRLib.dll MyComClient.cs

This references the assembly containing the .NET definitions for the COM type library, and compiles the C# application into a .NET assembly. If the csc.exe file is not already in your path, you can find it in \Windows\Microsoft.NET\Framework\v1.0.3705 for the .NET release (WINNT at the root for Windows 2000). This is another good folder to have in your path.

4.   You should now be able to run MyComClient.exe, which will seamlessly use the COM component. The code for the MyComSvr VC++ project and the MyComSvrCSClient VS.NET project is available below. The MyComClient.cs code file is part of the latter project, and can be compiled from the command line as described above, or compiled from the VS.NET IDE using the project. In that project, you will see a reference to the MYCOMSVRLib.dll assembly that accomplishes the same as the reference switch above.

If you want VS.NET to create the needed assembly without using the tlbimp.exe tool, all you need to do is add a reference to the desired COM server in your VS.NET project. Figure 3 shows how to add a com component to a .NET project.

Figure 3

VS.NET will run tlbimp.exe in the background, create the necessary assembly, and add the reference to your project. It will also create references and assemblies for any other COM servers that the one you added depends on.

The next question you probably have is what is required in the .NET code to actually use the COM component. COM types are exposed in .NET as if they were .NET classes and interfaces. So for the MyComponent coclass, you get a type of MyComponentClass. The members of all the interfaces for that component are added to that class - so they can be called directly without having to cast to an interface type - along with all the event methods it sources. If you want to use a more interface-based programming style, you can cast the component class to the type of any of the interfaces that it implements, and then call the interface methods from those reference types. There are some basic deconfliction rules defined for the import process to handle naming conflicts between methods on different interfaces, and there are a number of attributes you can use in combination with declared .NET classes to explicitly control the mapping between the COM coclass and interfaces and the corresponding .NET classes. Browse through the System.Runtime.InteropServices namespace to see those options. Look at the "Interoperating with Unmanaged Code" topics in the MSDN library for more information.

To use the COM components:

1.   Bring in the namespace for the COM type library wrapper assembly:

using MYCOMSVRLib;

2.   In the MyComClient class, create an instance of the MyComponentClass before using it like any other C# class:

m_comp = new MyComponentClass();

3.   Since the MyComponent coclass also sources an event named SomethingSimple(), wire the class up to sync that event with a call to a member function with the same signature, named OnSomethingSimple, when the event is fired.

m_comp.SomethingSimple += new_IMyComponentEvents_Something
Simple EventHandler(
this.OnSomethingSimple );

You can figure out what the resulting name of the delegate type for the event interface and method is by looking in the object browser in VS.NET after you add a reference to the wrapper assembly. You can also use the ildasm.exe tool to see what the resulting types are at the MSIL level. These match the naming at the C# level.

Finally, wherever you need to call a method on the COM object, you can just call it as a member method of the wrapper class:

m_comp.DoSomethingSimple("Hello?");

One thing that can easily bite you if you are using COM objects that hold onto resources from a .NET application while they are alive is the nondeterministic finalization in .NET that is a result of the way garbage collection (GC) works. .NET objects are not necessarily deleted as soon as they go out of scope or you set their reference to null; they will be cleaned up at the garbage collector's leisure, whenever the next GC runs. COM objects are usually designed with the assumption that once their reference count goes to zero, they can release any resource they are holding, such as database connections or file handles. However, when you use a COM object from .NET, the actual client of the COM object is the RCW object created for that object. It holds onto a reference until it shuts down. And left to its own devices, it will not shut down until a GC decides to shut it down, which is mostly out of your control. You could force a GC, but that is an expensive operation, so I don't recommend it.

Luckily, the designers of COM interop in .NET foresaw this situation, and provided the Marshal.ReleaseComObject() in the System.Runtime.InteropServices namespace. All you need to do to force a release of a COM object when you are done with it is call this method with a reference to the object, and the RCW will release its reference and the COM object can clean itself up and go away. See the download code in the MyComClient.cs file for an example of this.

Embedding ActiveX Controls in .NET Applications
The VS.NET form designers make it very easy to embed an ActiveX control (now more generally referred to as a COM control) in a .NET form. There are a couple of ways to approach it. You could use the command-line tools to generate the wrapper assemblies as described earlier. You could also use VS.NET to simply add a reference to the COM server as described earlier. For COM controls, the designer provides a higher level of sophistication and assistance. When a COM control gets wrapped for use in .NET, it gets wrapped in a .NET AxHost control that you can drag and drop onto the Windows form designer. The easiest way to do this is to add the COM control to the VS.NET Toolbox, and drag and drop it from there. When you drop the control on a form in your project, VS.NET will step in and generate the necessary wrapper assemblies and add them as references to your project.

To do this

1.   Right-click in the Toolbox, preferably in the Components section, and select Customize Toolbox. This brings up a dialog similar to the Add Reference dialog shown earlier. It contains tabs for COM Components and .NET Framework components.
2.   Find the COM control you want in the list, check the box next to it, and click OK. If the control is not in the list, you still can add it to the Toolbox by browsing to it.

When you drag and drop the control onto a form, two wrapper assemblies are actually created. One is basically the COM server wrapper as was generated for nonvisible COM components. The other is prefixed by "Ax", and contains additional wrapper info for the runtime to help it with the hosting of the control in forms. For example, in the download code, there is an example program that hosts the Windows Media Player control in a Windows Form. The project is named MyMediaPlayer. If you open the Object Browser in that project, you will see two assemblies: AxMediaPlayer and MediaPlayer. If you look at the methods available in these two assemblies, you will see that the AxMediaPlayer one contains classes and methods related to hosting the control (i.e., mouse events, keyboard events, and any other event interfaces that the control exposes). However, the AxHost class that it derives from makes all the methods of the underlying COM object available as methods on the AxMediaPlayer object, so you will generally just program against it as shown in the download MyMediaPlayer project.

Once you have dropped the control into the form, you can write code to interact with it based on other controls in your form. You can also easily wire up event handling for the control using the IDE Properties dialog.

1.   With the control highlighted in the form, press the Events button in the Properties dialog (the lightning-bolt icon). This will show a list of categorized events that the control supports.
2.   To wire up an event handler for one of the events, just type in a method in the box next to the event. If the method already exists, the form designer will add the code to subscribe to the event using that method.
3.   If the method does not already exist, the designer will add the subscription code and a method skeleton to handle the event.

For the Windows Media Player example, I did this for the EndOfStream event, entering a method name of OnEndofStream. VS.NET added the following method body to my form class.

private void OnEndofStream(object sender,
AxMediaPlayer._MediaPlayerEvents_EndOfStreamEvent e)
{
}

It also added the following subscription code to the Windows Form Designer Generated Code section:

this.axMediaPlayer2.EndOfStream += new
AxMediaPlayer._MediaPlayerEvents_EndOfStreamEventHandler( this.OnEndofStream);

If you are not sure how to manually wire up an event from a COM component, this is a good way to learn the syntax.

Commanding the COM control from your code is no different from commanding any other COM component that is part of your project. The only real difference is that if you follow the process above, your form class will already have a running instance of the control created in the Form Designer code section, so you will not have to construct the control. You can simply use the existing member instance in your class, and call the methods you need on it.

Conclusion
So I've taken you through a whirlwind tour of migrating COM components to .NET. Using this capability, the doors open up considerably for a progressive migration of your existing applications to .NET, so you don't have to trash everything you have already done and start over. If you are planning on migrating an application with complex COM components that pass a variety of data and types as method parameters, it behooves you to dig into the COM interoperability sections of the MSDN documentation and see what all you will want or need to do to get as much reuse out of your existing code as possible. In the second article of this series I will discuss consuming .NET classes from COM.

Author Bio
Brian Noyes is an independent software consultant with Software Insight (www.softinsight.com). He is an MCSD with over 11 years of programming, software development, and project management experience. He is a contributing editor to numerous programming publications, author of .NET and COM: Working Together, and contributing author to Sams Teach Yourself DirectX 7 in 24 Hours. brian@softinsight.com

Source Code: zip file format ~169 KB

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.

  E-mail: info@sys-con.com