/*
   ----------------------------------------------------------------
   Filename.....: runProcess.cc
   ClassName....: runProcess
   Programmer...: Rick Miller with most of the code derived
                  from Jim Sare's :dUFLP:RunWait.prg.
   Date.........: June 24, 2008.
   Purpose......: run a command line in various ways.
   Notes........: See Notes below.
   Written for..: dBASE 32-bit.
   Rev. History.: None.
   Dependencies.: None.
   API Calls....: See x_InitializeExterns method.
   Called by....: Any.
   Usage........: set procedure additive.
   Example......: See Example below.
   ----------------------------------------------------------------
   Example:
   ----------------------------------------------------------------
      oRef  =  new runProcess()
      // command line to run notepad.exe.
      cCommand =  [notepad.exe]
      // execute the command line.
      oRef.run(cCommand)

      // command line to unzip a file with 7za.exe.
      cCommand =  fullpath + [\] + [7za.exe e ] +;
                  chr(34) + fullpath + [\] + filename + chr(34) +;
                  [ ] +;
                  chr(34) + fullpathToextractfolder + chr(34)
      // execute the command line.
      oRef.runHiddenWait(cCommand)

      // clear out the runProcess object.
      oRef.release()
      oRef  := null
   ----------------------------------------------------------------
   Notes:
   ----------------------------------------------------------------
      1) all run??? methods default to running from the current
         directory when the 2nd parameter is not passed.
      2) the runHidden, or the runHiddenWait method
         should only be used for a command line
         that will run and exit on it's own.
   ----------------------------------------------------------------
   Properties: None.
   ----------------------------------------------------------------
   Methods:
   ----------------------------------------------------------------
      close()                                call to release(),
                                             close procedure file.
      release()                              release object.
      run(<char>[, <char> dir])              run in normal window.
      runHidden(<char>[, <char> dir])        run in hidden window.
      runHiddenWait(<char>[, <char> dir])    run in hidden window,
                                             wait until complete.
      runMaximized(<char>[, <char> dir])     run in maximized window.
      runMaximizedWait(<char>[, <char> dir]) run in maximized window.
                                             wait until complete.
      runMinimized(<char>[, <char> dir])     run in minimized window.
      runMinimizedWait(<char>[, <char> dir]) run in minimized window.
                                             wait until complete.
      runWait(<char>[, <char> dir])          run in normal window,
                                             wait until complete.
   ----------------------------------------------------------------
   */
