Правильный способ маршалировать SIZE_T *?
У меня есть следующее определение функции С++, которое я пытаюсь вызвать через PInvoke из управляемого кода:
bool FooBar(SIZE_T* arg1);
Моя управляемая декларация выглядела следующим образом:
[DllImport("mydll", SetLastError=true, CharSet=CharSet.Unicode)]
private static extern bool FooBar(ref uint arg1);
Некоторые из вас могут заметить ту же ошибку, которую я в конце концов сделал. Это не 64-битная портативная. SIZE_T имеет переменный размер (32-64 бит), как и указатель на него. В управляемом размере указатель правильно переводит на 64 бит, но uint не делает этого, и вы можете в итоге получить мусор в верхних битах arg1. Это была особенно стойкая ошибка, поскольку мусор часто был просто нулями: (
Единственное решение, которое я получил, это следующее управляемое объявление:
[DllImport("mydll", SetLastError=true, CharSet=CharSet.Unicode)]
private static extern bool FooBar(ref IntPtr arg1);
Это работает, конечно, потому что IntPtr может правильно изменить свой размер. В моем коде я просто рассматриваю IntPtr как целое число, и он работает, хотя он выглядит как уродливый хак. Мне кажется, должен быть какой-то способ указать это правильно, возможно, используя UnmanagedType.SysUInt, но я не смог найти какое-либо другое рабочее решение.
Ответы
Ответ 1
Использование IntPtr
и/или UIntPtr
делает это правильно - типы существуют специально для этой цели! Я не понимаю, почему вы считаете это "уродливым взломом". Я также не знаю, какова будет ваша предлагаемая альтернатива: любой атрибут, позволяющий сопоставлять значения с uint
, будет по своей сути неправильным, потому что С# uint
гарантированно будет 32-битным независимо от архитектуры и так что на 64-битной платформе, чтобы правильно ее маршалировать, ей пришлось бы обрезать половину, потерять данные и, вероятно, сделать результат бесполезным.
Ответ 2
UIntPtr - правильный тип.
size_t представляет собой целое число без знака, размер указателя и то, что означает UIntPtr. Я думаю, что "ptr" в названии может быть немного запутанным. Это на самом деле не означает, что "это указатель", это означает, что "это целочисленный размер указателя". Таким образом, ваше объявление будет выглядеть следующим образом:
[DllImport("mydll", SetLastError=true, CharSet=CharSet.Unicode)]
private static extern bool FooBar(ref UIntPtr arg1);