How to callback INTO Xojo app from dylib/DLL

OK, this may not be possible, but I have to ask:

If I have a dylib (MacOS) or DLL (Windows), could the code in that library call BACK INTO the Xojo app that invoked the call into the dylib in the first place?

I recognize the hazards of doing so – especially a circular call situation.

SCENARIO:

STEP 1) From inside the MAIN XOJO APP, a button is pressed, calling an external C-API method dylib_foo() in a dylib.

STEP 2) dylib_foo() executes, but before it completes, it calls main_bar(), a method in the MAIN XOJO APP.

STEP 3) main_bar() completes, returns control to dylib_foo().

STEP 4) dylib_foo() completes, returns control to the MAIN XOJO APP that originally invoked the external method.

I would need to provide dylib_foo() with the address of main_bar(), perhaps once when the program launches?

QUESTIONS:

Q1) Can I provide an address of a Xojo function to the dylib?

Q2) How does C-compiled code in the dylib make the call into XOJO, provide parameters, and receive return value?

Q3) If such a call can be made, are there any limitations? Can I call XOJO functions that draw to the screen, for example?

I saw some forum discussion from four years ago, where this sort of thing SHOULD work, but it seemed iffy back then.

[quote=377900:@Stephen Greenfield]

Q1) Can I provide an address of a Xojo function to the dylib?[/quote]

Use the AddressOf operator to get a Ptr to the callback, and then pass the Ptr to the dylib.

Declare Sub dylib_foo Lib "MyLib" (Callback As Ptr) dylib_foo(AddressOf main_bar)

Execution then proceeds exactly as in your scenario.

The Xojo compiler and the dylib have to agree about the calling convention. Xojo uses the CDecl convention by default, and can use the StdCall convention if you use a pragma directive.

Only primitive types (Ptrs, Ints, etc.) can be used as parameters and return values, just like with declares.

There really aren’t any limitations to what a callback can do. There is a limitation on what kinds of methods can be callbacks, however. Callback methods can’t be ordinary methods of a class. Only module methods and shared class methods may be used.

If you call the declare on a Xojo thread then the callback will also run on that thread. The standard rules and caveats about threads apply.

Also, callbacks that accept a varying number of parameters can be difficult if not impossible to implement in Xojo. Fortunately that doesn’t seem to be very common.

@Andrew Lambert – wow – thanks for the detailed response.

[quote]There is a limitation on what kinds of methods can be callbacks, however. Callback methods can’t be ordinary methods of a class. Only module methods and shared class methods may be used.
[/quote]

Is this because Class methods might have their AddressOf value change? I thought I read (in another post) that as long as the class remained allocated, the AddressOf that class method would be unchanging?

Thanks for thinking of possible variadic parameters, but as you guessed, I wouldn’t attempt that.

So if I want to make sure I can call a method that is part of the the thread that draws text – how can I make sure a particular module or shared class is part of that thread? If it’s not explicitly running in a thread I create and start, then it’s going to be in the main thread which is also the thread which CAN manipulate UI elements? (or I saw that the “Task” class has an UpdateUI event handler).

Anything called by the thread runs in that thread. The Task class uses a timer to get the event onto the main thread.

Class methods are virtual, they are looked up at call time. Your DLL wouldn’t be able to do that.

@Andrew Lambert, @Tim Hare –

OK, wow – this worked:

In my C++ DYLIB (Framework) compiled Xcode 7.2.1, GNU++11, libc++:

void TestCallBack(void (*PMyXojoCallbackAddress)(const char* aCString)) { const char* myCString = "callback test string: ?????"; (*PMyXojoCallbackAddress)(myCString); }

In .h file:

extern "C" void TestCallBack(void (*PMyXojoCallbackAddress)(const char* myCString));

In XOJO Window1:, I define this shared public method:

[code]CallBackAndSetText(inUTF8Text as CString)

 dim theText as Text = Text.FromCString( inUTF8Text, Xojo.Core.TextEncoding.UTF8 )
 Window1.TextAreaToSetInCallback.SetString(theText)

[/code]

In XOJO Test_Callback_PushButton Action:

TestCallBack(AddressOf CallBackAndSetText)

Execution:

  • Test_Callback_PushButton is pushed in Window1

  • Global Extern method TestCallBack() is called with parameter of AddressOf CallBackAndSetText

  • Framework C API function TestCallBack() executes, gets a unicode string, then calls the passed Xojo public method pointer CallBackAndSetText() with that string as the param

  • Xojo shared public method CallBackAndSetText() executes, converts unicode CString param to Text using Text.FromCString()

  • Window1.TextAreaToSetInCallback is set to the converted-to-Text value.

  • Xojo method CallBackAndSetText() returns to Framework C API function TestCallBack()

  • Framework C API function TestCallBack() returns to Xojo Window1.Test_Callback_PushButton Action()

It worked and all SEEMS well.

I did not use the _cdecl decoration in the C++ / C API, because I believe that’s the default.

Did that all make sense?

Yup. :slight_smile: