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.

RegisterEventSourceA

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 definition
FUNCTION 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

example2
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:

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:

lStructSize-32


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:

lStructSize-64


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:

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:

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.

Finding DLL interface prototype


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.

Quickly determining the base type of some commonly used extended types in C++


 

1
1