View
our advertisers

Advertise with
us

All Topics, .NET, C# >> .NET >> General

Understanding Classic COM
Interoperability With .NET Applications
By Aravind
C

Discusses how
existing COM components can be used from managed
code.

C++ (VC6, VC7), VB
Windows
(Win2K), .NET (.NET 1.0)
Win32, VS (VS6,
VS5)
Dev

Posted 4 Mar 2001

Updated 25 Jul 2001

Articles
by this author

292,447
views

Search:

Articles Authors

Help!

Articles

Message
Boards

StoreFront

Lounge

Ultimate
Combo
MFC tools $399

Send
to a friend

Sign in / Sign
up

Email

Password

Remember me

Lost your Password?

Print

Broken
Article?

Bookmark

Discuss

276 votes for this article.

Popularity:
10.52. Rating: 4.31 out of
5.

Download
source code for all examples - 156 KB

Download
demo - (Built COM Component binaries & .NET Assemblies only) - 320
KB

(Disclaimer: The information in this article and source code are
published in accordance with the Beta 2 bits of the .NET framework
SDK - Build 1.0.2914.16)

Ever wondered how all those COM components that you've written
through the years play along with the .NET runtime. If you are a diehard
COM developer interested in knowing how Classic COM Components are
positioned in the .NET world or how COM aware clients could consume .NET
components, read on.
Introduction

Part
I : Using Classic COM Components from .NET
Applications
Getting
started
Generating
metadata from the COM Type library
Binding
to the COM component and invoking its methods from a .NET
Application
Accessing
other supported interfaces (QueryInterface) and Dynamic Type
Discovery
Late
Binding to COM Objects
Event
Handling - Connection Points in Classic COM Vs Delegate Event Model in
.NET
Connection
Points in Classic COM
Creating
an ATL COM Component that sources
events
Event
Handling using Delegates
Sinking
Unmanaged COM Events in a .NET Application
Using
COM Collections from .NET
Applications
Creating
a COM collection Component using
ATL
Creating
a COM collection Component using
VB
Consuming
COM Collections in a .NET
Application
Enumerating
elements in a .NET Collection
Mapping
method parameter keywords in C# to IDL's Directional attributes
:
Reusing
Classic COM Components in Managed
code
Reuse
Mechanisms in Classic COM
Reuse
through Mixed-mode
inheritance
Reuse
through Mixed-mode containment
Understanding
COM Threading models & Apartments from a .NET application's
perspective

Part
II : Consuming .NET Components from COM aware
Clients
Creating
a .NET Component
Generating
a typelibrary from the assembly & Registering the
assembly
Consuming
the component from a VB Client
Shedding
more light on the COM Interop wizardry
Snooping
in on the Typelibrary generated by TLBEXP/REGASM
How
COM aware clients talk to .NET Components
Using
attributes to tweak the generated typelibrary
metadata
Altering
the interface type
Altering
the GUID and the ProgID
Hiding
Public types from being exposed to
COM
Altering
the Marshaling behavior for types
Exception
Handling - .NET Exceptions compared with COM
HRESULTs
Handling
events from .NET Components in Unmanaged Event
Sinks
Creating
a .NET Component that sources
Events
Handling
the events in a VB Client Application
Deploying
assemblies hosting .NET components
Thread
Affinity in .NET Components :

Conclusion:
COM's position in the über powerful .NET world.
Quotable
Quotes
Bibliography
Acknowledgements

Introduction:

After playing around with the .NET Beta 1 & Beta 2 bits,
there's no doubt in most developers' mind that the .NET technology is a
powerful way to build components and distributed systems for the
enterprise. But then, what about the tons of existing reusable COM
components that you've built through the last few years, not to mention
all those cups of coffee & sleepless nights. Is it the end of all
those components in the .NET world?. Will those components work
hand-in-hand with the .NET managed runtime?. For those of us who program
with COM for a living, and for those of us who live by the 'COM is love'
mantra, there is good news. COM is here to stay and .NET managed
applications can leverage existing COM components. Certainly, Microsoft
wouldn't want to force companies to abandon all their existing components,
especially components that were written in one of the most widely used
object models for developing both desktop & distributed applications.
Classic COM components interoperate with the .NET runtime through an
interop layer that will handle all the plumbing between
translating messages that pass back and forth between the managed runtime
and the COM components operating in the unmanaged realm, and vice versa.
Now, let's take a look at the other side of the fence. What if you decide
to code your components using a .NET friendly language of your
choice targeted for the CLR and still want to be able
to consume these .NET components from COM aware clients, say, like VB
6.0 or Classic ASP?. Despair not. COM aware clients will be more than
happy to party around with .NET Components through the COM Interop. The
tools provided with the .NET framework allow you to expose .NET components
to COM aware clients as if they were plain-vanilla COM components.
The COM Interop handles all the grunge work and plumbing under the covers.
In the first
part of the article, we will focus on how you can get COM components
to work with .NET applications and then in the latter
part, we'll take a look at how you can consume .NET
Components from COM aware clients in the unmanaged world. Hopefully, at
the end of the article, you'd have gained enough ground to understand how
Classic COM and the .NET framework can peacefully co-exist and tango
together. So if you're ready, let's take a journey through exploring how
Classic COM fits into the grand scheme of things in the .NET world.

Part I : Using Classic COM Components from .NET
Applications:

Getting started:

We'll begin by taking a look at how you can expose a Classic COM
Component to a .NET Application. Our first order of business is to
write a simple COM component using ATL that gives us the arrival details
for a specific airline. For simplicity, we only return details for the
'Air Scooby IC 5678' airline and return an error for any other airline.
That way, you can also take a look at how the error raised by the COM
component can be propagated back and caught by the calling .NET client
application.

Here's the IDL definition for the IAirlineInfo interface:

.....

interface IAirlineInfo : IDispatch
{

[id(1), helpstring("method GetAirlineTiming")]
HRESULT GetAirlineTiming([in] BSTR bstrAirline, [out,retval] BSTR* pBstrDetails);

[propget, id(2), helpstring("property LocalTimeAtOrlando")]
HRESULT LocalTimeAtOrlando([out, retval] BSTR *pVal);

};
.......

And here's the implementation of the GetAirlineTiming
method:

.......

CAirlineInfo::GetAirlineTiming(BSTR bstrAirline, BSTR *pBstrDetails)
{
_bstr_t bstrQueryAirline(bstrAirline);
if(NULL == pBstrDetails) return E_POINTER;

if(_bstr_t("Air Scooby IC 5678") == bstrQueryAirline)
{
// Return the timing for this Airline
*pBstrDetails = _bstr_t(_T("16:45:00 - Will arrive at Terminal 3")).copy();

}
else
{
// Return an error message if the Airline was not found
return Error(LPCTSTR(_T("Airline Timings not available for this Airline" )),
__uuidof(AirlineInfo), AIRLINE_NOT_FOUND);
}
return S_OK;

}
.......

Since we are ready with our component, let's take a look at generating
some metadata from the component's type library, so that the .NET client
can use this metadata to talk to our component and invoke it's methods.

Generating metadata from the COM Type
library:

Consuming a Classic COM
Component from a .NET application

A .NET application that needs to talk to our COM component cannot
directly consume the functionality that's exposed by it. So, we need to
generate some metadata. This metadata layer is used by the runtime to
glean out type information, so that it can use this type information at
runtime to manufacture what is called as a Runtime Callable Wrapper
(RCW). The RCW handles the actual activation of the COM object and
handles the marshaling requirements when the .NET application interacts
with it. The RCW also does tons of other chores like managing object
identity, object lifetime, and interface caching. Object
lifetime management is a very critical issue here because the .NET runtime
moves objects around and garbage collects them. The RCW serves the purpose
of giving the .NET application the notion that it is interacting with a
managed .NET component and it gives the COM component in the unmanaged
space, the impression that it's being called by a good old COM client. The
RCW's creation & behavior varies depending on whether you are early
binding or late binding to the COM object. Under the hood, the RCW is
doing all the hard work and thunking down all the method invocations into
corresponding v-table calls into the COM component that lives in the
unmanaged world. It's an ambassador of goodwill between the managed world
and the unmanaged IUnknown world.

Let's generate the metadata wrapper for our Airline COM component. To
do that, we need to use a tool called the TLBIMP.exe. The Type library
Importer (TLBIMP) ships with the .NET framework SDK and can be found
under the Bin subfolder of your SDK installation. The Type
library Importer utility reads a type library and generates the
corresponding metadata wrapper containing type information that the .NET
runtime can comprehend.

From the DOS command line, type the following command:

TLBIMP AirlineInformation.tlb
/out:AirlineMetadata.dll

This command tells the type library importer to read your AirlineInfo
COM type library and generate a corresponding metadata wrapper called
AirlineMetadata.dll. If everything went through fine, you should see a
message indicating that the metadata proxy has been generated from the
type library:

Type library imported to
E:\COMInteropWithDOTNET\AirlineMetadata.dll

What kind of type information does this generated metadata contain and
what does it look like?. As COM folks, we've always loved our beloved
OleView.exe, at times when we felt we needed to take a peek at a
type library's contents, or for the tons of other things that OleView is
capable of doing. Fortunately, the .NET SDK ships with a disassembler
called ILDASM that allows us to view the metadata & the
Intermediate language (IL) code generated for managed assemblies.
Every managed assembly contains self-describing metadata and ILDASM is a
very useful tool when you need to take a peek at that IL code and
metadata. Go ahead and open AirlineMetadata.dll using ILDASM. Take a look
at the metadata generated and you will see that the
GetAirlineTiming method is listed as a public method of the
IAirlineInfo interface that is implemented by the AirlineInfo
class. There is also a constructor that gets generated for the
AirlineInfo class. The datatypes for the method parameters and return
values have also been substituted to take their equivalent .NET
counterparts. In our example, the GetAirlineTiming method's
parameter with the BSTR datatype has been replaced by the
string(an alias for System.String) datatype. Also notice
that the parameter that was marked [out,retval] in the
GetAirlineTiming method has been converted to the actual return
value of the method (returned as a string). Any failure HRESULT
values that are returned back from the COM object (in case of an error or
failed run-of-the-mill business logic) are thrown back as .NET
exceptions.

IL Disassembler - a great tool for
viewing metadata and MSIL for managed assemblies

Binding to the COM component and invoking its
methods from a .NET Application :

Now that we have generated the metadata that is required by a .NET
client, let's try invoking the GetAirlineTiming method in our COM
object from the .NET Client. Here's a simple C# client application that
creates the COM object using the metadata that we generated earlier and
invokes the GetAirlineTiming method. The first method call
invocation should go through fine and we get back the details of airline
"Air Scooby IC 5678". Then, we'll pass in an unknown airline, "Air Jughead
TX 1234", so that the COM object throws us the AIRLINE_NOT_FOUND
error that we defined.

.......

