For MBS Xojo Plugins in version 20.3 we include a new set of classes to do load C libraries and call functions there. Similar to the declare commands in Xojo, but much more dynamic and with additional features:
Our new DeclareLibraryMBS class allows you to load a library file. On MacOS you load a dylib file, on Windows a DLL file and on Linux a shared object file with so file extension.
Just pass the file path (can be relative) or the folder item for the library file to the constructor for DeclareLibraryMBS. Then you can check with SymbolNames function the array of exported functions defined in the library. Once you picked a function name, you can use Symbol function to query the function pointer for an exported function.
The new DeclareFunctionMBS class allows you to define a callable function as an object, similar to the built-in delegates in Xojo. You pass in to the constructor the function pointer you got via Symbol function before. And you pass the function signature to define the parameters and return values. For those we have kType* constants for you where you can add them together as a string. The types include things like "i" for an integer parameter or "p" for a pointer. The signature must match exactly the parameters of the C function with parameters in brackets and the return value attached to it. If the signature is wrong, the call later will fail and most likely crash the application.
Let's check an example:
bool test(int i, double d, const char* test)
This gives a signature "(idZ)B" with bool as result and int, double and a C string as parameter.
Once you have the function object, you can set parameters. We allow you to use ParameterInteger() and other setter/getter functions to pass in parameters directly as integer, pointer, boolean, single, double or string. We also allow to set/get the values as variant using ParameterValue(). If you need to set all parameters, you can use SetParameters method to pass values directly or as dictionary.
To call the C function, please call Invoke method. There we also allow you to pass parameters directly. The result is converted to a variant to handle all the possible data types.
Sometimes you may need to get a callback from a library and for this we have the DeclareCallBackMBS class. For the constructor you pass the signature for the function you need to pass to the delegate. A dynamic function stub is created, which accepts the given parameters, puts them in variables and passes them as parameters to the Callback event as variants. You can return a value, which then is returned as result of the function. Use the FunctionPtr property to get the ptr to the callback function.
For callbacks arriving on a different thread, we have a special handling. We can forward the call to the main thread for all platforms. For MacOS we can in addition do this asynchronously to not block the calling thread while we wait for main thread to process the request. When a call is forwarded to another thread, you may need to copy or retain object references. To solve this you can mark parameters to be Strings, NSObject or CFObject references. If you for example declare second parameter to be a NSObject, you can set ParameterNSRetain(1) = true. The callback function will retain it, so the object is staying alive while the call is performed and released later.
The table for the types to use for parameters and return value:
|c||char||signed 8 bit value|
|C||unsigned char||unsigned 8 bit value|
|s||short||signed 16 bit value|
|S||unsigned short||unsigned 16 bit value|
|i||int||signed 32 bit value|
|I||unsigned int||unsigned 32 bit value|
|j||long||for Windows 32-bit value, for MacOS and Linux 64-bit value|
|J||unsigned long||for Windows 32-bit value, for MacOS and Linux 64-bit value|
|l||long long||signed 64 bit value|
|L||unsigned long long||unsigned 64 bit value|
|f||float||floating point number in 32-bit size|
|d||double||floating point number in 64-bit size|
|p||pointer||pointer in 32 or 64-bit.|
|Z||C String||C style string with zero byte as end.|
The prefixes you can put before all parameters to define the calling conventions:
|Default||Normal C function without prefix|
|_e||Ellipsis||C function taking variable arguments|
|_s||stdcall||standard calling convention|
|_f||fastcall GNU||Fastcall convention as used in GNU compilers|
|_F||fastcall Microsoft||Fastcall convention as used in Microsoft compilers|
|_+||thiscall Microsoft||Thiscall convention as used in Microsoft compilers|
Here is a simple example using libz and the zlibVersion function. On MacOS the library is installed by default in "/usr/lib/libz.1.dylib". The function returns a string and takes no parameters. Calling convention is default C one, so no prefix. The signature is just "()Z" as there are no parameters and the result is a C string. We can then invoke the function and the plugin will take the returned C String and return it as a variant, where we can get the string from:
Please do not hesitate to contact us with your questions.