

Related Topics: PowerBuilder

PowerBuilder: Article

Introduction To PBNI

Introduction To PBNI

Have you ever thought of adding new features to PowerBuilder? Or wanted to call Java or .NET from PowerScript? Or imagined that you can call PowerScript from C++ or Java? With PBNI, you can do them all and more. PowerBuilder Native Interface (PBNI) is a standard interface for third parties to extend the functionality of PowerBuilder.

PBNI is a valuable new addition to the PowerBuilder programmer's toolbox that allows developers to reach beyond the already feature-rich constructs of PowerScript and custom class user objects (commonly referred to as nonvisual objects or NVOs). The following are a few of the things you can do with PBNI.

Creating PowerBuilder Extensions
Suppose you want to use a COM component in PowerBuilder. Unfortunately, one of the COM functions takes a pointer to a user-defined COM interface as a parameter, so you can't map the interface into any of PowerBuilder's datatypes. What should you do? With PowerBuilder 9, you can create a PowerBuilder extension using PBNI.

Now you want to create a SAX XML parser to complement the DOM parser provided in PowerBuilder 9. It's not trivial to implement the parser in PowerScript and you're concerned about the performance. You can write a PowerBuilder extension to do the job.

What if you want to invoke part of the Adaptive Server Anywhere API to provide a PowerBuilder interface for database backups and maintenance? Some of that functionality requires callback functions, which we all know aren't supported in PowerScript. The solution? Write a PowerBuilder extension using PBNI.

A PowerBuilder extension is a DLL written in C++ that exposes one or more native classes to be used in PowerScript as NVOs. A native class is a PowerScript class implemented in C++. The DLL is loaded by the PowerBuilder Virtual Machine (PBVM) dynamically at runtime when the native classes are used for the first time by a PowerBuilder application. When the functions of the native classes are called in PowerScript, the PBVM automatically delegates the function calls to the corresponding C++ classes.

To develop a PowerBuilder extension, first determine what native classes the extension will support. Next, for each native class, declare and implement a C++ class that inherits from IPBX_NonVisualObject and implement the standard functions required by PBNI using the ANSI C++ tool of your choice. Once the DLL has been built, generate a PBD file for the extension with the pbx2pbd tool shipped with PowerBuilder 9. With the DLL and PBD file in hand, the extension is ready to use. Simply add the PBD file to the library list of your PowerScript target and put the DLL in a directory on the PATH, and you can use the native classes as easily as the NVOs you're accustomed to building and using in PowerBuilder.

Each extension can include one or more native classes. When the pbx2pbd tool generates a PBD file for an extension, it calls the PBX_GetDescription() function to get a description of all the native classes supported by the DLL. Each class description basically follows PowerScript syntax:

static const TCHAR desc[] =
"class xmlevent from nonvisualobject\n"
"event boolean startdocument(string docname)\n"
"event boolean enddocument(string docname)\n"
"event boolean startelement(string elementName)\n"
"event boolean endelement(string elementName)\n"
"end class\n"

"class xmlparser from nonvisualobject\n"
"function boolean parse(xmlevent handler, string xmlFileName)\n"
"end class\n"
This extension supplies two native classes, xmlevent and xmlparser, both inheriting from the NonVisualObject PowerBuilder system class. xmlevent defines four events and xmlparser defines one function.

In PowerBuilder, create a new NVO, e.g., myXmlEventHandler, that inherits from xmlevent and implements the four events. Then you can use the extension as follows:

myXmlEventHandler eventHandler
XmlParser parser

eventHandler = create myXmlEventHandler
parser = create XmlParser
parser.parse(eventHandler, "xyz.xml")
Here's what happens behind the scenes when the script is executed. To create XmlParser, the PBVM calls the PBX_CreateNonVisualObject() function exposed by the extension. This function creates the corresponding C++ object and returns it to the PBVM. When the parse() function of XmlParser is called, the PBVM calls the Invoke() function on the corresponding C++ object, then the function can parse the XML file and trigger events accordingly. This way, the event handlers of myXmlEventHandler are called to do whatever you want.

This means you can actually call a PowerScript function and trigger a PowerScript event from a PowerBuilder extension, because the PBVM and PowerBuilder extensions work together seamlessly. If you're familiar with the Java Native Interface (JNI), you'll see there are several similarities between JNI and PBNI.

