我想了解在C#中从内部分配内存给指针(C/C++风格)的正确方式,以及将该内存保留一段时间。此外,这个分配的内存是用于调用DeviceIoControl()的。考虑以下类:
class Example {
const uint memCommit = 0x1000;
const uint pgReadWrite = 0x04;
[DllImport("Kernel32.dll", SetLastError = true)]
public static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, uint flAllocationType, uint flProtect);
[StructLayout(LayoutKind.Sequential)]
struct AStruct {
uint val1;
uint val2;
uint val3;
}
private static unsafe AStruct* pStruct = (AStruct*)VirtualAlloc(IntPtr.Zero, (UIntPtr)(sizeof(AStruct)), memCommit, pgReadWrite).ToPointer();
public static unsafe void ReadFromDevice() {
// setup the structure for the IOCTL call
pStruct->val1 = 70; //
pStruct->val2 = 0;
pStruct->val3 = 0x0f;
// call P/Invoked DeviceIoControl() here with pStruct as both the in/out pointers
// check that all is well
}
}
整个事情让我感觉不对劲,但多年来我已经学到了不立即质疑实现方法的教训,需要先进行研究。这就是我来到这个论坛的原因。我看到了以前从未见过的行为。
使用调试器,在指针通过调用VirtualAlloc()实例化的点上设置断点。我记录下给我的地址(例如0x03bf78cc)。我还在另一个调用上设置了断点,该调用调用了上面的ReadFromDevice()方法。当我逐步执行ReadFromDevice()时,我注意到pStruct包含的地址与程序最初开始时分配的地址不同。pStruct的值已从上面的数字更改为,假设为0x03cd9004。
我之前曾经使用过调用Kernel32函数DeviceIoControl()的方法,并且在调用DeviceIoControl()时使用的方法是将数据结构实例化为固定的GCHandle,进行调用,复制出适当的数据,然后释放句柄。这种方法似乎更少出错,并保持了尽快分配和释放非托管内存的模式的一致性。
正如我所提到的,我现在正在使用的代码方法让我感觉不对劲,但我不确定原因。在Google上搜索" c sharp memory address changed after alloc "等内容也没有找到任何信息。应该更改这种方法吗?(过去,我使用固定的GC句柄。)上述方法是否可靠?无论如何,在程序启动时指针为什么会显示一个内存地址,而在实际执行ReadFromDevice()调用时会显示另一个内存地址?当我写这篇文章时,我在想,地址的更改是否像我最初想的那么“奇怪”。尽管如此,我仍然对这种指针使用方式提出质疑。请给予建议。
谢谢, Andy