Tech Articles


Interfacing PB Applications with the Windows API - Part 4


This tutorial has discussed 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. In the first three parts, the groundwork was laid to make you more knowledgeable and comfortable coding External Function Declarations (EFD's) and understanding the nuances of how information is exchanged between PB and Windows API functions. Part four contains a list of coding tips and techniques and mapping tables to help you with the translation between many common WinAPI datatypes and the standard PB datatypes. It also describes a free PB sample application and non-visual user object that can dynamically determine the memory size and layout of a structure in either 32-bit or 64-bit environments.

* * * Part four content has been revised and expanded from the original version * * *

Structure Size and Layout Sample Application & Non-Visual User Object

PowerBuilder does not currently have a “SizeOf” function for returning the amount of memory required by a standard datatype or a structure. The ability to dynamically determine the size of a structure would be very helpful in setting the “size” member required in some WinAPI structures, particularly if it could take into account structure member boundary-alignment padding and other conventions for both the 32-bit and 64-bit environments. Review part two of the tutorial for a refresher on structure member boundary-alignment padding.

Roland Smith provides a free PB code sample (named “SizeOf”) on his TopWizProgramming.com website for calculating the size of a structure. The calculation logic is contained within a single non-visual user object that was originally authored many years ago by Rui Cruz. Roland enhanced the original object to handle the datatypes added to PowerBuilder in version 12.6. With Roland’s permission, I’ve made several additional enhancements to the object:

  • It recognizes the process bitness and uses that value as the default to determine member and structure size.
  • Either bitness can be requested prior to requesting a SizeOf calculation on a structure (this is how the Structure Layout sample app populates the list of structure sizes for both 32-bit and 64-bit).
  • It can determine boundary alignment for structure members and nested structures using either the progma_pack( 8 ) or progma_pack( 1 ) rules and includes padding bytes in the size calculations when needed.
  • It can calculate member and structure size for either Unicode or ANSI characters/strings.
  • It can produce a report that describes the layout of a structure in memory, including nested structures.

With this newly-enhanced object it is now possible to determine the size of PowerBuilder structures at execution time, even if PB creates a temporary, behind-the-scenes version of the structure to satisfy the progma_pack( 1 ) directive specified in an EFD.

There are, however, a few caveats:

  • The utility examines the layout of a structure by inspecting an array of PB VariableDefinition objects contained within the structure’s ClassDefinition object. Each structure member is described by a VariableDefinition object. A limitation of this approach is that although this information describes how the structure’s members are defined, it cannot determine the current condition or contents of any structure member during execution of the application. For example:
     
    • It is not possible to determine from the structure definition whether any structure member is null.
       
    • While it is possible to determine that a structure member is defined as an unbounded array, it cannot determine from the structure definition the number of elements the unbounded array currently contains.
       
    • It cannot determine the type of data currently contained in a structure member of type Any.
       
    • It cannot determine the current value of any structure member…this requires a reference to the structure member in question, which is not available from looking at only the structure definition.

With Roland’s permission, I have posted in the CodeXchange section of the Appeon Community this enhanced version of his SizeOf utility and the accompanying Structure Layout sample application.

https://community.appeon.com/index.php/codeexchange/powerbuilder/260-enhanced-sizeof-utility-with-sample-application-and-winapi-tutorial-demo-tests#280

The sample app demonstrates how the structure size calculator works by determining the size of approximately thirty structures (primarily WinAPI structures) for both the 32-bit and 64-bit environments. Some of these WinAPI structures are relatively large and some are small. Some grow in size in a 64-bit environment while others do not. Here’s a screenshot of the application’s main window:

Demo Application - Main Window

The Structure Layout sample application was developed in PB 2017 R2 and can be compiled into either a 32-bit or 64-bit executable. It has been successfully tested in PB 2019 R2.

The Structure Layout Report

A structure layout report can be displayed for any of the listed structures by clicking on the “Layout” button in either the 32-bit or 64-bit columns. Here’s a snapshot of the structure layout report for the 64-bit version of the FLASHWINFO structure:

Demo Application - Report Window

Tutorial-Related Test Suites

The sample application can perform four test suites related to topics covered in this tutorial:

  1. Demo Flash Window API

    Calls the FlashWindowEx Windows API function using coding techniques described in the tutorial that work properly in both the 32-bit and 64-bit environments without relying on bitness-specific coding or hard-coded structure size values.
     
  2. Test Progma_Pack

    Calls the FlashWindowEx Windows API to illustrate the impact of specifying the progma_pack( 8 ) or progma_pack( 1 ) directive in an EFD and what happens when the progma_pack directive is omitted from an EFD. The progma_pack( 1 ) test fails (gracefully, thanks to a Try-Catch block) in a 64-bit environment, as expected, because Windows expects to find structure member boundary alignment padding and structure size padding to exist but it gets omitted when progma_pack( 1 ) is specified.
     
  3. Test Any’s in Memory

    Shows how variables of type Any are actually represented in memory, how a structure member of type Any is represented in memory and what can happen when a variable of type Any is passed as an argument parameter to an external function.
     
  4. Test Nested Structures

    Populates two-element arrays of a nested hierarchy of small, medium and large-sized structures that contain values that are recognizable when memory is examined. The test takes a memory snapshot, then extracts the structure member data values from the snapshot byte-by-byte to illustrate that the boundary alignment and padding rules for structure members and structures described in this tutorial are correct.
     

Coding Tips and Techniques For 32-Bit and 64-Bit Applications

The following compendium of tips and techniques (many of which have been discussed in the tutorial) can help you interface 32-bit and 64-bit Windows-hosted PowerBuilder applications with Windows dynamic link libraries:

  1. Use the “REF” keyword in EFD’s whenever the WinAPI function requires a pointer to a value or structure, even if the called API function will not make any change to the argument parameter.
     
  2. Because a string is an array (of characters), strings are automatically passed as a pointer, so the “REF” keyword is not required with a parameter of type String unless the called API function will be changing the value of the string and the calling PB application needs access to the changed value.
     
  3. Whenever the definition of an argument parameter in a Windows API function includes "*" (as in "long * pLength", for example), the API is specifying that a pointer is to be passed, so the "REF" keyword will be needed in the EFD... except for strings (see #2 above).
     
  4. It is not uncommon for a C/C++ type definition to equate to a pointer. For example, "LPDWORD" is a pointer to a DWORD (a 32-bit unsigned integer). Therefore, you should include the "REF" keyword in the EFD to ensure a pointer is passed as the argument value.
     
  5. Strings that are going to be given a value by the called API function must first be initialized to its maximum possible length before the API function is invoked, Typically, this is accomplished via the Space PowerScript function.
     
    Tip: For extra memory overflow safety, always use a “+1” in the Space PowerScript function:
     
      ls_FilePath = Space(li_MaxPathLength+1)
     
    Example:
            The following code invokes the WinAPI GetComputerNameW function to obtain the (Unicode)
            name of the computer that is executing the application.
     
    The Windows API definition:
     
       BOOL GetComputerNameW(
          LPWSTR lpBuffer,
          LPDWORD nSize
       );
     
    The PB EFD:
       FUNCTION Boolean GetComputerName ( &
          REF String       lpBuffer, &
          REF UnsignedLong nSize &
          ) LIBRARY "kernel32.dll" ALIAS FOR "GetComputerNameW"
     
    Example PB Code:
       Constant UnsignedLong MAX_COMPUTERNAME_LENGTH = 31

       Boolean      lb_rc
       UnsignedLong lul_size = MAX_COMPUTERNAME_LENGTH
       String       ls_name
      
       ls_name = Space(lul_size+1) // Pre-allocate string (w/1 extra char)
       lul_size++                  // Add 1 char for the terminating null
      
       lb_rc = GetComputerName(ls_name,lul_size)
      
       if not lb_rc then ls_name = ''
       Return ls_name
     
  6. The names given to WinAPI functions involving string and/or character information end in either “W” (for “Wide character”, i.e., Unicode) or “A” (for “ANSI”). The EFD for any API function that works with ANSI-encoded data must include the ALIAS FOR directive with “;ansi” appended to the name of the actual API function because PowerBuilder v10 and upward assume Unicode by default.
     
  7. It is good practice to use the ALIAS FOR directive in an EFD to normally hide the distinction between Unicode and ANSI versions of an API function, as shown below and in Tip number 5 above:

      FUNCTION Boolean GetOpenFileName ( &
          REF s_openfilenamew Arg1 &
          ) LIBRARY “comdlg32.dll” ALIAS FOR “GetOpenFileNameW”

    I suggest if you do this, do it only for the Unicode version of API functions because use of Unicode is the norm within PB – I prefer to leave the trailing “A” in the EFD for any ANSI functions to clearly delineate any cases where ANSI encoding is required, as illustrated in the example below:

       // Note: ANSI-encoding version
       // (The ALIAS FOR directive is needed only to specify the ";ansi" keyword)

       FUNCTION Boolean GetOpenFileNameA ( &
          REF s_openfilenamea Arg1 &
          ) LIBRARY “comdlg32.dll” ALIAS FOR "GetOpenFileNameA;ansi"
     
  8. Specify the name of the actual WinAPI function using the same capitalization as in the online Microsoft WinAPI documentation to avoid run-time errors that are difficult to diagnose.
     
  9. The runtime bitness of a PB application can be obtained by checking the ProcessBitness property of the PB Environment object. This integer value will be either 32 or 64. Use the PowerScript GetEnvironment function to obtain an instance of the PB Environment object.
     
  10. If you need to use the progma_pack( 1 ) directive in an EFD, include a comment that explains your intent or requirement.
     
  11. Named constants in WinAPI functions are typically defined in a #define declaration in a C/C++ header (.h) file. For example, the sample code that invokes the FlashWindowEx API function sets a structure member to FLASHW_ALL, which has the value 3.

    Where can you look up the value of named constants in the WinAPI?

    Sometimes the normal online Windows API documentation will list these values and explain what each supported value means. Unfortunately, the documentation doesn’t always include this information. When this happens, look in the C/C++ “header” files in the Windows System Development Kit (SDK). Header files have a file extension of “.h”, such as “winbase.h”. Keep in mind there are hundreds of header files in the WinAPI, so it can be a challenge to find the definition in source code.

    Even if you do not write code in Visual Studio, I suggest you install it (Community editions can be downloaded from Microsoft and installed for free) so you can locate and search the contents of the folder where the source header files reside. For example, in Visual Studio 2017 Community, I find a total of 1,508 header files in:

           C:\Program Files (x86)\Windows Kits\10\Include\10.0.17763.0\um

    You’ll also likely need use of a tool that can search the contents of files. I use an excellent, free utility named Notepad++. A technique I’ve found that works well is to use Notepad++ to search for the #define statement containing the named constant I'm looking for. For example, to find the FLASHW_ALL constant, I search for (without the double-quotes, of course):

            “#define FLASHW_ALL “      <- Note the trailing blank
                            -or-
            “ FLASHW_ALL “                 <- Note the leading AND trailing blanks

    There are nearly always multiple, related constants having names that begin the same. In this example, the FLASHW_ALL constant is defined as FLASHW_CAPTION | FLASHW_TRAY (two other constants combined via the C/C++ bitwise OR operator), so I will often search for all of the related constants, as in (without the double-quotes):

            “#define FLASHW_”            <- Note NO trailing blank
                            -or-
            “ FLASHW_”                       <- Leading blank only

    In this case, we see that FLASHW_CAPTION = 1 (0x00000001) and FLASHW_TRAY = 2 (0x00000002), so after a bitwise OR operation, FLASHW_ALL = 3 (0x00000003).

    I prefer to include these constants in PowerBuilder code as constant instance variables (hmmm... isn’t a “constant variable” an oxymoron?) using the same names as defined in the Windows header files. For example, in PowerBuilder:

       Constant UnsignedLong FLASHW_CAPTION = 1  // 0x00000001
       Constant UnsignedLong FLASHW_TRAY    = 2  // 0x00000002
       Constant UnsignedLong FLASHW_ALL     = FLASHW_CAPTION + FLASHW_TRAY // 0x00000003
     
  12. Don’t use PB Any’s when interfacing to the WinAPI or third-party DLL’s. Just don’t. Even if you believe you have a really good reason to do so. Review part three of the tutorial to remember why. Use only the standard PB datatypes that Windows recognizes (refer to the first table in the next section).
     
  13. Unbounded arrays are OK to use if the API function expects it. Typically, the WinAPI function definition or a structure passed to the WinAPI function will include a parameter/member stating how many elements are currently contained in the unbounded array so that Windows can determine what to look for.

    Just as you do for strings (see tip #5), if the DLL function is going to change the contents of an unbounded array, initialize the array to the maximum size the DLL function will be using. PB needs to manage the array's memory and you do not want the DLL to corrupt PB-controlled memory by writing beyond what PB knows to be the end of the array.
     
  14. Avoid empty unbounded arrays. Windows generally doesn’t know what to do with them. When unbounded arrays are used in an external function call, they are not actually empty! When I have encountered an unbounded array in a WinAPI function (refer to the SendInput WinAPI function, for example), you usually have to also supply a parameter that contains the number of elements contained in the unbounded array.
     
  15. Do not attempt to pass a null PB variable or structure member to an external DLL function. Chaos (or errors, at the very least) will likely ensue. Instead, assign a value of the expected datatype where all bits are zero, as this is typically how Windows defines NULL.
     
  16. When interfacing to an in-house or third-party DLL that accepts and/or returns character data (including strings) determine if the DLL expects characters to be encoded in Unicode or in ANSI, and code the EFD accordingly.
     
  17. When interfacing to an in-house or third-party DLL, verify that the DLL functions have been exported using the __stdcall calling convention. PowerBuilder will not be able to successfully interface with the DLL unless the DLL has been created using this calling convention.

  18. In rare instances, you may find that you need to populate a structure with the memory address of another structure or the address of some other morsel of information. PowerBuilder takes great pains to hide its inner workings, so you have to not only get "outside of the box" to obtain the memory address of a structure or variable, you have to get outside of PowerBuilder.

    One solution is to create a DLL in Visual Studio which contains a function that takes an argument that is a pointer to anything ("* VOID" in C/C++) and returns that address as an unsigned, 32-bit integer. In the PowerBuilder external function declaration, you can pass any datatype as an argument by reference and the function returns an UnsignedLong, which you can subsequently place into the structure. Of course, this will work only for 32-bit PB applications. For 64-bit applications, you would need to create an alternate version of the DLL that accepts a 64-bit memory address and returns that address as a LongLong.

    I have a very small Visual Studio project that produces a 32-bit DLL that I will make available to anyone that wants it. Email me via jfauss-at-cox-dot-net and mention you would like the AddressOf32 DLL project.

  19. You might not think it possible, but the online Windows API documentation may not be correct. It does not happen very often, but it can happen!

    One known example is the WindowFromPoint API function. The documentation states that a POINT structure is to be passed by value to the function, but if you do so, a runtime error occurs. What you need to do to get this API function to successfully interface with PB is to instead pass two PB Long values (exactly what a POINT structure contains) by value.

    If all else fails, search the web for code samples that invoke the API function. You probably will not find any PowerBuilder code samples, but any working code sample will show you the argument parameters. Posting a question on the Appeon Community Q&A forum never hurts, either.
     

Resources

I have a top-level bookmark menu in my browser named “Win API”. Here are the URL’s I’ve found to be most helpful:

  • The Windows API Index – Online documentation about WinAPI functions and structures. The pages that describe a particular WinAPI function nearly always names the system DLL that contains the function, and you need to know this in order to code the EFD. That piece of information is typically located near the end of the help topic.

    https://docs.microsoft.com/en-us/windows/win32/apiindex/windows-api-list
     
  • Windows datatypes – Online documentation about Windows type definitions.

    https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types
     
  • Roland Smith’s terrific website contains dozens (almost 50, at last count) of free PowerBuilder sample applications, nearly all of which interface with the WinAPI. These sample applications interface with a combined total of over 200 different external functions (yes, I actually counted them). This is a great place to turn to when you want to see how to call a WinAPI function from PB. Very highly recommended.

    https://www.topwizprogramming.com/
     
  • List of Windows Message ID’s. Want to find the numeric value of WM_MOUSEMOVE (or any other Windows Message ID)? Here’s where I go to find it:

    https://wiki.winehq.org/List_Of_Windows_Messages
     
  • Well-known PowerBuilder expert and sage Chris Pollach has recently announced a new release of the free Software Tool & Die, Inc. (STD) Integrated Foundation Class library framework, which now includes EFD’s for over 1,500 WinAPI functions and other “goodies” designed to help you interface your PowerBuilder application to the Windows O/S. You can find out more here:

    http://chrispollach.blogspot.com/2020/05/techtip.html
     

Appeon’s PowerBuilder documentation (which you can access online) contains helpful information about how to define and use external functions. Here are two documents and the drill-down to the pertinent information:

  • Application Techniques:

    Program Access Techniques à Using External Functions and Other Processing Extensions à Using External Functions
     

https://docs.appeon.com/appeon_online_help/pb2019r2/application_techniques/ch24s01.html
 

  • PowerScript Reference:

    Declarations à Declaring External Functions
     

https://docs.appeon.com/appeon_online_help/pb2019r2/powerscript_reference/ch03s04.html
 

The Memory Size of Standard PB Datatypes

What is the memory size of a Boolean in PB? A Double? It would be nice if PB provided a SizeOf operator similar to C/C++, particularly for use with structures.

Over on the Windows side of the aisle, what is the memory size of a WORD in the WinAPI? An LPARAM (Long message parameter)?

Type definitions have run amuck in the WinAPI. A primary reason for using type definitions is to convey context and categorization to data values (to which I applaud) and if I coded in the WinAPI extensively I likely would not have trouble remembering the underlying datatype of a DWORD, for example. But I do have trouble remembering, and you might struggle with this, also, so the tables in this section and in the next section can help.

The following table lists the memory size (in bytes) of the standard PB datatypes. Any PB datatype that does not have a standard WinAPI datatype equivalent is noted.

Standard PB Datatype

Size (Bytes)

Equivalent WinAPI Datatype

Note

Any (as a variable)

8 or 12

n/a

An internal PB structure. Its size depends on the target compilation bitness. When used in a structure, an Any assumes the size and characteristics of the argument parameter it represents. No WinAPI equivalent.

Blob

4 or 8

BYTE array

An array of type BYTE (i.e., a pointer), so the size depends on the target compilation bitness.

Boolean

2

BOOL

BOOL is a 4-byte integer in WinAPI, but a PB Boolean is 2 bytes. PB automatically promotes/demotes when passing as an argument.

Byte

1

BYTE

 

Char or Character

2

WCHAR

Unicode character. "W" stands for "wide", i.e., Unicode.

Date

4 or 8

-none-

A pointer to an internal PB structure, so the size depends on the target compilation bitness. No WinAPI equivalent.

Datetime

4 or 8

-none-

A pointer to an internal PB structure, so the size depends on the target compilation bitness. No WinAPI equivalent.

Decimal

8

-none-

No WinAPI equivalent.

Double

8

double (C/C++)

Internal bit layout may differ. No WinAPI equivalent.

Integer / Int

2

SHORT

 

Long

4

INT or LONG

 

LongLong

8

LONGLONG

 

Longptr

4 or 8

LONG_PTR

Signed 32-bit or 64-bit integer. Its size depends on the target compilation bitness.

Real

4

FLOAT

Internal bit layout may differ. Rarely used in Windows.

String

4 or 8

LPWSTR

An array of Unicode characters (i.e., a pointer), so the size depends on the target compilation bitness.

Time

4 or 8

-none-

A pointer to an internal PB structure, so the size depends on the target compilation bitness. No WinAPI equivalent.

UnsignedInteger or UnsignedInt or UInt

2

USHORT or WORD

 

UnsignedLong or ULong

4

DWORD

 

 Common Windows API to PowerBuilder Datatype Conversions

The online documentation that describes the various type definition datatypes used in the WinAPI is the authoritative source for this information. Even though I listed the URL above, for convenience I've included much of that list here:

https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types

The following table lists the PowerBuilder (version 12.6 and later) equivalent for the some of the most common WinAPI datatypes:

Windows API Datatype

Equivalent PB Datatype

Description

BOOL

Boolean

Boolean (True/False) value. A 4-byte integer in Windows. A PB Boolean is 2 bytes.

BOOLEAN

Byte

Boolean (True/False) value. An 8-bit, unsigned integer. **NOTE** - Not the same datatype as BOOL, but also used for True/False values.

BYTE

Byte

An 8-bit, unsigned integer.

BYTE Array (block of memory)

Blob

A block of memory is typically referenced by a pointer in Windows.

CONST **

Constant **

C/C++ directive that denotes a variable whose value is to remain constant during execution.
(** = NOT a datatype)

double (C/C++)

Double

Basic C/C++ language datatype – Not a WinAPI-defined type definition.

DWORD

UnsignedLong (ULong)

A 32-bit unsigned integer.

FLOAT

Real

Internal bit-pattern may differ. Rarely used in Windows.

HANDLE

Longptr

A generic handle to an undetermined object.

HBITMAP

Longptr

A handle to a bitmap.

HBRUSH

Longptr

A handle to a brush.

HDC

Longptr

A handle to a device context.

HFILE

Longptr

A handle to a file.

HFONT

Longptr

A handle to a font.

HICON

Longptr

A handle to an icon.

HINSTANCE

Longptr

A handle to an instance (same as HMODULE today).

HMENU

Longptr

A handle to a menu.

HMODULE

Longptr

A handle to a module (same as HINSTANCE today).

HMONITOR

Longptr

A handle to a display monitor.

HRESULT

Long

A return code used by COM interfaces (this is not a Windows handle, despite how it is named).

HWND

Longptr

A handle to a "window". Keep in mind all visual controls in WIndows are actually implemented as sub-classed windows.

INT

Long

A signed 32-bit integer.

INT16

Integer (Int)

A signed 16-bit integer.

INT32

Long

A signed 32-bit integer.

INT64

LongLong

A signed 64-bit integer.

INT8

-none-

A signed 8-bit integer. Note: The PB Byte data type is an unsigned 8-bit integer.

INT_PTR

Longptr

A signed integer (32-bit or 64-bit, depending on process bitness). Used for pointer arithmetic.

LANGID

Integer (Int)

A language identifier.

LONG

Long

A signed 32-bit integer.

LONGLONG

LongLong

A signed 64-bit integer.

LONG_PTR

Longptr

A signed integer (32-bit or 64-bit, depending on process bitness). Used in Windows for pointer arithmetic.

LONG32

Long

A signed 32-bit integer.

LONG64

LongLong

A signed 64-bit integer.

LPARAM

Longptr

A Windows event message parameter.

LPCSTR

Longptr

A pointer to a constant, null-terminated string of ANSI characters.

LPCVOID

Longptr

A pointer to a constant of undetermined type.

LPCWSTR

Longptr

A pointer to a constant, null-terminated string of Unicode (“Wide”) characters.

LPVOID

Longptr

A pointer to a value of undetermined type.

LPDWORD

Longptr

A pointer to a DWORD (32-bit unsigned integer)

LPWORD

Longptr

A pointer to a WORD (16-bit unsigned integer).

LPWSTR

Longptr

A pointer to a null-terminated string of Unicode (“Wide”) characters.

LRESULT

Longptr

A signed result of message processing.

PVOID

Longptr

A pointer to a value of undetermined type.

QWORD

-none-

An unsigned 64-bit integer. Note: The PB LongLong data type is a 64-bit signed integer.

SHORT

Integer (Int)

A signed 16-bit integer.

UINT

UnsignedLong (ULong)

An unsigned 32-bit integer.

ULONG

UnsignedLong (ULong)

An unsigned 32-bit integer.

ULONGLONG

-none-

An unsigned 64-bit integer. Note: The PB LongLong data type is a 64-bit signed integer.

USHORT

UnsignedInt (UInt)

An unsigned 16-bit integer.

VOID

Any

A value of undetermined type. Means “returns nothing” when describing a function’s return value.

WCHAR

Character (Char)

A Unicode (“Wide”) character.

WORD

Integer (Int)

A 16-bit unsigned integer.

WPARAM

Longptr

A Windows event message parameter.

Tutorial Summary

We've learned how to code External Function Declarations (EFD’s) in PowerBuilder, as these are necessary in order to call exported entry points in “external” (to PowerBuilder) dynamic link libraries such as the WinAPI functions and third-party DLL’s. The means for invoking external functions using either Unicode or ANSI character/string encoding was described. ANSI encoding was included because DLL’s from third-party vendors may not support Unicode. We saw that passing argument values by reference to WinAPI functions is common practice, particularly when an argument is a structure, because a passing a pointer (memory address) can be much more efficient than passing the structure data (by value) through the call stack.

In part two the topic of boundary alignment was introduced; the reason it exists and the 8-byte alignment convention used by Windows and by PowerBuilder. Fundamental differences between Windows’ 32-bit and 64-bit environments and how the Longptr datatype in PB helps to mitigate those differences were covered. The effect of boundary alignment and 32/64-bit differences on structure layout and size were examined because some WinAPI functions require the caller to accurately specify structure length. Single-byte (or 1-byte) alignment was described as an alternative that some non-Windows DLL’s utilize. The optional progma_pack( 1 ) directive in an EFD allows PB to interface with DLL’s that uses 1-byte structure packing alignment. WinAPI functions all use progma_pack( 8 ), or eight-byte boundary alignment, which is what PB uses by default.

The third installment of the tutorial focused on several nuances of the use of structures in the interface between PB and Windows and in particular, how PB null variables and the PB Any datatype can adversely affect this interface. Although the Any datatype can be used in WinAPI function calls in a limited manner and with considerable care, we examined the potential dangers of using Any’s and described the circumstances where using Any’s simply does not work.

Finally, several coding tips and techniques were listed and mapping tables for translating between many of the common WinAPI datatypes and standard PowerBuilder datatypes were presented.

Acknowledgements

I’m very grateful to Armeen Mazda and everyone at Appeon for rescuing and resuscitating a languishing yet unparalleled product named PowerBuilder. You are the reason the future for RAD looks bright!

Roland Smith does many great things for the PowerBuilder community. He graciously gave me permission to post the source code for the free sample application that accompanies this tutorial, since it is based on his “SizeOf” code sample he makes available to all on his TopWizProgramming.com web site. Many thanks, Roland!

Andrew Barnes cast his critical eyes on the initial version of this tutorial and graciously offered his observations and suggestions for improving the content. Several of the revisions I've made are a direct result of his involvement. Thank you, Andrew!

I’m especially indebted to Chris Pollach, who used some of his valuable time and energy to review the original version of this tutorial and improve it with excellent corrections and suggestions. Thank you, Chris!

Revisions

The following changes have been made to the original version of Part Four:

  • A tip describing how to populate a structure with the address of a structure or variable has been added.

  • A tip mentioning the remote possibility that the online Windows API documentation may contain errors has been added.

  • The equivalent PB datatype of the WinAPI's WPARAM type definition has been corrected.

  • Andrew Barnes contributions to the improvement of the content in the tutorial are acknowledged.

  • Some minor word omissions, grammatical errors and other miscellaneous corrections have been made.
Comments (2)
Thursday, Jun 17 2021

Excellent series of articles, thanks a lot, John.

There is one bit I would like to mention: although Windows uses 4 and 8 Bytes for handles in 32/64 respectively, it uses in fact only the 32 Bit portion of it even in 64 Bit Windows: https://docs.microsoft.com/en-us/windows/win32/winprog64/interprocess-communication.

I first thought that this piece of information might lead to not having to change Long to LongPtr in EFDs for 64 Bit support, but on second thought this might lead to some nasty memory overwriting issues.

0
Thursday, Jun 17 2021

Thank you, Roland, for taking the time to pass along your kind words and comment. I had not seen the inter-process communication topic until you pointed it out, and what it describes make sense.
Using the PB Long or ULong datatypes for WinAPI handle-related argument values in a 64-bit process seems to work ok in many cases. but I don't advocate doing it. I'm strong proponent of using the proper datatype, and using Longptr makes the topic a non-issue, so it makes perfect sense (to me, at least) to use it in both 32-bit and 64-bit code. Then, over time, repeated usage becomes a habit.
One area where using 32-bit integer datatypes in a 64-bit process is more likely to result in problems is when the WinAPI handles are contained in a structure, because the potential impact of differences in the alignment of structure members in memory and the overall length of the structure can be significant. As you imply, "bad things" may happen.

0

Find Articles by Tag

Elevate Conference Database Table Data Service UI Modernization 64-bit iOS MessageBox Migration Icon Export JSON .NET DataStore OLE PowerBuilder Compiler SDK SqlModelMapper BLOB Azure Installation Outlook Model TreeView RibbonBar Mobile DataWindow JSON Data DataWindow .NET Std Framework Transaction IDE TLS/SSL SQL Server DevOps CoderObject Database Connection Design Database Object ODBC Branch & Merge Oracle RESTClient Web API Debug PowerBuilder (Appeon) PDF .NET Assembly Text CrypterObject DLL C# Window Event Handler Database Table Schema SqlExecutor UI Themes Database Profile Windows OS Event SQL TFS Charts External Functions Repository Windows 10 Icons Class PostgreSQL Event Handling SnapObjects Encryption Web Service Proxy Filter Error RichTextEdit Control TortoiseGit Debugging JSONGenerator HTTPClient 32-bit Database PFC Jenkins Resize Trial PowerServer Web JSON Sort Import Deployment DragDrop Syntax WinAPI PowerServer Mobile SnapDevelop Interface PostgreSQL ODBC driver PBDOM License NativePDF Application Configuration Authorization DataType Database Table Linux OS Source Code COM Database Painter OAuth Stored Procedure JSONParser Import JSON Performance UI File Android Variable XML OAuth 2.0 Source Control Graph Authentication RibbonBar Builder PowerScript (PS) Messagging PowerBuilder Script InfoMaker Bug Encoding SOAP OrcaScript GhostScript Open Source Excel SVN PDFlib Git WebBrowser Expression CI/CD Platform ActiveX REST Export Validation Testing Debugger Array API Automated Testing Menu