Неожиданное / противоречивое поведение от ShellExecuteEx - PullRequest
0 голосов
/ 28 января 2019

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

0 голосов
/ 28 января 2019

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

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 ++.

...