Presentation is loading. Please wait.

Presentation is loading. Please wait.

P/Invoke Made Easy Wei-Chen Wang. Marshaling governs how data is passed between managed and unmanaged memory during platform invoke. Function Call Interop.

Similar presentations


Presentation on theme: "P/Invoke Made Easy Wei-Chen Wang. Marshaling governs how data is passed between managed and unmanaged memory during platform invoke. Function Call Interop."— Presentation transcript:

1 P/Invoke Made Easy Wei-Chen Wang

2 Marshaling governs how data is passed between managed and unmanaged memory during platform invoke. Function Call Interop Marshaling In Parameters Out Parameters Managed Client Unmanage d Library

3 Pass Parameters 3 ways to transfer parameters between managed code and unmanaged code 1.Marshaling the data by Marshaler 2.Allocating an unmanaged memory block, populating the data, and passing the address of the memory block 3.Just passing the address of the managed object without marshaling the object

4 The unsafe Way unsafe { IntPtr ms = Marshal.AllocHGlobal(sizeof(MyStruct)); MyStruct *pms = (MyStruct*)ms; ms->a = MAGIC_NUMBER; ms->str1 = Marshal.AllocHGlobal(MAGIC_STRING.length*2); strcpy( ms->str1, MAGIC_STRING); func( ms ); } /* C Declarations */ struct MyStruct { DWORD num; LPWSTR str1; } void func(struct MyStruct *ms); /* C# Wrapper */ struct MyStruct { Int32 num; IntPtr str1; } [DllImport("dll.dll")] void func(IntPtr ms); directly operate the object via pointer pass the address, IntPtr, of the struct

5 A Better Way { IntPtr pms = Marshal.AllocHGlobal(sizeof(MyStruct)); MyStruct ms; ms.num = MAGIC_NUMBER; ms.str1 = Marshal.StringToHGlobalUni(MAGIC_STRING); Marshal.StructureToPtr(ms, pms, false); func(ms); } /* C Declarations */ struct MyStruct { DWORD num; LPWSTR str1; } void func(struct MyStruct *ms); /* C# Wrapper */ struct MyStruct { Int32 num; IntPtr str1; } [DllImport("dll.dll")] void func(IntPtr ms); operate the managed object allocate memory and copy string by Mashaler utility

6 Summary, So Far... unsafe code is hard to write (in C#), hard to debug, and lack of compiling-time and runtime checking. In most case, we don't have to use unsafe pointers. We should operate everything on managed objects, and convert them to unmanaged objects only when we want to perform platform invoke. It is free to encapsulate the converting code as methods of the object. Methods won't change the memory layout of the object. Using AllocHGlobal to manually allocate an unmanaged memory block is necessary, if we want to pass the object for an asynchronous call.

7 Marshaling by Marshaler (the best way) { MyStruct ms; ms.num = MAGIC_NUMBER; ms.str = MAGIC_STRING; func(ref ms); } /* C Declarations */ struct MyStruct { DWORD num; LPWSTR str1; } void func(struct MyStruct *ms); /* C# Wrapper */ struct MyStruct { Int32 num; [MarshalAs(UnmanagedType.LPWStr)] String str1; } [DllImport("dll.dll")] void func(ref MyStruct ms); indicate the string should be converted to a pointer, which points to a string buffer num str1 string operate on managed object the Marsahler will convert it to unmanaged object for you

8 Marshaling Internals 9. Return from C# wrapper Heap Object Code Stack Heap Code Stack C# Wrapper()Function() 1. Call C# wrapper Object 2. Marshal managed In-parameters to native 3. Call native code Parameters 6. Clean up managed Out- parameters 7. Marshal native Out-parameters to managed Object 8. Clean up native data Managed Memory Unmanaged Memory 5. Return from native code 4. native code operate on unmanaged object

9 Default Marshaling for Blittable Types struct MyStruct { Int32 a; Int32 b; public MyStruct(Int32 _a, Int32 _b) { a = _a; b = _b; } } [DllImport("dll.dll")] void CFunction(ref MyStruct ms); MyStruct ms = new MyStruct(MAGIC_A, MAGIC_B); CFunction(ref ms); struct MyStruct { DWORD a; DWORD b; }; void f(struct MyStruct *ms);

10 Default Marshaling for Blittable Types 8. Return from C# wrapper Heap Code Stack Heap Code Stack C# Wrapper()Function() 1. Call C# wrapper4. Call native code Parameters Object Managed Memory Unmanaged Memory 6. Return from native code 5. native code operate on managed object 2. Pin the object 3. Pass the address (call-by- reference) 7. Un-Pin the object

11 Write Your Own Marshaler class MyClass { /*... */ } class MyClassMarshaler: ICustomMarshaler { public void CleanUpManagedData(object ManagedObj); public void CleanUpNativeData(IntPtr pNativeData); public IntPtr MarshalManagedToNative(object ManagedObj); public object MarshalNativeToManaged(IntPtr pNativeData); } [DllImport("dll.dll")] static extern void Function( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(MyClassMarshaler))] MyClass mc ); Custom marshaler used For marshaling MyClass Use MyClassMarshaler to marshal MyClass

