Prototyping Windows SDK calls to PowerBuilder API calls

Calling windows functions from within PowerBuilder used to be somewhat painful but over the years it has become easier and the mapping between datatypes more well defined.  This excludes functions with a callback (having “Callback” as a datatype) that cannot be performed by PowerBuilder.  There are a few simple steps in converting a SDK call to a PowerBuilder API call, irregardless of  PowerBuilder version.  There are a few inconsistencies and gotchas to watch out for that are listed in a troubleshooting section.

Step 1: Get the Microsoft syntax for the SDK call to be performed in PowerBuilder.

   Get the syntax that would be used in a Microsoft Windows program from the MSDN (Microsoft Developers Network).

 Step 2:  Determine whether the Windows SDK call is a function or subroutine

 Function calls return a value and subroutines do not so if the call starts with “void” rather than a datatype it is a subroutine.  If it starts with a datatype then it is a function.  Here are examples of both.

 Microsoft Function: 

     BOOL GetFileVersionInfo( LPTSTR lptstrFilename, DWORD dwHandle, DWORD dwLen, LPVOID lpData );

 Microsoft Subroutine: 

   VOID GlobalMemoryStatus(LPMEMORYSTATUS lpBuffer);

Step 3: Converting the datatypes from Microsoft to PowerBuilder.

 
  MICROSOFT PowerBuilder
 Bool Boolean
