2010-11-04 30 views
2

我有下面的Delphi代码来为CreateProcess API调用提供友好的包装。CreateProcess立即返回,但前提是隐藏起始进程

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; 

我正在使用它来启动批处理文件,并等待批处理文件返回。 只要我将“ShowWindow”值保留为True,它就可以很好地工作。如果我试图隐藏命令行窗口,那么它立即返回而没有错误。任何人都可以帮我理解我的错误吗?示例用法在下面加注释。

//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); 

奇怪的是,当窗口被隐藏时,我仍然得到一个进程ID,就好像它开始了。但是它退出的速度太快,我无法在任务管理器中看到它。

如果我将批处理文件更改为在其结尾处有一个“暂停”(因此它永远不会真的完成),我仍然会得到相同的结果。所以看起来,当我在代码的“if not(ShowWindow)”块中设置标志时,程序确实无法启动。

罗伯肯尼迪的建议后,我的代码看起来像这样:

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; 
+2

请注意,此函数会泄漏线程句柄和进程句柄。如果你真的想阻止这个函数的返回,直到这个进程停止运行,那么跳过这个循环就调用'WaitForSingleObject(ProcInfo.hProcess,Infinite)'。 – 2010-11-04 20:18:11

回答

7

当您设置ShowWindow = False,您设置的启动标志,包括StartF_UseStdHandles,但你永远不提供对标准的任何值,I/O处理。当新进程尝试写入任何输出时,它将失败,因为它没有有效的输出句柄。

如果您不打算为句柄提供值,则不要告诉CreateProcess句柄字段中有有效值。从启动标志中忽略该标志。

创建过程时不会出现任何错误,因为创建的过程正常。这只是之后该进程开始运行时遇到问题。您没有检查流程的退出代码,因此您无法检测到任何失败。

+1

感谢罗布,我上面发布了我的修改版本,它工作得很好。这就是我从网络上整理一些例子所得到的结果。还有一个问题是我看到内存泄漏,但是释放分配给FillChar的结构的最佳做法是什么?我在网上看到了两个想法:1)定义一个与结构类似的常量,并将其分配给本地实例,或者2)通过指定OUT参数的另一个函数运行本地实例。这两个人对我来说似乎很难受。 – JosephStyons 2010-11-05 17:07:35

+1

FillChar不分配任何东西。你没有泄漏结构。我说你正在泄漏*句柄*,所以通过代码来查找分配句柄的函数。如果您还没有阅读CreateProcess的文档,请从这里开始。它告诉你想要处理。 – 2010-11-05 17:20:13

+0

谢谢Rob。我查看了CreateProcess文档,它指向了“创建进程”指南,这使我在进程和线程句柄上添加了CloseHandle()调用。上面的代码示例现在包含了这一点。 – JosephStyons 2010-11-05 17:32:11