12 ICustomMarshaler in Action Heap Object Code Stack Heap Code Stack C# Wrapper()Function() Object Parameters Object Managed Memory Unmanaged Memory 1. GetInstance() to get a instance of the marshaler 2. MarshalManaged ToNative() 3. CleanUp ManagedData() 3. Call/Return from native code 4. MarshalNative ToManaged() 5. CleanUp NativeData()

13 Delegate/Callback Heap Code Stack Code Stack delegate delegate() Managed Memory Unmanaged Memory callback() C# Wrapper()Function() 2. pass delegate marshaled as a function pointer 4. invoke callback Parameters 1. Call C# wrapper 7. Return from C# wrapper 3. Call native code 6. Return from native code 5. return from callback marshaling parameters

14 Windows String at A Glance ANSI String= char array=user locale encoding Unicode String= wchar array=UTF-16 Little-Endian (Unicode is not necessary UTF-16LE, but in Windows, it is) T-String= TCHAR array, char or wchar depends on compiling configuration WINUSERAPI int WINAPI MessageBoxA( __in_opt HWND hWnd, __in_opt LPCSTR lpText, __in_opt LPCSTR lpCaption, __in UINT uType); WINUSERAPI int WINAPI MessageBoxW( __in_opt HWND hWnd, __in_opt LPCWSTR lpText, __in_opt LPCWSTR lpCaption, __in UINT uType); #ifdef UNICODE #define MessageBox MessageBoxW #else #define MessageBox MessageBoxA #endif // !UNICODE

15 [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] struct MyStruct { [MarshalAs(UnmanagedType.LPStr)] string str1; [MarshalAs(UnmanagedType.LPWStr)] string str2; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = N)] string str3; } More on Marshaling String struct MyStruct { LPSTR str1; LPWSTR str2; WCHAR str3[N]; }; MyStruct ms; ms.str1 = "Hello"; ms.str2 = "world"; ms.str3 = "!!!"; function( ref ms); Just pass the struct. The marshaler will do the rest for you

16 Get a String Out void GetString( LPWSTR *text, int nMaxCount ); [DllImport("dll.dll")] static extern void GetString(StringBuilder text, int nMaxCount); 1 [DllImport("dll.dll")] static extern void GetString(out String text, int nMaxCount); 3 Don't Do It! Just Do It StringBuilder sbtext = new Stringbuilder(STR_LENGTH); GetString( sbtext, sbtext.Capacity ); The memory allocated on text in GetString() will leak. [DllImport("dll.dll")] static extern void GetString(ref String text, int nMaxCount); 2 Don't Do It Too You cannot specify the size of the output buffer. void GetString( LPWSTR text, int nMaxCount );

17 InAttribute and OutAttribute InAttribute indicates that data should be marshaled from the caller to the callee, but not back to the caller OutAttribute indicates that data should be marshaled from callee back to caller Use the proper attributes to reduce unnecessary data copy /* Example */ [DllImport("mydll.dll")] static extern void func([in] ref MyStruct); // pass the reference(pointer) of the structure // parameter will be copied in, but not out

18 Keep Objects in Memory Local variable can be garbage collected as it reach a point where it appears no longer being referenced { MyFile file = new MyFile("file.txt"); IntPtr h = file.Handle; // file is eligible for finalization after push the parameter WriteFile( file.Handle ); } Suppose that MyFile will close the file handle when finalizing, hence the file handle will be invalid.

19 2 1 Solutions { MyFile file = new MyFile("file.txt"); IntPtr h = file.Handle; DoSomething(h); GC.KeepAlive(file); } { MyFile file = new MyFile("file.txt"); IntPtr h = file.Handle; GCHandle gh = GCHnalde.Alloc(file); DoSomething(h); gh.Free(); } 3 { MyFile file = new MyFile("file.txt"); IntPtr h = file.Handle; PInvoke(new HandleRef(file, h)); } The purpose of KeepAlive is to keep a reference the object. Besides that, KeepAlive have no side-effect. Allocating a GCHandle prevents the object from being collected. HandleRef guarantees that the object is not collected until the p/invoke completes

20 GCHandle GCHandle can be used to 1.prevent an objects being garbage collected 2.pin an object in memory, so it won't be relocated by GC (the object has to be of a value-type) 3.get the address of a pinned object

21 Pin an Object struct PinMe { int a; int b; } void PinYou() { struct PinMe pm; pm.a = MAGIC_NUM1; pm.b = MAGIC_NUM2; GCHandle gh = GCHandle.Alloc(pm, GCHandleType.Pinned); CFunction(gh.AddrOfPinnedObject()); gh.Free(); } Only blittable object can be pinned. The only reason to pin an blittable object it that you want to pass it to unmanaged code for an asynchronous operation, hence you don't want GC relocate it. manipulate the managed object in un managed code, CFunction()

22 Get the Handle of an Object void function() { Object obj = new Object(); GCHandle gh = GCHandle.Alloc(obj); CFunction( GCHandle.ToIntPtr(gh), Callback ); } void Callback(IntPtr p) { GCHandle gh = GCHandle.FromIntPtr(p); Object obj = gh.Target; gh.Free(); } We can't manipulate a managed object in unmanaged code, but we can pass the handle of it between managed and unmanaged code as callback data.

23 References Interop Marshaling http://msdn.microsoft.com/en-us/library/04fy9ya1.aspx Marshaling Data with Platform Invoke http://msdn.microsoft.com/en-us/library/fzhhdwae.aspx Blittable and Non-Blittable Types http://msdn.microsoft.com/en-us/library/75dwhxf7.aspx HandleRef Structure http://msdn.microsoft.com/en- us/library/system.runtime.interopservices.handleref.aspx SafeHandles: the best V2.0 feature of the.NET Framework https://blogs.msdn.com/bclteam/archive/2005/03/15/396335.aspx SafeHandle: A Reliability Case Study http://blogs.msdn.com/bclteam/archive/2005/03/16/396900.aspx The Truth About GCHandles http://blogs.msdn.com/clyon/archive/2005/03/18/398795.aspx GCHandle.ToIntPtr vs. GCHandle.AddrOfPinnedObject http://blogs.msdn.com/jmstall/archive/2006/10/09/gchandle_5F00_intptr.aspx GCHandles, Boxing and Heap Corruption http://blogs.msdn.com/clyon/archive/2004/09/17/230985.aspx SafeHandle: A Reliability Case Study [Brian Grunkemeyer] http://blogs.msdn.com/bclteam/archive/2005/03/16/396900.aspx


Download ppt "P/Invoke Made Easy Wei-Chen Wang. Marshaling governs how data is passed between managed and unmanaged memory during platform invoke. Function Call Interop."

Similar presentations


Ads by Google