Question How CreateProcessA procedure is used in ProTop and Readprobe?

ProTop and Readprobe use CreateProcessA procedure to create new processes on Windows.
But the programs use the following construction to define the types of CreateProcessA procedure's parameters as well as for the functions used to read the memory structures:
Code:
&IF DECIMAL(SUBSTRING(PROVERSION,1,INDEX(PROVERSION,".") + 1)) < 11.3 &THEN
  LONG
&ELSE
  &IF {&PROCESS-ARCHITECTURE} = 64 &THEN
    INT64
  &ELSE
    LONG
  &ENDIF
&ENDIF
Progress knowledgebase has a few articles with the examples how to call the CreateProcessA procedure but they don't use such construction.

What was changes in OpenEdge V11.3?
Why we need to use INT64 instead of LONG on Windows 64-bit but only with newest Progress versions?
Is it about the signed vs unsigned 4-byte integers?
My CreateProcessTest.p (attached to Question - OS-COMMAND and the quotes.) is using only LONG type and it seems to work correctly. Why?

Second question:
CreateProcessA procedure allows to return a PID of newly created process:
Code:
opPID = GET-LONG(hProcessInfo, 9). /* dwProcessId */
My test program allows to compare these PIDs with the real PIDs reported in database log. They do not match:
Code:
[2026-03-16T11:04:55.059+03:00 CreateProcessA (Sync) test # 1
[2026/03/16@11:04:55.186+0300] P-32508      T-34260 I ABL     2: (452)   Login by george on CON:.
[2026/03/16@11:04:55.191+0300] P-32508      T-34260 I ABL     2: (7129)  Usr 2 set name to george.
[2026/03/16@11:04:56.287+0300] P-32508      T-34260 I ABL     2: (453)   Logout by george on CON:.
[2026-03-16T11:04:56.356+03:00 PID: 5664

[2026-03-16T11:04:56.363+03:00 CreateProcessA (Sync) test # 2
[2026/03/16@11:04:56.485+0300] P-51292      T-49412 I ABL     2: (452)   Login by george on CON:.
[2026/03/16@11:04:56.489+0300] P-51292      T-49412 I ABL     2: (7129)  Usr 2 set name to george.
[2026/03/16@11:04:57.596+0300] P-51292      T-49412 I ABL     2: (453)   Logout by george on CON:.
[2026-03-16T11:04:57.646+03:00 PID: 4652

[2026-03-16T11:04:57.653+03:00 CreateProcessA (Sync) test # 3
[2026/03/16@11:04:57.776+0300] P-45612      T-2416  I ABL     2: (452)   Login by george on CON:.
[2026/03/16@11:04:57.781+0300] P-45612      T-2416  I ABL     2: (7129)  Usr 2 set name to george.
[2026/03/16@11:04:58.878+0300] P-45612      T-2416  I ABL     2: (453)   Logout by george on CON:.
[2026-03-16T11:04:58.925+03:00 PID: 2336
Any thoughts why?

Code:
_progres empty -zp
The id of this process is 5868. (1408)
[2026/03/16@13:04:25.378+0300] P-5868       T-37620 I ABL     2: (452)   Login by george on CON:.

prowin empty -zp
The id of this process is 27052. (1408)
[2026/03/16@13:06:06.099+0300] P-27052      T-50184 I ABL     2: (452)   Login by george on CON:.

proserve sports -zp
13:08:05 SERVER   : The id of this process is 38908. (1408)
[2026/03/16@13:08:05.859+0300] P-15496      T-46740 I BROKER  0: (333)   Multi-user session begin.
_progres.exe and prowin.exe connect a database with PIDs given them from a birth (unlike _mprosrv.exe).
 
Thanks, Stefan.

The article says "Change parameter from LONG to INT64" but it does not say which one - some or all?

"Technically, hMonitor should also be an INT64 in 64-bit processes but Windows allows you to use 32-bit values for handles."

Readprobe -> lib/windows.i
Code:
PROCEDURE CreateProcessA EXTERNAL "kernel32.dll":
  &IF DECIMAL(SUBSTRING(PROVERSION,1,INDEX(PROVERSION,".") + 1)) < 11.3 &THEN
    DEFINE INPUT  PARAMETER lpApplicationName    AS LONG.
    DEFINE INPUT  PARAMETER lpCommandline        AS CHAR.
    DEFINE INPUT  PARAMETER lpProcessAttributes  AS LONG.
    DEFINE INPUT  PARAMETER lpThreadAttributes   AS LONG.
    DEFINE INPUT  PARAMETER bInheritHandles      AS LONG.
    DEFINE INPUT  PARAMETER dCreationFlags       AS LONG.
    DEFINE INPUT  PARAMETER lpEnvironment        AS LONG.
    DEFINE INPUT  PARAMETER lpCurrentDirectory   AS LONG.
    DEFINE INPUT  PARAMETER lpStartupInfo        AS LONG.
    DEFINE INPUT  PARAMETER lpProcessInformation AS LONG.
    DEFINE RETURN PARAMETER bResult              AS LONG.
  &ELSE
    &IF {&PROCESS-ARCHITECTURE} = 64 &THEN
      DEFINE INPUT  PARAMETER lpApplicationName    AS INT64.
      DEFINE INPUT  PARAMETER lpCommandline        AS CHAR.
      DEFINE INPUT  PARAMETER lpProcessAttributes  AS INT64.
      DEFINE INPUT  PARAMETER lpThreadAttributes   AS INT64.
      DEFINE INPUT  PARAMETER bInheritHandles      AS INT64.
      DEFINE INPUT  PARAMETER dCreationFlags       AS INT64.
      DEFINE INPUT  PARAMETER lpEnvironment        AS INT64.
      DEFINE INPUT  PARAMETER lpCurrentDirectory   AS INT64.
      DEFINE INPUT  PARAMETER lpStartupInfo        AS INT64.
      DEFINE INPUT  PARAMETER lpProcessInformation AS INT64.
      DEFINE RETURN PARAMETER bResult              AS INT64.
    &ELSE
      DEFINE INPUT  PARAMETER lpApplicationName    AS LONG.
      DEFINE INPUT  PARAMETER lpCommandline        AS CHAR.
      DEFINE INPUT  PARAMETER lpProcessAttributes  AS LONG.
      DEFINE INPUT  PARAMETER lpThreadAttributes   AS LONG.
      DEFINE INPUT  PARAMETER bInheritHandles      AS LONG.
      DEFINE INPUT  PARAMETER dCreationFlags       AS LONG.
      DEFINE INPUT  PARAMETER lpEnvironment        AS LONG.
      DEFINE INPUT  PARAMETER lpCurrentDirectory   AS LONG.
      DEFINE INPUT  PARAMETER lpStartupInfo        AS LONG.
      DEFINE INPUT  PARAMETER lpProcessInformation AS LONG.
      DEFINE RETURN PARAMETER bResult              AS LONG.
    &ENDIF
  &ENDIF
end procedure.
lp* parameters are the pointers. Should we use MEMPTR type?
dCreationFlags is a bitfield (4 bytes). It's an INTEGER.

Code:
procedure spawn:
[snip]
  define variable lpProcessInformation AS MEMPTR.
  SET-SIZE( lpProcessInformation )   = 16.
[snip]
      &IF DECIMAL(SUBSTRING(PROVERSION,1,INDEX(PROVERSION,".") + 1)) < 11.3 &THEN
        PID = GET-LONG( lpProcessInformation, 9 ).
        RUN CloseHandle ( input GET-LONG( lpProcessInformation, 1 ), OUTPUT ReturnValue ).
        RUN CloseHandle ( input GET-LONG( lpProcessInformation, 5 ), OUTPUT ReturnValue ).
      &ELSE
        &IF {&PROCESS-ARCHITECTURE} = 64 &THEN
          PID = GET-INT64( lpProcessInformation, 9 ).
          RUN CloseHandle ( input GET-INT64( lpProcessInformation, 1 ), OUTPUT ReturnValue ).
          RUN CloseHandle ( input GET-INT64( lpProcessInformation, 5 ), OUTPUT ReturnValue ).
        &ELSE
          PID = GET-LONG( lpProcessInformation, 9 ).
          RUN CloseHandle ( input GET-LONG( lpProcessInformation, 1 ), OUTPUT ReturnValue ).
          RUN CloseHandle ( input GET-LONG( lpProcessInformation, 5 ), OUTPUT ReturnValue ).
        &ENDIF
      &ENDIF
lpProcessInformation (PROCESS_INFORMATION) is 4 4-byte fields:
Code:
PROCESS_INFORMATION (processthreadsapi.h)
typedef struct _PROCESS_INFORMATION {
  HANDLE hProcess;
  HANDLE hThread;
  DWORD  dwProcessId;
  DWORD  dwThreadId;
} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;

Will GET-INT64 correctly read 4 bytes of 16?

Is PID on {&PROCESS-ARCHITECTURE} = 64 still an 4-byte integer?

PROVERSION < 11.3 is still unclear.

I seem to miss something important.

I would use:
Code:
&IF {&PROCESS-ARCHITECTURE} = 64 &THEN
 &SCOPED-DEFINE LONG INT64
&ELSE
 &SCOPED-DEFINE LONG LONG
&ENDIF

DEFINE INPUT PARAMETER lpMonitorInfo AS {&LONG} NO-UNDO.
But I would like to know which LONG words in old code should be replaced by {&LONG}.
 
From the code construction I would think that that was when the PROCESS-ARCHITECTURE functionality was added to OpenEdge.

Bingo!
Can I tell whether an OE installation is 32-bit or 64-bit without PROCESS-ARCHITECTURE?
"Can be used in OE versions prior to 11.3, when the PROCESS-ARCHITECTURE function was introduced."

How to programmatically determine bitness in an ABL session running on Windows?
"Progress introduced a 64-bit Windows product in OpenEdge 10.2A. This was a Server-only product that included a 64-bit character (TTY) client. A 32-bit GUI client was also included; this was intended for running GUI Administration tools such as the Data Dictionary only.
Progress introduced a full 64-bit Windows GUI client in OpenEdge 11.3. The PROCESS-ARCHITECTURE function was also introduced in OE 11.3
"


Thanks!
 
Last edited:
Finally (I hope), the input parameters of the CreateProcessA procedure get NULL value with just a few exceptions.
Type change seems to be required only for two parameters: lpStartupInfo and lpProcessInformation.
 
Back
Top