Search code examples
c#pinvoke

Is it valid to use unsafe struct * as an opaque type instead of IntPtr in .NET Platform Invoke?


.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?


Solution

  • It appears that the answer is "yes"... "unsafe pointers are treated as value types", which means it's safe to use them to store opaque types. In a sense, they work just like IntPtr, but they come with additional type-checking, because different types of unsafe pointers are not considered the same, as they would be if you made them all IntPtr.

    For a more detailed article I wrote on the topic, check out..

    http://www.codeproject.com/script/Articles/ArticleVersion.aspx?waid=1210&aid=339290