Correct way to marshal SIZE_T*?

10,936

Solution 1

Using IntPtr and/or UIntPtr is doing it properly - the types are there specifically for this purpose! I do not understand why you consider it an "ugly hack". I'm also not sure what your proposed alternative would be - any kind of attribute to allow values to be mapped to uint would be inherently wrong, because C# uint is guaranteed to be 32-bit regardless of the architecture, and so on 64-bit platform, to marshal it correctly, it would have to trim half of it, losing data, and likely rendering the result useless.

Solution 2

UIntPtr is the correct type to use.

size_t is an unsigned, pointer-sized integer and that's exactly what UIntPtr means. The "ptr" in the name can be a bit confusing, I agree. It doesn't actually mean "this is a pointer", it means "this is a pointer-sized integer". So your declaration would be:

[DllImport("mydll", SetLastError=true, CharSet=CharSet.Unicode)]
private static extern bool FooBar(ref UIntPtr arg1);
Share:
10,936

Related videos on Youtube

sooniln
Author by

sooniln

Updated on August 21, 2020

Comments

  • sooniln
    sooniln over 3 years

    I have the following C++ function definition, which I am trying to call through PInvoke from managed code:

    bool FooBar(SIZE_T* arg1);
    

    My managed declaration looked as follows:

    [DllImport("mydll", SetLastError=true, CharSet=CharSet.Unicode)]
    private static extern bool FooBar(ref uint arg1);
    

    Some of you may notice the same bug I eventually did. This is not 64bit portable. SIZE_T is of variable size (32-64 bit) as is the pointer to it. On the managed size the pointer correctly translates to 64bit, but the uint does not, and you can end up with trash in the upper bits of arg1. This was an especially persistent error since the trash was often just zeros :(

    The only solution I have gotten to work is the following managed declaration:

    [DllImport("mydll", SetLastError=true, CharSet=CharSet.Unicode)]
    private static extern bool FooBar(ref IntPtr arg1);
    

    This works of course because IntPtr can vary its size correctly. In my code I just treat the IntPtr as an integer, and it works, though it looks like an ugly hack. It seems to me there should be some way to specify this properly, perhaps using UnmanagedType.SysUInt, but I have been unable to come up with any other working solution.

  • sooniln
    sooniln over 14 years
    I view it as an ugly hack because IntPtr should represent a pointer to something. I am simply treating it directly as an integer however. SysUInt specifically states that it is a variable size representation of a uint. Exactly what I want, except I can't figure out how to corectly marshal it as a SysUInt pointer, rather than a SysUInt.
  • Vinko Vrsalovic
    Vinko Vrsalovic over 14 years
    You are using a pointer to SIZE_T as an argument, Soonil. IntPtr looks very appropriate
  • sooniln
    sooniln over 14 years
    You'll notice I am not using IntPtr however, I am using ref IntPtr.
  • Pavel Minaev
    Pavel Minaev over 14 years
    ref will always correctly map to pointer on any platform. IntPtr will correctly map to size_t.
  • Pavel Minaev
    Pavel Minaev over 14 years
    @Soonil, there is nothing to say that IntPtr "should represent a pointer to something". MSDN describes it thus: "The IntPtr type is designed to be an integer whose size is platform-specific. That is, an instance of this type is expected to be 32-bits on 32-bit hardware and operating systems, and 64-bits on 64-bit hardware and operating systems". So, it is simply an integral type that matched the word size of the platform. So is size_t and ptrdiff_t (usually, anyway), so the mapping is perfectly normal.
  • Pavel Minaev
    Pavel Minaev over 14 years
    On a side note, on MSIL/CLR level (which is the lowest you can get), int is known as int32, while IntPtr is known as native int. If it's the "Ptr" prefix of the type in BCL that confuses you, then you shouldn't treat it to mean "integer that is pointer". Rather, it means "integer that is of the same size as pointer".
  • Pavel Minaev
    Pavel Minaev over 14 years
    Actually, that's kind of a common name for that kind of type. Win32 APIs has typedefs INT_PTR and UINT_PTR. And ISO C99 has intptr_t and uintptr_t in <stddef.h>. So while it may be a misnomer, by now it's a traditional one.
  • eodabash
    eodabash over 11 years
    Seems like the answers given here: stackoverflow.com/questions/5589846/… contradict the idea that "there is nothing to say that IntPtr "should represent a pointer to something". The lack of math operators defined for IntPtr and UIntPtr seem to reinforce this as well.
  • Lilith River
    Lilith River over 11 years
    What about [MarshalAsAttribute(UnmanagedType.SysUInt)].... Seems to be the 'recommended' way, but I haven't gotten it to work...