.NET Platform Invoke advocates declaring pointer types as IntPtr. For example, the following
[DllImport("mylib")] static extern IntPtr get_foo(); [DllImport("mylib")] static extern void do_something_foo(IntPtr foo);
However, I find when interfacing with interesting native interfaces that have many pointer types, flattening everything into IntPtr makes the code very hard to read and removes the typical typechecking that a compiler can do.
I've been using a pattern where I declare an unsafe struct to be an opaque pointer type. I can store this pointer type in a managed object, and the compiler can typecheck it for me. For example:
class Foo { unsafe struct FOO {}; // opaque type unsafe FOO *my_foo; class if { [DllImport("mydll")] extern static unsafe FOO* get_foo(); [DllImport("mydll")] extern static unsafe void do_something_foo(FOO *foo); } public unsafe Foo() { this.my_foo = if.get_foo(); } public unsafe do_something_foo() { if.do_something_foo(this.my_foo); }
NOTE: I'm not trying to marshal a structure. The DLL is providing an opaque pointer which I'm not supposed to touch, I merely need to provide it to future calls to the DLL.
I know that the published way to do this is IntPtr, but I don't like using an untyped pointer. When there are several pointer types moving between managed and native code, using IntPtrs is very dangerous. Using my opaque struct pointer types for typechecking is a godsend.
I have not run into any trouble using this technique in practice. However, I also have not seen an examples of anyone using this technique, and I wonder why. Is there any reason that the above code is invalid in the eyes of the .NET runtime?
My main question is about how the .NET GC system treats "unsafe FOO *my_foo". My hope is that because the underlying type is a struct, and it's declared unsafe, that the GC would ignore it.
Is this pointer something the GC system is going to try to trace, or is it simply going to ignore it? Is it safe to use my technique instead of IntPtr?
ref FOO foo
instead ofFOO *foo
? This entire class could be rewritten without requiringunsafe
. – cdhowieref FOO foo
because that is a marshalled pointer to a structure which lives in the managed heap. If you try it, you will see that you need to declare aFOO foo
somewhere in managed space in order to supply aref FOO foo
parameter.FOO* foo
is a value-type pointer, just likeIntPtr
, however, it carries the typeFOO
so it's not the same type as all otherIntPtr
s. – David Jeske