Author: John W. Long
In my article "Capturing all of the Output from a Console application (32 bit)"
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.netmailto:johnwlong@characterlink.net, I have not had a
chance to thoroughly test this version so any feed back would be helpful.
1
2 function GetConsoleOutput(const CommandLine: string): string;
3 var
4 SA: TSecurityAttributes;
5 SI: TStartupInfo;
6 PI: TProcessInformation;
7 StdOutFile, AppProcess, AppThread: THandle;
8 RootDir, WorkDir, StdOutFileName: string;
9 const
10 FUNC_NAME = 'GetConsoleOuput';
11 begin
12 try
13 StdOutFile := 0;
14 AppProcess := 0;
15 AppThread := 0;
16 Result := '';
17
18 // Initialize dirs
19 RootDir := ExtractFilePath(ParamStr(0));
20 WorkDir := ExtractFilePath(CommandLine);
21
22 // Check WorkDir
23 if not (FileSearch(ExtractFileName(CommandLine), WorkDir) <> '') then
24 WorkDir := RootDir;
25
26 // Initialize output file security attributes
27 FillChar(SA, SizeOf(SA), #0);
28 SA.nLength := SizeOf(SA);
29 SA.lpSecurityDescriptor := nil;
30 SA.bInheritHandle := True;
31
32 // Create Output File
33 StdOutFileName := RootDir + 'output.tmp';
34 StdOutFile := CreateFile(PChar(StdOutFileName),
35 GENERIC_READ or GENERIC_WRITE,
36 FILE_SHARE_READ or FILE_SHARE_WRITE,
37 @SA,
38 CREATE_ALWAYS, // Always create it
39 FILE_ATTRIBUTE_TEMPORARY or // Will cache in memory
40 // if possible
41 FILE_FLAG_WRITE_THROUGH,
42 0);
43
44 // Check Output Handle
45 if StdOutFile = INVALID_HANDLE_VALUE then
46 raise Exception.CreateFmt('Function %s() failed!' + #10#13 +
47 'Command line = %s', [FUNC_NAME, CommandLine]);
48
49 // Initialize Startup Info
50 FillChar(SI, SizeOf(SI), #0);
51 with SI do
52 begin
53 cb := SizeOf(SI);
54 dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
55 wShowWindow := SW_HIDE;
56 hStdInput := GetStdHandle(STD_INPUT_HANDLE);
57 hStdError := StdOutFile;
58 hStdOutput := StdOutFile;
59 end;
60
61 // Create the process
62 if CreateProcess(nil, PChar(CommandLine), nil, nil,
63 True, 0, nil,
64 PChar(WorkDir), SI, PI) then
65 begin
66 WaitForSingleObject(PI.hProcess, INFINITE);
67 AppProcess := PI.hProcess;
68 AppThread := PI.hThread;
69 end
70 else
71 raise Exception.CreateFmt('CreateProcess() in function %s() failed!'
72 + #10#13 + 'Command line = %s', [FUNC_NAME, CommandLine]);
73
74 CloseHandle(StdOutFile);
75 StdOutFile := 0;
76
77 with TStringList.Create do
78 try
79 LoadFromFile(StdOutFileName);
80 Result := Text;
81 finally
82 Free;
83 end;
84
85 finally
86 // Close handles
87 if StdOutFile <> 0 then
88 CloseHandle(StdOutFile);
89 if AppProcess <> 0 then
90 CloseHandle(AppProcess);
91 if AppThread <> 0 then
92 CloseHandle(AppThread);
93
94 // Delete Output file
95 if FileExists(StdOutFileName) then
96 DeleteFile(StdOutFileName);
97 end;
98
99 end;
|