This section presents a simple example that illustrates how to call a function on a PowerBuilder custom class user object from a C++ application:
To keep the code for this example simple, create an application with one custom class user object that has one function. The function returns the product of two integers:
-
In PowerBuilder, create a new workspace.
-
Select the Application icon from the Target page of the New dialog box and name the application loadpbvm.
-
Select the Custom Class icon from the PB Object page of the New dialog box.
-
In the Function prototype window, create a function with this signature:
f_mult ( integer arg1, integer arg2 ) returns integer
-
Save the user object as nvo_mult and close the User Object painter.
To write the C++ code that invokes the f_mult function, you need to obtain its method ID. The method ID is used to initialize the PBCallInfo structure and to invoke the function. There are two IPB_Session functions that return a method ID: GetMethodID, which takes a signature, and FindMatchingFunction, which takes a comma-separated list of arguments. You use the same functions when you call PowerScript from the code in your extension; see Calling PowerScript from an extension.
If you want to use GetMethodID, you need a signature. This function is simple enough that you do not need a tool to obtain a signature -- the signature is the string III, which indicates that the function returns an integer and takes two integers as arguments.
For more complicated functions, you can get the signature from the System Tree or with the pbsig220 tool.
Getting a signature from the System Tree
To get the signature of f_mult in the System Tree, expand nvo_mult, right-click on the f_mult function, and select Properties from the pop-up menu. The signature displays in the Properties dialog box in the Signature text box:
Getting a signature using pbsig220
To get the signature of f_mult with pbsig220, type the following at a command prompt:
pbsig220 d:\pbls\loadpbvm.pbl
In the output of pbsig220, the comment on the last line contains the signature to be passed as the method ID argument to GetMethodID:
PB Object Name: loadpbvm PB Object Name: nvo_mult public function integer f_mult (integer arg1, integer arg2) /* III */
For more information about the pbsig220 tool and the format of method signatures, see pbsig220.
To create the C++ application, follow these steps:
In your C++ development tool, create a new console application project. The include directory for the PBNI SDK, typically PowerBuilder 2019\SDK\PBNI\include, must be in your include path. If you use any helper classes, the source file that contains them must be added to your project. For a list of files and helper classes, see the table in The PBNI SDK.
The code for the C++ application creates an IPB_VM object using the PB_GetVM function and loads the PowerBuilder VM:
#include "pbext.h" #include "stdio.h" typedef PBXEXPORT PBXRESULT (*P_PB_GetVM)(IPB_VM** vm); int main(int argc, char *argv[]) { IPB_Session* session; IPB_VM* pbvm = NULL; //Load the PowerBuilder VM module HINSTANCE hinst = LoadLibrary("pbvm.dll"); if ( hinst== NULL) return 0; fprintf(stderr, "Loaded PBVM successfully\n");
The next step is to call the PB_GetVM function to get a pointer to the IPB_VM interface:
P_PB_GetVM getvm = (P_PB_GetVM)GetProcAddress (hinst,"PB_GetVM"); if (getvm == NULL) return 0; getvm(&pbvm); if (pbvm == NULL) return 0;
Next create an IPB_Session object within IPB_VM, using the PowerBuilder application's name and library list as arguments:
// loadpbvm.pbl must contain an application object // named loadpbvm and it must be on the search path // for the executable file LPCTSTR LibList[] = {"loadpbvm.pbl"}; if ( pbvm->CreateSession("loadpbvm", LibList, 1, &session) != PBX_OK ) { fprintf(stderr, "Error in CreateSession\n"); return 1; } fprintf(stderr, "Created session successfully\n");
After the session has been created, the C++ application can create PowerBuilder objects and call PowerBuilder functions in that session.
You use the FindGroup function to locate the group that contains the user object you want to use. FindGroup takes the name of the object as its first argument, and an enumerated type as its second argument. You are looking for a user object, so the second argument is pbgroup_userobject.
You pass the group returned from FindGroup to the FindClass function to get a class that you can pass to the NewObject function:
// Create the PowerBuilder object contained // in loadpbvm.pbl. // First find the group that contains the // user object nvo_mult pbgroup group = session->FindGroup("nvo_mult", pbgroup_userobject); if (group == NULL) return 0; // Now find the class nvo_mult in the group pbclass cls = session->FindClass(group,"nvo_mult"); if (cls == NULL) return 0; // Create an instance of the PowerBuilder object pbobject pbobj = session->NewObject(cls);
Next, get the method ID for the function you want to call and initialize a PBCallInfo structure. You pass the signature obtained in Getting the signature of a function to the GetMethodID function:
// PBCallInfo contains arguments and return value PBCallInfo ci; // To call the class member function f_mult, // pass its signature as the last argument // to GetMethodID pbmethodID mid = session->GetMethodID(cls, "f_mult", PBRT_FUNCTION, "III"); // Initialize call info structure based on method ID session->InitCallInfo(cls, mid, &ci);
You could use FindMatchingFunction instead of GetMethodID to get the method ID. The call would look like this, because f_mult takes two integer arguments:
pbmethodID mid = session->FindMatchingFunction(cls, "f_mult", PBRT_FUNCTION, "int, int");
Before you call the function, you must supply the integers to be multiplied. For the sake of simplicity, the following code sets them directly in the PBCallInfo structure.
// Set IN arguments. The prototype of the function is // integer f_mult(integer arg1, integer arg2) ci.pArgs-> GetAt(0)->SetInt(123); ci.pArgs-> GetAt(1)->SetInt(45);
Finally call the function, wrapping it in a try-catch statement to handle any runtime errors:
// Call the function try { session->InvokeObjectFunction(pbobj, mid, &ci); // Was PB exception thrown? if (session->HasExceptionThrown()) { // Handle PB exception session->ClearException(); } } catch (...) { // Handle C++ exception } // Get the return value and print it to the console pbint ret = ci.returnValue->GetInt(); fprintf(stderr, "The product of 123 and 45 is %i\n", ret);
When you have finished with the PBCallInfo structure, call FreeCallInfo to release the memory allocated to it, then delete the structure, release the session, and free the library:
// Release Call Info session->FreeCallInfo(&ci); delete &ci; // Release session session->Release(); FreeLibrary(hinst); return 0; }
When you run the compiled executable file at the command prompt, if the PowerBuilder VM is loaded and the session is created successfully, the following output displays in the command window:
Loaded PBVM successfully Created session successfully The product of 123 and 45 is 5535