Неожиданное / противоречивое поведение от ShellExecuteEx

Я создаю самоконфигурируемый программный инструмент, который запускает несколько других сторонних инсталляторов по мере необходимости в Visual C++ (2015) с использованием Qt. Когда программное обеспечение обнаружит, что ему нужна недоступная библиотека или драйвер, оно вызовет соответствующий установщик, используя ShellExecuteEx, чтобы открыть соответствующий исполняемый файл.

Если требуется установить только одну вещь (ShellExecuteEx запускается только один раз), все работает нормально. Если необходимо установить две вещи одновременно, то примерно в 25% случаев один раз установщик будет работать правильно, а другой откроет папку, содержащую исполняемый файл, вместо запуска исполняемого файла. Нет согласованности относительно того, правильно ли выполняется первая или вторая операция.

ShellExecuteEx утверждает, что работал в обоих случаях правильно (возвращает true). Я проверил, что каждый раз указывается правильный путь к исполняемым файлам (и рабочим каталогам). Я попытался использовать как глагол "open", так и "NULL" в ShellExecuteEx, без изменений в поведении.

Если это имеет значение, оба исполняемых файла запрашивают административные привилегии. Когда все работает, они оба делают так, как ожидалось. Когда кто-то терпит неудачу, он не запрашивает привилегии администратора.

Я не могу найти записи о том, что у кого-то есть подобные проблемы, что, вероятно, означает, что я неправильно использую 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 указывает, что я не был должным образом инициализировать структуры, что смущает. К сожалению, исправление не изменило поведение. Спасибо хоть!

1 ответ

В опубликованном коде есть как минимум две ошибки.

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

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

Затем вы идете и установите некоторые члены SHELLEXECUTEINFOA, но вы их все поставили? Что если вы пропустили несколько участников?

Для многих функций Windows API, которые требуют использования структуры, такой как SHELLEXECUTEINFOAструктура должна быть инициализирована путем обнуления всех элементов перед установкой каких-либо элементов. Поскольку структура является локальной переменной, при объявлении таким способом она остается неинициализированной.

Вероятная причина ошибочного поведения заключается в том, что в этой структуре содержатся члены, о которых вы, возможно, не знаете, но API Win32 будет использоваться. Если эти члены неинициализированы, функция API будет использовать любое значение, которое содержит неинициализированный элемент.

Самый простой способ инициализации структуры - просто использовать инициализацию фигурной скобки:

SHELLEXECUTEINFOA execInfo = {};

Другой способ обнулить struct с которым вы можете столкнуться, если увидите примеры Win32 API C программирование - это использование ZeroMemory. Однако для C++ это не нужно из-за { } синтаксис инициализации, который существует в C++.

Другие вопросы по тегам