#define  CREATE_NEW_PROCESS_GROUP      0x00000200
#define  NORMAL_PRIORITY_CLASS         0x00000020
#define  STARTF_USESHOWWINDOW          0x00000001
#define  SW_HIDE                       0
#define  SW_SHOWNORMAL                 1
#define  SW_SHOWMINIMIZED              2
#define  SW_SHOWMAXIMIZED              3
#define  WAIT_NONE                     0
#define  WAIT_INFINITE                 0xFFFFFFFF
#define  ZEROS(n)                      replicate(chr(0), n)
//---------------------------------------------------------------//
//             start of constructor.
//---------------------------------------------------------------//
class runProcess
   protect  x_GetDirectory, x_InitializeExterns, x_ReadDword,;
            x_Run, x_WriteDword, x_WriteWord
   this.x_InitializeExterns()
   //------------------------------------------------------------//
   //          end of constructor   -  methods below.
   //------------------------------------------------------------//
   function close
      this.release()
      try
         close procedure (program(1))
      catch(exception e)
      endtry
   return
   //------------------------------------------------------------//
   function release
      try
         release object this
      catch(exception e)
      endtry
   return
   //------------------------------------------------------------//
   function run(cCommand, cFolder)
   return   this.x_Run(cCommand, cFolder, SW_SHOWNORMAL, false)
   //------------------------------------------------------------//
   function runHidden(cCommand, cFolder)
   return   this.x_Run(cCommand, cFolder, SW_HIDE, false)
   //------------------------------------------------------------//
   function runHiddenWait(cCommand, cFolder)
   return   this.x_Run(cCommand, cFolder, SW_HIDE, true)
   //------------------------------------------------------------//
   function runMaximized(cCommand, cFolder)
   return   this.x_Run(cCommand, cFolder, SW_SHOWMAXIMIZED, false)
   //------------------------------------------------------------//
   function runMaximizedWait(cCommand, cFolder)
   return   this.x_Run(cCommand, cFolder, SW_SHOWMAXIMIZED, true)
   //------------------------------------------------------------//
   function runMinimized(cCommand, cFolder)
   return   this.x_Run(cCommand, cFolder, SW_SHOWMINIMIZED, false)
   //------------------------------------------------------------//
   function runMinimizedWait(cCommand, cFolder)
   return   this.x_Run(cCommand, cFolder, SW_SHOWMINIMIZED, true)
   //------------------------------------------------------------//
   function runWait(cCommand, cFolder)
   return   this.x_Run(cCommand, cFolder, SW_SHOWNORMAL, true)
   //------------------------------------------------------------//
   //       protected methods below.
   //------------------------------------------------------------//
   function x_GetDirectory(cValu)
      Local cRet
      if type("argVector(1)") == "C"
         cRet  =  "" + cValu
      else
         cRet  =  set("directory")
      endif
   return   cRet
   //------------------------------------------------------------//
   function x_InitializeExterns
      if not type("RMM_CloseHandle") == "FP"
         extern CLOGICAL RMM_CloseHandle(CHANDLE) ;
            kernel32 from "CloseHandle"
      endif
      if not type("RMM_CreateProcess") == "FP"
         extern CLOGICAL RMM_CreateProcess( ;
            CSTRING, CSTRING, CPTR, CPTR, CLOGICAL,;
            CULONG, CPTR, CSTRING, CPTR, CPTR);
            kernel32 from "CreateProcessA"
      endif
      if not type("RMM_GetStartupInfo") == "FP"
         extern CVOID RMM_GetStartupInfo(CPTR) ;
            kernel32 from "GetStartupInfoA"
      endif
      if not type("RMM_WaitForSingleObject") == "FP"
         extern CULONG RMM_WaitForSingleObject(CHANDLE, CULONG) ;
            kernel32 from "WaitForSingleObject"
      endif
   return
   //------------------------------------------------------------//
   // return <int> (DWORD = 0 to 4294967295) from a structure.
   // a DWORD is the same as a CULONG.
   // sBuff =  <char>   structure.
   // nPos  =  <int>    zero-based position in sBuff to begin read.
   function x_ReadDword(sBuff, nPos)
   return   int( ;
               sBuff.getByte(nPos) + ;
               bitlshift(sBuff.getByte(nPos + 1), 8) + ;
               bitlshift(sBuff.getByte(nPos + 2), 16) + ;
               bitlshift(sBuff.getByte(nPos + 3), 24))
   //------------------------------------------------------------//
   function x_Run(cLine, cFolder, nShow, bWait)
      Local bRet, cDir, nProc, nThrd, sInfo, sProc
      bRet  =  false
      cDir  =  this.x_GetDirectory(cFolder)
      // create a STARTUPINFO structure.
      sInfo =  ZEROS(34)
      // create a PROCESS_INFORMATION structure.
      sProc =  ZEROS(8)
      // assign the size member of the STARTUPINFO.
      this.x_WriteDword(sInfo, 0, 68)
      // initialize the sInfo structure
      // using the current calling process information.
      RMM_GetStartupInfo(sInfo)
      // set the dwFlags member of the STARTUPINFO structure,
      // so that wShowWindow member is enabled.
      this.x_WriteDword(sInfo, 44, STARTF_USESHOWWINDOW)
      // set the wShowWindow member of the STARTUPINFO structure.
      this.x_WriteWord(sInfo, 48, nShow)
      if RMM_CreateProcess(null, cLine, null, null, false,;
                           bitOr(CREATE_NEW_PROCESS_GROUP,;
                           NORMAL_PRIORITY_CLASS),;
                           null, cDir, sInfo, sProc)
         // the process has been created.
         // get the handle to the created process.
         nProc =  this.x_ReadDword(sProc, 0)
         // get the handle to the primary thread
         // of the created process.
         nThrd =  this.x_ReadDword(sProc, 4)
         // wait for a signal from the created process.
         // if the wait time is 0 (WAIT_NONE), return immediately.
         // if the wait time is infinite (WAIT_INFINITE),
         // wait until the created process ends.
         RMM_WaitForSingleObject(nProc, iif(bWait,;
                                 WAIT_INFINITE, WAIT_NONE))
         // close the process (open object) handle (reference).
         RMM_CloseHandle(nProc)
         // close the thread (open object) handle (reference).
         RMM_CloseHandle(nThrd)
         // assign success.
         bRet  := true
      endif
   return   iif(bRet, true, false)  // success/fail.
   //------------------------------------------------------------//
   // write a DWORD into a structure.
   // a DWORD is the same as a CULONG.
   // sBuff =  <char>   structure.
   // nPos  =  <int>    zero-based position in sBuff to begin write.
   // nValu =  <int>    0 to 4294967295 for a DWORD.
   function x_WriteDword(sBuff, nPos, nValu)
      sBuff.setByte(nPos, bitand(nValu, 0xFF))
      sBuff.setByte(nPos + 1, bitand(bitzrshift(nValu,  8), 0xFF))
      sBuff.setByte(nPos + 2, bitand(bitzrshift(nValu, 16), 0xFF))
      sBuff.setByte(nPos + 3, bitand(bitzrshift(nValu, 24), 0xFF))
   return
   //------------------------------------------------------------//
   // write a WORD into a structure.
   // a WORD is the same as a CUSHORT.
   // sBuff =  <char>   structure.
   // nPos  =  <int>    zero-based position in sBuff to begin write.
   // nValu =  <int>    0 to 65535 for a WORD.
   function x_WriteWord(sBuff, nPos, nValu)
      sBuff.setByte(nPos, bitand(nValu, 0xFF))
      sBuff.setByte(nPos + 1, bitand(bitzrshift(nValu,  8), 0xFF))
   return
endclass
//---------------------------------------------------------------//
//          end of class.
//---------------------------------------------------------------//