I try several samples in the internet and none of them work - the scripts are not executed- (maybe because are for pre Delphi 2009 unicode?).
I need to run some python scripts and pass arguments to them, like:
python "..\Plugins\RunPlugin.py" -a login -u Test -p test
And capture the output to a string & the errors to other.
This is what I have now:
procedure RunDosInMemo(DosApp:String; var OutData: String);
var
SA: TSecurityAttributes;
SI: TStartupInfo;
PI: TProcessInformation;
StdOutPipeRead, StdOutPipeWrite: THandle;
WasOK: Boolean;
Buffer: array[0..255] of Char;
BytesRead: Cardinal;
WorkDir: string;
Handle: Boolean;
begin
OutData := '';
with SA do begin
nLength := SizeOf(SA);
bInheritHandle := True;
lpSecurityDescriptor := nil;
end;
CreatePipe(StdOutPipeRead, StdOutPipeWrite, @SA, 0);
try
with SI do
begin
FillChar(SI, SizeOf(SI), 0);
cb := SizeOf(SI);
dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES or CREATE_UNICODE_ENVIRONMENT;
wShowWindow := SW_HIDE;
hStdInput := GetStdHandle(STD_INPUT_HANDLE); // don't redirect stdin
hStdOutput := StdOutPipeWrite;
hStdError := StdOutPipeWrite;
end;
WorkDir := 'C:\';
Handle := CreateProcess(nil, PChar(DosApp),
nil, nil, True, 0, nil,
PChar(WorkDir), SI, PI);
CloseHandle(StdOutPipeWrite);
if Handle then
begin
try
repeat
WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil);
if BytesRead > 0 then
begin
Buffer[BytesRead] := #0;
OutData := OutData + String(Buffer);
end;
until not WasOK or (BytesRead = 0);
WaitForSingleObject(PI.hProcess, INFINITE);
finally
CloseHandle(PI.hThread);
CloseHandle(PI.hProcess);
end;
end else begin
raise Exception.Create('Failed to load python plugin');
end;
finally
CloseHandle(StdOutPipeRead);
end;
end;
-
I'm not certain the WaitForSingleObject is the way to go... I think its better to loop with GetExitCodeProcess(pi.hProcess,iExitCode) until iExitCode <> STILL_ACTIVE and then check for data on each pass through the loop.
The code as written does not operate under Delphi 2007 either, so its not a Delphi 2009 unicode issue.
Changing your inner loop to the following works:
if Handle then begin try repeat WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil); for ix := 0 to BytesRead-1 do begin OutData := OutData + AnsiChar(Buffer[ix]); end; GetExitCodeProcess(pi.hProcess,iExit); until (iExit <> STILL_ACTIVE); finally CloseHandle(PI.hThread); CloseHandle(PI.hProcess); end;
I made the following corrections/additions to the local variables:
Buffer: array[0..255] of byte; iExit : Cardinal; IX : integer;
I also moved the CloseHandle(StdOutPipeWrite) just before the close of the StdOutPipeRead.
Rob Kennedy : If the script didn't run before, those changes won't make the script run. The "for" loop runs one too many times; fix that, and the assignment of zero into Buffer[BytesRead] no longer has any effect. Avoid the type-cast by declaring Buffer as an array of AnsiChar from the get-go. What's the purpose of your WaitForSingleObject call? You ignore the return value.skamradt : The waitforsingleobject call is a place holder to "burn some time". A sleep would have worked too, but the waitforsingleobject with a timeout just says to wait UNLESS the application is no longer running. I was just trying to not hog the CPU in the loop.mamcx : Ok, I copy this code from elsewhere, I have not skills in API development so I hardly understand what was going. Thanks for your input!Rob Kennedy : The delay is unnecessary. ReadFile won't return until there's data available to read, or the pipe is broken. -
Create_Unicode_Environment
is a process creation flag, meant for use in thedwCreationFlags
parameter ofCreateFile
. It is not a flag for use in theTStartupInfo
record. API functions are liable to fail if you give them flag values they don't understand, and they're liable to do strange things if you give them flag values that mean something other than what you expected.You declare a buffer of 256
Char
s; recall thatChar
in Delphi 2009 is a 2-byte Unicode type. You then callReadFile
and tell it that the buffer is 255 bytes long instead of the real value, 512. When the documentation says that a value is the number of bytes, take that as your cue to use theSizeOf
function.Since
ReadFile
reads bytes, it would be a good idea to declare your buffer array to be an array of byte-sized elements, such asAnsiChar
. That way, when you setBuffer[BytesRead]
, you won't include twice the data you actually read.The Unicode version of
CreateProcess
may modify its command-line argument. You must ensure that the string you pass to that parameter has a reference count of 1. CallUniqueString(DosApp)
before you callCreateProcess
.When an API function fails, you will of course want to know why. Don't just make up a reason. Use the functions provided, such as
Win32Check
andRaiseLastOSError
. At the very least, callGetLastError
, like MSDN tells you to. Don't throw a generic exception type when a more specific one is readily available.
0 comments:
Post a Comment