Declaring external functions

Description

External functions are functions written in languages other than PowerScript and stored in dynamic link libraries. On Windows, dynamic libraries have the extension DLL. If you deploy a component written in PowerBuilder to a UNIX server, the dynamic libraries it calls have the extension .so, .sl, or .a, depending on the UNIX operating system. You can use external functions that are written in any language that supports dynamic libraries.

Before you can use an external function in a script, you must declare it as one of two types:

  • Global external functions

    These are available anywhere in the application.

  • Local external functions

    These are defined for a particular type of window, menu, user object, or user-defined function. These functions are part of the object's definition and can always be used in scripts for the object itself. You can also choose to make these functions accessible to other scripts.

To understand how to declare and call an external function, see the documentation from the developer of the external function library.

Syntax

External function syntax

Use the following syntax to declare an external function:

{ access } FUNCTION returndatatype name ( { { REF } datatype1 arg1,
   ..., { REF } datatypen argn } ) LIBRARY "libname"
   ALIAS FOR "extname{;ansi}"

External subroutine syntax

To declare external subroutines (which are the same as external functions except that they do not return a value), use this syntax:

{ access } SUBROUTINE name ( { { REF } datatype1 arg1, ...,
   { REF } datatypen argn } ) LIBRARY "libname"
   ALIAS FOR "extname{;ansi}"

The following table describes the parameters used to declare external functions and subroutines:

Parameter

Description

access (optional)

(Local external functions only) Public, Protected, or Private specifies the access level of a local external function. The default is Public.

For more information, see the section about specifying access of local functions in Usage.

FUNCTION or SUBROUTINE

A keyword specifying the type of call, which determines the way return values are handled. If there is a return value, declare it as a FUNCTION; if it returns nothing or returns VOID, specify SUBROUTINE.

returndatatype

The datatype of the value returned by the function.

name

The name of a function or subroutine that resides in a DLL. Function names cannot contain special characters, such as the @ character, because they cause a compiler error. Use the ALIAS FOR clause described later in this table if the function name in the DLL contains special characters.

REF

A keyword that specifies that you are passing by reference the argument that follows REF. The function can store a value in arg that will be accessible to the rest of the PowerBuilder script.

datatype arg

The datatype and name of the arguments for the function or subroutine. The list must match the definition of the function in the DLL. Each datatype arg pair can be preceded by REF.

For more information on passing arguments, see the section called “Passing arguments” in Application Techniques.

LIBRARY "libname"

A keyword followed by a string containing the name of the dynamic library in which the function or subroutine is stored. libname is a dynamic link library, which is a file that usually has the extension DLL on Windows.

ALIAS FOR "extname" (optional)

Keywords followed by a string giving the name of the function as defined in the dynamic library. If the name in the dynamic library is not the name you want to use in your script, or if the name in the database is not a legal PowerScript name, you must specify ALIAS FOR "extname" to establish the association between the PowerScript name and the external name.

;ansi

Required if the function passes a string as an argument or returns a string that uses ANSI encoding. Even if you use the default name for an ANSI function, you must always use the ALIAS keyword if you want to specify that the string uses ANSI encoding, because you must qualify the ALIAS with the ansi keyword.


Usage

Specifying access of local functions

When declaring a local external function, you can specify its access level -- which scripts have access to the function.

The following table describes where local external functions can be used when they are declared with a given access level:

Access level

Where you can use the local external function

Public

Any script in the application.

Private

Scripts for events in the object for which the function is declared. You cannot use the function in descendants of the object.

Protected

Scripts for the object for which the function is declared and its descendants.


Use of the access keyword with local external functions works the same as the access-right keywords for instance variables.

Availability of the dynamic library at runtime

To be available to a PowerBuilder application running on any Windows platform, the DLL must be in one of the following directories:

  • The current directory

  • The Windows directory

  • The Windows System subdirectory

  • Directories on the DOS path

Examples

In the examples application that comes with PowerBuilder, external functions are declared as local external functions in a user object called u_external_function_win32. The scripts that call the functions are user object functions, but because they are part of the same user object, you do not need to use object notation to call them.

Example 1

