其实我有很多问题,所以我会详细描述它们,感谢您的耐心等待。
我想编写一些包装本机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的指针?如果上面的解决方案有任何错误?