[Mono-list] need some help with PInvoke..

Jonathan Pryor jonpryor@vt.edu
10 Jul 2003 10:58:16 -0400


You've already answered your GC questions in a later email.  There are
just two things I'd like to clarify.

First of all, IntPtrs, shouldn't be exposed to client code, if at all
possible.  Granted, this isn't always possible (S.W.F exposes them
everywhere so you can manually call Win32 functions, and the Gtk#
wrapper also exposes them), but ideally you could provide a complete
wrapper around a type, and not need to expose an IntPtr.

Alternatively, creating a new struct that just has an IntPtr member
should be an equivalent, which would allow some type safety.  I'm
surprised I don't see this more often.

The second thing is in your sample code, in particular:

	[StructLayout(LayoutKind.Sequential)]
	public unsafe struct HDF {
	  public int link;
	  public int alloc_value;
	  public char *name;
	  public int name_len;
	  public char *value;
	  // ...
	};

The `char' type is an unsigned 16-bit type.  Your other functions
specify that string marshaling should be done as LPStrs (an 8 bit
type).  Which means there's a mismatch between your structure and method
signatures.

The fix is to use "sbyte*" or "byte*" (depending on whether your
compiler uses signed or unsigned characters by default) instead of
"char*" in your structures.  You can convert it into a System.String by
using the System.String.String(sbyte*) constructor, or cast the byte* to
an IntPtr and pass the pointer to
System.Runtime.InteropServices.Marshal.PtrToStringAnsi(IntPtr).

Going from System.String to a sbyte* would likely require that you
P/Invoke to malloc/free (or whatever memory management functions your C
code uses), allocate unmanaged memory, and do the copy yourself (or use
System.Runtime.InteropServices.Marshal.Copy(byte[], int, IntPtr, int)). 
You'll have to convert the System.String to a byte[] first, though,
which will likely require using the System.Text.Encoding class (making
sure that you use the same encoding as your C code does).

 - Jon

On Thu, 2003-07-10 at 04:22, David Jeske wrote:
> On Wed, Jul 09, 2003 at 02:15:09PM -0400, Jonathan Pryor wrote:
> > The first way is similar to what's done in Gtk# -- use System.IntPtr
> > instead of "void*" and use the IntPtr exclusively as a pointer into
> > managed memory:
> 
> For opaque stuff, I like the IntPtr concept. However, I want specific
> types of IntPtrs. I have a bunch of different opaque types and turning
> everything into an IntPtr is too dangerous.
> 
> You'd think they would have wanted to have a more specific type than
> IntPtr on all those HWND elements. I can easily crash the world with
> supposedly "safe" code just by handing an IntPtr of the wrong type
> somewhere.
> 
> However, unless I'm totally misunderstanding something, it seems like
> I can do what I want with unsafe code. 
> 
> > However, what you posted doesn't exactly match the above.  You have a
> > "mostly opaque" data structure (given that your C code directly
> > references the `desc' member of the NOERR class).
> > 
> > There's a problem with this.  The .NET/mono runtime systems make two
> > assumptions: structs are located on the stack, and classes are allocated
> > in garbage-collected, .NET-controlled, memory.  Non-stack, non-garbage
> > collected memory doesn't enter the picture AT ALL.
> 
> That's fine. What I want to do shouldn't conflict with this
> concept. For non-opaque types (C-structs), I just want .NET to keep a
> pointer in managed memory, but I want it to point to unmanaged data
> (which it can clearly already handle). Then want the ability to use a
> C-style struct definition to get at elements in the unmanaged struct
> memory. I'm fine if I have to do this inside unsafe code blocks.
> 
> I used unsafe structs and it seems to do exactly what I
> want. Accessing structure members in the unmanaged memory works
> correctly. There is no object machinery around structs, so the offsets
> match the unmanaged data just fine. The only question is, does the GC
> ignore a struct pointer or an unsafe struct pointer? 
> 
> For example, given this code:
> 
>   unsafe struct DATA {};
> 
>   unsafe class Foo {
>     unsafe DATA *bar;
>   }
> 
> Will the GC have any problems if bar points to unmanaged memory?
> 
> I can see why it would want to take a look at the location bar is
> pointing to, because there are cases where you are allowed to have
> pointers which point to the interior of managed objects. However, it
> can't go walking the bar pointer, because there is no object machinery
> on the other end. Because of this, I can't see why it would crash the
> GC if this pointed to unmanaged memory.
> 
> Can anyone more in the know confirm what happens here?
> 
> I've attached my most recent test code which uses unsafe structs and
> seems to do the right thing. (i.e. it can correctly access internal
> structure members of the data sitting in unmanaged memory)