Tech Articles


PBNI Extension Framework


Addressing the complexities of PBNI

PowerBuilder Native Interface (PBNI) allows developers to extend the capabilities of PowerBuilder by implementing features in C++ or using third-party libraries. However, the traditional process for creating these extensions can be cumbersome.

For a simple sum function with 9 lines of code...

... you have to write 63 lines of code just to make it callable from PowerBuilder and for every change, you have to make PB-specific modifications in three files:

A more efficient approach

The PBNI framework introduces a more streamlined development experience. By leveraging the framework, developers can focus on the core logic of their extensions without being bogged down by the repetitive setup and boilerplate code inherent in traditional PBNI development. The framework handles much of the underlying plumbing, allowing for a cleaner and more maintainable codebase.

For example, where previously developers would need to manually implement the PBX_GetDescription, PBX_CreateNonVisualObject, and Invoke functions, the framework simplifies this with a class registration macro.

Without the framework:

// main.cpp
PBXEXPORT LPCTSTR PBXCALL PBX_GetDescription() {
   static const TCHAR desc[] = {
      "class arithmetic from nonvisualobject \n" \
      "function int f_add(int a,int b)\n" \
      "end class \n"
   };
   return desc;
}

PBXEXPORT PBXRESULT PBXCALL PBX_CreateNonVisualObject (
   IPB_Session* pbSession,
   pbobject        pbobj,
   LPCSTR          xtraName,
   IPBX_NonVisualObject   **obj ) {
   if (strcmp(xtraName, "arithmetic") == 0) {
         *obj = new arithmetic;
   }
   return 0;
};

With the framework:

// arithmetic.cpp
#include "arithmetic.h"

namespace Inf {
  INF_REGISTER_CLASS(arithmetic, L"u_pbni_arithmetic");
  
  INF_REGISTER_FUNC(f_add, L"of_add", L"ai_left", L"ai_right");
  PBInt arithmetic::f_add(PBInt arg0, PBInt arg1) {
    return arg0 + arg1;
  };
}

This approach significantly reduces the amount of code required and centralizes the registration of classes and functions, making the development process faster and less error-prone.

Quick start

This guide will help you set up your environment to build your first PBNI extension using the framework template.

Prerequisites

First, ensure you have the following tools installed:

  1. PowerBuilder 2022 R3 (also compatible with other versions) 
  2. Visual Studio (MSBuild): https://visualstudio.microsoft.com/
  3. CMake: https://cmake.org/
  4. Conan: https://conan.io/

Setup conan

  1. If PowerBuilder is not installed in the default path (C:/Program Files (x86)/Appeon), you must set an environment variable PB_DIRECTORY that points to the corresponding directory.
  2. Add the Informaticon remote repository by running this command in your terminal:
    conan remote add inf-conan https://artifactory.informaticon.com/artifactory/api/conan/conan
  3. Have Conan detect your default environment settings:
    conan profile detect
  4. Change the default Conan profile (usually found at %userprofile%/.conan2/profiles/default) or create a new one with the following settings. This profile tells Conan to build for a 32-bit architecture and specifies compiler settings.
    # %userprofile%/.conan2/profiles/default
    [settings]
    arch=x86 # change this to x86_64 for 64bit builds
    build_type=Debug # change this to MinSizeRel for release builds
    compiler=msvc
    compiler.cppstd=20
    compiler.runtime=static
    compiler.version=194 # Corresponds to MSVC v143 (VS 2022)
    os=Windows
    
    [options]
    *:pb_version=22.0 # Change this to 25.0 to use PowerBuilder 2025

Code the extension

With your environment configured, you can now build the extension.

    1. Clone the template repository.
    2. Modify the project for your needs / program your library:
      • Create a .cpp and an .h file in the src directory containing your C++ code.
        // arithmetic.h (example)
        namespace Inf {
            class arithmetic : public PBNI_Class {
            public:
                PBInt f_add(PBInt, PBInt);
            };
        }
        
        // arithmetic.cpp (example)
        #include "arithmetic.h"
        
        namespace Inf {
            INF_REGISTER_CLASS(arithmetic, L"u_pbni_arithmetic");
        
            INF_REGISTER_FUNC(f_add, L"of_add", L"ai_left", L"ai_right");
            PBInt arithmetic::f_add(PBInt arg0, PBInt arg1) {
                return arg0 + arg1;
            }
        }
        
      • In the CMakeLists.txt file, replace °°°PACKAGE_NAME°°° with your desired project name.
      • In the CMakeLists.txt file, replace °°°SOURCE_FILES°°° with the relative paths to your source files.
        add_library(${PROJECT_NAME} SHARED
        	src/arithmetic.cpp
        	src/arithmetic.h
        )

