我想知道从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(),并且使用的方法是将固定的GCHandle实例化为在调用DeviceIoControl()时使用的数据结构,进行调用,复制出适当的数据然后释放句柄。这种方法似乎不易出错,并且与尽可能快地分配和释放非托管内存的模型保持一致。
正如我所提到的,我现在所使用的代码中使用的方法并没有“感觉”正确,但我不确定原因。我在谷歌上搜索了“在分配后更改了内存地址”之类的内容,结果一无所获。这种方法应该改变吗? (过去,我使用了固定的GC手柄。)上述方法听起来有效吗?无论如何,为什么指针会在程序启动期间说出一个内存地址,然后在实际执行ReadFromDevice()调用时又是另一个?当我写这篇文章时,我想知道地址的变化是否像我最初想的那样“奇怪”。尽管如此,我仍在质疑指针的使用。请指教。
谢谢, 安迪
答案 0 :(得分:1)
这没有必要,你可以把它留给pinvoke marshaller来做正确的事情。只需提供函数的重载即可传递参数,并根据调用进行自定义。例如:
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool DeviceIoControl(
IntPtr hDevice, uint dwIoControlCode,
IntPtr lpInBuffer, uint nInBufferSize,
out AStruct lpOutBuffer, uint nOutBufferSize,
out uint lpBytesReturned, IntPtr lpOverlapped);
你会这样称呼它:
AStruct result;
uint resultSize = Marshal.SizeOf(result);
uint bytesReturned;
bool okay = DeviceIoControl(hDev, somecode, IntPtr.Zero, 0,
out result, resultSize,
out bytesReturned, IntPtr.Zero);
if (!okay) throw new Win32Exception();
System.Diagnostic.Debug.Assert(bytesReturned == resultSize);
答案 1 :(得分:0)
是否有效?你是想尝试修复有用的东西吗?
我从不在c#中使用VirtualAlloc。如果需要内存缓冲区,为什么不直接在c#中分配它。我认为你不需要VirtualAlloc
。直接在c#中分配一个非托管固定数组,或者分配一个常规数组并通过编组将它传递给IOCTL函数。