.
NET COM Interop
Sam Gentile
.NET Consultant
[Link]
A Little Bit About Me
.NET Consultant, Speaker
– Currently working for Microsoft
– Working in managed code for over 2 years with
Microsoft, Groove, Pacific Mindworks, NaviSite
– Designed and implemented
Groove Toolkit for Microsoft Visual Studio .NET
using VSIP, C#, COM Interop, Managed C++, etc.
.NET Writer, Blogger, Community
– Co-author Wrox Visual C++.NET – Interop, COM
Interop
– Major .NET Blogger at
[Link]
Contacts
– .NET home at [Link]
– ManagedCode@[Link]
[Link]
What we will cover
Review of COM and .NET
Calling COM from .NET
Calling .NET from COM
Pitfalls and Workarounds
[Link]
Session Prerequisites
This is a programmer group
You know some C++ or VB for COM side
You know some C# for .NET side
You have a familiarity with COM: Cannot
learn COM here
You have familiarity with .NET and CLR; I
will review concepts directly relevant to
Interop
[Link]
So Why This Presentation?
There are over 1 Billion lines of existing
legacy COM
Most companies will not start with
managed code right away
– New skill sets to learn
– Preserve existing investment in components
– Economy
Transition to .NET will be a lasting process
Big opportunities for you
[Link]
Demonstrations
Generating Interops (RCWs) with [Link]
and tlbimp
Peering Inside Interops
Using tlbexp to generate Interops and
Implementing Class Interfaces
[Link]
Agenda
Review of COM and .NET
Calling COM from .NET
Calling .NET from COM
Pitfalls and Workarounds
[Link]
COM Is…
Interface based
Separate interface and implementation
Dynamic runtime linking
Binary Standard Object Model
Language neutral (in theory)
Deterministic
[Link]
.NET
Metadata
Virtualization of contracts
Runtime provides services
Virtualization of types (CTS)
Versioning
Deterministic
[Link]
Review of .NET Concepts
Managed code
Metadata
Assemblies
Strong names
GAC
Reflection
Custom Attributes
[Link]
Bridging Different Worlds
Unmanaged Code Managed Code
Type libraries Meta data
Immutable types Resilient bind
DLL issues Assemblies
Interface based Object based
HRESULTS Exceptions
GUIDS Strong names
[Link]
Agenda
Review of COM and .NET
.NET to COM
COM to .NET
Windows Forms
[Link]
Need for RCWs
COM types defined in type library (and IDL)
.NET types in assemblies and have metadata
Need to import types and generate metadata in
form of Interop Assembly
Need proxy to transparently handle .NET to
COM transition
Runtime Callable Wrapper (RCW)
Marshal between managed and COM
[Link]
The Role of the RCW
Marshal calls back and forth (proxy)
“Fool” .NET clients
Maintain COM identity and lifetime
Implement low-level COM Interfaces
.NET class type dynamically created by
CLR based on Interop Assembly
Created when reference to COM object
marshaled across MSCOREE boundary
[Link]
Creating an RCW
Visual Studio .NET Add COM Reference
– Easiest and least flexible
– Type lib provides definition of types
Framework SDK [Link]
[Link]
Converter
[Link]
Using [Link]
Add COM Reference – equivalent to:
– Tlbimp InputFile /out:[Link] /namespace:LibraryName
/sysarray
[Link]
Using [Link]
More flexible and don’t need [Link]
Command line tool generates .NET
Interop Assembly
Reads type info from TLB and
generates .NET metadata
Simplest form: tlbimp [Link]
Can specify name of output assembly with
/out - important
[Link]
Important tlbimp options
/sysarray converts SAFEARRAYS to
[Link]
/ref to resolve references is key
/delaysign option
/publickey, /keyfile, /keycontainer affect
signing
/primary will be discussed soon
[Link]
Demo 1
Generating Interops (RCWs) with
[Link] and tlbimp
Using Interops and RCWs from C#
clients
[Link]
Interop Assemblies
Generated by Type Lib Importer
Metadata that resolves calls and let
runtime create an RCW
Another “view” into same COM types
Underlying COM Object does not change
Mostly metadata
[Link]
Look Inside at Types Generated
Generated Managed Type Meaning in Life
Tlbimp generates a type that has the same
CAdd name as the [default] interface minus the
“I-” prefix. Type creatable but you will only
be able to access types explicitly defined
by this interface.
Tlbimp always generates managed
IAdd eqivalents for each interface found in TLB.
Of course, these are NOT creatable.
For every co-class listed in the IDL library
CAddClass section there is a .NET Managed Class
Type. In other words, class CAdd becomes
CAddClass. These .NET types are directly
creatable and support the members of each
and every implemented COM interface.
[Link]
COM value type COM reference type System type
bool bool * System.Int32
char, small char *, small * [Link]
short short * System.Int16
long, int long *, int * System.Int32
Hyper hyper * System.Int64
unsigned char, byte unsigned char *, byte [Link]
*
wchar_t, unsigned wchar_t *, unsigned System.UInt16
short short *
unsigned long, unsigned long *, System.UInt32
unsigned int unsigned int *
float float * [Link]
double double * [Link]
void * void ** [Link]
HRESULT HRESULT * System.Int16 or
[Link]
[Link]
Type Conversion Specifics
DATE [Link]
BSTR [Link]
SafeArray(int) int[]
OLECOLOR uint
CURRENCY [Link]
VARIANT [Link]
Interface interface IMyInterface
IMyInterface
coclass Bar class Bar
IEnumVariant IEnumerator
[Link]
More Marshaling
Isomorphic (Blittable) Types
– Integers, Float Values
Non-isomorphic Types
– Booleans, Chars, Strings, Arrays, Objects
Copying vs. Pinning
– Isomorphic Types are pinned
– Non-isomorphic are copied
[Link]
Demo 2
Peering Inside Interops
[Link]
Primary Interop Assemblies
Every time someone generates Interops
creates distinct identity, version
Want single, “blessed” managed identity
– Digitally signed by publisher
– Strongly named
– Marked primary
– Put in the GAC
Created with tlbimp /primary
[Link]
Agenda
Review of COM and .NET
.NET to COM
COM to .NET
Pitfalls and Workarounds
[Link]
Why?
The world is filled with unmanaged clients and
hosts (and will be for quite a while…)
– Windows Shell
– Internet Explorer
– Office XP
– VB 6.0
Managed objects in unmanaged hosts use Interop
You can easily host .NET objects from both
unmanaged and managed hosts
[Link]
COM to .NET Interop
Common Language Traced
IUnknown Runtime Reference
COM Client IDispatch
IMyInterface
COM
Callable Object
Wrapper
Reference IMyInterface
Counted
Client
[Link]
COM Callable Wrapper
Created when marshal ref to CLR object
across boundary
Proxy to transform .NET to COM
Unmanaged COM object holding one
reference to managed object
Single CCW shared among multiple COM
clients for given COM type
– Insures identity
– No matter how object created
[Link]
Role of the CCW
Fool COM clients
Transform .NET data types into COM
equivalents (marshaling)
Canned implementation of standard COM
Interfaces
Proxying custom interfaces
Maintaining object lifetime
[Link]
Generating COM Type
Definitions
1. [Link] command line tool
2. [Link] command line tool
3. [Link] Project Properties (calls 1,2)
4. Programmatically using
TypeLibConverter
5. [Link] command line tool
[Link]
Using [Link]
Command line tool
Very few options needed because
attributes
Most options useless
Syntax: [Link] [Link]
/out flag lets you control name
[Link]
Registering with regasm
Command line utility
Creates all needed COM Registry settings
With /tlb also generates and registers type
lib
Result of /tlb is a .tlb containing IDL defs
Default value for InProcServer32 key is
[Link]
[Link]
Registry Settings by Regasm
HKEY_CLASSES_ROOT\ProgID\[default]=“NamespaceQualifiedClassName”
HKEY_CLASSES_ROOT\ProgID\CLSID\[default]=“{CLSID}”
HKEY_CLASSES_ROOT\CLSID\{CLSID}\
[default]=“NamespaceQualifiedClassName”
HKEY_CLASSES_ROOT\CLSID\{CLSID}\ImplementedCategories\
– {62C8FE65-4EBB-45E7-B440-6E39B2CDBF29}
HKEY_CLASSES_ROOT\CLSID\{CLSID}\ InprocServer32\[default]=
– “WindowsaSystemDirectory\[Link]”
HKEY_CLASSES_ROOT\CLSID\
{CLSID}\InprocServer32\Assembly=“FullAssemblyName”
HKEY_CLASSES_ROOT\CLSID\
{CLSID}\InprocServer32\Class=“NamespaceQualifiedClassName”
HKEY_CLASSES_ROOT\CLSID\{CLSID}\InprocServer32\RuntimeVersion=“Version”
HKEY_CLASSES_ROOT\CLSID\{CLSID}\InprocServer32\ThreadingModel=“Both”
HKEY_CLASSES_ROOT\CLSID\{CLSID}\ProgID=“ProgID”
[Link]
Finding and Loading the Interop
DllGetClassObject called, CLR checks
Class value
Fusion finds Assembly by normal rules
Must be placed where Fusion can find it
GAC is recommended place since shared
anyhow
/codebase can be used for development
only
[Link]
.NET Metadata to COM Types
DATE [Link]
BSTR [Link]
Safearray(int) int[]
OLECOLOR [Link]
DECIMAL [Link]
VARIANT [Link]
Interface interface IMyInterface
IMyInterface
Class CMyClass coclass CMyClass
IEnumVariant IEnumerator
[Link]
Controlling Translation
ComVisible attribute
InterfaceTypeAttribute
ClassInterfaceAttribute
StructLayoutAttribute
GuidAttribute
ProgIdAttribute
[Link]
Understanding the Class
Interface
[Link]
Understanding the .NET Class
Interface
You may be surprised by what you get by
default from export!
Empty [default] dual interface
exporter generates empty IDispatch-
derived interfaces named _CoClassname
to “preserve identity of .NET class Types.”
Tlbexp will always generate an
AutoDispatch class interface unless you
say differently
[Link]
The Case Against Class
Interfaces
MSFT: Avoid and use [Link]
Why evil? Dreaded versioning problem
.NET interface can change breaking COM
Empty AutoDispatch interface generated and
clients must access via late binding only
EX: COM class implements .NET interface with
member containing a MyClass parameter
– Exporter can match the _MyClass parameter in
COM’s TLB to original MyClass .NET definition
– Enabled by special IDL custom attribute
[Link]
Recommended Approach
Microsoft recommends defining and
implementing real .NET interface
And [Link]
Generates real COM interface that be
accessed through a v-table
Let’s see how in the following demo
[Link]
Demo 3
Using tlbexp to generate Interops
Regasm
Class Interfaces
[Link]
Agenda
Review of COM and .NET
.NET to COM
COM to .NET
Pitfalls and Workarounds
[Link]
Determinstic Finalization
BIG problem in REAL COM applications – spent months on this at one
company
Problem: Release calls to COM Interface Pointers are non-deterministic
because COM object release not released until RCW itself is garbage
collected (whenever) and COM wants to be deterministic
Also CCW holds rooted reference to underlying CLR object & prevents GC
as long as at least one outstanding COM Interface Pointer
Not good because of performance reasons and possible semantics of object
model require certain objects to be released at certain times (i.e.
Connection Points)
Solution: call the Marshal::ReleaseComObject method in the
System::Runtime::InteropServices namespace for each COM interface.
Solution: Convert rooted ref inside CCW to weak with
[Link]
Is this a Design Flaw or a COM Cycle Issue?
See my essays Is COM Interop Fundamentally Flawed? And Part 3 - A
Solution and discussion
[Link]
The Problem
COM objects wrapped in RCWs are not reference-
counted based on number of .NET clients
Instead, any COM interface obtained through RCW has
its reference count incremented once
When RCW garbage collected, its finalizer calls
[Link] on every cached interface pointer
[Link] does deterministic release –
release now
This can become very messy quickly
If intermediate RCWs are created when using an RCW,
may have to call ReleaseComObject more than once to
clean up everything
[Link]
Solutions
Implement IDisposable pattern
– Helper class per COM object that uses types
defined by the Interop Assemblies
– Helper class per COM object that uses
another helper class which uses raw UCOM
(ex: raw connection points)
Convert rooted ref inside CCW to weak
with
[Link]
[Link]
Case 1
In EventHandlers:
[Link](interface);
And:
public new Dispose()
{
// unsubscribe from event
m_foo.OnSomething -= delegate(eventHandler);
[Link](m_foo);
m_foo = null;
}
[Link]
Case 2
In event handlers, create raw helper class and delegate calls to it
(i.e. Advise)
Helper class uses Raw Unmanaged COM semantics and UCOM
classes
private UCOMIConnectionPoint m_ConnectionPoint = null;
...
UCOMIConnectionPointConatiner cpc =
(UCOMIConnectionPointContainer)interface;
[Link](ref iiid, out m_ConnectionPoint);
m_ConnectionPoint.Advise(...);
For more see
[Link]
[Link]
[Link]
Session Summary
All your existing COM investments
preserved and usable
Prepare COM components for use with
.NET
Interop works…
…But is hard in many non-trivial cases
[Link]
Resources
.NET and COM: The Complete Interoperability Guide, Adam Nathan,
Microsoft, Sam’s Publishing ISBN 067232170x
Visual C++.NET: A Primer for C++ Developers, Corera, Fraser,
Gentile, etc. Wrox. Chapters 7-8, ISBN 1861005962
COM and .NET Interoperability, Andrew Troelsen, APress,
1590590112
My .NET Blog [Link]
My Web Site [Link]
My Friends
– Peter Drayton [Link]
– Tomas Restapo [Link]
– Don Box [Link]
– Chris Sells [Link]
– Ingo Rammer [Link]
– Paresh Suthar [Link]
[Link]
Questions
[Link]