【问题标题】:Unexpected/Inconsistent Behavior from ShellExecuteExShellExecuteEx 的意外/不一致行为
【发布时间】:2019-01-28 05:43:25
【问题描述】:

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

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

ShellExecuteEx 声称在这两种情况下都正确运行(返回真)。我已经验证每次都提供了可执行文件(和工作目录)的正确路径。我尝试在 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 指出我没有正确初始化结构,这很尴尬。不幸的是,修复它并没有改变行为。不过还是谢谢!

【问题讨论】:

  • 一件事你没有做,那就是确保在使用之前将SHELLEXECUTEINFOA 等结构清零。您永远不应该使用未初始化的结构,设置一些成员,然后使用它。 WinAPI 调用很可能使用了您未设置的成员的垃圾值。
  • SHELLEXECUTEINFOA execinfo = { }; -- 这将自动将结构清零。如果您这样做了,您的功能是否始终如一地工作?

标签: c++ winapi visual-c++ shellexecuteex


【解决方案1】:

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

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

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

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

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

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

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

SHELLEXECUTEINFOA execInfo = {};

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

【讨论】:

  • 在C语言中你可以使用= { 0 };
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-08-03
  • 2017-07-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多