Попытка вызвать функцию dll из Matlab, вызывающая сбой

Я пытаюсь использовать стороннюю внешнюю DLL (от usbmicro) в MATLAB, но она продолжает давать сбой MATLAB. Это из документации, указывающей синтаксис вызова функции из программы C:

int USBm_About( char *about );

Я попробовал этот скрипт MATLAB (да, это очень глупо, я новичок в MATLAB):

>> loadlibrary('USBm.dll','USBmAPI.h')
>> libfunctions('USBm')
>> s='sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss';
>> st=strcat(s,s,s,s);
>> vp = libpointer('voidPtr',[int8(st) 0]);
>> result=calllib('USBm','USBm_About',vp)

и этот:

>> loadlibrary('USBm.dll','USBmAPI.h')
>> libfunctions('USBm')
>> s='sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss';
>> st=strcat(s,s,s,s);
>> vp=libpointer('cstring',st);
>> result=calllib('USBm','USBm_About',vp)

В обоих случаях вызов calllib() вызывает сбой MATLAB с ошибкой сегментации.

Версия MATLAB 7.10; ОС — Windows Vista.


Обновлять:

Вот скриншот libfunctionsview USBm: screenshot

Вот заголовочный файл:

#ifndef FILE_USBmAPI_h
#define FILE_USBmAPI_h


// Prototypes for this DLL.
// These are the API functions available to the .dll user.


// Discovery routine
extern "C" __declspec(dllexport) int USBm_FindDevices(void);

// Return info about devices
extern "C" __declspec(dllexport) int USBm_NumberOfDevices(void);
extern "C" __declspec(dllexport) int USBm_DeviceValid(unsigned char);
extern "C" __declspec(dllexport) int USBm_DeviceVID(unsigned char);
extern "C" __declspec(dllexport) int USBm_DevicePID(unsigned char);
extern "C" __declspec(dllexport) int USBm_DeviceDID(unsigned char);
extern "C" __declspec(dllexport) int USBm_DeviceFirmwareVer(unsigned char);
extern "C" __declspec(dllexport) int USBm_DeviceMfr(unsigned char, char *);
extern "C" __declspec(dllexport) int USBm_DeviceProd(unsigned char, char *);
extern "C" __declspec(dllexport) int USBm_DeviceSer(unsigned char, char *);

// General USBmicro U4xx device access
extern "C" __declspec(dllexport) int USBm_ReadDevice(unsigned char, unsigned char *);
extern "C" __declspec(dllexport) int USBm_SetReadTimeout(unsigned int);
extern "C" __declspec(dllexport) int USBm_WriteDevice(unsigned char, unsigned char *);
extern "C" __declspec(dllexport) int USBm_CloseDevice(unsigned char);

// DLL string info access
extern "C" __declspec(dllexport) int USBm_RecentError(char *);
extern "C" __declspec(dllexport) int USBm_ClearRecentError(void);
extern "C" __declspec(dllexport) int USBm_DebugString(char *);
extern "C" __declspec(dllexport) int USBm_Copyright(char *);
extern "C" __declspec(dllexport) int USBm_About(char *);
extern "C" __declspec(dllexport) int USBm_Version(char *);



// General U4x1 device functions
// -----------------------------

// Port initialization
extern "C" __declspec(dllexport) int USBm_InitPorts(unsigned char);
extern "C" __declspec(dllexport) int USBm_InitPortsU401(unsigned char);
extern "C" __declspec(dllexport) int USBm_InitPortsU421(unsigned char);
extern "C" __declspec(dllexport) int USBm_InitPortsU451(unsigned char);

// Port/bit reading and writing
extern "C" __declspec(dllexport) int USBm_WriteA(unsigned char, unsigned char);
extern "C" __declspec(dllexport) int USBm_WriteB(unsigned char, unsigned char);
extern "C" __declspec(dllexport) int USBm_WriteABit(unsigned char, unsigned char, unsigned char);
extern "C" __declspec(dllexport) int USBm_WriteBBit(unsigned char, unsigned char, unsigned char);
extern "C" __declspec(dllexport) int USBm_ReadA(unsigned char, unsigned char *);
extern "C" __declspec(dllexport) int USBm_ReadB(unsigned char, unsigned char *);
extern "C" __declspec(dllexport) int USBm_SetBit(unsigned char, unsigned char);
extern "C" __declspec(dllexport) int USBm_ResetBit(unsigned char, unsigned char);

