This tutorial discusses how to interface traditional, Windows-hosted PowerBuilder 32-bit and 64-bit applications with the Windows Application Programming Interface (API), also known as the WinAPI. Much of the information presented here also applies to interfacing with DLL’s created in-house or by third-party vendors, because the interface mechanism in PB is the same. The focus, however, will be on interfacing to the WinAPI.
* * * Part one content has been revised and expanded from the original version * * *
The tutorial is presented in four parts. Part one covers External Function Declarations in PB and explores issues related to interfacing with the WinAPI. The second part examines the calling conventions in 64-bit Windows and important differences between the 32-bit and 64-bit environments. Part three looks at several factors that can affect the interfacing of PB applications with the WinAPI, such as null values, PB datatypes not supported by Windows, the PB Any datatype, unbounded arrays, nested structures and arrays of nested structures. The tutorial concludes in part four beginning with the description of an available free PB sample application and non-visual object that can dynamically determine the memory size and layout of a structure in either 32-bit or 64-bit environments. Part four also contains a list of coding tips & techniques to help you develop PB applications that interface with the WinAPI, and includes mapping tables to assist in the translation between many common WinAPI datatypes and the standard PB datatypes.
The Methodology Used to Research This Tutorial
Some of the information in this tutorial is documented in various sources such as the PowerBuilder publications published by Appeon and online Microsoft documentation (docs.microsoft.com). Several blogs and forums were helpful, particularly in regards to Windows. Additional material was discovered by extensive experimentation in PowerBuilder 2017R2 and Visual Studio Community 2017.
Using a small PowerBuilder application that utilizes the RtlMoveMemory WinAPI function, more than 100 variations in structure and nested structure configurations were examined. Structures were populated with values easily recognizable in a memory snapshot, then the RtlMoveMemory WinAPI function would be used to populate a Byte array with a snapshot of the memory occupied by the structure(s). To a lesser extent, similar work was performed in Microsoft Visual Studio. Small, simple DLL's coded in C/C++ were created to test what PowerBuilder passes to a DLL function in certain circumstances. The results frequently led to more questions, which in turn led to another round of web research and hands-on experiments. Over time patterns appeared and from these patterns, insight into the rules and factors that govern structure member layout was gained.
This approach also proved useful in determining how PB manages memory in regards to variables of type Any and structures with members of type Any or members that are an array of type Any. The results of the research concerning the PB Any datatype is detailed in part three of this tutorial.
PB External Function Declarations & the Windows API
For many years and through many versions of PowerBuilder, access to API functions in Windows has been accomplished by interfacing with what used to be called the Win32 API, a documented collection of entry points in several dynamic link libraries (DLL’s) provided by Microsoft as part of the Windows operating system, such as kernel32.dll, user32.dll and gdi32.dll. The Win32 API is now called the Windows API, presumably because it is no longer limited to the 32-bit execution environment.
Note: The names of several Windows system DLL’s falsely give the impression they are for use only for one specific bitness (e.g., “user32.dll”). In 64-bit versions of Windows, there are 32-bit and 64-bit versions of all system binaries that have the same name. The versions are kept in separate subdirectories (such as C:\Windows\System32 and C:\Windows\SysWOW64). The name of the Windows DLL stays the same, regardless of the bitness.
External Function Declaration Fundamentals
PowerBuilder is able to interface with DLL entry points via external function declarations. This tutorial uses the acronym EFD to refer to PB external function declarations. An EFD conveys to the PB compiler all of the information it needs to create the “under-the-covers” code to invoke a specific DLL entry point, pass arguments to the DLL entry point and accept a return value from it.
Important! The EFD does not actually execute an external function, it only describes how it will be executed.
Once the entry point for a WinAPI function, its parameters, return value (if any) and the DLL that contains the WinAPI function are declared via an EFD, that particular API function can be called in PowerScript by the name the developer assigns to it in the EFD, as if the API function is a PowerScript function.
Note: External functions can be triggered or posted, like PowerScript and object functions. However, return values are not accessible from posted functions, including external functions. External functions can also be executed using dynamic lookup (i.e., via the “DYNAMIC” keyword when the function is called), the same as PowerScript functions and object functions. Static (i.e., "normal") lookup can be explicitly requested via the “STATIC” keyword when the external function is called.
Here is an example of a simple WinAPI interface in C/C++, as it is described in the Microsoft online documentation:
As its name implies, this particular API is used to “flash” a window’s title and border. It can also be used to flash the window’s icon in the Windows System Tray, either separately or together. These can be flashed once or multiple times at a requested refresh rate. This function requires one argument parameter of type PFLASHWINFO, which the Windows online documentation describes as a pointer to a FLASHWINFO structure (Pointer to FLASHWINFO = PFLASHWINFO). We’ll examine the layout of this structure shortly. The function returns a BOOL (a C/C++ type definition equivalent of a PowerBuilder Boolean) value.
The Windows online documentation website notes this API function resides in the Windows system dynamic link library “user32.dll”. This website is my "go to" source for finding the name of the DLL where the API function resides. The URL will be listed in the "Tips & Techniques" section in part four of the tutorial.
Note: In order to interface with any DLL entry point, the name of the DLL containing the entry point is required. At execution time, the PowerBuilder Virtual Machine (PBVM) needs to be able to locate the DLL. Determining the whereabouts of Windows system DLL’s is not an issue, because Windows “knows” where its DLL’s reside. However, you should keep in mind when utilizing in-house or third-party DLL’s that they need to be accessible to the PB application and the underlying PBVM. Here are the four locations that the Microsoft O/S Loader will search for the DLL named in an EFD:
- The current (working) directory of the PB App EXE.
- The Windows O/S Start-up directory.
- All the Windows O/S Start-up subdirectories.
- All directories listed in the Windows O/S System Path (environment variable).
Note: If the PB Application is started from a .BAT (DOS command) file, then the DOS session will start with a copy of the System Path. This path can then be augmented further using SET Commands within the .BAT file. That also includes “environment” variables.
Here is one possible PowerBuilder EFD for the FlashWindowEx API:
FUNCTION Boolean FlashWindowEx ( &
REF s_flashwinfo pfwi &
) LIBRARY “user32.dll”
A brief comment about the style conventions you’ll see in this tutorial; I prefer to specify EFD keywords (such as “FUNCTION”, “REF” and “LIBRARY”) in uppercase and standard PB datatypes (such as “Boolean”) in mixed case, as I find this helps readability. Except for the name of the API function, use of upper/lower case is immaterial.
Important! The function name must always be specified exactly as the Windows documentation lists it. This also applies to the ALIAS FOR directive, if used.
I also like to specify each argument parameter on its own continuation line and the LIBRARY and optional ALIAS FOR directives on its own continuation line, in order to prevent the EFD from getting too “strung out” on long lines in the PowerScript Editor. With this coding convention, I can see at a glance the number of function arguments and how each argument is passed, the name of the DLL and whether or not an alias will be used.
The ALIAS FOR Directive
Here’s an example of an EFD for the same API function that uses the ALIAS FOR directive:
FUNCTION Boolean MakeTheWindowFlicker ( &
REF s_flashwinfo flickerwindowinformation &
) LIBRARY “user32.dll” ALIAS FOR “FlashWindowEx”
In this alternative example EFD, you can see that the name of the function as it will be referenced in PowerScript (“MakeTheWindowFlicker”) is an alias for the actual name of the WinAPI function (“FlashWindowEx”). The function name is critical. Windows may not be able to identify the DLL entry point (i.e., the function name) unless the name in the EFD exactly matches the documented name. When this happens, it can be a very difficult issue to troubleshoot, so I strongly recommend you always specify the API function name exactly as documented.
If the name of a DLL function contains non-standard characters (such as “@”, for example), you must use the ALIAS FOR directive so that the name of the external function used in PowerScript code contains only those characters acceptable to PowerBuilder:
- Letters and numbers
- “-” (dash/hyphen)
- “_” (underscore)
- “$” (dollar sign)
- “#” (number sign)
- “%” (percent sign)
Passing Arguments by Reference
The “REF” keyword preceding any argument parameter datatype in the EFD tells the PB compiler to pass this argument by reference instead of by value. When an argument parameter is passed by reference, the calling method (the PowerBuilder application) can access any changes made to the parameter by the called method (the DLL function). The PB compiler accomplishes this by passing the memory address (i.e. a pointer) as the parameter value in the call stack instead of passing a copy of the actual parameter value itself. Passing a copy of the parameter value is referred to as “passing by value” and it is the default method of passing arguments.
In this particular example, it so happens that the FlashWindowEx API function does not alter the contents of the FLASHWINFO structure. Even so, the specification for this API function requires that the argument be “a pointer to a FLASHWINFO structure”. Since PowerBuilder does not provide a mechanism for obtaining memory addresses, the use of the “REF” keyword in this instance provides a way that we can satisfy the syntactical requirement of the DLL function call.
Passing structures to DLL functions by reference is not unusual, because this keeps the amount of data passed in the call stack small. Some WinAPI structures may contain several hundred bytes of data, so it is more efficient in most cases to pass a four-byte (in 32-bit processes) or eight-byte (in 64-bit processes) pointer to an argument parameter than it is to pass the entire parameter through the call stack.
When a WinAPI function specifies that a pointer to an argument value is to be passed without changing the argument value, the typedef’d Windows datatype will typically (but not always) include a prefix code letter of “C” (for “constant”) to convey this information to the developer. For example, the WinAPI datatype “LPCWSTR” is used for an argument that is “a long (32-bit) pointer to a constant, wide (i.e., Unicode) string (“L”+”P”+”C”+”W”+”STR” = “LPCWSTR”).
Note the name given to the argument in any EFD (“flickerwindowinformation” in the preceding example) is immaterial, because the EFD is not executable. Its sole purpose is to inform the PB compiler how to interface with the external DLL entry point. I prefer to keep the same argument name that appears in the online WinAPI documentation so as to provide consistency with the documentation.
Passing Arguments Read-Only
A lessor-known and seldom-used (and not well-documented) alternative to the REF keyword is READONLY. Passing read-only arguments is strictly a PowerBuilder convention and it can be used within PB. The Windows API has no identical concept, although the "const" keyword in a C/C++ type definition or variable declaration is similar. Therefore, passing an argument read-only to an external DLL function involves compromises on PB's part and you probably will never use it. Syntactically, passing an argument parameter read-only to an external function is supported, however, so for completeness, I'll explain how it works
For a scalar (non-array) argument, the READONLY keyword passes the argument by value; a copy of the argument value is placed onto the execution call stack where the external function accesses it. The external function cannot effect a change in the value in PowerBuilder because a copy of the actual value is what is passed. The net effect is that the argument is treated as if it is a constant.
When an array argument (and remember: a string is an array) is passed using the READONLY keyword, the memory address of the first element in the array is supplied to the external function via the call stack, the same as when the REF keyword is used. Thus, you have the potential performance benefit of passing an array via a pointer.
Here's a really interesting quirk: The DLL can make changes to the "read-only" array!
Although this may initially seem counter-intuitive, it makes sense when you think about it. Since a pointer to the beginning of the array is passed as the argument value, the DLL knows where the array starts and therefore the DLL can do whatever it wants to the array - there's really nothing PowerBuilder can do to keep the contents of the array from being changed, since PB is (temporarily) not in control. This potentially confusing state of affairs is one reason why you seldom see the READONLY keyword used in PowerBuilder EFD's.
Let's be clear: You do not have to use the READONLY keyword. You can successfully interface with external functions by passing arguments by value or by reference.
Passing an Array by Value vs. Passing an Array by Reference
What is the difference between passing an array by value and passing the same array by reference? After all, doesn't a pointer to the array get passed in either case? There is a difference and that difference is significant. A different array and therefore different memory addresses get passed when an array is passed by value than when it is passed by reference.
To test this, I created a simple DLL containing a function that accepts two string arguments. In WinAPI terminology, both arguments are declared as datatype LPWSTR (a long pointer to a Unicode string). The DLL function copies the contents of the first string into the second string. In the PB app that calls the DLL function, the EFD can specify the second argument to be passed either by value, by reference or read-only, because in all cases a pointer to the string will be passed and that is critical thing the DLL expects. The PB app must initialize the array that contains the second string, so both strings are initialized prior to the DLL function being called. Here is how I accomplished this:
ls_source = "Hooby-Dooby!!!"
ls_target = Space(Len(ls_source)+1)
The rationale for doing this will be discussed a little later.
When the second string get passed, regardless whether it is passed by reference or read-only, the same memory address get passed and upon return from the DLL function, the second string contains a copy of the first string. When the second string gets passed by value, a different memory address get passed and upon return, the second string continues to contain spaces even though the DLL function put a copy of the first string into what it was told was the second string. No run-time error occurs, regardless of how the second string argument parameter is passed. An additional DLL function that returns the address of an array of any type passed to it verifies that a different memory address gets passed.
Remember that when a scalar value is passed by value, PB passes a copy of the value to the DLL function. I strongly suspect the same thing occurs when an array is passed by value; To ensure no changes are made to the array, PB makes a copy of the array and passes the address of the copy to the DLL. The DLL function can make changes to the array contents, but those changes are made to a throw-away copy of the original array.
How You Pass Arguments Is Up To the DLL, Not You
The choice on whether to pass an argument parameter to an external function by value or by reference is not yours to make; that decision was made by the creator of the DLL. Admittedly, it can sometimes be difficult to determine this critical piece of information when you're new to external functions. Experience is a great teacher, but until you "learn the ropes", here are some clues you can look for when "decoding" the Windows API:
First, if the external function is going to change or set the value of an argument parameter so that the caller can access the new value, the argument needs to be passed by reference. This applies to single variables, structures, strings and other types of arrays. You may need to read the description of the argument in the documentation to determine if the DLL will be changing or setting a value.
- Next, if the datatype of the argument datatype begins with "P" or "LP" (as in our example "PFLASHWINFO", or examples such as "LPLONG", "LPWSTR", etc.), this typically is an abbreviation for "Pointer" or "Long Pointer" and means the memory address of the variable or structure is to be supplied. Thus, the argument must be passed by reference.
Keep in mind that an array argument is always passed as a pointer; the memory address of the first element. When you specify an array as an argument in an EFD, PowerBuilder will automatically pass the array address and not the array contents.
Please note passing an array pointer by value means that PB passes the address of a throw-away copy of the array. This prevents the actual array contents from being changed.
Also, keep in mind that a string is actually an array of characters, so by default a string argument is automatically passed by value as a pointer to the first character of a copy of the string.
If the external function is going to change the contents of the argument array (or string), pass it by reference... in either case a pointer will be passed, but the REF keyword will be required if the called function makes any change to the array or string.
Next, even if the datatype does not begin "P" or "LP", carefully examine the description of the argument in the documentation. Any argument described as a memory address or pointer to a scalar (non-array) value or structure should be passed by reference.
- If none of the above guidelines tell you how the argument is to be passed, the likelihood is great that it should be passed by value.
Functions vs. Subroutines
Not all API functions return a value. In C/C++, the forward declaration for a function that does not return a value syntactically states that the function returns VOID.
A function that does not return a value is frequently called a subroutine, so the nomenclature used in the EFD changes accordingly. Here’s an example of an EFD for a WinAPI subroutine that copies a requested number of bytes from the start of a FLASHWINFO structure to an unbounded byte array:
SUBROUTINE Copy2ByteArray ( &
REF Byte Destination, &
REF s_flashwinfo Source, &
Long Length &
) LIBRARY “kernel32.dll” ALIAS FOR “RtlMoveMemory”
In this example, note that the PB Byte array (the destination) must be initialized by the PB application prior to invoking this WinAPI subroutine. PB always needs to “be in charge of” or manage the memory space it uses for its variables, structures and arrays, otherwise the memory may not be managed correctly. This applies to PB strings, too, because a string is but an array of characters. It also applies to a Blob, which is equivalent to an array of bytes. One of the coding tips later on in part four mentions this and will include an example how to properly initialize a String prior to an external function call.
Tip: When you create an EFD, save the object containing the EFD before you write code that calls the external function. PB compiles all of the scripts in an object before changes to EFD’s are saved, so code that calls an external function described by a new EFD will not successfully compile until after the object is successfully saved. If you forget, and write the code that calls the external function before saving the EFD, you have to temporarily comment out the calling code, save, then un-comment.
Scope and Access
A PowerBuilder EFD can either have global or local scope. I discourage the use of global external functions in general because (1) in my experience few external functions need to be accessible globally, and (2) a global EFD actually resides in the application object instead of the object where they are called. A locally-scoped EFD can be specified in windows, menus, and user objects (visual and non-visual). Public, protected, or private accessibility to local external functions can be explicitly specified in the EFD, if needed, in the same manner that access level is managed for instance variables. Public access is assumed if not specified. Like global PB functions, global external functions can only have public access.
Where Are EFD’s Defined?
You define local EFD’s in a PowerScript Painter using the “Declare Instance Variables” pane by changing the view (the drop-down selection located at the top-middle area of the pane) from “Instance Variables” to “Local External Functions.”
Note: Immediately above the “Local External Functions” selection in this drop-down is the “Global External Functions” selection. Even though you can view, edit or create global EFD’s in any object where local EFD’s can be created, be aware the code for all global EFD’s actually resides in the Application object. If your application is under source control, you should first perform a source control checkout on the Application object, then edit it and manage global EFD’s directly in the Application object.
Character and String Encoding
Since PB Version 10, external functions interact with string and character data using Unicode encoding (two bytes per character) by default. It is possible for PowerBuilder to interface with DLL functions that utilize ANSI encoding for character and string values via an optional keyword included in the ALIAS FOR directive:
... ALIAS FOR “externalname;ansi”
If a DLL does not seem to be able to accept character/string data with Unicode encoding or if “garbage” data is being returned for character/string values, you may be encountering a Unicode/ANSI encoding conflict. If so, try including “;ansi” in the ALIAS FOR directive.
Whenever a WinAPI function interacts with string or character data, Microsoft provides two similarly-named versions of an API function; one that utilizes Unicode encoding and the other for ANSI. Unicode-specific WinAPI function names end with “W” (for “wide” characters) and ANSI-specific function names end with “A”. For example, the name of the WinAPI functions to obtain the path of a system folder are:
SHGetFolderPathW (for Unicode)
SHGetFolderPathA (for ANSI)
This naming convention for different encoding versions also applies to WinAPI structures that contain string or character members, such as:
OPENFILENAMEW (for Unicode)
OPENFILENAMEA (for ANSI)
In PB 10 and later, you should use the Unicode version of WinAPI functions and structures unless there are unusual or special conditions warranting the use of the ANSI version.
Although WinAPI characters can utilize Unicode or ANSI encoding, Character variables in PB can only contain Unicode values when passed to a DLL. All ANSI Character variables passed to and from WinAPI calls are automatically converted by PowerBuilder to Unicode. PB variables (and structure members) of the Character datatype are converted to the C/C++ char type before passing when ANSI encoding is specified in the ALIAS FOR directive.
The Windows API defines several typedef'd character datatypes. In alphabetical order, they are:
- CCHAR An 8-bit ANSI character.
- CHAR An 8-bit ANSI character.
- TCHAR A WCHAR if Unicode is in use, or a CHAR if ANSI is in use.
- UCHAR An unsigned CHAR.
- WCHAR A 16-bit, or "Wide" Unicode character.
Most strings and characters in the Windows API are defined as having either the WCHAR or TCHAR datatypes.
From the Appeon PowerBuilder Application Techniques publication:
“PowerBuilder arrays of the Character datatype are converted to the equivalent C/C++ array of char variables. A PB array of the Character datatype 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.”
Unicode Comes in Several Flavors, but Windows & PB Utilize Only One
Unicode can be implemented by different character encodings. The Unicode standard defines UTF-8, UTF-16 and UTF-32, and several other encodings are in use (Source: Wikipedia). The Windows O/S uses UTF-16 encoding internally, as does PowerBuilder. Specifically, they use UTF-16LE (the “LE” stands for “Little Endian”) encoding. More about that in a minute.
Even though PB uses a single Unicode encoding internally, it is able in a limited fashion to convert string data between the following four encodings via the Blob and String PowerScript functions. Note that as of the latest PB2019 release, PB does not support conversion to/from UTF-32, only:
- UTF-16LE (the default used by PowerBuilder)
- UTF-16BE (the “BE” stands for “Big Endian”)
In case you're curious what the terms "Little Endian" and "Big Endian" signify, these refer to different ways to order the bytes that comprise a multi-byte value. For example, if we have the 16-bit hexadecimal value 0x1c95, we know it can be stored in two bytes. With Little Endian encoding, the byte containing the least significant (i.e., the "littlest") portion of the value is stored first (0x95 first, followed by 0x1c). The opposite occurs when "Big Endian" encoding is used (0x1c first, followed by 0x95) - the byte containing the most significant portion (the "biggest") is stored first. In UTF-16, every character is identified using two bytes. The ordering of the bytes (Little Endian or Big Endian) must be defined to ensure the proper interpretation of each character.
Interfacing with In-House and/or Third-Party DLL’s
The terms “calling convention” or “call format” are used interchangeably to identify the set of rules that define how data is placed onto and taken off of the execution call stack. If the code that calls a function and the function that is called do not use the same call format rules, argument values cannot be exchanged consistently and accurately, and therefore errors will occur.
The WinAPI uses the “__stdcall” calling convention, also known as the WINAPI format, because Visual Studio supports the WINAPI macro that codes the WINAPI/__stdcall calling convention for the target. The source code for all DLL entry points identifies the calling convention to tell the C/C++ compiler to create executable code that implements the desired calling convention rules. Because traditional PowerBuilder is a development platform for Windows applications, it uses the “__stdcall” calling convention exclusively.
In order for PB to be able to interface with DLL’s supplied by in-house developers and/or third-party vendors, the entry points in the DLL are required to be defined (the precise terminology is “exported”) using the “__stdcall” format. Some of the other calling conventions in existence are “__cdecl” (the default calling convention for the C programming language), “__clrcall”, “__thiscall”, “__fastcall” and “__vectorcall”. In older C/C++ code, you may see “_far _pascal” specified as the call format.
It’s a big world and not everyone follows the naming standards and calling convention used by Windows, so if a third-party DLL accepts or returns character or string data, you will need to determine from the vendor if that data is encoded using Unicode UTF-16LE or ANSI so that the appropriate EFD can be coded.
DLL's created using Delphi, for example, do not use the "__stdcall" calling convention by default, but they can use it if explicitly requested when the Delphi-based DLL is created. Also, strings in Delphi are not compatible with PowerBuilder (and Windows) unless they are declared using the Delphi PCHAR (Pointer to char) datatype, which is a single byte-per-character, ANSI-encoded string.
Not only must PowerBuilder and the Dynamic Link Libraries it interfaces with both use the same calling convention, they must agree on the rules for aligning structure members in memory based on member datatypes. Stay tuned to part two of this tutorial for details.
Initializing Strings, Blobs and Other Types of Arrays
PowerBuilder manages the memory used by PB applications. This statement may seem to be a pronouncement from Captain Obvious, but there is a potentially serious problem that can occur when an external function modifies memory that is being managed by PowerBuilder. Simply put, the external function must not change the amount of memory utilized by a string, blob or other type of array. The following scenario illustrates why:
Let's say your app passes an empty (but not null) PB String variable by reference to an external function and the external function sets the value of the string to be "Now is the time for all good computers to come to the aid of their programmers!". By doing this, the size of the character array holding the string increases from one character (the terminating null) to eighty characters (which includes the terminating null). The amount of storage needed to hold the string grows from two bytes to 160 bytes. When PB increases the length of the string, it utilizes a new or different block of memory if there is insufficient space to hold the new array.
This behind-the-scenes memory management won't occur if the external function blindly writes 160 bytes of data at a location where only two bytes were stored previously. It is very possible or even likely that the external function will overwrite one or more variables, or internal management data used by the PowerBuilder Virtual Machine to track and manage the activity in the PB application. Not a good idea!
This is the danger of relinquishing execution to software external from the PB application while giving the external software access to PB-controlled memory.
Important: To avoid this problem, you must always initialize strings, blobs and other arrays before you call an external function that will modify them.
A string is an array (of characters). A blob is an array (of bytes). To avoid issues caused by overwriting memory, load the array (be it string, blob or other) with the maximum amount of data the external function will place in the array. Keep in mind that a string is terminated with a "null" character, two bytes (for Unicode) containing binary zeroes, so always initialize the string to the maximum string length plus one to take the terminating null character into account.
The Space function is an excellent way to initialize a string to a specific length. The "Tips & Techniques" section in Part Four of this tutorial contains a code sample that shows how you do this, in addition to the code snippet presented earlier.
The Blob function loads the contents of a string into a Blob variable. Use the Space function to initialize a string (remembering that every space occupies two bytes and that the string contains a terminating null character), then copy that string into the blob.
To initialize an empty, unbounded array, set a value in what you want to be the last element of the array... PB will allocate enough memory to hold the entire array of the requested size. The elements of a bounded (fixed size) numeric array are initialized by PB to zeroes, so no explicit initialization need be taken.
If you cannot determine the maximum amount of data the external function is going to set, my advice is to "go large"... oversizing the amount of data is much better than not making it large enough.
This portion of the tutorial focused on external function declarations (EFD’s) in PowerBuilder and how they convey to the compiler the information needed to interface with a DLL entry point such as a WinAPI function or a function in an in-house or third-party’s DLL. We discussed character encoding and also how argument parameters can be passed by value, by reference or read-only. In part two, we’ll examine the calling conventions in 64-bit Windows processes and important differences between the 32-bit and 64-bit environments in particular, as it pertains to structures.
The following changes have been made to the original version of Part One:
- The distinction between passing arguments by reference versus passing by value has been clarified.
- A description of the optional READONLY keyword argument qualifier in an external function declaration has been added.
- An explanation of the subtle, but important difference between passing an array by value and passing an array by reference has been included.
- Some guidelines on what to look for when determining whether an argument should be passed by value or by reference have been included.
- A list of character datatypes in the Windows API has been added.
- An explanation of Little Endian and Big Endian encoding has been added.
- Delphi is now mentioned as an example of third-party software that has the capability to produce a DLL that PowerBuilder can interface with, but only when certain options are utilized in Delphi.
- The rationale why you must initialize strings, blobs and other types of arrays before calling an external function that will change the array contents has been added.
- Some minor word omissions, grammatical errors and other miscellaneous corrections have been made.