330 likes | 582 Views
.NET Interoperability. Liran Ben Haim liran@bna.co.il http://www.bna.co.il. Agenda. Overview P/invoke COM interop Performance What’s new in .NET framework 2.0 Questions . Overview. Platform Invoke Finding and invoking unmanaged functions
E N D
.NET Interoperability Liran Ben Haim liran@bna.co.il http://www.bna.co.il
Agenda • Overview • P/invoke • COM interop • Performance • What’s new in .NET framework 2.0 • Questions
Overview • Platform Invoke • Finding and invoking unmanaged functions • Marshaling managed arguments to and from unmanaged code • COM Interoperability • Runtime Callable Wrapper (RCW) • Used by managed clients to call a method on a COM object • COM Callable Wrapper (CCW) • Used by COM clients to call a method on a managed object
Win32 API / C DLL using System; using System.Runtime.InteropServices; class PlatformInvokeTest { [DllImport("msvcrt.dll")] public static extern int puts(string c); [DllImport("msvcrt.dll")] internal static extern int _flushall(); public static void Main() { puts("Test"); _flushall(); } }
C++ DLL (not COM) • Create a MC++ class library Or • Add 2 global functions: • Create object • Destroy object • Use dumpbin.exe to extract the required member functions • Use CallingConvention.ThisCall
Example class SAMP_API Csamp { int x; public: int getnum(void){return x;} void setnum(int x1){x=x1;} }; extern "C" Csamp SAMP_API *createobject() {return new Csamp;} extern "C" void SAMP_API deleteobject(Csamp *p) {delete p;}
[ DllImport( "samp.dll", EntryPoint="?getnum@Csamp@@QAEHXZ", CallingConvention=CallingConvention.ThisCall )] public static extern int getnum( IntPtr ths); [ DllImport( "samp.dll", EntryPoint="?setnum@Csamp@@QAEXH@Z", CallingConvention=CallingConvention.ThisCall )] public static extern void setnum( IntPtr ths,int n); [ DllImport( "samp.dll" )] public static extern IntPtr createobject(); [ DllImport( "samp.dll" )] public static extern void deleteobject(IntPtr p);
Using the object IntPtr p=Class1.createobject(); Class1.setnum(p,77); MessageBox.Show(Class1.getnum(p). ToString()); Class1.deleteobject(p);
Creating RCW • Adding reference to a COM component in VS.NET IDE • Tlbimp.exe • Tlbimp mycom.dll • Custom wrapper • Using reflection
Custom wrapper example Type excel; object[] parameter= new object[1]; object excelObject; excel = Type.GetTypeFromProgID("Excel.Application"); excelObject = Activator.CreateInstance(excel); parameter[0] = true; excel.InvokeMember("Visible", BindingFlags.SetProperty, null, excelObject, parameter);
Creating CCW • Tlbexp.exe • Regasm.exe • Register for com interop in VS.NET IDE
Marshaling • Governs how data is passed between managed and unmanaged memory during calls • Most data types require no conversion • byte • short • int • long • …
Custom marshaling [DllImport("msvcrt.dll")] public static extern int puts( [MarshalAs(UnmanagedType.LPStr)] string m); • Or use Marshal class
Marshal class • C++ typedef struct _MYSTRSTRUCT2 { char* buffer; UINT size; } MYSTRSTRUCT2; void TestOutArrayOfStructs(int* pSize, MYSTRSTRUCT2** ppStruct);
[ StructLayout( LayoutKind.Sequential)] public class MyStruct { public String buffer; public int size; } [ DllImport( "..\\LIB\\PinvokeLib.dll" )] public static extern void TestOutArrayOfStructs( out int size, out IntPtr outArray );
int size; IntPtr outArray; LibWrap.TestOutArrayOfStructs( out size, out outArray ); MyStruct[] manArray = new MyStruct[ size ]; IntPtr current = outArray; for( int i = 0; i < size; i++ ) { manArray[ i ] = new MyStruct(); Marshal.PtrToStructure( current, manArray[ i ]); Marshal.DestroyStructure( current, typeof(MyStruct) ); current = (IntPtr)((int)current + Marshal.SizeOf( manArray[ i ] )); } Marshal.FreeCoTaskMem( outArray );
Using unsafe code [ StructLayout( LayoutKind.Sequential )] public struct MyUnsafeStruct { public IntPtr buffer; public int size; } [ DllImport( "..\\LIB\\PinvokeLib.dll" )] public static extern void TestOutArrayOfStructs( out int size, MyUnsafeStruct** outArray );
public static unsafe void UsingUnsafe() { int size; MyUnsafeStruct* pResult; LibWrap.TestOutArrayOfStructs( out size, &pResult ); MyUnsafeStruct* pCurrent = pResult; for( int i = 0; i < size; i++, pCurrent++ ) { Console.WriteLine( "Element {0}: {1} {2}", i, Marshal.PtrToStringAnsi( pCurrent->buffer ), pCurrent->size ); Marshal.FreeCoTaskMem( pCurrent->buffer ); } Marshal.FreeCoTaskMem( (IntPtr)pResult ); }
Transition performance • With every transition from managed to unmanaged code (and vice versa) there is some performance overhead • Platform invoke call: • 10 machine instructions (approx) + data marshaling time • COM interop call: • 50 machine instructions + data marshaling • Make Chunky Calls
String vs. individual chars typedef struct _OSVERSIONINFOA { DWORD dwOSVersionInfoSize; DWORD dwMajorVersion; DWORD dwMinorVersion; DWORD dwBuildNumber; DWORD dwPlatformId; CHAR szCSDVersion[ 10 ]; } OSVERSIONINFOA, *POSVERSIONINFOA, *LPOSVERSIONINFOA;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] class OSVersionInfo { public UInt32 OSVersionInfoSize = (UInt32) Marshal.SizeOf(typeof(OSVersionInfo)); public UInt32 MajorVersion = 0; public UInt32 MinorVersion = 0; public UInt32 BuildNumber = 0; public UInt32 PlatformId = 0; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)] public String CSDVersion = null; }
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] internal struct OSVersionInfoBlittable { public UInt32 OSVersionInfoSize; public UInt32 MajorVersion; public UInt32 MinorVersion; public UInt32 BuildNumber; public UInt32 PlatformId; unsafe public String CSDVersion { get { fixed (Char* str = &CSDVersion0) { return new String(str); } } } Char CSDVersion0; Char CSDVersion1; Char CSDVersion2; Char CSDVersion3; Char CSDVersion4; Char CSDVersion5; Char CSDVersion6; Char CSDVersion7; Char CSDVersion8; Char CSDVersion9; }
Performance tips • If you can find a .NET type that satisfies your needs, make use of it • Primitive types require almost no marshaling at all • Translations from ASCII to Unicode are expensive
Example - MessageBox • 2 functions: • MessageBoxA - ansi • MessageBoxW – unicode [ DllImport( "User32.dll", EntryPoint="MessageBoxW", CharSet=CharSet.Unicode, ExactSpelling=true )] public static extern int MsgBox( int hWnd, String text, String caption, uint type );
Marshaling value types vs. reference types • Marshaling Value types is preferable • Managed structures are somewhat more similar to C structures than are managed classes • The runtime never reorganize the field layout of managed structures • The GC never moves unboxed values around in memory
SetLastError field [ DllImport( "User32.dll", SetLastError=true )] public static extern int MessageBox( IntPtr hWnd, String text, String caption, int type ); • False in c# • True in VB.NET • Use Marshal.GetLastWin32Error(); • Use FormatMessage() API
Design Considerations • Design chunky interfaces to avoid round trips. • Reduce round trips with a facade. • Implement IDisposable if you hold unmanaged resources across client calls. • Reduce or avoid the use of late binding and reflection. • In ASP.NET – Server.CreateObject or Server.CreateObjectFromClsid
More guidelines • Use IntPtr for manual marshaling. • Use [in] and [out] to avoid unnecessary marshaling. [DllImport("gdi32.dll", CharSet=CharSet.Auto)] public static extern int CreateFontIndirect( [In, MarshalAs(UnmanagedType.LPStruct)] LOGFONT lplf );
Code Access Security • Use SuppressUnmanagedCode for performance-critical trusted scenarios [DllImport("kernel32.dll"), SuppressUnmanagedCodeSecurity] public static extern bool Beep(int frequency, int duration); Allows managed code to call into unmanaged code without a stack walk • TLBIMP /unsafe • disable the full CAS stack walk for the unmanaged code
More info • Improving .NET Application Performance and Scalability http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt07.asp
New In .NET Framework 2.0 • C++/CLI will include some useful templates for simplifying C++ interop • The new SafeHandle class, provide safe and reliable means of manipulating operating system handles use with: [MethodImpl(MethodImplOptions.MustRun)]
Marshaling improvements • The ability to wrap native function pointers into delegates • Marshal.GetDelegateForFunctionPointer • Marshal.GetFunctionPointerForDelegate • The ability to marshal fixed-size arrays of structures inside structures • [module: DefaultCharSet(CharSet.Auto)]