Build the extension

    1. Install the required dependencies using Conan. This may take some time on the first run as it downloads and builds the necessary libraries.
      conan install . --build=missing
    2. Generate the Visual Studio project files using CMake:
      cmake --preset conan-default
    3. Build the PBNI extension:
      conan build . --build=missing

Use the extension in PowerBuilder

  1. Add the Informaticon Exception Framework to you project:
    1. Download the latest release from Github (e.g. lib.pb.base.exception-framework@1.2.3+pb22-x86-minsizerel.zip for PB2022R3).
    2. Unzip exf1.dll and exf1.pbl into you PowerBuilder project folder.
    3. Add exf1.pbl to your library list.
    4. Integrate the exception framework in the application object:
      // Global variables
      u_exf_error_manager gu_e
      
      // open() event
      gu_e = create u_exf_error_manager
      
      // systemerror() event
      gu_e.of_display(gu_e.of_new_error() &
      	.of_set_nested_error(gu_e.of_get_last_error()) &
      	.of_push(1 /*populateerror()-Return*/) &
      	.of_set_message('systemerror occured') &
      	.of_push('Notice', 'Nested error may be unrelated to this system error.') &
      	.of_set_type('u_exf_re_systemerror'))
      halt
  2. Finally, import the freshly built PBNI dll file into PowerBuilder. You can find it in the build directory, for example: ./out/MinSizeRel/my_project.dll:

Further reading

To learn more, explore the framework and its usage with these resources:

Informaticon has already utilized this framework to develop a range of PowerBuilder extensions, including tools for picture manipulation, QR code generation, and an advanced email and HTTP client. While these specific extensions are not yet open source, the underlying PBNI Framework is available under MIT license for the community to use in their own projects.

Exception Handling

The framework relies heavily on exception handling, each function declares a throws clause.
This is to force us to perform appropriate error handling:

u_pbni_arithmetic lu_arithmetic; lu_arithmetic = create u_pbni_arithmetic
long ll_result

try
	ll_result = lu_arithmetic.of_add(1, 2)
catch (u_exf_ex lu_e)
	// handle the error, e.g.
	// - Rollback the current transaction
	// - Retry
	// - Re-throw the exception to the caller or declare the
	//   exception in your own throws statement
	// - Display it: gu_e.of_display(lu_e)
end try

If you are new to this topic, read this:

Why PBNI remains essential alongside .NET integration

While PowerBuilder's .NET integration is powerful, the PBNI framework offers a fundamentally different and more direct level of integration. Its key advantage is the ability to communicate directly with the PowerBuilder Virtual Machine (PBVM). This allows developers to create custom extensions that behave like native PowerBuilder components, providing a feature set for deep integration and fine-grained control that is distinct from the .NET import functionality.

Comments (0)
There are no comments posted here yet

Find Articles by Tag

InfoMaker Configuration Filter BLOB SqlModelMapper PBNI Excel PBVM API Migration DataType PFC Graph Installation Database Connection RibbonBar Builder Export JSON Mobile RichTextEdit Control Oracle Branch & Merge Visual Studio Authorization SQL Server RibbonBar TLS/SSL Authentication JSONGenerator PowerServer Web SVN Service SqlExecutor Platform Database Table Data Sort Import WebBrowser Database PDF Repository Web Service Proxy Import JSON Trial Script Database Table Schema Text Windows 10 Icons iOS DataWindow 64-bit SQL Resize DLL PowerServer Mobile ODBC UI Modernization OrcaScript ActiveX Icon DragDrop DataWindow JSON Deployment TreeView Performance UI Themes Database Painter TFS PDFlib External Functions SDK Application GhostScript Array Azure Automated Testing OLE PowerBuilder NativePDF Debug SnapObjects Outlook PowerBuilder (Appeon) Syntax CI/CD Database Profile Stored Procedure File Linux OS Debugger DevOps OAuth Android XML Windows OS .NET Assembly RESTClient IDE Database Object PowerScript (PS) Git UI Transaction TortoiseGit Class Testing COM PowerBuilder Compiler .NET Std Framework PBDOM CoderObject WinAPI JSONParser Design PostgreSQL ODBC driver MessageBox Event Handling Event Web API Open Source REST 32-bit .NET DataStore Model Event Handler Database Table Source Control OAuth 2.0 SOAP PostgreSQL Expression Jenkins Encryption Interface SnapDevelop Debugging Elevate Conference JSON Menu License Validation Variable Export Source Code Encoding Bug Charts Data Window Messagging CrypterObject Error HTTPClient C#