Нужна помощь для вызова структуры prsvoke - System.AccessViolationException - PullRequest
5 голосов
/ 28 апреля 2011

Эй! Я только начал возиться с pinvoke и столкнулся с проблемой. Я получаю AccessViolationException. Прежде всего, есть ли способ отладки или отслеживания, какое поле вызывает эту ошибку? Единственное, что записывается - это структура результата.

Вызов c ++ выглядит так:

MyFunc(int var1, _tuchar *var2, _tuchar *var3, _tuchar *var4, MyStruct *Result,
       _tuchar *var5, _tuchar *var6);

Структура c ++:

typedef struct MyStruct 
{
   _tuchar *id;
   _tuchar *ErrorMessages;
   int int1; 
   _tuchar language[3]; 
   _tuchar *result;
   int type;
   int number;
   int *type2; 
   _tuchar **blocks;
}

Структура C #:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MyStruct
{
    [MarshalAs(UnmanagedType.LPStr)]
    public string Id;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst=500)]
    public char[] ErrorMessages;

    public int int1;

    [MarshalAs(UnmanagedType.LPStr)]
    public string language;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 15)]
    public char[] result;

    public int type;

    public int number;

    public int type2;

    [MarshalAs(UnmanagedType.ByValArray)]
    public string[] blocks;

Объявление метода C #:

[DllImport(MyPath, EntryPoint = "MyEntryPoint", SetLastError = true,
           CharSet = CharSet.Unicode)]
internal static extern int MyFunc(int var1, string var2, string var3,
      string var4, ref MyStruct Result, string var5, string var6);

Звонок на C #:

var result = new MyStruct();
MyFunc(0, "var2", "var3", "var4", ref result, "var5", "var6");

Надеюсь, я ничего не пропустил. Спасибо за любую помощь!

Ответы [ 2 ]

4 голосов
/ 28 апреля 2011

Ооооо, чувак!Вы выбрали довольно сложный случай для своего первого безумного опыта.Я рекомендую сначала сделать что-то попроще, а затем перейти к реальным вещам.

Сначала , CharSet=CharSet.Ansi выглядит подозрительно.Все ваши строки и символы _tuchar, и я собираю u там, что означает "Unicode", не так ли?Если это так, вам нужно CharSet=CharSet.Unicode.

Во-вторых (и это наиболее вероятный виновник), почему поле ErrorMessages маршалируется как ByValArray?Вы знаете, что ByVal здесь означает "по стоимости", не так ли?Как в, не по ссылке.И вы знаете, что маленькая звездочка в C ++ означает «ссылка», не так ли?Так почему же ваше ссылочное поле ErrorMessages маршалируется как массив значений?В случае, если вы не знаете, массив обычно считается переданным «по значению», когда передается весь его контент, вместо простой передачи ссылки (указателя) в область памяти, где хранится весь этот контент.В определении структуры C ++ вы указываете _tuchar*, что означает «ссылку (указатель) на некоторую память, содержащую один или несколько _tuchars», тогда как в C # вы указываете [MarshalAs(UnmanagedType.ByValArray, SizeConst=500)], что означает, что «здесь должно быть 500 _tuchars,не больше и не меньше ".Видя, что ссылка (указатель) обычно занимает 4 байта (или 8 байтов на 64-битных машинах), а 500 символов юникода занимают 1000 байтов, вы обнаружите очевидное несоответствие прямо здесь. в-четвертых , та же точка относится к полям result и blocks.

В-пятых , поле language полностью противоположно: код C ++ говорит: "здесь 3 _tuchars », в то время как код C # говорит« здесь есть ссылка (указатель) на строку »(если вы не знаете, LPStr означает« длинный указатель на STRing »)

И, наконец, , после того, как вы исправили все эти проблемы, я рекомендую вам выполнить вашу программу и распечатать результат вызова Marshal.SizeOf( typeof( MyStruct ) ).Это даст вам точную оценку вашей структуры, по мнению .NET.Перейдите на сторону C ++ и распечатайте sizeof( MyStruct ).Это даст вам то, что С ++ думает о размере.

Если они получатся разными, посмотрите, что не так.Попробуйте удалить поля одно за другим, пока они не станут одинаковыми.Это даст вам поле (и) виновника.Работайте с ними.

В целом, я предлагаю вам лучше понять, как все работает в первую очередь.Этот случай слишком сложен для начинающего.

Удачи!

0 голосов
/ 28 апреля 2011

Это немного в темноте, но вы пытались украсить строковые параметры с помощью MarshalAs(UnmanagedType.LPWStr):

[DllImport(MyPath, EntryPoint = "MyEntryPoint", SetLastError = true,
    CharSet = CharSet.Unicode)]
internal static extern int MyFunc(
    int var1,
    [MarshalAs(UnmanagedType.LPWStr)]
    string var2, 
    [MarshalAs(UnmanagedType.LPWStr)]
    string var3,
    [MarshalAs(UnmanagedType.LPWStr)]
    string var4,
    ref MyStruct Result,
    [MarshalAs(UnmanagedType.LPWStr)]
    string var5,
    [MarshalAs(UnmanagedType.LPWStr)]
    string var6);

Я считаю, что маршалинг по умолчанию, выбранный для строк, BStr и _tuchar должен расшириться до wchar_t, поэтому я предполагаю, что LPWStr - правильный метод маршалинга (указатель на строку широких символов). 1009 *


Обновление: Различные вещи на MyStruct выглядят не совсем правильно:

ErrorMessages помечен как ByValArray, и поэтому взаимодействие .Net, вероятно, ожидает, что MyStruct будет выглядеть примерно так:

typedef struct MyStruct 
{
   _tuchar *id;
   _tuchar ErrorMessages[500];
   // Rest of MyStruct

Что, вероятно, вызовет проблемы - то же самое для result.

Также я думаю, что language должен использовать ByValArray с размером 3.

Наконец, blocks, вероятно, следует передать, используя LPArray - ByValArray не кажется правильным.

(Между прочим, это в основном догадки - надеюсь, это указывает на правильное направление, но у меня нет такого большого опыта взаимодействия с P / Invoke)


Еще одно обновление: На MyStruct вы объявляете кодировку Ansi, а на MyFunc ее Unicode ... неуправляемая dll скомпилирована с Unicode или Ansi? Если он использует Unicode, то я считаю, что вы должны использовать LPWStr при сортировке строк, а с Ansi это должно быть LPStr.

...