File: n_runandwait.sru
Size: 14295
Date: Wed, 06 Apr 2022 02:26:18 +0200
$PBExportHeader$n_runandwait.sru
$PBExportComments$Functions to allow waiting for executed program to finish
forward
global type n_runandwait from nonvisualobject
end type
type process_information from structure within n_runandwait
end type
type startupinfo from structure within n_runandwait
end type
type shellexecuteinfo from structure within n_runandwait
end type
type security_attributes from structure within n_runandwait
end type
end forward

type process_information from structure
   long     hprocess
   long     hthread
   unsignedlong      dwprocessid
   unsignedlong      dwthreadid
end type

type startupinfo from structure
   unsignedlong      cb
   string      lpreserved
   string      lpdesktop
   string      lptitle
   unsignedlong      dwx
   unsignedlong      dwy
   unsignedlong      dwxsize
   unsignedlong      dwysize
   unsignedlong      dwxcountchars
   unsignedlong      dwycountchars
   unsignedlong      dwfillattribute
   unsignedlong      dwflags
   unsignedinteger      wshowwindow
   unsignedinteger      cbreserved2
   long     lpreserved2
   long     hstdinput
   long     hstdoutput
   long     hstderror
end type

type shellexecuteinfo from structure
   unsignedlong      cbsize
   unsignedlong      fmask
   long     hwnd
   string      lpverb
   string      lpfile
   string      lpparameters
   string      lpdirectory
   long     nshow
   long     hinstapp
   long     lpidlist
   string      lpclass
   long     hkeyclass
   long     hicon
   long     hmonitor
   long     hprocess
end type

type security_attributes from structure
   unsignedlong      nlength
   long     lpsecuritydescriptor
   boolean     binherithandle
end type

global type n_runandwait from nonvisualobject autoinstantiate
end type

type prototypes
Function ulong GetLastError( &
   ) Library "kernel32.dll"

Function ulong FormatMessage( &
   ulong dwFlags, &
   ulong lpSource, &
   ulong dwMessageId, &
   ulong dwLanguageId, &
   Ref string lpBuffer, &
   ulong nSize, &
   ulong Arguments &
   ) Library "kernel32.dll" Alias For "FormatMessageW"

Function long ShellExecute ( &
   long hwnd, &
   string lpOperation, &
   string lpFile, &
   long lpParameters, &
   long lpDirectory, &
   long nShowCmd &
   ) Library "Shell32.dll" Alias For "ShellExecuteW"
   
Function boolean ShellExecuteEx ( &
   Ref SHELLEXECUTEINFO lpExecInfo &
   ) Library "shell32.dll" Alias For "ShellExecuteExW"

Function boolean CreateProcess ( &
   long lpApplicationName, &
   Ref string lpCommandLine, &
   long lpProcessAttributes, &
   long lpThreadAttributes, &
   boolean bInheritHandles, &
   ulong dwCreationFlags, &
   long lpEnvironment, &
   long lpCurrentDirectory, &
   STARTUPINFO lpStartupInfo, &
   Ref PROCESS_INFORMATION lpProcessInformation &
   ) Library "kernel32.dll" Alias For "CreateProcessW"

Function ulong WaitForSingleObject ( &
   long hHandle, &
   long dwMilliseconds &
   ) Library "kernel32.dll"

Function boolean CloseHandle ( &
   long hObject &
   ) Library "kernel32.dll"

Function boolean GetExitCodeProcess ( &
   long hProcess, &
   Ref long lpExitCode &
   ) Library "kernel32.dll"

Function boolean TerminateProcess ( &
   long hProcess, &
   long uExitCode &
   ) Library "kernel32.dll"

Function boolean CreatePipe ( &
   Ref long hReadPipe, &
   Ref long hWritePipe, &
   SECURITY_ATTRIBUTES lpPipeAttributes, &
   long nSize &
   ) Library "kernel32.dll"

Function boolean ReadFile ( &
   long hFile, &
   Ref blob lpBuffer, &
   ulong nNumberOfBytesToRead, &
   Ref ulong lpNumberOfBytesRead, &
   ulong lpOverlapped &
   ) Library "kernel32.dll"