Char* Ref String
Colorref Ulong
Dword Ulong
Handle Ulong
Hdc Ulong
Hfile Ulong
Hinstance Ulong
Hwnd Ulong
Int Int
Lparm Ulong
Lpbyte Ref Long
Lpbword Ref Ulong
Lpstr,Lpststr Ref String
Lpfiletime Ref Time
Lpint Ref Long
Lpvoid Ref Struct struct_inst
Mcierror Long
Pbyte Reg Long[#]
Short Int
Structure Ref Struct Struct_inst
Uint Uint
Void** SUBROUTINE
Word Ulong
Wparam Ulong

*If the word “Callback appears as a datatype, it cannot be performed by PowerBuilder. Callback routines are functions that are called from within functions.

Step 4: Coding the Global/Local External Function:

Generally speaking you should be able to swap Microsoft data types with PowerBuilder data types.  If you see LPCTSTR, that means pointer to a string, in PowerBuilder use the “ref” keyword before declaring the string to pass it by reference (pointer).  If you see LPVOID, that means a structure is being used so you should create a similar structure in PowerBuilder and assign it to an instance of structure by reference.  You must also get the DLL name where the function exists.

Pointers & Structures

LPCTSTR = Pointer to a string, handled in PowerBuilder by using the “ref” keyword.

LPVOID = Structure, handled in PB by creating a structure and passing an instance by reference.

Example that uses both a pointer to a string (LPCTSTR) and a structure (LPVOID) with the matching sections color coded.  The additional “A” at the end of the function differentiates between (A)nsi and (W)ide Unicode format, MSDN doesn’t always list the proper names for the functions (e.g. GetFileVersionInfo is really GetFileVersionInfoA)

Microsoft:

BOOL GetFileVersionInfo( LPTSTR lptstrFilename, DWORD dwHandle, DWORD dwLen, LPVOID lpData );

     Equivalent PowerBuilder:

FUNCTION boolean GetFileVersionInfoA(ref string filename, ulong f_handle, ulong f_length, ref lpdata lpdata2) LIBRARY “version.dll”

Step 5: Creating a Structure

In this particular example a structure is needed so you’ll need information on what elements are contained within this structure from MSDN.

In the MSDN the structure appears like this:

     LPDATA = { DWORD dwSignature ; DWORD dwStrucVersion ; DWORD dwFileVersionMS ; DWORD dwFileVersionLS ; DWORD
dwProductVersionMS ; DWORD dwProductVersionLS ; DWORD dwFileFlagsMask ; DWORD dwFileFlags ; DWORD dwFileOS ;
DWORD dwFileType ; DWORD dwFileSubtype ; DWORD dwFileDateMS ; DWORD dwFileDateLS }

     In PB you would go into the structure painter and in this particular case all of these elements would be converted to ULONG. If one of the  elements within the structure was a nested structure or callback routine you would not be able to use this function within PB. In that case  the only option would be to create a C DLL that makes the function call and call it from PB.

Step 6: Scripting the Function Call.

After declaring the function or subroutine via “Local External Functions” or “Global External Functions“, you can use the function in PowerScript using the name of the function or subroutine.  If you’d rather use a different name you can ALIAS the function in the declaration by adding ALIAS “myPBFunctionName” and use the aliased name instead.

First you’ll need to declare the datatypes used in the function, the variable names you use do not matter and do not need to match the declaration.  If you run into problems you might want to try initializing the variables to allocate space prior to calling the function.  When calling external SAP BAPI functions it is necessary to pre-allocate space in the variables so if your string could return up to 100 characters you’d assign 100 chars of space by doing something like ls_variable = Space(100).  If the MSDN or function documentation indicates a variable is not used, or is reserved for future use, you may need to set it to null via SetNull function.

     boolean lb_rtn // Return code
string ls_filename // 1st argument – filename
ulong lu_hand // 2nd argument – f_handle
ulong lu_len // 3rd argument – f_length
lpdata lpdata2 // Last argument – assigning an instance of a structure.

Next is the hardest part and that is assigning values to the arguments. This part may require use of the MSDN or whatever  reference is available that covers the function you are calling. In this particular case the information is contained within the MSDN.    The first argument ” ls_filename “, should be set to the path and filename of the target file.

     The final script will appear as follows:

     boolean lb_rtn
string ls_filename
ulong lu_hand, lu_len
lpdata lpdata2   // the structure you created
ls_filename = “c:\windows\calc.exe” // The calculator would be a good file to test against.
setnull(lu_hand)   // not used according to MSDN so set to null to be safe
lu_len = 256   // the memory usage for the structure, 13 elements of ulong, so 256 should enough to prevent GPF
lb_rtn = GetFileVersionInfoA(ls_filename, lu_hand, lu_len, lpdata2)

     // Viewing output which returned via the structure ————–
sle_1.text = string(lpdata2.dwSignature)
sle_2.text = string(lpdata2.dwStrucVersion)
sle_3.text = string(lpdata2.dwFileVersionMS)
sle_4.text = string(lpdata2.dwFileVersionLS)
sle_5.text = string(lpdata2.dwProductVersionMS)
sle_6.text = string(lpdata2.dwProductVersionLS)
sle_7.text = string(lpdata2.dwFileFlagsMask)
sle_8.text = string(lpdata2.dwFileFlags)
sle_9.text = string(lpdata2.dwFileOS)
sle_10.text = string(lpdata2.dwFileType)
sle_11.text = string(lpdata2.dwFileSubtype)
sle_12.text = string(lpdata2.dwFileDateMS)
sle_13.text = string(lpdata2.dwFileDateLS)
Messagebox(“Return Code”, string(lb_rtn))
// ————————————————————————–
 

Troubleshooting Section

There are some thing so watch for when calling Windows SDK functions.

  • Some functions are case sensitive, so it is best to use exactly what is listed in MSDN. (i.e. FindWindowa will not work, you must use FindWindowA)
  • Be sure to use ULONG for all all handles in PowerBuilder, you might have success using a long or int, but only if the function happens to be in low memory on that computer.
  • Double check the function name, and search the internet to find out if the function should have a trailing “A” or “M”.
  • Declare the function globally if it needs to be referenced throughout the application. (common sense)

Error messages from calling Windows SDK functions in PowerBuilder

1. Error: Error opening DLL library <filename> for external function at line <line_number> in the <event name> event of object
<object_name> of <window_name>.

Possible causes: 
> DLL is 16 bit and thus incompatible.
> DLL is not in a searchable directory.
> DLL connects to another DLL that cannot be found.
> DLL has the same name as another already loaded into memory.
> DLL is corrupted or in an incompatible format.

2. Error: Error calling external function <function_name> at line <line_number> in the <event name> event of object <object_name> of  <window_name>.

This is probably the result of spelling the function wrong. Be sure to verify that the function name matches what is in the DLL exactly including the case of the letters.

3. Error: Specified argument type differs from required argument type at runtime in DLL function <function_name>. (Invalid stack pointer
on return from function call) at line <line_number> in <event_name> event of object <object_name> of <window_name>.

This error usually indicates the datatypes do not match what the DLL function is expecting.

4. Receiving garbage from the DLL. i.e. Last name populated as: “*#^&Ryan”

This problem is most likely the result of the byte alignment being set incorrectly. PowerBuilder expects the byte alignment to be set to one and if you are using MSVC++ compiler the default setting is four. To set the byte alignment to one you would need to do the following prior  to compiling to a dll.

– Select the desired target
– Right mouse click and select Settings
– Select the C/C++ tabpage
– Select Code Generation from the Category dropdown list
– Select desired byte alignment from the Struct Member Alignment dropdown list.

The DOS switch to set the byte alignment to one is: /zp1

Tip for finding functions quickly

1. On Win NT 4.0 click on the START button and select “Find”, then “Files or Folders”. (“Search”, then “For Files or Folders” on Win 2000)
2. In the SLE entitled “Named” enter “c:\*.dll”. If this is a Windows DLL that you’ll be calling enter “c:\windows\*.dll”.
3. Click on the “Advanced” tab and in the “Containing Text” SLE enter the exact function name you are looking for. For example:   FindWindowA
4. There will usually be a lot of DLL’s that contain the function you are looking for but try to use the main Windows DLL’s whenever  possible since they are already loaded into memory.

 

 

Tags:

One response

  1. Hi
    I need to know how to access the Windows 8.1 tablet API functions from the PowerBuilder. I Need to access the windows table Camera from powerbuilder application. Kindly help me for this problem.

    Regards
    Haifourraj

Leave a Reply

Your email address will not be published. Required fields are marked *