PowerBuilder does not support PowerScript code to be directly called from external functions.
As of PB9 there is a possibility to write a C++ extension for that task using PBNI.
However, implementing callbacks using PBNI has the following limitations:
PBNI requires knowledge of C++ from PowerBuilder developer
PBNI extensions are quite hard to write and debug.
PBNI requires separate DLLs for ANSI and Unicode versions.
PBNI is available only as of PB9.
Also, there are additional limitations not related to PBNI.
It's hard to implement (even in C++) passing of context data to the callback if the caller does not support that.
A charset conversion is needed when the caller is ANSI and PB is Unicode and vice versa.
PBInvoke has support for declaring and using callbacks directly in PowerScript code
without these limitations.
Callback declaration
In order to declare a callback you need the following steps:
Inherit a custom class from n_pi_method
In the constructor event, call of_declare_callback() with a callback type declaration
or a type name (in case the type was declared earlier).
Extend the ue_callback event
Example:
// in n_cst_timerproc's constructor (inherited from n_pi_method)
n_pi_core lnv_core
lnv_core.of_declare("&
typedef VOID CALLBACK TimerProc(&
HWND hwnd, &
UINT uMsg, &
UINT_PTR idEvent, &
DWORD dwTime &
);&
")
of_declare_callback("TimerProc")
Getting callback address
Use n_pi_method.of_get_addr() to get the numeric value of the callback address ready to be passed to
an external function which accepts function pointers of this type.
Example:
n_cst_timerproc lnv_timerproc
lnv_timerproc = Create n_cst_timerproc
// ef_SetTimer is an external function, alias for WinAPI SetTimer
ll_timer = ef_SetTimer(0, 0, 1000, lnv_timerproc.of_get_addr())
Accessing callback parameters and returning result
In the ue_callback event, callback parameters can be retrieved by
calling of_get_param("paramname") .
The return value is passed to the caller of the callback using usual RETURN operator.
Note, that PBInvoke does not support the following types as return types of callbacks:
float(real), double
longlong
structs/unions of size of more than 4 bytes
However, pointers or references to those type are supported.
Example:
ulong hwnd
ulong uMsg
ulong idEvent
ulong dwTime
// retrieve parameters
hwnd = of_get_param("hwnd")
uMsg = of_get_param("uMsg")
idEvent = of_get_param("idEvent")
dwTime = of_get_param("dwTime")
// process event
...
Return 0
Callback context data
All context data needed by a callback can be stored in the instance variables of the callback object
(which is a descendant of n_pi_method).
That is possible because PBInvoke generates a unique phisical address (function pointer) for
each callback object, and when an external function calls your callback via that address, PBInvoke maps that call
exactly to your object.
Performance of callbacks
Marshalling parameters between C++ and PB is a slow operation (compared to PB external functions calls) due to
the flexibility PBInvoke provides. If a PB callback is used as a handler for intercepting GUI messages
(e.g. via calling SetWindowsHookEx ), then it may cause slow repaint of UI, or even a hang for a long time,
because UI generates huge amount of messages, most of which are ignored by the callback but take time for marshalling.
PBInvoke provides an optimization for that case. For callbacks used with SetWindowsHookEx WinAPI
function, it's possible to setup a filter for message ID's which are processed by the callback.
If a message ID does not pass the filter, the PowerScript code of the callback will not be called and all the processing
will be done in C++ according to the rules described in MSDN for SetWindowsHookEx hooks: call
CallNextHookEx .
In order to activate the message filter you must call of_set_windows_hook_filter() immediately
after calling SetWindowsHookEx() .
Syntax:
callback.of_set_windows_hook_filter(hook_handle, hook_type, message_id_array[])
where
callback - callback object
hook_handle - hook handle returned by SetWindowsHookEx()
hook_type - one of supported by PBInvoke hook types (WH_CALLWNDPROCRET, WH_CALLWNDPROC, WH_GETMESSAGE)
message_id_array[] - array of long integers filled with accepted message ID's
Example:
// This code is part of CustomSaveAs sample, which demonstrates
// using callbacks for customizing Windows Shell 'Save As' dialog.
Long ll_hInstance, ll_hThreadID
ll_hInstance = inv_GetWindowLong.of_invoke(al_windowhandle, n_pi_winapi.GWL_HINSTANCE)
ll_hThreadID = inv_GetCurrentThreadId.of_invoke()
il_hHook = inv_SetWindowsHookEx.of_invoke(n_pi_winapi.WH_CALLWNDPROCRET, this.of_get_addr(), ll_hInstance, ll_hThreadID)
// IMPORTANT! As number of messages sent to UI related windows hooks is really huge,
// processing them all in PowerScript is inefficient, and probably will hang the UI.
// PBInvoke can efficiently filter hook messages by message id.
// The following call enables this feature.
// of_set_windows_hook_filter must be called immediately after SetWindowsHookEx()
// The args: hook handle, hook type, and array of message ids which pass via filter to the hook.
of_set_windows_hook_filter(il_hHook, n_pi_winapi.WH_CALLWNDPROCRET, &
{n_pi_winapi.CB_ADDSTRING, n_pi_winapi.CB_SETCURSEL} )
// Currently only WH_CALLWNDPROCRET, WH_CALLWNDPROC, WH_GETMESSAGE hook types are
// supported with this filtering feature
See also
Calling DLL functions using PBInvoke
|