System.AccessViolationException: shared_ptr между приложениями C # .NET и C ++ - PullRequest
0 голосов
/ 01 октября 2018

В нашем проекте мы связываем два приложения, одно на C #, а другое на C ++, по именованным каналам.Мы намерены передавать указатели памяти между ними и иметь возможность доступа к объектам, указанным ими в любом приложении.Наш текущий код вызывает исключение System.AccessViolationException:

System.AccessViolationException: Попытка чтения или записи в защищенную память.Это часто указывает на то, что другая память повреждена.

Пока что мы используем shared_ptr, который указывает на пользовательскую структуру и записывает указатель на буфер в C ++, как показано ниже:

typedef struct {
    int one;
    int a;
    int two;
    int b;
} DATA_STRUCT; // C++ struct

    DATA_STRUCT ds;
    ds.one = 10;
    ds.a = 5;
    ds.two = 99;
    ds.b = 0;

    shared_ptr<DATA_STRUCT> ptr_ds(new DATA_STRUCT);
    shared_ptr<DATA_STRUCT> p(ptr_ds);
    *ptr_ds = ds;

    const int size = BUFFER_SIZE;
    char buf[size];
    memset(buf, 0xCC, 100);

    while (keepReading)
    {
        printf("Write message:");
        scanf("%s", buf);
        memcpy(buf, &p, sizeof(shared_ptr<DATA_STRUCT>));
        if (strcmp(buf, "quit") == 0)
            keepReading = false;
        else
        {
            WriteFile(hPipe1, buf, dwBytesToWrite, &cbWritten, NULL);
            memset(buf, 0xCC, 100);
        }
    }

Затем в C # мы читаем весь буфер, сохраняем байты с соответствующей информацией в другом буфере (Rc) и преобразуем массив байтов в наши пользовательские данные.структура использует небезопасный IntPtr, как вы можете видеть ниже:

    buffer = new byte[BUFFER_SIZE];
    bytesRead = clientCSharp.stream.Read(buffer, 0, BUFFER_SIZE);

public struct DATA_STRUCT
    {
        public int one;
        public int a;
        public int two;
        public int b;
    }; // C# struct

unsafe
{
        Buffer.BlockCopy(buffer, 0, Rc, 0, ReadLength);    
        DATA_STRUCT ds = new DATA_STRUCT();

        IntPtr aux_ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));    
        IntPtr final_ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));

        Marshal.Copy(Rc, 0, aux_ptr, 4);

        final_ptr = (IntPtr)Marshal.PtrToStructure(aux_ptr, typeof(IntPtr));
        ds = (DATA_STRUCT)Marshal.PtrToStructure(final_ptr, typeof(IntPtr));
}

Исключение возникает, когда мы пытаемся получить доступ к final_ptr для загрузки DATA_STRUCT, последнейстрока кода представлена ​​выше.Здесь я приведу несколько изображений отладки:

C ++ Отладочный образ с Значение указателя, записанное в буфер именованного канала

C # Отладочный образ с Значение указателя, прочитанное из названного каналатруба уменьшенный буфер (Rc)

Может ли это быть что-то, связанное с длиной указателя?Как мне кажется, в приложении C ++ оно имеет 8 байтов, а в приложении C # - 16 байтов?Должны ли мы объявить безопасное место в памяти для C # и C ++?Если да, то как это можно сделать?

Примечание: Наша цель - работать с небезопасным IntPtr в приложении C #.В этом примере мы загружаем объект DATA_STRUCT, потому что мы хотели быть уверены, что в приложении C # мы извлекаем тот же объект, переданный в приложении C ++.Окончательные приложения предназначены для использования в Windows.

1 Ответ

0 голосов
/ 01 октября 2018

Пространства данных приложений полностью различны и существуют уже много лет.вы не можете просто передать необработанный указатель между приложениями и рассчитывать на доступ к одной и той же памяти.

Обычный подход состоит в том, чтобы сериализовать содержимое вашего объекта, распределить его по каналу и затем восстановить объект в приемнике..

Вы можете настроить именованные области совместно используемой памяти, и они быстрее совместно используют большие объекты (в Unix, и я предполагаю, что в Windows), но эти общие области, вероятно, не будут расположены по одному и тому же адресу, поэтомувсе еще хорош только для необработанных данных.

...