Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/292.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 对托管对象的非托管内存分配_C#_C# 4.0_Unsafe - Fatal编程技术网

C# 对托管对象的非托管内存分配

C# 对托管对象的非托管内存分配,c#,c#-4.0,unsafe,C#,C# 4.0,Unsafe,我想知道从C#中为指针(C/C++风格)分配内存的正确方法。然后,长时间保持记忆。此外,此分配的内存用于调用DeviceIoControl()。考虑这个类: class Example { const uint memCommit = 0x1000; const uint pgReadWrite = 0x04; [DllImport("Kernel32.dll", SetLastError = true)] public static extern IntPtr

我想知道从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

我以前曾处理过对内核32函数DeviceIoControl()的调用,其中使用的方法是将固定的GCHandle实例化为调用DeviceIoControl()时使用的数据结构,进行调用,复制适当的数据,然后释放句柄。这种方法似乎不太容易出错,并且与尽快分配和释放非托管内存的模型保持一致

正如我所提到的,我现在所研究的代码中使用的方法并不“感觉”正确,但我不确定为什么。我在谷歌上搜索“c sharp memory address changed after alloc”之类的东西时什么也没发现。这种做法是否应该改变?(在过去,我使用了固定的GC句柄。)上述方法正确吗?不管怎样,为什么指针会在程序启动时显示一个内存地址,然后在实际执行ReadFromDevice()调用时显示另一个?当我写这篇文章时,我想知道地址的变化是否像我最初想的那样“奇怪”。尽管如此,我仍然对指针的使用提出质疑。请告知

谢谢,
安迪:这行不行?你是想修复一些有效的东西还是什么


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

这不是必需的,您可以让pinvoke编组程序来做正确的事情。只需提供函数重载以传递参数,并根据调用进行自定义。例如:

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

它认为它有效,而且大多数时候似乎有效。我正在研究一个缺陷,我认为它与非托管内存的使用直接相关。我也习惯于这样做。或者,正如我在文章中提到的,通过使用固定对象。基本上,我想知道所使用的实现是否良好,或者我是否应该将其更改为您发布的内容?我想知道,使用的方法是否存在危险;i、 e.有这样一个静态不安全指针?