Working with callbacks

[ Home / PBInvoke library ]


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
  • PBInvoke
    Home
    Download Demo
    Documentation
    Purchase

    See Also
    Calling DLL functions using PBInvoke


    Copyright (C) 1999 - 2017 Anatoly Moskovsky Report a site problem