String strAirline = "Air Scooby IC 5678";
String strFoodJunkieAirline = "Air Jughead TX 1234";
try
{
AirlineInfo objAirlineInfo;
objAirlineInfo = new AirlineInfo();

// Call the GetAirlineTiming() method
System.Console.WriteLine("Details for Airline {0} --> {1}",
strAirline,
objAirlineInfo.GetAirlineTiming(strAirline));

// This should make the COM object throw us the
// AIRLINE_NOT_FOUND error as a COMException
System.Console.WriteLine("Details for Airline {0} --> {1}",
strFoodJunkieAirline,
objAirlineInfo.GetAirlineTiming(strFoodJunkieAirline));
}
catch(COMException e)
{
System.Console.WriteLine("Oops an error occured !. Error Code is : {0}.
Error message is : {1}",e.ErrorCode,e.Message);
}
.......

Here's how the output would look like :

Details for Airline Air Scooby IC 5678 -->
16:45:00 - Will arrive at Terminal 3
Oops an error occured !.
Error Code is : -2147221502. Error message is : Airline Timings not
available
for this Airline

Under the hood, the runtime fabricates an RCW and this maps
the metadata proxy's class methods and fields to methods and properties
exposed by the interface that the COM object implements. One RCW instance
is created for each instance of the COM object. The .NET runtime only
cares about managing the lifetime of the RCW and garbage collects the RCW.
It's the RCW that takes care of maintaining reference counts on the COM
object that it's mapped to, thereby, shielding the .NET runtime from
managing the reference counts on the actual COM object. As shown in the
ILDASM view, the AirlineInfo metadata class is defined under a
namespace called AirlineMetadata. This class implements the
IAirlineInfo interface. All you need to do is, just create an
instance of the AirlineInfo class using the new
operator, and call the public class methods of the created object. When
the method is invoked, the RCW thunks down the call to the corresponding
COM method. The RCW also handles all the marshaling & object lifetime
issues. To the .NET client, it looks nothing more than it's actually
creating a managed object and calling one of its public class members.
Anytime the COM method raises an error, the COM error is trapped by the
RCW, and the error is converted into an equivalent COMException
class (found in the System.Runtime.InteropServices namespace). Of
course, the COM object still needs to implement the
ISupportErrorInfo & IErrorInfo interfaces for this
error propagation to work, so that the RCW knows that the object provides
extended error information. The error is caught by the .NET client using
the usual try-catch exception handling mechanism and has access
to the actual error number, description, the source of the exception and
other details that would have been available to any COM aware client. You
could also return standard HRESULTs back and the RCW will take care of
mapping them to the corresponding .NET exceptions to throw back to the
client. For example, if you were to return a HRESULT of E_NOTIMPL
from your COM method, then the RCW will map this to the .NET
NotImplementedException exception and throw an exception of that
type.
Please refer to the Exception
Handling section in this article, to learn more about how .NET
exceptions are mapped to COM HRESULTs.

Accessing other supported interfaces and
Dynamic Type Discovery:

How does the classic QueryInterface scenario work from the
perspective of the .NET client when it needs to check if the COM Object
implements a specific interface ?. To QI for another interface, all you
need to do is cast the object to the interface that you are querying for,
and if it succeeds, voilà, your QI has succeeded as well. In case you
attempt to cast the object to some arbitrary interface that the object
does not support, a System.InvalidCastException exception is
thrown, indicating that the QI has failed. It's that simple. Again, the
RCW does all the hard work under the covers. It's a lot like how the VB
runtime shields us from having to write any explicit QueryInterface
related code and simply does the QI for you when you set one object type
to an object of another associated type.

An alternate way to check if the object instance that you are currently
holding supports or implements a specific interface type is to use C#'s
'is' operator. The 'is'
operator does runtime type checking to see if the object can be cast
safely to a specific type. If it returns true, then you can
safely perform a cast to get the QI done for you. This way the RCW ensures
that you are casting to only interfaces that are implemented by the COM
object and not just any arbitrary interface type. You can also use C#'s
'as' operator to cast from one type to another
compatible type as shown in the example below. These simple constructs are
all what you need to keep swinging between interfaces that the COM object
supports in a type safe manner.

.......
try
{
AirlineInfo objAirlineInfo = null;
IAirportFacilitiesInfo objFacilitiesInfo = null;

// Create a new AirlineInfo object
objAirlineInfo = new AirlineInfo();

// Invoke the GetAirlineTiming method
String strDetails = objAirlineInfo.GetAirlineTiming(strAirline);

// Check to see if the AirlineInfo object supports the
// IAirportFacilitiesInfo interface using C#'s 'is' operator
if(objAirlineInfo is IAirportFacilitiesInfo)
{
// Perform a cast to get the QI done
objFacilitiesInfo = (IAirportFacilitiesInfo)objAirlineInfo;

// There's always more than one way to skin a cat
// You could even perform the cast using C#'s 'as' operator
objFacilitiesInfo = objAirlineInfo as IAirportFacilitiesInfo;

//Invoke a method on the IAirportFacilitiesInfo interface
System.Console.WriteLine("{0}",objFacilitiesInfo.GetInternetCafeLocations());

}

// Let's check against an arbitrary interface type
if(objAirlineInfo is IJunkInterface)
{
System.Console.WriteLine("We should never get here ");
}
else
{
System.Console.WriteLine("I'm sorry I don't implement the IJunkInterface interface ");
}

// And now let's ask for some trouble and have the
// interop throw us an invalid cast exception.
IJunkInterface objJunk = null;
objJunk = (IJunkInterface)objAirlineInfo;

}/* end try */
catch(InvalidCastException eCast)
{
System.Console.WriteLine("Here comes trouble ... Error Message : {0}",eCast.Message);

}/* end catch */
.......

Here's how the output would look like :

Your nearest Internet Cafe is at Pavilion 3
in Terminal 2 - John Doe's Sip 'N' Browse Cafe
I'm sorry I don't
implement the IJunkInterface interface
Here comes trouble ...
Error Message : An exception of type System.InvalidCastException was
thrown.

Late Binding to COM Objects:

All the examples that you saw above used the metadata proxy to early
bind the .NET Client to the COM object. Though early binding provides a
whole smorgasbord of benefits like strong type checking at compile time,
providing auto-completion capabilities from type-information for
development tools, and of course, better performance, there may be
instances when you really need to late bind to a Classic COM object when
you don't have the compile time metadata for the COM object that you are
binding to. You can late bind to a COM object through a mechanism called
Reflection. This does not apply to COM objects alone. Even .NET
managed objects can be late bound and loaded using Reflection.
Also, if your object implements a pure dispinterface only, then
you are pretty much limited to only using Reflection to activate your
object and invoke methods on its interface. For late binding to a COM
object, you need to know the object's ProgID or CLSID. The
CreateInstance static method of the System.Activator
class allows you to specify the Type information for a specific
class and it will automatically create an object of that specific type.
But what we really have is a COM ProgID and COM CLSID
for our COM object and not true .NET Type Information. So we need
to get the Type information from the ProgID or
CLSID using the GetTypeFromProgID or
GetTypeFromCLSID static methods of the System.Type
class. The System.Type class is one of the core enablers for
Reflection. After creating an instance of the object using
Activator.CreateInstance, you can invoke any of the
methods/properties supported by the object using the
System.Type.InvokeMember method of the Type object that you got
back from Type.GetTypeFromProgID or
Type.GetTypeFromCLSID. All you need to know is, the name of the
method or property and the kind of parameters that the method call
accepts. The parameters are bundled up into a generic
System.Object array and passed away to the method. You would also
need to set the appropriate binding flags depending on whether
you are invoking a method or getting/setting the value of a property.
That's all there is to late binding to a COM object.

.......
try
{

object objAirlineLateBound;
Type objTypeAirline;

// Create an object array containing the input parameters for the method
object[] arrayInputParams= { "Air Scooby IC 5678" };

//Get the type information from the progid
objTypeAirline = Type.GetTypeFromProgID("AirlineInformation.AirlineInfo");

// Here's how you use the COM CLSID to get the associated .NET System.Type
// objTypeAirline = Type.GetTypeFromCLSID(new Guid("{F29EAEEE-D445-403B-B89E-C8C502B115D8}"));

// Create an instance of the object
objAirlineLateBound = Activator.CreateInstance(objTypeAirline);

// Invoke the 'GetAirlineTiming' method
String str = (String)objTypeAirline.InvokeMember("GetAirlineTiming",
BindingFlags.Default | BindingFlags.InvokeMethod,
null,
objAirlineLateBound,
arrayInputParams);

System.Console.WriteLine("Late Bound Call - Air Scooby Arrives at : {0}",str);

// Get the value of the 'LocalTimeAtOrlando' property
String strTime = (String)objTypeAirline.InvokeMember("LocalTimeAtOrlando",
BindingFlags.Default | BindingFlags.GetProperty,
null,
objAirlineLateBound,
new object[]{});

Console.WriteLine ("Late Bound Call - Local Time at Orlando,Florida is: {0}", strTime);

}/* end try */
catch(COMException e)
{
System.Console.WriteLine("Error code : {0}, Error message : {1}",
e.ErrorCode, e.Message);
}/* end catch */
.......

Take a look at the output that you'd get :

Late Bound Call - Air Scooby Arrives at
16:45:00 - Will arrive at Terminal 3
Late Bound Call - Local Time
at Orlando,Florida is: Sun Jul 15 16:50:01
2001

Event Handling - Connection Points in Classic
COM Vs Delegate Event Model in .NET:

The Connection Points event handling mechanism, as you know,
is one of the primary enablers for bi-directional communication between
your COM components and the consumers of your components. Just to jog your
memory, I'll brief you a little bit on the event handling mechanism in
Classic COM Components. Typically, COM components that support event
notifications have what is called, an outgoing interface. The
outgoing interface is used by the component to call into the client when a
specific event has occured. Outgoing interfaces are marked with the
[source] attribute in the coclass section of the
component's IDL file. The [source] attribute in the IDL
allows development tools and IDEs to parse the typelibrary to check to see
if the object supports an outgoing interface. Consumers or clients of
these components usually set up a sink object, which implements
this outgoing interface. An interface pointer to this sink object
is passed by the client to the component. The component stashes away this
interface pointer in typically, something like a map that contains a list
of outgoing interface pointers to sink objects that are interested in
receiving notifications from the component. Whenever a component needs to
raise an event, it uses the map to get a list of interface pointers to the
sink objects that have subscribed for notifications. It then notifies them
by calling the respective method on the outgoing interface implemented by
the sink object.

Connection Points in Classic COM :

Essentially, a COM object that supports outgoing
interfaces, implements the IConnectionPointContainer
interface. A client that wants to receive event notifications does a
QI on the COM object for the
IConnectionPointContainer interface to see if it supports
outgoing interfaces. If the QI fails, then the
object does not support events. If the QI
succeeds, the client calls the FindConnectionPoint method
(could also call EnumConnectionPoints) on the
IConnectionPointContainer interface by passing it the
IID of the outgoing interface. If such an
interface is supported, the client receives back an
IConnectionPoint interface pointer corresponding to the
outgoing interface. It then calls the
IConnectionPoint::Advise method and passes to
it, the sink object's IUnknown pointer. The COM object
adds this IUnknown pointer to its map to keep a list of the
sink objects that have subscribed for notifications. The client gets
back a cookie from the COM object that it can subsequently
use to revoke event notifications. When the COM
object needs to raise events, it iterates through the map, gets
a list of all the sink object interface pointers and calls
the corresponding event methods on the outgoing interface
(implemented by the sink object). When a client no longer desires to
receive notifications, it removes itself from the object's map by
calling the IConnectionPoint::Unadvise method by
passing it the cookie that it received earlier on the
IConnectionPoint::Advise call.
Connection Points in
Classic COM

In simple terms, that's how the event handling mechanism &
bi-directional communication works in Classic COM components. Most
books that teach COM programming usually have a full chapter
dedicated to explain this architecture and you might want to look
them up to further your understanding in this topic.

Creating
an ATL COM Component that sources events :

Let's take a look at how the Connection Points event handling mechanism
in COM translates to the delegate event handling mechanism in the .NET
world. We'll take a look at how you can use .NET managed event sinks to
catch event notifications sent from COM objects. To get started, let's
take a look at the COM object that's going to source events to your .NET
application. Let's put together a simple COM object that will page your
.NET application, whenever an airline arrives at a fictitious airport
called John Doe International Airport. We will subscribe to this paging
service from our .NET application so that we get paged whenever an
airplane taxies down on John Doe's runway.

We'll create an ATL EXE project that hosts an object called
AirlineArrivalPager. The AirlineArrivalPager object
supports an incoming interface called
IAirlineArrivalPager and an outgoing interface called
_IAirlineArrivalPagerEvents. Here's the interface definition of
the _IAirlineArrivalPagerEvents outgoing interface. This
interface is marked with the [source] attribute in the coclass
definition.

.....

interface IAirlineArrivalPager : IDispatch
{
[id(1), helpstring("method AddArrivalDetails")]
HRESULT AddArrivalDetails([in] BSTR bstrAirlineName, [in] BSTR bstrArrivalTerminal);
};

....

dispinterface _IAirlineArrivalPagerEvents
{
properties:
methods:
[id(1), helpstring("method OnAirlineArrivedEvent")]
HRESULT OnAirlineArrivedEvent([in] BSTR bstrAirlineName,
[in] BSTR bstrArrivalTerminal);
};

....

coclass AirlineArrivalPager
{
[default] interface IAirlineArrivalPager;
[default, source] dispinterface _IAirlineArrivalPagerEvents;
};

.......

Take a look at the implementation of the incoming
IAirlineArrivalPager interface's AddArrivalDetails
method:

.......
STDMETHODIMP CAirlineArrivalPager::AddArrivalDetails(BSTR bstrAirlineName,BSTR bstrArrivalTerminal)
{
// Notify all subscribers that an Airline has hit the tarmac
Fire_OnAirlineArrivedEvent(bstrAirlineName,bstrArrivalTerminal);

// Return the status to the caller
return S_OK;
}
.......

The implementation of this method uses the
Fire_OnAirlineArrivedEvent helper method to notify all sink
objects implementing _IAirlineArrivalPagerEvents that have
subscribed for event notifications. The
Fire_OnAirlineArrivedEvent is a method of the helper proxy class
derived from IConnectionPointImpl that is generated automatically
by the ATL Implement Connection point wizard. Essentially, it
iterates through the map where it stored the interface pointers to the
sink objects when IConnectionPoint::Advise was called, and uses
these interface pointers to call the event notification method
(OnAirlineArrivedEvent) implemented by the client's sink object.

If you were a C++ programmer coding a COM aware client application to
receive notifications, you would set up a sink object in the
client application that implements the
_IAirlineArrivalPagerEvents interface. You would then create the
AirlineArrivalPager object and pass it the IUnknown
interface pointer of the sink object through a call to
IConnectionPoint::Advise or use a helper method such as
AtlAdvise to wire your sink to the object that raises events, so
that you can receive event notifications. With VB 6.0, it's as simple as
using the WithEvents keyword in your declaration and defining a
handler function for receiving notifications. VB will do all the hard work
under the covers to hook up the notifications made on the
outgoing interface to the appropriate handler function.

Event
handling using Delegates:

If you are already familiar with how delegates are used in .NET, you
might want to skip this section and swing by to the next
section. Event handling in .NET is primarily based on the Delegate
Event model. A delegate is something akin to function
pointers that we use in C/C++. The delegate based Event model was
popularized by the simplicity of its use, right from the WFC
(Windows Foundation Classes in Visual J++) days. Delegates allow
an event raised by any component to be connected to a handler function or
method of any other component as long as the function signatures of the
handler function or method matches the exact signature of that of the
delegate. Take a look at this simple example below that shows you how you
can put delegates to action.

// Here's the SayGoodMorning delegate
delegate string SayGoodMorning();

public class HelloWorld
{
public string SpeakEnglish() {
return "Good Morning";
}

public string SpeakFrench() {
return "Bonjour";
}

public static void Main(String[] args) {

HelloWorld obj = new HelloWorld();

// Associate the delegate with a method reference
SayGoodMorning english = new SayGoodMorning(obj.SpeakEnglish);
SayGoodMorning french = new SayGoodMorning(obj.SpeakFrench);

// Invoke the delegate
System.Console.WriteLine(english());
System.Console.WriteLine(french());

}

}/* end class */

Here's the output that you get back:

Good
Morning
Bonjour

In the example above, we declare a delegate called
SayGoodMorning. Then we wire the delegate to reference the
SpeakEnglish and SpeakFrench methods of the
HelloWorld object. All that is required is that the
SpeakEnglish and SpeakFrench methods have the same
signature as that of the SayGoodMorning delegate. The reference
is typically made by instantiating the delegate as if it were an object
and passing in the referenced method as its parameter. The referenced
method could either be an instance method or a static method of a class.
The delegate maintains the reference it needs to call the right handler
for the event. This makes delegates first class object-oriented citizens
and they are also type-safe and secure to deal with. The .NET
event-handling model is based primarily on the delegate event
model. Take a look at the following example:

......

// Create a Button
private System.Windows.Forms.Button AngryButton = new Button();

....

// Add a delegate to the button's Click event list
AngryButton.Click += new System.EventHandler(AngryButton_Click);

.....

// Here's the handler function that the delegate references
protected void AngryButton_Click(object sender,EventArgs e)
{
MessageBox.Show("Please Stop clicking me !!");
}

.......

When your application deals with controls and wants to receive specific
notifications, it creates a new instance of an EventHandler
delegate that contains a reference to the actual handler function
that will handle the events raised by the control. In the example shown
above, the EventHandler delegate contains a reference to the
AngryButton_Click method. The AngryButton_Click method
needs to have the same method signature as that of the EventHandler
delegate. Here's how the signature of the
System.EventHandler delegate looks like:

public delegate void EventHandler(object sender, EventArgs e);

The EventHandler delegate instance will then have to be added
to the Click event's list of delegate instances. Delegates that
extend the System.MulticastDelegate allow you to add multiple
handler functions to the delegate's invocation list using C# operators
such as += and -= which are wrappers for the Delegate.Combine and
Delegate.Remove methods. Using an event provides users
with a foolproof scheme to only add or remove delegate
instances to the event using the += and -= operator and not accidentally
overwrite the invocation list. When the control raises an event, each of
the delegates that have been added to the button's Click event
will be invoked and the delegate will route it to the correct
handler function that it references.

In our example, whenever the Click Event occurs in the button
the call will be routed to the AngryButton_Click method. I guess
this gives you a fairly good idea on the role played by delegates
and events in the event-handling mechanism in the .NET framework.
The reason I explained to you how delegates work is because it's one of
the primary enablers of the .NET event handling model and it's important
to understand this to appreciate how .NET applications use delegates to
subscribe to event notifications from Classic COM Components.

Sinking
Unmanaged COM Events in a .NET Application:

Here's a simple VB Client application that assumes the role of the
Control Tower at John Doe International Airport and calls the
AddArrivalDetails method in the incoming interface. The
implementation of this method in turn triggers the event notifications
that are subsequently caught by the handler functions in the .NET
application that have subscribed for OnAirlineArrivedEvent event
notifications. The AirlineArrivalPager COM object is itself a
singleton object hosted in an out-of-proc COM server.
So, the same instance of the object services both the VB based Control
tower application (that triggers events) and the .NET pager applications
that have subscribed for OnAirlineArrivedEvent event
notifications.

Dim AirlinePager As New AIRLINENOTIFYLib.AirlineArrivalPager

Private Sub AirlineArrived_Click()
AirlinePager.AddArrivalDetails Me.AirlineName, Me.ArrivalTerminal
End Sub

With that said, let's see how a .NET managed application receives event
notifications generated by the AirlineArrivalPager COM object.
Firstly, you need to generate a .NET metadata proxy from the COM object's
typelibrary, so that it can be consumed by a .NET application. Let's use
the Type Library Importer (TLBIMP) to generate the metadata proxy
assembly for us.

tlbimp AirlineNotify.tlb
/out:AirlineNotifyMetadata.dll

This metadata proxy will be referenced in your .NET application. Here's
a simple .NET Windows Forms application that subscribes to event
notifications from the AirlineArrivalPager COM component using
delegates.

......

using AirlineNotifyMetadata;

public class AirlineNotifyForm : System.WinForms.Form
{
private System.Windows.Forms.CheckBox checkBoxPaging;
private System.Windows.Forms.ListBox listPager;
private AirlineArrivalPager m_pager = null;

......

public AirlineNotifyForm() {

.....

// Subscribe to event notifications from
// the AirlineArrivalPager component
subscribePaging();

}

......

void subscribePaging() {

// Create an AirlineArrivalPager object
m_pager = new AirlineArrivalPager();

// Add the delegate instance that references the OnMyPagerNotify method
// to the OnAirlineArrivedEvent event list (ICP::Advise)
m_pager.OnAirlineArrivedEvent +=
new _IAirlineArrivalPagerEvents_OnAirlineArrivedEventEventHandler(OnMyPagerNotify);

}/* end subscribePaging */

protected void checkBoxPaging_CheckedChanged (object sender, System.EventArgs e) {

if(checkBoxPaging.Checked) {

// If checked, add the delegate instance that references OnMyPagerNotify
// to the OnAirlineArrivedEvent event list (ICP::Advise)
m_pager.OnAirlineArrivedEvent +=
new _IAirlineArrivalPagerEvents_OnAirlineArrivedEventEventHandler(OnMyPagerNotify);

}
else {

// If Unchecked, remove the delegate instance that references OnMyPagerNotify
// from the OnAirlineArrivedEvent event list (ICP::Unadvise)
m_pager.OnAirlineArrivedEvent -=
new _IAirlineArrivalPagerEvents_OnAirlineArrivedEventEventHandler(OnMyPagerNotify);

}

}/* end checkBoxPaging_CheckedChanged */

public int OnMyPagerNotify(String strAirline, String strTerminal) {

StringBuilder strDetails = new StringBuilder("Airline ");
strDetails.Append(strAirline);
strDetails.Append(" has arrived in ");
strDetails.Append(strTerminal);
listPager.Items.Insert(0,strDetails);
return 0;

}/* end OnMyPagerNotify */

}/* end class */

The line of code that is most important here is the line:

m_pager.OnAirlineArrivedEvent +=
new _IAirlineArrivalPagerEvents_OnAirlineArrivedEventEventHandler(OnMyPagerNotify);

If you understand the semantics of how delegates work, you should have
absolutely no problem comprehending what's going on here. What you're
doing is adding the
_IAirlineArrivalPagerEvents_OnAirlineArrivedEventEventHandler
delegate instance that references the OnMyPagerNotify method to
the OnAirlineArrivedEvent event list. Usually the name of the
event (OnAirlineArrivedEvent) is the same as the method name in
the outgoing interface. The delegate name
(_IAirlineArrivalPagerEvents_OnAirlineArrivedEventEventHandler)
usually follows the pattern
InterfaceName_EventNameEventHandler. That's all
there is to receiving event notifications from COM components. All you
need to do is create an instance of the component and then add a
delegate referencing your handler function to the event list.
Effectively, what you are doing here is something that's analogous to the
IConnectionPoint::Advise in the COM world. Whenever the
OnAirlineArrivedEvent event is raised by the COM component, the
OnMyPagerNotify method will be called to handle the event
notification. It's that simple in .NET, to wire a handler sink to receive
event notifications from a COM object that sources events.

How the Connection Point Event Handling mechanism
in Classic COM maps to the Delegate event handling
mechanism in
.NET

When you no longer want to receive notifications, you can remove the
delegate from the event list by calling:

m_pager.OnAirlineArrivedEvent -=
new _IAirlineArrivalPagerEvents_OnAirlineArrivedEventEventHandler(OnMyPagerNotify);

This is analogous to the IConnectionPoint::Unadvise method
call that revokes further notifications by removing your sink object's
interface pointer from the map using the cookie that you received
in the Advise call. But, who handles the mapping between the
Connection point event handling model in Classic COM and
delegate event model in .NET? . The metadata proxy generated by
the Typelibrary importer (TLBIMP) contains classes that act as
adaptors to wire the Connection point Event Model in the
unmanaged world to the Delegate based event model in the .NET world via
the RCW stub that is created at runtime. If you are interested in
examining what happens under the hood, I encourage you to open up the
metadata proxy (AirlineNotifyMetadata.dll) using the IL
Diassembler (ILDASM) and examine the MSIL code for the various
methods in the helper classes.

Using COM Collections from .NET Applications:

Using COM based Collections allows you to categorize objects together
as a part of the group that exhibit similar behavior. For example, a
BookCollection could model all the books in a library. Each
Book object stored in this collection could represent the details
of the book such as the Author, ISBN etc. Iterating through the collection
is extremely simple and allows you to get back objects on demand that have
been added to the collection. There are other methods to model collections
too, such as using SAFEARRAYs. The problem with a SAFEARRAY is
that, if the collection is large, it involves moving entire data chunks
that the SAFEARRAY represents across to the client. Using collections
allows you to get data on demand. Also, it's much more elegant to iterate
through a collection from clients such as VB, using the For
Each..Next syntax. If you have existing COM objects that represent
COM based collections, they would continue to work well with .NET
applications. These collections can be enumerated just as easily by .NET
clients. We'll soon see how. If you are already familiar with how
collections work in Classic COM, you might want to skip this section and
go to the next
section.

Creating
a COM collection Component using ATL :

For the VB folks, who feel more at home with churning out COM
components with VB, you might want to skip this section and go to the Creating
a COM collection Component using VB section. First, let's put
together a simple COM Component using ATL that models an ice cream
Collection.The Collection class represents the menu at an ice cream
parlor that contains a variety of ice cream flavors. Take a look at the
IDL file for this component.

[
....
]
interface IIceCreamMenu : IDispatch
{
[propget, id(1), helpstring("property Count")]
HRESULT Count([out, retval] long *pVal);

[propget, id(DISPID_NEWENUM), helpstring("property _NewEnum"), restricted]
HRESULT _NewEnum([out, retval] LPUNKNOWN *pVal);

[propget, id(DISPID_VALUE), helpstring("property Item")]
HRESULT Item([in] long lIndex, [out, retval] VARIANT *pVal);

[id(2), helpstring("method AddFlavortoMenu")]
HRESULT AddFlavortoMenu([in] BSTR bstrNewFlavor);
};

The Count, _NewEnum and Item property are standard
properties that every COM Collection supports. The _NewEnum
property allows you to enumerate through the collection using constructs
like For Each .. Next in VB and is always assigned a DISPID of
DISPID_NEWENUM(-4) to indicate that it's the enumerator for the
collection. This property usually returns an IUnknown interface
pointer to an object that implements the IEnumVariant interface.
The IEnumVariant interface provides all the methods that you need
(such a Next, Skip, Reset, Clone) for
enumerating a collection containing VARIANTs. The Item
property is assigned a DISPID of DISPID_VALUE(0) to indicate that
this is the default property to be used, if the property name is omitted.
The Item Property allows you to locate an item in the collection
using an index. The index itself can be any type based on your business
model. For example, a Book Collection could provide an ISBN
Number as a string for its Item Index that could act as a Key Value in an
STL map to locate the corresponding book. In the ice cream menu example,
we use an Index of type long to locate an ice cream at a
specified index. Also, since we have modeled our collection based on an
STL vector, an index of type long is convenient
to access a specific element in the vector. The base class
ICollectionOnSTLImpl<> that our IceCreamMenu
collection component derives from provides us with a default
implementation of Item based on an index of type long.
The Count property returns the number of elements in the
collection. All 3 of them are read-only properties. Other than these
properties, you could add any number of helper methods that allow you to
add, remove and update elements in your collection. Take a look at the
code for the IceCreamMenu collection component below, coded using
ATL.

.......

//forward definition
class _CopyPolicyIceCream;

// Define an STL vector to hold all the Icecream flavors
typedef vector<_bstr_t> ICECREAM_MENU_VECTOR;

// Define a COM Enumerator based on our ICECREAM_MENU_VECTOR
typedef CComEnumOnSTL< IEnumVARIANT, &IID_IEnumVARIANT, VARIANT,
_CopyPolicyIceCream, ICECREAM_MENU_VECTOR > VarEnum;

// Collection Class Helper for STL based containers
typedef ICollectionOnSTLImpl< IIceCreamMenu, ICECREAM_MENU_VECTOR, VARIANT,
_CopyPolicyIceCream, VarEnum > IceCreamCollectionImpl;

// Simulate Deep copy semantics for the elements in our collection
class _CopyPolicyIceCream
{
public:

static HRESULT copy(VARIANT* pVarDest,_bstr_t* bstrIceCreamFlavor)
{
// Assign to a CComVariant
CComVariant varFlavor((TCHAR *)(*bstrIceCreamFlavor));

// Perform a deep copy
return ::VariantCopy(pVarDest,&varFlavor);
}

static void init(VARIANT* pVar)
{
pVar->vt = VT_EMPTY;
}

static void destroy(VARIANT* pVar)
{
VariantClear(pVar);
}

};

// Begin IceCreamMenu Class

class ATL_NO_VTABLE CIceCreamMenu :
public CComObjectRootEx< CComSingleThreadModel >,
public CComCoClass< CIceCreamMenu, &CLSID_IceCreamMenu >,
public ISupportErrorInfo,
public IDispatchImpl< IceCreamCollectionImpl, &IID_IIceCreamMenu,
&LIBID_ICECREAMPARLORLib, 1, 0 >
{
public:

...........

// IIceCreamMenu
public:
STDMETHOD(AddFlavortoMenu)(/*[in]*/ BSTR bstrNewFlavor);

// These three methods are not required because the
// base class ICollectionOnSTLImpl<> provides us with a default
// implementation.

// STDMETHOD(get_Item)(/*[in]*/ VARIANT Index, /*[out, retval]*/ VARIANT *pVal);
// STDMETHOD(get__NewEnum)(/*[out, retval]*/ LPUNKNOWN *pVal);
// STDMETHOD(get_Count)(/*[out, retval]*/ long *pVal);

};

The ICollectionOnSTLImpl<> class that the
CIceCreamMenu class extends provides the default implementation
for the Item,Count, and _NewEnum collection
properties. The underlying collection type that it represents (in our
case, a vector containing _bstr_t strings) is denoted by
the m_coll instance. To add items to the collection, you just
need to populate the m_coll with the elements in your collection.
This is what the FinalConstruct attempts to do by adding some
icecream flavors into the vector< _bstr_t > represented by
m_coll.

.......

HRESULT CIceCreamMenu::FinalConstruct()
{
// Fill up the menu with some flavors
m_coll.push_back(_bstr_t(_T("Chocolate Almond Fudge")));
m_coll.push_back(_bstr_t(_T("Peach Melba")));
m_coll.push_back(_bstr_t(_T("Black Currant")));
m_coll.push_back(_bstr_t(_T("Strawberry")));
m_coll.push_back(_bstr_t(_T("Butterscotch")));
m_coll.push_back(_bstr_t(_T("Mint Chocolate Chip")));
return S_OK;
}

STDMETHODIMP CIceCreamMenu::AddFlavortoMenu(BSTR bstrNewFlavor)
{
m_coll.push_back(_bstr_t(bstrNewFlavor));
return S_OK;
}

Creating
a COM collection Component using VB :
For the benefit of the VB
folks, here's an equivalent implementation of the IceCreamMenu
COM collection class in VB. Be sure to tag the NewEnum
function with a DISPID of -4 (DISPID_NEWENUM). You can do
this by using the Tools->Procedure Attributes dialog box in
the VB IDE. You need to set the Procedure ID for NewEnum
to -4 and also make sure that you turn on the Hide this
member attribute check box.

Option Explicit
Private mIceCreamFlavors As Collection

Private Sub Class_Initialize()
Set mIceCreamFlavors = New Collection
mIceCreamFlavors.Add "Chocolate Almond Fudge"
mIceCreamFlavors.Add "Peach Melba"
mIceCreamFlavors.Add "Black Currant"
mIceCreamFlavors.Add "Strawberry"
mIceCreamFlavors.Add "Butterscotch"
mIceCreamFlavors.Add "Mint Chocolate Chip"
End Sub

Public Function Count() As Integer
Count = mIceCreamFlavors.Count
End Function

Public Function Item(varIndex As Variant) As String
Item = mIceCreamFlavors(varIndex)
End Function

Public Function NewEnum() As IEnumVARIANT
Set NewEnum = mIceCreamFlavors.[_NewEnum]
End Function

Public Function AddFlavortoMenu(strNewFlavor As String)
mIceCreamFlavors.Add strNewFlavor
End Function

Consuming
COM Collections in a .NET Application :
To get a .NET application
to consume the collection COM component that we just coded in the last
section, we will need to generate the .NET metadata proxy from the
component's typelibrary. You can do this using the following command from
the command-line:

tlbimp IceCreamParlor.tlb
/out:IceCreamMenuMetadata.dll

Now open the IceCreamMenuMetadata.dll using the IL
Disassembler (ILDASM) and take a look at the methods generated for
the IceCreamMenu class.

Metadata proxy generated by TLBIMP for the IceCreamMenu
collection component

The IceCreamMenu class implements two interfaces: the
IIceCreamMenu interface and the
System.Collections.IEnumerable interface. Implementing the
IEnumerable interface tells consumers that the class allows you
to enumerate through elements in its collection. The
IIceCreamMenu interface in the metadata proxy object has the
Count and Item property preserved from the COM
component's IIceCreamMenu interface. But what has TLBIMP done to
the _NewEnum property that represents our collection's
enumerator?. It has replaced that with the GetEnumerator() method
that returns the IEnumerator interface of the object that handles
the actual enumeration.

Consuming COM collections in .NET Applications

Since the IceCreamMenu class implements the
IEnumerable interface, you could use extremely simple constructs
such as C#'s foreach statement to enumerate through elements in
such a collection. Here's how you could consume the IceCreamMenu
collection COM component from your .NET application.

using System;
using IceCreamMenuMetadata;

public class IceCreamMenuClient
{
public static void Main(String[] args)
{
// Create an instance of the Collection class
IceCreamMenu menu = new IceCreamMenu();

// Add a few more flavors to the Menu
menu.AddFlavortoMenu("Blueberry");
menu.AddFlavortoMenu("Chocolate Chip");

// Use the foreach statement to iterate through
// elements in the collection
foreach(Object objFlavor in menu)
{
System.Console.WriteLine("{0}",objFlavor);
}

}/* end Main */

}/* end class IceCreamMenuClient */

You can compile the above code using the following command-line:

csc /target:exe
/r:IceCreamMenuMetadata.dll /out:IceCreamMenuClient.exe
IceCreamMenuClient.cs

Here's the output that you get:

Chocolate Almond Fudge
Peach
Melba
Black Currant
Strawberry
Butterscotch
Mint
Chocolate Chip
Blueberry
Chocolate
Chip

Take a look at how easy it is to use C#'s foreach construct to
iterate through the elements in the collection. Again, the RCW powers the
enumeration under the covers by translating IEnumVARIANT based
COM Collection semantics into a representation that can be serviced by
IEnumerator based methods and frees us from all the marshaling
rigmarole.

Enumerating elements in a .NET Collection

The IEnumerable and the IEnumerator interfaces
are the primary enablers for enumerating collections in the .NET
world. As mentioned earlier, implementing the IEnumerable
interface tells consumers that the object allows you to enumerate
through elements in its collection The IEnumerator
interface consists of two methods: MoveNext and
Reset, and one property: Current, that needs to be
implemented by the object that provides the Enumerator for the
collection. If your class is based on a simple collection such as an
array, you need to just make an index move back and forth across the
array for the MoveNext implementation. You would need to
reset the index to point to the start of the array in the
implementation of Reset and would need to return the array
element at the current index position for the Current
property's implementation. Take a look at this example below so that
things become a little clearer as to what these two interfaces are
required to do if you were to implement a .NET class that modeled a
collection and allowed enumeration.

using System;
using System.Collections;

public class SevenDwarfs : IEnumerable , IEnumerator
{
private int nCurrentPos = -1;
private string[] strArrayDwarfs = new String[7] {"Doc", "Dopey", "Happy", "Grumpy",
"Sleepy", "Sneezy" , "Bashful"};
SevenDwarfs() {}

// Method : IEnumerable.GetEnumerator
// Return an appropriate Enumerator for the collection
public IEnumerator GetEnumerator()
{
return (IEnumerator)this;
}

// Method : IEnumerator.MoveNext
// Move the enumerator to the next element in the collection
// and return boolean status whether we still have elements to
// enumerate
public bool MoveNext()
{
if(nCurrentPos < strArrayDwarfs.Length - 1)
{
nCurrentPos++;
return true;
}
else
{
return false;
}
}

// Method : IEnumerator.Reset
// Reset the enumerator to the beginning of the collection
public void Reset()
{
nCurrentPos = -1;
}

// Method : IEnumerator.Current
// Return the element at the current enumerator position
public object Current
{
get
{
return strArrayDwarfs[nCurrentPos];
}
}

public static void Main(String[] args)
{
// Create an instance of the SevenDwarfs object
SevenDwarfs SnowWhitesDwarfs = new SevenDwarfs();

// Enumerate through the Collection
foreach(string dwarf in SnowWhitesDwarfs)
{
System.Console.WriteLine("{0}",dwarf);
}

}/* end Main */

}/* end class SevenDwarfs */

You can compile the above code using the following
command-line:

csc /target:exe /out:SevenDwarfs.exe
SevenDwarfs.cs

Here's the output that you get back:

Doc
Dopey
Happy
Grumpy
Sleepy
Sneezy
Bashful

Mapping method parameter keywords in C# to IDL's Directional
attributes :

There are certain rules that the Interop uses when mapping method
parameter keywords in C# such as out, ref to their
corresponding directional attributes such as [in], [out], [in,out],
[out,retval] and vice versa
(1) When the method parameter is
not qualified by a keyword in C#, it usually
gets mapped to the [in] attribute in IDL
assuming pass-by-value semantics.
(2) The return
value from the C# method is always mapped to the
[out, retval] directional attribute in
IDL.
(3) The ref method parameter keyword
gets mapped to the [in,out] directional
attribute in IDL.
(4) The out method
parameter keyword gets mapped to an [out]
directional attribute in IDL.
(5) Errors that occur in the .NET world
are not returned using the return value of the method. But are instead
thrown as exceptions.
Read more about error handling here.

Here are a few examples of how C# parameter types map to the
directional attributes in IDL:

C#
Method

IDL
Equivalent

Calling semantics in
C#

public void Method(String strInput);

HRESULT Method([in] BSTR strInput);

obj.Method("Hello There");

public String Method();

HRESULT Method([out, retval] BSTR* pRetVal);

String strOutput = obj.Method();

public String Method(ref String
strPassAndModify);

HRESULT Method([in, out] BSTR*
strPassAndModify, [out, retval] BSTR*
pRetVal);

String strHello = "Hello There";
String strOutput =
obj.Method(ref strHello);

public String Method(out String
strReturn);

HRESULT Method([out] BSTR* strReturn,
[out, retval] BSTR* pRetVal);

//Need not initialize strHello
String strHello;
String
strOutput = obj.Method(out
strHello);

public String Method(String strFirst,
out String strSecond,
ref String strThird);

HRESULT Method([in] BSTR bstrFirst,
[out] BSTR* strSecond,[in,
out] BSTR* strThird, [out,
retval] BSTR* pRetVal);

String strFirst = "Hi There";
String strSecond;
String
strThird = "Hello World";
String strOutput =
obj.Method(strFirst,out
strSecond,ref
strThird);

Reusing Classic COM Components in Managed code
:

One of the nice features of the interop is that you can have your
managed .NET class use the inheritance or containment
models to reuse functionality provided by an existing COM component. The
beauty of this that a .NET application consuming a managed .NET component
never gets to know that the managed component is internally leveraging
unmanaged code implementation from Classic COM components. We'll take a
look at some of the ways that a managed .NET component can reuse an
existing COM Component:

Mixed-Mode
inheritance

Mixed-Mode
containment

We call this mixed mode because we
have managed classes reusing code and functional logic already available
in unmanaged COM components.

Reuse Mechanisms in Classic COM :

Classic COM has never subscribed to the idea of
implementation inheritance but only played by interface
inheritance. The traditional reuse mechanisms in COM have been
to use containment and aggregation

Component reuse
through Containment in Classic COM

Just to refresh your memory, Containment allows you to
expose an outer component that totally subsumes the inner component
within itself. Only the outer component's interface is ever visible
to clients. The methods exposed by the outer component's interface
usually handle the implementation themselves and/or delegate the
work to the inner component when needed. The outer component creates
an instance of the inner component and forwards the calls to the
inner component when it needs to leverage some of the functionality
exposed by the inner component. From the client's perspective, it
never knows that there is an inner component shielded by the outer
component that does work for the outer component.

Component reuse
through Aggregation in Classic COM

In the case of Aggregation, the outer object no longer
forwards calls to the inner component. Instead, it allows the client
to party directly with the inner component by handing over the inner
component's interface pointer to it. The outer component no longer
gets to intercept method calls on the inner component's interface
because the client directly interacts with the inner component. The
inner component uses a default IUnknown (Non-delegating
unknown) implementation if it is not being aggregated
and uses the Outer Component's IUnknown implementation
(Controlling/Delegating unknown) if it's being aggregated. This
ensures that the client always gets the IUnknown interface
pointer of the outer component and never the non-delegating
IUnknown interface pointer of the inner
component. Again, from the client's perspective, it does not know
that there's an inner component. The client thinks that the inner
component's interface is just another interface exposed by the outer
component.

Let's take a look at the various ways in which you could reuse
unmanaged code in existing COM components from .NET classes:

Reuse through Mixed-mode inheritance
:

In this reuse model, you can have your managed .NET class
extend/inherit from an unmanaged COM coclass. In
addition to that, the managed class has the option of overriding
methods in the COM coclass' interface or accept the base COM
coclass' implementation. This is a very powerful model where you
get to mix both managed and unmanaged implementations
within the same class.

Inheriting unmanaged code from COM Components in
managed classes

Let's take a look at the code snippet below to understand this a little
better. We'll create a COM Object called Flyer using ATL that
supports an interface called IFlyer with two methods,
TakeOff() and Fly(). Here's the IDL declaration for the
component:

[
....
]
interface IFlyer : IDispatch
{
[id(1), helpstring("method TakeOff")]
HRESULT TakeOff([out,retval] BSTR* bstrTakeOffStatus);
[id(2), helpstring("method Fly")]
HRESULT Fly([out,retval] BSTR* bstrFlightStatus);
};

[
.....
]
coclass Flyer
{
[default] interface IFlyer;
};

Here's the implementation of the two methods:

STDMETHODIMP CFlyer::TakeOff(BSTR *bstrTakeOffStatus)
{
*bstrTakeOffStatus =
_bstr_t(_T("CFlyer::TakeOff - This is COM taking off")).copy();
return S_OK;
}

STDMETHODIMP CFlyer::Fly(BSTR *bstrFlyStatus)
{
*bstrFlyStatus =
_bstr_t(_T("CFlyer::Fly - This is COM in the skies")).copy();
return S_OK;
}

Before this component can be consumed by managed code, you'll have to
generate the metadata proxy for this component from its typelibrary. To do
that, you need to issue the following command from the command-line:

tlbimp MyFlyer.tlb
/out:MyFlyerMetadata.dll

We'll now create managed classes that inherit from this component using
the usual inheritance semantics, so that the component's functionality can
be reused. One of the managed classes, Bird, inherits from the
metadata type corresponding to the Flyer COM object. This means
that it would inherit all the methods of the Flyer
COM component. The other managed class, Airplane, overrides
the TakeOff and Fly methods with its own implementation.
One caveat here is that you cannot selectively override only specific
methods from the COM coclass in your managed code. If you decide to
override a single method in the COM coclass in your managed code, you'd
have to override the rest of the methods as well. In other words, you
cannot provide an overriden implementation just for the TakeOff
method and implicitly have the managed class use the Fly
implementation from the COM object. You would need to override the
Fly method as well in the managed class and provide an
implementation for it. If you need to reuse the COM coclass'
implementation, you could call base.Fly from the managed class'
Fly implementation. You might get away with selectively
overriding specific methods during compile time. But at runtime, you'd end
up running into a System.TypeLoadException exception with an
error message that reads something like: 'Types extending from COM
objects should override all methods of an interface implemented by the
base COM class'.

using System;
using MyFlyerMetadata;

// Inherit from the metadata type representing
// the unmanaged COM component
// Use the COM Component's implementation
// of TakeOff & Fly (Use the base class' implementation)
public class Bird : Flyer
{

}/* end class Bird */

// Inherit from the metadata type representing
// the unmanaged COM component
// Override the COM object's method implementations in our
// derived managed-class.
// (Also call base class implementation when necessary using base.MethodName())
public class Airplane : Flyer
{

// Override the COM Component's Flyer::TakeOff implementation
// with our own implementation
public override String TakeOff() {

return "Airplane::TakeOff - This is .NET taking off";

}/* end TakeOff */

// Override the COM Component's Flyer::Fly implementation
// with our own implementation
public override String Fly() {

// Can call the base class' implementation too if you
// wish to.
System.Console.WriteLine(base.Fly());
return "Airplane::Fly - This is .NET in the skies";

}/* end Fly */

}/* end class Airplane */

public class FlightController
{
public static void Main(String[] args)
{
Bird falcon = new Bird();
System.Console.WriteLine("BIRD: CLEARED TO TAKE OFF");
System.Console.WriteLine(falcon.TakeOff());
System.Console.WriteLine(falcon.Fly());

Airplane skyliner = new Airplane();
System.Console.WriteLine("AIRPLANE: CLEARED TO TAKE OFF");
System.Console.WriteLine(skyliner.TakeOff());
System.Console.WriteLine(skyliner.Fly());

}/* end Main */

}/* end FlightController */

You can compile the above program using the following command in the
DOS command-line:

csc /target:exe
/out:FlightClient.exe /r:MyFlyerMetadata.dll
FlightClient.cs

Running the program above, gives you the following output:

BIRD: CLEARED TO TAKE
OFF
CFlyer::TakeOff - This is COM taking off
CFlyer::Fly -
This is COM in the skies
AIRPLANE: CLEARED TO TAKE
OFF
Airplane::TakeOff - This is .NET taking off
CFlyer::Fly -
This is COM in the skies
Airplane::Fly - This is .NET in the
skies

Consumers of the Bird and Airplane managed classes
are shielded from having to know that these classes are actually reusing
existing COM components via inheritance. Whenever necessary, the
managed class overrides all the methods in the COM component with its own
implementation. This reuse model, where managed code inherits from
unmanaged code is called the Mixed-mode inheritance reuse model.

Reuse through Mixed-mode Containment
:

In this model, the managed class uses the same principles of
containment in Classic COM. All it does is, stash away an
instance of metadata proxy class representing the unmanaged COM
component as a member. Whenever it requires the services of the COM
component, it forwards a request to the component's methods.

Reusing unmanaged COM code through
Containment/Composition

The managed class has the ability to inject its own code before and
after the call to the contained COM component. Here's an example:

using System;
using MyFlyerMetadata;

.....

// Contains an instance of the metadata type representing
// the unmanaged COM Component
public class HangGlider
{
private Flyer flyer = new Flyer();

// Forward the call to the contained class' implementation
public String TakeOff()
{
return flyer.TakeOff();
}

// Forward the call to the contained class' implementation
public String Fly()
{
// Do what you need to do before or after fowarding the
// call to flyer
System.Console.WriteLine("In HangGlider::Fly - Before delegating to flyer.Fly");
return flyer.Fly();
}

}/* end class HangGlider */

public class FlightController
{
public static void Main(String[] args)
{
....

HangGlider glider = new HangGlider();
System.Console.WriteLine("HANGGLIDER: CLEARED TO TAKEOFF");
System.Console.WriteLine(glider.TakeOff());
System.Console.WriteLine(glider.Fly());

}/* end Main */

}/* end FlightController */

Here's the output of the above program:

HANGGLIDER: CLEARED TO
TAKEOFF
CFlyer::TakeOff - This is COM taking off
In
HangGlider::Fly - Before delegating to flyer.Fly
CFlyer::Fly -
This is COM in the skies

In the example above, the HangGlider class creates an instance
of the Flyer COM component and stores it away as a private
member. Whenever a method call arrives that requires the Flyer
component's services, it calls into the component using the private
instance that it had stashed away earlier. Also, the HangGlider
class has the liberty to inject code before and after a call is delegated
to the Flyer component's methods. This would not be possible with
Mixed-mode inheritance reuse model unless you override
all the base class COM methods in your managed class.

Understanding COM Threading models & Apartments from a .NET
application's perspective:

I remember that when I first started programming with COM, I had
not yet ventured into the murky waters of COM Threading models and
apartments and had little knowledge of what they really were. I thought it
was cool that my object was free threaded and simply assumed that it would
be the best performing threading model. Little did I realize, what was
happening under the covers. I never knew the performance penalties that
would be incurred when an STA client thread created my
MTA object. Also, since my object was not thread safe, I never
knew I would be in trouble when concurrent threads accessed my object.
Truly at that time, ignorance of COM threading models was bliss. Well,
that bliss was only ephemeral and my server started crashing unexpectedly.
It was then that I was forced to get my feet wet in the COM Threading
model waters and learn how each of those models behaved, how COM managed
apartments, and what were the performance implications that arose when
calling between two incompatible Apartments. As you know, before a thread
can call into a COM object, it has to declare its affiliation to an
apartment by declaring whether it will enter an STA or
MTA. STA client threads call CoInitialize(NULL)
or CoInitializeEx(0, COINIT_APARTMENTTHREADED) to enter an
STA apartment and MTA threads call CoInitializeEx(0,
COINIT_MULTITHREADED) to enter an MTA. Similarly, in the
.NET managed world, you have the option of allowing the calling thread in
the managed space declare its apartment affinity. By default, the calling
thread in a managed application chooses to live in a MTA. It's as
if the calling thread initialized itself with CoInitializeEx(0,
COINIT_MULTITHREADED).But think about the overhead and the
performance penalties that would be incurred if it were calling a classic
STA COM component that was designed to be apartment
threaded. The incompatible apartments would incur the overhead of an
additional proxy/stub pair and this is certainly a performance
penalty. You can override the default choice of Apartment for a managed
thread in a .NET application by using the ApartmentState property
of the System.Threading.Thread class. The ApartmentState property
takes one of the following enumeration values: MTA, STA and
Unknown. The ApartmentState.Unknown is equivalent to the
default MTA behavior. You will need to specify the ApartmentState
for the calling thread before you make any calls to the COM object. It's
not possible to change the ApartmentState once the COM object has been
created. Therefore, it makes sense to set the thread's
ApartmentState as early as possible in your code.

// Set the client thread's ApartmentState to enter an STA
Thread.CurrentThread.ApartmentState = ApartmentSTate.STA;

// Create our COM object through the Interop
MySTA objSTA = new MySTA();
objSTA.MyMethod()

As an alternative method, you can tag your managed client's
Main entry point method with the STAThreadAttribute or
the MTAThreadAttribute to start it up with the desired threading
affiliation for consuming COM components. For example, take a look at the
code snippet below:

public class HelloThreadingModelApp {

.....

[STAThread]
static public void Main(String[] args) {

System.Console.WriteLine("The apartment state is: {0}",
Thread.CurrentThread.ApartmentState.ToString());

}/* end Main */

}/* end class */

The output that you'd get would be something like:

The apartment state is:
STA

If the MTAThread attribute is set, then the
ApartmentState would be set to MTA. If no thread state
attribute is specified in the client's Main entry point or if the
ApartmentState property is not set for the thread from which the
COM component is created, then the ApartmentState would be
Unknown, which defaults to MTA behavior.

Part II: Consuming .NET Components from COM aware
clients:

In this section, we'll see how we can consume managed .NET
components from unmanaged COM aware clients. Limiting
clients for .NET Components to only managed clients
would've been a tough pill to swallow for most developers who have spent a
great deal of time over the years developing applications that can't be
ported overnight to managed code. The .NET framework allows disparate
applications in different platforms to talk to managed applications using
wire protocols like SOAP. Unmanaged COM aware clients still get
easier ways to talk to managed components. The .NET runtime allows
unmanaged COM aware clients to seamlessly access .NET Components
through the COM Interop and through the tools provided by the framework.
This ensures that COM aware clients can talk to .NET components, as if
they were talking to plain-vanilla Classic COM Components.

Creating a .NET Component :

To begin with, let's put together a simple .NET Component that allows
you to look up the temperature at your city. Only classes that are
public are added to the typelibrary and exposed to COM aware
clients. Also, if the class needs to be creatable from a COM aware client,
it needs to have a public default constructor. A
public class that does not have a public
default constructor still appears in the typelibrary, although it
cannot be directly co-creatable from COM. The
Temperature Component has two methods
DisplayCurrentTemperature and GetWeatherIndications. It
has a public read-write property called Temperature defined with
the corresponding get/set methods.

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

public enum WeatherIndications
{
Sunny = 0,
Cloudy,
Rainy,
Snowy
}

[ClassInterface(ClassInterfaceType.AutoDual)]
public class TemperatureComponent
{
private float m_fTemperature = 0;

// Public Constructor
public TemperatureComponent()
{
m_fTemperature = 30.0f;
}

//Public Property Accessors (Defines get/set methods)
public float Temperature
{
get { return m_fTemperature; }

set { m_fTemperature = value;}

}/* end Temperature get/set property */

// Public Method that displays the Current Temperature
public void DisplayCurrentTemperature()
{
String strTemp = String.Format("The current temperature at Marlinspike is : " +
"{0:####} degrees fahrenheit", m_fTemperature);

MessageBox.Show(strTemp,"Today's Temperature");

}/* end DisplayCurrentTemperature */

// Another public method that returns an enumerated type
public WeatherIndications GetWeatherIndications()
{
if(m_fTemperature > 70) {

return WeatherIndications.Sunny;
}
else {

// Let's keep this simple and just return Cloudy
return WeatherIndications.Cloudy;
}

}/* end GetWeatherIndications */

}/* end class Temperature */

You will also notice that there is an attribute called
ClassInterface that is tagged to the Temperature class
with its value set to ClassInterfaceType.AutoDual. We'll see the
significance of applying this attribute in the section, Snooping
in on the generated Typelibrary. For now, think of this as a way to
tell typelibrary generation tools like REGASM.EXE and
TLBEXP.EXE to export the public members of the .NET Component's
class into a default Class Interface in the generated
typelibrary. Also remember that using a Class Interface to expose
the public methods of a .NET class is not generally recommended because
it's creedless to COM versioning. We'll take a look at how we can use
interfaces explicitly to achieve the same thing. Defining an interface
explicitly, deriving your .NET Component class from this interface and
then implementing the interface's methods in your .NET Component is the
recommended way of doing things if you are going to expose your .NET
Component to COM aware clients. We will compare and constrast these two
approaches in detail and also see why the former approach is not
recommended, in the section, Snooping
in on the generated Typelibrary.

If you are using Visual Studio.NET, you can create a
Visual C# project and use the Class Library template to
code the above component. If you are a command-line jockey, then here's
the command to build the component. This creates an assembly called
Temperature.dll

csc /target:library
/r:System.Windows.Forms.dll /out:Temperature.dll
TemperatureComponent.cs

Generating a typelibrary from the assembly &
Registering the assembly:

What you just generated now is a .NET assembly that a COM aware client
like Visual Basic 6.0 is clueless about. You need to get some COM friendly
type information from it so that our VB client will be happy to party
around with it. Earlier, you used a tool called TLBIMP (Type Library
Importer) to create a .NET metadata proxy from a COM typelibrary. You
need to do the reverse of that here. You need to take in a .NET assembly
and generate a typelibrary out of it so that it's usable from a COM aware
client. The .NET framework provides a couple of tools for this. You can
use the Type Library Exporter utility (TLBEXP.exe) or the
Assembly Registration Utility (Regasm.exe), both of which
you'll find in the Bin directory of your .NET SDK installation.
REGASM is a superset of the TLBEXP utility in that it
also does much more than generating a typelibrary. It's also used to
register the assembly, so that the appropriate
registry entries are made to facilitate the COM runtime and the .NET
runtime to hook up the COM aware client to the .NET component. We'll use
REGASM.EXE here because we can get both the assembly registration
and the typelibrary generation done in one go. But you could use
TLBEXP as well to generate the typelibrary, and then use
REGASM to register the assembly.

regasm Temperature.dll
/tlb:Temperature.tlb

The above call to REGASM.EXE makes the appropriate registry
entries and also generates a typelibrary (Temperature.tlb) from
the .NET assembly so that the typelibrary can be referenced from our VB 6
client application.

Consuming the component from a VB 6.0
Client:

Let's quickly put together a VB Form based application that creates and
invokes the .NET Component whose assembly we registered and generated a
typelibrary from. Creation of the component is the same as how you would
create a COM object. You could either reference the typelibrary and
early-bind to the Component or perform a CreateObject call using
the component's ProgID to late-bind to the component. Usually,
the ProgID generated is the fully qualified name of the class. In
our case, the ProgID generated would be TemperatureComponent. But
you could use the ProgIDAttribute
to specify an user-defined ProgID to override the default ProgID
that is generated.

Private Sub MyButton_Click()

On Error GoTo ErrHandler

' Create an instance of the temperature component
Dim objTemperature As New TemperatureComponent

' Display the current temperature
objTemperature.DisplayCurrentTemperature

' Set the temperature property
objTemperature.Temperature = 52.7

' Display the current temperature after property mutation
objTemperature.DisplayCurrentTemperature

' Check the weather indications
If (objTemperature.GetWeatherIndications() = WeatherIndications_Sunny) Then
MsgBox "Off to the beach"
Else
MsgBox "Stay at home and watch Godzilla on TV"
End If

Exit Sub

ErrHandler:

MsgBox "Error Message : " & Err.Description, _
vbOKOnly, "Error Code " & CStr(Err.Number)
End Sub

To enable the .NET Assembly Resolver to find the assembly housing your
component, you will either need to place the component in the same
directory as the application that's consuming it, or deploy
the assembly as a Shared Assembly in the Global Assembly
Cache (GAC). For now, just copy the Temperature.dll to the
same directory as your VB Client Application executable. If VB can use the
usual Classic COM based invocation mechanism and still get away with
invoking and consuming the .NET component, there's got to be a good
samaritan sitting inbetween the VB6 Client and the .NET component and
wiring the COM invocation requests to the actual .NET component. We'll
soon see, what
happens under the covers.

Shedding more light on the COM Interop wizardry
:

Let's take a peek at the registry entries that Regasm.exe made
when we registered our assembly.

Registry Entries made during Assembly
registration by REGASM

You can check your Component's CLSID in OLEVIEW.EXE
by opening the typelibrary generated by REGASM. Check for the
uuid attribute under the coclass section. If you
navigate to your HKCR\CLSID\{...Component's CLSID...} key in the
registry, you can see REGASM has made the relevant registry entries
required by COM to activate an object hosted by an Inproc server. In
addition, it has created a few other registry entries such as Class,
Assembly, and RuntimeVersion that are used by the .NET
runtime. The Inproc Server handler (indicated by the InProcServer32 key's
default value) is set to mscoree.dll, which is the core CLR
runtime execution engine. The COM runtime calls the
DllGetClassObject entry point in MSCOREE.dll (The CLR runtime).
The runtime then uses the Class ID (CLSID) passed to
DllGetClassObject to look up the Assembly and Class
keys under the InProcServer32 key to load and resolve the
.NET assembly that will service this request. The runtime also dynamically
creates a COM Callable Wrapper (CCW) proxy (a mirror image of the
RCW) to handle the interaction between unmanaged code and the
managed components. This makes COM aware clients think that they are
interacting with Classic COM components and makes .NET Components think
that they are receiving requests from a managed application. There is one
CCW created per .NET component instance.

Under the hood: Accessing .NET Components from COM aware
clients

As the saying goes, 'A picture is worth a thousand words', so let the
illustration here, do most of the talking as to what is happening under
the covers when a COM aware client interacts with a .NET component. The
primary players here are the CLR runtime and the COM Callable
Wrapper (CCW) that gets fabricated by the .NET runtime. From then on,
the CCW takes over and handles most of the spadework to get the two to
work together. The CCW handles the lifetime management issues here. COM
clients in the unmanaged realm maintain reference counts on the CCW proxy
rather than on the actual .NET component. The CCW only holds a reference
to the .NET component. The .NET Component lives by the rules of the CLR
garbage collector as any other managed type would. The CCW lives in the
unmanaged heap and is torn down when the COM aware clients no longer have
any outstanding references to the objects. Just like the RCW, the CCW is
also responsible for marshaling the method call parameters that move back
and forth between the unmanaged client and managed .NET components. It's
also responsible for synthesizing v-tables on demand. V-tables for
specific interfaces are generated dynamically. They are built lazily only
when the COM aware client actually requests a specific interface via a
QueryInterface call. Calls on the CCW proxy are eventually routed away to
a stub that actually makes the call into the managed object.

Snooping in on the generated Typelibrary:

Let's take a quick look at the kind of information that was put into
the typelibrary generated by the Assembly Registration utility
(REGASM). Open the typelibrary through OLEVIEW's Type
Library Viewer so that you can take a look at the IDL file that was
reverse engineered from the typelibrary.

// Generated .IDL file (by the OLE/COM Object Viewer)
// typelib filename: Temperature.tlb

[
uuid(A9F20157-FDFE-36D6-90C3-BFCD3C8C8442),
version(1.0)
]
library Temperature
{
// TLib : Common Language Runtime Library : {BED7F4EA-1A96-11D2-8F08-00A0C9A6186D}
importlib("mscorlib.tlb");
// TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
importlib("STDOLE2.TLB");

// Forward declare all types defined in this typelib
interface _TemperatureComponent;

typedef [uuid(0820402E-B8B6-330F-8D56-FF079E5B4659), version(1.0),
custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "WeatherIndications")]
enum {
WeatherIndications_Sunny = 0,
WeatherIndications_Cloudy = 1,
WeatherIndications_Rainy = 2,
WeatherIndications_Snowy = 3
} WeatherIndications;

[
uuid(01FAD74C-3DC4-3DE0-86A9-8490FAEE8964),
version(1.0),
custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "TemperatureComponent")
]
coclass TemperatureComponent {
[default] interface _TemperatureComponent;
interface _Object;
};

[
odl,
uuid(C51D54FA-7C81-35A5-9998-3963EAB4AA12),
hidden,
dual,
nonextensible,
oleautomation,
custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "TemperatureComponent")
]
interface _TemperatureComponent : IDispatch {
[id(00000000), propget]
HRESULT ToString([out, retval] BSTR* pRetVal);
[id(0x60020001)]
HRESULT Equals([in] VARIANT obj,[out, retval] VARIANT_BOOL* pRetVal);
[id(0x60020002)]
HRESULT GetHashCode([out, retval] long* pRetVal);
[id(0x60020003)]
HRESULT GetType([out, retval] _Type** pRetVal);
[id(0x60020004), propget]
HRESULT Temperature([out, retval] single* pRetVal);
[id(0x60020004), propput]
HRESULT Temperature([in] single pRetVal);
[id(0x60020006)]
HRESULT DisplayCurrentTemperature();
[id(0x60020007)]
HRESULT GetWeatherIndications([out, retval] WeatherIndications* pRetVal);
};
};

