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

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