2012-03-06 2 views
2

EDIT 3код выхода «потерял» из дочернего процесса в Windows XP, а не в Windows Server 2003

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

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, а также эту функцию. И да, псудо-венгерский злой бла-бла-бла.

Редактировать: Вот версия кода C. Это дает точно такой же вопрос:

#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 Визуальный окне Studio 2008, я получаю код ошибки, как и ожидалось:

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

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

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

Обе машины имеют установщик Windows 3.1. XP - 3.01.4001.5512, 2003 R2 - 3.01.4000.3959.

Так что это что-то другое между коробками, хотя я понятия не имею, что.

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

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

"RunMigrationActionCA", "1 537", "Calllaunchchildprocess", «RunMigrationAction», «0»

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

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

+0

Если все, что вы хотите сделать, это запустить исполняемый файл , почему вы не используете запуск исполняемого типа пользовательского действия (базовый тип 2, 34 или 50)? Тогда вам даже не нужно возиться с CustomActionData. Возвращаемые значения 1 и 3 в установщик Windows также кажутся неправильными; они должны быть одним из значений, описанных на http://msdn.microsoft.com/en-us/library/windows/desktop/aa368072.aspx –

+0

Я вызываю пакетный файл, а не исполняемый файл, и, что еще хуже, мне нужно данные настраиваемого действия, доступные для параметризации вызова. 1 и 3 являются правильными для пользовательских действий скрипта - см. URL-адрес в комментарии к коду. –

+0

Хорошая точка в кодах возврата сценария (хотя версия C может пострадать, я бы сопоставил коды возврата от нее до одного из 0, 1602, 1603). Если вы хотите увидеть фактический код возврата, установите его в свойство или запишите его. Затем верните безопасный код из пользовательского действия. Регистрация поведения выполнения может помочь выяснить, когда и как все происходит. –

ответ

0

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

+0

Я ранее написанные пользовательские DLL-файлы; это огромный кошмар поддержки, чтобы дать клиенту, когда все, что я хочу сделать, это запустить подпроцесс. Я мог бы уйти от рассмотрения этого по-другому, поэтому я собираюсь пойти по этому маршруту.Для других, смотрящих на это, есть тонкий момент, который не описан в связанном блоге, но находится в комментариях; в отличие от некоторых мест (http://stackoverflow.com/questions/98778/executing-a-script-file-from-a-windows-installer-custom-action), которые прямо указывают на то, что ProgID находится в пространстве имен WScript имеет смысл. Что раздражает большинство работ. –

+0

ОК, ну, оказывается, это не имеет к этому никакого отношения. Я написал C DLL и имел ту же самую точную проблему. Кроме того, тот же код работает в моем тестовом окне Windows Server 2003 - я получаю код ошибки. Я не получаю код ошибки в Windows XP. Такая же версия установщика Windows. –

+0

Попробуйте выполнить собственное действие без олицетворения. – Ciprian

5

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

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

@echo off 
call %* 
exit %errorlevel% 

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

system("wrapper.bat your.bat all your args") 
+0

'exit /?' Говорит: * exitCode указывает числовое число. если задано/B, устанавливает ERRORLEVEL это число. Если вы выходите из CMD.EXE, он устанавливает код выхода процесса с этим номером. *, Поэтому предложенное вами решение дает желаемые результаты. Powershell наследует код выхода последней команды, как «код выхода из командной строки», как представляется, cmd, по крайней мере, не во всех случаях. –

+0

Были проблемы с выходом/b и WinXP, спасибо за это. – DaveU

Смежные вопросы