Nonvisual extension example

To illustrate the principles involved in building and using an extension, this chapter starts with a sample application that uses a PowerBuilder extension to perform a simple arithmetic operation. Ordinarily, this is not a task that needs PBNI, but it is used here to make the basic process clear. The rest of this chapter describes building extensions in more detail.

PBX file suffix

PowerBuilder extensions are DLL files but typically use the file extension .pbx instead of .dll. Your extension is compiled into a PBX file by default if you use the wizard described in Using the Visual Studio Wizards 

For more realistic examples, see the PowerBuilder Code Samples Web site at https://www.appeon.com/developers/library/code-samples-for-pb.

The following sample application has two main steps:

Building the pbadd PowerBuilder extension

In this example, the C++ code is in three files:

  • The class declaration is in a header file, pbadd.h

  • The standard functions that every PowerBuilder extension must expose are in main.cpp

  • The implementation of the class is in pbadd.cpp.

To implement the pbadd extension:

  1. Create the pbadd.h header file.

    The pbadd.h header file declares the pbadd class. The file includes pbext.h, which must be included in all PowerBuilder extensions because it declares the ancestor classes for native classes and the standard functions that the extension must expose. Here is the code for pbadd.h:

    #include "pbext.h"
    class pbadd: public IPBX_NonVisualObject
    {
    public:
       pbadd();
       virtual ~pbadd();
       PBXRESULT Invoke(
          IPB_Session   *session,
          pbobject      obj,
          pbmethodID    mid,
          PBCallInfo    *ci);
    
       int f_add(IPB_Session*, pbint, pbint);
    // Enum used to provide entry points for each
    // method in the class - the only one in this case
    // is mAdd   enum MethodIDs
       {
          mAdd = 0 
       };
    
    private:
       virtual void Destroy();
    };
  2. Create the main.cpp file, which includes pbadd.h and implements the standard functions, PBX_GetDescription and PBX_CreateNonvisualObject:

    • PBX_GetDescription is used to pass the descriptions of classes in the extension to PowerBuilder.

    • The PBX_CreateNonVisualObject method creates the object instance. The PowerScript CREATE statement maps to this PBNI method.

    The following is the code for main.cpp:

    #include "pbadd.h"
    // initialize the PBX
    BOOL APIENTRY DllMain(HANDLE hModule,
                    DWORD ul_reason_for_all,
                     LPVOID lpReserved
                  )
    {
       switch(ul_reason_for_all)
       {
          case DLL_PROCESS_ATTACH:
          case DLL_THREAD_ATTACH:
          case DLL_THREAD_DETACH:
          case DLL_PROCESS_DETACH:
             break;
       }
       return TRUE;
    }
    
    
    // describe the pbadd class
    PBXEXPORT LPCTSTR PBXCALL PBX_GetDescription()
    {
       static const TCHAR desc[]={
          "class pbadd from nonvisualobject \n" \
          "function int f_add(int a,int b)\n" \
          "end class \n"
       };
    return desc;
    }
    
    
    // export the required PBX_CreateNonVisualObject
    // function so that the PBVM can
    // create an instance of the class
    PBXEXPORT PBXRESULT PBXCALL PBX_CreateNonVisualObject
    (
       IPB_Session*    pbSession,
       pbobject        pbobj,
       LPCSTR          xtraName,
       IPBX_NonVisualObject   **obj
    )
    {
       // if the calling function requests the pbadd
       // class, create an instance
       if (strcmp(xtraName,"pbadd")==0)
          {
             *obj=new pbadd;
       }
       return 0;
    };
  3. Create the pbadd.cpp file, which includes pbadd.h and contains the implementation of the pbadd class and its single method, f_add.

    #include "pbadd.h"
    
    // Implement the required Invoke method
    PBXRESULT pbadd:: Invoke(IPB_Session *Session,
       pbobject obj, pbmethodID mid, PBCallInfo *ci)
    {
       // if the method to call is f_add
       if (mid == mAdd)
       {
          int sum = f_add(Session, ci->pArgs->GetAt(0)->
             GetInt(), ci->pArgs->GetAt(1)->GetInt());
          ci->returnValue->SetInt(sum);
       }
       return PBX_OK;
    }
    
    // constructor and destructor
    pbadd:: pbadd()
    {
    }
    pbadd:: ~pbadd()
    {
    }
    
    // implement the class's f_add method
    int pbadd:: f_add(IPB_Session* session, pbint arg1,
       pbint arg2)
    {
       return arg1+arg2;
    }
    
    // Implement the required Destroy method
    void pbadd::Destroy()
    {
       delete this;
    }

To compile and link the PBX:

  • In your C++ development tool or on the command line, compile and link the PBX.

    Make sure the include directory in PowerBuilder 19.0\SDK\PBNI is in your include path. For this example, the generated DLL is called pbadd.pbx.

Using the extension in PowerBuilder

To use the PowerBuilder native class in a PowerBuilder application, import the object descriptions in the PBX file into a library in your application.

To import the extension into an application:

  1. Copy the PBX (or DLL) file to a directory on your application's path.

  2. In PowerBuilder, create a new workspace.

  3. On the Target page of the New dialog box, select the Application icon to create a new target, library, and application object.

  4. In the System Tree, expand the new target, right-click the library, and select Import PB Extension from the pop-up menu.

  5. Navigate to the location of the pbadd.pbx file and click Open.

To invoke the f_add function in PowerBuilder:

  1. Create a new window called w_add, and add three single-line edit boxes and a command button to it.

  2. Declare an instance variable called mypbadd for the pbadd native class, and then add this script to the button's Clicked event:

    TRY
       mypbadd = CREATE pbadd
       sle_3.text = string (mypbadd.f_add( &
          integer(sle_1.text), integer(sle_2.text)))
    CATCH (runtimeerror re)
       MessageBox("Error", &
          "pbadd native class could not be created: " + &
          re.getmessage() )
    END TRY

    The pbadd class displays in the System Tree. As shown in the following screen shot, you can expand its function list:

  3. Add open(w_add)to the application's Open event.

  4. Run the application.

The application runs just as it would if you had created a custom class user object in PowerBuilder with an f_add function. If PowerBuilder cannot find pbadd.pbx, the runtime error in the Clicked event script will be triggered and caught. Put pbadd.pbx in the same directory as the executable or the PowerBuilder runtime DLLs to make sure it can be found.