Код выхода «потерян» из дочернего процесса в Windows XP, а не в Windows Server 2003 - PullRequest
2 голосов
/ 07 марта 2012

РЕДАКТИРОВАТЬ 3

ОК, так что, похоже, это не проблема установщика в конце концов.Когда я делаю простой пакетный файл:

exit /b 12

и называю его

cmd /c test.cmd
echo %ERRORLEVEL%

, я получаю «12» на Windows Server 2003 R2, но «0» на XP.Я думал, что тестировал этот простой тестовый пример много раз раньше, но, видимо, нет.

Итак, я изменил теги и заголовок, но оставляю здесь другую информацию, поскольку здесь действительно много полезногоэто не имеет прямого отношения к этой проблеме.

Мысли?

Оригинал ниже

У меня есть пользовательское действие, написанное наVBScript, который, в свою очередь, вызывает пакетный файл Windows (пользовательское действие, по сути, позволяет пользователю выполнить что-то во время установки, которое он может запустить позже, запустив пакетный файл - это удобно).Функция ниже:

Function MainFunction
    strCustomActionData = Session.Property("CustomActionData")
    strSplit = Split(strCustomActionData, ";")
    strInstallDir = strSplit(0)
    strPostCopyAction = strSplit(1)

    strScriptLocation = strInstallDir & "\MigrationMasterProcess.cmd"

    strFullCommand = """" & strScriptLocation & """ " & strPostCopyAction

    Set objShell = CreateObject("WScript.Shell")

    Dim objExec
    Set objExec = objShell.Exec(strFullCommand)

    intReturnCode = objExec.ExitCode

    Set objExec = Nothing
    Set objShell = Nothing

    WriteMessage "Return value: " & intReturnCode

    ' cf. http://msdn.microsoft.com/en-us/library/windows/desktop/aa371254(v=vs.85).aspx
    If (intReturnCode = 0) Then
        MainFunction = 1
    Else
        MainFunction = 3
    End If
End Function

Когда я запускаю такой же код вне пользовательского действия, и пакетный файл возвращает код ошибки (через EXIT / B), возвращаемое значение корректно фиксируется вintReturnCode.Однако из пользовательского действия код выхода кажется «потерянным» - я всегда получаю обратно 0 (я вижу это в журнале установщика из вызова WriteMessage).Не имеет значения, использую ли я Exec или Run на оболочке, я все равно получаю обратно 0. Скрипт записывает свой собственный код возврата перед его возвратом (я вижу это в потоке stdout из Exec), поэтому я знаю, что это не такфактически 0. Мне нужен этот код возврата, чтобы правильно сообщить об ошибке установщику.

Идеи?

Для записи это Windows Installer 3.0 на Windows XP SP3.Установщик находится в Wise, поэтому у меня нет фрагмента WiX, или я бы включил его, но это вызываемая функция.

Также это несколько убрано - я оставил комментарии и другие вызовыWriteMessage, а также эта функция.И да, псевдо-венгерский - это злой бла-бла-бла.

Edit : Вот версия кода на С.Это дает точно такую ​​же проблему:

#include <Windows.h>
#include <msi.h>
#include <msiquery.h>
#include <stdio.h>
#include <stdlib.h>
#include "LaunchChildProcess.h"

BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) {
    return TRUE;
}

UINT __stdcall RunMigrationAction(MSIHANDLE hModule) {
    UINT  uiStat;
    DWORD dwPropertySize = MAX_PATH * 2;
    TCHAR szValueBuf[MAX_PATH * 2]; // arbitrary but we know the strings won't be near that long
    TCHAR *szInstallDir, *szPostCopyAction;
    TCHAR *szNextToken;
    TCHAR szScriptLocation[MAX_PATH * 2];
    TCHAR szParameters[MAX_PATH * 2];
    INT   iReturnValue;

    LogTaggedString(hModule, TEXT("Action Status"), TEXT("Starting"));

    uiStat = MsiGetProperty(hModule, TEXT("CustomActionData"), szValueBuf, &dwPropertySize);
    if (ERROR_SUCCESS != uiStat) {
        LogTaggedString(hModule, TEXT("Startup"), TEXT("Failed to get custom action data"));
        return ERROR_INSTALL_FAILURE;
    }

    LogTaggedString(hModule, TEXT("Properties given"), szValueBuf);
    LogTaggedInteger(hModule, TEXT("Property length"), dwPropertySize);

    if (0 == dwPropertySize) {
        return ERROR_INSTALL_FAILURE;
    }

    LogTaggedString(hModule, TEXT("Properties given"), szValueBuf);

    szInstallDir     = wcstok_s(szValueBuf, TEXT(";"), &szNextToken);
    szPostCopyAction = wcstok_s(NULL,       TEXT(";"), &szNextToken);

    LogTaggedString(hModule, TEXT("Install dir"), szInstallDir);
    LogTaggedString(hModule, TEXT("Post-copy action"), szPostCopyAction);

    wcscpy_s(szScriptLocation, MAX_PATH * 2, szInstallDir);
    wcscat_s(szScriptLocation, MAX_PATH * 2, TEXT("\\MigrationMasterProcess.cmd"));

    LogTaggedString(hModule, TEXT("Script location"), szScriptLocation);

    wcscpy_s(szParameters, MAX_PATH * 2, TEXT(" /C "));
    wcscat_s(szParameters, MAX_PATH * 2, szScriptLocation);
    wcscat_s(szParameters, MAX_PATH * 2, TEXT(" "));
    wcscat_s(szParameters, MAX_PATH * 2, szPostCopyAction);

    LogTaggedString(hModule, TEXT("Parameters to cmd.exe"), szParameters);

    iReturnValue = ExecuteProcess(TEXT("cmd.exe"), szParameters);
    LogTaggedInteger(hModule, TEXT("Return value from command"), iReturnValue);

    LogTaggedString(hModule, TEXT("Action Status"), TEXT("Finished"));

    return (0 == iReturnValue) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
}

