COM Interop & P/Invoke .NET and the Old World
Objectives � Introduction to interoperation between � .NET and COM � COM and .NET � .NET and platform services
Contents � Section 1: Overview � Section 2: Calling COM Services from .NET � Section 3: Calling .NET Services from COM � Section 4: Calling Platform Services from .NET
Existing Stuff Must Coexist � Transition to .NET will be a lasting process � Portions of systems will remain as they are now � ...and even maintained in their current form � Interoperability increases the momentum of .NET � Goal is to achieve total transparency � Mere presence of .NET must not interfere with COM � Performance should not be hit
What is different in the two Worlds? � Managed Code vs. Unmanaged Code � Lifetime management � Metadata � Common Type System � Inheritance concept � Threading model � Error handling mechanisms � Registration
Calling COM Services from .NET � Metadata for COM class must be available � Can be generated from Type Library � Can declare Custom Wrapper � Use COM class like .NET class � Early and late bound activation � COM component must be registered locally
Early Bound Activation � Instantiate like creating a .NET object COMLib.COMClass COMLib.COMClass aCOMObject; aCOMObject; aCOMObject aCOMObject = new = new COMLib.COMClass; COMLib.COMClass; � Metadata must be available at compile time � Must be referenced in development environment � At runtime, Metadata and COM library must be available
Runtime Callable Wrappers � Preserving object identity � Maintaining object lifetime � Proxying interfaces � Marshalling method calls � Consuming selected interfaces IUnknown COM IDispatch .NET RCW Object Object IIfc IIfc
Consuming Selected Interfaces � ISupportErrorInfo ISupportErrorInfo � Additional information with exceptions � IProvideClassInfo IProvideClassInfo � Typed wrapper � ITypeInfo ITypeInfo TlbImp converts references to System.Type � TlbImp System.Type
Identity and Lifetime Control � COM lifetime control is wrapped by RCW � COM object released when RCW garbage collected � Non-deterministic finalization issue COMLib.COMClass COMLib.COMClass aCOMObject; aCOMObject; aCOMObject aCOMObject = new = new COMLib.COMClass(); COMLib.COMClass(); aCOMObject.SomeFunc(); aCOMObject.SomeFunc(); Marshal.ReleaseComObject(aCOMObject); Marshal.ReleaseComObject(aCOMObject);
Converting Type Library to Metadata � Use SDK tool TlbImp � Or just add reference to server in Visual Studio � Type library can be imported from executable � Reference resulting metadata file in project � Custom IDL attributes are preserved IDL: IDL: [custom(„FE42746F-...-AC...84178”, “Value”)] [custom(„FE42746F-...-AC...84178”, “Value”)] .NET: .NET: [IdlAttribute(„FE42746F-...-AC...84178”, “Value”)] [IdlAttribute(„FE42746F-...-AC...84178”, “Value”)]
Conversions by TlbImp 1/2 � Library level � Library to namespace library library COMLib { COMLib { namespace namespace COMLib { COMLib { interface Widget {}; interface Widget {}; interface Widget {}; interface Widget {}; coclass coclass Slingshot {}; Slingshot {}; class Slingshot {}; class Slingshot {}; } } custom(0F21F359-AB84-41e8-9A78-36D110E6D2F9, custom(0F21F359-AB84-41e8-9A78-36D110E6D2F9, „MyNamespace.Class") „MyNamespace.Class") � Module is converted to class � Constants within module to constants within that class � Module functions to static members of the class
Conversions by TlbImp 2/2 � Interfaces � Base interfaces IUnknown and IDispatch are stripped � Type definitions are not imported � Imported as the underlying types � Properties � Generates corresponding get get and set set � Events namespace namespace COMLib { COMLib { interface Widget {}; interface Widget {}; class Slingshot {}; class Slingshot {}; }
Marshalling � Isomorphic Types � Integer, Float Values � Non-isomorphic Types � Booleans, Chars, Strings, Arrays, Objects � Copying vs. Pinning � Isomorphic Types are pinned � Non-Isomorphic Types are copied � Watch out with Unicode Strings! � Passing by value actually passes a reference! � Passing by reference creates new String
Marshalling a Union � Use attributed struct in .NET StructLayout attribute for � StructLayout Sequential layout – is a regular structure � Sequential � Default Union Union layout Explicit layout � Explicit [StructLayout(Layout.Explicit)] [StructLayout(Layout.Explicit)] public class SYSTEM_INFO public class SYSTEM_INFO { { [FieldOffset(0)] ulong [FieldOffset(0)] ulong OemId; OemId; [FieldOffset(4)] ulong [FieldOffset(4)] ulong PageSize; PageSize; [FieldOffset(16)] ulong [FieldOffset(16)] ulong ActiveProcessorMask; ActiveProcessorMask; [FieldOffset(20)] ulong [FieldOffset(20)] ulong NumberOfProcessors; NumberOfProcessors; [FieldOffset(24)] ulong [FieldOffset(24)] ulong ProcessorType; ProcessorType; } }
Inheritance � COM uses containment/aggregation � .NET adds implementation inheritance � Mixed-mode objects � Managed objects can derive from COM objects � Must have metadata available � Must be instantiatable � Must be aggregatable � Interfaces can be inherited � IUnknown, IDispatch derivation is removed
Calling an COM Server namespace CallingCOMServer namespace CallingCOMServer { using System; using System; using COMServerLib; using COMServerLib; public class DotNET_COMClient public class DotNET_COMClient {... {... public static int Main(string[] args) public static int Main(string[] args) { MyCOMServer myS MyCOMServer myS = new MyCOMServer(); = new MyCOMServer(); return return (myS.Add (myS.Add (17,4)); (17,4)); } } }; };
Late Bound Activation � Accomplished by Reflection API � No difference whether COM or .NET object � Type can be obtained from ProgID or ClsID InvokeMember to access methods and properties � InvokeMember � Metadata is not needed, but useful � COM object is wrapped by __ComObject
Late Bound Call to COM Server namespace LateBoundClient namespace LateBoundClient { using System.Reflection; using System.Reflection; ... ... Type typ; Type typ; Object Object obj; obj; Object[] prms Object[] prms = new = new Object[2]; Object[2]; int r; int r; typ typ = Type.GetTypeFromProgID(„MyLib.MyServer"); = Type.GetTypeFromProgID(„MyLib.MyServer"); obj obj = Activator.CreateInstance(typ); = Activator.CreateInstance(typ); prms[0] = 10; prms[0] = 10; prms[1] = 150; prms[1] = 150; r = (int)typ.InvokeMember(„aMethod", r = (int)typ.InvokeMember(„aMethod", BindingFlags.InvokeMethod, null, obj, prms); BindingFlags.InvokeMethod, null, obj, prms); ... ... }
Calling an Automation Server namespace AutomationClient namespace AutomationClient { using EmotionalTulip; using EmotionalTulip; using System.Reflection; using System.Reflection; ... ... TulipApplication tulipApp = new TulipApplication(); TulipApplication tulipApp = new TulipApplication(); Type t Type t = tulipApp.GetType(); tulipApp.GetType(); Object[] prms Object[] prms = new Object[1]; = new Object[1]; prms[0] = true; prms[0] = true; t.InvokeMember("Visible", t.InvokeMember("Visible", BindingFlags.SetProperty, BindingFlags.SetProperty, null, tulipApp, prms); null, tulipApp, prms); t.InvokeMember("Exit", t.InvokeMember("Exit", BindingFlags.InvokeMethod, BindingFlags.InvokeMethod, null, tulipApp, null); null, tulipApp, null); ... ... }
Threading � Most COM objects are single threaded � .NET thread must initialize COM apartment � Deferred initialization � Initialized either as MTA or STA � Runtime provides transparent use of COM threads � Access apartment threaded objects through proxy � Can avoid proxy by setting apartment state of thread
Using ApartmentState � Let the runtime initialize the default MTA using System.Threading; using System.Threading; using APTOBJLib; using APTOBJLib; AptSimple AptSimple obj obj = new AptSimple = new AptSimple (); (); obj.Counter obj.Counter = 1; = 1; � Setting ApartmentState explicitly eliminates proxy � Do before the object is created! using System.Threading; using System.Threading; using APTOBJLib; using APTOBJLib; Thread.CurrentThread.ApartmentState Thread.CurrentThread.ApartmentState = = ApartmentState.STA; ApartmentState.STA; AptSimple AptSimple obj obj = new AptSimple(); = new AptSimple(); obj.Counter obj.Counter = 1; = 1;
Results and Exceptions � COM reports errors via result code � .NET methods throws exceptions � Runtime manages transition � HRESULT specifies exception class � Extended information via IErrorInfo � COM object should implement interface ErrorCode , HelpLink HelpLink , Message � ErrorCode Message Source , StackTrace StackTrace , TargetSite � Source TargetSite PreserveSigAttribute in custom wrapper � PreserveSigAttribute � Suppresses translation to exception
Design for Interoperability � Provide and register Type Libraries � Use Automation Compliant Data Types � Use Chunky Calls � Marshalling may be costly � Explicitly Free External Resources � Avoid Using Members of System.Object � Object, Equals, Finalize, GetHashCode, GetType � MemberwiseClone and ToString
Recommend
More recommend