| By Catalin Sandu | Article Rating: |
|
| July 28, 2007 05:30 PM EDT | Reads: |
10,327 |
The default value used by .NET when the class is not marked with this attribute is AutoDispatch. This means that the class implements the IDispatch interface and supports late binding COM clients only - that's right, type information will not be generated for the exposed class, and its functionality can be discovered by calling IDispatch::GetIDsOfNames. Class methods will then be callable via IDispatch::Invoke. This is exactly what I did in Listing 1, and this is what happens behind the scenes in the Visual Basic 6 snippet shown earlier.
The next enumeration value is AutoDual. This will generate a dual class interface for you. The resulting component can be used both by late binding clients and by early binding COM clients. This might be wonderful, but let me spoil your happiness before it's too late: AutoDual has its own problems and its use is not recommended at all for good designs.
Remember that the order of methods in a COM interface is very important. You can't reorder the methods inside an interface and live happily ever after, especially when that piece of code is already in production. A dual class interface needs its own dispatch identifiers (DISPIDs) for the public stuff defined for that class. This is exactly what AutoDual will do: it will automatically assign unique DISPIDs to your exposed methods, properties, public fields, and so on.
Why is this bad news? A project is a living thing, and as it evolves, you'll find yourself redefining classes, moving stuff around, and the next thing you know, your DISPIDs are not the same anymore. Other .NET applications are quite happy with that, but this presents a real versioning problem for already existing early binding COM clients, or clients that have cached the initial dispatch identifier values.
The basic advice here is to avoid using AutoDual at all costs, or to rely on it only if you don't have any other option. However, there might be a solution for this after all: the ace up .NET's sleeve is called DispId (surprise!), which is a nice attribute that can be applied to methods, fields, and properties. Thus, even if you move things around, rest assured nothing will explode. Here's how it's used:
public class COMExposedClass {
[DispId(1000)] void DoSomething();
}
Voilà! You have successfully created an AutoDual class with set dispatch identifiers.
Let's move on. What's left for us is ClassInterfaceType.None. No class interface is generated if you use this value, and you need to define your own interfaces that will then be implemented by the exposed class. This offers the greatest flexibility from all values defined by ClassInterfaceType, and is in fact the recommended way of exposing .NET types to COM.
When defining interfaces that will later be implemented by a COM-visible .NET component, you do it by giving it a Global Unique Identifier (GUID) and specifying the interface type via yet another attribute (InterfaceTypeAttribute):
[Guid("place-a-newly-generated-GUID-here")]
[InterfaceType(...a ComInterfaceType enumerated value goes here...)]
public class ICoolInterface {
...add some methods...
}
Note that the GUID attribute is optional - all types in .NET have a GUID associated with them, but if you do want to control this aspect, this is the attribute you need.
ComInterfaceType is an enumeration that lets you define an IUnknown-derivative interface (ComInterfaceType.InterfaceIsIUnknown), an IDispatch-derivative interface (ComInterfaceType.InterfaceIsIDispatch), or a dual interface (ComInterfaceType.InterfaceIsDual), similar to ClassInterface Type.
Of course, you can implement non-COM visible interfaces, too (just use ComVisible(false) when defining them, or make them non-public). Obviously, these won't be accessible to COM clients. See Listing 2 for a simple illustration of what I've just described. It contains a class that implements an IUnknown-type interface, an IDispatch-type interface (I omitted the GUID values from this code), as well as the class that implements them.
What if you need to implement a standard COM interface? You're in luck here, too. All you have to do is rewrite that interface in your solution and add an extra attribute, ComImport; it informs the framework that you're bringing an outside interface into your program. See Listing 3 for a possible definition of the well-known IOleCommandTarget interface (I left the definition for OLECMD and OLECMDTEXT for you as an exercise). Note that here you're forced to use a GUID attribute, one whose value must be the same as the one assigned by COM to the original interface.
That's all I need to say about interfaces and classes for now.
Now, before moving to more interesting things, I think we have to take another detour and see how .NET types are mapping to COM data types. After all, you need to know what other legacy project will have to deal with when talking with your coolest application ever. I promise to make it short. It'll be only...
An Interlude: Mapping of .NET Built-in Types to Win32 Types
Table 1 shows
which Interface Definition Language (IDL) type corresponds to which
.NET native data type. Everything else (structures, for example) will
build upon this table. Remember, IDL is the language used to define COM
interfaces (among others). For good measure, I included C++ types, too
(there are slight variations between the two).
In fact, there is a very simple way to find out this correspondence. Check out Listing 4; it defines an ITestTypes interface that contains only dummy methods whose return types are exactly all .NET built-in types. I then implemented this interface in a TestTypes class. Now all I have to do is build and register the resulting library (remember our friend, RegAsm.exe?).
Once the component is registered, you can use the OleView tool installed with Visual Studio to browse for and look at the generated type library. All I did was apply the methods described in this article.
Exposing Events
Sooner or later you would need to
expose some events for your components. You're already familiar with
.NET delegates and events, and you're probably wondering if this
knowledge can't be leveraged in the COM world. True enough, this is
exactly how COM clients can respond to events happening in your .NET
code - plus a little help from an intermediate interface.
Remember our hello-there class from the beginning of the article? I didn't; it's time to add some event-exposing stuff to it. What if the powers that be decide that your class can't accept an empty string as parameter - after all, you can't say hello to nobody. An empty string parameter will trigger some kind of error in the code, and this can be done in the form of an HelloError event.
To do it right from the COM point of view, the event will be defined as a method in the intermediate interface that I told you about:
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface IHelloEvents {
[DispId(0x00000001)]
void HelloError(string error);
}
Published July 28, 2007 Reads 10,327
Copyright © 2007 SYS-CON Media, Inc. — All Rights Reserved.
Syndicated stories and blog feeds, all rights reserved by the author.
More Stories By Catalin Sandu
Catalin Sandu is a software developer at RomSoft (www.rms.ro) and has 10 years of experience. He is both a Microsoft Certified Professional (on C++ and .NET), and an Advanced ColdFusion MX 7 Developer. Catalin is also a member of the British Computer Society since 2005.
- Kindle 2 vs Nook
- Confessions of a Ulitzer Addict
- IBM Hardware Chief, Intel VC Exec Arrested in Insider Trading Scam
- Tactical Cloud Computing Panel at 1st Annual GovIT Expo
- Ulitzer.com Named Exclusive "New Media" Sponsor of Cloud Computing Conference & Expo
- Infrastructure-as-a-Service Will Mature in 2010: Microsoft's David Chou
- Windows 7 – Microsoft’s First Step to the Cloud
- Cloud Expo and the End of Tech Recession
- Jill Tummler Singer, Deputy CIO of CIA, Keynotes at GovIT Expo
- Reality Check at the Cloud Computing Expo
- Visual Studio 2010 Is Cloud Friendly
- Fired SCO CEO Fires Back
- Kindle 2 vs Nook
- The Difference Between Web Hosting and Cloud Computing
- Ajax in RichFaces 3.3, JSF 2 and RichFaces 4
- Confessions of a Ulitzer Addict
- Wave on Ulitzer: Confessions of a Google Wave Fanboy
- IBM Hardware Chief, Intel VC Exec Arrested in Insider Trading Scam
- Cloud Computing Best Practices
- Tactical Cloud Computing Panel at 1st Annual GovIT Expo
- Ulitzer.com Named Exclusive "New Media" Sponsor of Cloud Computing Conference & Expo
- Infrastructure-as-a-Service Will Mature in 2010: Microsoft's David Chou
- Eval JavaScript in a Global Context
- Windows 7 – Microsoft’s First Step to the Cloud
- Google Maps and ASP.NET
- Crystal Reports XI & How It Has Changed
- Converting VB6 to VB.NET, Part I
- Creating Controls for.NET Compact Framework in Visual Studio 2005
- Where Are RIA Technologies Headed in 2008?
- How to Write High-Performance C# Code
- AJAX World RIA Conference & Expo Kicks Off in New York City
- Implementing Tab Navigation with ASP.NET 2.0
- i-Technology Photo Exclusive: Bill Gates & Steve Jobs In "Nerds"
- .NET Archives: Getting Reacquainted with the Father of C#
- i-Technology Viewpoint: "SOA Sucks"
- Programmatically Posting Data to ASP .NET Web Applications




























