home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / vp21beta.zip / OEXMPSRC.RAR / OS2EXEC / OS2EXEC.PAS next >
Pascal/Delphi Source File  |  2000-08-15  |  10KB  |  289 lines

  1. {█▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█}
  2. {█                                                       █}
  3. {█      Virtual Pascal v2.0                              █}
  4. {█      Execute child process and redirect output        █}
  5. {█      ─────────────────────────────────────────────────█}
  6. {█      Copyright (C) 1996-2000 vpascal.com              █}
  7. {█                                                       █}
  8. {▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀}
  9.  
  10. // This unit is provided on an as-is basis and has been made
  11. // available due to popular demand.
  12.  
  13. // It implements a class, which can be used to execute a
  14. // program that writes output to StdOut or StdErr.  This
  15. // output is catched and is returned to the calling program,
  16. // which can use it in any way it sees fit.
  17.  
  18. Unit Os2Exec;
  19.  
  20. {$Delphi+,X+,T-}
  21.  
  22. interface
  23.  
  24. uses
  25.   Os2Base, VPUtils, SysUtils, Classes;
  26.  
  27. const
  28.   MsgBufSize = 512;
  29.  
  30. type
  31.   MsgCharResult = (mcrTimeOut, mcrReady, mcrEOF);
  32.  
  33.   tRedirExec = class( tObject )
  34.   private
  35.     // Variables used internally by the class
  36.     fReadHandle  : Longint;
  37.     fWriteHandle : Longint;
  38.     fMsgBufPtr   : LongInt;
  39.     fBytesRead   : LongInt;
  40.     fReadSem     : HEv;
  41.     fMsgReady    : Boolean;
  42.     fMsgBuffer   : array [0..MsgBufSize-1] of Char;
  43.     fMessageChar : Char;
  44.  
  45.     // Fields published as properties
  46.     fMessageLine : String;
  47.     fTerminated  : Boolean;
  48.     fReturnCode  : Longint;
  49.     fOnTerminate : TNotifyEvent;
  50.     fOnCharReady : TNotifyEvent;
  51.     fOnLineReady : TNotifyEvent;
  52.   protected
  53.     function ReadMessageChar: MsgCharResult;
  54.     function GetMessageReady: Boolean;
  55.     function GetMessage: String;
  56.     property MessageChar: Char read fMessageChar write fMessageChar;
  57.   published
  58.     constructor create;
  59.     destructor Destroy; override;
  60.     function Execute(const S: String; CmdLine: PChar; Env: PChar): Boolean;
  61.  
  62.     property Terminated: Boolean read fTerminated;
  63.     property MessageReady: Boolean read GetMessageReady;
  64.     property ReturnCode: Longint read fReturnCode;
  65.     property Message: String read GetMessage;
  66.  
  67.     property OnTerminate: TNotifyEvent read fOnTerminate write fOnTerminate;
  68.     property OnCharReady: TNotifyEvent read fOnCharReady write fOnCharReady;
  69.     property OnLineReady: TNotifyEvent read fOnLineReady write fOnLineReady;
  70.   end;
  71.  
  72. implementation
  73.  
  74. const
  75.   StdOut = 1;     // Standard output file handle
  76.   StdErr = 2;     // Standard error file handle
  77.  
  78. constructor tRedirExec.create;
  79. begin
  80.   inherited create;
  81.  
  82.   DosCreateEventSem(nil, fReadSem, dc_Sem_Shared, False); // Create event semaphore
  83.   fTerminated := True;
  84.   fMsgReady := False;
  85. end;
  86.  
  87. function tRedirExec.Execute(const S: String; CmdLine: PChar; Env: PChar): Boolean;
  88. var
  89.   NewOut    : Longint;                   // File handles
  90.   OldOut    : Longint;
  91.   OldErr    : Longint;
  92.   NewErr    : Longint;
  93.   ExecErr   : Longint;
  94.   PostCount : Longint;
  95.   PipeName  : String;                    // Name of pipe used to communicate
  96.   Action    : Longint;                   // Action taken by DosOpen
  97.   ExecRes   : ResultCodes;
  98.   Os2Args   : PChar;
  99.   Args      : array [0..1024*2] of Char;
  100.   PrgName   : array [0..259] of Char;
  101.   FailedObj : array [0..259] of Char;
  102.  
  103. begin
  104.   Result := False;
  105.   If not fTerminated then               // Process already running - exit
  106.     Exit;
  107.  
  108.   // Create Named Pipe with a unique name, so several instances of the
  109.   // program can run without interfering by embedding a timer count into
  110.   // the pipe name.
  111.   // The server (read) handle of the pipe is used by the program, while the
  112.   // client (write) handle is redirected to be the STDOUT handle for the
  113.   // program to execute.
  114.   PipeName := Format( '\PIPE\VPX%.4x%.8x'#0, [ GetForegroundProcessID, GetTimeMSec ] );
  115.   if DosCreateNPipe(@PipeName[1], fReadHandle, np_Access_InBound,
  116.     np_NoWait + 1, 0, 4*1024, 0) <> 0 then
  117.     exit;                               // Fail if pipe creation fails
  118.  
  119.   DosConnectNPipe(fReadHandle);         // Connect to pipe
  120.   DosOpen(@PipeName[1], fWriteHandle, Action, 0, file_Normal, file_Open,
  121.     open_access_WriteOnly+open_share_DenyNone, nil);
  122.  
  123.   DosResetEventSem(fReadSem, PostCount);// Reset read event semaphore
  124.   DosSetNPipeSem(fReadHandle, HSem(fReadSem), 0);         // Associate with pipe
  125.  
  126.   OldOut := $FFFFFFFF;                  // Save original StdOut to OldOut
  127.   DosDupHandle(StdOut,OldOut);
  128.   NewOut := StdOut;                     // Redirect StdOut to Write pipe handle
  129.   DosDupHandle(fWriteHandle,NewOut);
  130.  
  131.   OldErr := $FFFFFFFF;                  // Save original StdErr to OldErr
  132.   DosDupHandle(StdErr,OldErr);
  133.   NewErr := StdErr;                     // Redirect StdErr to Write pipe handle
  134.   DosDupHandle(fWriteHandle,NewErr);
  135.  
  136.   DosClose(fWriteHandle);               // Close write pipe end to sense EOF on read
  137.  
  138.   StrPCopy(PrgName,S);                  // Set up DosExecPgm parameters
  139.   Os2Args := Args;
  140.   // Work around OS/2 bug: Argument to ExecPgm must not cross 64K boundary
  141.   if ((Longint(Os2Args) + 1024) and $FFFF) < 1024 then
  142.     Inc(Os2Args, 1024);
  143.   StrCat(StrCat(StrCopy(Os2Args, PrgName), ' '), CmdLine);
  144.   Os2Args[StrLen(Os2Args)+1] := #0;     { #0#0 at the end }
  145.   Os2Args[Length(S)] := #0;             { #0 after program name }
  146.   ExecErr := DosExecPgm(FailedObj, SizeOf(FailedObj), exec_AsyncResult, Os2Args, Env, ExecRes, PrgName);
  147.  
  148.   // Restore Handles before returning
  149.   DosDupHandle(OldOut,NewOut);          // Restore StdOut to original meaning
  150.   DosClose(OldOut);                     // Close duplicate of StdOut
  151.   DosDupHandle(OldErr,NewErr);          // Restore StdErr to original meaning
  152.   DosClose(OldErr);                     // Close duplicate of StdErr
  153.  
  154.   if ExecErr <> 0 then                  // If execution failed, exit
  155.     exit;
  156.  
  157.   fMsgBufPtr  := 0;                     // Reset state variables
  158.   fBytesRead   := 0;
  159.   fTerminated  := False;
  160.   fMessageLine := '';
  161.   Result       := True;
  162. end;
  163.  
  164. { Returns next message character if available }
  165.  
  166. function tRedirExec.ReadMessageChar: MsgCharResult;
  167. var
  168.   PostCount : Longint;
  169.   PipeState : Longint;
  170.   RCWait    : Longint;
  171.   Avail     : AvailData;
  172. begin
  173.   if fMsgBufPtr = fBytesRead then
  174.     begin
  175.       fMsgBufPtr := 0;
  176.       fBytesRead := 0;
  177.       RCWait := DosWaitEventSem(fReadSem, 1);   // Wait 1 msec for posting sem
  178.       DosPeekNPipe(fReadHandle, fMsgBuffer, 1, fBytesRead, Avail, PipeState);
  179.       if fBytesRead = 0 then                   // No data available...
  180.         begin
  181.           if PipeState = np_State_Closing then // If exiting, return EOF
  182.             begin
  183.               ReadMessageChar := mcrEOF;
  184.               Exit;
  185.             end;
  186.           if RCWait <> 0 then                  // If error, return timeout
  187.             begin
  188.               ReadMessageChar := mcrTimeOut;
  189.               Exit;
  190.             end;
  191.         end;
  192.       DosResetEventSem(fReadSem, PostCount);    // Reset semaphore
  193.       DosRead( fReadHandle, fMsgBuffer,        // Read data from pipe
  194.                SizeOf(fMsgBuffer), fBytesRead);
  195.       if fBytesRead = 0 then                   // If no data was read...
  196.         begin
  197.           ReadMessageChar := mcrTimeOut;       // Return timout
  198.           Exit;
  199.         end;
  200.     end;
  201.   ReadMessageChar := mcrReady;                 // Character ready
  202.   fMessageChar := fMsgBuffer[fMsgBufPtr];      // Fill buffer
  203.   Inc(fMsgBufPtr);
  204. end;
  205.  
  206. { Checks state of pipe, and returns True if a full line is available }
  207.  
  208. Function tRedirExec.GetMessageReady : Boolean;
  209. var
  210.   Len     : Longint;
  211.   MsgInx  : Longint;
  212.   RetPID  : Longint;
  213.   ExecRes : ResultCodes;
  214.  
  215. begin
  216.   If fMsgReady then
  217.     begin
  218.       Result := True;
  219.       exit;
  220.     end;
  221.  
  222.   Result := False;
  223.   Len := Length(fMessageLine);
  224.  
  225.   for MsgInx := 0 to MsgBufSize-1 do
  226.     case ReadMessageChar of
  227.       mcrEOF:                                  // EOF reached
  228.         begin
  229.           DosClose(fReadHandle);               // Close handle
  230.           DosWaitChild(dcwa_Process, dcww_Wait, ExecRes, RetPID, 0);
  231.           fReturnCode := ExecRes.codeResult;   // Save return code
  232.           fTerminated := True;
  233.           If Assigned( fOnTerminate ) then     // Execute OnTerminate method
  234.             OnTerminate( Self );
  235.           Exit;
  236.         end;
  237.  
  238.       mcrReady:                                // Character is received
  239.         begin
  240.           if Assigned( fOnCharReady ) then     // Execute OnCharReady method
  241.             OnCharReady( Self );
  242.  
  243.           if (fMessageChar = #10) and (fMessageLine <> '') then
  244.             begin
  245.               Result := True;                  // End-of-line
  246.               fMsgReady := True;
  247.               if Assigned( fOnLineReady ) then // Execute OnLineReady method
  248.                 OnLineReady( Self );
  249.               Exit;
  250.             end;
  251.  
  252.           if MessageChar >= ' ' then           // Filter printable chars
  253.             begin
  254.               Inc(Len);                        // Append char to string
  255.               fMessageLine[Len] := MessageChar;
  256.               SetLength( fMessageLine, Len );
  257.             end;
  258.         end;
  259.  
  260.       mcrTimeOut: Exit;                        // Timeout: Exit
  261.     end;
  262.  
  263. end;
  264.  
  265. { Returns a message, if one is ready.  Otherwise, the empty string is returned }
  266.  
  267. Function tRedirExec.GetMessage : string;
  268. begin
  269.   if MessageReady then                 // If a message is ready
  270.     begin
  271.       Result := fMessageLine;          // Return message
  272.       fMessageLine := '';
  273.       fMsgReady := False;
  274.     end
  275.   else
  276.     Result := '';                      // else return blank line
  277. end;
  278.  
  279. destructor tRedirExec.Destroy;
  280. begin
  281.   DosDisConnectNPipe(fReadHandle);     // Disconnect from pipe
  282.   DosCloseEventSem(fReadSem);          // Close event semaphore
  283.  
  284.   inherited destroy;
  285. end;
  286.  
  287. end.
  288.  
  289.