Subroutine ZeroMemory ( &
   Ref blob dest, &
   long size &
   ) Library "kernel32.dll" Alias For "RtlZeroMemory"

Function boolean PeekNamedPipe ( &
   long hNamedPipe, &
   ulong lpBuffer, &
   ulong nBufferSize, &
   ulong lpBytesRead, &
   Ref ulong lpTotalBytesAvail, &
   ulong lpBytesLeftThisMessage &
   ) Library "kernel32.dll"

end prototypes

type variables
Private:

// Common options
Constant Long NULL = 0
Constant Long INFINITE        = -1
Constant Long WAIT_TIMEOUT    = 258
Constant Long STARTF_USESHOWWINDOW = 1
Constant Long STARTF_USESTDHANDLES = 256
Constant Long CREATE_NEW_CONSOLE = 16
Constant Long NORMAL_PRIORITY_CLASS = 32

Boolean Process64Bit

Public:

// nShowCmd options
Constant Long SW_HIDE            = 0
Constant Long SW_SHOWNORMAL      = 1
Constant Long SW_SHOWMINIMIZED   = 2
Constant Long SW_SHOWMAXIMIZED   = 3

Boolean Terminate
Long LastExitCode
Long WaitTimeout
String LastErrorText
ULong LastErrorNum

end variables

forward prototypes
public function boolean shellrun (string as_filename, string as_shellverb, long al_showcmd)
private function string getlasterrortext ()
public function boolean shellrunandwait (string as_filename, string as_shellverb, long al_showcmd)
public function boolean runandwait (string as_exefullpath, long al_showcmd)
public function boolean runandcapture (string as_exefullpath, ref string as_output)
end prototypes

public function boolean shellrun (string as_filename, string as_shellverb, long al_showcmd);// -----------------------------------------------------------------------------
// SCRIPT:     ShellRun
//
// PURPOSE:    This function performs a shell operation on a file.
//
// ARGUMENTS:  as_filename    - Full path file name
//             as_shellverb   - Shell verb (open, edit, print, runas)
//             al_showcmd     - How the application will be displayed.
//                               (use nShowCmd instance constants)
//
// RETURN:     True=Success, False=Error
//
// DATE        PROG/ID     DESCRIPTION OF CHANGE / REASON
// ----------  --------    -----------------------------------------------------
// 03/31/2022  RolandS     Initial Coding
// -----------------------------------------------------------------------------

Long ll_return

ll_return = ShellExecute(Handle(this), as_shellverb, &
               as_filename, NULL, NULL, al_showcmd)
If ll_return <= 32 Then
   // ShellExecute failed
   LastErrorNum  = GetLastError()
   LastErrorText = GetLastErrorText()
   Return False
End If

Return True

end function

private function string getlasterrortext ();// -----------------------------------------------------------------------------
// SCRIPT:     GetLastErrorText
//
// PURPOSE:    This function returns the last Windows API error.
//
// RETURN:     Error message text
//
// DATE        PROG/ID     DESCRIPTION OF CHANGE / REASON
// ----------  --------    -----------------------------------------------------
// 03/31/2022  RolandS     Initial Coding
// -----------------------------------------------------------------------------

Constant ULong FORMAT_MESSAGE_FROM_SYSTEM = 4096
Constant ULong LANG_NEUTRAL = 0
Constant Long BUFFER_SIZE = 260
String ls_buffer

ls_buffer = Space(BUFFER_SIZE)

FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, &
      LastErrorNum, LANG_NEUTRAL, ls_buffer, BUFFER_SIZE, 0)

Return ls_buffer

end function

public function boolean shellrunandwait (string as_filename, string as_shellverb, long al_showcmd);// -----------------------------------------------------------------------------
// SCRIPT:     ShellRunAndWait
//
// PURPOSE:    This function performs a shell operation on a file and waits
//             for it to finish.
//
// ARGUMENTS:  as_filename    - Full path file name
//             as_shellverb   - Shell verb (open, edit, print, runas)
//             al_showcmd     - How the program will be displayed.
//                               (use nShowCmd instance constants)
//
// NOTES:      LastExitCode: Set to the exit code from the program
//             WaitTimeout:  Milliseconds to stop waiting
//             Terminate:    If true, kill the program after timeout
//
// RETURN:     True=Success, False=Error
//
// DATE        PROG/ID     DESCRIPTION OF CHANGE / REASON
// ----------  --------    -----------------------------------------------------
// 03/31/2022  RolandS     Initial Coding
// -----------------------------------------------------------------------------

