У меня есть С++ DLL, которая взаимодействует с устройством чтения карт. Для этого требуется указатель на структуру данных, что не является проблемой. Однако при попытке взаимодействия с DLL на С# у меня возникают всевозможные проблемы. Ошибки записи в защищенную память, приложение просто закрывается после выполнения команды getData и т.д. Вот что мы имеем.
С++ Метод из заголовка
void readCard(cardData* dataBuffer);
Код С#
Wrapper.cs
public struct cardData{
Byte[] data01;
Byte[] data02;
}
[dllImport("card.dll")]
public static extern void readCard(ref cardData data);
Form1.cs
Wrapper.cardData tmpData = new wrapper.cardData();
tmpData.data01 = new Byte[];
tmpData.data02 = new Byte[];
readCard(ref tmpData);
Я также пробовал передавать cardData как IntPtr, используя Marshal.StructureToPtr, который не возвращал никаких данных, когда возвращался, пытался прочитать ptr в структуру Marshal.PtrToStructure...
Я пытался работать с файлами справки и другими сообщениями, потому что кажется, что многие люди не могут работать с C/С++ DLL. Я даже попытался написать все это на С++ и вернуть ему строку с данными, проанализированными в С++ DLL, но которая бросает чтение/запись на защищенную память
Самая большая проблема, которую я вижу с вашим кодом, заключается в том, что вы не указали своим байтам [] члены явного размера. Без этого оператора размера маршаллер будет рассматривать их точно так же, как простой ссылочный тип. Полученная структура будет иметь размер 8 байтов на 32-битной платформе и почти наверняка приведет к написанию защищенной памяти.
Предполагая, что байтовые массивы имеют фиксированный размер в коде C, вы должны использовать атрибут MarshalAs, чтобы дать байт-массивам одну и ту же семантику в управляемом коде. Это влечет за собой предоставление фиксированного размера.
[StructLayout(LayoutKind.Sequential)]
public struct cardData{
[MarshalAs(UnmanagedType.ByValArray, SizeConst=300)]
Byte[] data01;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=300)]
Byte[] data02;
}
Измените 300 на любой размер, указанный в собственном коде для массива.
Также вы должны добавить атрибут StructLayout.
Ok. Таким образом, структура была задана в соответствии со структурой структуры.
Wrapper.cs
[StructLayout(LayoutKind.Sequential)]
public struct cardData{
[MarshalAs(UnmanagedType.ByValArray, SizeConst=99)]
Byte[] data01;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=101)]
Byte[] data02;
}
[DllImport("card.Dll")]
public static extern void readCard(ref cardData data);
И теперь он просто закрывается... Нет ошибок, никаких изменений в данных, приложение просто отключается.
Инструмент подписи PInvoke помог мне в прошлом.
Например, следующий C/С++:
struct cardData{
byte[] data01;
byte[] data02;
}
void readCard(cardData* dataBuffer);
Он имеет:
System.Runtime.InteropServices.StructLayoutAttribute( System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct cardData {
/// byte[]
public byte[] data01;
/// byte[]
public byte[] data02;
}
/// Return Type: cardData
///dataBuffer: cardData*
public delegate cardData readCard(ref cardData dataBuffer);
Вот фрагмент C-DLL-оболочки в С#, который я сделал.
Как упоминал Юрий, вам не хватает атрибута StructLayout, и вы, вероятно, должны использовать собственные типы в своей структуре и объявлении своей функции. Вероятно, вам понадобится использовать ключевое слово unsafe
в нескольких местах, что может быть или не быть приемлемым для вас, но это было хорошо для меня.
[StructLayout(LayoutKind.Sequential)]
public unsafe struct X_Message
{
public byte id;
public byte* data;
public DWORD data_length;
}
// ...
[DllImport("x-driver.dll")]
protected unsafe static extern int X_ReadMessage(void* h, X_Message* message);
Я заметил, что ваш байт [] не имеет никакого размера, связанного с ними. Знаете ли вы, какого размера должны быть массивы? IIRC, пространство для массивов должно выделяться при инициализации.
Если я ухожу из базы, дайте мне знать, и я удалю этот пост.
Возможно, вашей карточке необходимо использовать StructLayoutAttribute. Также вы можете использовать Dependency Walker для поиска местоположения в файле card.dll для этого метода и добавить его как именованный параметр.
Используйте IntPtr вместо байта []. Ваша DLL не может обрабатывать управляемые данные.