Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
http://samgentile.com
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
http://radio.weblogs.com/0105852/
Contacts
– .NET home at http://www.samgentile.com/
– ManagedCode@attbi.com
http://samgentile.com
What we will cover
Review of COM and .NET
Calling COM from .NET
Calling .NET from COM
Pitfalls and Workarounds
http://samgentile.com
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
http://samgentile.com
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
http://samgentile.com
Demonstrations
Generating Interops (RCWs) with VS.NET
and tlbimp
Peering Inside Interops
Using tlbexp to generate Interops and
Implementing Class Interfaces
http://samgentile.com
Agenda
Review of COM and .NET
Calling COM from .NET
Calling .NET from COM
Pitfalls and Workarounds
http://samgentile.com
COM Is…
Interface based
Separate interface and implementation
Dynamic runtime linking
Binary Standard Object Model
Language neutral (in theory)
Deterministic
http://samgentile.com
.NET
Metadata
Virtualization of contracts
Runtime provides services
Virtualization of types (CTS)
Versioning
Deterministic
http://samgentile.com
Review of .NET Concepts
Managed code
Metadata
Assemblies
Strong names
GAC
Reflection
Custom Attributes
http://samgentile.com
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
http://samgentile.com
Agenda
Review of COM and .NET
.NET to COM
COM to .NET
Windows Forms
http://samgentile.com
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
http://samgentile.com
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
http://samgentile.com
Creating an RCW
Visual Studio .NET Add COM Reference
– Easiest and least flexible
– Type lib provides definition of types
Framework SDK tlbimp.exe
System.Runtime.InteropServices.TypeLib
Converter
http://samgentile.com
Using VS.NET
Add COM Reference – equivalent to:
– Tlbimp InputFile /out:Interop.LibraryName.dll /namespace:LibraryName
/sysarray
http://samgentile.com
Using tlbimp.exe
More flexible and don’t need VS.NET
Command line tool generates .NET
Interop Assembly
Reads type info from TLB and
generates .NET metadata
Simplest form: tlbimp foo.dll
Can specify name of output assembly with
/out - important
http://samgentile.com
Important tlbimp options
/sysarray converts SAFEARRAYS to
System.Array
/ref to resolve references is key
/delaysign option
/publickey, /keyfile, /keycontainer affect
signing
/primary will be discussed soon
http://samgentile.com
Demo 1
Generating Interops (RCWs) with
VS.NET and tlbimp
Using Interops and RCWs from C#
clients
http://samgentile.com
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
http://samgentile.com
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.
http://samgentile.com
COM value type COM reference type System type
bool bool * System.Int32
char, small char *, small * System.SByte
short short * System.Int16
long, int long *, int * System.Int32
Hyper hyper * System.Int64
unsigned char, byte unsigned char *, byte System.Byte
*
wchar_t, unsigned wchar_t *, unsigned System.UInt16
short short *
unsigned long, unsigned long *, System.UInt32
unsigned int unsigned int *
float float * System.Single
double double * System.Double
void * void ** System.IntPtr
HRESULT HRESULT * System.Int16 or
http://samgentile.com
System.IntPtr
Type Conversion Specifics
DATE System.DateTime
BSTR System.String
SafeArray(int) int[]
OLECOLOR uint
CURRENCY System.Decimal
VARIANT System.Object
Interface interface IMyInterface
IMyInterface
coclass Bar class Bar
IEnumVariant IEnumerator
http://samgentile.com
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
http://samgentile.com
Demo 2
Peering Inside Interops
http://samgentile.com
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
http://samgentile.com
Agenda
Review of COM and .NET
.NET to COM
COM to .NET
Pitfalls and Workarounds
http://samgentile.com
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
http://samgentile.com
COM to .NET Interop
Reference IMyInterface
Counted
Client
http://samgentile.com
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
http://samgentile.com
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
http://samgentile.com
Generating COM Type
Definitions
1. Tlbexp.exe command line tool
2. Regasm.exe command line tool
3. VS.NET Project Properties (calls 1,2)
4. Programmatically using
TypeLibConverter
5. Regsvcs.exe command line tool
http://samgentile.com
Using tlbexp.exe
Command line tool
Very few options needed because
attributes
Most options useless
Syntax: tlbexp.exe foo.dll
/out flag lets you control name
http://samgentile.com
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
MSCOREE.DLL
http://samgentile.com
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\mscoree.dll”
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”
http://samgentile.com
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
http://samgentile.com
.NET Metadata to COM Types
DATE System.DateTime
BSTR System.String
Safearray(int) int[]
OLECOLOR System.Drawing.Color
DECIMAL System.Decimal
VARIANT System.Object
Interface interface IMyInterface
IMyInterface
Class CMyClass coclass CMyClass
IEnumVariant IEnumerator
http://samgentile.com
Controlling Translation
ComVisible attribute
InterfaceTypeAttribute
ClassInterfaceAttribute
StructLayoutAttribute
GuidAttribute
ProgIdAttribute
http://samgentile.com
Understanding the Class
Interface
http://samgentile.com
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
http://samgentile.com
The Case Against Class
Interfaces
MSFT: Avoid and use ClassInterface.None
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
http://samgentile.com
Recommended Approach
Microsoft recommends defining and
implementing real .NET interface
And ClassInterfaceType.None
Generates real COM interface that be
accessed through a v-table
Let’s see how in the following demo
http://samgentile.com
Demo 3
Using tlbexp to generate Interops
Regasm
Class Interfaces
http://samgentile.com
Agenda
Review of COM and .NET
.NET to COM
COM to .NET
Pitfalls and Workarounds
http://samgentile.com
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
Marshal.ChangeWrapperHandleStrength
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
http://samgentile.com
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
IUnknown.Release on every cached interface pointer
Marshal.ReleaseComObject 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
http://samgentile.com
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
Marshal.ChangeWrapperHandleStrength
http://samgentile.com
Case 1
In EventHandlers:
Marshal.ReleaseComObject(interface);
And:
public new Dispose()
{
// unsubscribe from event
m_foo.OnSomething -= delegate(eventHandler);
Marshal.ReleaseComObject(m_foo);
m_foo = null;
}
http://samgentile.com
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;
cpc.FindConnectionPoint(ref iiid, out m_ConnectionPoint);
m_ConnectionPoint.Advise(...);
For more see
http://radio.weblogs.com/0111019/stories/2002/08/05/connectionPoi
ntBasedEventHandlingBetweenComAndnet.html
http://samgentile.com
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
http://samgentile.com
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 http://radio.weblogs.com/0105852/
My Web Site http://www.samgentile.com/
My Friends
– Peter Drayton http://www.razorsoft.net/weblog/index.html
– Tomas Restapo http://www.winterdom.com/weblog/
– Don Box http://www.gotdotnet.com/team/dbox/spoutlet.aspx
– Chris Sells http://www.sellsbrothers.com/
– Ingo Rammer http://www.dotnetremoting.cc/DotNetCentric/
– Paresh Suthar http://radio.weblogs.com/0111019/
http://samgentile.com
Questions
http://samgentile.com