3
votes

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

2
Is there a particular reason you cannot use ref FOO foo instead of FOO *foo? This entire class could be rewritten without requiring unsafe.cdhowie
Yes, you can't use ref 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 a FOO foo somewhere in managed space in order to supply a ref FOO foo parameter. FOO* foo is a value-type pointer, just like IntPtr, however, it carries the type FOO so it's not the same type as all other IntPtrs.David Jeske

2 Answers

2
votes

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

-1
votes

I wouldn't use unsafe code and pointers. Why not simply define the structure and let the CLR do the mapping:

[StructLayout(LayoutKind.Sequential)]
public struct Foo
{
    public int Field1;
    public long Field2;
}

and then:

[DllImport("mylib")]
static extern void do_something_foo(ref Foo foo);