If you take a look at the coclass section, it specifies the
default interface as the Class Name prefixed by an _ (underscore)
character. This interface is called the Class Interface and its
methods comprise all the non-static public methods,
fields, and properties of the class. The Class
Interface gets generated because you tagged your .NET class with the
ClassInterface attribute. This attribute tells typelibrary
generation tools such as RegAsm.exe and TlbExp.exe to
generate a default interface known as the Class Interface and add
all the public methods, fields, and properties of the
class into it, so that it could be exposed to COM aware clients.

If
you do not tag the ClassInterface attribute to a .NET
Component's class, a default Class Interface is still
generated. But in this case, it's an IDispatch based
Class Interface that does not include any type information
for the methods exposed nor their DISPIDs. This type of
Class Interface is only available to late binding clients.
This effect is the same as that of applying the ClassInterfaceType.AutoDispatch
value to the ClassInterface attribute. The advantage of the
AutoDispatch
option is that, since the DISPIDs are not cached and not available
as a part of the type information, they don't break existing clients
when a new version of the component is released since the DISPIDs
are obtained at runtime by clients using something like
IDispatch::GetIDsOfNames.

Only public methods are visible in the typelibrary and can be
used by COM clients. The private members don't make it into the
typelibrary and are hidden from COM Clients. The public
properties and fields of the class are transformed into
IDL propget/propput types. The Temperature property in
our example has both set and get accessor defined and
so, both the propset and propget are emitted for this
property. There is also another interface called _Object that
gets added to the coclass. The Class interface also contains 4
other methods. They are:

ToString

Equals

GetHashCode

GetType

These methods are added to the default
Class Interface because it implicitly inherits from the
System.Object class. Each one of the methods and properties
that's added to the interface gets a DISPID that is generated
automatically. You can override this DISPID with a user defined one by
using the DispId attribute. You'll notice that the
ToString method has been assigned a DISPID of 0 to
indicate that it is the default method in the class Interface. This means
that if you leave out the method name, the ToString method will
be invoked.

' Create an instance of the temperature component
Dim objTemperature As New TemperatureComponent

' Invoke the ToString method (the default method)
MsgBox objTemperature

Let's examine the various ways in which we can facilitate the
generation of the implicit Class Interface. We'll start by taking
a look at the effect of applying the ClassInterfaceType.AutoDual
value to the ClassInterface attribute.

[ClassInterface(ClassInterfaceType.AutoDual)]
public class TemperatureComponent
{
....
}

Notice that the value (positional parameter value) assigned to
the ClassInterface attribute is
ClassInterfaceType.AutoDual. This option tells typelibrary
generation tools (like RegAsm.exe) to generate the Class
Interface as a dual interface and export all the type
information (for the methods, properties etc. and their corresponding
Dispatch IDs) into the typelibrary. Imagine what would happen if you
decided that you want to add another public method to the class. This
mutates the Class Interface that gets generated and breaks the
fundamental interface immutabilty law in COM because the
v-table's structure changes now. Late Bound clients also have their share
of woes when they try to consume the component. The Dispatch IDs (DISPIDs)
get regenerated because of the addition of the new method and this breaks
late bound clients too. As a general rule, using
ClassInterfaceType.AutoDual is evil since it is totally agnostic
about COM Versioning. Let's take a look at the next possible value that
you can set for your ClassInterface attribute. Tagging your
ClassInterface attribute with a value of
ClassInterfaceType.AutoDispatch forces typelibrary generation
tools to avoid generating type information in the typelibrary. So, if you
had your TemperatureComponent class tagged with the
ClassInterface attribute as shown below:

[ClassInterface(ClassInterfaceType.AutoDispatch)]
public class TemperatureComponent
{
.....
}

then, the corresponding typelibrary generated by RegAsm.exe
would have an IDL structure such as this:

// Generated .IDL file (by the OLE/COM Object Viewer)
// typelib filename: Temperature.tlb

[
uuid(A9F20157-FDFE-36D6-90C3-BFCD3C8C8442),
version(1.0)
]
library Temperature
{
......

[
uuid(01FAD74C-3DC4-3DE0-86A9-8490FAEE8964),
version(1.0),
custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "TemperatureComponent")
]
coclass TemperatureComponent {
[default] interface IDispatch;
interface _Object;
};
};

You will notice that the default interface is an IDispatch
interface and neither the DISPIDs nor the method type
information is present in the Typelibrary. This leaves the COM aware
client to consume .NET Components using only late-binding. Also, since the
DISPID details are not stored as a part of the type
information in the typelibrary, the clients obtain these
DISPIDs on demand using something like
IDispatch::GetIDsOfNames. This allows clients to use newer
versions of the components without breaking existing code. Using
ClassInterfaceType.AutoDispatch is much safer than using
ClassInterfaceType.AutoDual because it does not break existing
client code when newer versions of the component are released, albeit the
former allows only late binding. The recommended way of modelling your
.NET component to be exposed to COM aware clients is to do away with the
Class Interface itself and instead, explicitly factor out the
methods you are exposing into a seperate interface, and have the .NET
component implement that interface. Using a Class Interface is a
quick and easy way to get your .NET component exposed to COM aware
clients. But it's not the recommended way. Let's try rewriting our
TemperatureComponent by factoring out the methods explicitly into
an interface and see how the typeLibrary generation differs:

// Define the ITemperature interface
public interface ITemperature {

float Temperature { get; set; }
void DisplayCurrentTemperature();
WeatherIndications GetWeatherIndications();

}/* end interface ITemperature */

// (1) Implement the ITemperature interface in the TemperatureComponent class
// (2) Set the ClassInterfaceType for the ClassInterface atrribute to ClassInterfaceType.None

[ClassInterface(ClassInterfaceType.None)]
public class TemperatureComponent : ITemperature
{
......

//Implement the methods in your class

// Property Accessors (Defines get/set methods)
public float Temperature
{
get { return m_fTemperature; }
set { m_fTemperature = value;}

}/* end Temperature get/set property */

// Displays the Current Temperature
public void DisplayCurrentTemperature() {
.....
}/* end DisplayCurrentTemperature */

// Returns an enumerated type indicating weather condition
public WeatherIndications GetWeatherIndications() {
....
}/* end GetWeatherIndications */

}/* end class Temperature */

Here's how the corresponding IDL file looks like for the generated
typelibrary. Notice that the TemperatureComponent class' default
interface is now the ITemperature interface that was implemented
by the class.

// Generated .IDL file (by the OLE/COM Object Viewer)
// typelib filename: Temperature.tlb

[
uuid(A9F20157-FDFE-36D6-90C3-BFCD3C8C8442),
version(1.0)
]
library Temperature
{
......

[
odl,
uuid(72AA177B-C6B2-3694-B083-4FF535B40AD2),
version(1.0),
dual,
oleautomation,
custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "ITemperature")
]
interface ITemperature : IDispatch {
[id(0x60020000), propget]
HRESULT Temperature([out, retval] single* pRetVal);
[id(0x60020000), propput]
HRESULT Temperature([in] single pRetVal);
[id(0x60020002)]
HRESULT DisplayCurrentTemperature();
[id(0x60020003)]
HRESULT GetWeatherIndications([out, retval] WeatherIndications* pRetVal);
};

[
uuid(01FAD74C-3DC4-3DE0-86A9-8490FAEE8964),
version(1.0),
custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "TemperatureComponent")
]
coclass TemperatureComponent {
interface _Object;
[default] interface ITemperature;
};
};

This approach of factoring out the methods of the .NET class explicitly
into an interface and having the class derive from the interface and
implement it, is the recommended way for exposing your .NET Components to
COM aware clients. The ClassInterfaceType.None option tells the
typelibrary generation tools that you do not require a Class
Interface. This ensures that the ITemperature interface is
the default interface. Were you to not specify the
ClassInterfaceType.None value for the Class Interface attribute, then the
Class Interface would have been made the default
interface. Here's the gist of what we learnt in this section:

Class Interfaces with ClassInterfaceType.AutoDual
are COM version agnostic. Try to avoid using them.

Class Interfaces with
ClassInterfaceType.AutoDispatch do not export type
information and DISPIDs to the typelibrary. They are
COM versioning friendly. They can be accessed from COM
aware clients only through late-binding.

Class Interfaces are a hack. Try to avoid them if possible.
Use explicit interfaces instead.

Use ClassInterfaceType.None to make your explicit
interface the default interface.

Another interesting observation is the way REGASM and
TLBEXP generate a mangled method name in the IDL by appending a
'_' followed by a sequence number when you have overloaded methods in your
.NET class or interface that you are exporting to a typelibrary. For
example, if you had exposed the following interface in your .NET
Component:

public interface MyInterface
{
String HelloWorld();
String HelloWorld(int nInput);
}

Then, the IDL corresponding to the typelibrary that REGASM/TLBEXP
generated would look like this.

[
.....
]
interface MyInterface : IDispatch {

[id(0x60020000)]
HRESULT HelloWorld([out, retval] BSTR* pRetVal);

[id(0x60020001)]
HRESULT HelloWorld_2([in] long nInput,[out,retval] BSTR* pRetVal);
};

Notice the '_2' appended to the second HelloWorld method to
distinguish it from the first one in the IDL file.

Using attributes to tweak the generated typelibrary
metadata:

Knowing the rules that RegAsm.exe or TlbExp.exe
utilities use, to generate the IDL and subsequently, the typelibrary by
introspecting a .NET assembly allows you to mould the typelibrary
generation to your requirements. You could inject .NET attributes into
your assembly that give these utilities the hints they need to alter
metadata in the IDL to affect the typelibrary that gets created. For
example, you could change the interface type from dual to an
IUnknown only based custom interface or a pure
dispinterface using the InterfaceTypeAttribute. Here are
some of the ways you can inject attributes to qualify the types in your
.NET component and modify the generated typelibrary to suit your
requirements.

Altering the interface type:

By default, the interfaces used by a .NET Class are transformed to dual
interfaces in the IDL. This allows the client to get the best of both
early binding and late binding. However, there may be
occasions when you want the interface to be a pure-dispinterface
or a custom IUnknown only based interface. You can override the
default type of the interface using the InterfaceTypeAttribute.
Take a look at the example below.

[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface ISnowStorm
{
....
}

[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IHurricane
{
....
}

[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface ITyphoon
{
....
}

public interface IWeatherStatistics
{
.....
}

As seen in the above example, the InterfaceType attribute is
used to emit metadata information into the interface so that utilities
like RegAsm.exe & TlbExp.exe can use this
information to generate the appropriate type of interface in the
typelibrary. Here's how the resulting IDL would look like:

[
odl,
uuid(1423FBFA-BE13-3766-9729-9C1AAF5DB08A),
version(1.0),
dual,
oleautomation,
custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "ISnowStorm")
]
interface ISnowStorm : IDispatch {
....
};

[
odl,
uuid(D95E54B8-FABC-3BDA-AA45-AC4EFF49AF92),
version(1.0),
oleautomation,
custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "IHurricane")
]
interface IHurricane : IUnknown {
....
};

[
uuid(676B3B85-7DB8-306D-A1E9-B6AA1008EDF2),
version(1.0),
custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "ITyphoon")
]
dispinterface ITyphoon {
properties:
methods:
};

[
odl,
uuid(A1A37136-341A-3631-9275-FC7B0F0DB695),
version(1.0),
dual,
oleautomation,
custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "IWeatherStatistics")
]
interface IWeatherStatistics : IDispatch {
.....
}

As seen above, setting the InterfaceType attribute to
ComInterfaceType.InterfaceIsIUnknown results in the generation of
an IUnknown only based custom interface. Setting the
InterfaceType attribute to
ComInterfaceType.InterfaceIsIDispatch results in the generation
of a pure dispatch only dispinterface. Setting the
InterfaceType attribute to
ComInterfaceType.InterfaceIsDual or ignoring the
InterfaceType attribute altogether (such as the
IWeatherStatistics interface in the example) results in the
emission of a dual interface.

Altering the GUID and the ProgID:

GUIDs for types such as classes and interfaces are generated
automatically by the REGASM/TLBEXP utilities, when these types are
exported into a typelibrary. But, you still have your final say if you
need to assign a specific GUID to the interface or class. You can use the
Guid Attribute to specify an user defined GUID. You can also
affect the way that a ProgID gets generated for a specific class. By
default, the RegAsm.exe utility assigns the fully qualified type
name of the class for the Progid. You can use the ProgId
Attribute to assign a user specified ProgID for your coclass.

[
GuidAttribute("AD4760A9-6F5C-4435-8844-D0BA7C66AC50"),
ProgId("WeatherStation.TornadoTracker")
]
public class TornadoTracker {
.....
}

Here's how the IDL file looks like after the Guid attribute
has been used on the class:

[
uuid(AD4760A9-6F5C-4435-8844-D0BA7C66AC50),
version(1.0),
custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "TornadoTracker")
]
coclass TornadoTracker {
....
};

Notice that the uuid attribute (representing the CLSID of the
COM class) in the coclass section contains the value of the GUID
that we specified. If you check the
HCKR\CLSID\{AD4760A9-6F5C-4435-8844-D0BA7C66AC50}\ProgID Registry
Key, you should see the value WeatherStation.TornadoTracker
representing the class' ProgID.

Hiding Public types from being exposed to
COM:

You saw earlier, how all the public classes and interfaces of the class
were automatically added to the typelibrary so that they can be referenced
and used by COM aware clients. But there may be circumstances when you
need to prevent certain public interfaces and public classes from being
available to COM. You can do this using the ComVisible attribute.
Setting this attribute with a value of false prevents the type to
which it is applied from appearing in the typelibrary.

[ComVisible(false)]
public interface IWeatherStatistics
{
float GetLowestTemperatureThisMonth();
float GetHighestTemperatureThisMonth();
}

In the above example, the IWeatherStatistics interface is not
exported as type-information into the typelibrary, when the assembly is
exported to the typelibrary. If the IWeatherStatistics type is
used by any other type that's exposed to COM, then it's replaced by an
IUnknown interface in the typelibrary. For example:

HRESULT SetWeatherStatistics([in] IWeatherStatistics* pWeatherIndications);

becomes
HRESULT SetWeatherStatistics([in] IUnknown* pUnkWeatherIndications);

There's a caveat here, though. It does not make sense to apply a
[ComVisible(true)] to turn on the visibility of private
or protected members in the class because they can never be
exposed to COM.
The ComVisible attribute can be applied not
only to classes and interfaces, but to a whole slew of other types such as
assemblies, methods, fields, properties, delegates, structs etc. too. Take
a look at this example below, where we selectively hide specific methods
in an interface from being exposed to COM.
public interface IHurricaneWatch {

void AlertWeatherStations(String strDetails);

[ComVisible(false)]
void PlotCoordinates();

void IssueEvacuationOrders();
}

The above snippet of code indicates to the typelibrary generator
(RegAsm.exe/TlbExp.exe) that we intend to hide the
PlotCoordinates method of the IHurricaneWatch interface
from appearing in the generated type library. Here's how the IDL
looks like, for the typelibrary that gets generated:

[
....
]
interface IHurricaneWatch : IDispatch {

[id(0x60020000)] HRESULT AlertWeatherStations([in] BSTR strDetails);
[id(0x60020002)] HRESULT IssueEvacuationOrders();
};

Altering the Marshaling behavior for types
:

When the runtime marshals managed types into the unmanaged world, it
follows certain data type conversion rules. For example, a managed type
such as a String is always converted to a BSTR. Types
such as Strings could have more than one possible representation
in the unmanaged world such as array of ANSI characters (LPSTR),
an array of UNICODE characters (LPWSTR), or a BSTR. When
the target data type in the unmanaged realm presents more than one
possible representation of the underlying managed type, then we call such
types, Non-isomorphic types. The COM Interop converts
non-isomorphic types to a specific default target type (such as a
String always converted to a BSTR). Consider the
following piece of code in a C# class:

public void SetProductName(String strProductName) { ... }

When the above code is run through the Typelibrary exporter such as
TLBEXP or REGASM, what we get back in the IDL corresponding to the
generated typelibrary looks like this:

HRESULT SetProductName([in] BSTR strProductName);

However, there may be occasions when you need to provide alternate
representations for the types. For example, you might want to convert a
managed type such as a String to a null-terminated array of ANSI
characters (LPSTR), instead of a BSTR. This is where the
MarshalAs attribute helps. You could use the MarshalAs
attribute to control how the conversion happens between managed and
unmanaged types. So if you'd like to have the managed String
converted to a null-terminated ANSI character array as opposed to a
BSTR, you would need to apply the MarshalAs attribute to
the method parameter as shown below:
public void SetProductName( [MarshalAs(UnmanagedType.LPStr)]
String strProductName) { .... }

The resulting conversion would look something like this in the IDL
representation:

HRESULT SetProductName([in] LPSTR strProductName);

Thus, the MarshalAs attribute can be very useful when you need
to tweak data representation mappings between types in the managed and
unmanaged worlds.

Exception Handling - .NET Exceptions compared
with COM HRESULTs:

Let's take a look at how the exceptions raised by .NET components get
mapped to COM based HRESULTs. You'll notice that the return types from
.NET component methods are converted into an [out,retval] IDL
type when run through a typelibrary exporter. The actual return type for
the method in the IDL is a HRESULT that indicates a success or
failure of a method call. A failed HRESULT is usually the result of a
system exception or a user-defined exception raised because of
failed business logic. If the method call goes through fine with no
exception thrown from the .NET component, then the CCW fills in the
returned HRESULT with 0 (S_OK). If the method call fails or
business logic validation fails, the .NET component is expected to raise
an exception. This exception usually has a failure HRESULT assigned to it
and an Error description associated with it. The CCW gleans out
details such as the Error Code, Error message etc. from the .NET Exception
and provides these details in a form that can be consumed by the COM
client. It does this by implementing the ISupportErrorInfo
interface (to indicate that it supports rich error information) and the
IErrorInfo interface (through which it provides all the error
information details) or through the EXCEPINFO structure that is
passed through an IDispatch::Invoke call (in case the COM aware client was
late binding through a dispinterface). The error propagation happens
seamlessly such that .NET exceptions are mapped to their equivalent COM
counterparts and delivered to the COM Client by the CCW. Let's modify the
Temperature property's set method to raise a user
defined error if the temperature specified is not within acceptable
limits. We'll soon see how the VB Client traps this error using the usual
COM error handling mechanisms.

public class TemperatureComponent
{
private float m_fTemperature = 0;

/* Temperature Property (In Fahrenheit)*/
public float Temperature
{
get
{
return m_fTemperature;

}/* end get */

set
{
if((value < -30) || (value > 150))
{
TemperatureException excep = new TemperatureException(
"Marlinspike has never experienced such extreme temperatures. " +
"Please recalibrate your thermometer");
throw excep;
}

m_fTemperature = value;

}/* end set */

}

}/* end class TemperatureComponent */

class TemperatureException : ApplicationException
{
public TemperatureException(String message) : base(message)
{
}

}/* end class TemperatureException*/

Here's a VB client that tries to set the temperature in Marlinspike to
a value that's out-of-bounds of the temperature readings accepted by the
method and this triggers an exception from the .NET Component.

Private Sub MyButton_Click()

On Error GoTo ErrHandler

' Create an instance of the temperature component
Dim objTemperature As New TemperatureComponent