CONSTANT Long SEE_MASK_NOCLOSEPROCESS = 64
SHELLEXECUTEINFO lstr_sei
ULong lul_Reason

// initialize SHELLEXECUTEINFO structure
If Process64Bit Then
   lstr_sei.cbSize = 112
Else
   lstr_sei.cbSize = 60
End If
lstr_sei.fMask  = SEE_MASK_NOCLOSEPROCESS
lstr_sei.hWnd   = Handle(this)
lstr_sei.lpVerb = as_shellverb
lstr_sei.lpFile = as_filename
lstr_sei.nShow  = al_showcmd

If ShellExecuteEx(lstr_sei) Then
   // wait for the process to complete
   If WaitTimeout > 0 Then
      // wait until process ends or timeout period expires
      lul_Reason = WaitForSingleObject(lstr_sei.hProcess, WaitTimeout)
      // terminate process if not finished
      If Terminate And lul_Reason = WAIT_TIMEOUT Then
         TerminateProcess(lstr_sei.hProcess, -1)
         LastExitCode = WAIT_TIMEOUT
      Else
         // check for exit code
         GetExitCodeProcess(lstr_sei.hProcess, LastExitCode)
      End If
   Else
      // wait until process ends
      WaitForSingleObject(lstr_sei.hProcess, INFINITE)
      // check for exit code
      GetExitCodeProcess(lstr_sei.hProcess, LastExitCode)
   End If
   // close process and thread handles
   CloseHandle(lstr_sei.hProcess)
Else
   // ShellExecuteEx failed
   LastErrorNum  = GetLastError()
   LastErrorText = GetLastErrorText()
   LastExitCode = -1
   Return False
End If

Return True

end function

public function boolean runandwait (string as_exefullpath, long al_showcmd);// -----------------------------------------------------------------------------
// SCRIPT:     RunAndWait
//
// PURPOSE:    This function runs a program and waits for it to finish.
//
// ARGUMENTS:  as_exefullpath - Full path file name of the program.
//             al_showcmd     - How the program will be displayed.
//                               (use nShowCmd instance constants)
//
// NOTES:      LastExitCode: Set to the exit code from the program
//             WaitTimeout:  Milliseconds to stop waiting
//             Terminate:    If true, kill the program after timeout
//
// RETURN:     True=Success, False=Error
//
// DATE        PROG/ID     DESCRIPTION OF CHANGE / REASON
// ----------  --------    -----------------------------------------------------
// 03/31/2022  RolandS     Initial Coding
// -----------------------------------------------------------------------------

STARTUPINFO lstr_si
PROCESS_INFORMATION lstr_pi
ULong lul_Reason

// initialize STARTUPINFO structure
If Process64Bit Then
   lstr_si.cb = 104
Else
   lstr_si.cb = 68
End If
lstr_si.dwFlags = STARTF_USESHOWWINDOW
lstr_si.wShowWindow = al_showcmd

// create process/thread and execute the passed program
If CreateProcess(NULL, as_exefullpath, NULL, NULL, &
         False, 0, NULL, NULL, lstr_si, lstr_pi) Then
   // wait for the process to complete
   If WaitTimeout > 0 Then
      // wait until process ends or timeout period expires
      lul_Reason = WaitForSingleObject(lstr_pi.hProcess, WaitTimeout)
      // terminate process if not finished
      If Terminate And lul_Reason = WAIT_TIMEOUT Then
         TerminateProcess(lstr_pi.hProcess, -1)
         LastExitCode = WAIT_TIMEOUT
      Else
         // check for exit code
         GetExitCodeProcess(lstr_pi.hProcess, LastExitCode)
      End If
   Else
      // wait until process ends
      WaitForSingleObject(lstr_pi.hProcess, INFINITE)
      // check for exit code
      GetExitCodeProcess(lstr_pi.hProcess, LastExitCode)
   End If
   // close process and thread handles
   CloseHandle(lstr_pi.hProcess)
   CloseHandle(lstr_pi.hThread)
