Я пытаюсь получить свойство MSI «UILevel» из настраиваемого действия C ++, чтобы определить, работает ли пользователь в «режиме без пользовательского интерфейса», но мне не везет. Функция, которую я вызываю, передается MSIHANDLE из функции, которую я экспортирую в мою DLL (это может быть действие «отложенного» или «первого порядка»). Я вижу, что MsiGetPropertyW
всегда возвращается ERROR_MORE_DATA
и trueLength
поле всегда 0. Вот мой код:
bool runningInNoUIMode(MSIHANDLE hInstall)
{
unsigned long nBufLen = 64UL;
WCHAR *wszValue = new WCHAR[nBufLen];
DWORD trueLength = 0UL;
UINT result = ::MsiGetPropertyW(hInstall, L"UILevel", L"", &trueLength); // Get the size of the property value first to see if there is enough storage allocated.
if (ERROR_MORE_DATA == result || nBufLen <= trueLength)
{
if (NULL != wszValue)
{
delete [] wszValue;
}
// Allocate more memory for the property adding one for the null terminator.
nBufLen = trueLength + 1;
wszValue = new WCHAR[nBufLen];
}
if (NULL == wszValue)
{
WcaLog(LOGMSG_STANDARD, "Unable to determine the user interface level the MSI is being run with because we were unable to allocate storage for accessing the 'UILevel' property.");
return false;
}
memset(wszValue, L'\0', nBufLen * sizeof(WCHAR));
result = ::MsiGetPropertyW(hInstall, L"UILevel", wszValue, &trueLength);
if (ERROR_SUCCESS != result)
{
WcaLog(LOGMSG_STANDARD, "Unable to determine the user interface level the MSI is being run with, error code = '%lu'.", result);
delete [] wszValue;
return false;
}
if (0 == wcscmp(L"2", wszValue)) // INSTALLUILEVEL_NONE == 2
{
delete [] wszValue;
return true;
}
delete [] wszValue;
return false;
}
Я полагаю, что пока могу обойти это, передав свойство UILevel через WiX и проверив его таким образом в C ++, но мне любопытно, в чем здесь проблема.
Я использую Visual Studio / Visual C ++ 2010 на Windows 7 с WiX 3.5.2519.
Спасибо за любую помощь, вы можете предоставить!
Еще один способ сделать это проще — использовать Функция MsiEvaluateCondition.
BOOL bUI = MsiEvaluateCondition(L"UILevel<3");
в C # с использованием Microsoft.Deployment.WindowsIntaller (DTF) это:
var uiLevel = session["UILevel"];
В C ++ есть образец на Функция MsiGetProperty:
UINT __stdcall MyCustomAction(MSIHANDLE hInstall)
{
TCHAR* szValueBuf = NULL;
DWORD cchValueBuf = 0;
UINT uiStat = MsiGetProperty(hInstall, TEXT("MyProperty"), TEXT(""), &cchValueBuf);
//cchValueBuf now contains the size of the property's string, without null termination
if (ERROR_MORE_DATA == uiStat)
{
++cchValueBuf; // add 1 for null termination
szValueBuf = new TCHAR[cchValueBuf];
if (szValueBuf)
{
uiStat = MsiGetProperty(hInstall, TEXT("MyProperty"), szValueBuf, &cchValueBuf);
}
}
if (ERROR_SUCCESS != uiStat)
{
if (szValueBuf != NULL)
delete[] szValueBuf;
return ERROR_INSTALL_FAILURE;
}
// custom action uses MyProperty
// ...
delete[] szValueBuf;
return ERROR_SUCCESS;
}
Благодаря @DanielGehriger мы выяснили, что проблема не в коде, а в планировании пользовательских действий. UILevel
Свойство MSI просто недоступен при запуске deferred
настраиваемое действие (я обнаружил, что код работает правильно для настраиваемого действия, запланированного для firstsequence
). Я обошел это ограничение, передав явную информацию о пользовательских действиях с помощью WiX:
<CustomAction Id="CustomAction.SetProperty" Property="CustomActionCall"Value="UILEVEL=[UILevel];" />
а затем проверить это в C ++ с WcaIsPropertySet
а также WcaGetProperty
, Обратите внимание, что здесь важен регистр символов имени свойства в квадратных скобках.