none
GCHandle.AddrOfPinnedObject odd beahvior. RRS feed

  • Question

  • I am seeing puzzling behavioir with this interop mechanism.

    Basically we have a type T that is a fully blittable struct, we declare an instance (call this 'datum') and set it to default(T);

    I then allocate a pinned GCHandle on this instance.

    We then copy directly from unmanaged memory to the object by accessing AddrOfPinnedObject() on the handle.

    After this is complete though the data structire is empty (still set to it's default value).

    If I look at the address AddrOfPinnedObject in debug I can see that data does get copied to that address.

    What is also odd is that if I access a Memory Dump window in the debugger and just put 'datum' in the address field I see an address for datum, the address of the struct, but this is a different value to the address obtained from AddrOfPinnedObject().

    Does anyone know what is going on here???

    H

    Monday, December 15, 2008 7:46 PM

Answers

  • Since the parameter to GCHandle.Alloc is of type object, the structure is boxed and that boxed copy of the structure is pinned.

    As I have usually seen it, GCHandle tends to be used with arrays.  Since array are reference types, the correct thing is pinned.

    Since a single instance of a structure should be very small, the performance hit from copying a single instance of it should be minimal.  Therefore, you should not need to use GCHandle in this case.  See instead Marshal.PtrToStructure(IntPtr, Type).  http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx
    • Marked as answer by Captain Kernel Tuesday, December 16, 2008 10:03 AM
    Tuesday, December 16, 2008 1:18 AM

All replies

  • Since the parameter to GCHandle.Alloc is of type object, the structure is boxed and that boxed copy of the structure is pinned.

    As I have usually seen it, GCHandle tends to be used with arrays.  Since array are reference types, the correct thing is pinned.

    Since a single instance of a structure should be very small, the performance hit from copying a single instance of it should be minimal.  Therefore, you should not need to use GCHandle in this case.  See instead Marshal.PtrToStructure(IntPtr, Type).  http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx
    • Marked as answer by Captain Kernel Tuesday, December 16, 2008 10:03 AM
    Tuesday, December 16, 2008 1:18 AM
  • BinaryCoder said:

    Since the parameter to GCHandle.Alloc is of type object, the structure is boxed and that boxed copy of the structure is pinned.

    As I have usually seen it, GCHandle tends to be used with arrays.  Since array are reference types, the correct thing is pinned.

    Since a single instance of a structure should be very small, the performance hit from copying a single instance of it should be minimal.  Therefore, you should not need to use GCHandle in this case.  See instead Marshal.PtrToStructure(IntPtr, Type).  http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx


    Hi BC

    Thanks for this explanation, it makes sense as the whole situation was puzzling. We do use PtrToStructure already and despite the above handle based failure, do see a 20% reduction in processing time by doing this(but sadly it doesnt work).

    Our structures are big and there are lots of them, being sequentially 'read' from unmanaged memory, for 10,000 items to be 'pulled out' this way takes 0.2 secs using the pinned handle approach and 0.3 secs using PtrToStructure, that is a drop of 33% processor time (the copy doesnt work but data is still being copied (somewhere) so the measurement is valid) this includes getting and closing the handle per item (which is a local var used in a return, soperhaps we would not need to call handle.Free() anyway.

    Is there a way to get the address (the debugger is clearly able to)?

    We cannot simply use a void * either and use &(datum) because in this case our data is of a generic type.

    There is still one very annoying weakness in C# andI have raised it several time, starting in 2005, namely one cannot constrain a generic type argment in such a way that oe can declare a pointer to a generic. One could solve this (and some other) issue with this, just saying: datum = (*ptr); for example would be a direct memory copy too.

    There has to be some way to get the address of this struct...

    Thanks
    H

    Tuesday, December 16, 2008 10:03 AM
  • Well we had and idea, why not define a simple interface IStructAddress that has one get property, namely an IntPtr named 'Address'.

    In our library we examine the datum and (try to) cast it (using 'as') to an IStructAddress.

    If we succeed we can call 'Address' on the datum and get the actual address directly from the struct itself.

    Any struct implementing this interface just needs to do: IntPtr p = &(this); (more or less).

    This all looks fine and works, BUT it too ultimately fails.

    It seems there is NO way to invoke 'Address' on the actual instance we declare, because in casting to an IStructAddress a temporary copy (or some boxing is going on). Sure we gat a nice value from 'Address' and the copy works etc, but again no data is in the datum structure.

    If we recast the IStructAddress back to a T and then simply assign this to the original declared temp 'datum' then fine, we do have our data, but we've done the copying twice.

    Is there no way to invoke 'Address' on the actual instance of T that we declare?

    Thx
    H

    Tuesday, December 16, 2008 11:07 AM
  • No; casting a value type to an interface will always box it.

    I hate to ask the obvious, but have you done performance testing with making your value types reference types?

    As someone who came from a C++ background, I was hesitant to "make everything a reference" by default, but it is a fact that in .NET, value types should be scarce.

    Managed C++ is able to do what you want, but not C#.

           -Steve
    Wednesday, December 17, 2008 1:39 AM
  • Stephen Cleary said:

    No; casting a value type to an interface will always box it.

    I hate to ask the obvious, but have you done performance testing with making your value types reference types?

    As someone who came from a C++ background, I was hesitant to "make everything a reference" by default, but it is a fact that in .NET, value types should be scarce.

    Managed C++ is able to do what you want, but not C#.

           -Steve

    Thanks for commenting.

    Well these particular instances are value types structs. They need to be because we want blittable structures so that we can access them via C# pointers. The data you see resides in shared (mapped) memory. Although we can and do access the data via C# pointers, we sometimes need to 'reinstantiate' them for more involved processing.

    C# needs to start supporting generic pointers (for data types that are valid of course) and this will eliminate the need to jump through hoops so much.



    Wednesday, December 17, 2008 9:53 AM