Attempt to call dll function from matlab causing c

2019-07-12 18:38发布

问题:

I'm trying to use a third-party external DLL (from usbmicro) within MATLAB, but it keeps crashing MATLAB. This is from the documentation indicating the syntax of the function call from within a C program:

int USBm_About( char *about );

I tried this MATLAB script (yes it's very kludgy, I'm a MATLAB noob):

>> 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)

and this one:

>> 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)

In both cases, the calllib() call causes MATLAB to crash with a segmentation fault.

The version of MATLAB is 7.10; the OS is Windows Vista.


Update:

Here's a screenshot of libfunctionsview USBm:

Here's the header file:

#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
//---------------------------------------------------------------------------

Update:

I tried changing this line:

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

to this:

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

in the header file, and then restarting MATLAB. I ran my code again and MATLAB still crashes.

回答1:

This might be the standard problem that your include file needs to have

extern "C" __declspec(dllexport)

when you are building the .dll but Matlab (or whichever project uses your .dll) needs to have:

extern "C" __declspec(dllimport)

when you are including it.

This is why people usually have the following macro:

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

and then on your functions in your header you write:

extern "C" USBMAPIDECLSPEC int USBm_FindDevices(void);    

and you need to have USBMAPIEXPORTS defined when you build the .dll (but not when you use it in Matlab.)

Note that this is not a problem with Matlab. This is the standard way to use a .dll. Did you try this?



回答2:

I downloaded the DLL from USBmicro website, and tried to use it by calling loadlibrary(). Unfortunately, this crashed my MATLAB session as you said..

After some research, I came to find out that the way this DLL exposes its functions is not compatible with the calling convention that MATLAB expects (cdecl vs. stdcall).

Here is how I fixed the header file:

#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

Now you can call any of the exported functions. Example:

%# 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

Note the signature of the function that MATLAB gives us:

[int32, cstring] USBm_About(cstring)

Interestingly, it has an additional output argument for this function. The reason is that MATLAB does not really support passing by reference, although you can create MATLAB arguments that are compatible with C pointers.

Thus if the C function return data in input arguments passed by reference, MATLAB will create additional output arguments to return these values (and any input arguments ending in Ptr or PtrPtr).

Now although the input argument pStr resembles a pointer to type char, it is not a true one as it does not contain the address of the MATLAB character array str. When the function executes, it returns the correct result, but does not modify the value in str (nor in this case pStr for that matters).

The output I got was:

>> num
num =
     0

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