Нечетные ошибки из структуры / функции PInvoke - PullRequest
2 голосов
/ 29 июля 2011

В настоящее время я пишу оболочку C # для API C ++, но определенная структура и функция, которая опирается на эту структуру, выдают очень странные ошибки при отладке.

C ++ Struct:

typedef struct  
{  
    unsigned __int handle;  
    char name[80];  
    unsigned int unique_ID;  
} DeviceInfo;

Затем следует эта функция:

int __stdcall get_device_info(DeviceInfo di[], const int length_of_di_array, int* p_numValidDevices);

Структура и функция импортируются следующим образом:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]  
public struct DeviceInfo  
{
    public UInt32 handle;  
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 80)]  
    public String name;  
    public UInt32 unique_ID;  
}

[DllImportAttribute("MyC++API.dll", EntryPoint = "get_device_info", CallingConvention = CallingConvention.StdCall)]  
public static extern int get_device_info(ref DeviceInfo di, int length_of_di_array, ref numValidDevices);

Предполагаемое использование этой структуры и функции - просто получить некоторую информацию об устройстве.с доски доступа.В настоящее время у меня нет доступа к телу функции в C ++, поэтому я могу только предположить, что она работает на 100% (работает нормально в C ++).

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

C # код:

static void Main()  
{    
    int numValidDevices = 0; //initialize variable  
    DeviceInfo[] di = new DeviceInfo[16]; //max of 16 devices  

    for (int i = 0; i < numValidDevices; ++i) //sorts through all validated devices  
    {  
        rc = get_device_info(ref di[i], 16, ref numValidDevices); //accesses each device element and returns the data  
        Console.WriteLine("Handle: {0}\nName: {1}\nUnique ID: {2}", di[i].handle, di[i].name, di[i].unique_ID);  
    }  
    Console.ReadLine(); //stops console from closing prematurely  
    API_close();  //custom close function from the C++ API
}

Ошибки при отладке (информацияпо-прежнему отображается): «Произошло необработанное исключение типа« System.Threading.ThreadStateException »в System.dll

Дополнительная информация: поток не был запущен."

"Необработанное исключениетипа «System.ExecutionEngineException» возникла в mscorlib.dll »

Ошибка при отладке (информация не отображается, программа не выполняется):« Произошло необработанное исключение типа «System.AccessViolationException» в mscorlib.dll

Дополнительная информация: Попытка чтения или записи в защищенную память. Это часто свидетельствует о повреждении другой памяти. "

При закрытии окна консоли: «Инструкция в« 0x7c9113c0 »ссылается на память в« 0x00000000 ».Память не может быть «записана». »(Иногда говорится« прочитано »вместо« записано »).

Я провел много исследований по PInvoke и наткнулся на приложение Microsoft InteropAssistant , различные статьи о переполнении стека, такие как , эта и эта публикация , кажется даже ближе к тому, что я делаю, но я все еще копаюсь в том, как использовать Marshal.CoTaskMemAlloc /Освободите, и посмотрите, будет ли это вообще что-нибудь делать ...

Пока то, что у меня есть для моей структуры и функции, верно, я попытался изменить структуру, чтобы использовать IntPtr, но это не возвращает di.name значение и di.unique_ID становится бестолковым (как ни странно, di.handle остается в силе)

C # код:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]  
public struct DeviceInfo  
{
    public UInt32 handle;  
    IntPtr p_name;
    public String name { get { return Marshal.PtrToStringAnsi(p_name); } }
    public UInt32 unique_ID;  
}

Предполагаемый вывод:

Handle: 3126770193  
Name: DEVICE_A  
Unique ID: 12345678  

Выходные данные IntPtr:

Handle: 3126770193  
Name:  
Unique ID: 1145128264  

Как ни странно, использование IntPtr не приводит ни к одной из вышеперечисленных ошибок и работает нормально. Это приводит меня к мысли, что проблема заключается в маршалинге над символом C ++ в строку, но яЯ не уверен, что проблема заключается вмаршалинг, управление памятью (нет?) или что-то, что я не уловил полностью.