// Port direction
extern "C" __declspec(dllexport) int USBm_DirectionA(unsigned char, unsigned char, unsigned char);
extern "C" __declspec(dllexport) int USBm_DirectionAOut(unsigned char);
extern "C" __declspec(dllexport) int USBm_DirectionAIn(unsigned char);
extern "C" __declspec(dllexport) int USBm_DirectionAInPullup(unsigned char);
extern "C" __declspec(dllexport) int USBm_DirectionB(unsigned char, unsigned char, unsigned char);
extern "C" __declspec(dllexport) int USBm_DirectionBOut(unsigned char);
extern "C" __declspec(dllexport) int USBm_DirectionBIn(unsigned char);
extern "C" __declspec(dllexport) int USBm_DirectionBInPullup(unsigned char);

// Strobbing a byte of data
extern "C" __declspec(dllexport) int USBm_StrobeWrite(unsigned char, unsigned char, unsigned char, unsigned char);
extern "C" __declspec(dllexport) int USBm_StrobeRead(unsigned char, unsigned char *, unsigned char, unsigned char);
extern "C" __declspec(dllexport) int USBm_StrobeWrite2(unsigned char, unsigned char, unsigned char, unsigned char, unsigned char, unsigned char);
extern "C" __declspec(dllexport) int USBm_StrobeRead2(unsigned char, unsigned char *, unsigned char, unsigned char, unsigned char, unsigned char);
extern "C" __declspec(dllexport) int USBm_StrobeWrites(unsigned char, unsigned char *, unsigned char *);
extern "C" __declspec(dllexport) int USBm_StrobeReads(unsigned char, unsigned char *, unsigned char *);

// Reading pin-change latches
extern "C" __declspec(dllexport) int USBm_ReadLatches(unsigned char, unsigned char *);

// LCD routines
extern "C" __declspec(dllexport) int USBm_InitLCD(unsigned char, unsigned char, unsigned char);
extern "C" __declspec(dllexport) int USBm_LCDCmd(unsigned char, unsigned char);
extern "C" __declspec(dllexport) int USBm_LCDData(unsigned char, unsigned char);

// SPI routines
extern "C" __declspec(dllexport) int USBm_InitSPI(unsigned char, unsigned char);
extern "C" __declspec(dllexport) int USBm_SPIMaster(unsigned char, unsigned char *, unsigned char *);
extern "C" __declspec(dllexport) int USBm_SPISlaveWrite(unsigned char, unsigned char, unsigned char *);
extern "C" __declspec(dllexport) int USBm_SPISlaveRead(unsigned char, unsigned char *, unsigned char *);

// 2-wire routines
extern "C" __declspec(dllexport) int USBm_Wire2Control(unsigned char, unsigned char *);
extern "C" __declspec(dllexport) int USBm_Wire2Data(unsigned char, unsigned char *);

// Stepper routine
extern "C" __declspec(dllexport) int USBm_Stepper(unsigned char, unsigned char, unsigned char, unsigned char, unsigned char, unsigned char, unsigned char, unsigned char);

// 1-wire routines
extern "C" __declspec(dllexport) int USBm_Reset1Wire(unsigned char, unsigned char *);
extern "C" __declspec(dllexport) int USBm_Write1Wire(unsigned char, unsigned char);
extern "C" __declspec(dllexport) int USBm_Read1Wire(unsigned char, unsigned char *);
extern "C" __declspec(dllexport) int USBm_Write1WireBit(unsigned char, unsigned char);
extern "C" __declspec(dllexport) int USBm_Read1WireBit(unsigned char, unsigned char *);




#endif // multiple inclusion prevention

// End of file
//---------------------------------------------------------------------------

Обновлять:

Я попытался изменить эту строку:

extern "C" __declspec(dllexport) int USBm_About(char *);

к этому:

extern "C" __declspec(dllimport) int USBm_About(char *);

в заголовочном файле, а затем перезапустите MATLAB. Я снова запустил свой код, и MATLAB все еще падает.


person opike    schedule 25.04.2011    source источник
comment
Как бы мы могли решить это без USBmAPI.h и без знания правил взаимодействия с USBm_About?   -  person David Heffernan    schedule 25.04.2011
comment
Это было в исходном посте: int USBm_About( char *about ); А потом я просто добавил скриншот вывода из libfunctionsview USBm. Если вам нужна дополнительная информация, просто попросите ее.   -  person opike    schedule 25.04.2011
comment
аналогичный вопрос: Передача аргумента указателя в MATLAB на C- Функция DLL foo(char**)   -  person Amro    schedule 19.10.2011


Ответы (2)


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

extern "C" __declspec(dllexport)

когда вы создаете .dll, но Matlab (или любой другой проект, использующий вашу .dll) должен иметь:

