Неуправляемое выделение памяти для управляемых объектов - PullRequest
3 голосов
/ 11 октября 2011

Меня интересует правильный способ выделения памяти для указателя (стиль 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 (), выполняет вызов, копирует соответствующие данные и освобождает дескриптор.Этот подход кажется менее подверженным ошибкам и согласуется с моделью распределения и освобождения неуправляемой памяти как можно быстрее.

Как я уже упоминал, подход, используемый в коде, которым я сейчас занимаюсь, просто не имеет "чувствую "правильно, но я не уверен, почему.Я ничего не нашел при поиске в Google таких вещей, как «c резкий адрес памяти изменился после alloc» и так далее.Следует ли изменить этот подход?(В прошлом я использовал закрепленные ручки GC.) Озвучен ли вышеуказанный подход?Независимо от того, почему указатель говорит один адрес памяти во время запуска программы, а затем другой, когда фактически выполняется вызов ReadFromDevice ()?Когда я пишу этот пост, мне интересно, является ли изменение адреса таким же «странным», как я сначала подумал.Тем не менее, я все еще подвергаю сомнению это использование указателей.Пожалуйста, сообщите.

Спасибо, Энди

Ответы [ 2 ]

1 голос
/ 11 октября 2011

В этом нет необходимости, вы можете оставить за маршаллером пинвока делать правильные вещи.Просто предоставьте перегрузки функции для передачи аргументов, настроенных под вызов.Например:

[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 голосов
/ 11 октября 2011

Работает или нет? Вы пытаетесь исправить что-то, что работает или как?

Мне никогда не был нужен VirtualAlloc в c #. Если вам нужен буфер памяти, почему бы вам не выделить его непосредственно в c #. Я думаю, вам не нужно VirtualAlloc. Либо непосредственно выделите неуправляемый фиксированный массив в c #, либо выделите обычный массив и передайте его функции IOCTL с помощью маршалинга.

...