In order to improve product security, the compiler has been upgraded from Visual Studio 2010 to Visual Studio 2022. 64-bit programs in Visual Studio 2010 will basically not use the upper 32 bits which Visual Studio 2022 will use. This change will cause the definition of external functions that can be called by 64-bit programs in PowerBuilder 2019 fails to be called in PowerBuilder 2022 and later. This section will show you how to modify the definition parameter type to avoid this type of problem.
For example, in the following scenario, we will need to modify the data type of the parameter or return value to LongPtr in the external function.
#1: The original PowerBuilder API is defined as Long/uLong Integer type, the interface prototype is defined as a pointer type parameter or return value.
#2: The original PowerBuilder API is defined as Long/uLong Integer type, the interface prototype definition type is a 64-bit data type parameter or return value.
#3: The type defined in the interface prototype is the Handle type.
Here is a code example:
32-bit DLL function reference definition
FUNCTION uLong RegisterEventSource ( uLong lpUNCServerName, string lpSourceName ) Library "ADVAPI32.dll" Alias For "RegisterEventSourceW"
64-bit DLL function definitions, compatible with 32/64 bits
FUNCTION Longptr RegisterEventSource ( Longptr lpUNCServerName, string lpSourceName ) Library "ADVAPI32.dll" Alias For "RegisterEventSourceW"
PowerScript
String ls_message_source
Longptr lul_event_handler
lul_event_handler = RegisterEventSource (0, ls_message_source )
Areas to modify:
a. The RegisterEventSource API function prototype can be found on the Microsoft official website and the return value type is HANDLE, as shown below. Therefore, the return value type must be modified to Longptr.
b. The lpUNCServerName parameter is an LPCSTR pointer type, as shown below, therefore, the type must be modified to Longptr.
c. The lul_event_handler variable for the interface return value must be defined as Longptr type.
When calling the Windows API interface in PowerBuilder, if the interface contains a structure, you will need to calculate the size of the structure and define the structure in PowerBuilder.
For example, ChooseFont () in Windows
Declaration:
Function boolean ChooseFont ( &Ref CHOOSEFONT lpcf ) Library "comdlg32.dll" Alias For "ChooseFontA;Ansi"
Structure CHOOSEFONT:
typedef struct tagCHOOSEFONTA { DWORD lStructSize; HWND hwndOwner; // caller's window handle HDC hDC; // printer DC/IC or NULL LPLOGFONTA lpLogFont; // ptr. to a LOGFONT struct INT iPointSize; // 10 * size in points of selected font DWORD Flags; // enum. type flags COLORREF rgbColors; // returned text color LPARAM lCustData; // data passed to hook fn. LPCFHOOKPROC lpfnHook; // ptr. to hook function LPCSTR lpTemplateName; // custom template name HINSTANCE hInstance; // instance handle of.EXE that // contains cust. dlg. template LPSTR lpszStyle; // return the style field here // must be LF_FACESIZE or bigger WORD nFontType; // same value reported to the EnumFonts // call back with the extra FONTTYPE_ // bits added WORD ___MISSING_ALIGNMENT__; INT nSizeMin; // minimum pt size allowed & INT nSizeMax; // max pt size allowed if // CF_LIMITSIZE is used } CHOOSEFONTA;
Since the first parameter lStructSize needs to obtain the size of the structure, it is necessary to calculate the size of the structure first. In C++, the sizes of 32-bit and 64-bit program pointers are different, resulting in different sizes of structures.
The size of the above 32-bit structure is 60. The details are as follows:
DWORD lStructSize; 4 HWND hwndOwner; 4 HDC hDC; 4 LPLOGFONTA lpLogFont; 4 INT iPointSize; 4 DWORD Flags; 4 COLORREF rgbColors; 4 LPARAM lCustData; 4 LPCFHOOKPROC lpfnHook; 4 LPCSTR lpTemplateName; 4 HINSTANCE hInstance; 4 LPSTR lpszStyle; 4 WORD nFontType; 2 // bits added WORD ___MISSING_ALIGNMENT__; 2 INT nSizeMin; 4 INT nSizeMax; 4
The size of the 64-bit structure is different between the 8-byte alignment and the 1-byte alignment. For 8-byte alignment, the size is 104, and for 1-byte alignment, the size is 92.
DWORD lStructSize; 4 HWND hwndOwner; 8 HDC hDC; 8 LPLOGFONTA lpLogFont; 8 INT iPointSize; 4 DWORD Flags; 4 COLORREF rgbColors; 4 LPARAM lCustData; 8 LPCFHOOKPROC lpfnHook; 8 LPCSTR lpTemplateName; 8 HINSTANCE hInstance; 8 LPSTR lpszStyle; 8 WORD nFontType; 2 // bits added WORD ___MISSING_ALIGNMENT__; 2 INT nSizeMin; 4 INT nSizeMax; 4
In PowerBuilder 12.6 and later, the default byte alignment is 8-byte alignment, therefore, the structure field lStructSize needs to be entered 104 in 64-bit systems.
PowerBuilder code example that is compatible with 32/64-bit DLL instance:
Structure definition:
DLL function reference definition:
Function boolean ChooseFont (Ref CHOOSEFONT lpcf ) Library "comdlg32.dll" Alias For "ChooseFontA;Ansi"
Before modification:
// initialize 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 modification:
environment env // initialize the structure further // determine whether the current app is 32 or 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
In the previous sections, a few examples are given to help explain modifying the data type to be 64-bit compatible, however, in actual usage, it is much more complicated to identify the data type in the interface. This section will guide you on how to locate interface prototype and identify data types.
For more information on how to identify the C++ 64-bit data type, refer to
https://docs.microsoft.com/en-us/windows/win32/winprog64/programming-guide-for-64-bit-windows
https://docs.microsoft.com/en-us/cpp/build/common-visual-cpp-64-bit-migration-issues?view=msvc-170
C++ ILP32 and LP64 data model
The 32-bit environment involves the "ILP32" data model because the C data types are 32-bit int, long, and pointers. The 64-bit environment uses a different data model. At this time, long and pointers are 64-bit, so it is called the "LP64" data model. The following table lists common type size comparisons (this table is ultimately subject to the official version and is just a reference table when writing examples).
From the above table, we can see that the porting work from 32-bit to 64-bit is mainly to deal with various problems caused by length changes. Many correct operations on 32-bit platforms no longer hold true on 64-bit platforms.
Looking for DLL interface prototype
Generally, a C++ library file will be accompanied by a header file, as shown below. When you open this fml.h file, it will generally contain the definition prototype of the interface; we can analyze and confirm whether the parameter type is a 64-bit data type based on the interface prototype.
Looking for base types for common extension types in C++
As shown in the figure below, create a new C++ project, enter an extended type: DWORD, and then press F12, it will jump to the definition of the type. The original definition type of DWORD is Unsigned long in a 64-bit program. It is a 64-bit data type.