Попытка чтения или записи защищенной памяти при попытке передать большую последовательность символов в C # - PullRequest
4 голосов
/ 14 марта 2012

Я работаю над устройством чтения тегов, и мне удалось подключить его и прочитать некоторые данные.Моя проблема в том, что я пытаюсь прочитать идентификатор тега, который представляет собой большую последовательность символов.

SDK написан на языке C, и я разрабатываю приложение ac #.

short GetIDBuffer(HANDLE hCom, unsigned char* DataFlag, unsigned char * Count, 
      unsigned char *value, unsigned char* StationNum)

Inмое приложение на C #:

[DllImport("Reader2.dll",CharSet = CharSet.Ansi)]
public static extern short GetIDBuffer(IntPtr hCom, ref uint DataFlag, 
       ref uint Count, ref String value, ref uint StationNum);

Флажок данных, счетчик, номер станции - это в основном небольшие последовательности, в которых тип uint работает хорошо.Но когда дело доходит до значение , это большая последовательность.Я попытался набрать string , но выдает это исключение:

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

  • [MarshalAs (UnmanagedType.LPWStr)] строковое значение

    не удалось решитьпроблема

  • количество значение возвращается правильно

  • Моя операционная система 64-разрядная: я использовал corflags application.exe/32bit+ иЯ был в состоянии загрузить DLL правильно.

снимок кода:

 [DllImport("Reader2.dll")] 
    public static extern byte OpenReader(ref IntPtr hCom, byte LinkType, string com_port, uint port);
    [DllImport("Reader2.dll")]
    public static extern  short GetIDBuffer(IntPtr hCom, ref byte DataFlag, ref byte Count,**(type)** value , ref byte StationNum);

    static  void Main(string[] args)
    {

        byte count = 0, station = 1, flag = 0;
        IntPtr hcom = IntPtr.Zero;        
        OpenReader(ref hcom, 2, "192.168.0.178", 4001);
        // valid handle returned from openReader 
      //
        **GetIDBuffer code**
            //

Ответы [ 3 ]

4 голосов
/ 17 марта 2012

Вам не нужно использовать corflags application.exe / 32bit + .Все, что вам нужно сделать, это установить целевую платформу на x86 в project / properties / build.

Это сработает (хорошо, что это работает с использованием собственного метода тестирования, который я создал с той же сигнатурой, что и приведенная выше).Этот первый метод не требует ключевого слова unsafe или требует, чтобы проект был построен с установленным в true значением «Разрешить небезопасный код».

internal static class NativeMethods
{
    [DllImport("Reader2.dll")]
    public static extern short GetIDBuffer(
           IntPtr hCom, ref byte dataFlag, ref byte count, 
           byte [] value, ref byte stationNum);
}

static int TestGetIDBuffer()
{
    const int arraySize = 255;
    byte[] bytes = new byte[arraySize + 1]; 

    byte dataFlag = 0;
    byte count = arraySize;
    byte status = 0;

    int retval = NativeMethods.GetIdBuffer(IntPtr.Zero, ref dataFlag, ref count, bytes, ref status);

    Debug.WriteLine(Encoding.ASCII.GetString(bytes));
    Debug.WriteLine(dataFlag);
    Debug.WriteLine(status);
    Debug.WriteLine(count);
    Debug.WriteLine(retval);

    return retval;
}

Вот альтернативный вариант с использованием исправлено массив байтов.Для этого второго метода требуется ключевое слово unsafe , а также проект, в котором для «Разрешить небезопасный код» установлено значение true.

internal static class NativeMethods
{
    [DllImport("Reader2.dll")]
    public static extern unsafe short GetIDBuffer(
           IntPtr hCom, ref byte dataFlag, ref byte count, 
           byte* value, ref byte stationNum);
}

static unsafe int TestGetIDBuffer()
{
    const int arraySize = 255;
    byte[] bytes = new byte[arraySize + 1];

    byte dataFlag = 0;
    byte count = arraySize;
    byte status = 0;

    int retval;
    fixed (byte* buffer = bytes)
    retval = NativeMethods.GetIdBuffer(
             IntPtr.Zero, ref dataFlag, ref count, buffer, ref status);

    Debug.WriteLine(Encoding.ASCII.GetString(bytes));
    Debug.WriteLine(dataFlag);
    Debug.WriteLine(status);
    Debug.WriteLine(count);
    Debug.WriteLine(retval);

    return retval;
}

Кажется, что все dataFlag, count и stationNumзначения байтов in / out.

Заполняемый буфер данных представляет собой массив байтов.Этот буфер необходимо исправить, чтобы GC не перемещал его, пока вы вызываете нативный метод.Это делается неявно в первом примере и явно во втором.

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

На самом деле есть две формы оператора fixed .Одна из упомянутых в этой статье MSDN позволяет создавать массив фиксированного размера, как в общедоступных байтах с фиксированным байтом [ArraySize];Другой в этой статье MSDN позволяет закрепить местоположение переменной, чтобы получить ее адрес.

Вот мой тестовый код C ++:

extern "C" __declspec(dllexport) unsigned short __stdcall GetIDBuffer( 
    HANDLE hCom, unsigned char * dataFlag, unsigned char * count, 
    unsigned char* buffer,  unsigned char * status )
{
    memset(buffer, 0x1E, *count);

    *dataFlag = 0xa1;
    *count = 0x13;
    *status = 0xfe;

    return 0x7531;
}

единственное различие между приведенным выше кодом C # и моим тестовым кодом заключается в том, что точку входа нужно указывать по-разному, поскольку я использовал компилятор C ++, например

[DllImport("PInvokeTestLib.dll", EntryPoint = "_GetIDBuffer@20")]
public static extern unsafe short GetIdBuffer(...

. Можно смело указывать параметры, передаваемые методу (не включая параметр массива значений) в качестве примитивных типов, отличных от байта, таких как int, long и т. д. Это связано с тем, что 1) значения передаются в виде ссылки и 2) x86 использует порядок байтов с прямым порядком байтов.Это приводит к тому, что отдельный байт записывается в младший байт из четырех байтов, переданных в int.

