Interop with COM

The CLR provides support both for exposing C# objects as COM objects and for using COM objects from C#.

Binding COM and C# Objects

Interoperating between COM and C# works through either early or late binding. Early binding allows you to program with types known at compile time, while late binding forces you to program with types via dynamic discovery, using reflection on the C# side and IDispatch on the COM side.

When calling COM programs from C#, early binding works by providing metadata in the form of an assembly for the COM object and its interfaces. TlbImp.exe takes a COM type library and generates the equivalent metadata in an assembly. With the generated assembly, it’s possible to instantiate and call methods on a COM object just as you would on any other C# object.

When calling C# programs from COM, early binding works via a type library. Both TlbExp.exe and RegAsm.exe allow you to generate a COM type library from your assembly. You can then use this type library with tools that support early binding via type libraries such as Visual Basic 6.

Exposing COM Objects to C#

When you instantiate a COM object you are actually working with a proxy known as the Runtime Callable Wrapper (RCW). The RCW is responsible for managing the lifetime requirements of the COM object and translating the methods called on it into the appropriate calls on the COM object. When the garbage collector finalizes the RCW, it releases all references to the object it was holding. For situations where you need to release the COM object without waiting for the garbage collector to finalize the RCW, you can use the static ReleaseComObject method of the System. Runtime.InteropServices.Marshal type.

The following example demonstrates adding a contact to MSN Instant Messenger from C# via COM Interop:

// IMAdd.cs - compile with /r:Messenger.dll
// Run IMAdd.exe <UserID> to add an MSN Instant 
//   Messenger user to Contacts
// Run TlbImp.exe "C:Program FilesMessengermsmsgs.exe" 
//   to create Messenger.dll
using System.Runtime.InteropServices;
using Messenger; // COM API for MSN Instant Messenger
class COMConsumer {
   static void Main(string[] args) {
      MessengerApp m = new MessengerApp( )
      m.LaunchAddContactUI(args[0]);
   }
}

Exposing C# Objects to COM

Just as an RCW proxy wraps a COM object when you access it from C#, code that accesses a C# object as a COM object must do so through a proxy as well. When your C# object is marshaled out to COM, the runtime creates a COM Callable Wrapper (CCW). The CCW follows the same lifetime rules as other COM objects, and as long as it is alive, a CCW maintains a traceable reference to the object it wraps, which keeps the object alive when the garbage collector is run.

The following example shows how you can export both a class and an interface from C# and control the Global Unique Identifiers (GUIDs) and Dispatch IDs (DISPIDs) assigned. After compiling IRunInfo and StackSnapshot you can register both using RegAsm.exe.

[GuidAttribute("aa6b10a2-dc4f-4a24-ae5e-90362c2142c1")]
public interface : IRunInfo {
  [DispId(1)]
  string GetRunInfo( );
}
[GuidAttribute("b72ccf55-88cc-4657-8577-72bd0ff767bc")]
public class StackSnapshot : IRunInfo {
  public StackSnapshot( ) {
    st = new StackTrace( );
  }
  [DispId(1)]
  public string GetRunInfo( ) {
    return st.ToString( );
  }
  private StackTrace st;
}

COM Mapping in C#

When you use a COM object from C#, the RCW makes a COM method look like a normal C# instance method. In COM, methods normally return an HRESULT to indicate success or failure and use an out parameter to return a value. In C#, however, methods normally return their result values and use exceptions to report errors. The RCW handles this by checking the HRESULT returned from the call to a COM method and throwing a C# exception when it finds a failure result. With a success result, the RCW returns the parameter marked as the return value in the COM method signature.

Tip

For more information on the argument modifiers and default mappings from COM type library types to C# types, see Appendix D.

Common COM Interop Support Attributes

The BCL provides a set of attributes you can use to mark up your objects with information needed by the CLR interop services to expose managed types to the unmanaged world as COM objects.

This section describes the most common attributes you will use for this purpose. These attributes all exist in the System.Runtime.InteropServices namespace.

ComVisible attribute

[ComVisible(true|false)]
(for assemblies, classes, structs, enums, interfaces, delegates)

When generating a type library, all public types in an assembly are exported by default. The ComVisible attribute specifies that particular public types (or even the entire assembly) should not be exposed.

DispId attribute

[DispId( dispatch-id )]
(for methods, properties, fields)

The DispId attribute specifies the DispID assigned to a method, field, or property for access via an IDispatch interface.

ProgId attribute

[ProgId( progid )] (for classes)

The ProgId attribute specifies the COM ProgID to be used for your class.

Guid attribute

[GuidAttribute( guid )]
(for assemblies, modules, classes, structs, enums, interfaces, delegates)

The Guid attribute specifies the COM GUID to be used for your class or interface. This attribute should be specified using its full type name to avoid clashes with the Guid type.

HasDefaultInterface attribute

[HasDefaultInterface] (for classes)

The HasDefaultInterface attribute specifies that the first inherited interface on the class should be used as the default interface (instead of generating a unique interface).

InterfaceType attribute

[InterfaceType( ComInterfaceType )]
(for interfaces)

By default, interfaces are generated as dual interfaces in the type library, but you can use this attribute to one of the three COM interface types (dual, dispatch, or a traditional IUnknown-derived interface).

ComRegisterFunction attribute

[ComRegisterFunction] (for methods)

Requests that RegAsm.exe call a method during the process of registering your assembly.

NoIDispatch attribute

[NoIDispatch] (for classes)

The NoIDispatch attribute specifies that a request for IID_IDispatch for the class should return E_NOINTERFACE.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.142.40.32