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

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