|
|
// 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; }
|