Else
   // ShellExecuteEx failed
   LastErrorNum  = GetLastError()
   LastErrorText = GetLastErrorText()
   LastExitCode = -1
   Return False
End If

Return True

end function

public function boolean runandcapture (string as_exefullpath, ref string as_output);// -----------------------------------------------------------------------------
// SCRIPT:     RunAndCapture
//
// PURPOSE:    This function runs a program and waits for it to finish. Once
//             done, it captures the console output.
//
// ARGUMENTS:  as_exefullpath - Full path file name of the program.
//             as_output      - By ref string containing the console output.
//
// RETURN:     True=Success, False=Error
//
// DATE        PROG/ID     DESCRIPTION OF CHANGE / REASON
// ----------  --------    -----------------------------------------------------
// 03/31/2022  RolandS     Initial Coding
// -----------------------------------------------------------------------------

Constant ULong BUFFERSIZE = 1024 * 8
SECURITY_ATTRIBUTES lstr_sa
STARTUPINFO lstr_si
PROCESS_INFORMATION lstr_pi
Blob lbl_Output
Boolean lb_Return
Long ll_PipeRead, ll_PipeWrite
ULong lul_Reason, lul_BytesRead, lul_BytesAvail

// allocate buffer space
lbl_Output = Blob(Space(BUFFERSIZE), EncodingAnsi!)

// initialize SECURITY_ATTRIBUTES structure
If Process64Bit Then
   lstr_sa.nLength = 24
Else
   lstr_sa.nLength = 12
End If
lstr_sa.bInheritHandle = True
lstr_sa.lpSecurityDescriptor = NULL

// create a pipe
If Not CreatePipe(ll_PipeRead, ll_PipeWrite, lstr_sa, 0) Then
   LastErrorNum  = GetLastError()
   LastErrorText = GetLastErrorText()
   Return False
End If

// initialize STARTUPINFO structure
If Process64Bit Then
   lstr_si.cb = 104
Else
   lstr_si.cb = 68
End If
lstr_si.dwFlags = STARTF_USESHOWWINDOW + STARTF_USESTDHANDLES
lstr_si.wShowWindow = SW_HIDE
lstr_si.hStdOutput = ll_PipeWrite
lstr_si.hStdError = ll_PipeWrite

// run the program
If CreateProcess(NULL, as_exefullpath, NULL, NULL, &
      True, 0, NULL, NULL, lstr_si, lstr_pi) Then
   do
      // wait on the process
      lul_Reason = WaitForSingleObject(lstr_pi.hProcess, 1)
      do
         // check to see if there is any data in the pipe
         PeekNamedPipe(ll_PipeRead, NULL, 0, NULL, lul_BytesAvail, NULL)

         // read data from the pipe
         If lul_BytesAvail > 0 Then
            ZeroMemory(lbl_Output, BUFFERSIZE)
            ReadFile(ll_PipeRead, lbl_Output, BUFFERSIZE, lul_BytesRead, NULL)
            If lul_BytesRead > 0 Then
               as_Output += String(lbl_Output, EncodingAnsi!)
            End If
         End If
      loop while lul_BytesRead = BUFFERSIZE
   loop while lul_Reason = WAIT_TIMEOUT
   lb_Return = True
Else
   // CreateProcess failed
   LastErrorNum  = GetLastError()
   LastErrorText = GetLastErrorText()
   lb_Return = False
End If

// close open handles
CloseHandle(lstr_pi.hProcess)
CloseHandle(lstr_pi.hThread)
CloseHandle(ll_PipeRead)
CloseHandle(ll_PipeWrite)

Return lb_Return

end function

on n_runandwait.create
call super::create
TriggerEvent( this, "constructor" )
end on

on n_runandwait.destroy
TriggerEvent( this, "destructor" )
call super::destroy
end on