我正在使用Qt在Visual C ++(2015)中创建自配置软件工具,该工具将根据需要运行其他一些第三方安装程序。当软件发现需要的库或驱动程序不可用时,它将使用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指出我没有正确初始化结构,这很尴尬。不幸的是,修复它并没有改变行为。不过谢谢!

最佳答案

发布的代码中至少存在两个错误。

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

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


然后,您去设置SHELLEXECUTEINFOA的某些成员,但是是否都设置了所有成员?如果您错过了一些会员怎么办?

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

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

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

SHELLEXECUTEINFOA execInfo = {};

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

09-08 05:01