Upload
others
View
5
Download
0
Embed Size (px)
Citation preview
COM Interop & P/Invoke
.NET and the Old World
Objectives
Introduction to interoperation between.NET and COMCOM 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 CodeLifetime managementMetadata
Common Type System
Inheritance concept
Threading model
Error handling mechanisms
Registration
Calling COM Services from .NET
Metadata for COM class must be availableCan be generated from Type LibraryCan 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
Metadata must be available at compile timeMust be referenced in development environment
At runtime, Metadata and COM library must be available
COMLib.COMClass aCOMObject;
aCOMObject = new COMLib.COMClass;
Runtime Callable WrappersPreserving object identity
Maintaining object lifetime
Proxying interfaces
Marshalling method calls
Consuming selected interfaces
.NET Object
COM Object RCW
IUnknownIDispatch
IIfcIIfc
Consuming Selected Interfaces
ISupportErrorInfo
Additional information with exceptions
IProvideClassInfo
Typed wrapper
ITypeInfo
TlbImp converts references to 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 aCOMObject;
aCOMObject = new COMLib.COMClass();
aCOMObject.SomeFunc();
Marshal.ReleaseComObject(aCOMObject);
Converting Type Library to MetadataUse 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 preservedIDL:
[custom(„FE42746F-...-AC...84178”, “Value”)]
.NET:
[IdlAttribute(„FE42746F-...-AC...84178”, “Value”)]
Conversions by TlbImp 1/2
Library level Library to namespace
Module is converted to classConstants within module to constants within that classModule functions to static members of the class
library COMLib { interface Widget {}; coclass Slingshot {};
}
namespace COMLib { interface Widget {}; class Slingshot {};
}
custom(0F21F359-AB84-41e8-9A78-36D110E6D2F9, „MyNamespace.Class")
Conversions by TlbImp 2/2
InterfacesBase interfaces IUnknown and IDispatch are stripped
Type definitions are not importedImported as the underlying types
PropertiesGenerates corresponding get and set
Events
namespace COMLib { interface Widget {}; class Slingshot {};
}
Marshalling Isomorphic Types
Integer, Float Values
Non-isomorphic TypesBooleans, Chars, Strings, Arrays, Objects
Copying vs. PinningIsomorphic Types are pinnedNon-Isomorphic Types are copiedWatch out with Unicode Strings!
Passing by value actually passes a reference!
Passing by reference creates new String
Marshalling a UnionUse attributed struct in .NET
StructLayout attribute forSequential layout – is a regular structureDefault Union layoutExplicit layout
[StructLayout(Layout.Explicit)] public class SYSTEM_INFO { [FieldOffset(0)] ulong OemId; [FieldOffset(4)] ulong PageSize; [FieldOffset(16)] ulong ActiveProcessorMask;[FieldOffset(20)] ulong NumberOfProcessors;[FieldOffset(24)] ulong ProcessorType;
}
Inheritance COM uses containment/aggregation
.NET adds implementation inheritance
Mixed-mode objectsManaged objects can derive from COM objects
Must have metadata available
Must be instantiatable
Must be aggregatable
Interfaces can be inheritedIUnknown, IDispatch derivation is removed
Calling an COM Servernamespace CallingCOMServer
{
using System;
using COMServerLib;
public class DotNET_COMClient
{...
public static int Main(string[] args)
{
MyCOMServer myS = new MyCOMServer();
return (myS.Add (17,4));
}
}
};
Late Bound Activation
Accomplished by Reflection APINo difference whether COM or .NET object
Type can be obtained from ProgID or ClsID
InvokeMember to access methods and properties
Metadata is not needed, but usefulCOM object is wrapped by __ComObject
Late Bound Call to COM Servernamespace LateBoundClient
{
using System.Reflection;...Type typ;Object obj;Object[] prms = new Object[2];int r;
typ = Type.GetTypeFromProgID(„MyLib.MyServer");obj = Activator.CreateInstance(typ);prms[0] = 10; prms[1] = 150;
r = (int)typ.InvokeMember(„aMethod", BindingFlags.InvokeMethod, null, obj, prms);
...
}
Calling an Automation Servernamespace AutomationClient{
using EmotionalTulip; using System.Reflection;...TulipApplication tulipApp = new TulipApplication();
Type t = tulipApp.GetType();Object[] prms = new Object[1];prms[0] = true;
t.InvokeMember("Visible", BindingFlags.SetProperty,null, tulipApp, prms);
t.InvokeMember("Exit", BindingFlags.InvokeMethod, null, tulipApp, null);
...}
ThreadingMost COM objects are single threaded
.NET thread must initialize COM apartmentDeferred initialization Initialized either as MTA or STA
Runtime provides transparent use of COM threadsAccess apartment threaded objects through proxyCan avoid proxy by setting apartment state of thread
Let the runtime initialize the default MTA
Setting ApartmentState explicitly eliminates proxyDo before the object is created!
Using ApartmentState
using System.Threading;using APTOBJLib;
AptSimple obj = new AptSimple ();
obj.Counter = 1;
using System.Threading; using APTOBJLib;
Thread.CurrentThread.ApartmentState = ApartmentState.STA;
AptSimple obj = new AptSimple();
obj.Counter = 1;
Results and ExceptionsCOM reports errors via result code
.NET methods throws exceptionsRuntime manages transition
HRESULT specifies exception class
Extended information via IErrorInfoCOM object should implement interfaceErrorCode, HelpLink, MessageSource, StackTrace, TargetSite
PreserveSigAttribute in custom wrapperSuppresses translation to exception
Design for InteroperabilityProvide and register Type Libraries
Use Automation Compliant Data Types
Use Chunky CallsMarshalling may be costly
Explicitly Free External Resources
Avoid Using Members of System.ObjectObject, Equals, Finalize, GetHashCode, GetTypeMemberwiseClone and ToString
Restrictions and Limitations
void* parameters need marshalling
Success HRESULTS cannot be distinguishedFailure HRESULT do raise exceptionsSuccess code don‘t
Typedef do not get through
Variable length arrays cannot be imported
is imported asHRESULT DoSomething(int cb, [in] byte buf[]);
public void DoSomething(int cb, ref Byte buf);
Calling .NET Services from COM
Use .NET class like COM classType and methods must be public
Early and late bound activation
Convert Metadata to Type Library
Wrapper of .NET component must be registeredCan use RegAsm tool
COM Callable Wrappers Responsibilities of the COM Callable Wrapper
Preserving Object Identity Maintaining object lifetime Proxying custom interfaces Marshalling method calls Synthezising selected interfaces
COM Object CCW
.NET Object
IUnknownIDispatch
IIfc
IIfc
Identity and Lifetime Control Single CCW for .NET class insures identity
No matter how many COM classes call to itNo matter how object is created
Each interface of the CCW is referenced countedNo reference counting on the managed object
Managed objects lives at least while the CCW livesBut finalization is undeterministicManaged objects may occasionally leak
Should be explicitly freed using CoEEShutDown
Synthesizing Selected InterfacesmyClass implementing myItf derived from myBase
_myClass: all methods of myItf, myBase, myClass_myBase: all methods of myBasemyItf: all methods of myItf_Object
Additional interfaces implemented on CCWIUnknown
Standard implementation for lifetime management
IDispatchAutomation compatible or internal
ITypeInfo, IProvideClassInfo, IErrorInfo
Exporting a Type Library
Use SDK tool TlbExpType library exported from assembly
Use the TypeLibConverter Class
Leave the conversion to RegAsm utility
Export process involves a lot of conversionsName collisions are solved using name mangling
tlbexp AssemblyName.dll /out:TlbFile
Sample Conversionpublic class MyClass {public void Write(string p, ref string q) {...};public int GetInt() {...};
}
[uuid(…), hidden, dual, odl, nonextensible, oleautomation]interface _MyClass : IDispatch {[propget] HRESULT ToString([out, retval] BSTR* pRetVal);HRESULT Equals([in] VARIANT obj,
[out, retval] VARIANT_BOOL* pRetVal);HRESULT GetHashCode([out, retval] long* pRetVal); HRESULT GetType([out, retval] _Type** pRetVal);HRESULT Write([in] BSTR p, [in, out] BSTR* q);HRESULT GetInt([out, retval] long* pRetVal);
}
[ uuid(...) ]coclass MyClass {[default] interface _MyClass;interface _Object;
}
Special Attributes
Attributes control the conversion process[In], [Out], [MarshalAs(...)]
Parameters, Fields
[GuidAttribute("...")]
Assemblies, classes, interfaces, structures, enumerations, or delegates
Uses Marshal.GenerateGuidForType function
[ComVisibleAttribute(...)]
Individual type or assembly level
Registration (RegAsm)Adds assembly information to the registry
regasm assembly /tlb:tlbfile /regfile:regfile
[HKCR\Namespace.Class] @=„Namespace.Class"
[HKCR\Namespace.Class\CLSID] @="{...}"
[HKCR\CLSID\{...}] @="Namespace.Class"
[HKCR\CLSID\{...}\InprocServer32]
@="C:\WINNT\System32\MSCorEE.dll"
"ThreadingModel"="Both"
"Class"="Namespace.Class"
"Assembly"=„AssemblyName, Ver=1.0.0.0, Loc="""
[HKCR\CLSID\{...}\ProgId] @="Namespace.Class"
Results and ExceptionsYou know: COM uses HRESULTs, .NET exceptions
Can‘t propagate exceptions to unmanaged codeUser defined exception classes define HRESULT
Default is HRESULT of base class
Class NoAccessException : public ApplicationException{
NoAccessException(){
HResult = E_ACCESSDENIED; }
} CMyClass::MethodThatThrows{
NoAccessException e = new NoAccessException(); throw e;
}
Aggregation and Containment Simple requirements to be met
Class must be publicClass must have default constructor
Containment:Simply create instance and delegate calls to it
AggregationJust use CoCreateInstance
COM object‘s IUnknown as outer IUnknown
Queries for unsupported interfaces are delegated backReference counting is done on outer IUnknown
Exposing Inheritance Managed interfaces provide inheritance
Always derive from IUnknown or IDispatchInterface inheritance is flattened
Advantages with versioningUsers of interfaces not effected when base changes Interfaces may be separately attributed
DisadvantageInterfaces cannot be used polymorphically
Converted Inheritance Tree Original managed codeinterface IBase{
void m1(); }
interface IDrvd : IBase{
void m2(); }
class CDrvdImpl : IDrvd{
void m1();void m2();
}
Converted type libraryinterface IBase : IDispatch {
void m1(); }
interface IDrvd : IDispatch {
void m2(); }
interface _CDrvdImpl : IDispatch{
boolean Equals(); // and other methods of objectvoid m1(); void m2();
}
coclass CDrvdImpl{
interface IBase; interface IDerived; interface _CDerivedImpl;interface _Object;
}
Restrictions and Limitations
Parameterized constructors are not exposedAlways uses default constructor
Static methods are not exposed Wrapping with instance method required
Calling Platform Services from .NET
Calling static functions in DLLs
P/Invoke provides servicesLocates implementing DLLLoads DLLFinds function address
Fuzzy name matching algorithm
Pushes arguments on stackPerforms marshallingEnables pre-emptive garbage collectionTransfers control to unmanaged code
Code Samplenamespace HelloWorld{using System;
class MyClass{[dllimport(„user32.dll“, CharSet=CharSet.Ansi)]static extern int MessageBox(int h, string m,
string c, int t);
public static int Main() {return MessageBox(0, "Hello World!", "Caption", 0);
}}
}
Marshalling
Strings are copiedConverted to platform format: ANSI or UnicodeStrings are never copied back – use StringBuilder
Interfaces supportedCannot be used as out parameter
Arrays of primitive types can be used
Custom marshalling using attributes
Showcase for the Standard Marshaller
int SomeFunc( int sel, void* buf, int size);
[DllImport("some.dll")]static extern int SomeFunc(int sel,
[MarshalAs(UnmanagedType.LPArray)] byte[] buf, int size);
[DllImport("some.dll", EntryPoint="SomeFunc")]static extern int SomeFunc_String(int sel,
StringBuilder sb, int size);
[DllImport("some.dll", EntryPoint="SomeFunc")]static extern int SomeFunc_Int(int sel,
ref int i, int size);
Given a method in some DLL
Standard declaration may need special decoding
May use taylored versions
Callbacks Unmanaged code can call back to managed code
Unmanaged parameter is function pointerMust supply parameter as delegateP/Invoke creates callback thunk
Passes address of thunk as callback parameter
Managed Code
.NET Application
Call passes pointer to callback function
Implementation of callback function
Unmanaged Code
DLL
DLL function
Code Samplepublic class EnumReport{
public bool Report(int hwnd, int lParam) { // report the window handle
Console.Write("Window handle is ");Console.WriteLine(hwnd);return true;
}};
public class SampleClass{
delegate bool CallBack(int hwnd, int lParam);
[DllImport("user32")]static extern int EnumWindows(CallBack x, int y);
public static void Main() {
EnumReport er = new EnumReport();CallBack myCallBack = new CallBack(er.Report);EnumWindows(myCallBack, 0);
}}
Security
Suppressing demands for unmanaged code permission
Calling class must be fully trustedUnmanaged code runs with no runtime checks
Any code calling that class must be fully trusted
[SuppressUnmanagedCodeSecurityAttribute()] [dllimport("some.dll")] private static extern int EntryPoint(args);
Component ServicesFundamental building blocks
Just-In-Time activation, synchronization, poolingTransaction processing, shared properties, etc.
Mostly restricted to Windows 2000
COM+ generates and services context object
.NET Client
ContextObject A
ContextObject B
.NET Framework
COM+ Services
Object A(TX required)
Object B(TX supported)
Using COM+ Services
Derive class from System.ServicedComponent
Add attributes to parameterize servicesUse attributes from Microsoft.ComServices namespaceHelper classes: ContextUtil, RegistrationHelper
Use regsvcs to register assembly as serverOr rely on lazy registration
Transactions Sampleusing System;using System.Runtime.CompilerServices;using Microsoft.ComServices;
[assembly:ApplicationName(“TestApp”)]
[Transaction(TransactionOption.Required)]public class Account : ServicedComponent {
[AutoComplete] public void Debit(int amount) {// Any exception thrown here aborts transaction // otherwise transaction commits
}}
class client {static int Main() {
Account accountX = new Account(); accountX.Debit(100); return 0;
}}
Summary
Preserving investments and moving onKeep COM components and enhance using .NETAdd managed components to COM projectsUse Windows Component Services
Prepare COM components for use with .NET
.NET offers maximum transparency
Questions?