To recap, you can write PowerBuilder extensions in C++ to provide one or more native classes that you can use in PowerScript as NVOs. PowerBuilder extensions can create PowerBuilder objects, call PowerScript functions, and trigger PowerScript events, making these constructs easily available from other environments such as Java.

Creating PowerBuilder Marshaler Extensions
PBNI also enables you to write marshaler extensions. Marshaler extensions can act as bridges between PowerBuilder and other components, such as CORBA components, Java classes, and Web services, as long as those components can be called from C++.

Let's say you want to call Java classes from PowerScript. Without PBNI, it's almost impossible to do that, but there is a way with PBNI.

Developing a Tool for Generating PowerBuilder Proxies for Java Classes
First develop PowerBuilder proxies for the Java classes you wish to invoke from PowerBuilder. You might do this using Java reflection, from Java source code directly, or using the javap tool.

For this Java class:

public class Converter
public double dollarToYen(double dollar);
public double yenToEuro(double yen);
the PowerBuilder proxy would be something like this:

$PBExportComments$Proxy for Java class.
global type Converter from NonVisualObject
end type
global Converter Converter

forward prototypes
function dollarToYen(double ad_1) alias for "dollarToYen,(D)D"
function yenToEuro(double ad_1) alias for "yenToEuro,(D)D"
Notice that both PowerBuilder proxy functions have an alias that contains the Java function name and function signature. This is necessary since Java is case sensitive, but PowerBuilder is not. The alias information is used by the extension to find the corresponding Java functions.

Developing a PowerBuilder Extension
Develop a PowerBuilder extension that provides a native class, called JavaProxy. The JavaProxy class has a function that looks something like this:

function long createJavaObject(ref PowerObject javaObject,string className)

You need to write another C++ class, e.g., JavaMarshaler, that implements the IPBX_Marshaler interface. This interface is responsible for marshaling requests to a Java object and unmarshaling the result returned from the Java object.

Once the code has been written, build the extension (containing the JavaProxy and the JavaMarshaler classes) and put it in a directory on the PATH. Then generate a PBD file for the extension with the pbx2pbd tool, add the PBD file to the library list of your PowerScript target, and import the proxy for the Converter Java class into your PowerScript target. Now you can call the Converter class:

JavaProxy l_jp
Converter l_conv
double l_yen
l_jp = create JavaProxy
l_jp.createJavaObject(l_conv, "com.sybase.pbni.Converter")
l_yen = l_conv.dollarToYen(100.0)
When the createJavaObject() function of JavaProxy is called in PowerScript, the PBVM calls the corresponding C++ function in the extension. The C++ function creates a Java Converter object through JNI. If successful, the function creates an instance of the PowerBuilder Converter proxy and a JavaMarshaler object, and associates the JavaMarshaler object with the PowerBuilder proxy. Later, when conv.dollarToYen(100.0) is reached, the PBVM calls the InvokeRemoteMethod() function on the JavaMarshaler object. This function then delegates the call to the Java Converter object though JNI and returns the result to PowerBuilder. Now you can call Java from PowerBuilder.

Invoking PowerScript from Other Languages
Let's say you're writing a C++ (or even a Java) application and some part of it includes some intensive database operations. You already have many PowerBuilder NVOs doing the same thing. Can you reuse those PowerBuilder NVOs in the C++ application? Yes, using PBNI.

In the C++ application, load the PBVM DLL and call the PB_GetVM() function to get a pointer to the IPB_VM interface. From there call the CreateSession() function of the IPB_VM interface to create a session and get a pointer to the IPB_Session interface. Next create the NVO you want to use and invoke its functions through the IPB_Session interface.

C++ code in Listing 1 shows how to call the function int foo(string) of mynvo, which lives in mypbl.pbl (error-handling code has been removed for the sake of simplicity).

The last parameter to the GetMethodID() function is the signature of the PowerScript function. PBNI provides a utility, pbsig, to help you get the signatures of PowerBuilder functions.

PowerBuilder is wide open to the outside world. With PBNI, anything that can be called from C++ can be called from PowerScript; anything that can call C++ can also call PowerScript. The flexibility and interoperability provided by this technology is being put to use in the PowerBuilder product - features such as the PowerBuilder Document Object Model (PBDOM) and SOAP support are being implemented using PBNI. PBNI's potential is only limited by your imagination.

More Stories By Jim O'Neil


Comments (1)

Share your thoughts on this story.

Add your comment
