将结构复制到byte []后,是否需要调用Marshal.DestroyStructure?

时间:2018-05-04 21:32:24

标签: c# pinvoke marshalling unmanaged

其实我有很多问题,所以我会详细描述它们,感谢您的耐心等待。

我想编写一些包装本机API的通用方法,就像ReadProcessMemory和WriteProcessMemory一样。

以WriteProcessMemory为例,我找到了一些方法:

1.GCHandle

// Write memory: Non-Array
var lengthInBytes = (IntPtr)Marshal.SizeOf<T>();
var tHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
try
{
    // Call native api;
}
finally
{
    tHandle.Free();
}

// Write memory: Array
var lengthInBytes = (IntPtr)(data.Length * Marshal.SizeOf<T>());
var tHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
try
{
     // Call native api;
}
finally
{
    tHandle.Free();
}

这种方式非常简单,但唯一的问题是要固定的对象必须是内置值类型(固定数组除外,但数组中元素的类型符合相同的要求),.所以我找到了另一种方法:Marshal.AllocHGlobal方法。

2。元帅级别的成员

//Write memory: Non-Array
var lengthInBytes = Marshal.SizeOf<T>();
var memPtr = Marshal.AllocHGlobal(lengthInBytes);
try
{
    Marshal.StructureToPtr<T>(data, memPtr, false);
    // Call native API
}
finally
{
    Marshal.DestroyStructure<T>(memPtr);
    Marshal.FreeHGlobal(memPtr);
}

// Write memory: Array
var perSize = Marshal.SizeOf<T>();
var arrayLength = data.Length;
var lengthInBytes = arrayLength * perSize;
var memPtr = Marshal.AllocHGlobal(lengthInBytes);
var baseAdr = memPtr;
try
{
    foreach (var item in data)
    {
        Marshal.StructureToPtr<T>(item, baseAdr, false);
        baseAdr += perSize;
    }
    // call native API.
}
finally
{
    for (int i = 0; i < arrayLength; i++, baseAdr -= perSize)
    {
        Marshal.DestroyStructure<T>(baseAdr);
    }
    Marshal.FreeHGlobal(memPtr);
}

这种方式会分配一些内存,复制数据,甚至遍历数组两次,但它支持引用类型,我很满意,但后来我有了一个想法:如何使用Byte []而不是IntPtr?

具体来说,extern方法是:

[DllImport("Kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern Boolean WriteProcessMemory([In] HandleRef hProcess, [In]IntPtr lpBaseAddress, [In, MarshalAs(UnmanagedType.LPArray)] Byte[] lpBuffer, [In, MarshalAs(UnmanagedType.SysInt)]IntPtr nSize, [Out, Optional, MarshalAs(UnmanagedType.SysInt)] out IntPtr lpNumberOfBytesWritten);

方法中的代码是:

var perSize = Marshal.SizeOf<T>();
var buffer = new Byte[perSize];
var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
var pBuffer = handle.AddrOfPinnedObject();
try
{
    Marshal.StructureToPtr<T>(data, pBuffer, false);

    //Call native API.

}
finally
{
   // Question!!!!!!!!!!!!!!
    Marshal.DestroyStructure<T>(pBuffer);
    handle.Free();
}

所以有一个问题:我是否需要调用Marshal.DestroyStructure方法?

MSDN说

  

StructureToPtr(T,IntPtr,Boolean)复制结构的内容   到ptr参数指向的预分配内存块。   如果结构包含编组到COM接口的引用类型   指针(接口,没有布局的类和System.Object),.   托管对象通过引用计数保持活动状态。所有其他   引用类型(例如,字符串和数组)被封送到   副本。要释放这些托管或非托管对象,您必须调用   释放内存之前的DestroyStructure(IntPtr)方法   块。

此方法是&#34;将来自指定类型的托管对象的数据编组为 非托管内存块 。&#34;

但是变量缓冲区是Byte [](在托管内存中),所以如果我不调用DestroyStructure,GC可以释放T中的引用类型吗? (顺便说一句,在我的测试中,这种方式比第二种方式快得多,即使Marshal.DestroyStructure方法也被调用了......)

还有更多问题,是否有更多方法可以获得泛型T的指针?如果上面的解决方案有任何错误?

0 个答案:

没有答案
相关问题