1 Background
In order to enhance product security, the compiler used to build PowerBuilder IDE has been upgraded from VS2010 to VS2022. In VS2010, 64-bit programs generally do not use the upper 32 bits, while in VS2022, the upper 32 bits are used. This change will cause the declaration of external extension functions callable by 64-bit programs in PB2019 to fail when called on PB2022. The issue can be resolved by modifying the parameter types in the declaration.
2 PB code change example:
2.1 Parameters or return value in external function calls that need to be modified to LongPtr type
1. The original PB API is defined as types Long/uLong, with interface prototypes defined as pointer types for parameters or return values.
2. The original PB API is defined as Long/uLong, with interface prototypes defined as 64-bit data type parameters or return values.
3. In the interface prototypes those that are defined as Handle type.
4. For an interface parameter that is not easy to identify, check its description. If it is said to be an address, you can try converting it to longptr to try it out.
Example 1: Handle and pointer type conversion
32-bit DLL declaration
FUNCTION uLong RegisterEventSource ( uLong lpUNCServerName, string lpSourceName ) Library "ADVAPI32.dll" Alias For "RegisterEventSourceW"
64-bit DLL declaration (32/64 bit compatible)
FUNCTION Longptr RegisterEventSource ( Longptr lpUNCServerName, string lpSourceName ) Library "ADVAPI32.dll" Alias For "RegisterEventSourceW"
PowerScript change
String ls_message_source Longptr lul_event_handler lul_event_handler = RegisterEventSource (0, ls_message_source )
Modification points
a. The return value type is found to be HANDLE in Microsoft official website. API function prototype is as shown in the screenshot below. Therefore, we modify the return value type to Longptr.
b. The parameter lpUNCServerName is of type LPCSTR pointer, so modify the type to Longptr.
c. The variable receiving the return value of the interface call: lul_event_handler is defined using Longptr.
Example 2: For a parameter that is not easy to identify, if based on the description, when the parameter is of address type, you can try using longptr to define it. As shown in the screenshot below, the parameter pidlRoot in the structure LPBROWSEINFOW is of address type. Therefore, try to modify it to longptr type and verify if it works.
PB code
Interface definitionFUNCTION Longptr SHBrowseForFolder ( str_LPBrowseINFOW LPBrowse ) LIBRARY "SHELL32.dll" ALIAS FOR "SHBrowseForFolderW"PB Call Interface Script
String ls_Title = "Browser Folder" Longptr ll_PIDL str_LPBrowseINFOW lstr_LPBrowse w_genapp_sheet1 l_win l_win = parentwindow() lstr_LPBrowse.HwndOwner = handle(l_win) lstr_LPBrowse.pidlRoot = 0 setnull(lstr_LPBrowse.pidlroot) lstr_LPBrowse.pszDisplayName = Space(256) lstr_LPBrowse.lpszTitle = ls_Title lstr_LPBrowse.ulFlags = 112 ll_PIDL = SHBrowseForFolder(lstr_LPBrowse) PB Structure:str_LPBrowseINFOW definition: global type str_LPBrowseINFOW from structure longptr HwndOwner longptr pidlRoot string pszDisplayName string lpszTitle integer ulFlags longptr lpfn longptr lParam integer iImage end type
2.2 Win API interface with regard to structure size
Origin of the problem:
When calling WIN API interfaces in PB, if the interface contains structures, it required to define the structures in PB.
For example, the interface ChooseFont() in Windows:
Declaration: `Function boolean ChooseFont ( &Ref CHOOSEFONT lpcf ) Library
"comdlg32.dll" Alias For "ChooseFontA;Ansi"`
Definition of the structure CHOOSEFONT:
Because the first parameter lStructSize needs to obtain the size of the structure, it is necessary to calculate the size of this structure first. However, in C++, the size of pointers in 32-bit and 64-bit programs is different, resulting in inconsistent size of the structure. Therefore, it is necessary to distinguish it here. For example, the size of the structure in a 32-bit structure is 60, detailed as follows:
In a 64-bit program, the size of the structure differs between default byte alignment and byte alignment by one. Default alignment actually aligns to 8 bytes, resulting in a size of 104. However, with byte alignment by one, the size is simply the sum of all data sizes, resulting in a size of 92 as shown below:
Currently, PB uses the default byte alignment, so the corresponding structure field lStructSize needs to be set to 104 on a 64-bit system.
2.2.1 PB code example that compatible for 32/64-bit call
Struct Definition:
DLL function declaration
Function boolean ChooseFont (Ref CHOOSEFONT lpcf ) Library "comdlg32.dll" Alias For "ChooseFontA;Ansi"
Original:
// initialise the structure further lstr_ChooseFont.lstructsize = 60 lstr_ChooseFont.hwnd = Handle(aw_parent) lstr_ChooseFont.flags = CF_SCREENFONTS+CF_INITTOLOGFONTSTRUCT+CF_EFFECTS If Not ChooseFont(lstr_ChooseFont) Then GlobalUnlock(ll_LogFont) GlobalFree(ll_LogFont) Return False End If
After the change:
environment env // initialise the structure further //Check if the current application is 32/64-bit ll_return = GetEnvironment ( env ) IF ll_return <> 1 THEN RETURN false CHOOSE CASE env.ProcessBitness CASE 32 lstr_ChooseFont.lstructsize = 60 CASE 64 lstr_ChooseFont.lstructsize = 104 CASE ELSE RETURN false END CHOOSE lstr_ChooseFont.hwnd = Handle(aw_parent) lstr_ChooseFont.flags = CF_SCREENFONTS+CF_INITTOLOGFONTSTRUCT+CF_EFFECTS // call the dialog If Not ChooseFont(lstr_ChooseFont) Then GlobalUnlock(ll_LogFont) GlobalFree(ll_LogFont) Return False End If
3 Explanation of some parameter types for C++ DLL interface
https://learn.microsoft.com/en-us/windows/win32/winprog64/programming-guide-for-64-bit-windows
https://learn.microsoft.com/en-us/cpp/build/common-visual-cpp-64-bit-migration-issues?view=msvc-170
3.1 C++ ILP32 and LP64 Data Models
In a 32-bit environment, the "ILP32" data model is used because C data types are 32-bit int, long, and pointer. In contrast, in a 64-bit environment, different data models are used, where long and pointers are 64-bit, thus referred to as the "LP64" data model. The following table outlines common type size comparisons:
From the table above, we can see that the main task of porting from 32-bit to 64-bit is to deal with various issues caused by changes in length. Many correct operations on a 32-bit platform are no longer valid on a 64-bit platform.
3.2 Finding DLL interface prototype
Typically, a C++ library file will come with a header file, as shown in the image below. Opening this fml.h file will generally contain the prototype definitions of the interfaces. Based on these interface prototypes, we can analyze and confirm whether the parameter types are 64-bit data types.
3.3 Quickly determining the base type of some commonly used extended types in C++
As an example, you can find the original definition of DWORD as an unsigned long, which is a 64-bit data type.