取决于进程创建的执行异常(ShellExecute vs CreateProcess)

Posted

技术标签:

【中文标题】取决于进程创建的执行异常(ShellExecute vs CreateProcess)【英文标题】:Execution abnormalities depending on process creation (ShellExecute vs CreateProcess) 【发布时间】:2015-09-30 10:02:05 【问题描述】:

我们正在运行一个负责监控一组进程的 Windows 服务。该服务基本上只负责 (a) 检查定义的作业是否正在运行,以及 (b) 如果它们没有运行则启动这些作业。

服务是通过以下命令创建的(sc:https://technet.microsoft.com/en-us/library/bb490995.aspx):

sc create "My Service" binPath= C:\heyoo\myservice.exe type= own start= auto error= normal
sc start "SCF Service"

该服务负责创建的作业之一是“Camera.exe”。 Camera.exe 从连接的摄像机 (FireWire 1394) 检索视频源,并对它们进行一些处理。

一周前,该服务已从使用 ShellExecute 重写为使用 CreateProcess,因此它能够更好地监控已定义的作业(因为它获取进程的 HANDLE)

ShellExecute 调用(旧方法):

bool Execute()

    int result = (int)ShellExecute(NULL, "open", "C:\\bin\\Camera.exe", NULL, NULL, SW_SHOWDEFAULT);
    return result > 32;

CreateProcess 调用(新方法):

// Called with Execute("C:\\bin\\Camera.exe", "");
bool Execute(std::string prog, std::string args)

    std::string cmd = std::string(prog) + " " + args;
    char *path = new char[cmd.length()+1];
    strcpy(path, cmd.c_str());

    STARTUPINFO si = 0;
    si.cb = sizeof(STARTUPINFO);
    PROCESS_INFORMATION pi;
    ZeroMemory(&pi, sizeof(pi));

    DWORD creationFlags = REALTIME_PRIORITY_CLASS;

    BOOL result = CreateProcess(NULL, path, NULL, NULL, FALSE, creationFlags, NULL, NULL, &si, &pi);
    delete[] path;

    if (result) 
        SetProcInfo(pi);
    

    return result;

使用新的 CreateProcess 方法,我们注意到 (A) 网络在一定时间间隔后系统性地失败,并且 (B) 从相机检索到的图像包含无效的时间戳(正确的时间戳对我们至关重要)。

A 经常断开整个网络连接,并且需要重新启动才能重新联机。 B 导致图像处理失败,因为我们高度依赖有效的时间戳。

问题(A 和 B)在 Service.exe 作为服务运行时出现。从命令行运行Service.exe或Camera.exe时,没有出现任何问题。

今天我从服务中删除了 CreateProcess 调用(返回到 ShellExecute),问题又消失了。我对 API 调用做错了什么?

【问题讨论】:

【参考方案1】:
DWORD creationFlags = REALTIME_PRIORITY_CLASS;

这是最明显的区别。当您调用ShellExecute 时,将以正常优先级创建进程。实时优先级的documentation 表示:

具有最高优先级的进程。实时优先级进程的线程抢占所有其他进程的线程,包括执行重要任务的操作系统进程。例如,执行时间超过非常短暂的时间间隔的实时进程可能会导致磁盘缓存不刷新或导致鼠标无响应。

你真的不想这样做!

传递0 作为创建标志。 documentation 说:

如果没有指定优先级类标志,则优先级类默认为 NORMAL_PRIORITY_CLASS,除非创建进程的优先级类是 IDLE_PRIORITY_CLASS 或 BELOW_NORMAL_PRIORITY_CLASS。在这种情况下,子进程接收调用进程的默认优先级。

无论如何,您可以使用ShellExecuteEx 而不是ShellExecute 来获取进程句柄。事实上,你应该总是更喜欢ShellExecuteEx 而不是ShellExecute,因为后者不能正确报告错误。尽管如此,您正在创建一个新进程,因此CreateProcess 是该任务的正确函数。

【讨论】:

谢谢 - 这确实是正确的。我删除了“实时”标志,它似乎工作顺利。 您还泄露了 CreateProcess() 返回的 2 个 HANDLEs。你必须关闭它们。而您的path 变量是多余的,您可以将&cmd[0] 直接传递给CreateProcess() @Remy 大概是SetProcInfo 另一端的任何东西都清理了句柄。标准是否保证&cmd[0] 为空终止? @DavidHeffernan; C++11 保证使用指向字符串内部存储的指针是安全的。 O(1)operator[] 的索引要求(size() 是一个有效的索引)有效地强制内部存储为空终止,即使标准没有明确说明:***.com/a/7554172/65863

以上是关于取决于进程创建的执行异常(ShellExecute vs CreateProcess)的主要内容,如果未能解决你的问题,请参考以下文章

ShellExecute的返回值是进程的句柄吗

具有管理员权限的 shellexecute 不会结束

MFC中如何关闭ShellExecute调用的外部执行程序?

等待由 IShellDispatch2.ShellExecute 启动的进程

摆脱 ShellExecute 造成的邪恶延迟

VC中用ShellExecute等函数执行exe出现错误,但是鼠标双击就可以,这是为啥?