' Set the temperature to the boiling point of water
objTemperature.Temperature = 212

Exit Sub

ErrHandler:
MsgBox "Error Message : " & Err.Description, _
vbOKOnly, "Error Code: " & CStr(Err.Number)

End Sub

In the example above, the VB6 client's ErrorHandler block set through
the On Error Goto statement uses VB's global intrinsic
Err object to get all the error details that the CCW has
mapped from the .NET Exception into a COM specific error object
implementing the IErrorInfo interface.

The .NET Component here raises a TemperatureException which
extends the ApplicationException class. An
ApplicationException is usually thrown to indicate errors that
relate to usual run-of-the-mill failure in application business logic. The
TemperatureException calls the base class to initialize the error
message. Since no explicit HRESULT was specified, a failure
HRESULT is generated by the ApplicationException class and is
returned back. If you want to take control of the value of the
HRESULTs instead of accepting the auto generated HRESULT
values by the base class, then you can use the HResult
protected member in the ApplicationException class (that
is accessible from the TemperatureException class) to specify a
specific HRESULT value to be returned. Another way to throw exceptions
from your .NET component is to use the ThrowExceptionForHR method
of the System.Runtime.InteropServices.Marshal class. This method
takes an integer that represents a standard HRESULT parameter. Most of
these standard HRESULTs map to .NET exception types and the corresponding
.NET exception is thrown. For example, if you were to use,

System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(COR_E_OUTOFMEMORY);

then, an OutOfMemoryException would be thrown.

Handling events from .NET Components in
unmanaged Event Sinks:

We saw earlier how COM objects in the unmanaged world raise events
asynchronously using Connection
Points and how these events could be consumed by .NET applications.
We'll now take a look at the other way round. We'll get a .NET Component
to raise events and then have an unmanaged sink consume these events. A
.NET Component should declare events representing
delegate instances for each of the methods in its
outgoing event interface. When an event is raised, all
the delegates in the event's list will be invoked. These
delegates reference the notification target's handler and can
therefore call into the correct handler function provided by the
subscriber. The unmanaged sink goes about subscribing to events as if it
were interacting with a COM object that supports outgoing
interfaces using connection points. The CCW takes
care of mapping both these event handling models so that a COM Client's
unmanaged handler could still receive notifications when a managed .NET
event occurs.

Creating a .NET Component that sources Events:

Let's create a .NET Component that notifies subscribers on inclement
weather conditions. The component allows the weather station master to set
wind speeds that have been recorded. If the wind speeds exceed a certain
limit (300 mph), it senses an impending Tornado and notifies subscribers
by firing off the OnTornadoWarning event.
using System;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Reflection;
using System.Diagnostics;

// Outgoing Event Interface which the Sink implements. We'll
// use a dispinterface here so that it remains friendly to
// scripting clients

