none
Marshal.StructureToPtr RRS feed

  • Question

  • Hi

    I have just discovered something I didn't really appreciate before.

    I can call Marshal.StructureToPtr on a class, and that class can contain both value types and reference types.

    I can then call the reverse operation with the same pointer and recreate the object.

    Previoulsy my erroneous thinking was that only structs (value types) were permitted.

    But I have a problem. The Marshaler dynamically allocates buffers to hold any embedded references, and can free these up later.

    This is a problem because we allocate memory from our own special heap allocator in order to get the pointer used by Marshal.StructureToPtr, but .Net calls the default allocator when it internally allocates the buffers for reference object in the class being marshaled.

    Is there any way we can override the allocate/free function that .Net calls when it does this?

    Thx
    Saturday, October 25, 2008 12:24 PM

Answers

  • The memory for the string is allocated with SysAllocString().  You cannot tell it to use a different allocator.  If using yours is a hard requirement, you'll have to marshal the structure yourself.  Declare the unmanaged version of the structure, use IntPtr for the string.


    Hans Passant.
    Saturday, October 25, 2008 1:52 PM
    Moderator

All replies

  • > .Net calls the default allocator when it internally allocates the buffers for reference object

    It has to allocate these in a garbage collected heap (basically because they are objects of reference types).  Therefore, it has to use its own allocation mechanisms.
    Saturday, October 25, 2008 1:20 PM
  • The marshaller uses the COM memory allocators, SysAllocString for strings, CoTaskMemAlloc for structures.  Release with SysFreeString and CoTaskMemFree.  Freeing memory allocated by the .NET runtime in unmanaged code is generically a bad idea, ownership becomes too fuzzy and leaks or crashing double frees are the result.  There's never a question when managed code consumes data allocated by unmanaged code, it can never be freed by the .NET runtime unless the unmanaged code follows the explicit COM allocator protocol.  Make it just as explicit going the other way around: copy the data and leave ownership to the code that allocated it.
    Hans Passant.
    Saturday, October 25, 2008 1:41 PM
    Moderator
  • Are you 100% certain of what you say? I am not sure that you are quite correct, but I may be wrong too!

    Marshal.StructureToPtr in effect "copies" the object to unmanaged memory, this is why you must supply a pointer to the method, you are responsbile for allocating the unmanaged block.

    The size of the block that is needed is obtained from Marshal.SizeOf (Type).

    This size returned from this call is the size required to store the fields AND references, the references are stored as pointers.

    So with a class that contains

    int          X = 1;
    int          Y = 2;
    string     S = "Hello";

    SizeOf yields 16, that is 4 for each of the ints and 8 for the pointer.

    Then when we allocate that memory (usually with AllocHGlobal but not so in our case) we can call Marshal.StructureToPtr.

    When we do this, the Marshaller (from what I am seeing) copies the value of each field by value and then for each reference it finds in the object, automatically allocates more unmanaged memory to hold the values of these members and stores the pointers in the block too.

    So in my example, the result of calling StructureToPtr is (in effect) this:

    4 bytes for int X
    4 bytes for int Y
    8 bytes for a new pointers P

    At the address P, we find

    P bytes for ANSI -> Hello

    So the marshaller allocates the small block to hold the text for S and puts this address into the appropriate offset inside the original block we allocated.

    It does this automatically and I preseume recursively, it also seems to mimic the operations carried out by a binary serializer to an extent, namely copying a managed object tree by dynamically creating and populating an unmanaged object tree.

    Bear in mind the class must have the [StructLayout(LayoutKind.Sequential)] attribute for any of this to work.

    My problem is that we want the system to allocate not from the default process heap but a different heap, so we want it to not call LocalAlloc (which is ultimately what gets done) but an alternative function, likewise for freeing.

    Thx





    Saturday, October 25, 2008 1:47 PM
  • The memory for the string is allocated with SysAllocString().  You cannot tell it to use a different allocator.  If using yours is a hard requirement, you'll have to marshal the structure yourself.  Declare the unmanaged version of the structure, use IntPtr for the string.


    Hans Passant.
    Saturday, October 25, 2008 1:52 PM
    Moderator
  • Hey

    Thanks for the feedback, I think you're right we are stuck with this. Unless we craft our own version of Marshal.StructureToPtr and Marshal.PtrToStructure.

    Does anybody here have a feel for the size of such a project? is it not reasonably straightforward?

    Thx
    Saturday, October 25, 2008 1:55 PM