2006. szeptember 25., hétfő
Capturing all of the Output from a Console application (16 bit)
Problem/Question/Abstract:
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.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;
Feliratkozás:
Megjegyzések küldése (Atom)
Nincsenek megjegyzések:
Megjegyzés küldése