External functions are functions that are written in languages other than PowerScript and stored in dynamic libraries. External functions are stored in dynamic link libraries (DLLs).
You can use external functions written in any language that supports the standard calling sequence for 32-bit platforms.
If you are calling functions 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.
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() { ... };
Using PBNI
You can also call external functions in PowerBuilder extensions. PowerBuilder extensions are built using the PowerBuilder Native Interface (PBNI). For more information about building PowerBuilder extensions, see the PowerBuilder Native Interface Programmers Guide and Reference. For more information about using PowerBuilder extensions, see PowerBuilder Extension Reference.
Before you can use an external function in a script, you must declare it.
Two types
You can declare two types of external functions:
-
Global external functions, which are available anywhere in the application
-
Local external functions, which are defined for a particular type of window, menu, or user object
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 as well.
Datatypes for external function arguments
When you declare an external function, the datatypes of the arguments must correspond with the datatypes as declared in the function's source definition.
For a comparison of datatypes in external functions and datatypes in PowerBuilder, see the section called “Declaring external functions” in PowerScript Reference.
To declare an external function:
-
If you are declaring a local external function, open the object for which you want to declare it.
-
In the Script view, select Declare in the first drop-down list and either Global External Functions or Local External Functions from the second list.
-
Enter the function declaration in the Script view.
For the syntax to use, see the section called “Declaring external functions” in PowerScript Reference or the examples below.
-
Save the object.
PowerBuilder compiles the declaration. If there are syntax errors, an error window opens, and you must correct the errors before PowerBuilder can save the declaration.
Modifying existing functions
You can also modify existing external function declarations in the Script view.
Suppose you have created a C dynamic library, SIMPLE.DLL, that contains a function called SimpleFunc that accepts two parameters: a character string and a structure. The following statement declares the function in PowerBuilder, passing the arguments by reference:
FUNCTION int SimpleFunc(REF string lastname, & REF my_str pbstr) LIBRARY "simple.dll"
By default, PowerBuilder handles string arguments and return values as if they have Unicode encoding. If SimpleFunc passes ANSI strings as arguments, you must use this syntax to declare it:
FUNCTION int SimpleFunc(REF string lastname, & REF my_str pbstr) LIBRARY "simple.dll" & ALIAS FOR "SimpleFunc;ansi"
Declaring Windows API functions
The Windows API includes over a thousand functions that you can call from PowerBuilder. The following examples show sample declarations for functions in the 32-bit Windows API libraries KERNEL32.DLL, GDI32.DLL, and USER32.DLL.
Windows API calls
Some 32-bit function names end with A (for ANSI) or W (for wide). Use wide function names in PowerBuilder.
For a complete list of Windows API functions, see the Microsoft Windows SDK documentation.
The following statements declare a function that gets the handle of any window that is called by name, and a function that releases the open object handle:
FUNCTION ulong FindWindowW(ulong classname, & string windowname) LIBRARY "User32.dll" FUNCTION boolean CloseHandle(ulong w_handle) & LIBRARY "Kernel32.dll"
The following statement declares a function that draws a pie chart based on the coordinates received:
FUNCTION boolean Pie(ulong hwnd,long x1,long y1, & long x2,long y2,long x3,long y3,long x4, & long y4) LIBRARY "Gdi32.dll"
The following statement declares an external C function named IsZoomed:
FUNCTION boolean IsZoomed(Ulong handle) & LIBRARY "User32.DLL"
A script that uses IsZoomed is included as an example in Using utility functions to manage information.
For more information about these functions, see the Microsoft documentation in the MSDN Library at http://msdn.microsoft.com/en-us/library/ms674884(VS.85).aspx.
In PowerBuilder, you can define external functions that expect arguments to be passed by reference or by value. When you pass an argument by reference, the external function receives a pointer to the argument and can change the contents of the argument and return the changed contents to PowerBuilder. When you pass the argument by value, the external function receives a copy of the argument and can change the contents of the copy of the argument. The changes affect only the local copy; the contents of the original argument are unchanged.
The syntax for an argument that is passed by reference is:
REF datatype arg
The syntax for an argument that is passed by value is:
datatype arg
The following statement declares the external function TEMP in PowerBuilder. This function returns an integer and expects an integer argument to be passed by reference:
FUNCTION int TEMP(ref int degree) LIBRARY "LibName.DLL"
The same statement in C would be:
int _stdcall TEMP(int * degree)
Since the argument is passed by reference, the function can change the contents of the argument, and changes made to the argument within the function will directly affect the value of the original variable in PowerBuilder. For example, the C statement *degree = 75 would change the argument named degree to 75 and return 75 to PowerBuilder.
The following statement declares the external function TEMP2 in PowerBuilder. This function returns an Integer and expects an Integer argument to be passed by value:
FUNCTION int TEMP2(int degree) LIBRARY "LibName.DLL"
The same statement in C would be:
int _stdcall TEMP2(int degree)
Since the argument is passed by value, the function can change the contents of the argument. All changes are made to the local copy of the argument; the variable in PowerBuilder is not affected.
PowerBuilder assumes all string arguments and returned values use Unicode encoding. If a function uses strings with ANSI encoding, you need to add an ALIAS FOR clause to the function declaration and add a semicolon followed by the ansi keyword. For example:
FUNCTION string NAME(string CODE) LIBRARY "LibName.DLL" ALIAS FOR "NAME;ansi"
Passing by value
The following statement declares the external C function NAME in PowerBuilder. This function expects a String argument with Unicode encoding to be passed by value:
FUNCTION string NAME(string CODE) LIBRARY "LibName.DLL"
The same statement in C would point to a buffer containing the String:
char * _stdcall NAME(char * CODE)
Since the String is passed by value, the C function can change the contents of its local copy of CODE, but the original variable in PowerBuilder is not affected.
Passing by reference
PowerBuilder has access only to its own memory. Therefore, an external function cannot return to PowerBuilder a pointer to a string. (It cannot return a memory address.)
When you pass a string to an external function, either by value or by reference, PowerBuilder passes a pointer to the string. If you pass by value, any changes the function makes to the string are not accessible to PowerBuilder. If you pass by reference, they are.
The following statement declares the external C function NAME2 in PowerBuilder. This function returns a String and expects a String argument to be passed by reference:
FUNCTION string NAME2(ref string CODE) & LIBRARY "LibName.DLL"
In C, the statement would be the same as when the argument is passed by value, shown above:
char * _stdcall NAME2(char * CODE)
The String argument is passed by reference, and the C function can change the contents of the argument and the original variable in PowerBuilder. For example, Strcpy(CODE,STUMP) would change the contents of CODE to STUMP and change the variable in the calling PowerBuilder script to the contents of variable STUMP.
If the function NAME2 in the preceding example takes a user ID and replaces it with the user's name, the PowerScript string variable CODE must be long enough to hold the returned value. To ensure that this is true, declare the String and then use the Space function to fill the String with blanks equal to the maximum number of characters you expect the function to return.
If the maximum number of characters allowed for a user's name is 40 and the ID is always five characters, you would fill the String CODE with 35 blanks before calling the external function:
String CODE CODE = ID + Space(35) . . . NAME2(CODE)
For information about the Space function, see the section called “Space” in PowerScript Reference.
Passing chars to WinAPI
WinApi characters can have ANSI or Unicode values, while PowerBuilder characters have only Unicode values. ANSI Char values passed to and from WinAPI calls are automatically converted by PowerBuilder. Therefore, when defining character array length, you must always use the PowerBuilder character length (two bytes per character).
Passing chars to C functions
Char variables passed to external C functions are converted to the C char type before passing. Arrays of Char variables are converted to the equivalent C array of char variables.
An array of Char variables embedded in a structure produces an embedded array in the C structure. This is different from an embedded String, which results in an embedded pointer to a string in the C structure.
Recommendation
Whenever possible, pass String variables back to PowerBuilder as a return value from the function.