[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface ITornadoWatchEvents
{
void OnTornadoWarning(int nWindSpeed);

}/* end interface ITornadoWatchEvents */

// Incoming interface containing the methods
// being exposed to COM aware clients
public interface IWeatherNotify {

int WindSpeed { get; set; }

}/* end interface IWeatherNotify */

// Delegate representing the OnTornadoWarning method of the
// outgoing event interface
public delegate void TornadoWarningDelegate(int nWindSpeed);

[
ComSourceInterfaces("ITornadoWatchEvents"),
ClassInterface(ClassInterfaceType.None)
]
public class WeatherNotify : IWeatherNotify
{
// Indicates Windspeed in Miles Per Hour
private int m_nWindSpeed = 20;

// Define an event associated with the TornadoWarningDelegate
public event TornadoWarningDelegate OnTornadoWarning;

// Constructor
public WeatherNotify() {
}

public int WindSpeed
{
get {

// Return the current Wind speed
return m_nWindSpeed;
}

set {

// Set the WindSpeed to the new value
m_nWindSpeed = value;

// Check if the Wind Speed warrants an event notification
if(value >= 300) {

try {

// Check if the delegate instance for the event exists
if(null != OnTornadoWarning) {

// Twister on the loose. Run for cover !!!.
// Fire the event to all the managed/unmanaged sink handlers
// that have registered for this event.

OnTornadoWarning(m_nWindSpeed);

}/* end if */

}/* end try */
catch(Exception ex) {

Trace.WriteLine(ex.Message);
}/* end catch */

}/* end if */

}/* end set */

}/* End WindSpeed Property Accessors */

}/* end class WeatherNotify */

Let's dissect the code a bit and try to understand what's going on. The
ITornadoWatchEvents interface is the outgoing interface
that unmanaged sinks will need to implement inorder to receive event
notifications. This interface consists of a single method,
OnTornadoWarning that notifies clients of the possibility of an
approaching Tornado along with the current windspeed. You'll notice that
the outgoing interface ITornadoWatchEvents is tagged with an InterfaceType
attribute with the positional parameter
ComInterfaceType.InterfaceIsIDispatch. This is because by
default, the interface would be exported into the IDL and subsequently
into the typelibrary as a dual interface. Scripting clients
generally crack up when they try to sink in a dual interface and they're
only pure dispinterface friendly. So we inject an
InterfaceType attribute with a
ComInterfaceType.InterfaceIsIDispatch value to force typelibrary
generatiion tools to generate a pure dispinterface for the
outgoing interface. You would need to define a delegate
(TornadoWarningDelegate) that matches the exact signature of the
OnTornadoWarning method in the outgoing interface. If
you had more than one method in the outgoing interface, you need
to define matching delegates for each method. We are now done with the
definition of the outgoing interface and matching delegates for each of
the methods in the outgoing interface. Now comes the most important part.
You would need to define events representing each of the
delegates that you defined for the methods in the
outgoing interface.

public event TornadoWarningDelegate OnTornadoWarning;

The delegate instance that the event represents needs
to have the same name as the corresponding method in the outgoing
interface. This means that the event representing the
TornadoWarningDelegate delegate instance will need to be named
OnTornadoWarning. That's all there is to it. You are now ready to
send out event notifications. In our example, an event notification is
sent out when the WindSpeed is set to a value greater than or
equal to 300. You first need to check if the delegate instance
for the event exists and then fire-off the
OnTornadoWarning event. Since the delegate represented
by the event is a multicast delegate, all the COM sinks
and subscribers in the delegates invocation list will be notified that
there's a twister looming on the horizon. You can build the above .NET
component using the following command:

csc /target:library
/r:System.dll /out:WeatherNotify.dll
WeatherNotify.cs

You can now run the assembly through RegAsm.exe to register it
and generate a typelibrary out of it. This typelibrary can then be
referenced in your VB6 Client that will sink the events.

regasm WeatherNotify.dll
/tlb:WeatherNotify.tlb

Handling the events in a VB6 Client Application:

Here's a VB 6.0 Client that subscribes to OnTornadoWarning
event notifications from the WeatherNotify Component. It's a
simple Form based application that subscribes to event
notifications using the WithEvents keyword. The
objWeatherNotify_OnTornadoWarning subroutine receives event
notifications from the .NET component when the WindSpeed is set
to a value greater than or equal to 300.

Dim WithEvents objWeatherNotify As WeatherNotify.WeatherNotify

Private Sub Form_Load()
' Create an instance of the WeatherNotify Component
Set objWeatherNotify = New WeatherNotify.WeatherNotify
End Sub

Private Sub SetWindSpeedButton_Click()

' Clear the warning label
Me.LabelWarning = ""

' Set the Windspeed property in the WeatherNotify component
objWeatherNotify.WindSpeed = Me.WindSpeed

End Sub

Private Sub objWeatherNotify_OnTornadoWarning(ByVal nWindSpeed As Long)

' We've received a notification from the WeatherNotify Component
' that there could be a Tornado on the prowl
Me.LabelWarning = "Tornado Warning: Current Wind Speeds : " & _
nWindSpeed & " mph"

End Sub

Earlier, you saw how the RCW and the metadata helper classes
translated the Connection point event handling to
Delegate based event handling semantics so that a .NET
application could sink events from COM Components. On a similar relation,
the CCW here does most of the plumbing to allow unmanaged code to
subscribe to event notifications from .NET Components and to deliver these
events to the respective handlers in the unmanaged COM land.

Deploying assemblies hosting .NET
components:

In the examples that you saw earlier, you placed the .NET Components
and the COM Metadata Proxy assemblies in the same directory as the
application that was consuming it, so that the runtime's Assembly
resolver would locate it when it probes for the assembly. Assemblies
deployed this way in the same directory of the application referencing it
are called Private Assemblies. Yet another way of locating
referenced assemblies is by using configuration files to tell the
Assembly resolver where to look for the referenced assembly. When
the .NET runtime resolves and binds to such private
assemblies, it has no concerns for versioning. If your assembly
is often used by a large number of applications, then it makes sense to
deploy your assembly in a global repository so that they can be shared and
used by other applications. This shared assembly repository is called the
Global assembly cache (GAC). Assemblies deployed in the GAC are
called Shared Name Assemblies or Strong Name Assemblies.
This is because these assemblies have a unique "Shared Name" or
"Strong Name" associated with them and their identity is uniquely
qualified with a textual name, version number, public key token,
culture information, and a digital signature.
So how do
we go about generating a Strong name for an assembly? . The first
thing you need to do is to generate a Public-Private Key Pair using the
Strong Name utility (SN.exe). You can run SN.exe from
the command-line to create a new random key pair.

sn -k
MyKeyPair.snk

If everything went off well, you should see the following message:

Key pair written to
MyKeyPair.snk

Once the key pair is generated, you need to associate this key pair
with the Assembly for which you need to generate a shared name. You can do
this using the System.Reflection.AssemblyKeyFileAttribute. The
AssemblyKeyFile attribute associates the key pair file that was
generated from SN.exe with the assembly so that the public
key and the digital signature (generated using the
private key and the information in the assembly's manifest) can
be used to generate the assembly's shared name. The key pair file
is generally required during compile time to generate the fully qualified
shared-name signature. But for security reasons, most organizations are a
little apprehensive about passing their private key over to the developer
of the assembly. For this reason, there is a Delay signing option
available. The System.Reflection.AssemblyDelaySignAttribute
allows you to sign the assembly with the private key at a later time.

Let's make the Temperature.dll .NET assembly that we used
earlier as a Strong named assembly. To do that, you need to add
the AssemblyKeyFile attribute to your component. If you are using
Visual Studio.NET, there is a placeholder for this attribute and
other similar global attributes in AssemblyInfo.cs file in your
Project Solution. You can edit the file and specify MyKeyPair.snk
as the file containing your key pair.

[assembly:AssemblyKeyFile("MyKeyPair.snk")]

You can specify the versioning information for the assembly using the
System.Reflection.AssemblyVersionAttribute. This attribute allows
you to specify the version of the assembly. The version usually follows
the pattern major.minor.build.revision. There is usually a
placeholder for this attribute too in AssemblyInfo.cs in your C#
Class Library solution in Visual Studio.NET. You can go in there
and edit this attribute.

[assembly: AssemblyVersion("1.0.0.0")]

Rebuild the Temparature Component after editing the two
attributes specified above in the AssemblyInfo.cs file. Your
Assembly now has a strong name associated with it. To verify
this, issue the following command using the Strong Name utility to list
the assembly's public Key and token.

sn -Tp
Temperature.dll

An output similar to this is returned back:

Public key
is
0024000004800000940000000602000000240000525341310004000001000100ed87f0432cbf37
fc70eec5d0e59d7e47327729cd99e257a2790c690957691f20c01b47d46a72b20b4f37a829f6ad
82e6594221bbd0193b5499ca0a83db7fc9b78bcb07177f02ef9c827688246f6073f34405e9a441
37017cf6ed52c5001272b0b820926f078bbe8705fa9d411a18d692c94be9541bb3fde38b1b1f79
5a06dde8

Public
key token is f80b1601a4d8a9dd

If you use the same command on a private assembly that does not have a
strong name associated with it, this is what you get:

sn -Tp
WeatherNotify.dll

WeatherNotify.dll does not represent a
strongly named assembly

You are now ready to deploy the Temperature.dll in the
Global Assembly Cache(GAC), so that the assembly resolver can
locate this assembly in the GAC when applications attempt to load
or use this assembly. You can list out all the assemblies in the
GAC using the following command:

gacutil
-l

Alternatively, you can use a Windows shell extension in
shfusion.dll that allows you to view, add and remove assemblies
in the GAC. If you navigate to the Assembly folder under
your Windows Directory, this extension will provide you with a view of the
assemblies deployed in the GAC along with properties such as
Name, Type, Version, Culture and Public key token.
To
deploy the Temperature.dll assembly, just type in the following
command in the command-line and it should be deployed in the
GAC.

gacutil -i
Temperature.dll

If everything went off well, you would get a message such as:

Assembly successfully
added to the cache

You could also drag and drop your strong name assemblies into the view
provided by the shell extension and it should install it for you. Once
your assemblies are deployed in the GAC, you are relieved from the
rigmarole of having to place your assembly in the application's folder or
mess with the configuration files to tell the resolver where to find the
assembly. The assembly resolver will now locate the assembly in the GAC.

Deploying the assembly in the GAC has advantages like side-by-side
execution of assemblies with different version numbers, single-instancing
of assemblies (also called Code Page Sharing) that allows the
runtime to load fewer copies of the assembly when used by multiple
applications, decreased load times, and facilitates Quick fix Engineering
(QFE) based hot-deployment of assemblies.

Thread Affinity in .NET Components :

In the Understanding
COM Threading Models and Apartments from a .NET Application's
perspective section, you saw how .NET applications could declare
the calling thread's apartment affiliation before creating a classic COM
Component. Now, let's take a look at the other side of the equation.
Particularly, the kind of threading behavior that .NET Components exhibit
when they are created from unmanaged COM aware applications. The thread
affinity of a .NET Component is defined by the context that the object
lives in. A Context is essentially an environment hosted by the
AppDomain (a light weight process) in which the object gets
created. Each context in turn hosts objects that share common usage
requirements such as Thread Affinity, Object pooling, Transaction, JIT
Activation, Synchronization etc. These contexts are created as and
when required by the runtime depending on the attributes, and the
interception services required by the object. If there is an existing
context that matches the usage rules that govern the object, then the
runtime offers it accommodation in that context. If it does not find a
matching context, a new context is created for the object to live in.

With that said, every AppDomain also hosts a Default
context. The Default Context in turn hosts Context
Agnostic (Context Agile) objects. These objects are not bound to any
context. Context Agile objects do not require any attributes, special
usage policies and interception services. Take a look at the following
table that summarizes how .NET components behave in cross-context access
scenarios based on their context agility:

.NET classes that extend
MarshalByRefObject

.NET classes that extend
ContextBoundObject

.NET classes
that niether extend from MarshalByRefObject nor
ContextBoundObject

Cross-Context calls within
the same AppDomain (Intra AppDomain
calls)

Context-Agile. Direct Access. (Emulates a Classic COM object that
aggregates the Free-threaded Marshaler)

Context-Bound. Object is accessed from any other context only
through a proxy.

Context-Agile. Direct Access.(Emulates a Classic COM object that
aggregates the Free-threaded Marshaler)

Cross-Context calls across
AppDomains (Inter AppDomain calls)

Exhibit Marshal-By-Reference semantics. Object is accessed from
any other context, only through a proxy.

Exhibit Marshal-By-Reference semantics. Object is accessed from
any other context, only through a proxy.

Exhibit Marshal-By-Value semantics. When tagged with the
[serializable] attribute, a copy of the object is recreated in the
caller AppDomain's context.

Thread neutral behavior when accessed by unmanaged COM aware clients
:

Take a look at how a .NET component advertises its threading model to
COM when an assembly is run through REGASM.EXE in order to make
the appropriate registry entries for COM aware clients to look up.

The ThreadingModel key under InprocServer32 has a
value 'Both'. In Classic COM, objects that advertise their
ThreadingModel as 'Both' are willing to move into their
caller's apartment, be it STA or MTA. In addition,
'Both' threaded objects that also aggregate the Free-threaded
marshaler, give other apartments to which they are marshaled,
direct interface pointer references as opposed to
proxies. Context Agile .NET components (those that do
not extend from ContextBoundObject) are analogous to
thread-neutral COM objects that aggregate the free-threaded
marshaler. Let's check to see how the .NET component behaves as we
pass interface references to the .NET component across COM apartments in
the unmanaged client. Take a look at this simple C# class that we'll be
exposing to the unmanaged COM client:

using System;
using System.Runtime.InteropServices;

public interface IHelloDotNet {

String GetThreadID();

}/* end interface IHelloDotNet */

[ClassInterface(ClassInterfaceType.None)]
public class HelloDotNet : IHelloDotNet
{
public HelloDotNet() {
}

public String GetThreadID() {

return AppDomain.GetCurrentThreadId().ToString();
}

}/* end class HelloDotNet */

The above class implements the GetThreadID method from the
IHelloDotNet interface. This method returns the ID of the current
thread that's executing in the AppDomain into which this object
is loaded. To build the above class into an assembly and to make the
appropriate registry entries for COM, issue the following commands from
the command-line:

csc /target:library
/out:HelloDotNet.dll HelloDotNet.cs

regasm HelloDotNet.dll
/tlb:HelloDotNet.tlb

We'll now go on and consume the .NET component from a COM aware client.
We'll use a C++ console application that will create the .NET component in
its main thread (an STA) and then pass it across to two other
apartments (an STA apartment and an MTA apartment) by
spawning two worker threads. We'll take a look at what happens
when a raw interface pointer to the object is passed across
apartments. Then, we'll take a look at what happens when a
marshaled reference is passed across apartments by using explicit
inter-thread marshaling calls that use the
CoMarshalInterface/CoUnmarshalInterface API family. Take a look
at the code below (Error checking in the code is omitted for brevity):

.......

#import "mscorlib.tlb"
// Import the .NET component's typelibrary
#import "HelloDotNet.tlb" no_namespace

// Worker Thread functions
long WINAPI MySTAThreadFunction(long lParam);
long WINAPI MyMTAThreadFunction(long lParam);

// Use the compiler generated smart pointer wrappers
IHelloDotNetPtr spHelloNET = NULL;

// Stream ptr that will contain the marshaled interface
IStream* g_pStream1 = NULL;
IStream* g_pStream2 = NULL;

int main(int argc, char* argv[])
{
.......

// Make the primary thread enter an STA
::CoInitialize(NULL);

// Log the thread id
cout << "The Thread ID of the primary STA thread is : "
<< ::GetCurrentThreadId() << endl;

// Create the .NET object via the COM Interop
hr = spHelloNET.CreateInstance(__uuidof(HelloDotNet));

cout << "From .NET when called from the primary STA Thread : "
<< spHelloNET->GetThreadID() << endl;

.......

// Marshal the interface pointer to a stream so that the
// worker threads can get back an unmarshaled reference from it.
hr = CoMarshalInterThreadInterfaceInStream(_uuidof(IHelloDotNet),
spHelloNET,
&g_pStream1);

hr = CoMarshalInterThreadInterfaceInStream(_uuidof(IHelloDotNet),
spHelloNET,
&g_pStream2);

// Create a worker thread that enters a STA
hThreadSTA = CreateThread(NULL,0,
(LPTHREAD_START_ROUTINE)MySTAThreadFunction,
NULL,0 ,&dwThreadIDSTA);

// Log the thread id
cout << "The Thread ID of the STA based Worker thread is : "
<< dwThreadIDSTA << endl;

// Create a worker thread that enters a MTA
hThreadMTA = CreateThread(NULL,0,
(LPTHREAD_START_ROUTINE)MyMTAThreadFunction,
NULL,0,&dwThreadIDMTA);

// Log the thread id
cout << "The Thread ID of the MTA based Worker thread is : "
<< dwThreadIDMTA << endl;

// Wait for both the worker threads to complete
::WaitForSingleObject(hThreadSTA,INFINITE);
::WaitForSingleObject(hThreadMTA,INFINITE);

// Return the status
return 0;

}/* end main */

/*
* Worker Thread Function that enters a STA Apartment
*/
long WINAPI MySTAThreadFunction(long lParam)
{
// Let the thread enter a STA
::CoInitializeEx(NULL,COINIT_APARTMENTTHREADED);

// Invoke the method using the raw interface pointer
cout << "From .NET when called from the STA Worker Thread (Direct Access) : "
<< spHelloNET->GetThreadID() << endl;

// Unmarshal the interface pointer from the stream
IHelloDotNetPtr spHello = NULL;
HRESULT hr = CoGetInterfaceAndReleaseStream(g_pStream1,
__uuidof(IHelloDotNet),
(void **)&spHello);

if(S_OK == hr)
{
cout << "From .NET when called from the STA Worker Thread (Marshaled) : "
<< spHello->GetThreadID() << endl;
}

// Exit from the thread
return 0;

}/* end MySTAThreadFunction */

/*
* Worker Thread Function that enters a MTA Apartment
*/
long WINAPI MyMTAThreadFunction(long lParam)
{

// Let the thread enter a MTA
::CoInitializeEx(NULL,COINIT_MULTITHREADED);

// Invoke the method using the raw interface pointer
cout << "From .NET when called from the MTA Worker Thread (Direct Access) : "
<< spHelloNET->GetThreadID() << endl;

// Unmarshal the interface pointer from the stream
IHelloDotNetPtr spHello = NULL;
HRESULT hr = CoGetInterfaceAndReleaseStream(g_pStream2,
__uuidof(IHelloDotNet),
(void **)&spHello);

if(S_OK == hr)
{
cout << "From .NET when called from the MTA Worker Thread (Marshaled) : "
<< spHello->GetThreadID() << endl;
}

// Exit from the thread
return 0;

}/* end MyMTAThreadFunction */

Here's the output that you get back when the console application is
run.

The Thread ID of the
primary STA thread is : 2220
From .NET when called from the
primary STA Thread : 2220
The Thread ID of the STA based Worker
thread is : 2292
The Thread ID of the MTA based Worker thread is
: 2296
From .NET when called from the STA Worker Thread (Direct
Access) : 2292
From .NET when called from the STA Worker Thread
(Marshalled) : 2292
From .NET when called from the MTA Worker
Thread (Direct Access) : 2296
From .NET when called from the MTA
Worker Thread (Marshalled) : 2296

Notice that for all these calls, no thread switch occurs
between the thread making the call in the client and the
thread invoking the actual method in the .NET component. It other
words, the .NET component is Context agile and always executes in
the caller's thread. From the code snippet above, observe that the effect
of marshaling an object reference (using inter-thread marshaling APIs such
as
CoMarshalInterThreadInterfaceInStream/CoGetInterfaceAndReleaseStream)
is the same as that of passing a direct object reference across
apartments. Eventually, the receiving apartment gets an
apartment-neutral interface pointer that it can use to call into
the .NET component. The .NET component exhibits all the behavior that is
reminiscent of Both threaded Classic COM Components that
aggregate the free-threaded marshaler.

Conclusion: COM's position in the über powerful
.NET world:

In the first part of this article, we took a look at how you could
expose Classic COM components to .NET applications executing under the
purview of the Common Language Runtime (CLR). We saw how the COM
interop seamlessly allows you to reuse existing COM components from
managed code. Then, we skimmed through ways to invoke your COM component
using both early binding and late binding along with
ways to do runtime type checking and dynamic type discovery. We took a
journey through understanding how delegates work in .NET, the
role they play in the .NET event-handling model, and how the COM Interop
acts as an adaptor to wire the connection points event handling
model in classic COM to the delegate based Event handling model in .NET.
We discussed about how to expose COM collections to .NET applications and
use C#'s foreach syntax to easily iterate through the elements of
the collection. Then, we looked at how directional attributes in IDL files
get mapped to the corresponding directional parameter types in C#. We also
learnt about some of the reuse options available for Classic COM
components from .NET applications using inheritance and
containment. Finally, we saw how managed threads declare their
apartment affiliations when invoking COM components.

In the latter half of this article, we took a dip into exploring how
COM aware clients from the pre-.NET era could consume .NET components as
if they were classic COM components. We saw how the CCW and the
CLR facilitate this to happen seamlessly from a programming
perspective. We briefly explored the possibilities of using
attributes to emit metadata into .NET types so that the
typelibrary generated could be tailored and fine-tuned to your
requirements. We looked at how the exception handling mechanisms in the
two worlds are correlative. We also discussed about how to go about
receiving asynchronous event notifications from .NET components in
unmanaged event sinks. Then, we turned our attention to the deployment
options available and how to deploy .NET components as Shared
assemblies. Lastly, we discussed the thread-neutral behavior of .NET
components and saw how Context-agile .NET components are analogous to
Classic COM 'Both' threaded components that aggregate the
free-threaded marshaler (FTM).

As a COM developer, you might wonder if it makes sense to continue
writing COM components or make the transition directly into the .NET world
by writing all your components and business logic code wrapped up as
managed components using one of the languages such as C#, VB.NET or any of
your favorite languages that generates CLR compliant managed
code. In my opinion, if you have tons of COM code out there that you just
cannot port to managed code overnight, it makes sense to leverage the
interop's ability to reuse existing COM components from .NET applications.
But, if you are starting with writing new business logic code from
scratch, then it's best to wrap your code as managed components using one
of the languages that generate CLR managed code. That way, you
can do away with the performance penalties that are incurred while
transitioning between managed and unmanaged boundaries. Eventually, we COM
developers don't have to despair. Our beloved COM components will continue
to play well with .NET applications. The tools provided with the .NET
framework and the COM interop mechanism provided by the runtime make it
seamless from a programming perspective as to whether your .NET
application is accessing a Classic COM component or a managed component.
So in essence, the marriage between COM and the brave new über powerful
.NET world should be a happy one, and the COM that we all know and love so
much will still continue to be a quintessential part of our lives.

Quotable Quotes:
"Virtually
all aspects of the COM programming model have survived (interfaces,
classes, attributes, context, and so on). Some may consider COM dead
simply because CLR objects don't rely on IUnknown-compliant vptrs/vtbls. I
look at the CLR as breathing new life into the programming model that I've
spent the last seven years of my life working with, and I know there are
other programmers out there who share this sentiment."
- Don
Box, in his 'House of COM' column in the MSDN Magazine(Dec,
2000)

"COM lives on in spirit, if not in body
!"
- Peter Foreman, in the Developmentor DOTNET mailing
list

Bibliography:

House of COM - Migrating Native Code to the .NET CLR - MSDN Magazine,
May 2001

House of COM - Is COM Dead? - MSDN Magazine, December 2000

Acknowledgements:
I'd like to express my
sincerest gratitude to Tom Archer for encouraging me to write and for
including a part of this tutorial in his book Inside C#. I'd also like to thank the wonderful folks at
the Developmentor DOTNET mailing list for giving .NET
developers, food for thought, everyday.

Aravind C

Click here
to view Aravind C's online profile.

Other popular articles:

Code
generation at runtime
The .NET
platform contains no extensions which would allow for the use of
advanced aspect oriented programming. But it does contain
mechanisms which makes it possible to build components which
provide similar functionality. We will find out how to achieve
this in this article.

The 30
Minute Regex Tutorial
Learn how to
use regular expressions in 30 minutes with Expresso.

Floating
Point in .NET part 1: Concepts and Formats
Introducing the basic concepts of floating-point
arithmetic: number formats, accuracy and precision, and round-off
error. Includes a discussion of the .NET floating-point
types.

Methods
for a location proximity search using GPS (WGS-84)
mathematics
Unsatisfied with the
accuracy of code online that assumes the Earth is a sphere, I have
implemented the oblate spheroid model used in
GPS.

[Top]

Sign
in to vote for this article:
PoorExcellent

Note:
You must Sign
in to post to this message board.
Hint: For a
faster board use IE 4+ or Mozilla, choose 'Message View' from the
View dropdown and hit 'Set Options'.

FAQ

Noise
tolerance Very High High
Medium Low Very
low

Search comments

View Normal (slow) Preview
(slow) Message View
Topic View Thread
View Expanded (Supporters
only)

Per page 10 25
50 (must logon)

Msgs 1 to 25 of 99 (Total: 99) (Refresh)

First Prev
Next

Subject

Author

Date

Implement COM Interface, and create with
new

maihem

10:27 14 Mar
'06

passing COM interface between .net
threads

galias

3:16 7 Mar
'06

Excellent Article!

Bill
S

0:10 18 Feb
'06

VS2005 Beta registration
failed

mekanoo

10:59 6 Feb
'06

Multi threaded
.Net called from COM

R Lindeman

5:59 23 Jan
'06

Hi,

I have the
following, to me strange, problem.
I created
in .Net C# a component that creates events to
notify a calling VB6 application about
progress.
Some of the work to be done in the
.Net component are time consuming and/or might
be aborted by the user. To do this I create in
the .Net component a new (managed) thread so the
GUI does not block. From this new thread the
events are raised.
The strange thing is that
on some computers it works fine. On other
computers the program crashes (The program
cannot continue etc.) just at the moment the
first event is raised.

Has anybody an
idea what might be the trouble?

I develop
using VS 2003

Bob
Lindeman

[Sign
in][View
Thread][Get
Link]

Re: Multi threaded .Net called from
COM

mekanoo

11:03 6 Feb
'06

Re: Multi threaded .Net called from
COM

R
Lindeman

6:05 16 Feb
'06

Re: Multi threaded .Net called from
COM

R
Lindeman

8:18 16 Feb
'06

Re: Multi threaded .Net called from
COM

mekanoo

15:08 16 Feb
'06

Re: Multi threaded .Net called from
COM

galias

3:04 7 Mar
'06

Re: Multi threaded .Net called from
COM

R
Lindeman

6:21 8 Mar
'06

Matlab problems

mmcguirk1

14:48 6 Dec
'05

Method Name Attribute?

Paul
Gates

9:13 1 Dec
'05

regasm error

Philip
Simons

13:07 18 Jul
'05

[Msg Deleted]

Martin
Wittershagen

8:50 8 Sep
'05

Re: regasm error

Martin
Wittershagen

8:50 8 Sep
'05

Casting to some interfaces only
fails

rickengle

0:43 10 Jul
'05

Re: Casting to some interfaces only
fails

CE-Chris

23:49 9 Dec
'05

Re: Casting to some interfaces only
fails

rickengle

0:07 10 Dec
'05

Drag and drop problem

ArunHcl

2:45 16 Jun
'05

CCW Problems

Anonymous

8:49 9 Jun
'05

using net2phone dlls

Anonymous

3:51 28 May
'05

thanks

cbuenger

18:30 8 Apr
'05

Exception from HRESULT:
0x800A9C68.

NituP

3:46 12 Jan
'05

enum interop conversion

totonoos

17:35 4 Dec
'04

Last Visit: 1:44 Friday 17th March,
2006

First Prev
Next

General comment
News / Info    Question
Answer
Joke / Game
Admin message

All Topics, .NET, C# >> .NET >> General
Updated:
25 Jul 2001

Article content
copyright Aravind C, 2001
everything else Copyright © CodeProject, 1999-2006.

Web01 | Advertise
on The Code Project | Privacy

The Ultimate Toolbox • ASP Alliance • Developer Fusion • Developersdex • DevGuru • Programmers Heaven • Planet Source Code • Tek-Tips Forums •