void LogTaggedInteger(MSIHANDLE hInstall, TCHAR* szTag, INT iValue) {
    TCHAR szValue[15];
    _itow_s(iValue, szValue, 15, 10);
    LogTaggedString(hInstall, szTag, szValue);
}

void LogTaggedString(MSIHANDLE hInstall, TCHAR* szTag, TCHAR* szMessage) {
    MSIHANDLE hRecord;
    UINT uiStat;
    //TCHAR szFullMessage[4096];
    //wcscpy_s(szFullMessage, 4096, TEXT("--------------- "));
    //wcscat_s(szFullMessage, 4096, szTag);
    //wcscat_s(szFullMessage, 4096, TEXT(": "));
    //wcscat_s(szFullMessage, 4096, szMessage);
    hRecord = MsiCreateRecord(3);
    uiStat = MsiRecordSetString(hRecord, 0, TEXT("--------- [1]: [2]"));
    uiStat = MsiRecordSetString(hRecord, 1, szTag);
    uiStat = MsiRecordSetString(hRecord, 2, szMessage);
    uiStat = MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_INFO), hRecord);
    MsiCloseHandle(hRecord);
    return;
}


int MsiMessageBox(MSIHANDLE hInstall, TCHAR* szString, DWORD dwDlgFlags) {
    PMSIHANDLE newHandle = ::MsiCreateRecord(2);
    MsiRecordSetString(newHandle, 0, szString);
    return (MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_USER + dwDlgFlags), newHandle));
}


DWORD ExecuteProcess(TCHAR *szProcess, TCHAR *szParams) { 
    INT iMyCounter = 0, iPos = 0;
    DWORD dwReturnVal = 0;
    TCHAR *sTempStr = L""; 

    /* CreateProcessW can modify Parameters thus we allocate needed memory */
    wchar_t * pwszParam = new wchar_t[wcslen(szParams) + 1]; 
    if (NULL == pwszParam) { 
        return 1; 
    } 

    wcscpy_s(pwszParam, wcslen(szParams) + 1, szParams); 

    /* CreateProcess API initialization */
    STARTUPINFOW siStartupInfo; 
    PROCESS_INFORMATION piProcessInfo; 
    memset(&siStartupInfo, 0, sizeof(siStartupInfo)); 
    memset(&piProcessInfo, 0, sizeof(piProcessInfo)); 
    siStartupInfo.cb = sizeof(siStartupInfo); 

    if (CreateProcessW(const_cast<LPCWSTR>(szProcess), 
                            pwszParam, 0, 0, false, 
                            CREATE_DEFAULT_ERROR_MODE, 0, 0, 
                            &siStartupInfo, &piProcessInfo) != false) { 
        /* Watch the process. */
        WaitForSingleObject(piProcessInfo.hProcess, INFINITE);
        if (!GetExitCodeProcess(piProcessInfo.hProcess, &dwReturnVal)) {
            dwReturnVal = GetLastError();
        }
    } else { 
        /* CreateProcess failed */
        dwReturnVal = GetLastError(); 
    } 

    /* Free memory */
    free(pwszParam);
    pwszParam = NULL;

    /* Release handles */
    CloseHandle(piProcessInfo.hProcess); 
    CloseHandle(piProcessInfo.hThread); 

    return dwReturnVal; 
} 

При запуске на моем Windows Server 2003 R2 Visual Studio 2008, я получаю код ошибки, как и ожидалось:

--------- Return value from command: 5023

При запуске на моемОкно проверки Windows XP, я получаю 0, хотя это должно быть ошибкой:

--------- Return value from command: 0

На обеих машинах установлен Windows Installer 3.1.XP - 3.01.4001.5512, 2003 R2 - 3.01.4000.3959.

Таким образом, между полями действует нечто иное, хотя я понятия не имею, что.

РЕДАКТИРОВАТЬ 2

Точная строка таблицы для действия, созданная средством установки Wise для Windows, выглядит следующим образом:

"RunMigrationActionCA", "1537", "Calllaunchchildprocess", "RunMigrationAction"," 0 "

Чтобы проверить непосредственный флаг, я добавил 0x800 в столбец типа, и в конечном поведении не было замечено никаких изменений.

Для ясности - это прекрасно работает на 2003 R2машина.Эта машина не присоединена к домену, но машина XP есть.Есть ли что-то в групповой политике, что может вызвать такое поведение?(В этот момент хватается за соломинку.)

Ответы [ 2 ]

5 голосов
/ 15 июня 2013

Кажется, что это ошибка в cmd.exe WinXP.
Решение состоит в том, чтобы использовать exit 123 вместо exit /b 123 в командном файле.

Если вы не хотитечтобы изменить существующие пакетные файлы, просто добавьте файл wrapper.bat:

@echo off
call %*
exit %errorlevel%

и вызовите его:

system("wrapper.bat your.bat all your args")
0 голосов
/ 07 марта 2012

Объекты WScript не работают внутри пользовательских действий. Пожалуйста, читайте больше здесь .Вы можете использовать пользовательское действие DLL. Здесь - пошаговое руководство.

...