Callbacks

 

 

Search
 
While it is possible to call a C/C++ DLL from Smalltalk MT, sometimes we would like to be able to callback Smalltalk MT from a C/C++ DLL or EXE.

This enables us to share code in both environment.

A Smalltalk MT application can be called back on the class side or the instance side.

Let's deal with the simplest case first which is a callback on the class side.

Class side callbacks

In C/C++, we create a standard function pointer

typedef BOOL (WINAPI* STMTFUNC)(DWORD);

This function prototype says that the STMT function will return a boolean value and will be passed a single parameter.

We need to pass the address of the STMT method to the C/C++ program and so let's create a C/C++ method to do that. In this case I am going to assume that the C/C++ program is a DLL so here is the API.

INT WINAPI Setup(STMTFUNC renderFunction)
{
        ...code...
        if(!renderFunction(someParameter)) OutputDebugString("Callback failed");
        ...code...
}

In the Smalltalk code, we create a class method in the category .EXPORT (this is important since it specified the calling convention for STMT. .EXPORT uses the WINAPI calling convention)

CallbackFunction: aParameter
        "Answer a boolean representing the function success or failure"
        aParameter < 5 ifTrue: [^FALSE]. "It failed"
        ^TRUE

Notice that we must return C booleans TRUE (with a value of 1) or FALSE (with a value of zero) since these will be returned directly to the C/C++ code.

The last piece we need is how to pass the function pointer to the C/C++ program.

The STMT method would look like

"Pass the function pointer to the C/C++ program"
WINAPI Setup: (MyClass methodAddressAt: #CallbackFunction:)

The methodAddressAt returns the address of the class method CallbackFunction: and passes this to the C/C++ function Setup.

So now we execute this and the C/C++ program will call us back via the class method.

Class side callback that forwards to the instance side

Quite often, it is inconvenient to have to use a class method. There is a common technique to enable you to move from the class side to the instance side.

typedef BOOL (WINAPI* STMTFUNC2)(DWORD, DWORD);

This time the callback function will have two parameters and the first will be the Smalltalk instance address.

INT WINAPI Setup(DWORD instance, STMTFUNC renderFunction)
{
        ...code...
        if(!renderFunction(instance, someParameter)) OutputDebugString("Callback failed");
        ...code...
}

This time we are passed the Smalltalk instance address and we pass this on as a parameter to the callback function.

CallbackFunction: instance with: aParameter
        "Answer a boolean representing the function success or failure"
        ^instance _asObject someMethod: aParameter

The callback function turns the instance address back into an object (_asObject) and sends this Smalltalk instance the method someMethod with the parameter.

We can now setup the callback from a Smalltalk instance and pass the calling objects address as the first parameter.

"Pass the function pointer to the C/C++ program"
WINAPI Setup: self basicAddress with: (MyClass methodAddressAt: #CallbackFunction:)

Instance side callback

It is also possible to callback on the instance side directly. This is how COM works. The instance side callback follow the Microsoft C++ calling convention. This convention says that the first parameter must always be the receiving objects address.

This time we would place the callback function in the .EXPORT category on the instance side and use the same C/C++ sample that passes the instance as the first parameter.