c# - How do I write a custom marshaler which allows data to flow from native to managed? -
in attempting write custom marshaler related question (p/invoke c c# without knowing size of array), have come across cannot understand. first ever custom marshaler have written no doubt i'm missing obvious due ignorance.
here's c# code:
using system; using system.runtime.interopservices; using system.text; namespace custommarshaler { public class mycustommarshaler : icustommarshaler { static mycustommarshaler static_instance; public intptr marshalmanagedtonative(object managedobj) { if (managedobj == null) return intptr.zero; if (!(managedobj int[])) throw new marshaldirectiveexception("variablelengtharraymarshaler must used on int array."); int[] arr = (int[])managedobj; int size = sizeof(int) + arr.length * sizeof(int); intptr pnativedata = marshal.allochglobal(size); marshal.writeint32(pnativedata, arr.length); marshal.copy(arr, 0, pnativedata + sizeof(int), arr.length); return pnativedata; } public object marshalnativetomanaged(intptr pnativedata) { int len = marshal.readint32(pnativedata); int[] arr = new int[len]; marshal.copy(pnativedata + sizeof(int), arr, 0, len); return arr; } public void cleanupnativedata(intptr pnativedata) { marshal.freehglobal(pnativedata); } public void cleanupmanageddata(object managedobj) { } public int getnativedatasize() { return -1; } public static icustommarshaler getinstance(string cookie) { if (static_instance == null) { return static_instance = new mycustommarshaler(); } return static_instance; } } class program { [dllimport(@"mylib.dll")] private static extern void foo( [in, out, marshalas(unmanagedtype.custommarshaler, marshaltyperef = typeof(mycustommarshaler))] int[] arr ); static void main(string[] args) { int[] colortable = new int[] { 1, 2, 3, 6, 12 }; foo(colortable); foreach (int value in colortable) console.writeline(value); } } }
on other side trivial native dll, written in delphi happens.
library mylib; procedure foo(p: pinteger); stdcall; var i, len: integer; begin len := p^; writeln(len); := 1 len begin inc(p); writeln(p^); inc(p^); end; end; exports foo; begin end.
the idea array passed dll prints out length field, , values of array. native code increments each value of array 1.
so, expect see output:
5 1 2 3 6 12 2 3 4 7 13
but unfortunately see output:
5 1 2 3 6 12 1 2 3 6 12
under debugger can see marshalnativetomanaged
executing, , values returns have been incremented. these incremented values don't find there way object passed foo
.
what need fix this?
i had similar problem many years ago , found there little documentation on custom marshaling. suspect using icustommarshaler never took off since can done using manual marshaling in course of regular code. , there never need documentation of advanced custom marshaling scenarios.
anyway, through variety of sources , trial , error think teased out practical understanding of how of custom marshaling works.
in case, have set managedtonative method correctly [in] marshaling , nativetomanaged method correctly [out] marshaling [in, out] marshaling bit trickier. [in, out] marshaling in-place marshaling. on way out must marshal data same instance provided in [in] side of operation.
there number of small variations on depending on whether using reference or value types, whether call normal pinvoke call or callback on delegate, etc. thinking needs end key.
the following variation on code works way want (and seems works same way .net 2.0 , up):
//this must thread static since, in theory, marshaled //call executed simultaneously on 2 or more threads. [threadstatic] int[] marshaledobject; public intptr marshalmanagedtonative(object managedobj) { if (managedobj == null) return intptr.zero; if (!(managedobj int[])) throw new marshaldirectiveexception("variablelengtharraymarshaler must used on int array."); //this called on way in must keep reference //the original object can marshal on way out. marshaledobject = (int[])managedobj; int size = sizeof(int) + marshaledobject.length * sizeof(int); intptr pnativedata = marshal.allochglobal(size); marshal.writeint32(pnativedata, marshaledobject.length); marshal.copy(marshaledobject, 0, (intptr)(pnativedata.toint64() + sizeof(int)), marshaledobject.length); return pnativedata; } public object marshalnativetomanaged(intptr pnativedata) { if (marshaledobject == null) throw new marshaldirectiveexception("this marshaler can used in-place ([in. out]) marshaling."); int len = marshal.readint32(pnativedata); if (marshaledobject.length != len) throw new marshaldirectiveexception("the size of array cannot changed when using in-place marshaling."); marshal.copy((intptr)(pnativedata.toint64() + sizeof(int)), marshaledobject, 0, len); //reset null next call; marshalledobject = null; return marshaledobject; }
Comments
Post a Comment