COM to .NETExposing a .NET managed object as a COM object for consumption by COM clients follows a similar process as exposing a COM object for consumption by managed clients. This next section will show you the ins and outs of getting started with .NET and COM Interop by exposing a .NET managed component as a COM component. The COM Callable WrapperJust as .NET components need a runtime callable wrapper in order to communicate across process boundaries with COM objects, COM objects need a COM callable wrapper (CCW) in order to communicate with .NET components. Unable to communicate directly with managed code, the CCW acts as a proxy that both provides access to the exposed component and handles the default marshalling behavior (refer to Table 13.1 for a list of data type marshalling). Figure 13.3 illustrates the relative position of the COM callable wrapper in a typical communication process. Figure 13.3. Illustration of the CCW..NET Code Attributes for COM Interop ProgrammingEven with the help of the CCW, it can be quite complicated to expose a .NET component to the COM world. One of the main reasons for that is because there are settings governing component behavior that are normally configured by developers within their COM components. Although you can create simple components that don't need any custom settings, it certainly helps to know what you can and can't modify. To get access to the various behavior modification properties of a COM component, the .NET Framework has created several code attributes that mark classes and assemblies for various levels of COM Interop. Marshalling Data from COM to .NETTable 13.2 contains a listing of some of the most common attributes used in COM Interop programming (a few of the less common attributes have been deliberately omitted). Although you might be able to avoid many of these by doing everything from within Visual Studio .NET, they're still good to know. This list is available in the MSDN library. If you are an old hand at COM programming, many of these attributes will make perfect sense to you. If you get more confused after seeing these attributes, you might benefit from curling up with a good book on COM. (Don Box's Essential COM is just that: essential.) Interop Programming Example: COM Code Utilizing .NET ComponentsTo consume a .NET component via COM from a COM client, there are a few things you need to do. First, you have to create the .NET component. Second, you have to expose or register that component with COM. Third, you need to write the COM client in an unmanaged language, such as VB6 or C++. To get started with the .NET component, create a new C# class library called COMtoDotNet. Then create a single class called DataGrabber. Here you're going to simulate the activity of some kind of data-driven component by querying data out of the Northwind database Listing 13.3. The DataGrabber Class to Be Exposed to COM Clientsusing System; using System.Data; using System.Data.SqlClient; namespace COMtoDotNet { /// <summary> /// Summary description for Class1. /// </summary> public class DataGrabber { public DataGrabber() { } public string GetData() { This code doesn't look too complicated. All you're doing is returning a string that contains the XML representation of the entire Customers table in the Northwind database. Right-click the project and open the Properties window. Select the Configuration Properties node; on the Build tab, change the Register for COM Interop setting to true. This will allow Visual Studio .NET to automatically register your assembly with COM. If you want to register your assembly manually, you can open a command prompt and use the regasm utility. After making sure that your DataGrabber class builds without error in the class library, it's time to move on to creating the COM client. You could use C++ to create a COM client, but VB6 is much easier to read and takes up much less room. Open the VB6 IDE and create a new Windows application. Now you can add a reference to your newly created COM object. Open the Project menu and choose References. You'll see a check box list of various program IDs. Somewhere near the top, you will see a COM object called COMtoDotNet. Check that box and then click OK. With the reference to the .NET component exposed via COM in place, you can write code that makes use of that component's methods. To get started, drop a button onto the form's surface; that button will be used to trigger the invocation of the managed COM component. The code for the form is shown in Listing 13.4. Listing 13.4. The VB6 Client Consuming the .NET COM ObjectPrivate Sub Command1_Click() Dim c2d As COMtoDotNet.DataGrabber Dim data As String Set c2d = New COMtoDotNet.DataGrabber data = c2d.GetData() Text1.Text = data End Sub It all looks remarkably easy. This fact is actually a source of contention among many programmers. As you'll see in the next section, COM Interop isn't always a good thing. And even when COM Interop is a good thing, it is always more complicated than the tools make it seem. When to Use InteropSo far you've seen the simplest ways that you can make use of COM Interop. These methods will actually work for a majority of the situations you'll come across. However, you might encounter some situations in which you have to get deep down into the attributes and type libraries and write custom marshalling to get things working. With that aside, you need to be able to decide when you should use COM Interop. The first thing that you should know is that COM Interop is slow. Each time a .NET component makes a call to a COM object or a COM object makes a call to a .NET component, a significant amount of latency and overhead occurs. If you find yourself in a situation in which you need to make many repeated calls to the same object on the other side of a callable wrapper, you might want to reconsider the use of COM Interop. Another question that often comes up is whether you should rewrite your existing COM objects in .NET or use Interop to reuse the existing components. Obviously if those objects have to stay alive for backward compatibility, you will need to use Interop. However, if you can spare the time and effort to rewrite the components to take full advantage of not only the Framework itself but also the additional architectural structure available, it will be well worth your trouble to do so. |