0

我正在使用 Qt 在 Visual C++ (2015) 中创建自配置软件工具,该工具根据需要运行其他几个第 3 方安装程序。当软件发现它需要一个不可用的库或驱动程序时,它将使用 ShellExecuteEx 调用相应的安装程序来打开相应的可执行文件。

如果只需要安装一件事(ShellExecuteEx 只运行一次),一切都会正常运行。如果需要同时安装件东西,那么大约 25% 的情况下,安装程序将正确运行,而另一个将打开包含可执行文件的文件夹,而不是运行可执行文件。对于正确运行的是第一个操作还是第二个操作没有一致性。

ShellExecuteEx 声称在这两种情况下都正确运行(返回 true)。我已经验证每次都提供了可执行文件(和工作目录)的正确路径。我尝试在 ShellExecuteEx 中同时使用动词“open”和“NULL”,但行为没有改变。

如果重要,两个可执行文件都请求管理权限。当一切正常时,他们都会按预期进行。当一个失败时,它不会要求管理员权限。

我找不到任何有类似问题的人的记录,这可能意味着我以某种基本方式滥用 ShellExecuteEx,但我没有看到它。提前感谢您的任何建议或意见。

这是代码:

if(dummy1Required && !dummy1Present){
        SHELLEXECUTEINFOA execinfo = {};
        execinfo.cbSize = sizeof(SHELLEXECUTEINFOA);
        execinfo.fMask = NULL;
        execinfo.hwnd = NULL;
        execinfo.lpVerb = NULL;
        execinfo.lpFile = QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + "/Drivers/dummy1/install.bat").toLocal8Bit().data();
        execinfo.lpParameters = NULL;
        execinfo.lpDirectory = QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + "/Drivers/dummy1").toLocal8Bit().data();
        printf("Dummy1 Executing: %s\n In directory:%s\n", QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + "/Drivers/dummy1/install.bat").toLocal8Bit().data(), QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + "/Drivers/dummy1").toLocal8Bit().data());
        fflush(stdout);
        execinfo.nShow = SW_SHOW;
        execinfo.hInstApp = NULL;
        execinfo.fMask = execinfo.fMask | SEE_MASK_NOCLOSEPROCESS;
        if(ShellExecuteExA(&execinfo)){
            WaitForSingleObject(execinfo.hProcess,INFINITE);
            CloseHandle(execinfo.hProcess);
        }else{
            printf("Failed to launch dummy1 installer because...%lu\n", GetLastError());
            fflush(stdout);
        }
    }

    if(dummy2Required && !dummy2Present){
        SHELLEXECUTEINFOA execinfo = {};
        execinfo.cbSize = sizeof(SHELLEXECUTEINFOA);
        execinfo.fMask = NULL;
        execinfo.hwnd = NULL;
        execinfo.lpVerb = NULL;
        execinfo.lpFile = QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + "/Drivers/dummy2/setup.exe").toLocal8Bit().data();
        execinfo.lpParameters = "setupParamters.ini /qb /acceptlicenses y /norestart";
        execinfo.lpDirectory = QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + "/Drivers/dummy2").toLocal8Bit().data();
        printf("Dummy2 Executing: %s\n In directory:%s\n", QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + "/Drivers/dummy2/setup.exe").toLocal8Bit().data(), QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + "/Drivers/dummy2").toLocal8Bit().data());
        fflush(stdout);
        execinfo.nShow = SW_SHOW;
        execinfo.hInstApp = NULL;
        execinfo.fMask = execinfo.fMask | SEE_MASK_NOCLOSEPROCESS;
        if(ShellExecuteExA(&execinfo)){
            WaitForSingleObject(execinfo.hProcess,INFINITE);
            CloseHandle(execinfo.hProcess);
        }else{
            printf("Failed to launch dummy2 installer because...%lu\n", GetLastError());
            fflush(stdout);
        }
    }

编辑:我不正确。如果只运行一个可执行文件,我会观察到这种行为的频率要低得多,因此它与运行两个操作没有内在联系。

编辑 2: PaulMckenzie 指出我没有正确初始化结构,这很尴尬。不幸的是,修复它并没有改变行为。不过谢谢!

4

1 回答 1

1

贴出的代码至少有两个错误。

if(dummy1Required && !dummy1Present){
        SHELLEXECUTEINFOA execinfo; // <-- Uninitialized
    ...
}

if(dummy2Required && !dummy2Present){
        SHELLEXECUTEINFOA execinfo; // <-- Uninitialized
     ...
}

然后你去设置 的一些成员SHELLEXECUTEINFOA,但是你设置了所有的成员吗?如果你错过了一些成员怎么办?

对于许多需要使用 struct 的 Windows API 函数,SHELLEXECUTEINFOA应在设置任何成员之前通过将所有成员清零来初始化该结构。由于该结构是一个局部变量,当以这种方式声明时,它不会被初始化。

不稳定行为的可能原因是此结构包含您可能不知道存在但 Win32 API 将使用的成员。如果这些成员未初始化,API 函数将使用未初始化成员持有的任何值。

初始化结构的最简单方法是简单地使用大括号初始化:

SHELLEXECUTEINFOA execInfo = {};

struct如果您看到 Win32 API 编程示例,您可能会遇到另一种将 a 归零的方法C是使用ZeroMemory{ }但是对于 C++,由于C++ 中存在初始化语法,因此不需要这样做。

于 2019-01-28T11:17:41.447 回答