2011-10-10 76 views
3

我想知道从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()中使用的数据结构,进行调用,复制出适当的数据,然后释放句柄。这种方法似乎不太容易出错,并与尽可能快地分配和释放非托管内存的模型保持一致。

正如我所提到的,我现在所使用的代码中使用的方法并不是“感觉”正确,但我不确定原因。我在谷歌搜索上没有找到任何东西,比如“c语言内存地址在alloc后更改”等等。这种方法应该改变吗? (过去,我使用固定的GC手柄。)上述方法是否正确?无论如何,指针在程序启动时会说一个内存地址,而在ReadFromDevice()调用实际执行时会有另一个内存地址?当我写这篇文章时,我想知道地址变化是否像我第一次想到的那样“奇怪”。不过,我仍然质疑指针的使用。请指教。

感谢, 安迪

回答

1

这不是必要的,你可以让它停止向编译器做正确的事情。只需提供函数的重载以传递参数,并根据调用进行自定义。例如:

[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); 
+0

这就是我习惯这样做的方式。或者,正如我在帖子中提到的那样,通过使用固定对象。基本上,我想知道是否使用的实现是好的,还是应该将其更改为您发布的内容?我想知道,在使用的方法中是否存在危险?即具有这样的静态不安全指针? –

0

是否行得通不?你想解决一些有效的或什么?

我从来不需要c#中的VirtualAlloc。如果你需要一个内存缓冲区,为什么不直接在C#中分配它。我认为你不需要VirtualAlloc。直接在c#中分配一个非托管的固定数组,或者分配一个常规数组并通过编组将其传递给IOCTL函数。

+0

它认为它的工作原理,以及大部分的时间似乎。我正在研究一个我认为与使用非托管内存直接相关的缺陷。 –