IntPtr и избегая небезопасного кода
У меня есть внешняя библиотека, которая принимает IntPtr. Есть ли безопасный способ сделать это...
int BytesWritten = 0;
Output.WriteBytes(buffer, new IntPtr(&BytesWritten));
... без необходимости использовать "небезопасный" код? Я не очень знаком с IntPtrs, но я хотел бы сделать что-то вроде этого:
fixed int BytesWritten = 0;
Output.WriteBytes(buffer, IntPtr.GetSafeIntPtr(ref BytesWritten));
... таким образом, что мне не нужно компилировать с / unsafe.
Я не могу изменить функцию WriteBytes, это внешняя функция.
Кажется, между ref 'int int' и IntPtr должен быть какой-то тип приведения, но мне не повезло найти его.
3 ответа
Да, есть. Вы можете использовать P/Invoke для вашего кода. Это создаст указатель для вас автоматически. Что-то вроде этого:
[DllImport("yourlib", SetLastError=true)]
static extern bool WriteBytes(
[MarshalAs(UnmanagedType.LPArray)]
byte [] buffer,
ref int BytesWritten);
(Я добавил массив в качестве бонуса). Более подробную информацию о P/Invoke, с примерами gazillion, можно найти на http://pinvoke.net/.
Каждый параметр выше может принимать out
, in
а также ref
, Параметры out и ref переводятся как указатели, где параметр ref является двусторонним.
Я предполагаю что Output.WriteBytes
это [DllImport]
метод. Вы можете опубликовать декларацию?
Вы должны быть в состоянии избежать указателя, объявив последний параметр как out int
вместо IntPtr
- пусть маршаллер P/Invoke сделает все остальное.
Вот класс, который предоставит вам безопасную реализацию IntPtr. Он наследуется от класса SafeHandleZeroOrMinusOneIsInvalid, предоставляемого.NET Framework.
/// <summary>
/// IntPtr wrapper which can be used as result of
/// Marshal.AllocHGlobal operation.
/// Call Marshal.FreeHGlobal when disposed or finalized.
/// </summary>
class HGlobalSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
{
/// <summary>
/// Creates new instance with given IntPtr value
/// </summary>
public HGlobalSafeHandle(IntPtr ptr) : base(ptr, true)
{
}
/// <summary>
/// Creates new instance with zero IntPtr
/// </summary>
public HGlobalSafeHandle() : base(IntPtr.Zero, true)
{
}
/// <summary>
/// Creates new instance which allocates unmanaged memory of given size
/// Can throw OutOfMemoryException
/// </summary>
public HGlobalSafeHandle(int size) :
base(Marshal.AllocHGlobal(size), true)
{
}
/// <summary>
/// Allows to assign IntPtr to HGlobalSafeHandle
/// </summary>
public static implicit operator HGlobalSafeHandle(IntPtr ptr)
{
return new HGlobalSafeHandle(ptr);
}
/// <summary>
/// Allows to use HGlobalSafeHandle as IntPtr
/// </summary>
public static implicit operator IntPtr(HGlobalSafeHandle h)
{
return h.handle;
}
/// <summary>
/// Called when object is disposed or finalized.
/// </summary>
override protected bool ReleaseHandle()
{
Marshal.FreeHGlobal(handle);
return true;
}
/// <summary>
/// Defines invalid (null) handle value.
/// </summary>
public override bool IsInvalid
{
get
{
return (handle == IntPtr.Zero);
}
}
}