Mega Code Archive

 
Categories / Delphi / Examples
 

Update Capturing all of the Output from a Console application

Title: Update: Capturing all of the Output from a Console application Question: In my article http://www.delphi3000.com/articles/article_2112.asp posted a function for retrieving all of the output of a console application. Unfortunately, though it worked fine on 32-bit apps it did not work well with 16-bit apps. This was not a problem with the code, but rather a bug in Windows (http://support.microsoft.com/support/kb/articles/Q150/9/56.ASP). But how can this be done? Answer: { Here is new function I have been working on which seems to do the trick. It bypasses the problem with 16-bit apps by directing windows to send the output to a text file, and then reads it back in, deletes the file, and sends the result back to you. Be careful when calling this with command.com. Because it waits on the process infinitely it will hang on this because command.com waits for user input... Special thanks to Theo Bebekis for his help on this. If you have questions or comments please email me at johnwlong@characterlink.net, I have not had a chance to thoroughly test this version so any feed back would be helpful. } function GetConsoleOutput(const CommandLine:string): string; var SA: TSecurityAttributes; SI: TStartupInfo; PI: TProcessInformation; StdOutFile, AppProcess, AppThread : THandle; RootDir, WorkDir, StdOutFileName:string; const FUNC_NAME = 'GetConsoleOuput'; begin try StdOutFile:=0; AppProcess:=0; AppThread:=0; Result:=''; // Initialize dirs RootDir:=ExtractFilePath(ParamStr(0)); WorkDir:=ExtractFilePath(CommandLine); // Check WorkDir if not (FileSearch(ExtractFileName(CommandLine),WorkDir)'') then WorkDir:=RootDir; // Initialize output file security attributes FillChar(SA,SizeOf(SA),#0); SA.nLength:=SizeOf(SA); SA.lpSecurityDescriptor:=nil; SA.bInheritHandle:=True; // Create Output File StdOutFileName:=RootDir+'output.tmp'; StdOutFile:=CreateFile(PChar(StdOutFileName), GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE, @SA, CREATE_ALWAYS, // Always create it FILE_ATTRIBUTE_TEMPORARY or // Will cache in memory // if possible FILE_FLAG_WRITE_THROUGH, 0); // Check Output Handle if StdOutFile = INVALID_HANDLE_VALUE then raise Exception.CreateFmt('Function %s() failed!' + #10#13 + 'Command line = %s',[FUNC_NAME,CommandLine]); // Initialize Startup Info FillChar(SI,SizeOf(SI),#0); with SI do begin cb:=SizeOf(SI); dwFlags:=STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES; wShowWindow:=SW_HIDE; hStdInput:=GetStdHandle(STD_INPUT_HANDLE); hStdError:=StdOutFile; hStdOutput:=StdOutFile; end; // Create the process if CreateProcess(nil, PChar(CommandLine), nil, nil, True, 0, nil, PChar(WorkDir), SI, PI) then begin WaitForSingleObject(PI.hProcess,INFINITE); AppProcess:=PI.hProcess; AppThread:=PI.hThread; end else raise Exception.CreateFmt('CreateProcess() in function %s() failed!' + #10#13 + 'Command line = %s',[FUNC_NAME,CommandLine]); CloseHandle(StdOutFile); StdOutFile:=0; with TStringList.Create do try LoadFromFile(StdOutFileName); Result:=Text; finally Free; end; finally // Close handles if StdOutFile 0 then CloseHandle(StdOutFile); if AppProcess 0 then CloseHandle(AppProcess); if AppThread 0 then CloseHandle(AppThread); // Delete Output file if FileExists(StdOutFileName) then DeleteFile(StdOutFileName); end; end;