These declarations allow PowerBuilder to call the functions required for playing a sound in the WINMM.DLL:

//playsoundFUNCTION boolean sndPlaySoundA (string SoundName, 
   uint Flags) LIBRARY "WINMM.DLL" ALIAS FOR   "sndPlaySoundA;ansi"
FUNCTION uint waveOutGetNumDevs () LIBRARY "WINMM.DLL"

A function called uf_playsound in the examples application provided with PowerBuilder calls the external functions. Uf_playsound is called with two arguments (as_filename and ai_option) that are passed through to sndPlaySoundA.

Values for ai_option are as defined in the Windows documentation, as commented here:

//Options as defined in mmystem.h. 
//These may be or'd together.
//#define SND_SYNC 0x0000  
//play synchronously (default) 
//#define SND_ASYNC 0x0001  
//play asynchronously 
//#define SND_NODEFAULT 0x0002  
//do not use default sound 
//#define SND_MEMORY 0x0004  
//lpszSoundName points to a memory file 
//#define SND_LOOP 0x0008  
//loop the sound until next sndPlaySound 
//#define SND_NOSTOP 0x0010  
//do not stop any currently playing sound 
 
uint lui_numdevs
 
lui_numdevs = WaveOutGetNumDevs() 
IF lui_numdevs > 0 THEN 
      sndPlaySoundA(as_filename,ai_option)
      RETURN 1
ELSE
      RETURN -1
END IF

Example 2

This is the declaration for the Windows GetSysColor function:

FUNCTION ulong GetSysColor (int index) LIBRARY "USER32.DLL"

This declaration uses longptr instead of ulong:

FUNCTION longptr FindWindowW (ulong classname, string windowname) LIBRARY "USER32.DLL"

This statement calls the external function. The meanings of the index argument and the return value are specified in the Windows documentation:

RETURN GetSysColor (ai_index)

Example 3

This is the declaration for the Windows GetSystemMetrics function:

FUNCTION int GetSystemMetrics (int index) LIBRARY "USER32.DLL"

These statements call the external function to get the screen height and width:

RETURN GetSystemMetrics(1)
RETURN GetSystemMetrics(0)

Datatypes for external function arguments

When you declare an external function in PowerBuilder, the datatypes of the arguments must correspond with the datatypes as declared in the function's source definition. This section documents the correspondence between datatypes in external functions and datatypes in PowerBuilder. It also includes information on byte alignment when passing structures by value.

Use the tables to find out what PowerBuilder datatype to use in an external function declaration. The PowerBuilder datatype you select depends on the datatype in the source code for the function. The first column lists datatypes in source code. The second column describes the datatype so you know exactly what it is. The third column lists the PowerBuilder datatype you should use in the external function declaration.

Boolean

BOOL and Boolean on Windows are 16-bit, signed. Both are declared in PowerBuilder as boolean.

Pointers

Datatype in source code

Size, sign, precision

PowerBuilder datatype

* (any pointer)

32-bit pointer

Long

char *

Array of bytes of variable length

Blob


Windows 32-bit FAR pointers, such as LPBYTE, LPDWORD, LPINT, LPLONG, LPVOID, and LPWORD, are declared in PowerBuilder as long datatypes. HANDLE is defined as 32 bits unsigned and is declared in PowerBuilder as an UnsignedLong.

Near-pointer datatypes (such as PSTR and NPSTR) are not supported in PowerBuilder.

Characters and strings

Datatype in source code

Size, sign, precision

PowerBuilder datatype

char

8 bits, signed

Char

string

32-bit pointer to a null-terminated array of bytes of variable length

String


The Windows 32-bit FAR pointer LPSTR is declared in PowerBuilder as string.

Reference arguments

When you pass a string to an external function by reference, all memory management is done in PowerBuilder. The string variable must be long enough to hold the returned value. To ensure that this is true, first declare the string variable, and then use the Space function to fill the variable with blanks equal to the maximum number of characters that you expect the function to return.

Fixed-point values

Datatype in source code

Size, sign, precision

PowerBuilder datatype

byte

8 bits, unsigned

Byte

short

16 bits, signed

Integer

unsigned short

16 bits, unsigned