Рекомендуется использовать совпадающие типы, в данном случае - байт.

2 голосов
/ 17 марта 2012

Ваше определение для Count неверно, оно должно быть ref byte, а не ref Uint на основе собственного прототипа. Если вы измените его на правильный тип и инициализируете его значением, которое вы передаете в конструктор StringBuilder, все должно работать ... если нет, я бы сделал шаг назад и использовал бы байтовый массив вместо StringBuilder, чтобы помочь получить лучшее представление о том, что делает неуправляемый код.

редактирование:

Ошибка, которую вы получаете, указывает на переполнение буфера

[DllImport("Reader2.dll")]
public static extern  short GetIDBuffer(IntPtr hCom, ref byte DataFlag, ref byte Count,**(type)** value , ref byte StationNum);

static  void Main(string[] args)
{

    byte count = 0, station = 1, flag = 0; //this right here is probably your problem
    IntPtr hcom = IntPtr.Zero;

Неуправляемый код, который вы вызываете, не может знать, насколько велик буфер, в который вы передаете. Если ваш API вменяемый, вы инициализируете переменную count, чтобы вызываемый объект знал размер буфера.

Если это не так, вам нужно будет просмотреть документацию, чтобы выяснить, какой объем буфера вам нужно предоставить.

Если ни один из них не является истинным, мы должны предположить, что возвращаемое значение - это количество записанных байтов и, поскольку это короткий, вам нужно передать в буфер длиной не менее 65 535 байтов.

Если ничего из этого не сработает, вам нужно будет позвонить поставщику и выяснить, как указать размер буфера, поскольку на самом деле это не выглядит как проблема взаимодействия.

Вам также не нужно вообще использовать fixed. Цель исправления состоит в том, чтобы предоставить вам указатель, который будет использоваться между несколькими неуправляемыми вызовами, или написать управляемый код, который немного быстрее (из-за отсутствия проверки границ), поскольку ни один из них не использует байтовый массив.

ваш готовый код должен выглядеть примерно так:

[DllImport("Reader2.dll")]
public static extern short GetIDBuffer(IntPtr hCom, ref byte dataFlag, ref byte count,byte [] value, ref byte stationNum);

// ...
byte[] value = new byte[65536];
byte count = 255; //does 255 imply some buffer size?

short len = GetIDBuffer(hCom, ref dataFlag, ref count,value, ref stationNum);
var s1 = Encoding.ASCII.GetString(value,0,count);
var s2 = Encoding.ASCII.GetString(value,0,len);
Console.WriteLine("using count gives\""+s1+"\"");
Console.WriteLine("using return value gives\""+s2+"\"");
0 голосов
/ 17 марта 2012

Попробуйте использовать SafeHandle вместо IntPtr в вашем P / Invoke. Возможно, что управляемая оболочка около hCom получает GC'd и завершается в середине вашего собственного вызова, что делает hCom недействительным.

...