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. Есть ли что-либо в групповой политике, которая может вызвать такое поведение? (Захват на соломинках в этой точке.)
Если все, что вы хотите сделать, это запустить исполняемый файл , почему вы не используете запуск исполняемого типа пользовательского действия (базовый тип 2, 34 или 50)? Тогда вам даже не нужно возиться с CustomActionData. Возвращаемые значения 1 и 3 в установщик Windows также кажутся неправильными; они должны быть одним из значений, описанных на http://msdn.microsoft.com/en-us/library/windows/desktop/aa368072.aspx –
Я вызываю пакетный файл, а не исполняемый файл, и, что еще хуже, мне нужно данные настраиваемого действия, доступные для параметризации вызова. 1 и 3 являются правильными для пользовательских действий скрипта - см. URL-адрес в комментарии к коду. –
Хорошая точка в кодах возврата сценария (хотя версия C может пострадать, я бы сопоставил коды возврата от нее до одного из 0, 1602, 1603). Если вы хотите увидеть фактический код возврата, установите его в свойство или запишите его. Затем верните безопасный код из пользовательского действия. Регистрация поведения выполнения может помочь выяснить, когда и как все происходит. –