CreateProcess returns immediately, but only if the started process is hidden
I have the below Delphi code to provide a friendly wrapper for the CreateProcess API call.
function StartProcess(ExeName: string; CmdLineArgs: string = '';
ShowWindow: boolean = True; WaitForFinish: boolean = False): integer;
const
c_Wait = 100;
var
StartInfo: TStartupInfo;
ProcInfo: TProcessInformation;
begin
//Simple wrapper for the CreateProcess command
//returns the process id of the started process.
FillChar(StartInfo,SizeOf(TStartupInfo),#0);
FillChar(ProcInfo,SizeOf(TProcessInformation),#0);
StartInfo.cb := SizeOf(TStartupInfo);
//this block is the only part of execution that is different
//between my two calls. What am I doing wrong with these flags?
if not(ShowWindow) then begin
StartInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
StartInfo.wShowWindow := SW_HIDE;
end;
CreateProcess(nil,PChar(ExeName + ' ' + CmdLineArgs),nil,nil,False,
CREATE_NEW_PROCESS_GROUP + NORMAL_PRIORITY_CLASS,nil,nil,StartInfo,
ProcInfo);
Result := ProcInfo.dwProcessId;
if WaitForFinish then begin
while IsProcessRunning(Result) do begin
Sleep(c_Wait);
end;
end;
end;
I am using it to start a batch file, and wait for the batch file to return. It works nicely as long as I leave the "ShowWindow" value as True. If I try to hide the command line window, then it returns immediately with no error. Can anyone help me understand my mistake here? Example usage is below with comments.
//this will not show the cmd line window, and it will return immediately
StartProcess('C:\run_me.bat','',False,True);
//this will show the cmd line, and (correctly) wait for the job to finish
StartProcess('C:\run_me.bat','',True,True);
An odd thing is when the window is hidden, I still get a process ID back, as if it started. But it quits so fast that I can't see it in the task manager.
If I change the batch file to have a "pause" at the end of it (so it will never really finish), I still get the same result. So it appear开发者_Python百科s that the process really does not start when I set the flags in the "if not(ShowWindow)" block of my code.
After Rob Kennedy's suggestions, my code looks like this:
function StartProcess(ExeName: string; CmdLineArgs: string = '';
ShowWindow: boolean = True; WaitForFinish: boolean = False): integer;
var
StartInfo: TStartupInfo;
ProcInfo: TProcessInformation;
begin
//Simple wrapper for the CreateProcess command
//returns the process id of the started process.
FillChar(StartInfo,SizeOf(TStartupInfo),#0);
FillChar(ProcInfo,SizeOf(TProcessInformation),#0);
StartInfo.cb := SizeOf(TStartupInfo);
if not(ShowWindow) then begin
StartInfo.dwFlags := STARTF_USESHOWWINDOW;
StartInfo.wShowWindow := SW_HIDE;
end;
CreateProcess(nil,PChar(ExeName + ' ' + CmdLineArgs),nil,nil,False,
CREATE_NEW_PROCESS_GROUP + NORMAL_PRIORITY_CLASS,nil,nil,StartInfo,
ProcInfo);
Result := ProcInfo.dwProcessId;
if WaitForFinish then begin
WaitForSingleObject(ProcInfo.hProcess,Infinite);
end;
//close process & thread handles
CloseHandle(ProcInfo.hProcess);
CloseHandle(ProcInfo.hThread);
end;
When you set ShowWindow = False
, you set the startup flags to include StartF_UseStdHandles
, but you never provide any values for the standard I/O handles. The moment the new process attempts to write any output, it will fail because it doesn't have a valid output handle.
If you're not going to provide values for the handles, then don't tell CreateProcess
that the handle fields have valid values in them. Omit that flag from the startup flags.
You don't get any error when creating the process because creating the process went fine. It's only after the process started running that it ran into problems. You're not checking the process's exit code, so there's no way you'd detect any failure.
精彩评论