UnsignedInteger

int

32 bits, signed

Long

unsigned int

32 bits, unsigned

UnsignedLong

long

32 bits, signed

Long

unsigned long

32 bits, unsigned

UnsignedLong

longlong

64 bits, signed

LongLong


The Windows definition WORD is declared in PowerBuilder as UnsignedInteger and the Windows definition DWORD is declared as an UnsignedLong. You cannot call external functions with return values or arguments of type short.

Floating-point values

Datatype in source code

Size, sign, precision

PowerBuilder datatype

float

32 bits, single precision

Real

double

64 bits, double precision

Double


PowerBuilder does not support 80-bit doubles on Windows.

Date and time

The PowerBuilder datatypes Date, DateTime, and Time are structures and have no direct equivalent for external functions in C.

Passing structures by value

You can pass PowerBuilder structures to external C functions if they have the same definitions and alignment as the structure's components. The DLL or shared library must be compiled using byte alignment; no padding is added to align fields within the structure.

Using 1-byte structure member alignment in external function

When you use a structure or structure array as parameters to external function in the older versions of PowerBuilder, the structure member alignment was one byte. However, the default alignment is 8 bytes on Windows platform, which means that most (if not all) Windows standard APIs use this value to align arguments with structure members. This will cause a mismatch between Windows APIs and PB applications in PowerBuilder 12.5 and earlier versions. A well adopted solution to this issue was to add some bytes within PowerBuilder structures manually to fill those gaps. Such gap filling can be complex and error-prone if involving complex nested structures. And what is worse, this solution fails with the introduction of 64-bit application development in PowerBuilder 12.6 because the number of bytes you have to fill may be different between 64-bit and 32-bit applications. This was the major reason to make this change in PowerBuilder 12.6. With PowerBuilder 12.6 for Windows API and Visual C++ the default structure member alignment is now 8 bytes. The structure member alignment was changed to 8 bytes in PowerBuilder 12.6 for both 64-bit and 32-bit applications. This was an intentional change. Customers can now call Windows API easier and use the same code for both 64-bit and 32-bit applications.

Customers can switch to the old behavior in two ways with PowerBuilder 12.6 build 4058 and above.

1. Check "Use 1-byte structure member alignment in external function" (or set UseZp1=1 in [pb] section, pb.ini, the results are the same). The effect is global with this setting changed. To make this work at runtime, please remember to deploy your pb.ini file with your application.

2. Add “progma_pack(1)” external function’s declaration, like this:

FUNCTION int STLAREGIO ( ref struc_kfzrechnerneu struc_kfz ) LIBRARY "KFZ_SS.DLL" alias for "STLAREGIO;Ansi" progma_pack(1)

progma_pack(1) is 1-byte align, progma_pack(8) is 8-bytes align. In this way, the effect is only for external function that is declared with this alignment.

Calling external functions

Global external functions

In PowerBuilder, you call global external functions using the same syntax as for calling user-defined global and system functions. As with other global functions, global external functions can be triggered or posted but not called dynamically.

Local external functions

Call local functions using the same syntax as for calling object functions. They can be triggered or posted and called dynamically.

For information

For information, see Syntax for calling PowerBuilder functions and events.

Defining source for external functions

You can use external functions written in any language that supports the standard calling sequence for 32-bit platforms. If you are calling functions on Windows in libraries that you have written yourself, remember that you need to export the functions. Depending on your compiler, you can do this in the function prototype or in a linker definition (.DEF) file. For more information about using external functions, see the section called “Using external functions” in Application Techniques.

Use _stdcall convention

C and C++ compilers typically support several calling conventions, including _cdecl (the default calling convention for C programs), _stdcall (the standard convention for Windows API calls), _fastcall, and thiscall. PowerBuilder, like many other Windows development tools, requires external functions to be exported using the WINAPI (_stdcall) format. Attempting to use a different calling convention can cause an application crash.

When you create your own C or C++ DLLs containing functions to be used in PowerBuilder, make sure that they use the standard convention for Windows API calls.

For example, if you are using a DEF file to export function definitions, you can declare the function like this:

LONG WINAPI myFunc()
{
...
};