2016-07-28 7 views
4

Я пытаюсь следовать ответ на this questionPInvoke - маршалу массив структур с указателем

Моя структура выглядит следующим образом в C

typedef struct drive_info_t { 
    unsigned char drive_alias[32]; 
} drive_info_t; 

Моя функция выглядит в C

unsigned int get_drive_info_list(drive_info_t **list, unsigned int *item_count) { 
    //fill list in native C 

    //print out in native C 
    printf("list.alias - %s\r\n",list[i]->drive_alias); 
} 

Моя C# структура выглядит следующим образом

[StructLayout(LayoutKind.Sequential)] 
public struct drive_info_t 
{ 
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] 
    public byte[] drive_alias; 
} 

Моего C# функция объявления выглядит следующим образом

[DllImport("mydll.dll", EntryPoint = "get_drive_info_list", CallingConvention = CallingConvention.Cdecl)] 
public static extern uint GetDriveInfoList(out System.IntPtr ptr_list_info, out System.IntPtr ptr_count); 

Я звоню C# функции как этого

IntPtr ptr_list_info = IntPtr.Zero; 
IntPtr ptr_cnt = IntPtr.Zero; 

ret = api.GetDriveInfoList(out ptr_list_info, out ptr_cnt); 

Я сортировочный возвращаемые указатели, как этот

nAlloc = ptr_cnt.ToInt32(); 

int szStruct = Marshal.SizeOf(typeof(api.drive_info_t)); 
api.drive_info_t[] localStructs = new api.drive_info_t[nAlloc]; 

for (int i = 0; i < nAlloc; i++) 
{ 
    localStructs[i] = (api.drive_info_t)Marshal.PtrToStructure(ptr_list_info, typeof(api.drive_info_t)); 
    ptr_list_info = new IntPtr(ptr_list_info.ToInt32() + (szStruct)); 
} 

печатая struct alias типа

for (uint i = 0; i < localStructs.Length; i++) 
{  
    Console.WriteLine("list.alias - {0}", System.Text.Encoding.Default.GetString(localStructs[i].drive_alias)); 
} 

Спасибо, что останетесь со мной ..

Это то, что мой вывод выглядит на консольном приложении в C#. Вы можете увидеть родную печать библиотеки DLL C в консоль это значение, но мой маршалинг Мессинг где:

======================== C values ============================ 
list.alias - drv1 
list.alias - drv2 
list.alias - drv3 
list.alias - drv4 
======================== C# values ============================ 
list.alias - drv1 
list.alias - o£Q95drv2 
list.alias -   o£Q95drv3 
list.alias -     o£Q95drv4 

Я понятия не имею, где этот мусор текст и смещение происходит из.

Я отвечаю за .Net, другие члены команды могут при необходимости изменить родной C, но, если необходимо, изменения на родном C должны быть кросс-платформенными OSX/Windows/Linux.

Заранее спасибо.

+0

Из любопытства, что произойдет, если вы измените '[StructLayout (LayoutKind.Sequential)]' на '[StructLayout (LayoutKind.Sequential, Pack = 5)]'? – itsme86

+0

Pack = 5 не является допустимым значением, также я пробовал просматривать значения 1, 2, 4, 8 без изменений. – ScottN

+0

Мне было интересно ... Если вы измените IntPtr на UIntPtr и все ссылки ToInt32 на UInt32. Я знаю, иногда это может показаться несущественным, но ... –

ответ

1

Это прекрасный пример того, почему типы аргументов не определяют интерфейс. Рассмотрим

drive_info_t **list 

Это может быть указатель на массив структур. В этом случае предположительно массив выделяется вызываемым. Вот почему указатель на массив используется в отличие от массива.

Или это может быть массив указателей на структуру. Здесь массив будет выделен вызывающим, а структуры могут быть выделены либо вызываемым, либо вызывающим. Мы не можем сказать.

Мы можем заметить, что ваш интерфейс принимает массив указателей на struct. Посмотрите на этот код:

list[i]->drive_alias 

Совершенно очевидно list представляет собой массив указателей на структуры.

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

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

Помимо этого, второй аргумент должен быть ref uint или, возможно, out uint в зависимости от семантики функции. Опять же, это то, что мы не можем решить.

+0

'Почти наверняка вы будете использовать IntPtr [] для маршалирования массива', да, я в конечном итоге изменил объявление на '[In, Out] System.IntPtr [] ptr_list_info', а также 2-й параметр для uint. Благодаря! – ScottN