Callback from usb device and external dll

Hello all,

I am testing a callback method to get events from a USB connected instrument. I looked at many (!!!) posts on Callbacks and found this message:

https://forum.xojo.com/t/use-a-callback-function-from-a-c-lib/28698

with which I based my Declare, with no success.

I can currently connect to the instrument and get all the data I want with the API.
I would just like to add the Callback method, if possible. My problems and questions are written in the text where appropriate.

The API definitions are:

//-------------------------------------------------------------------------------
// c++ API
// NOTE: the API events, functions, and DLL were renamed.

RegisterDeviceEventHandler()

API FPtr_DeviceEventHandler RegisterDeviceEventHandler (
FPtr_DeviceEventHandler handler,
void * context )

Registers a device event handler callback function.

Parameters:
in : handler : a pointer to the event handler callback function. Or NULL if a registered handler should be removed
in : context : a context for the callbacks use. May be NULL

//---------------------------
FPtr_DeviceEventHandler

typedef void(CALLING_CONVENTION * FPtr_DeviceEventHandler) (DeviceHandle devHndl, DeviceEvent event, void *context)

Callback signature of a device event handler function.

Parameters:
in : devHndl : handle to the device
in : event : event type, e.g. eButtonPressed
in : context : the context which was registered with the handler

//---------------------------
// “DeviceEvent” definition

enum {
eArrival = 0x11, /< device plugged-in */
eDeparture = 0x12, /
< device unplugged */
eButtonPressed = 0x01, /**< button pressed */
};
typedef UInteger DeviceEvent;

//-------------------------------------------------------------------------------

The c++ demo code is

//-------------------------------------------------------------------------------
// c++ demo code
// This function is called if an event happens

void MyDeviceEventFunction(DeviceHandle devHndl, DeviceEvent event, void *refCon)
{
// increase our event counter
int eventCounter = static_cast<int>(refCon);
*eventCounter += 1;

std::cout << “Event #” << *eventCounter;
switch(event) {
case eButtonPressed: std::cout << " ‘Button pressed’ 0x" << std::hex << devHndl << std::dec << std::endl; break;
case eArrival: std::cout << " ‘Device attached’ 0x" << std::hex << devHndl << std::dec << std::endl; break;
case eDeparture: std::cout << " ‘Device removed’ 0x" << std::hex << devHndl << std::dec << std::endl; break;
}
}

//---------------------------
// c++ demo
// This function demonstrates the device event registration usage

void DeviceEventHandlerDemo()
{
std::cout << “\n\n------------------------------” << std::endl;
std::cout << " Device Event Handler demonstration" << std::endl;

int eventCounter = 0; // number of events
// register our event handler with eventCounter
RegisterDeviceEventHandler(&MyDeviceEventFunction, &eventCounter);

std::cout << “We need three events. Press button, and/or dis-/reconnect a device” << std::endl;
while (eventCounter < 3) {
Sleep(100); // sleep for some milliseconds
}

// Unregister our event handler
RegisterDeviceEventHandler(NULL, NULL);
}

//-------------------------------------------------------------------------------

And my Xojo code is:

//-------------------------------------------------------------------------------
// prototype code in Xojo
// Located in the program Window Open event:
// The USB device is verified to be connected and a device handle (devHandle) is already obtained.

// Pragma; required or not? (Note: such a Pragma is not required for the other API calls for this device)
#Pragma X86CallingConvention StdCall

// “devicedll” is the external 3rd party Windows library (devicedll.dll); an equivalent macOS Framework could be used
// refcon was included to match the c++ demo but is it required?
// this Declare is NOT valid; a Syntax error is flagged by Xojo; it is accepted (but does nothing) if “Return” and text after it is removed…

Soft Declare Function RegisterDeviceEventHandler Lib “@executable_path/…/…/devicedll” (callback As Ptr, refCon As Ptr) As Ptr Return RegisterDeviceEventHandler(AddressOf MyDeviceEventFunction, refCon)

//-------------------------------------------------------------------------------
// Method: (TO BE TESTED WHEN CALLBACK WORKS!)

MyDeviceEventFunction(devHndl As Integer, devEvent As Integer, refCon As Ptr)

const eArrival = &h11 //< device plugged-in */
const eDeparture = &h12 //
< device unplugged */
const eButtonPressed = &h01 //**< button pressed */

// refCon optional?

Select Case devEvent
Case eButtonPressed
MsgBox “Button pressed!”

Case eArrival
MsgBox “Device attachedf”

Case eDeparture
MsgBox “Device removed”
End Select

//-------------------------------------------------------------------------------

Thank-you!

This should be on two lines as above, the second line is the actual call, removing that means the callback isn’t registered so nothing will happen.

The return will only be needed if you’re using it in a Method with a return value, so you could simply do the following if you didn’t want to go that route:

Soft Declare Function RegisterDeviceEventHandler Lib “@executable_path/…/…/devicedll” (callback As Ptr, refCon As Ptr) As Ptr
Dim p as Ptr
p = RegisterDeviceEventHandler(AddressOf MyDeviceEventFunction, refCon)
break // check here if p has been set so you know if the registration worked

refCon is optional and you can pass Nil if you want as it is a pointer so it would be

p = RegisterDeviceEventHandler(AddressOf MyDeviceEventFunction, Nil)

Remember that MyDeviceEventFunction must be a Public Shared Method or nothing will happen.

The pragma wont be needed if you don’t need it for other calls.

Thanks Julian! With ALL your answers it now works!

I did the registration as you suggested (and assigned Nil to refCon, the reference context, as well).

Interestingly, looking at the returned pointer “p”, the address is always zero the first time the registration is done, so I registered a second time just after and the pointer is then non-zero. According to the API manual, registering the device event handler returns “the previously installed event handler function or NULL if none has been installed yet”. The behavior I see does seem to makes sense.

Removed, and everything works fine!

Globally Public as a saw! First I corrected a mistake in my function call which should be:

MyDeviceEventFunction(ActualDevHndl As Integer, devEvent As Integer, refCon As Ptr)

where the device handle is the actual handle, a global variable, then I moved the “Public” Method from the window where it was to a module at the root of the app. Before the move, the callback function was called for the expected events but with all zero parameters and would crash except for a simple message box. After the move, the parameters are correct and the function can execute code associated to specific events.

Done, and thanks again!

1 Like