View
102
Download
2
Category
Tags:
Preview:
DESCRIPTION
.NET and the Old World. COM Interop & P/Invoke. 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 - PowerPoint PPT Presentation
Citation preview
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
Metadata must be available at compile time Must be referenced in development environment
At runtime, Metadata and COM library must be available
COMLib.COMClass aCOMObject;
aCOMObject = new COMLib.COMClass;
Runtime Callable Wrappers Preserving object identity
Maintaining object lifetime
Proxying interfaces
Marshalling method calls
Consuming selected interfaces
.NET Object
COM Object RCW
IUnknown
IDispatch
IIfc
IIfc
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 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:
[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 class Constants within module to constants within that class Module 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
Interfaces Base interfaces IUnknown and IDispatch are stripped
Type definitions are not imported Imported as the underlying types
Properties Generates corresponding get and set
Events
namespace COMLib { interface Widget {}; 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 Sequential layout – is a regular structure Default Union layout Explicit 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 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 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 API No 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 useful COM 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);
...}
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
Let the runtime initialize the default MTA
Setting ApartmentState explicitly eliminates proxy Do 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 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, Message Source, StackTrace, TargetSite
PreserveSigAttribute in custom wrapper 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
Restrictions and Limitations
void* parameters need marshalling
Success HRESULTS cannot be distinguished Failure HRESULT do raise exceptions Success code don‘t
Typedef do not get through
Variable length arrays cannot be imported
is imported as
HRESULT DoSomething(int cb, [in] byte buf[]);
public void DoSomething(int cb, ref Byte buf);
Calling .NET Services from COM
Use .NET class like COM class Type and methods must be public
Early and late bound activation
Convert Metadata to Type Library
Wrapper of .NET component must be registered Can 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
IUnknown
IDispatch
IIfc
IIfc
Identity and Lifetime Control
Single CCW for .NET class insures identity No matter how many COM classes call to it No matter how object is created
Each interface of the CCW is referenced counted No reference counting on the managed object
Managed objects lives at least while the CCW lives But finalization is undeterministic Managed objects may occasionally leak
Should be explicitly freed using CoEEShutDown
Synthesizing Selected Interfaces myClass implementing myItf derived from myBase
_myClass: all methods of myItf, myBase, myClass _myBase: all methods of myBase myItf: all methods of myItf _Object
Additional interfaces implemented on CCW IUnknown
Standard implementation for lifetime management
IDispatch Automation compatible or internal
ITypeInfo, IProvideClassInfo, IErrorInfo
Exporting a Type Library
Use SDK tool TlbExp Type library exported from assembly
Use the TypeLibConverter Class
Leave the conversion to RegAsm utility
Export process involves a lot of conversions Name 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 Exceptions You know: COM uses HRESULTs, .NET exceptions
Can‘t propagate exceptions to unmanaged code User 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 public Class must have default constructor
Containment: Simply create instance and delegate calls to it
Aggregation Just use CoCreateInstance
COM object‘s IUnknown as outer IUnknown
Queries for unsupported interfaces are delegated back Reference counting is done on outer IUnknown
Exposing Inheritance
Managed interfaces provide inheritance Always derive from IUnknown or IDispatch Interface inheritance is flattened
Advantages with versioning Users of interfaces not effected when base changes Interfaces may be separately attributed
Disadvantage Interfaces 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 object void m1(); void m2(); }
coclass CDrvdImpl { interface IBase; interface IDerived; interface _CDerivedImpl; interface _Object; }
Restrictions and Limitations
Parameterized constructors are not exposed Always 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 services Locates implementing DLL Loads DLL Finds function address
Fuzzy name matching algorithm
Pushes arguments on stack Performs marshalling Enables pre-emptive garbage collection Transfers control to unmanaged code
Code Sample
namespace 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 copied Converted to platform format: ANSI or Unicode Strings are never copied back – use StringBuilder
Interfaces supported Cannot 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 pointer Must supply parameter as delegate P/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 trusted Unmanaged 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 Services Fundamental building blocks
Just-In-Time activation, synchronization, pooling Transaction processing, shared properties, etc.
Mostly restricted to Windows 2000
COM+ generates and services context object
.NET Client
Context
Object A
Context
Object 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 services Use attributes from Microsoft.ComServices namespace Helper classes: ContextUtil, RegistrationHelper
Use regsvcs to register assembly as server Or 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 on Keep COM components and enhance using .NET Add managed components to COM projects Use Windows Component Services
Prepare COM components for use with .NET
.NET offers maximum transparency
Questions?
Recommended