Exchanging Data with PowerBuilder

About this chapter

This chapter describes how PBNI extensions exchange data with PowerBuilder.

About exchanging data with PowerBuilder

You can use the IPB_Session interface or the IPB_Value and IPB_Arguments interfaces to exchange data between PowerBuilder and PBNI. The IPB_Session interface contains many virtual functions that enable the C++ code in an extension to interact with the PBVM. The IPB_Value and IPB_Arguments interfaces contain methods that you can use to pass values between PowerBuilder and extensions.

Passing values between extensions and the PBVM

PBNI uses two interfaces, IPB_Value and IPB_Arguments, to pass PowerBuilder values between the PBVM and extension PBXs. The PBNICallInfo structure holds the data.

PBCallInfo structure

The PBCallInfo structure is used to hold data and return type information for calls between extensions and the PBVM. It has three public members:

IPB_Arguments*   pArgs;
IPB_Value*           returnValue;
pbclass                   returnClass;

The following code initializes a PBCallInfo structure using the IPB_Session InitCallInfo method. After allocating a PBCallInfo structure called ci, the IPB_Session GetClass and GetMethodID methods are used to get the class and method ID of the current method. Then, these parameters are used to initialize the ci structure:

pbclass cls;
pbmethodID mid;
PBCallInfo* ci = new PBCallInfo;

cls = Session -> GetClass(myobj);
mid = Session -> GetMethodID(cls, "myfunc",
   PBRT_FUNCTION, "II");

Session -> InitCallInfo(cls, mid, ci);

When you have finished using a PBCallInfo structure, you must call FreeCallInfo to release the allocated memory:

Session -> FreeCallInfo(ci);
delete ci;

The IPB_Arguments and IPB_Value interfaces have methods that enable you to pass values between the PBVM and PowerBuilder extension modules using PBCallInfo to hold the data.

IPB_Arguments interface

The IPB_Arguments interface has two methods:

  • GetCount obtains the number of arguments in a method call.

  • GetAt obtains the value at a specific index of the pArgs member of the PBCallInfo structure. For each argument, GetAt returns a pointer to the IPB_Value interface.

The following code fragment uses GetCount and GetAt in a FOR loop to process different argument types. The ci-> pArgs -> GetCount()statement gets the number of arguments, and ci -> pArgs -> GetAt(i) gets the value at the index i. This value is a pointer to the IPB_Value interface on which IPB_Value methods, such as IsArray and GetArray, can be called (see IPB_Value interface):

int i;
for (i=0; i < ci-> pArgs -> GetCount();i++)
{
   pbuint ArgsType;
   if( ci -> pArgs -> GetAt(i) -> IsArray())
         pArguments[i].array_val = 
         ci -> pArgs -> GetAt(i) -> GetArray();
      continue;
   }

   if( ci -> pArgs -> GetAt(i) -> IsObject()) 
   {
      if (ci -> pArgs -> GetAt(i) -> IsNull()) 
         pArguments[i].obj_val=0;
      else
         pArguments[i].obj_val = 
            ci -> pArgs -> GetAt(i) -> GetObject();
      continue;
   }
   ...

IPB_Value interface

IPB_Value has three sets of methods: helper methods, set methods, and get methods.

Helper methods

The IPB_Value interface helper methods provide access to information about variables and arguments, including the value's class and type, whether it is an array or simple type, whether it is set by reference, and whether the null flag is set. There is also a method that sets the value to null:

virtual pbclass          GetClass() const = 0;
virtual pbint              GetType() const = 0;
virtual pbboolean     IsArray() const = 0;
virtual pbboolean     IsObject() const = 0;
virtual pbboolean     IsByRef() const = 0;
virtual pbboolean     IsNull() const = 0;
virtual PBXRESULT   SetToNull() = 0;

The example shown in the previous section, IPB_Arguments interface, shows how you can use three of these methods: IsArray, IsObject, and IsNull.

This example shows how you can use the SetToNull method to set the returnValue member of the PBCallInfo structure to null:

if ( ci->pArgs->GetAt(0)->IsNull() ||
   ci->pArgs->GetAt(1)->IsNull() )
   {
// if either of the passed arguments is null, 
// return the null value
ci->returnValue->SetToNull();

Set methods

The IPB_Value set methods set values in the PBCallInfo structure. There is a set method for each PowerBuilder datatype: SetInt, SetUint, SetLong, SetUlong, and so on. These methods automatically set the value represented by IPB_Value to not null. The syntax is:

virtual PBXRESULT Set<type>(<pbtype> arg);

For example, the SetLong method takes an argument of type pblong.

In this example, the method has two integer arguments, set to int_val1 and int_val2:

ci-> pArgs -> GetAt(0) -> SetInt(int_val1);
ci-> pArgs -> GetAt(1) -> SetInt(int_val2);

The IPB_Value set methods set the datatype of the value represented by IPB_Value to a specific type. If the original type of the value is any, you can set it to any other type. Then, because the value now has a specific type, setting it to another type later returns the error PBX_E_MISMATCHED_DATA_TYPE. If the argument is readonly, the error PBX_E_READONLY_ARGS is returned.

Get methods

The IPB_Value get methods obtain values from the PBCallInfo structure. There is a get method for each PowerBuilder datatype: GetInt, GetUint, GetLong, GetUlong, and so on.The syntax is:

virtual <pbtype> Get<type>( ); 

For example, the GetString method returns a value of type pbstring.

The following example uses the IPB_Value GetAt method to assign the value at the first index of the pArgs member of the PBCallInfo structure to a variable of type IPB_Value* called pArg. If pArg is not null, the GetLong method sets the variable longval to the value in pArg:

PBCallInfo   *ci
...
pblong longval = NULL;
IPB_Value* pArg = ci-> pArgs-> GetAt(0);
   if (!pArg->IsNull())
   longval = pArg -> GetLong();

If the value is null, or if you use a get method that is expected to return one datatype when the value is a different datatype (such as using GetLong when the datatype is pbarray), the result returned is undetermined.

The get methods can also be used with the returnValue member of PBCallInfo:

ret_val = ci.returnValue->GetInt();
return ret_val;

Using the IPB_Session interface

The IPB_Session interface is an abstract interface that enables the PBVM to interact with PowerBuilder extensions and with external applications. It defines hundreds of methods for accessing PowerScript variables, calling PowerScript methods, handling exceptions, and setting a marshaler to convert PowerBuilder data formats to the user's communication protocol.

The IPB_Session interface includes several categories of methods:

  • Class accessor methods are used to find PowerBuilder classes, call PowerBuilder methods and events, and get and set instance variables of PowerBuilder objects.

  • Exception-handling methods communicate with the PowerBuilder exception handling mechanism.

  • Array accessor methods create and access PowerBuilder bounded and unbounded arrays.

  • Result set accessor methods work with result sets in DataStores and DataWindow controls.

  • Typed data access methods create and access data of the PowerBuilder types string, double, decimal, blob, date, time, datetime, and so forth.

  • Proxy access methods provide an interface for the implementation of new protocols.

  • The Release method releases the IPB_Session object itself.

For a complete list of methods, see IPB_Session interface.

You use IPB_Session methods in conjunction with IPB_Value and IPB_Arguments methods.

The following code fragment shows the body of a method that tests whether a date passed to a PBNI function is handled correctly by a PowerBuilder function. It uses the IPB_Value SetToNull, SetDate, and IsNull methods to set and test the date values in the PBCallInfo structure, as well as the IPB_Session SplitDate, SetDate, and NewDate methods.

// boolean isNull[], pbobject myobj, 
// and pbdate* d_date arguments passed in
pbclass cls;
pbmethodID mid;
PBCallInfo* ci = new PBCallInfo;
pbdate ret_date;
pbint yy,mm,dd;

cls = Session-> GetClass(myobj);
mid = Session-> GetMethodID(cls,"uf_getdate_byref",
      PBRT_FUNCTION,"YR");
Session-> InitCallInfo(cls, mid, ci);

if (isNull[0]) 
   ci -> pArgs -> GetAt(0)->SetToNull();
else
         ci-> pArgs -> GetAt(0) ->SetDate(*d_date);

Session->InvokeObjectFunction(myobj, mid, ci);

Session->SplitDate(ci->pArgs->GetAt(0)->GetDate(),
   &yy,&mm,&dd); 
Session->SetDate(*d_date, yy, mm, dd); 

if (ci-> returnValue ->IsNull())
{
   ret_date = Session-> NewDate();
   Session-> SetDate(ret_val, 1900, 1, 1);
}
else
{
   ret_date = Session-> NewDate();
   Session -> SplitDate(ci-> returnValue -> GetDate(),
      &yy,&mm,&dd); 
   Session -> SetDate(ret_val,yy,mm,dd);
}
Session -> FreeCallInfo(ci);
delete ci;
return ret_date;

Saving data from IPB_Value to a local variable

To avoid memory leaks, you must call FreeCallInfo to free the values stored in the PBCallInfo structure after using the structure. However, after making a function call, you might want to save the return value or a by reference argument value into a local variable you can use later.

There are techniques for saving values so they are still available after the call to FreeCallInfo. How you save your result into a local variable depends on whether you want to save a simple value, a pointer value, or an object value.

Saving simple values

Saving simple values is straightforward. When you call one of the IPB_Value Get<type> methods for a simple value, such as GetInt or GetReal, the actual data is returned. As a result, you can simply save the values of any of the following datatypes:

pbvalue_byte

pbvalue_int

pbvalue_uint

pbvalue_long

pbvalue_ulong

pbvalue_real

pbvalue_double

pbvalue_longlong

pbvalue_boolean

pbvalue_char

Saving pointer values

A pointer value does not contain data. It contains a pointer to a memory location where the data is stored. When you call one of the IPB_Value Get<type> methods for a pointer value, such as GetBlob or GetTime, it returns a pointer to memory that is also pointed to by IPB_Value.

When you call FreeCallInfo, the memory to which IPB_Value points is released and the data is deleted. Because this is the same data pointed to by the pointer returned by the Get<type> method, that pointer can no longer be used to represent the data.

This applies to the following pointer value datatypes, as well as to the pbarray datatype:

pbvalue_dec

pbvalue_string

pbvalue_blob

pbvalue_date

pbvalue_time

pbvalue_datetime

If you want to save the data in a pointer value, you can use the AcquireValue, AcquireArrayItemValue, and ReleaseValue methods to acquire and release the data.These methods clone a new IPB_Value that is not freed until you call ReleaseValue and reset the existing IPB_Value pointer.

Can be used for other datatypes

You can use AcquireValue and AcquireArrayItemValue to acquire values of any datatype.

Like the Get<type> methods, AcquireValue and AcquireArrayItemValue return a pointer to the memory where the data is stored, but they also reset the IPB_Value pointer so that IPB_Value no longer points to the actual data. When you call FreeCallInfo, the data pointed to by the value acquired using AcquireValue and AcquireArrayItemValue is unaffected.

The original value is reset to zero or null, so it can no longer be used. Attempts to get or acquire the original value return zero or null until another IPB_Value is set to the value.

If the IPB_Value acquired using AcquireValue is an array, the entire array is acquired. To acquire only an element of the array, use AcquireArrayItemValue. When you have finished using the data, you must free the memory using the ReleaseValue method.

The processing that the AcquireArrayItemValue and ReleaseValue methods perform results in poor performance when handling large arrays. It is more efficient to get the type of the array and handle each type with appropriate type-specific functions.

Caution

You must call the ReleaseValue method to free the data. If you do not do so, a memory leak will occur. You must not call ReleaseValue to release a pointer that was not acquired using AcquireValue and AcquireArrayItemValue. Doing so might cause the PBVM to crash.

Saving object values

Strictly speaking, object values are also pointer values, but the PBVM handles them differently. You use the IPB_Session AddLocalRef and AddGlobalRef methods to add a reference to the object. If there is a reference to an object, it is not deleted when FreeCallInfo is called.

When you no longer need the object, call RemoveLocalRef or RemoveGlobalRef to decrease the reference count for the object. If the reference count is decreased to zero, the object is deleted automatically.

There is an important difference between AddLocalRef and AddGlobalRef. A reference added by AddLocalRef can be deleted automatically when the local frame is popped up. The local frame can be popped by calling PopLocalFrame or when the current function returns. However, a reference added by AddGlobalRef is deleted only when RemoveGlobalRef is called or the session ends.

You must use these methods in pairs; that is, use RemoveLocalRef to remove references created with AddLocalRef, and use RemoveGlobalRef to remove references created with AddGlobalRef.

Using variables throughout a session

The SetProp function enables you to use a variable value throughout an IPB session without using a global variable, which is susceptible to namespace conflicts with other sessions. SetProp is one of a set of three functions:

  • Use SetProp to register a new variable with the session or to change the value of an existing variable.

  • Use GetProp to access the variable.

  • Use RemoveProp to remove the variable from the list of variables associated with the session when it is no longer needed.

Suppose you want to throw an exception from within a PBNI extension and the exception itself is also defined by the PBNI extension. You call the IPB_Session NewObject function to create an instance of the exception, causing the PBX_CreateNonVisualObject function to be called.

One way to set the value of the fields of the exception before the function returns in a thread-safe manner is to create a new object or structure to hold the exception information before calling NewObject. You can call SetProp to store the structure or the object in the current IPB_Session. When PBX_CreateNonVisualObject is called, you can call GetProp to get the structure or object to obtain the exception information, then call RemoveProp to remove the data you stored in the current session.

You can also use these functions when initializing and uninitializing a session. If the extension exports the PBX_NOTIFY function, the PBVM calls PBX_Notify immediately after an extension PBX is loaded and just before the PBX is unloaded. You can use this function to initialize and uninitialize a session. For example, you could create a session manager object, and store it in the IPB session using the SetProp function. Later, you could use GetProp to obtain the session object.

Handling enumerated types

The GetEnumItemValue and GetEnumItemName functions allow you to convert the name of an enumerated value to an integer value, and to convert an integer value to the name of an enumerated value.

This example gets the numeric value for the boolean! enumerated value, then uses it to return the string value:

pblong lType = session->GetEnumItemValue("object",
   boolean" ); // returns 138
LPCTSTR szEnum = session->GetEnumItemName( "object", 
   lType ); // returns "boolean"

Notice that the second argument in the GetEnumItemValue call, the enumerated value, must not have an appended exclamation mark (!).

To return an enumerated value from an extension to PowerScript, use the SetLong function to set the value of the enumerated variable into IPB_Value (you cannot use SetInt or SetShort).

To obtain an enumerated variable's value, you can use GetInt or GetShort as well as GetLong, as long as the value is in the appropriate range. For example, if you attempt to use GetInt to obtain a value that is more than 32767, the returned value is truncated.