extern "C" __declspec(dllimport)

когда вы включаете его.

Вот почему у людей обычно есть следующий макрос:

#ifdef USBMAPIEXPORTS 
#define USBMAPIDECLSPEC __declspec(dllexport)
#else
#define USBMAPIDECLSPEC __declspec(dllimport)
#endif

а потом по своим функциям в шапке пишешь:

extern "C" USBMAPIDECLSPEC int USBm_FindDevices(void);    

и вам нужно определить USBMAPIEXPORTS при создании .dll (но не при использовании его в Matlab).

Обратите внимание, что это не проблема с Matlab. Это стандартный способ использования .dll. Сделал ты попробуешь это?

person Chris A.    schedule 25.04.2011
comment
DLL пришла от третьего лица. У меня нет источника. Итак, из того, что вы видите, dll в ее нынешнем виде не будет работать с Matlab? - person opike; 26.04.2011
comment
Мне кажется, что тот, кто дал вам включаемый файл, сделал это неправильно. Я бы изменил включаемый файл USBmAPI.h на другой файл (сначала сделайте резервную копию), чтобы всякий раз, когда вы видите extern "C" __declspec(dllexport), меняли его на extern "C" __declspec(dllimport). (В качестве альтернативы вы можете использовать определение блока препроцессора, который я написал выше, а затем написать extern "C" USBMAPIDECLSPEC. Убедитесь, что вы не определяете USBMAPIEXPORTS, если делаете это.) - person Chris A.; 26.04.2011
comment
Я бы также посоветовал поставщику ваших файлов .dll и .h в будущем делать то, что я сказал в своем первоначальном ответе. Они должны определять USBMAPIEXPORTS при построении. Это стандартно и избавляет от многих головных болей. Просто чтобы было ясно, они создали .dll правильно, они просто не упростили вам ее включение. - person Chris A.; 26.04.2011
comment
Я попытался заменить все dllexports на dllimport, но все равно получил тот же результат. Я также попробовал USBm_Copyright, и это также вызывает нарушение сегментации. - person opike; 26.04.2011
comment
Я вижу, что OP также опубликован в ответах MATLAB: mathworks.com/matlabcentral/answers/ - person Amro; 19.10.2011

Я загрузил DLL с веб-сайта USBmicro и попытался используйте его, вызвав loadlibrary(). К сожалению, это разбило мою сессию MATLAB, как вы сказали.

После некоторых исследований я пришел к выводу, что способ DLL показывает, что ее функции несовместимы с соглашением о вызовах, которое ожидает MATLAB (cdecl или stdcall).

Вот как я исправил заголовочный файл:

#ifndef FILE_USBmAPI_h
#define FILE_USBmAPI_h

#ifdef __cplusplus
extern "C" {
#endif

// ...
int __stdcall USBm_About(char *);
int __stdcall USBm_Version(char *);
// ...

#ifdef __cplusplus
}
#endif

#endif

Теперь вы можете вызывать любую из экспортированных функций. Пример:

%# load library and see exported functions signatures
if ~libisloaded('USBm')
    loadlibrary('USBm.dll','USBmAPI.h')
    libfunctions('USBm','-full')
end

%# call the function: `int USBm_About(char *)`
str = repmat(' ',1,100);                           %# allocate buffer
pStr = libpointer('stringPtr',str);                %# pointer to string
[num str2] = calllib('USBm','USBm_About',pStr)
clear pStr

%# unload library
unloadlibrary USBm

Обратите внимание на сигнатуру функции, которую дает нам MATLAB:

[int32, cstring] USBm_About(cstring)

Интересно, что у этой функции есть дополнительный выходной аргумент. Причина в том, что MATLAB на самом деле не поддерживает передачу по ссылке, хотя вы можете создавать аргументы MATLAB, совместимые с указателями C.

Таким образом, если функция C возвращает данные во входных аргументах, переданных по ссылке, MATLAB создаст дополнительные выходные аргументы, чтобы вернуть эти значения (и любые входные аргументы, оканчивающиеся на Ptr или PtrPtr).

Теперь, хотя входной аргумент pStr напоминает указатель на тип char, он не является истинным, поскольку не содержит адрес массива символов MATLAB str. Когда функция выполняется, она возвращает правильный результат, но не изменяет значение в str (и в данном случае pStr, если это имеет значение).

Результат, который я получил, был:

>> num
num =
     0

>> str2
str2 =
 USBm.dll by USBmicro L.L.C. (www.usbmicro.com). Supports: U401, U421
person Amro    schedule 19.10.2011