Любая и вся обратная связь была бы очень признательна, я был озадачен этим несколько недель назад...

Ответы [ 3 ]

2 голосов
/ 29 июля 2011

Исключения, которые вы получаете, указывают на то, что неуправляемый код, который вы вводите, уничтожает кучу мусора. Это не кристально почему, но ты не даешь маршаллеру пинвока большой шанс сделать правильную вещь. Он не может правильно закрепить массив. Начните с правильного объявления функции, она принимает массив, поэтому объявите его:

[DllImportAttribute("MyC++API.dll", CallingConvention = CallingConvention.StdCall)]  
public static extern int get_device_info(
    DeviceInfo[] di, 
    int length_of_di_array, 
    out int p_numValidDevices
);

Ваше первое объявление DeviceInfo правильное, второе - нет, поскольку строка не является указателем.

2 голосов
/ 29 июля 2011

Что-то здесь не складывается.Мне не понятно, как должна вызываться функция.

В частности, это объявление:

int __stdcall get_device_info(DeviceInfo di[], const int length_of_di_array, int* p_numValidDevices);

не соответствует тому, как вы его используете:

[DllImportAttribute("MyC++API.dll", EntryPoint = "get_device_info", CallingConvention = CallingConvention.StdCall)]  
public static extern int get_device_info(ref DeviceInfo di, const int length_of_di_array, int* p_numValidDevices);

...

DeviceInfo[] di = new DeviceInfo[16]; //max of 16 devices  
for (int i = 0; i < numValidDevices; ++i) //sorts through all validated devices  
{
    rc = get_device_info(ref di[i], 16, ref numValidDevices); //accesses each device element and returns the data
}

Вы говорите, что длина массива равна 16, начиная с индекса i, что неверно.Вы хотели пропустить только один элемент массива за раз?

DeviceInfo[] di = new DeviceInfo[16]; //max of 16 devices  
for (int i = 0; i < numValidDevices; ++i) //sorts through all validated devices  
{
    rc = get_device_info(ref di[i], 1, ref numValidDevices); //accesses each device element and returns the data
}

Или вы хотели пропустить весь массив один раз?

DeviceInfo[] di = new DeviceInfo[16]; //max of 16 devices
rc = get_device_info(ref di[0], 16, ref numValidDevices); //accesses each device element and returns the data
for (int i = 0; i < numValidDevices; ++i) //sorts through all validated devices  
{
  Console.WriteLine(...);
}

PS Я бы подумал об изменении вашегоp / invoke объявление должно быть:

[DllImportAttribute("MyC++API.dll", EntryPoint = "get_device_info", CallingConvention = CallingConvention.StdCall)]  
public static extern int get_device_info(
    [In, Out] [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] DeviceInfo[] di,
    int length_of_di_array,
    ref int p_numValidDevices);
0 голосов
/ 11 августа 2011

Итак, как указано в ответах ниже, у меня были две проблемы:
1. Я не правильно вызывал свой DllImport, как я пытался взломать вывод при этомЯ напортачил с выделением памяти для массива структур.
2. Я попытался объединить вывод и еще больше испортил код (пытался передать массив DeviceInfo di как один элемент di [число] вместов целом).

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]  
public struct DeviceInfo  
{
    public UInt32 handle;  
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 80)]  
    public String name;  
    public UInt32 unique_ID;  
}

[DllImportAttribute("MyC++API.dll", EntryPoint = "get_device_info", CallingConvention = CallingConvention.StdCall)]  
public static extern int get_device_info(
    [In, Out] [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] DeviceInfo[] di,
    int length_of_di_array,
    ref int p_numValidDevices);

static void Main()
{

    int numValidDevices = 0;

    DeviceInfo[] di = new DeviceInfo[16];

    get_device_info(di, 16, ref numValidDevices);

    for (int i = 0; i < numValidDevices; ++i)
    {
        Console.WriteLine("Handle: {0}\nName: {1}\nUnique ID: {2}", di[i].handle, di[i].name, di[i].unique_ID);
    }

    Console.ReadLine();
    API_close();
}
...