You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3670 lines
132 KiB
3670 lines
132 KiB
// muimsidll.cpp : Defines the entry point for the DLL application.
|
|
//
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <windows.h>
|
|
#include <tchar.h>
|
|
#include <Msiquery.h>
|
|
#include <wmistr.h>
|
|
#include <wmiumkm.h>
|
|
#include <Shlwapi.h>
|
|
#include <Setupapi.h>
|
|
#include <advpub.h>
|
|
#include <lmcons.h>
|
|
#include <strsafe.h>
|
|
#include <intlmsg.h>
|
|
|
|
|
|
//
|
|
// DEFINES
|
|
//
|
|
#define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
|
|
#define MUI_LANG_GROUP_FILE TEXT("muilang.txt")
|
|
#define BUFFER_SIZE 1024
|
|
#define MUISETUP_PATH_SEPARATOR TEXT("\\")
|
|
#define MUIDIR TEXT("MUI")
|
|
#define MUI_LANGPACK_SECTION TEXT("LanguagePack")
|
|
#define MUI_COMPONENTS_SECTION TEXT("Components")
|
|
#define LANGGROUPNUMBER 32
|
|
#define LANGPACKDISKCOST 300000000
|
|
#define DEFAULT_INSTALL_SECTION TEXT("DefaultInstall")
|
|
#define DEFAULT_UNINSTALL_SECTION TEXT("DefaultUninstall")
|
|
#define FALLBACKDIR TEXT("MUI\\FALLBACK")
|
|
#define EXTDIR TEXT("External")
|
|
#define COMP_TICK_INC 5000000
|
|
#define LANGPACK_TICK_INC 200000000
|
|
#define OEM_COMPONENT 1
|
|
|
|
#define SELECTMUIINFBINSTREAM TEXT("SELECT `Data` FROM `Binary` WHERE `Name` = 'MUIINF'")
|
|
|
|
// name of intl.cpl event source
|
|
#define REGOPT_EVENTSOURCE TEXT("SYSTEM\\CurrentControlSet\\Services\\EventLog\\System\\Regional and Language Options")
|
|
#define REGOPT_EVENTSOURCE_NAME TEXT("Regional and Language Options")
|
|
|
|
|
|
#ifdef MUI_DEDUG
|
|
#define DEBUGMSGBOX(a, b, c, d) MessageBox(a, b, c, d)
|
|
#else
|
|
#define DEBUGMSGBOX(a, b, c, d)
|
|
#endif
|
|
|
|
|
|
//
|
|
// TYPEDEFS
|
|
//
|
|
typedef
|
|
BOOL (WINAPI *pfnMUI_InstallMFLFiles)(
|
|
TCHAR* pMUIInstallLanguage
|
|
);
|
|
|
|
|
|
//
|
|
// Internal function prototypes
|
|
//
|
|
void NotifyKernel(LPTSTR LangList, ULONG Flags, MSIHANDLE hInstall);
|
|
BOOL MofCompileLanguage(LPTSTR Languages, MSIHANDLE hInstall);
|
|
BOOL EnumLanguageGroupLocalesProc(LGRPID langGroupId, LCID lcid, LPTSTR lpszLocale, LONG_PTR lParam);
|
|
BOOL EnumLanguageGroupsProc(LGRPID LanguageGroup, LPTSTR lpLanguageGroupString, LPTSTR lpLanguageGroupNameString, DWORD dwFlags, LONG_PTR lParam);
|
|
LGRPID GetLanguageGroup(LCID lcid, MSIHANDLE hInstall);
|
|
BOOL RunRegionalOptionsApplet(LPTSTR pCommands, BOOL bSilent, MSIHANDLE hInstall);
|
|
BOOL DeleteSideBySideMUIAssemblyIfExisted(LPTSTR Language, TCHAR pszLogFile[BUFFER_SIZE]);
|
|
BOOL ReturnAllRequiredLangGroups(LPTSTR szLcid, UINT cchLcidBufsize, LPTSTR szMuiInfPath, UINT cchPathbufsize, LGRPID *lgrpids, UINT *uiNumFoundGroups, MSIHANDLE hInstall);
|
|
BOOL ExecuteComponentINF(PTSTR pComponentName, PTSTR pComponentInfFile, PTSTR pInstallSection, BOOL bInstall, MSIHANDLE hInstall);
|
|
INT InstallComponentsMUIFiles(PTSTR pszLanguage, BOOL isInstall, MSIHANDLE hInstall);
|
|
BOOL FileExists(LPTSTR szFile);
|
|
void LogCustomActionInfo(MSIHANDLE hInstall, LPCTSTR szErrorMsg);
|
|
BOOL SetUILanguage(TCHAR *szLanguage, BOOL bCurrent, BOOL bDefault, MSIHANDLE hInstall);
|
|
UINT GetMUIComponentsNumber(PTSTR pszLanguage, MSIHANDLE hInstall);
|
|
BOOL GetMUIInfPath(TCHAR *szMUIInfPath, UINT cchBufSize, MSIHANDLE hInstall);
|
|
BOOL GetLCID(TCHAR *szLanguage, UINT cchBufSize, MSIHANDLE hInstall);
|
|
BOOL MUICchPathAppend(LPTSTR szDestination, UINT cchDestBufSize, LPTSTR szAppend, UINT cchAppBufSize, MSIHANDLE hInstall);
|
|
BOOL MUIReportInfoEvent(DWORD dwEventID, TCHAR *szLanguage, UINT cchBufSize, MSIHANDLE hInstall);
|
|
BOOL MUICheckEventSource(MSIHANDLE hInstall);
|
|
LANGID GetDotDefaultUILanguage(MSIHANDLE hInstall);
|
|
BOOL IsOEMSystem();
|
|
|
|
//
|
|
// Global Variables
|
|
//
|
|
// Flags to indicate whether a language group is found for the locale or not.
|
|
BOOL gFoundLangGroup;
|
|
LGRPID gLangGroup;
|
|
LCID gLCID;
|
|
|
|
// The language groups installed in the system.
|
|
LGRPID gLanguageGroups[LANGGROUPNUMBER] ;
|
|
int gNumLanguageGroups;
|
|
|
|
//
|
|
// Main dll entry point
|
|
//
|
|
BOOL APIENTRY DllMain( HANDLE hModule,
|
|
DWORD ul_reason_for_call,
|
|
LPVOID lpReserved)
|
|
{
|
|
switch (ul_reason_for_call)
|
|
{
|
|
case ( DLL_THREAD_ATTACH ) :
|
|
{
|
|
return (TRUE);
|
|
}
|
|
case ( DLL_THREAD_DETACH ) :
|
|
{
|
|
return (TRUE);
|
|
}
|
|
case ( DLL_PROCESS_ATTACH ) :
|
|
{
|
|
return (TRUE);
|
|
}
|
|
case ( DLL_PROCESS_DETACH ) :
|
|
{
|
|
return (TRUE);
|
|
}
|
|
}
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// DisableCancelButton
|
|
//
|
|
// The DisableCancelButton checks to see if a specific parameter has been passed
|
|
// to the current installation, if it has, it will issue a command to disable
|
|
// the cancel button in the UI during installation. This is used by our
|
|
// muisetup.exe wrapper so that the user cannot cancel out of an installation or
|
|
// uninstallation once it has started.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
UINT CA1(MSIHANDLE hInstall)
|
|
{
|
|
UINT uiRet = ERROR_SUCCESS;
|
|
PMSIHANDLE hRecord = MsiCreateRecord(3);
|
|
TCHAR szBuffer[BUFFER_SIZE] = { 0 };
|
|
HRESULT hr = S_OK;
|
|
|
|
if (NULL == hInstall)
|
|
{
|
|
uiRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
|
|
// if can't create a msi record, just return
|
|
if (NULL == hRecord)
|
|
{
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("CA1 Failure: cannot create MSI Record."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
uiRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
|
|
// field 0 = unused, field 1 = 2 (cancel button), field 2 = 0 (0 to disable/hide cancel button)
|
|
if (ERROR_SUCCESS != MsiRecordSetInteger(hRecord, 1, 2))
|
|
{
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("CA1 Failure: MsiRecordSetInteger function failed."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
uiRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
|
|
if (ERROR_SUCCESS != MsiRecordSetInteger(hRecord, 2, 0))
|
|
{
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("CA1 Failure: MsiRecordSetInteger function failed."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
uiRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
|
|
MsiProcessMessage(hInstall, INSTALLMESSAGE_COMMONDATA, hRecord);
|
|
|
|
Exit:
|
|
return uiRet;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// InstallComponentInfs
|
|
//
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
UINT CA10(MSIHANDLE hInstall)
|
|
{
|
|
TCHAR szLanguage[5] = {0};
|
|
TCHAR szBuffer[BUFFER_SIZE] = {0};
|
|
PMSIHANDLE hRec = MsiCreateRecord(3);
|
|
PMSIHANDLE hProgressRec = MsiCreateRecord(3);
|
|
UINT iFunctionResult = ERROR_SUCCESS;
|
|
INT iInstallResult = IDOK;
|
|
HRESULT hr = S_OK;
|
|
|
|
if (NULL == hInstall)
|
|
{
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
if ((NULL == hRec) || (NULL == hProgressRec))
|
|
{
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("CA10 Failure: cannot create MSI Record."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
if (!GetLCID(szLanguage, ARRAYSIZE(szLanguage), hInstall))
|
|
{
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("CA10 Failure: Cannot retrieve MuiLCID property."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
// Tell the installer to check the installation state and execute
|
|
// the code needed during the rollback, acquisition, or
|
|
// execution phases of the installation.
|
|
if (MsiGetMode(hInstall,MSIRUNMODE_ROLLBACK))
|
|
{
|
|
// Installer is rolling back the installation, here we just remove what we had before
|
|
// Since we are in rollback, we don't set progress bar or update message in UI
|
|
// we also don't check for returned results here, since the installation has failed already.
|
|
InstallComponentsMUIFiles(szLanguage, FALSE, hInstall);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
if (!MsiGetMode(hInstall,MSIRUNMODE_SCHEDULED))
|
|
{
|
|
// Installer is generating the installation script of the custom
|
|
// action. Tell the installer to increase the value of the final total
|
|
// length of the progress bar by the total number of ticks in the
|
|
// custom action.
|
|
|
|
UINT iCount = GetMUIComponentsNumber(szLanguage, hInstall);
|
|
|
|
if (iCount > 0)
|
|
{
|
|
MsiRecordSetInteger(hRec,1,3);
|
|
MsiRecordSetInteger(hRec,2,COMP_TICK_INC * iCount);
|
|
MsiRecordSetInteger(hRec,3,0);
|
|
iInstallResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRec);
|
|
}
|
|
|
|
//
|
|
// we just want to trap the cancel message here, otherwise we always return success since
|
|
// we are just setting progressbar here.
|
|
//
|
|
if (iInstallResult == IDCANCEL)
|
|
{
|
|
return ERROR_INSTALL_USEREXIT;
|
|
}
|
|
else
|
|
{
|
|
return ERROR_SUCCESS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Installer is executing the installation script. Set up a
|
|
// record specifying appropriate templates and text for messages
|
|
// that will inform the user about what the custom action is
|
|
// doing. Tell the installer to use this template and text in
|
|
// progress messages.
|
|
MsiRecordSetString(hRec,1,TEXT("Installing Components."));
|
|
MsiRecordSetString(hRec,2,TEXT("Installing External Component Inf files..."));
|
|
MsiRecordSetString(hRec,3,TEXT("Installing MUI files for Component [1]."));
|
|
MsiProcessMessage(hInstall, INSTALLMESSAGE_ACTIONSTART, hRec);
|
|
|
|
// Tell the installer to use explicit progress messages.
|
|
MsiRecordSetInteger(hRec,1,1);
|
|
MsiRecordSetInteger(hRec,2,1);
|
|
MsiRecordSetInteger(hRec,3,0);
|
|
MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRec);
|
|
|
|
// do the actual work for the custom action
|
|
iInstallResult = InstallComponentsMUIFiles(szLanguage, TRUE, hInstall);
|
|
if (IDCANCEL == iInstallResult)
|
|
{
|
|
iFunctionResult = ERROR_INSTALL_USEREXIT;
|
|
}
|
|
else if (-1 == iInstallResult)
|
|
{
|
|
iFunctionResult = ERROR_INSTALL_FAILURE;
|
|
}
|
|
else
|
|
{
|
|
iFunctionResult = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return iFunctionResult;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// UninstallComponentInfs
|
|
//
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
UINT CA11(MSIHANDLE hInstall)
|
|
{
|
|
TCHAR szLanguage[5] = {0};
|
|
TCHAR szBuffer[BUFFER_SIZE] = {0};
|
|
PMSIHANDLE hRec = MsiCreateRecord(3);
|
|
PMSIHANDLE hProgressRec = MsiCreateRecord(3);
|
|
UINT iFunctionResult = ERROR_SUCCESS;
|
|
INT iInstallResult = IDOK;
|
|
HRESULT hr = S_OK;
|
|
|
|
if (NULL == hInstall)
|
|
{
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
if ((NULL == hRec) || (NULL == hProgressRec))
|
|
{
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("CA11 Failure: cannot create MSI Record."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
if (!GetLCID(szLanguage, ARRAYSIZE(szLanguage), hInstall))
|
|
{
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("CA11 Failure: Cannot retrieve MuiLCID property."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
|
|
// Tell the installer to check the installation state and execute
|
|
// the code needed during the rollback, acquisition, or
|
|
// execution phases of the installation.
|
|
if (MsiGetMode(hInstall,MSIRUNMODE_ROLLBACK))
|
|
{
|
|
// Installer is rolling back the installation. We will reinstall what we uninstalled before.
|
|
// we don't update progress message here. And we always return SUCCESS
|
|
InstallComponentsMUIFiles(szLanguage, TRUE, hInstall);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
if (!MsiGetMode(hInstall,MSIRUNMODE_SCHEDULED))
|
|
{
|
|
// Installer is generating the installation script of the custom
|
|
// action. Tell the installer to increase the value of the final total
|
|
// length of the progress bar by the total number of ticks in the
|
|
// custom action.
|
|
UINT iCount = GetMUIComponentsNumber(szLanguage, hInstall);
|
|
if (iCount > 0)
|
|
{
|
|
MsiRecordSetInteger(hRec,1,3);
|
|
MsiRecordSetInteger(hRec,2,COMP_TICK_INC * iCount);
|
|
MsiRecordSetInteger(hRec,3,0);
|
|
iInstallResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRec);
|
|
}
|
|
//
|
|
// we just want to trap the cancel message here, otherwise we always return success since
|
|
// we are just setting progressbar here.
|
|
//
|
|
if (iInstallResult == IDCANCEL)
|
|
{
|
|
return ERROR_INSTALL_USEREXIT;
|
|
}
|
|
else
|
|
{
|
|
return ERROR_SUCCESS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Installer is executing the installation script. Set up a
|
|
// record specifying appropriate templates and text for messages
|
|
// that will inform the user about what the custom action is
|
|
// doing. Tell the installer to use this template and text in
|
|
// progress messages.
|
|
MsiRecordSetString(hRec,1,TEXT("Uninstall Components."));
|
|
MsiRecordSetString(hRec,2,TEXT("Removing External Component Inf files..."));
|
|
MsiRecordSetString(hRec,3,TEXT("Removing MUI files for Component [1]."));
|
|
MsiProcessMessage(hInstall, INSTALLMESSAGE_ACTIONSTART, hRec);
|
|
|
|
// Tell the installer to use explicit progress messages.
|
|
MsiRecordSetInteger(hRec,1,1);
|
|
MsiRecordSetInteger(hRec,2,1);
|
|
MsiRecordSetInteger(hRec,3,0);
|
|
MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRec);
|
|
|
|
// do the actual work for the custom action, we only check for user cancel here
|
|
// and nothing else
|
|
iInstallResult = InstallComponentsMUIFiles(szLanguage, FALSE, hInstall);
|
|
if (IDCANCEL == iInstallResult)
|
|
{
|
|
iFunctionResult = ERROR_INSTALL_USEREXIT;
|
|
}
|
|
else if (-1 == iInstallResult)
|
|
{
|
|
iFunctionResult = ERROR_INSTALL_FAILURE;
|
|
}
|
|
else
|
|
{
|
|
iFunctionResult = ERROR_SUCCESS;
|
|
}
|
|
|
|
}
|
|
|
|
return iFunctionResult;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// SetLangPackRequirement
|
|
//
|
|
// This function is used to set a property in the MSI Database so that the installation knows whether
|
|
// it needs to install the language pack or not so it can reserve diskcost for it
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
UINT CA3(MSIHANDLE hInstall)
|
|
{
|
|
LGRPID lgrpid[LANGGROUPNUMBER] = {0};
|
|
UINT iRet = ERROR_SUCCESS;
|
|
UINT uiLGrpNums = 0;
|
|
UINT i;
|
|
DWORD uiAddCost = 0;
|
|
TCHAR tcMessage[BUFFER_SIZE] = {0};
|
|
TCHAR szMUIInfPath[MAX_PATH+1] = {0};
|
|
TCHAR szLanguage[5] = {0};
|
|
HRESULT hr = S_OK;
|
|
|
|
if (NULL == hInstall)
|
|
{
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
if (!GetLCID(szLanguage, ARRAYSIZE(szLanguage), hInstall))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA3 Failure: Cannot retrieve MuiLCID property."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
if (!GetMUIInfPath(szMUIInfPath, MAX_PATH+1, hInstall))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA3 Failure: Cannot find installation temp file."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
if (!ReturnAllRequiredLangGroups(szLanguage, ARRAYSIZE(szLanguage), szMUIInfPath, ARRAYSIZE(szMUIInfPath), lgrpid, &uiLGrpNums, hInstall))
|
|
{
|
|
// log an error
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA3: ReturnAllRequiredLangGroups failed."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
iRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
|
|
#ifdef MUI_DEBUG
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA3: The number of lang groups required is %d."), uiLGrpNums);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DEBUGMSGBOX(NULL, tcMessage, NULL, MB_OK);
|
|
}
|
|
#endif
|
|
|
|
// Enumerate through all the lang groups required, check there are any language groups that requires installation
|
|
for (i = 0; i < uiLGrpNums; i++)
|
|
{
|
|
if (!IsValidLanguageGroup(lgrpid[i], LGRPID_INSTALLED))
|
|
{
|
|
uiAddCost += LANGPACKDISKCOST;
|
|
}
|
|
}
|
|
|
|
if (uiAddCost > 0)
|
|
{
|
|
DEBUGMSGBOX(NULL, TEXT("CA3: Need to install additional language groups."), NULL, MB_OK);
|
|
if (ERROR_SUCCESS != MsiSetProperty(hInstall, TEXT("MsiRequireLangPack"), TEXT("1")))
|
|
{
|
|
// log an error
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA3 Failure: Cannot set MsiRequireLangPack property in the MSI Database."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
iRet = ERROR_INSTALL_FAILURE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DEBUGMSGBOX(NULL, TEXT("CA3: Language group already installed."), NULL, MB_OK);
|
|
if (ERROR_SUCCESS != MsiSetProperty(hInstall, TEXT("MsiRequireLangPack"), NULL))
|
|
{
|
|
// log an error
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA3 Failure: Cannot set MsiRequireLangPack property in the MSI Database."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
iRet = ERROR_INSTALL_FAILURE;
|
|
}
|
|
}
|
|
|
|
|
|
Exit:
|
|
return iRet;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// IsNTSuiteWebBlade
|
|
//
|
|
// This function is used by a custom action in our setup package to detect whether setup was invoked on a windows Blade server.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
/*UINT CA5(MSIHANDLE hInstall)
|
|
{
|
|
OSVERSIONINFOEX osvi;
|
|
TCHAR tcMessage[BUFFER_SIZE] = {0};
|
|
HRESULT hr = S_OK;
|
|
|
|
if (NULL == hInstall)
|
|
{
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
|
|
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
|
|
|
if (!GetVersionEx ((OSVERSIONINFO *) &osvi))
|
|
{
|
|
// log an error
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA5 Failure: GetVersionEx failed, cannot retrieve platform OS version. Error returned is: %d."), GetLastError());
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
else
|
|
{
|
|
if ((osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) && // test for NT
|
|
( (osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion > 0) ) && // test for version > 5.0 (Whistler or later)
|
|
( (osvi.wSuiteMask & VER_SUITE_BLADE ) != 0) && // test for Suite Web Server
|
|
( (osvi.wProductType != VER_NT_WORKSTATION ) )) // test for non-workstation type (server)
|
|
{
|
|
// here we need to set a MSI property so that the current installation knows that NT Suite is WebBlade
|
|
if (ERROR_SUCCESS != MsiSetProperty(hInstall, TEXT("MsiNTSuiteWebBlade"), TEXT("1")))
|
|
{
|
|
// log an error
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA5 Failure: Failed to set required MUI MSI property."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return ERROR_INSTALL_FAILURE; // can't set the property, return error
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// here we need to set a MSI property so that the current installation knows that NT Suite is WebBlade
|
|
if (ERROR_SUCCESS != MsiSetProperty(hInstall, TEXT("MsiNTSuiteWebBlade"), NULL))
|
|
{
|
|
// log an error
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA5: Failed to set required MUI MSI property."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return ERROR_INSTALL_FAILURE; // can't set the property, return error
|
|
}
|
|
}
|
|
}
|
|
return ERROR_SUCCESS;
|
|
}
|
|
*/
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CheckDefaultSystemUILang
|
|
//
|
|
// This function is used by our setup to check whether setup is invoked on a system with US-English as the default language (0x0409)
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
UINT CA4(MSIHANDLE hInstall)
|
|
{
|
|
// get the system default UI language, and set a MSI property accordingly
|
|
LANGID liSysLang = GetSystemDefaultUILanguage();
|
|
TCHAR tcMessage[BUFFER_SIZE] = {0};
|
|
HRESULT hr = S_OK;
|
|
|
|
if (NULL == hInstall)
|
|
{
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
if (liSysLang == 0x0409)
|
|
{
|
|
if (ERROR_SUCCESS != MsiSetProperty(hInstall, TEXT("MUISystemLangIsEnglish"), TEXT("1")))
|
|
{
|
|
// log an error
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA4 Failure: Failed to set property MUISystemLangIsEnglish."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return ERROR_INSTALL_FAILURE; // can't set the property, return error
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ERROR_SUCCESS != MsiSetProperty(hInstall, TEXT("MUISystemLangIsEnglish"), NULL))
|
|
{
|
|
// log an error
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA4 Failure: Failed to set property MUISystemLangIsEnglish."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return ERROR_INSTALL_FAILURE; // can't set the property, return error
|
|
}
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// LogInstallComplete
|
|
//
|
|
// This function is used by setup to log a message to the system event logger
|
|
// to indicate that the MUI language is installed.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
UINT CA12(MSIHANDLE hInstall)
|
|
{
|
|
TCHAR tcMessage[BUFFER_SIZE] = {0};
|
|
TCHAR szLanguage[5] = {0};
|
|
HRESULT hr = S_OK;
|
|
|
|
if (!GetLCID(szLanguage, ARRAYSIZE(szLanguage), hInstall))
|
|
{
|
|
// log an error
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA12 Failure: Failed to get property MUILcid."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
if (MUIReportInfoEvent(MSG_REGIONALOPTIONS_LANGUAGEINSTALL, szLanguage, BUFFER_SIZE, hInstall))
|
|
{
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
// log an error, if we get to here it's always an error
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA12 Failure: Failed to log to system event logfile."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// LogUninstallComplete
|
|
//
|
|
// This function is used by setup to log a message to the system event logger
|
|
// to indicate that the MUI language is uninstalled.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
UINT CA13(MSIHANDLE hInstall)
|
|
{
|
|
TCHAR tcMessage[BUFFER_SIZE] = {0};
|
|
TCHAR szLanguage[5] = {0};
|
|
HRESULT hr = S_OK;
|
|
|
|
if (!GetLCID(szLanguage, ARRAYSIZE(szLanguage), hInstall))
|
|
{
|
|
// log an error
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA13 Failure: Failed to get property MUILcid."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
if (MUIReportInfoEvent(MSG_REGIONALOPTIONS_LANGUAGEUNINSTALL, szLanguage, BUFFER_SIZE, hInstall))
|
|
{
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
// log an error, if we get to here it's always an error
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA13 Failure: Failed to log to system event logfile."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// InstallWBEMMUI
|
|
//
|
|
// This function is used to set up WMI\WBEM stuff for MUI.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
UINT CA6(MSIHANDLE hInstall)
|
|
{
|
|
UINT iRet = ERROR_SUCCESS;
|
|
TCHAR szLanguage[5] = {0};
|
|
TCHAR tcMessage[BUFFER_SIZE] = {0};
|
|
HRESULT hr = S_OK;
|
|
size_t cch = 0;
|
|
|
|
if (NULL == hInstall)
|
|
{
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
if (!GetLCID(szLanguage, ARRAYSIZE(szLanguage), hInstall))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA6 Failure: Cannot retrieve MuiLCID property."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
//
|
|
// call WBEM API to mofcompile MUI MFL's for each language
|
|
//
|
|
if (!MofCompileLanguage(szLanguage, hInstall))
|
|
{
|
|
// log an error
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA6 Failure: MofCompileLanguage Failed."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
iRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Inform kernel that new languages have been added
|
|
//
|
|
NotifyKernel(szLanguage, WMILANGUAGECHANGE_FLAG_ADDED, hInstall);
|
|
|
|
Exit:
|
|
return iRet;
|
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// UninstallWBEMMUI
|
|
//
|
|
// This function is called by our setup to uninstall external component associated with a MSI package.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
UINT CA7(MSIHANDLE hInstall)
|
|
{
|
|
UINT iRet = ERROR_SUCCESS;
|
|
TCHAR szLanguage[5] = {0};
|
|
TCHAR tcMessage[BUFFER_SIZE] = {0};
|
|
HRESULT hr = S_OK;
|
|
size_t cch = 0;
|
|
|
|
if (NULL == hInstall)
|
|
{
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
if (!GetLCID(szLanguage, ARRAYSIZE(szLanguage), hInstall))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA7 Failure: Cannot retrieve MuiLCID property."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
//
|
|
// Inform kernel that new languages have been added
|
|
//
|
|
NotifyKernel(szLanguage, WMILANGUAGECHANGE_FLAG_REMOVED, hInstall);
|
|
|
|
return iRet;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// SetDefaultUserLanguage
|
|
//
|
|
// This function does two things - depending on the MSI execution mode:
|
|
//
|
|
// 1. Immediate:
|
|
// This function in immediate mode will schedule the deferred and the rollback
|
|
// custom action C8D/C8R. (C8D and C8R are the expected CA identifiers used
|
|
// in the MUI MSI package)
|
|
//
|
|
// 2. Deferred/Rollback:
|
|
// This function sets the default language of new users to that of the MUI
|
|
// language that is being installed. GetLCID in this instance will read
|
|
// the set CustomActionData property which is what we want.
|
|
//
|
|
// Also, reboot is needed after this CA in deferred mode, but this is specified
|
|
// in the template itself, so the CA itself will not prompt for reboot (it can't
|
|
// anyways, since it is deferred).
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
UINT CA8(MSIHANDLE hInstall)
|
|
{
|
|
UINT iRet = ERROR_SUCCESS;
|
|
TCHAR szLanguage[5] = {0};
|
|
TCHAR szOrigLanguage[5] = {0};
|
|
TCHAR tcMessage[BUFFER_SIZE] = {0};
|
|
HRESULT hr = S_OK;
|
|
size_t cch = 0;
|
|
|
|
if (NULL == hInstall)
|
|
{
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
// get the MUI LCID that we want to set the default UI Language to.
|
|
if (!GetLCID(szLanguage, ARRAYSIZE(szLanguage), hInstall))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA8 Failure: Cannot retrieve MuiLCID property."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
iRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
|
|
// if immediate mode (not scheduled/commit/rollback), schedule the custom actions
|
|
if (!MsiGetMode(hInstall, MSIRUNMODE_SCHEDULED) &&
|
|
!MsiGetMode(hInstall, MSIRUNMODE_ROLLBACK)&&
|
|
!MsiGetMode(hInstall, MSIRUNMODE_COMMIT))
|
|
{
|
|
// get the current default UI language
|
|
LANGID lgID = GetDotDefaultUILanguage(hInstall);
|
|
hr = StringCchPrintf(szOrigLanguage, ARRAYSIZE(szOrigLanguage), TEXT("%04x"), lgID);
|
|
if (FAILED(hr))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA8 Failure: Cannot retrieve default UI language."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
iRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
|
|
// schedule the appropriate custom actions and property setting custom actions
|
|
// Rollback custom action goes first
|
|
// Create a rollback custom action (in case install is stopped and rolls back)
|
|
// Rollback custom action can't read tables, so we have to set a property
|
|
if (ERROR_SUCCESS != MsiSetProperty(hInstall, TEXT("CA8R"), szOrigLanguage))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA8 Failure: Failed to set rollback custom action property."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
iRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
if (ERROR_SUCCESS != MsiDoAction(hInstall, TEXT("CA8R")))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA8 Failure: Failed to schedule rollback custom action."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
iRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
|
|
// Create a deferred custom action (gives us the right priviledges to create the user account)
|
|
// Deferred custom actions can't read tables, so we have to set a property
|
|
if (ERROR_SUCCESS != MsiSetProperty(hInstall, TEXT("CA8D"), szLanguage))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA8 Failure: Failed to set deferred custom action property."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
iRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
|
|
if (ERROR_SUCCESS != MsiDoAction(hInstall, TEXT("CA8D")))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA8 Failure: Failed to schedule deferred custom action."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
iRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (FALSE == SetUILanguage(szLanguage, FALSE, TRUE, hInstall))
|
|
{
|
|
// log an error
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA8: Failed to set Default UI language."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
iRet = ERROR_INSTALL_FAILURE;
|
|
}
|
|
else
|
|
{
|
|
iRet = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
return iRet;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// SetCurrentUserLanguage
|
|
//
|
|
// This function does two things - depending on the MSI execution mode:
|
|
//
|
|
// 1. Immediate:
|
|
// This function in immediate mode will schedule the deferred and the rollback
|
|
// custom action C9D/C9R. (C9D and C9R are the expected CA identifiers used
|
|
// in the MUI MSI package)
|
|
//
|
|
// For rollback functions it will also capture the original LCID of the
|
|
// current user and use that as the custom action for the rollback.
|
|
//
|
|
// 2. Deferred:
|
|
// This function sets the UI language of the current users to that of the MUI
|
|
// language that is being installed.
|
|
//
|
|
// Also, reboot is needed after this CA in deferred mode, but this is specified
|
|
// in the template itself, so the CA itself will not prompt for reboot (it can't
|
|
// anyways, since it is deferred).
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
UINT CA9(MSIHANDLE hInstall)
|
|
{
|
|
UINT iRet = ERROR_SUCCESS;
|
|
TCHAR szLanguage[5] = {0};
|
|
TCHAR szOrigLanguage[5] = {0};
|
|
TCHAR tcMessage[BUFFER_SIZE] = {0};
|
|
HRESULT hr = S_OK;
|
|
size_t cch = 0;
|
|
|
|
if (NULL == hInstall)
|
|
{
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
// get the MUI LCID that we want to set the current UI Language to.
|
|
if (!GetLCID(szLanguage, ARRAYSIZE(szLanguage), hInstall))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA9 Failure: Cannot retrieve MuiLCID property."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
iRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
|
|
// if immediate mode (not scheduled/commit/rollback), schedule the custom actions
|
|
if (!MsiGetMode(hInstall, MSIRUNMODE_SCHEDULED) &&
|
|
!MsiGetMode(hInstall, MSIRUNMODE_ROLLBACK)&&
|
|
!MsiGetMode(hInstall, MSIRUNMODE_COMMIT))
|
|
{
|
|
// get the current user UI language
|
|
LANGID lgID = GetUserDefaultUILanguage();
|
|
hr = StringCchPrintf(szOrigLanguage, ARRAYSIZE(szOrigLanguage), TEXT("%04x"), lgID);
|
|
if (FAILED(hr))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA9 Failure: Cannot retrieve current UI language."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
iRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
|
|
// schedule the appropriate custom actions and property setting custom actions
|
|
// Rollback custom action goes first
|
|
// Create a rollback custom action (in case install is stopped and rolls back)
|
|
// Rollback custom action can't read tables, so we have to set a property
|
|
if (ERROR_SUCCESS != MsiSetProperty(hInstall, TEXT("CA9R"), szOrigLanguage))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA9 Failure: Failed to set rollback custom action property."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
iRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
if (ERROR_SUCCESS != MsiDoAction(hInstall, TEXT("CA9R")))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA9 Failure: Failed to schedule rollback custom action."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
iRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
|
|
// Create a deferred custom action (gives us the right priviledges to create the user account)
|
|
// Deferred custom actions can't read tables, so we have to set a property
|
|
if (ERROR_SUCCESS != MsiSetProperty(hInstall, TEXT("CA9D"), szLanguage))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA9 Failure: Failed to set deferred custom action property."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
iRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
|
|
if (ERROR_SUCCESS != MsiDoAction(hInstall, TEXT("CA9D")))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA9 Failure: Failed to schedule deferred custom action."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
iRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (FALSE == SetUILanguage(szLanguage, TRUE, FALSE, hInstall))
|
|
{
|
|
// log an error
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA9: Failed to set current UI language."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
iRet = ERROR_INSTALL_FAILURE;
|
|
}
|
|
else
|
|
{
|
|
iRet = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
return iRet;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// InstallLanguageGroup
|
|
//
|
|
// This function is called by our setup to install language group files if they are needed.
|
|
// Note, a reboot is required after installation of the langpack, however, this is
|
|
// flagged using a property by the SetLangPackRequirement function instead.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
UINT CA2(MSIHANDLE hInstall)
|
|
{
|
|
LGRPID lgrpid[LANGGROUPNUMBER] = {0};
|
|
UINT iRet = ERROR_SUCCESS;
|
|
UINT uiLGrpNums = 0;
|
|
UINT i;
|
|
TCHAR tcMessage[BUFFER_SIZE] = {0};
|
|
TCHAR szLanguage[5] = {0};
|
|
TCHAR szMuiInfPath[MAX_PATH+1] = {0};
|
|
TCHAR * szUILevel = NULL;
|
|
PMSIHANDLE hRec = MsiCreateRecord(3);
|
|
PMSIHANDLE hProgressRec = MsiCreateRecord(3);
|
|
UINT iTemp = ERROR_SUCCESS;
|
|
BOOL bSilent = FALSE;
|
|
HRESULT hr = S_OK;
|
|
size_t cch = 0;
|
|
INT iResult = IDOK;
|
|
|
|
if (NULL == hInstall)
|
|
{
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
if ((NULL == hRec) || (NULL == hProgressRec))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA2 Failure: cannot create a MSI record."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
if (!GetLCID(szLanguage, ARRAYSIZE(szLanguage), hInstall))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA2 Failure: Cannot retrieve MuiLCID property."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
iRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
|
|
// Tell the installer to check the installation state and execute
|
|
// the code needed during the rollback, acquisition, or
|
|
// execution phases of the installation.
|
|
if (MsiGetMode(hInstall,MSIRUNMODE_ROLLBACK))
|
|
{
|
|
// Installer is rolling back the installation. Additional code
|
|
// could be inserted here to enable the custom action to do
|
|
// something during an installation rollback.
|
|
iRet = ERROR_SUCCESS;
|
|
goto Exit;
|
|
}
|
|
|
|
if (!MsiGetMode(hInstall,MSIRUNMODE_SCHEDULED))
|
|
{
|
|
if (!GetMUIInfPath(szMuiInfPath, MAX_PATH+1, hInstall))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA2 Failure: Cannot find installation temp file."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
if (!ReturnAllRequiredLangGroups(szLanguage, ARRAYSIZE(szLanguage), szMuiInfPath, ARRAYSIZE(szMuiInfPath), lgrpid, &uiLGrpNums, hInstall))
|
|
{
|
|
// log an error
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA2: ReturnAllRequiredLangGroups function failed."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
iRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
|
|
// uiLGrpNums should be less than 32, the size of the passed-in buffer, if it is returned as larger,
|
|
// we truncate that number down to 32.
|
|
if (uiLGrpNums > LANGGROUPNUMBER)
|
|
{
|
|
uiLGrpNums = LANGGROUPNUMBER;
|
|
}
|
|
|
|
// Installer is generating the installation script of the custom
|
|
// action. Tell the installer to increase the value of the final total
|
|
// length of the progress bar by the total number of ticks in the
|
|
// custom action.
|
|
MsiRecordSetInteger(hRec,1,3);
|
|
MsiRecordSetInteger(hRec,2,LANGPACK_TICK_INC * uiLGrpNums);
|
|
MsiRecordSetInteger(hRec,3,0);
|
|
MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRec);
|
|
iRet = ERROR_SUCCESS;
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
// Installer is executing the installation script. Set up a
|
|
// record specifying appropriate templates and text for messages
|
|
// that will inform the user about what the custom action is
|
|
// doing. Tell the installer to use this template and text in
|
|
// progress messages.
|
|
|
|
// Get the CustomActionData Property that tells us whether we are going to pop up dialog for windows source or not
|
|
// the first character is the UILevel in CustomActionData (MuiLCID UILevel)
|
|
DWORD dwCount = 7; // e.g. "0404 1\0" adds up to 7
|
|
TCHAR szCustomActionData[7] = {0};
|
|
|
|
if (ERROR_SUCCESS != MsiGetProperty(hInstall, TEXT("CustomActionData"), szCustomActionData, &dwCount))
|
|
{
|
|
// log an error
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA2: Failed to get CustomActionData property - assuming we are calling intl.cpl in silent mode."));
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// we can't validate much here, if buffer overruns, MsiGetProperty will return failure.
|
|
hr = StringCchLength(szCustomActionData, ARRAYSIZE(szCustomActionData), &cch);
|
|
if (FAILED(hr) || (cch >= 7))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA2 Failure: CustomActionData property value is invalid."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
szCustomActionData[4] = UNICODE_NULL; // end of MuiLCID portion
|
|
szCustomActionData[6] = UNICODE_NULL; // end of UILevel portion
|
|
szUILevel = szCustomActionData + 5;
|
|
|
|
#ifdef MUI_DEBUG
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA2: UI Level is set to %s."), szUILevel);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
#endif
|
|
if (INSTALLUILEVEL_NONE == (INSTALLUILEVEL) _tcstol(szUILevel, NULL, 10))
|
|
bSilent = TRUE;
|
|
else
|
|
bSilent = FALSE;
|
|
}
|
|
|
|
if (!GetMUIInfPath(szMuiInfPath, MAX_PATH+1, hInstall))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA2 Failure: Cannot find installation temp file."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
if (!ReturnAllRequiredLangGroups(szLanguage, ARRAYSIZE(szLanguage), szMuiInfPath, ARRAYSIZE(szMuiInfPath), lgrpid, &uiLGrpNums, hInstall))
|
|
{
|
|
// log an error
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA2: ReturnAllRequiredLangGroups function failed."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
iRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
|
|
// uiLGrpNums should be less than 32, the size of the passed-in buffer, if it is returned as larger,
|
|
// we truncate that number down to 32.
|
|
if (uiLGrpNums > LANGGROUPNUMBER)
|
|
{
|
|
uiLGrpNums = LANGGROUPNUMBER;
|
|
}
|
|
|
|
MsiRecordSetString(hRec,1,TEXT("Install LanguageGroup."));
|
|
MsiRecordSetString(hRec,2,TEXT("Installing language groups files ..."));
|
|
MsiRecordSetString(hRec,3,TEXT("Installing language group [1]."));
|
|
MsiProcessMessage(hInstall, INSTALLMESSAGE_ACTIONSTART, hRec);
|
|
|
|
// Tell the installer to use explicit progress messages.
|
|
MsiRecordSetInteger(hRec,1,1);
|
|
MsiRecordSetInteger(hRec,2,1);
|
|
MsiRecordSetInteger(hRec,3,0);
|
|
iResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRec);
|
|
if (iResult != IDOK)
|
|
{
|
|
if (iResult == IDCANCEL)
|
|
{
|
|
iRet = ERROR_INSTALL_USEREXIT;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// do the actual work for the custom action
|
|
// Enumerate through all the lang groups required, check if language group already installed, if so, just return success
|
|
// otherwise install it
|
|
iRet = ERROR_SUCCESS;
|
|
for (i = 0; i < uiLGrpNums; i++)
|
|
{
|
|
// display on the UI that we are installing language group lgrpid[i]
|
|
MsiRecordSetInteger(hRec,1,lgrpid[i]);
|
|
iResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_ACTIONDATA, hRec);
|
|
if (iResult != IDOK)
|
|
{
|
|
if (iResult == IDCANCEL)
|
|
{
|
|
iRet = ERROR_INSTALL_USEREXIT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!IsValidLanguageGroup(lgrpid[i], LGRPID_INSTALLED))
|
|
{
|
|
TCHAR pCommands[MAX_PATH] = {0};
|
|
|
|
hr = StringCchPrintf(pCommands, ARRAYSIZE(pCommands), TEXT("LanguageGroup = %d"), lgrpid[i]);
|
|
if (FAILED(hr))
|
|
{
|
|
// log an error
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA2: Failed to install language group %d."), lgrpid[i]);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
iRet = ERROR_INSTALL_FAILURE;
|
|
break;
|
|
}
|
|
|
|
DEBUGMSGBOX(NULL, pCommands, NULL, MB_OK);
|
|
if (!RunRegionalOptionsApplet(pCommands, bSilent, hInstall))
|
|
{
|
|
// log an error
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA2: Failed to install language group %d."), lgrpid[i]);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
iRet = ERROR_INSTALL_FAILURE;
|
|
break;
|
|
}
|
|
}
|
|
if (!IsValidLanguageGroup(lgrpid[i], LGRPID_INSTALLED))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA2: Failed to install language group %d."), lgrpid[i]);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
iRet = ERROR_INSTALL_FAILURE;
|
|
break;
|
|
}
|
|
|
|
// we installed the current language group, update progress bar and move onto the next one
|
|
MsiRecordSetInteger(hProgressRec,1,2);
|
|
MsiRecordSetInteger(hProgressRec,2,LANGPACK_TICK_INC);
|
|
MsiRecordSetInteger(hProgressRec,3,0);
|
|
iResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hProgressRec);
|
|
if (iResult != IDOK)
|
|
{
|
|
if (iResult == IDCANCEL)
|
|
{
|
|
iRet = ERROR_INSTALL_USEREXIT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
return iRet;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// DeleteMUIInfFile
|
|
//
|
|
// This custom action will delete the extracted mui.tmp file that we are using
|
|
// during the installation from the temporary directory.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
UINT CA15(MSIHANDLE hInstall)
|
|
{
|
|
TCHAR tcMessage[BUFFER_SIZE] = {0};
|
|
TCHAR tcMUIINFPath[MAX_PATH+1] = {0};
|
|
UINT uiRet = ERROR_SUCCESS;
|
|
HRESULT hr = S_OK;
|
|
DWORD cbPathSize = MAX_PATH+1;
|
|
|
|
// form a path to the temporary directory that we want %windir%\mui.tmp
|
|
cbPathSize = GetSystemWindowsDirectory(tcMUIINFPath, MAX_PATH+1);
|
|
if ((0 == cbPathSize) || (MAX_PATH+1 < cbPathSize))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA15 Failure: failed to get windows directory path."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
if (!MUICchPathAppend(tcMUIINFPath, ARRAYSIZE(tcMUIINFPath), TEXT("mui.tmp"), 8, hInstall))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA15 Failure: failed to get installation temp file path."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
if (FileExists(tcMUIINFPath))
|
|
{
|
|
if (!DeleteFile(tcMUIINFPath))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA15 Failure: failed to delete installation temp file."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA15 Failure: installation temp file does not exist."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ExtractMUIInfFile
|
|
//
|
|
// This custom action will extract mui.inf file embedded in the binary table
|
|
// of the current installation database. It will place the extracted file
|
|
// in the %winddir% directory as mui.tmp. This file will be referenced
|
|
// during the installation. The tmp file will be cleaned up later in the
|
|
// installation.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
UINT CA14(MSIHANDLE hInstall)
|
|
{
|
|
PMSIHANDLE hDb = NULL;
|
|
PMSIHANDLE hView = NULL;
|
|
PMSIHANDLE hRec = NULL;
|
|
TCHAR tcMessage[BUFFER_SIZE] = {0};
|
|
TCHAR tcQuery[BUFFER_SIZE] = SELECTMUIINFBINSTREAM;
|
|
TCHAR tcMUIINFPath[MAX_PATH+1] = {0};
|
|
char cBuffer[BUFFER_SIZE] = {0};
|
|
DWORD cbBuf = BUFFER_SIZE;
|
|
DWORD cbPathSize = 0;
|
|
DWORD dwNumWritten = 0;
|
|
UINT uiRet = ERROR_SUCCESS;
|
|
HRESULT hr = S_OK;
|
|
HANDLE hFile = NULL;
|
|
UINT uiResult = ERROR_SUCCESS;
|
|
|
|
// form a path to the temporary directory that we want %windir%\mui.tmp
|
|
cbPathSize = GetSystemWindowsDirectory(tcMUIINFPath, MAX_PATH+1);
|
|
if ((0 == cbPathSize) || (MAX_PATH+1 < cbPathSize))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA14: failed to get windows directory path."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
uiRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
|
|
if (!MUICchPathAppend(tcMUIINFPath, ARRAYSIZE(tcMUIINFPath), TEXT("mui.tmp"), 8, hInstall))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA14: failed to get installation temp file path."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
uiRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
|
|
hDb = MsiGetActiveDatabase(hInstall);
|
|
if (NULL == hDb)
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA14: failed to get current installation database handle."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
uiRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
uiResult = MsiDatabaseOpenView(hDb, tcQuery, &hView);
|
|
if (ERROR_SUCCESS != uiResult)
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA14: failed to open current installation database."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
uiRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
uiResult = MsiViewExecute(hView, 0);
|
|
if (ERROR_SUCCESS != uiResult)
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA14: query on current installation database failed."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
uiRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
uiResult = MsiViewFetch(hView, &hRec);
|
|
if (ERROR_SUCCESS != uiResult)
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA14: database operation failed."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
uiRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
|
|
// create our temp file
|
|
hFile = CreateFile(tcMUIINFPath,
|
|
GENERIC_WRITE,
|
|
0L,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
if (INVALID_HANDLE_VALUE == hFile)
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA14: failed to create installation temporary file."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
uiRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
|
|
do
|
|
{
|
|
uiResult = MsiRecordReadStream(hRec, 1, cBuffer, &cbBuf);
|
|
if (ERROR_SUCCESS != uiResult)
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA14: failed to read data from installation database."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
uiRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
|
|
// here, we need to write the read buffer out to a file
|
|
WriteFile(hFile,
|
|
cBuffer,
|
|
cbBuf,
|
|
&dwNumWritten,
|
|
NULL);
|
|
|
|
if (dwNumWritten != cbBuf)
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA14: failed to write to installation temporary file."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
uiRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
} while (cbBuf == BUFFER_SIZE);
|
|
|
|
Exit:
|
|
if (NULL != hFile)
|
|
{
|
|
CloseHandle(hFile);
|
|
}
|
|
|
|
// delete the actual file if there is an error
|
|
if (uiRet == ERROR_INSTALL_FAILURE)
|
|
{
|
|
CA15(hInstall); // DeleteMUIInfFile()
|
|
}
|
|
|
|
return uiRet;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// RestoreSystemSettings
|
|
//
|
|
// This function checks the default and current user languages, and determines whether
|
|
// system needs to reboot when uninstallation happens (immediate). It also clears
|
|
// the shell registry cache (commit action)
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
UINT CA16(MSIHANDLE hInstall)
|
|
{
|
|
UINT iRet = ERROR_SUCCESS;
|
|
UINT iRetProp = ERROR_SUCCESS;
|
|
TCHAR szCustomActionData[5];
|
|
TCHAR tcMessage[BUFFER_SIZE];
|
|
TCHAR szDefLang[5];
|
|
DWORD dwCount = 5;
|
|
LANGID langID;
|
|
BOOL bRestoreDefault = FALSE;
|
|
BOOL bRestoreCurrent = FALSE;
|
|
LANGID sysLangID;
|
|
UINT iTemp = ERROR_SUCCESS;
|
|
HRESULT hr = S_OK;
|
|
|
|
// get MuiLCID
|
|
if (!GetLCID(szCustomActionData, ARRAYSIZE(szCustomActionData), hInstall))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage),TEXT("CA16: Failed to retrieve MuiLCID property."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
iRet = ERROR_INSTALL_FAILURE;
|
|
goto Exit;
|
|
}
|
|
|
|
szCustomActionData[4] = NULL;
|
|
langID = (LANGID)_tcstol(szCustomActionData, NULL, 16);
|
|
#ifdef MUI_DEBUG
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage),TEXT("CA16: LCID is %s."), szCustomActionData);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
#endif
|
|
|
|
// check what the current ui and system ui language is, if they are the same as the current mui langauge to be uninstalled
|
|
// then we will do some additional things during uninstallation
|
|
if (GetDotDefaultUILanguage(hInstall) == langID)
|
|
{
|
|
#ifdef MUI_DEBUG
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA16: Default UI Language is the same as the MUI language being uninstalled. Changing default UI language back to 0409 (English)."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
#endif
|
|
bRestoreDefault = TRUE;
|
|
}
|
|
if (GetUserDefaultUILanguage() == langID)
|
|
{
|
|
#ifdef MUI_DEBUG
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA16: Current UI Language is the same as the MUI language being uninstalled. Changing Current UI language back to 0409 (English)."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
#endif
|
|
bRestoreCurrent = TRUE;
|
|
}
|
|
|
|
if (bRestoreDefault || bRestoreCurrent)
|
|
{
|
|
if (MsiGetMode(hInstall, MSIRUNMODE_COMMIT))
|
|
{
|
|
// we will attempt to delete the shell reg key here, but if we fail, we won't fail the installtion, just
|
|
// log an error
|
|
if (ERROR_SUCCESS != SHDeleteKey(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\ShellNoRoam\\MUICache")))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA16: Failed to delete registry cache."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
}
|
|
}
|
|
else if (!MsiGetMode(hInstall, MSIRUNMODE_ROLLBACK) &&
|
|
!MsiGetMode(hInstall, MSIRUNMODE_SCHEDULED))
|
|
{
|
|
// indicate to the installer that a reboot is required at the end since we changed the default/current UI.
|
|
// again, if this fails, we just log error, and not fail the installation
|
|
iTemp = MsiSetMode(hInstall, MSIRUNMODE_REBOOTATEND, TRUE);
|
|
if (ERROR_SUCCESS != iTemp)
|
|
{
|
|
// log an error
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA16: Failed to schedule reboot operation. MsiSetMode returned %d as the error."), iTemp);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
return iRet;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
// Internal functions, not exported are listed below
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// SetUILanguage
|
|
//
|
|
// This is the internal worker function that calls intl.cpl to set the current
|
|
// and/or default user MUI UI language.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
BOOL SetUILanguage(TCHAR *szLanguage, BOOL bCurrent, BOOL bDefault, MSIHANDLE hInstall)
|
|
{
|
|
BOOL bRet = TRUE;
|
|
DWORD dwCount;
|
|
TCHAR szCommands[BUFFER_SIZE] = {0};
|
|
TCHAR tcMessage[2*BUFFER_SIZE] = {0};
|
|
TCHAR szBuffer[BUFFER_SIZE] = {0};
|
|
BOOL success;
|
|
HRESULT hr = S_OK;
|
|
|
|
if (NULL == szLanguage)
|
|
{
|
|
bRet = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
// return TRUE if there is nothing to set
|
|
if (!bCurrent && !bDefault)
|
|
{
|
|
bRet = TRUE;
|
|
goto Exit;
|
|
}
|
|
|
|
szCommands[0] = TEXT('\0');
|
|
if (bCurrent)
|
|
{
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("MUILanguage=\"%s\"\n"), szLanguage);
|
|
if (FAILED(hr))
|
|
{
|
|
bRet = FALSE;
|
|
goto Exit;
|
|
}
|
|
hr = StringCchCat(szCommands, ARRAYSIZE(szCommands), szBuffer);
|
|
if (FAILED(hr))
|
|
{
|
|
bRet = FALSE;
|
|
goto Exit;
|
|
}
|
|
}
|
|
if (bDefault)
|
|
{
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("MUILanguage_DefaultUser=\"%s\""), szLanguage);
|
|
if (FAILED(hr))
|
|
{
|
|
bRet = FALSE;
|
|
goto Exit;
|
|
}
|
|
hr = StringCchCat(szCommands, ARRAYSIZE(szCommands), szBuffer);
|
|
if (FAILED(hr))
|
|
{
|
|
bRet = FALSE;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
#ifdef MUI_DEBUG
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("SetUILanguage: Command passed to intl.cpl is: %s"), szCommands);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
#endif
|
|
|
|
success = RunRegionalOptionsApplet(szCommands, FALSE, hInstall);
|
|
if (success)
|
|
{
|
|
bRet = TRUE;
|
|
#ifdef MUI_DEBUG
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("SetUILanguage: Successfully set default and/or current user language."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
bRet = FALSE;
|
|
// log an error
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("SetUILanguage: Failed to set default and/or current user language.\nCommand passed to regional options applet is %s."), szCommands);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
return bRet;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// NotifyKernel
|
|
//
|
|
// Call the kernel to notify it that a new language is being added or
|
|
// removed
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void NotifyKernel(LPTSTR LangList, ULONG Flags, MSIHANDLE hInstall )
|
|
{
|
|
HANDLE Handle;
|
|
WMILANGUAGECHANGE LanguageChange;
|
|
ULONG ReturnSize;
|
|
BOOL IoctlSuccess;
|
|
ULONG Status;
|
|
TCHAR tcMessage[BUFFER_SIZE] = {0};
|
|
HRESULT hr = S_OK;
|
|
|
|
if ((LangList != NULL) &&
|
|
(*LangList != 0))
|
|
{
|
|
Handle = CreateFile(WMIAdminDeviceName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if (Handle != INVALID_HANDLE_VALUE)
|
|
{
|
|
memset(&LanguageChange, 0, sizeof(LanguageChange));
|
|
hr = StringCchCopy(LanguageChange.Language, MAX_LANGUAGE_SIZE, LangList); // dest buffer size taken from wmiumkm.h
|
|
if (FAILED(hr))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("NotifyKernel Failure: Kernel language notification failed."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
goto ExitClose;
|
|
}
|
|
LanguageChange.Flags = Flags;
|
|
|
|
IoctlSuccess = DeviceIoControl(Handle,
|
|
IOCTL_WMI_NOTIFY_LANGUAGE_CHANGE,
|
|
&LanguageChange,
|
|
sizeof(LanguageChange),
|
|
NULL,
|
|
0,
|
|
&ReturnSize,
|
|
NULL);
|
|
|
|
if (!IoctlSuccess)
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("NotifyKernel: Language change notification to %ws failed, the error is %d."), LangList, GetLastError());
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
}
|
|
ExitClose:
|
|
CloseHandle(Handle);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// MofCompileLanguage
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
BOOL MofCompileLanguage(LPTSTR Languages, MSIHANDLE hInstall)
|
|
{
|
|
pfnMUI_InstallMFLFiles pfnMUIInstall = NULL;
|
|
TCHAR buffer[5] = {0};
|
|
LPTSTR Language = Languages;
|
|
TCHAR tcMessage[2*BUFFER_SIZE] = {0};
|
|
HMODULE hWbemUpgradeDll = NULL;
|
|
TCHAR szDllPath[MAX_PATH+1] = {0};
|
|
HRESULT hr = S_OK;
|
|
size_t cch = 0;
|
|
BOOL bRet = TRUE;
|
|
|
|
//
|
|
// Load the WBEM upgrade DLL from system wbem folder
|
|
//
|
|
if (GetSystemDirectory(szDllPath, ARRAYSIZE(szDllPath)))
|
|
{
|
|
hr = StringCchLength(szDllPath, ARRAYSIZE(szDllPath), &cch);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (!MUICchPathAppend(szDllPath, ARRAYSIZE(szDllPath), TEXT("wbem\\wbemupgd.dll"), 18, hInstall))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MofCompileLanguage: Failed to form path to Mof Library."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
bRet = FALSE;
|
|
goto Exit2;
|
|
}
|
|
|
|
DEBUGMSGBOX(NULL, szDllPath, NULL, MB_OK);
|
|
hWbemUpgradeDll = LoadLibrary(szDllPath);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Fall back to system default path if previous loading fails
|
|
//
|
|
if (!hWbemUpgradeDll)
|
|
{
|
|
hWbemUpgradeDll = LoadLibrary(TEXT("WBEMUPGD.DLL"));
|
|
if (!hWbemUpgradeDll)
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MofCompileLanguage: Failed to load WBEMUPGD.DLL."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
bRet = FALSE;
|
|
goto Exit2;
|
|
}
|
|
}
|
|
|
|
DEBUGMSGBOX(NULL, TEXT("Loaded WBEMUPGD.DLL"), NULL, MB_OK);
|
|
|
|
//
|
|
// Hook function pointer
|
|
//
|
|
pfnMUIInstall = (pfnMUI_InstallMFLFiles)GetProcAddress(hWbemUpgradeDll, "MUI_InstallMFLFiles");
|
|
|
|
if (pfnMUIInstall == NULL)
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MofCompileLanguage: Can't get address for function MUI_InstallMFLFiles."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
bRet = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
DEBUGMSGBOX(NULL, TEXT("Loaded address for function MUI_InstallMFLFiles"), NULL, MB_OK);
|
|
|
|
hr = StringCchCopy(buffer, ARRAYSIZE(buffer), Language);
|
|
if (FAILED(hr))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MofCompileLanguage: MUI_InstallMFLFiles failed."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
bRet = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
if (!pfnMUIInstall(buffer))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MofCompileLanguage: MUI_InstallMFLFiles failed - argument passed in is %s."), buffer);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
FreeLibrary(hWbemUpgradeDll);
|
|
Exit2:
|
|
return bRet;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// RunRegionalOptionsApplet
|
|
//
|
|
// Run the Regional Option silent mode installation using the specified pCommands.
|
|
//
|
|
// This function will create the "[RegigionalSettings]" string, so there is no need
|
|
// to supply that in pCommands.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
BOOL RunRegionalOptionsApplet(LPTSTR pCommands, BOOL bSilent, MSIHANDLE hInstall)
|
|
{
|
|
HANDLE hFile;
|
|
TCHAR szFilePath[MAX_PATH+1] = {0};
|
|
TCHAR szSysDir[MAX_PATH+1] = {0};
|
|
TCHAR szCmdLine[BUFFER_SIZE+2*MAX_PATH+1] = {0};
|
|
DWORD dwNumWritten = 0L;
|
|
STARTUPINFO si;
|
|
PROCESS_INFORMATION pi = {0};
|
|
TCHAR szSection[MAX_PATH] = TEXT("[RegionalSettings]\r\n");
|
|
TCHAR tcMessage[BUFFER_SIZE+MAX_PATH+1] = {0};
|
|
HRESULT hr = S_OK;
|
|
size_t cch = 0;
|
|
|
|
//
|
|
// prepare the file for un-attended mode setup
|
|
//
|
|
szFilePath[0] = UNICODE_NULL;
|
|
if (!GetSystemWindowsDirectory(szFilePath, MAX_PATH+1))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("RunRegionalOptionsApplet: GetSystemWindowsDirectory Failed - error is %d."), GetLastError());
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return FALSE;
|
|
}
|
|
hr = StringCchLength(szFilePath, ARRAYSIZE(szFilePath), &cch);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (!MUICchPathAppend(szFilePath, ARRAYSIZE(szFilePath), MUI_LANG_GROUP_FILE, 12, hInstall))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("RunRegionalOptionsApplet: Failed to form path to temp control file."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
DEBUGMSGBOX(NULL, szFilePath, NULL, MB_OK);
|
|
|
|
hFile = CreateFile(szFilePath,
|
|
GENERIC_WRITE,
|
|
0L,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
if (INVALID_HANDLE_VALUE == hFile)
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("RunRegionalOptionsApplet: failed to create temporary file %s, error is %d."), szFilePath, GetLastError());
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
WriteFile(hFile,
|
|
szSection,
|
|
(lstrlen(szSection) * sizeof(TCHAR)),
|
|
&dwNumWritten,
|
|
NULL);
|
|
|
|
if (dwNumWritten != (_tcslen(szSection) * sizeof(TCHAR)))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("RunRegionalOptionsApplet: failed to write to temporary file %s, error is %d."), szFilePath, GetLastError());
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
CloseHandle(hFile);
|
|
return FALSE;
|
|
}
|
|
|
|
WriteFile(hFile,
|
|
pCommands,
|
|
(lstrlen(pCommands) * sizeof(TCHAR)),
|
|
&dwNumWritten,
|
|
NULL);
|
|
|
|
if (dwNumWritten != (_tcslen(pCommands) * sizeof(TCHAR)))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("RunRegionalOptionsApplet: failed to write to temporary file %s, error is %d."), szFilePath, GetLastError());
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
CloseHandle(hFile);
|
|
return (FALSE);
|
|
}
|
|
|
|
CloseHandle(hFile);
|
|
|
|
// form a path to the system directory's rundll32.exe
|
|
if (ARRAYSIZE(szSysDir) < GetSystemDirectory(szSysDir, ARRAYSIZE(szSysDir)))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("RunRegionalOptionsApplet: Failed to form path to rundll32."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return (FALSE);
|
|
}
|
|
|
|
// append rundll32.exe at the end of sysdir
|
|
if (!MUICchPathAppend(szSysDir, ARRAYSIZE(szSysDir), TEXT("rundll32.exe"), 13, hInstall))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("RunRegionalOptionsApplet: Failed to form path to rundll32."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return (FALSE);
|
|
}
|
|
|
|
// Call the control panel regional-options applet, and wait for it to complete
|
|
hr = StringCchPrintf(szCmdLine, ARRAYSIZE(szCmdLine), TEXT("\"%s\" shell32,Control_RunDLL intl.cpl,, /f:\"%s\" "), szSysDir, szFilePath);
|
|
if (FAILED(hr))
|
|
{
|
|
DWORD dwError = HRESULT_CODE(hr);
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("RunRegionalOptionsApplet: Failed to form launch command for intl.cpl, error is %d."), dwError);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return (FALSE);
|
|
}
|
|
|
|
if (bSilent)
|
|
{
|
|
hr = StringCchCat(szCmdLine, ARRAYSIZE(szCmdLine), TEXT(" /D"));
|
|
if (FAILED(hr))
|
|
{
|
|
DWORD dwError = HRESULT_CODE(hr);
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("RunRegionalOptionsApplet: Failed to form launch command for intl.cpl, error is %d."), dwError);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
DEBUGMSGBOX(NULL, szCmdLine, NULL, MB_OK);
|
|
|
|
memset( &si, 0x00, sizeof(si));
|
|
si.cb = sizeof(STARTUPINFO);
|
|
if (!CreateProcess(NULL,
|
|
szCmdLine,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
0L,
|
|
NULL,
|
|
NULL,
|
|
&si,
|
|
&pi))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("RunRegionalOptionsApplet: failed to create a process for running intl.cpl, error is %d."), GetLastError());
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Wait forever till intl.cpl terminates.
|
|
//
|
|
WaitForSingleObject(pi.hProcess, INFINITE);
|
|
DEBUGMSGBOX(NULL, TEXT("RunRegionalOptionApplet: intl.cpl execution is complete"), NULL, MB_OK);
|
|
|
|
CloseHandle(pi.hThread); // We have to close out hThread before we can close hProcess
|
|
CloseHandle(pi.hProcess);
|
|
|
|
//
|
|
// Delete the File, don't return false if we fail to delete the command file though
|
|
//
|
|
if (!DeleteFile(szFilePath))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("RunRegionalOptionsApplet: failed to delete regionaloption applet command file %s, error is %d."), szFilePath, GetLastError());
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetLanguageGroup
|
|
//
|
|
// Retreive the Language Group of this locale.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
LGRPID GetLanguageGroup(LCID lcid, MSIHANDLE hInstall)
|
|
{
|
|
int i;
|
|
TCHAR tcMessage[BUFFER_SIZE] = {0};
|
|
HRESULT hr = S_OK;
|
|
|
|
gLangGroup = LGRPID_WESTERN_EUROPE;
|
|
gFoundLangGroup = FALSE;
|
|
gLCID = lcid;
|
|
|
|
if (!EnumSystemLanguageGroups(EnumLanguageGroupsProc, LGRPID_SUPPORTED, 0))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("GetLanguageGroup: EnumLanguageGroups failed, error is %d."), GetLastError());
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
}
|
|
|
|
for (i=0 ; i<gNumLanguageGroups; i++)
|
|
{
|
|
// The globals gLangGroup and gFoundLangGroup is used in the callback function
|
|
// EnumLanguageGroupLocalesProc.
|
|
if (!EnumLanguageGroupLocales(EnumLanguageGroupLocalesProc, gLanguageGroups[i], 0L, 0L))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("GetLanguageGroup: EnumLanguageGroupLocales failed, error is %d."), GetLastError());
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we found it, then break now
|
|
//
|
|
if (gFoundLangGroup)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return gLangGroup;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// EnumLanguageGroupsProc
|
|
//
|
|
// This function is called by EnumLanguageGroups to enumerate the system installed language groups
|
|
// and store it in the global variables for other uses
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
BOOL CALLBACK EnumLanguageGroupsProc(
|
|
LGRPID LanguageGroup, // language group identifier
|
|
LPTSTR lpLanguageGroupString, // pointer to language group identifier string
|
|
LPTSTR lpLanguageGroupNameString, // pointer to language group name string
|
|
DWORD dwFlags, // flags
|
|
LONG_PTR lParam) // user-supplied parameter
|
|
{
|
|
gLanguageGroups[gNumLanguageGroups] = LanguageGroup;
|
|
gNumLanguageGroups++;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// EnumLanguageGroupLocalesProc
|
|
//
|
|
// This function is called to by enumerateLanguageGroupLocales to search for an installed language
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
BOOL CALLBACK EnumLanguageGroupLocalesProc(
|
|
LGRPID langGroupId,
|
|
LCID lcid,
|
|
LPTSTR lpszLocale,
|
|
LONG_PTR lParam)
|
|
|
|
{
|
|
if (lcid == gLCID)
|
|
{
|
|
gLangGroup = langGroupId;
|
|
gFoundLangGroup = TRUE;
|
|
|
|
DEBUGMSGBOX(NULL, TEXT("EnumLanguageGroupLocalesProc: Found same LCID"), NULL, MB_OK);
|
|
|
|
// stop iterating
|
|
return FALSE;
|
|
}
|
|
|
|
// next iteration
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ReturnAllRequiredLangGroups
|
|
//
|
|
// This function returns all the required language groups as specified by the
|
|
// system and in extracted mui.inf in the returned array. It also returns
|
|
// the number of required language groups in the return parameter.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
BOOL ReturnAllRequiredLangGroups(LPTSTR szLanguage, UINT cchLangBufsize, LPTSTR szMuiInfPath, UINT cchPathBufsize, LGRPID *lgrpids, UINT *uiNumFoundGroups, MSIHANDLE hInstall)
|
|
{
|
|
int iArg;
|
|
UINT iRet = ERROR_SUCCESS;
|
|
DWORD dwCount;
|
|
TCHAR tcMessage[BUFFER_SIZE+MAX_PATH+1] = {0};
|
|
INFCONTEXT InfContext;
|
|
int LangGroup;
|
|
int iMuiInfCount = 0;
|
|
int i;
|
|
HINF hInf;
|
|
HRESULT hr = S_OK;
|
|
size_t cch = 0;
|
|
|
|
if (NULL == uiNumFoundGroups)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
*uiNumFoundGroups = 0;
|
|
|
|
if ((NULL == szLanguage) || (NULL == szMuiInfPath) || (NULL == lgrpids) || (NULL == hInstall))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// check length of the passed in string
|
|
hr = StringCchLength(szLanguage, cchLangBufsize, &cch);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (cch > 4)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
hr = StringCchLength(szMuiInfPath, cchPathBufsize, &cch);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (cch > MAX_PATH)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef MUI_DEBUG
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("ReturnAllRequiredLangGroups: MuiLCID is %s, installation temp file path is %s."), szLanguage, szMuiInfPath);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
#endif
|
|
|
|
// convert lcid to appropriate language group
|
|
iArg = _tcstol(szLanguage, NULL, 16);
|
|
lgrpids[0] = GetLanguageGroup(MAKELCID(iArg, SORT_DEFAULT), hInstall);
|
|
*uiNumFoundGroups = 1; // at this point we should have 1 lang group at least
|
|
iMuiInfCount = 1;
|
|
|
|
DEBUGMSGBOX(NULL, szMuiInfPath, NULL, MB_OK);
|
|
|
|
hInf = SetupOpenInfFile(szMuiInfPath, NULL, INF_STYLE_WIN4, NULL);
|
|
if (hInf != INVALID_HANDLE_VALUE)
|
|
{
|
|
#ifdef MUI_DEBUG
|
|
TCHAR szMessage[BUFFER_SIZE] = {0};
|
|
hr = StringCchPrintf(szMessage, ARRAYSIZE(szMessage), TEXT("Language is %s."), szLanguage);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DEBUGMSGBOX(NULL, szMessage, NULL, MB_OK);
|
|
}
|
|
#endif
|
|
if (SetupFindFirstLine(hInf, MUI_LANGPACK_SECTION, szLanguage, &InfContext))
|
|
{
|
|
DEBUGMSGBOX(NULL, TEXT("Found the LanguagePack section in installation temp file!"), NULL, MB_OK);
|
|
while (SetupGetIntField(&InfContext, iMuiInfCount, &LangGroup))
|
|
{
|
|
lgrpids[iMuiInfCount] = LangGroup;
|
|
iMuiInfCount++;
|
|
#ifdef MUI_DEBUG
|
|
hr = StringCchPrintf(szMessage, ARRAYSIZE(szMessage), TEXT("Found langgroup %d in installation temp file"), LangGroup);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DEBUGMSGBOX(NULL, szMessage, NULL, MB_OK);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifdef MUI_DEBUG
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("ReturnAllRequiredLangGroups: installation temp file does not contain a LanguagePack section."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
#endif
|
|
}
|
|
SetupCloseInfFile(hInf);
|
|
}
|
|
else
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("ReturnAllRequiredLangGroups: installation temp file not found at location %s. The error is %d."), szMuiInfPath, GetLastError());
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
}
|
|
|
|
*uiNumFoundGroups = iMuiInfCount;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ExecuteComponentINF
|
|
//
|
|
// Installs component MUI files, by running the specified INF file.
|
|
//
|
|
// Parameters:
|
|
// pComponentName the name of the component (e.g. "ie5")
|
|
// pComponentInfFile: the full path of the component INF file.
|
|
// pInstallSection the section in the component INF file to be executed. (e.g "DefaultInstall" or "Uninstall")
|
|
// bInstall: TRUE for install, FALSE for uninstall
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
BOOL ExecuteComponentINF(
|
|
PTSTR pComponentName,
|
|
PTSTR pComponentInfFile,
|
|
PTSTR pInstallSection,
|
|
BOOL bInstall,
|
|
MSIHANDLE hInstall)
|
|
{
|
|
int iLen;
|
|
TCHAR tchCommandParam[MAX_PATH+6+BUFFER_SIZE] = {0};
|
|
CHAR chCommandParam[(MAX_PATH+6+BUFFER_SIZE)*sizeof(TCHAR)] = {0};
|
|
TCHAR tcMessage[2*BUFFER_SIZE+MAX_PATH+1] = {0};
|
|
HINF hCompInf; // the handle to the component INF file.
|
|
HSPFILEQ FileQueue;
|
|
PVOID QueueContext;
|
|
BOOL bRet = TRUE;
|
|
DWORD dwResult;
|
|
TCHAR szBuffer[BUFFER_SIZE] = {0};
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Advpack LaunchINFSection() command line format:
|
|
// INF file, INF section, flags, reboot string
|
|
// 'N' or 'n' in reboot string means no reboot message popup.
|
|
//
|
|
hr = StringCchPrintf(tchCommandParam, ARRAYSIZE(tchCommandParam), TEXT("%s,%s,1,n"), pComponentInfFile, pInstallSection);
|
|
if (FAILED(hr))
|
|
{
|
|
DWORD dwError = HRESULT_CODE(hr);
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("ExecuteComponentINF: failed to form Inf Execution command. The returned error is %d."), dwError);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
if (!WideCharToMultiByte(CP_ACP, 0, tchCommandParam, -1, chCommandParam, ARRAYSIZE(chCommandParam), NULL, NULL))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("ExecuteComponentINF: failed to form Inf Execution command. The returned error is %d."), GetLastError());
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
if (FileExists(pComponentInfFile))
|
|
{
|
|
if (LaunchINFSection(NULL, NULL, chCommandParam, SW_HIDE) != S_OK)
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("ExecuteComponentINF: LaunchINFSection failed for inf file %s, component name %s."), pComponentInfFile, pComponentName);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("ExecuteComponentINF: Failed to locate inf file %s."), pComponentInfFile);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// InstallComponentsMUIFiles
|
|
//
|
|
// Parameters:
|
|
// pszLangSourceDir The sub-directory name for a specific lanuage in the MUI CD-ROM.
|
|
// E.g. "jpn.MUI"
|
|
// pszLanguage The LCID for the specific language. E.g. "0404".
|
|
// isInstall TRUE if you are going to install the MUI files for the component. FALSE
|
|
// if you are going to uninstall.
|
|
//
|
|
// Return:
|
|
// -1 if failed, IDOK if succeeded, IDCANCEL if user clicked cancel during the operation
|
|
//
|
|
// Note:
|
|
// For the language resources stored in pszLangSourceDir, this function will enumerate
|
|
// the compoents listed in the [Components]
|
|
// (the real section is put in MUI_COMPONENTS_SECTION) section, and execute the INF file
|
|
// listed in every entry in
|
|
// the [Components] section.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
INT InstallComponentsMUIFiles(PTSTR pszLanguage, BOOL isInstall, MSIHANDLE hInstall)
|
|
{
|
|
BOOL result = TRUE;
|
|
BOOL bRollback = FALSE;
|
|
BOOL bOEMSystem = FALSE;
|
|
TCHAR szComponentName[BUFFER_SIZE] = {0};
|
|
TCHAR CompDir[MAX_PATH+1] = {0};
|
|
TCHAR szWinDir[MAX_PATH+1] = {0};
|
|
TCHAR CompINFFile[BUFFER_SIZE] = {0};
|
|
TCHAR CompInstallSection[BUFFER_SIZE] = {0};
|
|
TCHAR CompUninstallSection[BUFFER_SIZE] = {0};
|
|
TCHAR szMuiInfPath[MAX_PATH+1] = {0};
|
|
TCHAR szBuffer[3*BUFFER_SIZE+MAX_PATH+1] = {0};
|
|
TCHAR szCompInfFullPath[MAX_PATH+1] = {0};
|
|
TCHAR szCompInfAltFullPath[MAX_PATH+1] = {0};
|
|
INFCONTEXT InfContext;
|
|
PMSIHANDLE hRec = MsiCreateRecord(3);
|
|
PMSIHANDLE hProgressRec = MsiCreateRecord(3);
|
|
HRESULT hr = S_OK;
|
|
INT iResult = IDOK;
|
|
INT iFlag = 0;
|
|
|
|
if ((NULL == hRec) || (NULL == hProgressRec))
|
|
{
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: cannot create MSI Records."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
bRollback = MsiGetMode(hInstall,MSIRUNMODE_ROLLBACK);
|
|
|
|
// get path to the target installation temp file file on the target, it should be at WindowsFolder\mui.tmp
|
|
szMuiInfPath[0] = UNICODE_NULL;
|
|
if (!GetMUIInfPath(szMuiInfPath, MAX_PATH+1, hInstall))
|
|
{
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: Unable to find installation temp file."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
// also get the windows dir, for later use
|
|
if (!GetSystemWindowsDirectory(szWinDir, MAX_PATH+1))
|
|
{
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: cannot get Windows Directory."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
HINF hInf = SetupOpenInfFile(szMuiInfPath, NULL, INF_STYLE_WIN4, NULL);
|
|
|
|
if (hInf == INVALID_HANDLE_VALUE)
|
|
{
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: Unable to open installation temp file."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
//Check if its an OEM system
|
|
bOEMSystem = IsOEMSystem();
|
|
|
|
//
|
|
// Get the first component to be installed.
|
|
//
|
|
if (SetupFindFirstLine(hInf, MUI_COMPONENTS_SECTION, NULL, &InfContext))
|
|
{
|
|
do
|
|
{
|
|
if (SetupGetIntField(&InfContext, 5,&iFlag)) //Check the last field of the component to see if its an OEM component. If OEM component iIsOEM = 1
|
|
{
|
|
if ((iFlag == OEM_COMPONENT) && !bOEMSystem) //Skip installation if its an OEM component and this isnt an OEM system
|
|
continue;
|
|
}
|
|
|
|
if (!SetupGetStringField(&InfContext, 0, szComponentName, ARRAYSIZE(szComponentName), NULL))
|
|
{
|
|
// continue on the next line - but remember to log an error
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: Error reading installation temp file, component name is missing."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// tell the installer UI that we are installing a new component now
|
|
if (!bRollback)
|
|
{
|
|
MsiRecordSetString(hRec,1, szComponentName);
|
|
iResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_ACTIONDATA, hRec);
|
|
if (iResult == IDCANCEL)
|
|
{
|
|
SetupCloseInfFile(hInf);
|
|
return iResult;
|
|
}
|
|
}
|
|
|
|
if (!SetupGetStringField(&InfContext, 1, CompDir, ARRAYSIZE(CompDir), NULL))
|
|
{
|
|
// continue on the next line - but remember to log an error
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: MUI files for component %s was not installed because of missing component direcotry."), szComponentName);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
continue;
|
|
}
|
|
if (!SetupGetStringField(&InfContext, 2, CompINFFile, ARRAYSIZE(CompINFFile), NULL))
|
|
{
|
|
// continue on the next line - but remember to log an error
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: MUI files for component %s was not installed because of missing component INF filename."), szComponentName);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (isInstall && (!SetupGetStringField(&InfContext, 3, CompInstallSection, ARRAYSIZE(CompInstallSection), NULL)))
|
|
{
|
|
hr = StringCchCopy(CompInstallSection, ARRAYSIZE(CompInstallSection), DEFAULT_INSTALL_SECTION);
|
|
if (FAILED(hr))
|
|
{
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: Cannot locate Default Install section for component %s."), szComponentName);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
if (!isInstall && (!SetupGetStringField(&InfContext, 4, CompUninstallSection, ARRAYSIZE(CompUninstallSection), NULL)))
|
|
{
|
|
hr = StringCchCopy(CompUninstallSection, ARRAYSIZE(CompUninstallSection), DEFAULT_UNINSTALL_SECTION);
|
|
if (FAILED(hr))
|
|
{
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: Cannot locate Default Uninnstall section for component %s."), szComponentName);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Establish the correct path for component INF file.
|
|
// We execute the INFs on the target MUI directory after msi has copied the files, it's installed to MUIroot\fallback\LCID\external\componentdir\
|
|
// e.g. c:\windows\mui\fallback\lcid\external\ie5\ie5ui.inf
|
|
// This is done for both install and uninstall, since we should be guaranteed that the files will be located there.
|
|
// NOTE: for uninstall, we also try to look for inf files at c:\windows\mui\fallback\lcid - since they can be located there after installation
|
|
//
|
|
hr = StringCchCopy(szCompInfFullPath, ARRAYSIZE(szCompInfFullPath), szWinDir);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (!((MUICchPathAppend(szCompInfFullPath, ARRAYSIZE(szCompInfFullPath), FALLBACKDIR, 13, hInstall)) &&
|
|
(MUICchPathAppend(szCompInfFullPath, ARRAYSIZE(szCompInfFullPath), pszLanguage, 5, hInstall)) &&
|
|
(MUICchPathAppend(szCompInfFullPath, ARRAYSIZE(szCompInfFullPath), EXTDIR, 9, hInstall)) &&
|
|
(MUICchPathAppend(szCompInfFullPath, ARRAYSIZE(szCompInfFullPath), CompDir, ARRAYSIZE(CompDir), hInstall)) &&
|
|
(MUICchPathAppend(szCompInfFullPath, ARRAYSIZE(szCompInfFullPath), CompINFFile, ARRAYSIZE(CompINFFile), hInstall))))
|
|
{
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: Cannot form path to external component INF."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: Cannot form path to external component INF."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (isInstall)
|
|
{
|
|
if (!ExecuteComponentINF(szComponentName, szCompInfFullPath, CompInstallSection, TRUE, hInstall))
|
|
{
|
|
// log an error and continue
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: Failed to install external component %s. INF path is %s, INF installsection is %s."), szComponentName, szCompInfFullPath, CompInstallSection);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!ExecuteComponentINF(szComponentName, szCompInfFullPath, CompUninstallSection, FALSE, hInstall) && result)
|
|
{
|
|
// try this again at an alternate location
|
|
hr = StringCchCopy(szCompInfAltFullPath, ARRAYSIZE(szCompInfAltFullPath), szWinDir);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (!((MUICchPathAppend(szCompInfAltFullPath, ARRAYSIZE(szCompInfAltFullPath), FALLBACKDIR, 13, hInstall)) &&
|
|
(MUICchPathAppend(szCompInfAltFullPath, ARRAYSIZE(szCompInfAltFullPath), pszLanguage, 5, hInstall)) &&
|
|
(MUICchPathAppend(szCompInfAltFullPath, ARRAYSIZE(szCompInfAltFullPath), CompINFFile, ARRAYSIZE(CompINFFile), hInstall))))
|
|
{
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: Cannot form path to alternate external component INF."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: Cannot form path to alternate external component INF."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
continue;
|
|
}
|
|
if (!ExecuteComponentINF(szComponentName, szCompInfAltFullPath, CompUninstallSection, FALSE, hInstall) && result)
|
|
{
|
|
// log an error and continue
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: Failed to uninstall external component %s. INF path is %s, Alternate INF path is %s, INF uninstallsection is %s."), szComponentName, szCompInfFullPath, szCompInfAltFullPath, CompUninstallSection);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Specify that an update of the progress bar's position in this
|
|
// case means to move it forward by one increment now that we have installed it.
|
|
if (!bRollback)
|
|
{
|
|
MsiRecordSetInteger(hProgressRec,1,2);
|
|
MsiRecordSetInteger(hProgressRec,2,COMP_TICK_INC);
|
|
MsiRecordSetInteger(hProgressRec,3,0);
|
|
iResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hProgressRec);
|
|
if (iResult == IDCANCEL)
|
|
{
|
|
SetupCloseInfFile(hInf);
|
|
return iResult;
|
|
}
|
|
}
|
|
//
|
|
// Install the next component.
|
|
//
|
|
} while (SetupFindNextLine(&InfContext, &InfContext));
|
|
|
|
}
|
|
|
|
SetupCloseInfFile(hInf);
|
|
|
|
return (IDOK);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetMUIComponentsNumber
|
|
//
|
|
// Parameters:
|
|
// bInstall indicate whether this function is used for installing component infs or not
|
|
// this affects where it will look for mui.inf to get the component count.
|
|
// pszLangSourceDir The sub-directory name for a specific lanuage in the MUI CD-ROM.
|
|
// E.g. "jpn.MUI"
|
|
// pszLanguage The LCID for the specific language. E.g. "0404".
|
|
//
|
|
// Return:
|
|
// The number of MUI external components that need to be installed/uninstalled, if
|
|
// there is an error it will return 0, otherwise it returns the number of components
|
|
//
|
|
// Note:
|
|
// For the language resources stored in pszLangSourceDir, this function will enumerate
|
|
// the compoents listed in the [Components]
|
|
// (the real section is put in MUI_COMPONENTS_SECTION) section, and counts every entry in
|
|
// the [Components] section.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
UINT GetMUIComponentsNumber(PTSTR pszLanguage, MSIHANDLE hInstall)
|
|
{
|
|
UINT iResult = 0;
|
|
TCHAR szComponentName[BUFFER_SIZE] = {0};
|
|
TCHAR CompDir[MAX_PATH+1] = {0};
|
|
TCHAR CompINFFile[BUFFER_SIZE] = {0};
|
|
TCHAR szMuiInfPath[MAX_PATH+1] = {0};
|
|
TCHAR szBuffer[BUFFER_SIZE] = {0};
|
|
INFCONTEXT InfContext;
|
|
HRESULT hr = S_OK;
|
|
|
|
szMuiInfPath[0] = UNICODE_NULL;
|
|
|
|
// get path to the target mui.inf file
|
|
if (!GetMUIInfPath(szMuiInfPath, MAX_PATH+1, hInstall))
|
|
{
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("GetMUIComponentsNumber Failure: Unable to find installation temp file."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
HINF hInf = SetupOpenInfFile(szMuiInfPath, NULL, INF_STYLE_WIN4, NULL);
|
|
|
|
if (hInf == INVALID_HANDLE_VALUE)
|
|
{
|
|
// return true here so that there won't be an error - but remember to log an error
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("GetMUIComponentsNumber: Unable to open installation temp file."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
return (iResult);
|
|
}
|
|
|
|
// Get the first comopnent to be installed.
|
|
if (SetupFindFirstLine(hInf, MUI_COMPONENTS_SECTION, NULL, &InfContext))
|
|
{
|
|
do
|
|
{
|
|
if (!SetupGetStringField(&InfContext, 0, szComponentName, ARRAYSIZE(szComponentName), NULL))
|
|
{
|
|
// return true here so that there won't be an error - but remember to log an error
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("GetMUIComponentsNumber: Error reading installation temp file, component name is missing."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (!SetupGetStringField(&InfContext, 1, CompDir, ARRAYSIZE(CompDir), NULL))
|
|
{
|
|
// return true here so that there won't be an error - but remember to log an error
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("GetMUIComponentsNumber: MUI files for component %s was not counted because of missing component direcotry."), szComponentName);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
continue;
|
|
}
|
|
if (!SetupGetStringField(&InfContext, 2, CompINFFile, ARRAYSIZE(CompINFFile), NULL))
|
|
{
|
|
// return true here so that there won't be an error - but remember to log an error
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("GetMUIComponentsNumber: MUI files for component %s was not counted because of missing component INF filename."), szComponentName);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
iResult++;
|
|
|
|
} while (SetupFindNextLine(&InfContext, &InfContext));
|
|
|
|
}
|
|
|
|
SetupCloseInfFile(hInf);
|
|
|
|
#ifdef MUI_DEBUG
|
|
hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("GetMUIComponentsNumber: Found %d components to install."), iResult);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, szBuffer);
|
|
}
|
|
#endif
|
|
|
|
return (iResult);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// File Exists
|
|
//
|
|
// Returns TRUE if the file exists, FALSE if it does not.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
BOOL FileExists(LPTSTR szFile)
|
|
{
|
|
HANDLE hFile;
|
|
WIN32_FIND_DATA FindFileData;
|
|
HRESULT hr = S_OK;
|
|
size_t cch = 0;
|
|
|
|
if (NULL == szFile)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// check for valid input, the path cannot be larger than MAX_PATH+1
|
|
hr = StringCchLength(szFile, MAX_PATH+1, &cch);
|
|
if (FAILED(hr) || cch > MAX_PATH)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
hFile = FindFirstFile(szFile, &FindFileData);
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
FindClose(hFile);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// LogCustomActionInfo
|
|
//
|
|
// This function sends an INFORMATION-type log message record to the opened
|
|
// windows installer session so that it can be logged by the installer
|
|
// if logging is enabled.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
void LogCustomActionInfo(MSIHANDLE hInstall, LPCTSTR szErrorMsg)
|
|
{
|
|
// When reporting error, we will just put the message in the format string (field 0), errors are logged to log files as INFO messages. This is
|
|
// to prevent it from showing up as an error and stopping the installation.
|
|
PMSIHANDLE hRecord = MsiCreateRecord(0);
|
|
|
|
// if can't create a msi record, just return
|
|
if ((NULL == hInstall) || (NULL == szErrorMsg) || (NULL == hRecord))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (ERROR_SUCCESS == MsiRecordSetString(hRecord, 0, szErrorMsg))
|
|
{
|
|
MsiProcessMessage(hInstall, INSTALLMESSAGE_INFO, hRecord);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetLCID
|
|
//
|
|
// This function returns the 4-character LCID for the current installation package.
|
|
// We assume here that the passed in string array size is 5 TCHARs. If it is not,
|
|
// the function will fail.
|
|
//
|
|
// The behaviour is summarized as follows:
|
|
//
|
|
// 1. Immediate:
|
|
// a. Property "MuiLCID" is retrieved and tested from the current installation
|
|
// b. if LCID property can't be retrieved, returns FALSE.
|
|
//
|
|
// 2. Deferred/Rollback:
|
|
// a. Property "CustomActionData" is retrieved.
|
|
// b. Assumption is that LCID will be the first 4 character in the retrieved CustomActionData property.
|
|
// c. If property can't be retrieved, or if property testing fails, return FALSE.
|
|
//
|
|
// Parameters:
|
|
// szLanguage: This is a caller-allocated buffer of 5 TCHARS to store the LCID
|
|
// cchBufSize: This is the size of szLanguage, it has to be 5.
|
|
// hInstall: Current installation handle.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
BOOL GetLCID(TCHAR *szLanguage, UINT cchBufSize, MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
TCHAR szLcid[5] = {0};
|
|
TCHAR szCustomActionData[BUFFER_SIZE] = {0};
|
|
TCHAR tcMessage[BUFFER_SIZE] = {0};
|
|
DWORD dwCount = 0;
|
|
|
|
if ((NULL == hInstall) || (NULL == szLanguage))
|
|
{
|
|
#ifdef MUI_DEBUG
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("GetLCID: Internal error 1."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
|
|
if (cchBufSize != 5)
|
|
{
|
|
#ifdef MUI_DEBUG
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("GetLCID: Internal error 2."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
|
|
if (!MsiGetMode(hInstall, MSIRUNMODE_SCHEDULED) &&
|
|
!MsiGetMode(hInstall, MSIRUNMODE_ROLLBACK)&&
|
|
!MsiGetMode(hInstall, MSIRUNMODE_COMMIT))
|
|
{
|
|
dwCount = 5;
|
|
if (ERROR_SUCCESS != MsiGetProperty(hInstall, TEXT("MuiLCID"), szLcid, &dwCount))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("GetLCID: Failed to retrieve MuiLCID property."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// copy the Lcid to the output buffer
|
|
szLcid[4] = UNICODE_NULL;
|
|
hr = StringCchCopy(szLanguage, cchBufSize, szLcid);
|
|
if (FAILED(hr))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("GetLCID: Failed to retrieve MuiLCID property."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwCount = BUFFER_SIZE;
|
|
if (ERROR_SUCCESS != MsiGetProperty(hInstall, TEXT("CustomActionData"), szCustomActionData, &dwCount))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("GetLCID: Failed to retrieve CustomActionData property."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return FALSE;
|
|
}
|
|
// copy the Lcid to the output buffer
|
|
szCustomActionData[4] = UNICODE_NULL;
|
|
hr = StringCchCopy(szLanguage, cchBufSize, szCustomActionData);
|
|
if (FAILED(hr))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("GetLCID: Failed to retrieve CustomActionData property."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
szLanguage[4] = UNICODE_NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetMUIInfPath
|
|
//
|
|
// This function returns the path to mui.inf to the calling function. This function
|
|
// is intended for use only by the exported functions of the custom action functions
|
|
// in this dll.
|
|
//
|
|
// Note that mui.inf is extracted to %windir% as mui.tmp during the installation
|
|
//
|
|
// The function expects the mui.tmp to be at %windir%\mui.tmp.
|
|
//
|
|
// Return Value:
|
|
// If the function successfully finds a file named mui.inf, it returns TRUE, otherwise it returns FALSE
|
|
// The full path to mui.inf is returned in the caller supplied buffer szMUIInfPath
|
|
//
|
|
// Parameters:
|
|
// szMUIInfPath -
|
|
// [out] This is the output buffer that will contain the path of the mui.tmp.
|
|
// cchBufSize -
|
|
// This indicates the size of the input/output buffer the caller allocated for us, it should be no longer
|
|
// than MAX_PATH+1 (validated in the function.
|
|
// hInstall -
|
|
// This is the handle passed to us from the windows installer - it is a handle to the current installation
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
BOOL GetMUIInfPath(TCHAR *szMUIInfPath, UINT cchBufSize, MSIHANDLE hInstall)
|
|
{
|
|
TCHAR tcMessage[BUFFER_SIZE] = {0};
|
|
TCHAR szTempPath[MAX_PATH+1] = {0};
|
|
HRESULT hr = S_OK;
|
|
size_t cch = 0;
|
|
DWORD dwCount = 0;
|
|
|
|
if ((NULL == hInstall) || (NULL == szMUIInfPath))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if ((cchBufSize > MAX_PATH+1) || (cchBufSize <= 8)) // 8 = mui.tmp + null terminator
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!GetSystemWindowsDirectory(szTempPath, MAX_PATH+1))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("GetMUIInfPath: Unable to find the Windows directory, GetSystemWindowsDirectory returned %d."), GetLastError());
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
}
|
|
|
|
// check retrieved winpath, it needs to have space to append "mui.tmp" at the end
|
|
hr = StringCchLength(szTempPath, ARRAYSIZE(szTempPath), &cch);
|
|
if (FAILED(hr) || ((cch + 8) >= MAX_PATH+1))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("GetMUIInfPath: cannot locate installation temp file."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// append mui.tmp
|
|
if (!MUICchPathAppend(szTempPath, ARRAYSIZE(szTempPath), TEXT("mui.tmp"), 8, hInstall))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("GetMUIInfPath: cannot locate installation temp file."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// check if mui.tmp is there, if not, return failure
|
|
if (!FileExists(szTempPath))
|
|
{
|
|
// zero out the output buffer
|
|
ZeroMemory(szMUIInfPath, cchBufSize * sizeof(TCHAR));
|
|
return FALSE;
|
|
}
|
|
|
|
// copy result to output buffer
|
|
hr = StringCchCopy(szMUIInfPath, cchBufSize, szTempPath);
|
|
if (FAILED(hr))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("GetMUIInfPath: cannot locate installation temp file."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// MUICchPathAppend
|
|
//
|
|
// This function is a simple pathappend-like function that does limited parameter checking and uses the
|
|
// safe string functions internally. It is used only internally within this custom action to append
|
|
// file names to the end of a path (such as current directory or windows system directory)
|
|
//
|
|
// If error occurs, the content of SzDestination is undefined and should not be used.
|
|
//
|
|
// Parameters:
|
|
// szDestination: the buffer where the result of the pathappend will be held.
|
|
// cchDestBufSize: the size of szDestination (number of characters, not byes!).
|
|
// szAppend: the buffer where the path to be appended is held.
|
|
// cchAppBufSize: the size of szAppend (number of characters, not byes!).
|
|
// hInstall: windows installer session, used for logging only
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
BOOL MUICchPathAppend(LPTSTR szDestination, UINT cchDestBufSize, LPTSTR szAppend, UINT cchAppBufSize, MSIHANDLE hInstall)
|
|
{
|
|
size_t cch1 = 0;
|
|
size_t cch2 = 0;
|
|
HRESULT hr = S_OK;
|
|
TCHAR tcMessage[BUFFER_SIZE] = {0};
|
|
|
|
if ((NULL == szDestination) || (NULL == szAppend) || (NULL == hInstall))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MUICchPathAppend: Invalid paths specified or invalid windows installer session."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// get length of both strings
|
|
hr = StringCchLength(szDestination, cchDestBufSize, &cch1);
|
|
if (FAILED(hr))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MUICchPathAppend: Invalid destination path specified."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
hr = StringCchLength(szAppend, cchAppBufSize, &cch2);
|
|
if (FAILED(hr))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MUICchPathAppend: Invalid source path specified."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
if ((cch1 + cch2 + 2) > cchDestBufSize) // null terminator and a possible backslash
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MUICchPathAppend: final path would be too long."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// check for slashes at the start of the string that we are appending
|
|
if (szAppend[0] == TEXT('\\'))
|
|
{
|
|
// check for slashes at the end of the string to be appended, add if it is there, remove it
|
|
if (szDestination[cch1-1] == TEXT('\\'))
|
|
{
|
|
szDestination[cch1-1] = UNICODE_NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// check for slashes at the end of the string to be appended, add it if it is not there
|
|
if (szDestination[cch1-1] != TEXT('\\'))
|
|
{
|
|
szDestination[cch1] = TEXT('\\');
|
|
szDestination[cch1+1] = UNICODE_NULL;
|
|
}
|
|
}
|
|
|
|
hr = StringCchCat(szDestination, cchDestBufSize, szAppend);
|
|
if (FAILED(hr))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MUICchPathAppend: Failed to form new path."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// MUIReportInfoEvent
|
|
//
|
|
// This function logs the supplied event message to the system event log
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
BOOL MUIReportInfoEvent(DWORD dwEventID, TCHAR *szLanguage, UINT cchBufSize, MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
size_t cch = 0;
|
|
HANDLE hLog = NULL;
|
|
TCHAR szUserName[UNLEN+1];
|
|
TCHAR *pszDomain = NULL;
|
|
PSID psidUser = NULL;
|
|
DWORD cbSid = 0;
|
|
DWORD cbDomain = 0;
|
|
DWORD cbUser = UNLEN + 1;
|
|
SID_NAME_USE snu;
|
|
BOOL bResult = TRUE;
|
|
|
|
// check input parameters
|
|
if ((NULL == hInstall) || (NULL == szLanguage) || (cchBufSize > BUFFER_SIZE))
|
|
{
|
|
bResult = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
hr = StringCchLength(szLanguage, cchBufSize, &cch);
|
|
if (FAILED(hr))
|
|
{
|
|
bResult = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
// check to see if the registry key exists for the event source we are going to use
|
|
// if it does not exist, we create it
|
|
if (!MUICheckEventSource(hInstall))
|
|
{
|
|
bResult = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
// register the event source, first try not having written to the registry
|
|
hLog = RegisterEventSource(NULL, REGOPT_EVENTSOURCE_NAME);
|
|
if (NULL == hLog)
|
|
{
|
|
bResult = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
// get the sid from the current thread token, this should be the current user who's
|
|
// running the installation
|
|
if (!GetUserName(szUserName, &cbUser))
|
|
{
|
|
bResult = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
// convert user name to its security identifier, first time to get buffer size, second time
|
|
// to actually get the Sid
|
|
if (!LookupAccountName(NULL, szUserName, NULL, &cbSid, NULL, &cbDomain, &snu))
|
|
{
|
|
// allocate the buffers
|
|
psidUser = (PSID) LocalAlloc(LPTR, cbSid);
|
|
if (NULL == psidUser)
|
|
{
|
|
bResult = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
pszDomain = (TCHAR*) LocalAlloc(LPTR, cbDomain * sizeof(TCHAR));
|
|
if (NULL == pszDomain)
|
|
{
|
|
bResult = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
if (!LookupAccountName(NULL, szUserName, psidUser, &cbSid, pszDomain, &cbDomain, &snu))
|
|
{
|
|
bResult = FALSE;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (!ReportEvent(hLog,
|
|
EVENTLOG_INFORMATION_TYPE,
|
|
0,
|
|
dwEventID,
|
|
psidUser,
|
|
1,
|
|
0,
|
|
(LPCWSTR *) &szLanguage,
|
|
NULL))
|
|
{
|
|
bResult = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
Exit:
|
|
if (NULL != hLog)
|
|
{
|
|
if (!DeregisterEventSource(hLog))
|
|
{
|
|
bResult = FALSE;
|
|
}
|
|
}
|
|
|
|
if (psidUser)
|
|
{
|
|
if (LocalFree(psidUser))
|
|
{
|
|
bResult = FALSE;
|
|
}
|
|
}
|
|
|
|
if (pszDomain)
|
|
{
|
|
if (LocalFree(pszDomain))
|
|
{
|
|
bResult = FALSE;
|
|
}
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// MUICheckEventSource
|
|
//
|
|
// This function verifies that the intl.cpl is set up to report events, and
|
|
// returns TRUE if it is.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
BOOL MUICheckEventSource(MSIHANDLE hInstall)
|
|
{
|
|
HKEY hk;
|
|
DWORD dwData;
|
|
TCHAR tcMessage[BUFFER_SIZE] = {0};
|
|
TCHAR szPath[MAX_PATH+1] = {0};
|
|
HRESULT hr = S_OK;
|
|
size_t cch = 0;
|
|
size_t cb = 0;
|
|
|
|
if (!GetSystemWindowsDirectory(szPath, MAX_PATH+1))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MUICheckEventSource: Unable to find the Windows directory, GetSystemWindowsDirectory returned %d."), GetLastError());
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// check retrieved winpath, it needs to have space to append "system32\intl.cpl" at the end
|
|
hr = StringCchLength(szPath, ARRAYSIZE(szPath), &cch);
|
|
if (FAILED(hr) || ((cch + 17) >= MAX_PATH+1))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MUICheckEventSource: cannot find system windows path."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// append system32\intl.cpl
|
|
if (!MUICchPathAppend(szPath, ARRAYSIZE(szPath), TEXT("system32\\intl.cpl"), 18, hInstall))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MUICheckEventSource: cannot form path to muisetup.exe."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// get the byte count for RegSetValueEx
|
|
hr = StringCbLength(szPath, MAX_PATH+1 * sizeof(TCHAR), &cb);
|
|
if (FAILED(hr))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MUICheckEventSource: cannot form path to muisetup.exe."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// Add intl.cpl source name as a subkey under the System
|
|
// key in the EventLog registry key. This should be there already, but add it anyways if it is not.
|
|
if (ERROR_SUCCESS != RegCreateKeyEx(HKEY_LOCAL_MACHINE, REGOPT_EVENTSOURCE, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hk, NULL))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MUICheckEventSource: cannot add Intl.cpl event source regkey."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// Add the name to the EventMessageFile subkey.
|
|
if (ERROR_SUCCESS != RegSetValueEx(hk, TEXT("EventMessageFile"), 0, REG_EXPAND_SZ, (LPBYTE) szPath, cb))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MUICheckEventSource: cannot add event source Event message file information."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
RegCloseKey(hk);
|
|
return FALSE;
|
|
}
|
|
|
|
// Set the supported event types in the TypesSupported subkey.
|
|
dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
|
|
|
|
if (ERROR_SUCCESS != RegSetValueEx(hk, TEXT("TypesSupported"), 0, REG_DWORD, (LPBYTE) &dwData, sizeof(DWORD)))
|
|
{
|
|
hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MUICheckEventSource: cannot add event source TypeSupported information."));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogCustomActionInfo(hInstall, tcMessage);
|
|
}
|
|
RegCloseKey(hk);
|
|
return FALSE;
|
|
}
|
|
|
|
RegCloseKey(hk);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetDotDefaultUILanguage
|
|
//
|
|
// Retrieve the UI language stored in the HKCU\.Default.
|
|
// This is the default UI language for new users.
|
|
// This function sends an INFORMATION-type log message record to the opened
|
|
// windows installer session so that it can be logged by the installer
|
|
// if logging is enabled.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
LANGID GetDotDefaultUILanguage(MSIHANDLE hInstall)
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwKeyType;
|
|
DWORD dwSize;
|
|
BOOL success = FALSE;
|
|
TCHAR szBuffer[BUFFER_SIZE] = {0};
|
|
LANGID langID;
|
|
|
|
// Get the value in .DEFAULT.
|
|
if (RegOpenKeyEx( HKEY_USERS,
|
|
TEXT(".DEFAULT\\Control Panel\\Desktop"),
|
|
0L,
|
|
KEY_READ,
|
|
&hKey ) == ERROR_SUCCESS)
|
|
{
|
|
dwSize = sizeof(szBuffer);
|
|
if (RegQueryValueEx( hKey,
|
|
TEXT("MultiUILanguageId"),
|
|
0L,
|
|
&dwKeyType,
|
|
(LPBYTE)szBuffer,
|
|
&dwSize) == ERROR_SUCCESS)
|
|
{
|
|
if (dwKeyType == REG_SZ)
|
|
{
|
|
langID = (LANGID)_tcstol(szBuffer, NULL, 16);
|
|
success = TRUE;
|
|
}
|
|
}
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
if (!success)
|
|
{
|
|
langID = GetSystemDefaultUILanguage();
|
|
}
|
|
|
|
return (langID);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// IsOEMSystem
|
|
//
|
|
// Retrieve the Product ID stored in HKLM\Software\Microsoft\Windows NT\CurrentVersion
|
|
// If the product ID contains the string "OEM", it is determined to be an OEM system.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL IsOEMSystem()
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwKeyType;
|
|
DWORD dwSize;
|
|
BOOL bRet = FALSE;
|
|
TCHAR szBuffer[BUFFER_SIZE] = {0};
|
|
TCHAR szOEM[] = TEXT("OEM");
|
|
|
|
if (RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion"),
|
|
0L,
|
|
KEY_READ,
|
|
&hKey ) == ERROR_SUCCESS)
|
|
{
|
|
dwSize = sizeof(szBuffer);
|
|
if (RegQueryValueEx( hKey,
|
|
TEXT("ProductId"),
|
|
0L,
|
|
&dwKeyType,
|
|
(LPBYTE)szBuffer,
|
|
&dwSize) == ERROR_SUCCESS)
|
|
{
|
|
if (dwKeyType == REG_SZ)
|
|
{
|
|
if (StrStrI((LPCTSTR)szBuffer, (LPCTSTR)szOEM) != NULL)
|
|
{
|
|
bRet = TRUE;
|
|
}
|
|
}
|
|
}
|
|
RegCloseKey(hKey);
|
|
}
|
|
return bRet;
|
|
}
|
|
|