Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3670 lines
132 KiB

  1. // muimsidll.cpp : Defines the entry point for the DLL application.
  2. //
  3. #include <nt.h>
  4. #include <ntrtl.h>
  5. #include <nturtl.h>
  6. #include <windows.h>
  7. #include <tchar.h>
  8. #include <Msiquery.h>
  9. #include <wmistr.h>
  10. #include <wmiumkm.h>
  11. #include <Shlwapi.h>
  12. #include <Setupapi.h>
  13. #include <advpub.h>
  14. #include <lmcons.h>
  15. #include <strsafe.h>
  16. #include <intlmsg.h>
  17. //
  18. // DEFINES
  19. //
  20. #define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
  21. #define MUI_LANG_GROUP_FILE TEXT("muilang.txt")
  22. #define BUFFER_SIZE 1024
  23. #define MUISETUP_PATH_SEPARATOR TEXT("\\")
  24. #define MUIDIR TEXT("MUI")
  25. #define MUI_LANGPACK_SECTION TEXT("LanguagePack")
  26. #define MUI_COMPONENTS_SECTION TEXT("Components")
  27. #define LANGGROUPNUMBER 32
  28. #define LANGPACKDISKCOST 300000000
  29. #define DEFAULT_INSTALL_SECTION TEXT("DefaultInstall")
  30. #define DEFAULT_UNINSTALL_SECTION TEXT("DefaultUninstall")
  31. #define FALLBACKDIR TEXT("MUI\\FALLBACK")
  32. #define EXTDIR TEXT("External")
  33. #define COMP_TICK_INC 5000000
  34. #define LANGPACK_TICK_INC 200000000
  35. #define OEM_COMPONENT 1
  36. #define SELECTMUIINFBINSTREAM TEXT("SELECT `Data` FROM `Binary` WHERE `Name` = 'MUIINF'")
  37. // name of intl.cpl event source
  38. #define REGOPT_EVENTSOURCE TEXT("SYSTEM\\CurrentControlSet\\Services\\EventLog\\System\\Regional and Language Options")
  39. #define REGOPT_EVENTSOURCE_NAME TEXT("Regional and Language Options")
  40. #ifdef MUI_DEDUG
  41. #define DEBUGMSGBOX(a, b, c, d) MessageBox(a, b, c, d)
  42. #else
  43. #define DEBUGMSGBOX(a, b, c, d)
  44. #endif
  45. //
  46. // TYPEDEFS
  47. //
  48. typedef
  49. BOOL (WINAPI *pfnMUI_InstallMFLFiles)(
  50. TCHAR* pMUIInstallLanguage
  51. );
  52. //
  53. // Internal function prototypes
  54. //
  55. void NotifyKernel(LPTSTR LangList, ULONG Flags, MSIHANDLE hInstall);
  56. BOOL MofCompileLanguage(LPTSTR Languages, MSIHANDLE hInstall);
  57. BOOL EnumLanguageGroupLocalesProc(LGRPID langGroupId, LCID lcid, LPTSTR lpszLocale, LONG_PTR lParam);
  58. BOOL EnumLanguageGroupsProc(LGRPID LanguageGroup, LPTSTR lpLanguageGroupString, LPTSTR lpLanguageGroupNameString, DWORD dwFlags, LONG_PTR lParam);
  59. LGRPID GetLanguageGroup(LCID lcid, MSIHANDLE hInstall);
  60. BOOL RunRegionalOptionsApplet(LPTSTR pCommands, BOOL bSilent, MSIHANDLE hInstall);
  61. BOOL DeleteSideBySideMUIAssemblyIfExisted(LPTSTR Language, TCHAR pszLogFile[BUFFER_SIZE]);
  62. BOOL ReturnAllRequiredLangGroups(LPTSTR szLcid, UINT cchLcidBufsize, LPTSTR szMuiInfPath, UINT cchPathbufsize, LGRPID *lgrpids, UINT *uiNumFoundGroups, MSIHANDLE hInstall);
  63. BOOL ExecuteComponentINF(PTSTR pComponentName, PTSTR pComponentInfFile, PTSTR pInstallSection, BOOL bInstall, MSIHANDLE hInstall);
  64. INT InstallComponentsMUIFiles(PTSTR pszLanguage, BOOL isInstall, MSIHANDLE hInstall);
  65. BOOL FileExists(LPTSTR szFile);
  66. void LogCustomActionInfo(MSIHANDLE hInstall, LPCTSTR szErrorMsg);
  67. BOOL SetUILanguage(TCHAR *szLanguage, BOOL bCurrent, BOOL bDefault, MSIHANDLE hInstall);
  68. UINT GetMUIComponentsNumber(PTSTR pszLanguage, MSIHANDLE hInstall);
  69. BOOL GetMUIInfPath(TCHAR *szMUIInfPath, UINT cchBufSize, MSIHANDLE hInstall);
  70. BOOL GetLCID(TCHAR *szLanguage, UINT cchBufSize, MSIHANDLE hInstall);
  71. BOOL MUICchPathAppend(LPTSTR szDestination, UINT cchDestBufSize, LPTSTR szAppend, UINT cchAppBufSize, MSIHANDLE hInstall);
  72. BOOL MUIReportInfoEvent(DWORD dwEventID, TCHAR *szLanguage, UINT cchBufSize, MSIHANDLE hInstall);
  73. BOOL MUICheckEventSource(MSIHANDLE hInstall);
  74. LANGID GetDotDefaultUILanguage(MSIHANDLE hInstall);
  75. BOOL IsOEMSystem();
  76. //
  77. // Global Variables
  78. //
  79. // Flags to indicate whether a language group is found for the locale or not.
  80. BOOL gFoundLangGroup;
  81. LGRPID gLangGroup;
  82. LCID gLCID;
  83. // The language groups installed in the system.
  84. LGRPID gLanguageGroups[LANGGROUPNUMBER] ;
  85. int gNumLanguageGroups;
  86. //
  87. // Main dll entry point
  88. //
  89. BOOL APIENTRY DllMain( HANDLE hModule,
  90. DWORD ul_reason_for_call,
  91. LPVOID lpReserved)
  92. {
  93. switch (ul_reason_for_call)
  94. {
  95. case ( DLL_THREAD_ATTACH ) :
  96. {
  97. return (TRUE);
  98. }
  99. case ( DLL_THREAD_DETACH ) :
  100. {
  101. return (TRUE);
  102. }
  103. case ( DLL_PROCESS_ATTACH ) :
  104. {
  105. return (TRUE);
  106. }
  107. case ( DLL_PROCESS_DETACH ) :
  108. {
  109. return (TRUE);
  110. }
  111. }
  112. return (FALSE);
  113. }
  114. ////////////////////////////////////////////////////////////////////////////////////
  115. //
  116. // DisableCancelButton
  117. //
  118. // The DisableCancelButton checks to see if a specific parameter has been passed
  119. // to the current installation, if it has, it will issue a command to disable
  120. // the cancel button in the UI during installation. This is used by our
  121. // muisetup.exe wrapper so that the user cannot cancel out of an installation or
  122. // uninstallation once it has started.
  123. //
  124. ////////////////////////////////////////////////////////////////////////////////////
  125. UINT CA1(MSIHANDLE hInstall)
  126. {
  127. UINT uiRet = ERROR_SUCCESS;
  128. PMSIHANDLE hRecord = MsiCreateRecord(3);
  129. TCHAR szBuffer[BUFFER_SIZE] = { 0 };
  130. HRESULT hr = S_OK;
  131. if (NULL == hInstall)
  132. {
  133. uiRet = ERROR_INSTALL_FAILURE;
  134. goto Exit;
  135. }
  136. // if can't create a msi record, just return
  137. if (NULL == hRecord)
  138. {
  139. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("CA1 Failure: cannot create MSI Record."));
  140. if (SUCCEEDED(hr))
  141. {
  142. LogCustomActionInfo(hInstall, szBuffer);
  143. }
  144. uiRet = ERROR_INSTALL_FAILURE;
  145. goto Exit;
  146. }
  147. // field 0 = unused, field 1 = 2 (cancel button), field 2 = 0 (0 to disable/hide cancel button)
  148. if (ERROR_SUCCESS != MsiRecordSetInteger(hRecord, 1, 2))
  149. {
  150. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("CA1 Failure: MsiRecordSetInteger function failed."));
  151. if (SUCCEEDED(hr))
  152. {
  153. LogCustomActionInfo(hInstall, szBuffer);
  154. }
  155. uiRet = ERROR_INSTALL_FAILURE;
  156. goto Exit;
  157. }
  158. if (ERROR_SUCCESS != MsiRecordSetInteger(hRecord, 2, 0))
  159. {
  160. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("CA1 Failure: MsiRecordSetInteger function failed."));
  161. if (SUCCEEDED(hr))
  162. {
  163. LogCustomActionInfo(hInstall, szBuffer);
  164. }
  165. uiRet = ERROR_INSTALL_FAILURE;
  166. goto Exit;
  167. }
  168. MsiProcessMessage(hInstall, INSTALLMESSAGE_COMMONDATA, hRecord);
  169. Exit:
  170. return uiRet;
  171. }
  172. ////////////////////////////////////////////////////////////////////////////////////
  173. //
  174. // InstallComponentInfs
  175. //
  176. //
  177. ////////////////////////////////////////////////////////////////////////////////////
  178. UINT CA10(MSIHANDLE hInstall)
  179. {
  180. TCHAR szLanguage[5] = {0};
  181. TCHAR szBuffer[BUFFER_SIZE] = {0};
  182. PMSIHANDLE hRec = MsiCreateRecord(3);
  183. PMSIHANDLE hProgressRec = MsiCreateRecord(3);
  184. UINT iFunctionResult = ERROR_SUCCESS;
  185. INT iInstallResult = IDOK;
  186. HRESULT hr = S_OK;
  187. if (NULL == hInstall)
  188. {
  189. return ERROR_INSTALL_FAILURE;
  190. }
  191. if ((NULL == hRec) || (NULL == hProgressRec))
  192. {
  193. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("CA10 Failure: cannot create MSI Record."));
  194. if (SUCCEEDED(hr))
  195. {
  196. LogCustomActionInfo(hInstall, szBuffer);
  197. }
  198. return ERROR_INSTALL_FAILURE;
  199. }
  200. if (!GetLCID(szLanguage, ARRAYSIZE(szLanguage), hInstall))
  201. {
  202. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("CA10 Failure: Cannot retrieve MuiLCID property."));
  203. if (SUCCEEDED(hr))
  204. {
  205. LogCustomActionInfo(hInstall, szBuffer);
  206. }
  207. return ERROR_INSTALL_FAILURE;
  208. }
  209. // Tell the installer to check the installation state and execute
  210. // the code needed during the rollback, acquisition, or
  211. // execution phases of the installation.
  212. if (MsiGetMode(hInstall,MSIRUNMODE_ROLLBACK))
  213. {
  214. // Installer is rolling back the installation, here we just remove what we had before
  215. // Since we are in rollback, we don't set progress bar or update message in UI
  216. // we also don't check for returned results here, since the installation has failed already.
  217. InstallComponentsMUIFiles(szLanguage, FALSE, hInstall);
  218. return ERROR_SUCCESS;
  219. }
  220. if (!MsiGetMode(hInstall,MSIRUNMODE_SCHEDULED))
  221. {
  222. // Installer is generating the installation script of the custom
  223. // action. Tell the installer to increase the value of the final total
  224. // length of the progress bar by the total number of ticks in the
  225. // custom action.
  226. UINT iCount = GetMUIComponentsNumber(szLanguage, hInstall);
  227. if (iCount > 0)
  228. {
  229. MsiRecordSetInteger(hRec,1,3);
  230. MsiRecordSetInteger(hRec,2,COMP_TICK_INC * iCount);
  231. MsiRecordSetInteger(hRec,3,0);
  232. iInstallResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRec);
  233. }
  234. //
  235. // we just want to trap the cancel message here, otherwise we always return success since
  236. // we are just setting progressbar here.
  237. //
  238. if (iInstallResult == IDCANCEL)
  239. {
  240. return ERROR_INSTALL_USEREXIT;
  241. }
  242. else
  243. {
  244. return ERROR_SUCCESS;
  245. }
  246. }
  247. else
  248. {
  249. // Installer is executing the installation script. Set up a
  250. // record specifying appropriate templates and text for messages
  251. // that will inform the user about what the custom action is
  252. // doing. Tell the installer to use this template and text in
  253. // progress messages.
  254. MsiRecordSetString(hRec,1,TEXT("Installing Components."));
  255. MsiRecordSetString(hRec,2,TEXT("Installing External Component Inf files..."));
  256. MsiRecordSetString(hRec,3,TEXT("Installing MUI files for Component [1]."));
  257. MsiProcessMessage(hInstall, INSTALLMESSAGE_ACTIONSTART, hRec);
  258. // Tell the installer to use explicit progress messages.
  259. MsiRecordSetInteger(hRec,1,1);
  260. MsiRecordSetInteger(hRec,2,1);
  261. MsiRecordSetInteger(hRec,3,0);
  262. MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRec);
  263. // do the actual work for the custom action
  264. iInstallResult = InstallComponentsMUIFiles(szLanguage, TRUE, hInstall);
  265. if (IDCANCEL == iInstallResult)
  266. {
  267. iFunctionResult = ERROR_INSTALL_USEREXIT;
  268. }
  269. else if (-1 == iInstallResult)
  270. {
  271. iFunctionResult = ERROR_INSTALL_FAILURE;
  272. }
  273. else
  274. {
  275. iFunctionResult = ERROR_SUCCESS;
  276. }
  277. }
  278. return iFunctionResult;
  279. }
  280. ////////////////////////////////////////////////////////////////////////////////////
  281. //
  282. // UninstallComponentInfs
  283. //
  284. //
  285. ////////////////////////////////////////////////////////////////////////////////////
  286. UINT CA11(MSIHANDLE hInstall)
  287. {
  288. TCHAR szLanguage[5] = {0};
  289. TCHAR szBuffer[BUFFER_SIZE] = {0};
  290. PMSIHANDLE hRec = MsiCreateRecord(3);
  291. PMSIHANDLE hProgressRec = MsiCreateRecord(3);
  292. UINT iFunctionResult = ERROR_SUCCESS;
  293. INT iInstallResult = IDOK;
  294. HRESULT hr = S_OK;
  295. if (NULL == hInstall)
  296. {
  297. return ERROR_INSTALL_FAILURE;
  298. }
  299. if ((NULL == hRec) || (NULL == hProgressRec))
  300. {
  301. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("CA11 Failure: cannot create MSI Record."));
  302. if (SUCCEEDED(hr))
  303. {
  304. LogCustomActionInfo(hInstall, szBuffer);
  305. }
  306. return ERROR_INSTALL_FAILURE;
  307. }
  308. if (!GetLCID(szLanguage, ARRAYSIZE(szLanguage), hInstall))
  309. {
  310. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("CA11 Failure: Cannot retrieve MuiLCID property."));
  311. if (SUCCEEDED(hr))
  312. {
  313. LogCustomActionInfo(hInstall, szBuffer);
  314. }
  315. return ERROR_INSTALL_FAILURE;
  316. }
  317. // Tell the installer to check the installation state and execute
  318. // the code needed during the rollback, acquisition, or
  319. // execution phases of the installation.
  320. if (MsiGetMode(hInstall,MSIRUNMODE_ROLLBACK))
  321. {
  322. // Installer is rolling back the installation. We will reinstall what we uninstalled before.
  323. // we don't update progress message here. And we always return SUCCESS
  324. InstallComponentsMUIFiles(szLanguage, TRUE, hInstall);
  325. return ERROR_SUCCESS;
  326. }
  327. if (!MsiGetMode(hInstall,MSIRUNMODE_SCHEDULED))
  328. {
  329. // Installer is generating the installation script of the custom
  330. // action. Tell the installer to increase the value of the final total
  331. // length of the progress bar by the total number of ticks in the
  332. // custom action.
  333. UINT iCount = GetMUIComponentsNumber(szLanguage, hInstall);
  334. if (iCount > 0)
  335. {
  336. MsiRecordSetInteger(hRec,1,3);
  337. MsiRecordSetInteger(hRec,2,COMP_TICK_INC * iCount);
  338. MsiRecordSetInteger(hRec,3,0);
  339. iInstallResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRec);
  340. }
  341. //
  342. // we just want to trap the cancel message here, otherwise we always return success since
  343. // we are just setting progressbar here.
  344. //
  345. if (iInstallResult == IDCANCEL)
  346. {
  347. return ERROR_INSTALL_USEREXIT;
  348. }
  349. else
  350. {
  351. return ERROR_SUCCESS;
  352. }
  353. }
  354. else
  355. {
  356. // Installer is executing the installation script. Set up a
  357. // record specifying appropriate templates and text for messages
  358. // that will inform the user about what the custom action is
  359. // doing. Tell the installer to use this template and text in
  360. // progress messages.
  361. MsiRecordSetString(hRec,1,TEXT("Uninstall Components."));
  362. MsiRecordSetString(hRec,2,TEXT("Removing External Component Inf files..."));
  363. MsiRecordSetString(hRec,3,TEXT("Removing MUI files for Component [1]."));
  364. MsiProcessMessage(hInstall, INSTALLMESSAGE_ACTIONSTART, hRec);
  365. // Tell the installer to use explicit progress messages.
  366. MsiRecordSetInteger(hRec,1,1);
  367. MsiRecordSetInteger(hRec,2,1);
  368. MsiRecordSetInteger(hRec,3,0);
  369. MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRec);
  370. // do the actual work for the custom action, we only check for user cancel here
  371. // and nothing else
  372. iInstallResult = InstallComponentsMUIFiles(szLanguage, FALSE, hInstall);
  373. if (IDCANCEL == iInstallResult)
  374. {
  375. iFunctionResult = ERROR_INSTALL_USEREXIT;
  376. }
  377. else if (-1 == iInstallResult)
  378. {
  379. iFunctionResult = ERROR_INSTALL_FAILURE;
  380. }
  381. else
  382. {
  383. iFunctionResult = ERROR_SUCCESS;
  384. }
  385. }
  386. return iFunctionResult;
  387. }
  388. ////////////////////////////////////////////////////////////////////////////////////
  389. //
  390. // SetLangPackRequirement
  391. //
  392. // This function is used to set a property in the MSI Database so that the installation knows whether
  393. // it needs to install the language pack or not so it can reserve diskcost for it
  394. //
  395. ////////////////////////////////////////////////////////////////////////////////////
  396. UINT CA3(MSIHANDLE hInstall)
  397. {
  398. LGRPID lgrpid[LANGGROUPNUMBER] = {0};
  399. UINT iRet = ERROR_SUCCESS;
  400. UINT uiLGrpNums = 0;
  401. UINT i;
  402. DWORD uiAddCost = 0;
  403. TCHAR tcMessage[BUFFER_SIZE] = {0};
  404. TCHAR szMUIInfPath[MAX_PATH+1] = {0};
  405. TCHAR szLanguage[5] = {0};
  406. HRESULT hr = S_OK;
  407. if (NULL == hInstall)
  408. {
  409. return ERROR_INSTALL_FAILURE;
  410. }
  411. if (!GetLCID(szLanguage, ARRAYSIZE(szLanguage), hInstall))
  412. {
  413. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA3 Failure: Cannot retrieve MuiLCID property."));
  414. if (SUCCEEDED(hr))
  415. {
  416. LogCustomActionInfo(hInstall, tcMessage);
  417. }
  418. return ERROR_INSTALL_FAILURE;
  419. }
  420. if (!GetMUIInfPath(szMUIInfPath, MAX_PATH+1, hInstall))
  421. {
  422. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA3 Failure: Cannot find installation temp file."));
  423. if (SUCCEEDED(hr))
  424. {
  425. LogCustomActionInfo(hInstall, tcMessage);
  426. }
  427. return ERROR_INSTALL_FAILURE;
  428. }
  429. if (!ReturnAllRequiredLangGroups(szLanguage, ARRAYSIZE(szLanguage), szMUIInfPath, ARRAYSIZE(szMUIInfPath), lgrpid, &uiLGrpNums, hInstall))
  430. {
  431. // log an error
  432. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA3: ReturnAllRequiredLangGroups failed."));
  433. if (SUCCEEDED(hr))
  434. {
  435. LogCustomActionInfo(hInstall, tcMessage);
  436. }
  437. iRet = ERROR_INSTALL_FAILURE;
  438. goto Exit;
  439. }
  440. #ifdef MUI_DEBUG
  441. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA3: The number of lang groups required is %d."), uiLGrpNums);
  442. if (SUCCEEDED(hr))
  443. {
  444. DEBUGMSGBOX(NULL, tcMessage, NULL, MB_OK);
  445. }
  446. #endif
  447. // Enumerate through all the lang groups required, check there are any language groups that requires installation
  448. for (i = 0; i < uiLGrpNums; i++)
  449. {
  450. if (!IsValidLanguageGroup(lgrpid[i], LGRPID_INSTALLED))
  451. {
  452. uiAddCost += LANGPACKDISKCOST;
  453. }
  454. }
  455. if (uiAddCost > 0)
  456. {
  457. DEBUGMSGBOX(NULL, TEXT("CA3: Need to install additional language groups."), NULL, MB_OK);
  458. if (ERROR_SUCCESS != MsiSetProperty(hInstall, TEXT("MsiRequireLangPack"), TEXT("1")))
  459. {
  460. // log an error
  461. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA3 Failure: Cannot set MsiRequireLangPack property in the MSI Database."));
  462. if (SUCCEEDED(hr))
  463. {
  464. LogCustomActionInfo(hInstall, tcMessage);
  465. }
  466. iRet = ERROR_INSTALL_FAILURE;
  467. }
  468. }
  469. else
  470. {
  471. DEBUGMSGBOX(NULL, TEXT("CA3: Language group already installed."), NULL, MB_OK);
  472. if (ERROR_SUCCESS != MsiSetProperty(hInstall, TEXT("MsiRequireLangPack"), NULL))
  473. {
  474. // log an error
  475. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA3 Failure: Cannot set MsiRequireLangPack property in the MSI Database."));
  476. if (SUCCEEDED(hr))
  477. {
  478. LogCustomActionInfo(hInstall, tcMessage);
  479. }
  480. iRet = ERROR_INSTALL_FAILURE;
  481. }
  482. }
  483. Exit:
  484. return iRet;
  485. }
  486. ////////////////////////////////////////////////////////////////////////////////////
  487. //
  488. // IsNTSuiteWebBlade
  489. //
  490. // This function is used by a custom action in our setup package to detect whether setup was invoked on a windows Blade server.
  491. //
  492. ////////////////////////////////////////////////////////////////////////////////////
  493. /*UINT CA5(MSIHANDLE hInstall)
  494. {
  495. OSVERSIONINFOEX osvi;
  496. TCHAR tcMessage[BUFFER_SIZE] = {0};
  497. HRESULT hr = S_OK;
  498. if (NULL == hInstall)
  499. {
  500. return ERROR_INSTALL_FAILURE;
  501. }
  502. ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
  503. osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
  504. if (!GetVersionEx ((OSVERSIONINFO *) &osvi))
  505. {
  506. // log an error
  507. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA5 Failure: GetVersionEx failed, cannot retrieve platform OS version. Error returned is: %d."), GetLastError());
  508. if (SUCCEEDED(hr))
  509. {
  510. LogCustomActionInfo(hInstall, tcMessage);
  511. }
  512. return ERROR_INSTALL_FAILURE;
  513. }
  514. else
  515. {
  516. if ((osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) && // test for NT
  517. ( (osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion > 0) ) && // test for version > 5.0 (Whistler or later)
  518. ( (osvi.wSuiteMask & VER_SUITE_BLADE ) != 0) && // test for Suite Web Server
  519. ( (osvi.wProductType != VER_NT_WORKSTATION ) )) // test for non-workstation type (server)
  520. {
  521. // here we need to set a MSI property so that the current installation knows that NT Suite is WebBlade
  522. if (ERROR_SUCCESS != MsiSetProperty(hInstall, TEXT("MsiNTSuiteWebBlade"), TEXT("1")))
  523. {
  524. // log an error
  525. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA5 Failure: Failed to set required MUI MSI property."));
  526. if (SUCCEEDED(hr))
  527. {
  528. LogCustomActionInfo(hInstall, tcMessage);
  529. }
  530. return ERROR_INSTALL_FAILURE; // can't set the property, return error
  531. }
  532. }
  533. else
  534. {
  535. // here we need to set a MSI property so that the current installation knows that NT Suite is WebBlade
  536. if (ERROR_SUCCESS != MsiSetProperty(hInstall, TEXT("MsiNTSuiteWebBlade"), NULL))
  537. {
  538. // log an error
  539. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA5: Failed to set required MUI MSI property."));
  540. if (SUCCEEDED(hr))
  541. {
  542. LogCustomActionInfo(hInstall, tcMessage);
  543. }
  544. return ERROR_INSTALL_FAILURE; // can't set the property, return error
  545. }
  546. }
  547. }
  548. return ERROR_SUCCESS;
  549. }
  550. */
  551. ////////////////////////////////////////////////////////////////////////////////////
  552. //
  553. // CheckDefaultSystemUILang
  554. //
  555. // This function is used by our setup to check whether setup is invoked on a system with US-English as the default language (0x0409)
  556. //
  557. ////////////////////////////////////////////////////////////////////////////////////
  558. UINT CA4(MSIHANDLE hInstall)
  559. {
  560. // get the system default UI language, and set a MSI property accordingly
  561. LANGID liSysLang = GetSystemDefaultUILanguage();
  562. TCHAR tcMessage[BUFFER_SIZE] = {0};
  563. HRESULT hr = S_OK;
  564. if (NULL == hInstall)
  565. {
  566. return ERROR_INSTALL_FAILURE;
  567. }
  568. if (liSysLang == 0x0409)
  569. {
  570. if (ERROR_SUCCESS != MsiSetProperty(hInstall, TEXT("MUISystemLangIsEnglish"), TEXT("1")))
  571. {
  572. // log an error
  573. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA4 Failure: Failed to set property MUISystemLangIsEnglish."));
  574. if (SUCCEEDED(hr))
  575. {
  576. LogCustomActionInfo(hInstall, tcMessage);
  577. }
  578. return ERROR_INSTALL_FAILURE; // can't set the property, return error
  579. }
  580. }
  581. else
  582. {
  583. if (ERROR_SUCCESS != MsiSetProperty(hInstall, TEXT("MUISystemLangIsEnglish"), NULL))
  584. {
  585. // log an error
  586. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA4 Failure: Failed to set property MUISystemLangIsEnglish."));
  587. if (SUCCEEDED(hr))
  588. {
  589. LogCustomActionInfo(hInstall, tcMessage);
  590. }
  591. return ERROR_INSTALL_FAILURE; // can't set the property, return error
  592. }
  593. }
  594. return ERROR_SUCCESS;
  595. }
  596. ////////////////////////////////////////////////////////////////////////////////////
  597. //
  598. // LogInstallComplete
  599. //
  600. // This function is used by setup to log a message to the system event logger
  601. // to indicate that the MUI language is installed.
  602. //
  603. ////////////////////////////////////////////////////////////////////////////////////
  604. UINT CA12(MSIHANDLE hInstall)
  605. {
  606. TCHAR tcMessage[BUFFER_SIZE] = {0};
  607. TCHAR szLanguage[5] = {0};
  608. HRESULT hr = S_OK;
  609. if (!GetLCID(szLanguage, ARRAYSIZE(szLanguage), hInstall))
  610. {
  611. // log an error
  612. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA12 Failure: Failed to get property MUILcid."));
  613. if (SUCCEEDED(hr))
  614. {
  615. LogCustomActionInfo(hInstall, tcMessage);
  616. }
  617. return ERROR_INSTALL_FAILURE;
  618. }
  619. if (MUIReportInfoEvent(MSG_REGIONALOPTIONS_LANGUAGEINSTALL, szLanguage, BUFFER_SIZE, hInstall))
  620. {
  621. return ERROR_SUCCESS;
  622. }
  623. // log an error, if we get to here it's always an error
  624. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA12 Failure: Failed to log to system event logfile."));
  625. if (SUCCEEDED(hr))
  626. {
  627. LogCustomActionInfo(hInstall, tcMessage);
  628. }
  629. return ERROR_INSTALL_FAILURE;
  630. }
  631. ////////////////////////////////////////////////////////////////////////////////////
  632. //
  633. // LogUninstallComplete
  634. //
  635. // This function is used by setup to log a message to the system event logger
  636. // to indicate that the MUI language is uninstalled.
  637. //
  638. ////////////////////////////////////////////////////////////////////////////////////
  639. UINT CA13(MSIHANDLE hInstall)
  640. {
  641. TCHAR tcMessage[BUFFER_SIZE] = {0};
  642. TCHAR szLanguage[5] = {0};
  643. HRESULT hr = S_OK;
  644. if (!GetLCID(szLanguage, ARRAYSIZE(szLanguage), hInstall))
  645. {
  646. // log an error
  647. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA13 Failure: Failed to get property MUILcid."));
  648. if (SUCCEEDED(hr))
  649. {
  650. LogCustomActionInfo(hInstall, tcMessage);
  651. }
  652. return ERROR_INSTALL_FAILURE;
  653. }
  654. if (MUIReportInfoEvent(MSG_REGIONALOPTIONS_LANGUAGEUNINSTALL, szLanguage, BUFFER_SIZE, hInstall))
  655. {
  656. return ERROR_SUCCESS;
  657. }
  658. // log an error, if we get to here it's always an error
  659. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA13 Failure: Failed to log to system event logfile."));
  660. if (SUCCEEDED(hr))
  661. {
  662. LogCustomActionInfo(hInstall, tcMessage);
  663. }
  664. return ERROR_INSTALL_FAILURE;
  665. }
  666. ////////////////////////////////////////////////////////////////////////////////////
  667. //
  668. // InstallWBEMMUI
  669. //
  670. // This function is used to set up WMI\WBEM stuff for MUI.
  671. //
  672. ////////////////////////////////////////////////////////////////////////////////////
  673. UINT CA6(MSIHANDLE hInstall)
  674. {
  675. UINT iRet = ERROR_SUCCESS;
  676. TCHAR szLanguage[5] = {0};
  677. TCHAR tcMessage[BUFFER_SIZE] = {0};
  678. HRESULT hr = S_OK;
  679. size_t cch = 0;
  680. if (NULL == hInstall)
  681. {
  682. return ERROR_INSTALL_FAILURE;
  683. }
  684. if (!GetLCID(szLanguage, ARRAYSIZE(szLanguage), hInstall))
  685. {
  686. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA6 Failure: Cannot retrieve MuiLCID property."));
  687. if (SUCCEEDED(hr))
  688. {
  689. LogCustomActionInfo(hInstall, tcMessage);
  690. }
  691. return ERROR_INSTALL_FAILURE;
  692. }
  693. //
  694. // call WBEM API to mofcompile MUI MFL's for each language
  695. //
  696. if (!MofCompileLanguage(szLanguage, hInstall))
  697. {
  698. // log an error
  699. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA6 Failure: MofCompileLanguage Failed."));
  700. if (SUCCEEDED(hr))
  701. {
  702. LogCustomActionInfo(hInstall, tcMessage);
  703. }
  704. iRet = ERROR_INSTALL_FAILURE;
  705. goto Exit;
  706. }
  707. //
  708. // Inform kernel that new languages have been added
  709. //
  710. NotifyKernel(szLanguage, WMILANGUAGECHANGE_FLAG_ADDED, hInstall);
  711. Exit:
  712. return iRet;
  713. }
  714. ////////////////////////////////////////////////////////////////////////////////////
  715. //
  716. // UninstallWBEMMUI
  717. //
  718. // This function is called by our setup to uninstall external component associated with a MSI package.
  719. //
  720. ////////////////////////////////////////////////////////////////////////////////////
  721. UINT CA7(MSIHANDLE hInstall)
  722. {
  723. UINT iRet = ERROR_SUCCESS;
  724. TCHAR szLanguage[5] = {0};
  725. TCHAR tcMessage[BUFFER_SIZE] = {0};
  726. HRESULT hr = S_OK;
  727. size_t cch = 0;
  728. if (NULL == hInstall)
  729. {
  730. return ERROR_INSTALL_FAILURE;
  731. }
  732. if (!GetLCID(szLanguage, ARRAYSIZE(szLanguage), hInstall))
  733. {
  734. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA7 Failure: Cannot retrieve MuiLCID property."));
  735. if (SUCCEEDED(hr))
  736. {
  737. LogCustomActionInfo(hInstall, tcMessage);
  738. }
  739. return ERROR_INSTALL_FAILURE;
  740. }
  741. //
  742. // Inform kernel that new languages have been added
  743. //
  744. NotifyKernel(szLanguage, WMILANGUAGECHANGE_FLAG_REMOVED, hInstall);
  745. return iRet;
  746. }
  747. ////////////////////////////////////////////////////////////////////////////////////
  748. //
  749. // SetDefaultUserLanguage
  750. //
  751. // This function does two things - depending on the MSI execution mode:
  752. //
  753. // 1. Immediate:
  754. // This function in immediate mode will schedule the deferred and the rollback
  755. // custom action C8D/C8R. (C8D and C8R are the expected CA identifiers used
  756. // in the MUI MSI package)
  757. //
  758. // 2. Deferred/Rollback:
  759. // This function sets the default language of new users to that of the MUI
  760. // language that is being installed. GetLCID in this instance will read
  761. // the set CustomActionData property which is what we want.
  762. //
  763. // Also, reboot is needed after this CA in deferred mode, but this is specified
  764. // in the template itself, so the CA itself will not prompt for reboot (it can't
  765. // anyways, since it is deferred).
  766. //
  767. ////////////////////////////////////////////////////////////////////////////////////
  768. UINT CA8(MSIHANDLE hInstall)
  769. {
  770. UINT iRet = ERROR_SUCCESS;
  771. TCHAR szLanguage[5] = {0};
  772. TCHAR szOrigLanguage[5] = {0};
  773. TCHAR tcMessage[BUFFER_SIZE] = {0};
  774. HRESULT hr = S_OK;
  775. size_t cch = 0;
  776. if (NULL == hInstall)
  777. {
  778. return ERROR_INSTALL_FAILURE;
  779. }
  780. // get the MUI LCID that we want to set the default UI Language to.
  781. if (!GetLCID(szLanguage, ARRAYSIZE(szLanguage), hInstall))
  782. {
  783. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA8 Failure: Cannot retrieve MuiLCID property."));
  784. if (SUCCEEDED(hr))
  785. {
  786. LogCustomActionInfo(hInstall, tcMessage);
  787. }
  788. iRet = ERROR_INSTALL_FAILURE;
  789. goto Exit;
  790. }
  791. // if immediate mode (not scheduled/commit/rollback), schedule the custom actions
  792. if (!MsiGetMode(hInstall, MSIRUNMODE_SCHEDULED) &&
  793. !MsiGetMode(hInstall, MSIRUNMODE_ROLLBACK)&&
  794. !MsiGetMode(hInstall, MSIRUNMODE_COMMIT))
  795. {
  796. // get the current default UI language
  797. LANGID lgID = GetDotDefaultUILanguage(hInstall);
  798. hr = StringCchPrintf(szOrigLanguage, ARRAYSIZE(szOrigLanguage), TEXT("%04x"), lgID);
  799. if (FAILED(hr))
  800. {
  801. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA8 Failure: Cannot retrieve default UI language."));
  802. if (SUCCEEDED(hr))
  803. {
  804. LogCustomActionInfo(hInstall, tcMessage);
  805. }
  806. iRet = ERROR_INSTALL_FAILURE;
  807. goto Exit;
  808. }
  809. // schedule the appropriate custom actions and property setting custom actions
  810. // Rollback custom action goes first
  811. // Create a rollback custom action (in case install is stopped and rolls back)
  812. // Rollback custom action can't read tables, so we have to set a property
  813. if (ERROR_SUCCESS != MsiSetProperty(hInstall, TEXT("CA8R"), szOrigLanguage))
  814. {
  815. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA8 Failure: Failed to set rollback custom action property."));
  816. if (SUCCEEDED(hr))
  817. {
  818. LogCustomActionInfo(hInstall, tcMessage);
  819. }
  820. iRet = ERROR_INSTALL_FAILURE;
  821. goto Exit;
  822. }
  823. if (ERROR_SUCCESS != MsiDoAction(hInstall, TEXT("CA8R")))
  824. {
  825. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA8 Failure: Failed to schedule rollback custom action."));
  826. if (SUCCEEDED(hr))
  827. {
  828. LogCustomActionInfo(hInstall, tcMessage);
  829. }
  830. iRet = ERROR_INSTALL_FAILURE;
  831. goto Exit;
  832. }
  833. // Create a deferred custom action (gives us the right priviledges to create the user account)
  834. // Deferred custom actions can't read tables, so we have to set a property
  835. if (ERROR_SUCCESS != MsiSetProperty(hInstall, TEXT("CA8D"), szLanguage))
  836. {
  837. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA8 Failure: Failed to set deferred custom action property."));
  838. if (SUCCEEDED(hr))
  839. {
  840. LogCustomActionInfo(hInstall, tcMessage);
  841. }
  842. iRet = ERROR_INSTALL_FAILURE;
  843. goto Exit;
  844. }
  845. if (ERROR_SUCCESS != MsiDoAction(hInstall, TEXT("CA8D")))
  846. {
  847. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA8 Failure: Failed to schedule deferred custom action."));
  848. if (SUCCEEDED(hr))
  849. {
  850. LogCustomActionInfo(hInstall, tcMessage);
  851. }
  852. iRet = ERROR_INSTALL_FAILURE;
  853. goto Exit;
  854. }
  855. }
  856. else
  857. {
  858. if (FALSE == SetUILanguage(szLanguage, FALSE, TRUE, hInstall))
  859. {
  860. // log an error
  861. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA8: Failed to set Default UI language."));
  862. if (SUCCEEDED(hr))
  863. {
  864. LogCustomActionInfo(hInstall, tcMessage);
  865. }
  866. iRet = ERROR_INSTALL_FAILURE;
  867. }
  868. else
  869. {
  870. iRet = ERROR_SUCCESS;
  871. }
  872. }
  873. Exit:
  874. return iRet;
  875. }
  876. ////////////////////////////////////////////////////////////////////////////////////
  877. //
  878. // SetCurrentUserLanguage
  879. //
  880. // This function does two things - depending on the MSI execution mode:
  881. //
  882. // 1. Immediate:
  883. // This function in immediate mode will schedule the deferred and the rollback
  884. // custom action C9D/C9R. (C9D and C9R are the expected CA identifiers used
  885. // in the MUI MSI package)
  886. //
  887. // For rollback functions it will also capture the original LCID of the
  888. // current user and use that as the custom action for the rollback.
  889. //
  890. // 2. Deferred:
  891. // This function sets the UI language of the current users to that of the MUI
  892. // language that is being installed.
  893. //
  894. // Also, reboot is needed after this CA in deferred mode, but this is specified
  895. // in the template itself, so the CA itself will not prompt for reboot (it can't
  896. // anyways, since it is deferred).
  897. //
  898. ////////////////////////////////////////////////////////////////////////////////////
  899. UINT CA9(MSIHANDLE hInstall)
  900. {
  901. UINT iRet = ERROR_SUCCESS;
  902. TCHAR szLanguage[5] = {0};
  903. TCHAR szOrigLanguage[5] = {0};
  904. TCHAR tcMessage[BUFFER_SIZE] = {0};
  905. HRESULT hr = S_OK;
  906. size_t cch = 0;
  907. if (NULL == hInstall)
  908. {
  909. return ERROR_INSTALL_FAILURE;
  910. }
  911. // get the MUI LCID that we want to set the current UI Language to.
  912. if (!GetLCID(szLanguage, ARRAYSIZE(szLanguage), hInstall))
  913. {
  914. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA9 Failure: Cannot retrieve MuiLCID property."));
  915. if (SUCCEEDED(hr))
  916. {
  917. LogCustomActionInfo(hInstall, tcMessage);
  918. }
  919. iRet = ERROR_INSTALL_FAILURE;
  920. goto Exit;
  921. }
  922. // if immediate mode (not scheduled/commit/rollback), schedule the custom actions
  923. if (!MsiGetMode(hInstall, MSIRUNMODE_SCHEDULED) &&
  924. !MsiGetMode(hInstall, MSIRUNMODE_ROLLBACK)&&
  925. !MsiGetMode(hInstall, MSIRUNMODE_COMMIT))
  926. {
  927. // get the current user UI language
  928. LANGID lgID = GetUserDefaultUILanguage();
  929. hr = StringCchPrintf(szOrigLanguage, ARRAYSIZE(szOrigLanguage), TEXT("%04x"), lgID);
  930. if (FAILED(hr))
  931. {
  932. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA9 Failure: Cannot retrieve current UI language."));
  933. if (SUCCEEDED(hr))
  934. {
  935. LogCustomActionInfo(hInstall, tcMessage);
  936. }
  937. iRet = ERROR_INSTALL_FAILURE;
  938. goto Exit;
  939. }
  940. // schedule the appropriate custom actions and property setting custom actions
  941. // Rollback custom action goes first
  942. // Create a rollback custom action (in case install is stopped and rolls back)
  943. // Rollback custom action can't read tables, so we have to set a property
  944. if (ERROR_SUCCESS != MsiSetProperty(hInstall, TEXT("CA9R"), szOrigLanguage))
  945. {
  946. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA9 Failure: Failed to set rollback custom action property."));
  947. if (SUCCEEDED(hr))
  948. {
  949. LogCustomActionInfo(hInstall, tcMessage);
  950. }
  951. iRet = ERROR_INSTALL_FAILURE;
  952. goto Exit;
  953. }
  954. if (ERROR_SUCCESS != MsiDoAction(hInstall, TEXT("CA9R")))
  955. {
  956. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA9 Failure: Failed to schedule rollback custom action."));
  957. if (SUCCEEDED(hr))
  958. {
  959. LogCustomActionInfo(hInstall, tcMessage);
  960. }
  961. iRet = ERROR_INSTALL_FAILURE;
  962. goto Exit;
  963. }
  964. // Create a deferred custom action (gives us the right priviledges to create the user account)
  965. // Deferred custom actions can't read tables, so we have to set a property
  966. if (ERROR_SUCCESS != MsiSetProperty(hInstall, TEXT("CA9D"), szLanguage))
  967. {
  968. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA9 Failure: Failed to set deferred custom action property."));
  969. if (SUCCEEDED(hr))
  970. {
  971. LogCustomActionInfo(hInstall, tcMessage);
  972. }
  973. iRet = ERROR_INSTALL_FAILURE;
  974. goto Exit;
  975. }
  976. if (ERROR_SUCCESS != MsiDoAction(hInstall, TEXT("CA9D")))
  977. {
  978. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA9 Failure: Failed to schedule deferred custom action."));
  979. if (SUCCEEDED(hr))
  980. {
  981. LogCustomActionInfo(hInstall, tcMessage);
  982. }
  983. iRet = ERROR_INSTALL_FAILURE;
  984. goto Exit;
  985. }
  986. }
  987. else
  988. {
  989. if (FALSE == SetUILanguage(szLanguage, TRUE, FALSE, hInstall))
  990. {
  991. // log an error
  992. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA9: Failed to set current UI language."));
  993. if (SUCCEEDED(hr))
  994. {
  995. LogCustomActionInfo(hInstall, tcMessage);
  996. }
  997. iRet = ERROR_INSTALL_FAILURE;
  998. }
  999. else
  1000. {
  1001. iRet = ERROR_SUCCESS;
  1002. }
  1003. }
  1004. Exit:
  1005. return iRet;
  1006. }
  1007. ////////////////////////////////////////////////////////////////////////////////////
  1008. //
  1009. // InstallLanguageGroup
  1010. //
  1011. // This function is called by our setup to install language group files if they are needed.
  1012. // Note, a reboot is required after installation of the langpack, however, this is
  1013. // flagged using a property by the SetLangPackRequirement function instead.
  1014. //
  1015. ////////////////////////////////////////////////////////////////////////////////////
  1016. UINT CA2(MSIHANDLE hInstall)
  1017. {
  1018. LGRPID lgrpid[LANGGROUPNUMBER] = {0};
  1019. UINT iRet = ERROR_SUCCESS;
  1020. UINT uiLGrpNums = 0;
  1021. UINT i;
  1022. TCHAR tcMessage[BUFFER_SIZE] = {0};
  1023. TCHAR szLanguage[5] = {0};
  1024. TCHAR szMuiInfPath[MAX_PATH+1] = {0};
  1025. TCHAR * szUILevel = NULL;
  1026. PMSIHANDLE hRec = MsiCreateRecord(3);
  1027. PMSIHANDLE hProgressRec = MsiCreateRecord(3);
  1028. UINT iTemp = ERROR_SUCCESS;
  1029. BOOL bSilent = FALSE;
  1030. HRESULT hr = S_OK;
  1031. size_t cch = 0;
  1032. INT iResult = IDOK;
  1033. if (NULL == hInstall)
  1034. {
  1035. return ERROR_INSTALL_FAILURE;
  1036. }
  1037. if ((NULL == hRec) || (NULL == hProgressRec))
  1038. {
  1039. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA2 Failure: cannot create a MSI record."));
  1040. if (SUCCEEDED(hr))
  1041. {
  1042. LogCustomActionInfo(hInstall, tcMessage);
  1043. }
  1044. return ERROR_INSTALL_FAILURE;
  1045. }
  1046. if (!GetLCID(szLanguage, ARRAYSIZE(szLanguage), hInstall))
  1047. {
  1048. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA2 Failure: Cannot retrieve MuiLCID property."));
  1049. if (SUCCEEDED(hr))
  1050. {
  1051. LogCustomActionInfo(hInstall, tcMessage);
  1052. }
  1053. iRet = ERROR_INSTALL_FAILURE;
  1054. goto Exit;
  1055. }
  1056. // Tell the installer to check the installation state and execute
  1057. // the code needed during the rollback, acquisition, or
  1058. // execution phases of the installation.
  1059. if (MsiGetMode(hInstall,MSIRUNMODE_ROLLBACK))
  1060. {
  1061. // Installer is rolling back the installation. Additional code
  1062. // could be inserted here to enable the custom action to do
  1063. // something during an installation rollback.
  1064. iRet = ERROR_SUCCESS;
  1065. goto Exit;
  1066. }
  1067. if (!MsiGetMode(hInstall,MSIRUNMODE_SCHEDULED))
  1068. {
  1069. if (!GetMUIInfPath(szMuiInfPath, MAX_PATH+1, hInstall))
  1070. {
  1071. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA2 Failure: Cannot find installation temp file."));
  1072. if (SUCCEEDED(hr))
  1073. {
  1074. LogCustomActionInfo(hInstall, tcMessage);
  1075. }
  1076. return ERROR_INSTALL_FAILURE;
  1077. }
  1078. if (!ReturnAllRequiredLangGroups(szLanguage, ARRAYSIZE(szLanguage), szMuiInfPath, ARRAYSIZE(szMuiInfPath), lgrpid, &uiLGrpNums, hInstall))
  1079. {
  1080. // log an error
  1081. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA2: ReturnAllRequiredLangGroups function failed."));
  1082. if (SUCCEEDED(hr))
  1083. {
  1084. LogCustomActionInfo(hInstall, tcMessage);
  1085. }
  1086. iRet = ERROR_INSTALL_FAILURE;
  1087. goto Exit;
  1088. }
  1089. // uiLGrpNums should be less than 32, the size of the passed-in buffer, if it is returned as larger,
  1090. // we truncate that number down to 32.
  1091. if (uiLGrpNums > LANGGROUPNUMBER)
  1092. {
  1093. uiLGrpNums = LANGGROUPNUMBER;
  1094. }
  1095. // Installer is generating the installation script of the custom
  1096. // action. Tell the installer to increase the value of the final total
  1097. // length of the progress bar by the total number of ticks in the
  1098. // custom action.
  1099. MsiRecordSetInteger(hRec,1,3);
  1100. MsiRecordSetInteger(hRec,2,LANGPACK_TICK_INC * uiLGrpNums);
  1101. MsiRecordSetInteger(hRec,3,0);
  1102. MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRec);
  1103. iRet = ERROR_SUCCESS;
  1104. goto Exit;
  1105. }
  1106. else
  1107. {
  1108. // Installer is executing the installation script. Set up a
  1109. // record specifying appropriate templates and text for messages
  1110. // that will inform the user about what the custom action is
  1111. // doing. Tell the installer to use this template and text in
  1112. // progress messages.
  1113. // Get the CustomActionData Property that tells us whether we are going to pop up dialog for windows source or not
  1114. // the first character is the UILevel in CustomActionData (MuiLCID UILevel)
  1115. DWORD dwCount = 7; // e.g. "0404 1\0" adds up to 7
  1116. TCHAR szCustomActionData[7] = {0};
  1117. if (ERROR_SUCCESS != MsiGetProperty(hInstall, TEXT("CustomActionData"), szCustomActionData, &dwCount))
  1118. {
  1119. // log an error
  1120. if (SUCCEEDED(hr))
  1121. {
  1122. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA2: Failed to get CustomActionData property - assuming we are calling intl.cpl in silent mode."));
  1123. LogCustomActionInfo(hInstall, tcMessage);
  1124. }
  1125. }
  1126. else
  1127. {
  1128. // we can't validate much here, if buffer overruns, MsiGetProperty will return failure.
  1129. hr = StringCchLength(szCustomActionData, ARRAYSIZE(szCustomActionData), &cch);
  1130. if (FAILED(hr) || (cch >= 7))
  1131. {
  1132. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA2 Failure: CustomActionData property value is invalid."));
  1133. if (SUCCEEDED(hr))
  1134. {
  1135. LogCustomActionInfo(hInstall, tcMessage);
  1136. }
  1137. return ERROR_INSTALL_FAILURE;
  1138. }
  1139. szCustomActionData[4] = UNICODE_NULL; // end of MuiLCID portion
  1140. szCustomActionData[6] = UNICODE_NULL; // end of UILevel portion
  1141. szUILevel = szCustomActionData + 5;
  1142. #ifdef MUI_DEBUG
  1143. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA2: UI Level is set to %s."), szUILevel);
  1144. if (SUCCEEDED(hr))
  1145. {
  1146. LogCustomActionInfo(hInstall, tcMessage);
  1147. }
  1148. #endif
  1149. if (INSTALLUILEVEL_NONE == (INSTALLUILEVEL) _tcstol(szUILevel, NULL, 10))
  1150. bSilent = TRUE;
  1151. else
  1152. bSilent = FALSE;
  1153. }
  1154. if (!GetMUIInfPath(szMuiInfPath, MAX_PATH+1, hInstall))
  1155. {
  1156. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA2 Failure: Cannot find installation temp file."));
  1157. if (SUCCEEDED(hr))
  1158. {
  1159. LogCustomActionInfo(hInstall, tcMessage);
  1160. }
  1161. return ERROR_INSTALL_FAILURE;
  1162. }
  1163. if (!ReturnAllRequiredLangGroups(szLanguage, ARRAYSIZE(szLanguage), szMuiInfPath, ARRAYSIZE(szMuiInfPath), lgrpid, &uiLGrpNums, hInstall))
  1164. {
  1165. // log an error
  1166. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA2: ReturnAllRequiredLangGroups function failed."));
  1167. if (SUCCEEDED(hr))
  1168. {
  1169. LogCustomActionInfo(hInstall, tcMessage);
  1170. }
  1171. iRet = ERROR_INSTALL_FAILURE;
  1172. goto Exit;
  1173. }
  1174. // uiLGrpNums should be less than 32, the size of the passed-in buffer, if it is returned as larger,
  1175. // we truncate that number down to 32.
  1176. if (uiLGrpNums > LANGGROUPNUMBER)
  1177. {
  1178. uiLGrpNums = LANGGROUPNUMBER;
  1179. }
  1180. MsiRecordSetString(hRec,1,TEXT("Install LanguageGroup."));
  1181. MsiRecordSetString(hRec,2,TEXT("Installing language groups files ..."));
  1182. MsiRecordSetString(hRec,3,TEXT("Installing language group [1]."));
  1183. MsiProcessMessage(hInstall, INSTALLMESSAGE_ACTIONSTART, hRec);
  1184. // Tell the installer to use explicit progress messages.
  1185. MsiRecordSetInteger(hRec,1,1);
  1186. MsiRecordSetInteger(hRec,2,1);
  1187. MsiRecordSetInteger(hRec,3,0);
  1188. iResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRec);
  1189. if (iResult != IDOK)
  1190. {
  1191. if (iResult == IDCANCEL)
  1192. {
  1193. iRet = ERROR_INSTALL_USEREXIT;
  1194. goto Exit;
  1195. }
  1196. }
  1197. // do the actual work for the custom action
  1198. // Enumerate through all the lang groups required, check if language group already installed, if so, just return success
  1199. // otherwise install it
  1200. iRet = ERROR_SUCCESS;
  1201. for (i = 0; i < uiLGrpNums; i++)
  1202. {
  1203. // display on the UI that we are installing language group lgrpid[i]
  1204. MsiRecordSetInteger(hRec,1,lgrpid[i]);
  1205. iResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_ACTIONDATA, hRec);
  1206. if (iResult != IDOK)
  1207. {
  1208. if (iResult == IDCANCEL)
  1209. {
  1210. iRet = ERROR_INSTALL_USEREXIT;
  1211. break;
  1212. }
  1213. }
  1214. if (!IsValidLanguageGroup(lgrpid[i], LGRPID_INSTALLED))
  1215. {
  1216. TCHAR pCommands[MAX_PATH] = {0};
  1217. hr = StringCchPrintf(pCommands, ARRAYSIZE(pCommands), TEXT("LanguageGroup = %d"), lgrpid[i]);
  1218. if (FAILED(hr))
  1219. {
  1220. // log an error
  1221. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA2: Failed to install language group %d."), lgrpid[i]);
  1222. if (SUCCEEDED(hr))
  1223. {
  1224. LogCustomActionInfo(hInstall, tcMessage);
  1225. }
  1226. iRet = ERROR_INSTALL_FAILURE;
  1227. break;
  1228. }
  1229. DEBUGMSGBOX(NULL, pCommands, NULL, MB_OK);
  1230. if (!RunRegionalOptionsApplet(pCommands, bSilent, hInstall))
  1231. {
  1232. // log an error
  1233. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA2: Failed to install language group %d."), lgrpid[i]);
  1234. if (SUCCEEDED(hr))
  1235. {
  1236. LogCustomActionInfo(hInstall, tcMessage);
  1237. }
  1238. iRet = ERROR_INSTALL_FAILURE;
  1239. break;
  1240. }
  1241. }
  1242. if (!IsValidLanguageGroup(lgrpid[i], LGRPID_INSTALLED))
  1243. {
  1244. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA2: Failed to install language group %d."), lgrpid[i]);
  1245. if (SUCCEEDED(hr))
  1246. {
  1247. LogCustomActionInfo(hInstall, tcMessage);
  1248. }
  1249. iRet = ERROR_INSTALL_FAILURE;
  1250. break;
  1251. }
  1252. // we installed the current language group, update progress bar and move onto the next one
  1253. MsiRecordSetInteger(hProgressRec,1,2);
  1254. MsiRecordSetInteger(hProgressRec,2,LANGPACK_TICK_INC);
  1255. MsiRecordSetInteger(hProgressRec,3,0);
  1256. iResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hProgressRec);
  1257. if (iResult != IDOK)
  1258. {
  1259. if (iResult == IDCANCEL)
  1260. {
  1261. iRet = ERROR_INSTALL_USEREXIT;
  1262. break;
  1263. }
  1264. }
  1265. }
  1266. }
  1267. Exit:
  1268. return iRet;
  1269. }
  1270. ////////////////////////////////////////////////////////////////////////////////////
  1271. //
  1272. // DeleteMUIInfFile
  1273. //
  1274. // This custom action will delete the extracted mui.tmp file that we are using
  1275. // during the installation from the temporary directory.
  1276. //
  1277. ////////////////////////////////////////////////////////////////////////////////////
  1278. UINT CA15(MSIHANDLE hInstall)
  1279. {
  1280. TCHAR tcMessage[BUFFER_SIZE] = {0};
  1281. TCHAR tcMUIINFPath[MAX_PATH+1] = {0};
  1282. UINT uiRet = ERROR_SUCCESS;
  1283. HRESULT hr = S_OK;
  1284. DWORD cbPathSize = MAX_PATH+1;
  1285. // form a path to the temporary directory that we want %windir%\mui.tmp
  1286. cbPathSize = GetSystemWindowsDirectory(tcMUIINFPath, MAX_PATH+1);
  1287. if ((0 == cbPathSize) || (MAX_PATH+1 < cbPathSize))
  1288. {
  1289. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA15 Failure: failed to get windows directory path."));
  1290. if (SUCCEEDED(hr))
  1291. {
  1292. LogCustomActionInfo(hInstall, tcMessage);
  1293. }
  1294. return ERROR_INSTALL_FAILURE;
  1295. }
  1296. if (!MUICchPathAppend(tcMUIINFPath, ARRAYSIZE(tcMUIINFPath), TEXT("mui.tmp"), 8, hInstall))
  1297. {
  1298. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA15 Failure: failed to get installation temp file path."));
  1299. if (SUCCEEDED(hr))
  1300. {
  1301. LogCustomActionInfo(hInstall, tcMessage);
  1302. }
  1303. return ERROR_INSTALL_FAILURE;
  1304. }
  1305. if (FileExists(tcMUIINFPath))
  1306. {
  1307. if (!DeleteFile(tcMUIINFPath))
  1308. {
  1309. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA15 Failure: failed to delete installation temp file."));
  1310. if (SUCCEEDED(hr))
  1311. {
  1312. LogCustomActionInfo(hInstall, tcMessage);
  1313. }
  1314. return ERROR_INSTALL_FAILURE;
  1315. }
  1316. }
  1317. else
  1318. {
  1319. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA15 Failure: installation temp file does not exist."));
  1320. if (SUCCEEDED(hr))
  1321. {
  1322. LogCustomActionInfo(hInstall, tcMessage);
  1323. }
  1324. return ERROR_INSTALL_FAILURE;
  1325. }
  1326. return ERROR_SUCCESS;
  1327. }
  1328. ////////////////////////////////////////////////////////////////////////////////////
  1329. //
  1330. // ExtractMUIInfFile
  1331. //
  1332. // This custom action will extract mui.inf file embedded in the binary table
  1333. // of the current installation database. It will place the extracted file
  1334. // in the %winddir% directory as mui.tmp. This file will be referenced
  1335. // during the installation. The tmp file will be cleaned up later in the
  1336. // installation.
  1337. //
  1338. ////////////////////////////////////////////////////////////////////////////////////
  1339. UINT CA14(MSIHANDLE hInstall)
  1340. {
  1341. PMSIHANDLE hDb = NULL;
  1342. PMSIHANDLE hView = NULL;
  1343. PMSIHANDLE hRec = NULL;
  1344. TCHAR tcMessage[BUFFER_SIZE] = {0};
  1345. TCHAR tcQuery[BUFFER_SIZE] = SELECTMUIINFBINSTREAM;
  1346. TCHAR tcMUIINFPath[MAX_PATH+1] = {0};
  1347. char cBuffer[BUFFER_SIZE] = {0};
  1348. DWORD cbBuf = BUFFER_SIZE;
  1349. DWORD cbPathSize = 0;
  1350. DWORD dwNumWritten = 0;
  1351. UINT uiRet = ERROR_SUCCESS;
  1352. HRESULT hr = S_OK;
  1353. HANDLE hFile = NULL;
  1354. UINT uiResult = ERROR_SUCCESS;
  1355. // form a path to the temporary directory that we want %windir%\mui.tmp
  1356. cbPathSize = GetSystemWindowsDirectory(tcMUIINFPath, MAX_PATH+1);
  1357. if ((0 == cbPathSize) || (MAX_PATH+1 < cbPathSize))
  1358. {
  1359. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA14: failed to get windows directory path."));
  1360. if (SUCCEEDED(hr))
  1361. {
  1362. LogCustomActionInfo(hInstall, tcMessage);
  1363. }
  1364. uiRet = ERROR_INSTALL_FAILURE;
  1365. goto Exit;
  1366. }
  1367. if (!MUICchPathAppend(tcMUIINFPath, ARRAYSIZE(tcMUIINFPath), TEXT("mui.tmp"), 8, hInstall))
  1368. {
  1369. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA14: failed to get installation temp file path."));
  1370. if (SUCCEEDED(hr))
  1371. {
  1372. LogCustomActionInfo(hInstall, tcMessage);
  1373. }
  1374. uiRet = ERROR_INSTALL_FAILURE;
  1375. goto Exit;
  1376. }
  1377. hDb = MsiGetActiveDatabase(hInstall);
  1378. if (NULL == hDb)
  1379. {
  1380. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA14: failed to get current installation database handle."));
  1381. if (SUCCEEDED(hr))
  1382. {
  1383. LogCustomActionInfo(hInstall, tcMessage);
  1384. }
  1385. uiRet = ERROR_INSTALL_FAILURE;
  1386. goto Exit;
  1387. }
  1388. uiResult = MsiDatabaseOpenView(hDb, tcQuery, &hView);
  1389. if (ERROR_SUCCESS != uiResult)
  1390. {
  1391. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA14: failed to open current installation database."));
  1392. if (SUCCEEDED(hr))
  1393. {
  1394. LogCustomActionInfo(hInstall, tcMessage);
  1395. }
  1396. uiRet = ERROR_INSTALL_FAILURE;
  1397. goto Exit;
  1398. }
  1399. uiResult = MsiViewExecute(hView, 0);
  1400. if (ERROR_SUCCESS != uiResult)
  1401. {
  1402. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA14: query on current installation database failed."));
  1403. if (SUCCEEDED(hr))
  1404. {
  1405. LogCustomActionInfo(hInstall, tcMessage);
  1406. }
  1407. uiRet = ERROR_INSTALL_FAILURE;
  1408. goto Exit;
  1409. }
  1410. uiResult = MsiViewFetch(hView, &hRec);
  1411. if (ERROR_SUCCESS != uiResult)
  1412. {
  1413. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA14: database operation failed."));
  1414. if (SUCCEEDED(hr))
  1415. {
  1416. LogCustomActionInfo(hInstall, tcMessage);
  1417. }
  1418. uiRet = ERROR_INSTALL_FAILURE;
  1419. goto Exit;
  1420. }
  1421. // create our temp file
  1422. hFile = CreateFile(tcMUIINFPath,
  1423. GENERIC_WRITE,
  1424. 0L,
  1425. NULL,
  1426. CREATE_ALWAYS,
  1427. FILE_ATTRIBUTE_NORMAL,
  1428. NULL);
  1429. if (INVALID_HANDLE_VALUE == hFile)
  1430. {
  1431. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA14: failed to create installation temporary file."));
  1432. if (SUCCEEDED(hr))
  1433. {
  1434. LogCustomActionInfo(hInstall, tcMessage);
  1435. }
  1436. uiRet = ERROR_INSTALL_FAILURE;
  1437. goto Exit;
  1438. }
  1439. do
  1440. {
  1441. uiResult = MsiRecordReadStream(hRec, 1, cBuffer, &cbBuf);
  1442. if (ERROR_SUCCESS != uiResult)
  1443. {
  1444. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA14: failed to read data from installation database."));
  1445. if (SUCCEEDED(hr))
  1446. {
  1447. LogCustomActionInfo(hInstall, tcMessage);
  1448. }
  1449. uiRet = ERROR_INSTALL_FAILURE;
  1450. goto Exit;
  1451. }
  1452. // here, we need to write the read buffer out to a file
  1453. WriteFile(hFile,
  1454. cBuffer,
  1455. cbBuf,
  1456. &dwNumWritten,
  1457. NULL);
  1458. if (dwNumWritten != cbBuf)
  1459. {
  1460. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA14: failed to write to installation temporary file."));
  1461. if (SUCCEEDED(hr))
  1462. {
  1463. LogCustomActionInfo(hInstall, tcMessage);
  1464. }
  1465. uiRet = ERROR_INSTALL_FAILURE;
  1466. goto Exit;
  1467. }
  1468. } while (cbBuf == BUFFER_SIZE);
  1469. Exit:
  1470. if (NULL != hFile)
  1471. {
  1472. CloseHandle(hFile);
  1473. }
  1474. // delete the actual file if there is an error
  1475. if (uiRet == ERROR_INSTALL_FAILURE)
  1476. {
  1477. CA15(hInstall); // DeleteMUIInfFile()
  1478. }
  1479. return uiRet;
  1480. }
  1481. ////////////////////////////////////////////////////////////////////////////////////
  1482. //
  1483. // RestoreSystemSettings
  1484. //
  1485. // This function checks the default and current user languages, and determines whether
  1486. // system needs to reboot when uninstallation happens (immediate). It also clears
  1487. // the shell registry cache (commit action)
  1488. //
  1489. ////////////////////////////////////////////////////////////////////////////////////
  1490. UINT CA16(MSIHANDLE hInstall)
  1491. {
  1492. UINT iRet = ERROR_SUCCESS;
  1493. UINT iRetProp = ERROR_SUCCESS;
  1494. TCHAR szCustomActionData[5];
  1495. TCHAR tcMessage[BUFFER_SIZE];
  1496. TCHAR szDefLang[5];
  1497. DWORD dwCount = 5;
  1498. LANGID langID;
  1499. BOOL bRestoreDefault = FALSE;
  1500. BOOL bRestoreCurrent = FALSE;
  1501. LANGID sysLangID;
  1502. UINT iTemp = ERROR_SUCCESS;
  1503. HRESULT hr = S_OK;
  1504. // get MuiLCID
  1505. if (!GetLCID(szCustomActionData, ARRAYSIZE(szCustomActionData), hInstall))
  1506. {
  1507. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage),TEXT("CA16: Failed to retrieve MuiLCID property."));
  1508. if (SUCCEEDED(hr))
  1509. {
  1510. LogCustomActionInfo(hInstall, tcMessage);
  1511. }
  1512. iRet = ERROR_INSTALL_FAILURE;
  1513. goto Exit;
  1514. }
  1515. szCustomActionData[4] = NULL;
  1516. langID = (LANGID)_tcstol(szCustomActionData, NULL, 16);
  1517. #ifdef MUI_DEBUG
  1518. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage),TEXT("CA16: LCID is %s."), szCustomActionData);
  1519. if (SUCCEEDED(hr))
  1520. {
  1521. LogCustomActionInfo(hInstall, tcMessage);
  1522. }
  1523. #endif
  1524. // check what the current ui and system ui language is, if they are the same as the current mui langauge to be uninstalled
  1525. // then we will do some additional things during uninstallation
  1526. if (GetDotDefaultUILanguage(hInstall) == langID)
  1527. {
  1528. #ifdef MUI_DEBUG
  1529. 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)."));
  1530. if (SUCCEEDED(hr))
  1531. {
  1532. LogCustomActionInfo(hInstall, tcMessage);
  1533. }
  1534. #endif
  1535. bRestoreDefault = TRUE;
  1536. }
  1537. if (GetUserDefaultUILanguage() == langID)
  1538. {
  1539. #ifdef MUI_DEBUG
  1540. 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)."));
  1541. if (SUCCEEDED(hr))
  1542. {
  1543. LogCustomActionInfo(hInstall, tcMessage);
  1544. }
  1545. #endif
  1546. bRestoreCurrent = TRUE;
  1547. }
  1548. if (bRestoreDefault || bRestoreCurrent)
  1549. {
  1550. if (MsiGetMode(hInstall, MSIRUNMODE_COMMIT))
  1551. {
  1552. // we will attempt to delete the shell reg key here, but if we fail, we won't fail the installtion, just
  1553. // log an error
  1554. if (ERROR_SUCCESS != SHDeleteKey(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\ShellNoRoam\\MUICache")))
  1555. {
  1556. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA16: Failed to delete registry cache."));
  1557. if (SUCCEEDED(hr))
  1558. {
  1559. LogCustomActionInfo(hInstall, tcMessage);
  1560. }
  1561. }
  1562. }
  1563. else if (!MsiGetMode(hInstall, MSIRUNMODE_ROLLBACK) &&
  1564. !MsiGetMode(hInstall, MSIRUNMODE_SCHEDULED))
  1565. {
  1566. // indicate to the installer that a reboot is required at the end since we changed the default/current UI.
  1567. // again, if this fails, we just log error, and not fail the installation
  1568. iTemp = MsiSetMode(hInstall, MSIRUNMODE_REBOOTATEND, TRUE);
  1569. if (ERROR_SUCCESS != iTemp)
  1570. {
  1571. // log an error
  1572. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("CA16: Failed to schedule reboot operation. MsiSetMode returned %d as the error."), iTemp);
  1573. if (SUCCEEDED(hr))
  1574. {
  1575. LogCustomActionInfo(hInstall, tcMessage);
  1576. }
  1577. }
  1578. }
  1579. }
  1580. Exit:
  1581. return iRet;
  1582. }
  1583. ////////////////////////////////////////////////////////////////////////////////////
  1584. // Internal functions, not exported are listed below
  1585. ////////////////////////////////////////////////////////////////////////////////////
  1586. ////////////////////////////////////////////////////////////////////////////////////
  1587. //
  1588. // SetUILanguage
  1589. //
  1590. // This is the internal worker function that calls intl.cpl to set the current
  1591. // and/or default user MUI UI language.
  1592. //
  1593. ////////////////////////////////////////////////////////////////////////////////////
  1594. BOOL SetUILanguage(TCHAR *szLanguage, BOOL bCurrent, BOOL bDefault, MSIHANDLE hInstall)
  1595. {
  1596. BOOL bRet = TRUE;
  1597. DWORD dwCount;
  1598. TCHAR szCommands[BUFFER_SIZE] = {0};
  1599. TCHAR tcMessage[2*BUFFER_SIZE] = {0};
  1600. TCHAR szBuffer[BUFFER_SIZE] = {0};
  1601. BOOL success;
  1602. HRESULT hr = S_OK;
  1603. if (NULL == szLanguage)
  1604. {
  1605. bRet = FALSE;
  1606. goto Exit;
  1607. }
  1608. // return TRUE if there is nothing to set
  1609. if (!bCurrent && !bDefault)
  1610. {
  1611. bRet = TRUE;
  1612. goto Exit;
  1613. }
  1614. szCommands[0] = TEXT('\0');
  1615. if (bCurrent)
  1616. {
  1617. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("MUILanguage=\"%s\"\n"), szLanguage);
  1618. if (FAILED(hr))
  1619. {
  1620. bRet = FALSE;
  1621. goto Exit;
  1622. }
  1623. hr = StringCchCat(szCommands, ARRAYSIZE(szCommands), szBuffer);
  1624. if (FAILED(hr))
  1625. {
  1626. bRet = FALSE;
  1627. goto Exit;
  1628. }
  1629. }
  1630. if (bDefault)
  1631. {
  1632. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("MUILanguage_DefaultUser=\"%s\""), szLanguage);
  1633. if (FAILED(hr))
  1634. {
  1635. bRet = FALSE;
  1636. goto Exit;
  1637. }
  1638. hr = StringCchCat(szCommands, ARRAYSIZE(szCommands), szBuffer);
  1639. if (FAILED(hr))
  1640. {
  1641. bRet = FALSE;
  1642. goto Exit;
  1643. }
  1644. }
  1645. #ifdef MUI_DEBUG
  1646. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("SetUILanguage: Command passed to intl.cpl is: %s"), szCommands);
  1647. if (SUCCEEDED(hr))
  1648. {
  1649. LogCustomActionInfo(hInstall, tcMessage);
  1650. }
  1651. #endif
  1652. success = RunRegionalOptionsApplet(szCommands, FALSE, hInstall);
  1653. if (success)
  1654. {
  1655. bRet = TRUE;
  1656. #ifdef MUI_DEBUG
  1657. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("SetUILanguage: Successfully set default and/or current user language."));
  1658. if (SUCCEEDED(hr))
  1659. {
  1660. LogCustomActionInfo(hInstall, tcMessage);
  1661. }
  1662. #endif
  1663. }
  1664. else
  1665. {
  1666. bRet = FALSE;
  1667. // log an error
  1668. 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);
  1669. if (SUCCEEDED(hr))
  1670. {
  1671. LogCustomActionInfo(hInstall, tcMessage);
  1672. }
  1673. }
  1674. Exit:
  1675. return bRet;
  1676. }
  1677. ////////////////////////////////////////////////////////////////////////////
  1678. //
  1679. // NotifyKernel
  1680. //
  1681. // Call the kernel to notify it that a new language is being added or
  1682. // removed
  1683. //
  1684. ////////////////////////////////////////////////////////////////////////////
  1685. void NotifyKernel(LPTSTR LangList, ULONG Flags, MSIHANDLE hInstall )
  1686. {
  1687. HANDLE Handle;
  1688. WMILANGUAGECHANGE LanguageChange;
  1689. ULONG ReturnSize;
  1690. BOOL IoctlSuccess;
  1691. ULONG Status;
  1692. TCHAR tcMessage[BUFFER_SIZE] = {0};
  1693. HRESULT hr = S_OK;
  1694. if ((LangList != NULL) &&
  1695. (*LangList != 0))
  1696. {
  1697. Handle = CreateFile(WMIAdminDeviceName,
  1698. GENERIC_READ | GENERIC_WRITE,
  1699. 0,
  1700. NULL,
  1701. OPEN_EXISTING,
  1702. FILE_ATTRIBUTE_NORMAL,
  1703. NULL);
  1704. if (Handle != INVALID_HANDLE_VALUE)
  1705. {
  1706. memset(&LanguageChange, 0, sizeof(LanguageChange));
  1707. hr = StringCchCopy(LanguageChange.Language, MAX_LANGUAGE_SIZE, LangList); // dest buffer size taken from wmiumkm.h
  1708. if (FAILED(hr))
  1709. {
  1710. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("NotifyKernel Failure: Kernel language notification failed."));
  1711. if (SUCCEEDED(hr))
  1712. {
  1713. LogCustomActionInfo(hInstall, tcMessage);
  1714. }
  1715. goto ExitClose;
  1716. }
  1717. LanguageChange.Flags = Flags;
  1718. IoctlSuccess = DeviceIoControl(Handle,
  1719. IOCTL_WMI_NOTIFY_LANGUAGE_CHANGE,
  1720. &LanguageChange,
  1721. sizeof(LanguageChange),
  1722. NULL,
  1723. 0,
  1724. &ReturnSize,
  1725. NULL);
  1726. if (!IoctlSuccess)
  1727. {
  1728. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("NotifyKernel: Language change notification to %ws failed, the error is %d."), LangList, GetLastError());
  1729. if (SUCCEEDED(hr))
  1730. {
  1731. LogCustomActionInfo(hInstall, tcMessage);
  1732. }
  1733. }
  1734. ExitClose:
  1735. CloseHandle(Handle);
  1736. }
  1737. }
  1738. }
  1739. ////////////////////////////////////////////////////////////////////////////////////
  1740. //
  1741. // MofCompileLanguage
  1742. //
  1743. ////////////////////////////////////////////////////////////////////////////////////
  1744. BOOL MofCompileLanguage(LPTSTR Languages, MSIHANDLE hInstall)
  1745. {
  1746. pfnMUI_InstallMFLFiles pfnMUIInstall = NULL;
  1747. TCHAR buffer[5] = {0};
  1748. LPTSTR Language = Languages;
  1749. TCHAR tcMessage[2*BUFFER_SIZE] = {0};
  1750. HMODULE hWbemUpgradeDll = NULL;
  1751. TCHAR szDllPath[MAX_PATH+1] = {0};
  1752. HRESULT hr = S_OK;
  1753. size_t cch = 0;
  1754. BOOL bRet = TRUE;
  1755. //
  1756. // Load the WBEM upgrade DLL from system wbem folder
  1757. //
  1758. if (GetSystemDirectory(szDllPath, ARRAYSIZE(szDllPath)))
  1759. {
  1760. hr = StringCchLength(szDllPath, ARRAYSIZE(szDllPath), &cch);
  1761. if (SUCCEEDED(hr))
  1762. {
  1763. if (!MUICchPathAppend(szDllPath, ARRAYSIZE(szDllPath), TEXT("wbem\\wbemupgd.dll"), 18, hInstall))
  1764. {
  1765. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MofCompileLanguage: Failed to form path to Mof Library."));
  1766. if (SUCCEEDED(hr))
  1767. {
  1768. LogCustomActionInfo(hInstall, tcMessage);
  1769. }
  1770. bRet = FALSE;
  1771. goto Exit2;
  1772. }
  1773. DEBUGMSGBOX(NULL, szDllPath, NULL, MB_OK);
  1774. hWbemUpgradeDll = LoadLibrary(szDllPath);
  1775. }
  1776. }
  1777. //
  1778. // Fall back to system default path if previous loading fails
  1779. //
  1780. if (!hWbemUpgradeDll)
  1781. {
  1782. hWbemUpgradeDll = LoadLibrary(TEXT("WBEMUPGD.DLL"));
  1783. if (!hWbemUpgradeDll)
  1784. {
  1785. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MofCompileLanguage: Failed to load WBEMUPGD.DLL."));
  1786. if (SUCCEEDED(hr))
  1787. {
  1788. LogCustomActionInfo(hInstall, tcMessage);
  1789. }
  1790. bRet = FALSE;
  1791. goto Exit2;
  1792. }
  1793. }
  1794. DEBUGMSGBOX(NULL, TEXT("Loaded WBEMUPGD.DLL"), NULL, MB_OK);
  1795. //
  1796. // Hook function pointer
  1797. //
  1798. pfnMUIInstall = (pfnMUI_InstallMFLFiles)GetProcAddress(hWbemUpgradeDll, "MUI_InstallMFLFiles");
  1799. if (pfnMUIInstall == NULL)
  1800. {
  1801. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MofCompileLanguage: Can't get address for function MUI_InstallMFLFiles."));
  1802. if (SUCCEEDED(hr))
  1803. {
  1804. LogCustomActionInfo(hInstall, tcMessage);
  1805. }
  1806. bRet = FALSE;
  1807. goto Exit;
  1808. }
  1809. DEBUGMSGBOX(NULL, TEXT("Loaded address for function MUI_InstallMFLFiles"), NULL, MB_OK);
  1810. hr = StringCchCopy(buffer, ARRAYSIZE(buffer), Language);
  1811. if (FAILED(hr))
  1812. {
  1813. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MofCompileLanguage: MUI_InstallMFLFiles failed."));
  1814. if (SUCCEEDED(hr))
  1815. {
  1816. LogCustomActionInfo(hInstall, tcMessage);
  1817. }
  1818. bRet = FALSE;
  1819. goto Exit;
  1820. }
  1821. if (!pfnMUIInstall(buffer))
  1822. {
  1823. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MofCompileLanguage: MUI_InstallMFLFiles failed - argument passed in is %s."), buffer);
  1824. if (SUCCEEDED(hr))
  1825. {
  1826. LogCustomActionInfo(hInstall, tcMessage);
  1827. }
  1828. }
  1829. Exit:
  1830. FreeLibrary(hWbemUpgradeDll);
  1831. Exit2:
  1832. return bRet;
  1833. }
  1834. ////////////////////////////////////////////////////////////////////////////////////
  1835. //
  1836. // RunRegionalOptionsApplet
  1837. //
  1838. // Run the Regional Option silent mode installation using the specified pCommands.
  1839. //
  1840. // This function will create the "[RegigionalSettings]" string, so there is no need
  1841. // to supply that in pCommands.
  1842. //
  1843. ////////////////////////////////////////////////////////////////////////////////////
  1844. BOOL RunRegionalOptionsApplet(LPTSTR pCommands, BOOL bSilent, MSIHANDLE hInstall)
  1845. {
  1846. HANDLE hFile;
  1847. TCHAR szFilePath[MAX_PATH+1] = {0};
  1848. TCHAR szSysDir[MAX_PATH+1] = {0};
  1849. TCHAR szCmdLine[BUFFER_SIZE+2*MAX_PATH+1] = {0};
  1850. DWORD dwNumWritten = 0L;
  1851. STARTUPINFO si;
  1852. PROCESS_INFORMATION pi = {0};
  1853. TCHAR szSection[MAX_PATH] = TEXT("[RegionalSettings]\r\n");
  1854. TCHAR tcMessage[BUFFER_SIZE+MAX_PATH+1] = {0};
  1855. HRESULT hr = S_OK;
  1856. size_t cch = 0;
  1857. //
  1858. // prepare the file for un-attended mode setup
  1859. //
  1860. szFilePath[0] = UNICODE_NULL;
  1861. if (!GetSystemWindowsDirectory(szFilePath, MAX_PATH+1))
  1862. {
  1863. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("RunRegionalOptionsApplet: GetSystemWindowsDirectory Failed - error is %d."), GetLastError());
  1864. if (SUCCEEDED(hr))
  1865. {
  1866. LogCustomActionInfo(hInstall, tcMessage);
  1867. }
  1868. return FALSE;
  1869. }
  1870. hr = StringCchLength(szFilePath, ARRAYSIZE(szFilePath), &cch);
  1871. if (SUCCEEDED(hr))
  1872. {
  1873. if (!MUICchPathAppend(szFilePath, ARRAYSIZE(szFilePath), MUI_LANG_GROUP_FILE, 12, hInstall))
  1874. {
  1875. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("RunRegionalOptionsApplet: Failed to form path to temp control file."));
  1876. if (SUCCEEDED(hr))
  1877. {
  1878. LogCustomActionInfo(hInstall, tcMessage);
  1879. }
  1880. return FALSE;
  1881. }
  1882. }
  1883. DEBUGMSGBOX(NULL, szFilePath, NULL, MB_OK);
  1884. hFile = CreateFile(szFilePath,
  1885. GENERIC_WRITE,
  1886. 0L,
  1887. NULL,
  1888. CREATE_ALWAYS,
  1889. FILE_ATTRIBUTE_NORMAL,
  1890. NULL);
  1891. if (INVALID_HANDLE_VALUE == hFile)
  1892. {
  1893. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("RunRegionalOptionsApplet: failed to create temporary file %s, error is %d."), szFilePath, GetLastError());
  1894. if (SUCCEEDED(hr))
  1895. {
  1896. LogCustomActionInfo(hInstall, tcMessage);
  1897. }
  1898. return FALSE;
  1899. }
  1900. WriteFile(hFile,
  1901. szSection,
  1902. (lstrlen(szSection) * sizeof(TCHAR)),
  1903. &dwNumWritten,
  1904. NULL);
  1905. if (dwNumWritten != (_tcslen(szSection) * sizeof(TCHAR)))
  1906. {
  1907. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("RunRegionalOptionsApplet: failed to write to temporary file %s, error is %d."), szFilePath, GetLastError());
  1908. if (SUCCEEDED(hr))
  1909. {
  1910. LogCustomActionInfo(hInstall, tcMessage);
  1911. }
  1912. CloseHandle(hFile);
  1913. return FALSE;
  1914. }
  1915. WriteFile(hFile,
  1916. pCommands,
  1917. (lstrlen(pCommands) * sizeof(TCHAR)),
  1918. &dwNumWritten,
  1919. NULL);
  1920. if (dwNumWritten != (_tcslen(pCommands) * sizeof(TCHAR)))
  1921. {
  1922. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("RunRegionalOptionsApplet: failed to write to temporary file %s, error is %d."), szFilePath, GetLastError());
  1923. if (SUCCEEDED(hr))
  1924. {
  1925. LogCustomActionInfo(hInstall, tcMessage);
  1926. }
  1927. CloseHandle(hFile);
  1928. return (FALSE);
  1929. }
  1930. CloseHandle(hFile);
  1931. // form a path to the system directory's rundll32.exe
  1932. if (ARRAYSIZE(szSysDir) < GetSystemDirectory(szSysDir, ARRAYSIZE(szSysDir)))
  1933. {
  1934. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("RunRegionalOptionsApplet: Failed to form path to rundll32."));
  1935. if (SUCCEEDED(hr))
  1936. {
  1937. LogCustomActionInfo(hInstall, tcMessage);
  1938. }
  1939. return (FALSE);
  1940. }
  1941. // append rundll32.exe at the end of sysdir
  1942. if (!MUICchPathAppend(szSysDir, ARRAYSIZE(szSysDir), TEXT("rundll32.exe"), 13, hInstall))
  1943. {
  1944. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("RunRegionalOptionsApplet: Failed to form path to rundll32."));
  1945. if (SUCCEEDED(hr))
  1946. {
  1947. LogCustomActionInfo(hInstall, tcMessage);
  1948. }
  1949. return (FALSE);
  1950. }
  1951. // Call the control panel regional-options applet, and wait for it to complete
  1952. hr = StringCchPrintf(szCmdLine, ARRAYSIZE(szCmdLine), TEXT("\"%s\" shell32,Control_RunDLL intl.cpl,, /f:\"%s\" "), szSysDir, szFilePath);
  1953. if (FAILED(hr))
  1954. {
  1955. DWORD dwError = HRESULT_CODE(hr);
  1956. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("RunRegionalOptionsApplet: Failed to form launch command for intl.cpl, error is %d."), dwError);
  1957. if (SUCCEEDED(hr))
  1958. {
  1959. LogCustomActionInfo(hInstall, tcMessage);
  1960. }
  1961. return (FALSE);
  1962. }
  1963. if (bSilent)
  1964. {
  1965. hr = StringCchCat(szCmdLine, ARRAYSIZE(szCmdLine), TEXT(" /D"));
  1966. if (FAILED(hr))
  1967. {
  1968. DWORD dwError = HRESULT_CODE(hr);
  1969. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("RunRegionalOptionsApplet: Failed to form launch command for intl.cpl, error is %d."), dwError);
  1970. if (SUCCEEDED(hr))
  1971. {
  1972. LogCustomActionInfo(hInstall, tcMessage);
  1973. }
  1974. return (FALSE);
  1975. }
  1976. }
  1977. DEBUGMSGBOX(NULL, szCmdLine, NULL, MB_OK);
  1978. memset( &si, 0x00, sizeof(si));
  1979. si.cb = sizeof(STARTUPINFO);
  1980. if (!CreateProcess(NULL,
  1981. szCmdLine,
  1982. NULL,
  1983. NULL,
  1984. FALSE,
  1985. 0L,
  1986. NULL,
  1987. NULL,
  1988. &si,
  1989. &pi))
  1990. {
  1991. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("RunRegionalOptionsApplet: failed to create a process for running intl.cpl, error is %d."), GetLastError());
  1992. if (SUCCEEDED(hr))
  1993. {
  1994. LogCustomActionInfo(hInstall, tcMessage);
  1995. }
  1996. return FALSE;
  1997. }
  1998. //
  1999. // Wait forever till intl.cpl terminates.
  2000. //
  2001. WaitForSingleObject(pi.hProcess, INFINITE);
  2002. DEBUGMSGBOX(NULL, TEXT("RunRegionalOptionApplet: intl.cpl execution is complete"), NULL, MB_OK);
  2003. CloseHandle(pi.hThread); // We have to close out hThread before we can close hProcess
  2004. CloseHandle(pi.hProcess);
  2005. //
  2006. // Delete the File, don't return false if we fail to delete the command file though
  2007. //
  2008. if (!DeleteFile(szFilePath))
  2009. {
  2010. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("RunRegionalOptionsApplet: failed to delete regionaloption applet command file %s, error is %d."), szFilePath, GetLastError());
  2011. if (SUCCEEDED(hr))
  2012. {
  2013. LogCustomActionInfo(hInstall, tcMessage);
  2014. }
  2015. }
  2016. return (TRUE);
  2017. }
  2018. ////////////////////////////////////////////////////////////////////////////////////
  2019. //
  2020. // GetLanguageGroup
  2021. //
  2022. // Retreive the Language Group of this locale.
  2023. //
  2024. ////////////////////////////////////////////////////////////////////////////////////
  2025. LGRPID GetLanguageGroup(LCID lcid, MSIHANDLE hInstall)
  2026. {
  2027. int i;
  2028. TCHAR tcMessage[BUFFER_SIZE] = {0};
  2029. HRESULT hr = S_OK;
  2030. gLangGroup = LGRPID_WESTERN_EUROPE;
  2031. gFoundLangGroup = FALSE;
  2032. gLCID = lcid;
  2033. if (!EnumSystemLanguageGroups(EnumLanguageGroupsProc, LGRPID_SUPPORTED, 0))
  2034. {
  2035. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("GetLanguageGroup: EnumLanguageGroups failed, error is %d."), GetLastError());
  2036. if (SUCCEEDED(hr))
  2037. {
  2038. LogCustomActionInfo(hInstall, tcMessage);
  2039. }
  2040. }
  2041. for (i=0 ; i<gNumLanguageGroups; i++)
  2042. {
  2043. // The globals gLangGroup and gFoundLangGroup is used in the callback function
  2044. // EnumLanguageGroupLocalesProc.
  2045. if (!EnumLanguageGroupLocales(EnumLanguageGroupLocalesProc, gLanguageGroups[i], 0L, 0L))
  2046. {
  2047. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("GetLanguageGroup: EnumLanguageGroupLocales failed, error is %d."), GetLastError());
  2048. if (SUCCEEDED(hr))
  2049. {
  2050. LogCustomActionInfo(hInstall, tcMessage);
  2051. }
  2052. }
  2053. //
  2054. // If we found it, then break now
  2055. //
  2056. if (gFoundLangGroup)
  2057. {
  2058. break;
  2059. }
  2060. }
  2061. return gLangGroup;
  2062. }
  2063. ////////////////////////////////////////////////////////////////////////////////////
  2064. //
  2065. // EnumLanguageGroupsProc
  2066. //
  2067. // This function is called by EnumLanguageGroups to enumerate the system installed language groups
  2068. // and store it in the global variables for other uses
  2069. //
  2070. ////////////////////////////////////////////////////////////////////////////////////
  2071. BOOL CALLBACK EnumLanguageGroupsProc(
  2072. LGRPID LanguageGroup, // language group identifier
  2073. LPTSTR lpLanguageGroupString, // pointer to language group identifier string
  2074. LPTSTR lpLanguageGroupNameString, // pointer to language group name string
  2075. DWORD dwFlags, // flags
  2076. LONG_PTR lParam) // user-supplied parameter
  2077. {
  2078. gLanguageGroups[gNumLanguageGroups] = LanguageGroup;
  2079. gNumLanguageGroups++;
  2080. return TRUE;
  2081. }
  2082. ////////////////////////////////////////////////////////////////////////////////////
  2083. //
  2084. // EnumLanguageGroupLocalesProc
  2085. //
  2086. // This function is called to by enumerateLanguageGroupLocales to search for an installed language
  2087. //
  2088. ////////////////////////////////////////////////////////////////////////////////////
  2089. BOOL CALLBACK EnumLanguageGroupLocalesProc(
  2090. LGRPID langGroupId,
  2091. LCID lcid,
  2092. LPTSTR lpszLocale,
  2093. LONG_PTR lParam)
  2094. {
  2095. if (lcid == gLCID)
  2096. {
  2097. gLangGroup = langGroupId;
  2098. gFoundLangGroup = TRUE;
  2099. DEBUGMSGBOX(NULL, TEXT("EnumLanguageGroupLocalesProc: Found same LCID"), NULL, MB_OK);
  2100. // stop iterating
  2101. return FALSE;
  2102. }
  2103. // next iteration
  2104. return TRUE;
  2105. }
  2106. ////////////////////////////////////////////////////////////////////////////////////
  2107. //
  2108. // ReturnAllRequiredLangGroups
  2109. //
  2110. // This function returns all the required language groups as specified by the
  2111. // system and in extracted mui.inf in the returned array. It also returns
  2112. // the number of required language groups in the return parameter.
  2113. //
  2114. ////////////////////////////////////////////////////////////////////////////////////
  2115. BOOL ReturnAllRequiredLangGroups(LPTSTR szLanguage, UINT cchLangBufsize, LPTSTR szMuiInfPath, UINT cchPathBufsize, LGRPID *lgrpids, UINT *uiNumFoundGroups, MSIHANDLE hInstall)
  2116. {
  2117. int iArg;
  2118. UINT iRet = ERROR_SUCCESS;
  2119. DWORD dwCount;
  2120. TCHAR tcMessage[BUFFER_SIZE+MAX_PATH+1] = {0};
  2121. INFCONTEXT InfContext;
  2122. int LangGroup;
  2123. int iMuiInfCount = 0;
  2124. int i;
  2125. HINF hInf;
  2126. HRESULT hr = S_OK;
  2127. size_t cch = 0;
  2128. if (NULL == uiNumFoundGroups)
  2129. {
  2130. return FALSE;
  2131. }
  2132. *uiNumFoundGroups = 0;
  2133. if ((NULL == szLanguage) || (NULL == szMuiInfPath) || (NULL == lgrpids) || (NULL == hInstall))
  2134. {
  2135. return FALSE;
  2136. }
  2137. // check length of the passed in string
  2138. hr = StringCchLength(szLanguage, cchLangBufsize, &cch);
  2139. if (SUCCEEDED(hr))
  2140. {
  2141. if (cch > 4)
  2142. {
  2143. return FALSE;
  2144. }
  2145. }
  2146. else
  2147. {
  2148. return FALSE;
  2149. }
  2150. hr = StringCchLength(szMuiInfPath, cchPathBufsize, &cch);
  2151. if (SUCCEEDED(hr))
  2152. {
  2153. if (cch > MAX_PATH)
  2154. {
  2155. return FALSE;
  2156. }
  2157. }
  2158. else
  2159. {
  2160. return FALSE;
  2161. }
  2162. #ifdef MUI_DEBUG
  2163. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("ReturnAllRequiredLangGroups: MuiLCID is %s, installation temp file path is %s."), szLanguage, szMuiInfPath);
  2164. if (SUCCEEDED(hr))
  2165. {
  2166. LogCustomActionInfo(hInstall, tcMessage);
  2167. }
  2168. #endif
  2169. // convert lcid to appropriate language group
  2170. iArg = _tcstol(szLanguage, NULL, 16);
  2171. lgrpids[0] = GetLanguageGroup(MAKELCID(iArg, SORT_DEFAULT), hInstall);
  2172. *uiNumFoundGroups = 1; // at this point we should have 1 lang group at least
  2173. iMuiInfCount = 1;
  2174. DEBUGMSGBOX(NULL, szMuiInfPath, NULL, MB_OK);
  2175. hInf = SetupOpenInfFile(szMuiInfPath, NULL, INF_STYLE_WIN4, NULL);
  2176. if (hInf != INVALID_HANDLE_VALUE)
  2177. {
  2178. #ifdef MUI_DEBUG
  2179. TCHAR szMessage[BUFFER_SIZE] = {0};
  2180. hr = StringCchPrintf(szMessage, ARRAYSIZE(szMessage), TEXT("Language is %s."), szLanguage);
  2181. if (SUCCEEDED(hr))
  2182. {
  2183. DEBUGMSGBOX(NULL, szMessage, NULL, MB_OK);
  2184. }
  2185. #endif
  2186. if (SetupFindFirstLine(hInf, MUI_LANGPACK_SECTION, szLanguage, &InfContext))
  2187. {
  2188. DEBUGMSGBOX(NULL, TEXT("Found the LanguagePack section in installation temp file!"), NULL, MB_OK);
  2189. while (SetupGetIntField(&InfContext, iMuiInfCount, &LangGroup))
  2190. {
  2191. lgrpids[iMuiInfCount] = LangGroup;
  2192. iMuiInfCount++;
  2193. #ifdef MUI_DEBUG
  2194. hr = StringCchPrintf(szMessage, ARRAYSIZE(szMessage), TEXT("Found langgroup %d in installation temp file"), LangGroup);
  2195. if (SUCCEEDED(hr))
  2196. {
  2197. DEBUGMSGBOX(NULL, szMessage, NULL, MB_OK);
  2198. }
  2199. #endif
  2200. }
  2201. }
  2202. else
  2203. {
  2204. #ifdef MUI_DEBUG
  2205. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("ReturnAllRequiredLangGroups: installation temp file does not contain a LanguagePack section."));
  2206. if (SUCCEEDED(hr))
  2207. {
  2208. LogCustomActionInfo(hInstall, tcMessage);
  2209. }
  2210. #endif
  2211. }
  2212. SetupCloseInfFile(hInf);
  2213. }
  2214. else
  2215. {
  2216. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("ReturnAllRequiredLangGroups: installation temp file not found at location %s. The error is %d."), szMuiInfPath, GetLastError());
  2217. if (SUCCEEDED(hr))
  2218. {
  2219. LogCustomActionInfo(hInstall, tcMessage);
  2220. }
  2221. }
  2222. *uiNumFoundGroups = iMuiInfCount;
  2223. return TRUE;
  2224. }
  2225. ////////////////////////////////////////////////////////////////////////////////////
  2226. //
  2227. // ExecuteComponentINF
  2228. //
  2229. // Installs component MUI files, by running the specified INF file.
  2230. //
  2231. // Parameters:
  2232. // pComponentName the name of the component (e.g. "ie5")
  2233. // pComponentInfFile: the full path of the component INF file.
  2234. // pInstallSection the section in the component INF file to be executed. (e.g "DefaultInstall" or "Uninstall")
  2235. // bInstall: TRUE for install, FALSE for uninstall
  2236. //
  2237. ////////////////////////////////////////////////////////////////////////////////////
  2238. BOOL ExecuteComponentINF(
  2239. PTSTR pComponentName,
  2240. PTSTR pComponentInfFile,
  2241. PTSTR pInstallSection,
  2242. BOOL bInstall,
  2243. MSIHANDLE hInstall)
  2244. {
  2245. int iLen;
  2246. TCHAR tchCommandParam[MAX_PATH+6+BUFFER_SIZE] = {0};
  2247. CHAR chCommandParam[(MAX_PATH+6+BUFFER_SIZE)*sizeof(TCHAR)] = {0};
  2248. TCHAR tcMessage[2*BUFFER_SIZE+MAX_PATH+1] = {0};
  2249. HINF hCompInf; // the handle to the component INF file.
  2250. HSPFILEQ FileQueue;
  2251. PVOID QueueContext;
  2252. BOOL bRet = TRUE;
  2253. DWORD dwResult;
  2254. TCHAR szBuffer[BUFFER_SIZE] = {0};
  2255. HRESULT hr = S_OK;
  2256. //
  2257. // Advpack LaunchINFSection() command line format:
  2258. // INF file, INF section, flags, reboot string
  2259. // 'N' or 'n' in reboot string means no reboot message popup.
  2260. //
  2261. hr = StringCchPrintf(tchCommandParam, ARRAYSIZE(tchCommandParam), TEXT("%s,%s,1,n"), pComponentInfFile, pInstallSection);
  2262. if (FAILED(hr))
  2263. {
  2264. DWORD dwError = HRESULT_CODE(hr);
  2265. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("ExecuteComponentINF: failed to form Inf Execution command. The returned error is %d."), dwError);
  2266. if (SUCCEEDED(hr))
  2267. {
  2268. LogCustomActionInfo(hInstall, tcMessage);
  2269. }
  2270. return FALSE;
  2271. }
  2272. if (!WideCharToMultiByte(CP_ACP, 0, tchCommandParam, -1, chCommandParam, ARRAYSIZE(chCommandParam), NULL, NULL))
  2273. {
  2274. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("ExecuteComponentINF: failed to form Inf Execution command. The returned error is %d."), GetLastError());
  2275. if (SUCCEEDED(hr))
  2276. {
  2277. LogCustomActionInfo(hInstall, tcMessage);
  2278. }
  2279. return FALSE;
  2280. }
  2281. if (FileExists(pComponentInfFile))
  2282. {
  2283. if (LaunchINFSection(NULL, NULL, chCommandParam, SW_HIDE) != S_OK)
  2284. {
  2285. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("ExecuteComponentINF: LaunchINFSection failed for inf file %s, component name %s."), pComponentInfFile, pComponentName);
  2286. if (SUCCEEDED(hr))
  2287. {
  2288. LogCustomActionInfo(hInstall, tcMessage);
  2289. }
  2290. return FALSE;
  2291. }
  2292. }
  2293. else
  2294. {
  2295. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("ExecuteComponentINF: Failed to locate inf file %s."), pComponentInfFile);
  2296. if (SUCCEEDED(hr))
  2297. {
  2298. LogCustomActionInfo(hInstall, tcMessage);
  2299. }
  2300. return FALSE;
  2301. }
  2302. return TRUE;
  2303. }
  2304. ////////////////////////////////////////////////////////////////////////////////////
  2305. //
  2306. // InstallComponentsMUIFiles
  2307. //
  2308. // Parameters:
  2309. // pszLangSourceDir The sub-directory name for a specific lanuage in the MUI CD-ROM.
  2310. // E.g. "jpn.MUI"
  2311. // pszLanguage The LCID for the specific language. E.g. "0404".
  2312. // isInstall TRUE if you are going to install the MUI files for the component. FALSE
  2313. // if you are going to uninstall.
  2314. //
  2315. // Return:
  2316. // -1 if failed, IDOK if succeeded, IDCANCEL if user clicked cancel during the operation
  2317. //
  2318. // Note:
  2319. // For the language resources stored in pszLangSourceDir, this function will enumerate
  2320. // the compoents listed in the [Components]
  2321. // (the real section is put in MUI_COMPONENTS_SECTION) section, and execute the INF file
  2322. // listed in every entry in
  2323. // the [Components] section.
  2324. //
  2325. ////////////////////////////////////////////////////////////////////////////////////
  2326. INT InstallComponentsMUIFiles(PTSTR pszLanguage, BOOL isInstall, MSIHANDLE hInstall)
  2327. {
  2328. BOOL result = TRUE;
  2329. BOOL bRollback = FALSE;
  2330. BOOL bOEMSystem = FALSE;
  2331. TCHAR szComponentName[BUFFER_SIZE] = {0};
  2332. TCHAR CompDir[MAX_PATH+1] = {0};
  2333. TCHAR szWinDir[MAX_PATH+1] = {0};
  2334. TCHAR CompINFFile[BUFFER_SIZE] = {0};
  2335. TCHAR CompInstallSection[BUFFER_SIZE] = {0};
  2336. TCHAR CompUninstallSection[BUFFER_SIZE] = {0};
  2337. TCHAR szMuiInfPath[MAX_PATH+1] = {0};
  2338. TCHAR szBuffer[3*BUFFER_SIZE+MAX_PATH+1] = {0};
  2339. TCHAR szCompInfFullPath[MAX_PATH+1] = {0};
  2340. TCHAR szCompInfAltFullPath[MAX_PATH+1] = {0};
  2341. INFCONTEXT InfContext;
  2342. PMSIHANDLE hRec = MsiCreateRecord(3);
  2343. PMSIHANDLE hProgressRec = MsiCreateRecord(3);
  2344. HRESULT hr = S_OK;
  2345. INT iResult = IDOK;
  2346. INT iFlag = 0;
  2347. if ((NULL == hRec) || (NULL == hProgressRec))
  2348. {
  2349. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: cannot create MSI Records."));
  2350. if (SUCCEEDED(hr))
  2351. {
  2352. LogCustomActionInfo(hInstall, szBuffer);
  2353. }
  2354. return -1;
  2355. }
  2356. bRollback = MsiGetMode(hInstall,MSIRUNMODE_ROLLBACK);
  2357. // get path to the target installation temp file file on the target, it should be at WindowsFolder\mui.tmp
  2358. szMuiInfPath[0] = UNICODE_NULL;
  2359. if (!GetMUIInfPath(szMuiInfPath, MAX_PATH+1, hInstall))
  2360. {
  2361. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: Unable to find installation temp file."));
  2362. if (SUCCEEDED(hr))
  2363. {
  2364. LogCustomActionInfo(hInstall, szBuffer);
  2365. }
  2366. return -1;
  2367. }
  2368. // also get the windows dir, for later use
  2369. if (!GetSystemWindowsDirectory(szWinDir, MAX_PATH+1))
  2370. {
  2371. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: cannot get Windows Directory."));
  2372. if (SUCCEEDED(hr))
  2373. {
  2374. LogCustomActionInfo(hInstall, szBuffer);
  2375. }
  2376. return -1;
  2377. }
  2378. HINF hInf = SetupOpenInfFile(szMuiInfPath, NULL, INF_STYLE_WIN4, NULL);
  2379. if (hInf == INVALID_HANDLE_VALUE)
  2380. {
  2381. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: Unable to open installation temp file."));
  2382. if (SUCCEEDED(hr))
  2383. {
  2384. LogCustomActionInfo(hInstall, szBuffer);
  2385. }
  2386. return -1;
  2387. }
  2388. //Check if its an OEM system
  2389. bOEMSystem = IsOEMSystem();
  2390. //
  2391. // Get the first component to be installed.
  2392. //
  2393. if (SetupFindFirstLine(hInf, MUI_COMPONENTS_SECTION, NULL, &InfContext))
  2394. {
  2395. do
  2396. {
  2397. if (SetupGetIntField(&InfContext, 5,&iFlag)) //Check the last field of the component to see if its an OEM component. If OEM component iIsOEM = 1
  2398. {
  2399. if ((iFlag == OEM_COMPONENT) && !bOEMSystem) //Skip installation if its an OEM component and this isnt an OEM system
  2400. continue;
  2401. }
  2402. if (!SetupGetStringField(&InfContext, 0, szComponentName, ARRAYSIZE(szComponentName), NULL))
  2403. {
  2404. // continue on the next line - but remember to log an error
  2405. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: Error reading installation temp file, component name is missing."));
  2406. if (SUCCEEDED(hr))
  2407. {
  2408. LogCustomActionInfo(hInstall, szBuffer);
  2409. }
  2410. continue;
  2411. }
  2412. // tell the installer UI that we are installing a new component now
  2413. if (!bRollback)
  2414. {
  2415. MsiRecordSetString(hRec,1, szComponentName);
  2416. iResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_ACTIONDATA, hRec);
  2417. if (iResult == IDCANCEL)
  2418. {
  2419. SetupCloseInfFile(hInf);
  2420. return iResult;
  2421. }
  2422. }
  2423. if (!SetupGetStringField(&InfContext, 1, CompDir, ARRAYSIZE(CompDir), NULL))
  2424. {
  2425. // continue on the next line - but remember to log an error
  2426. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: MUI files for component %s was not installed because of missing component direcotry."), szComponentName);
  2427. if (SUCCEEDED(hr))
  2428. {
  2429. LogCustomActionInfo(hInstall, szBuffer);
  2430. }
  2431. continue;
  2432. }
  2433. if (!SetupGetStringField(&InfContext, 2, CompINFFile, ARRAYSIZE(CompINFFile), NULL))
  2434. {
  2435. // continue on the next line - but remember to log an error
  2436. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: MUI files for component %s was not installed because of missing component INF filename."), szComponentName);
  2437. if (SUCCEEDED(hr))
  2438. {
  2439. LogCustomActionInfo(hInstall, szBuffer);
  2440. }
  2441. continue;
  2442. }
  2443. if (isInstall && (!SetupGetStringField(&InfContext, 3, CompInstallSection, ARRAYSIZE(CompInstallSection), NULL)))
  2444. {
  2445. hr = StringCchCopy(CompInstallSection, ARRAYSIZE(CompInstallSection), DEFAULT_INSTALL_SECTION);
  2446. if (FAILED(hr))
  2447. {
  2448. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: Cannot locate Default Install section for component %s."), szComponentName);
  2449. if (SUCCEEDED(hr))
  2450. {
  2451. LogCustomActionInfo(hInstall, szBuffer);
  2452. }
  2453. continue;
  2454. }
  2455. }
  2456. if (!isInstall && (!SetupGetStringField(&InfContext, 4, CompUninstallSection, ARRAYSIZE(CompUninstallSection), NULL)))
  2457. {
  2458. hr = StringCchCopy(CompUninstallSection, ARRAYSIZE(CompUninstallSection), DEFAULT_UNINSTALL_SECTION);
  2459. if (FAILED(hr))
  2460. {
  2461. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: Cannot locate Default Uninnstall section for component %s."), szComponentName);
  2462. if (SUCCEEDED(hr))
  2463. {
  2464. LogCustomActionInfo(hInstall, szBuffer);
  2465. }
  2466. continue;
  2467. }
  2468. }
  2469. //
  2470. // Establish the correct path for component INF file.
  2471. // We execute the INFs on the target MUI directory after msi has copied the files, it's installed to MUIroot\fallback\LCID\external\componentdir\
  2472. // e.g. c:\windows\mui\fallback\lcid\external\ie5\ie5ui.inf
  2473. // This is done for both install and uninstall, since we should be guaranteed that the files will be located there.
  2474. // 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
  2475. //
  2476. hr = StringCchCopy(szCompInfFullPath, ARRAYSIZE(szCompInfFullPath), szWinDir);
  2477. if (SUCCEEDED(hr))
  2478. {
  2479. if (!((MUICchPathAppend(szCompInfFullPath, ARRAYSIZE(szCompInfFullPath), FALLBACKDIR, 13, hInstall)) &&
  2480. (MUICchPathAppend(szCompInfFullPath, ARRAYSIZE(szCompInfFullPath), pszLanguage, 5, hInstall)) &&
  2481. (MUICchPathAppend(szCompInfFullPath, ARRAYSIZE(szCompInfFullPath), EXTDIR, 9, hInstall)) &&
  2482. (MUICchPathAppend(szCompInfFullPath, ARRAYSIZE(szCompInfFullPath), CompDir, ARRAYSIZE(CompDir), hInstall)) &&
  2483. (MUICchPathAppend(szCompInfFullPath, ARRAYSIZE(szCompInfFullPath), CompINFFile, ARRAYSIZE(CompINFFile), hInstall))))
  2484. {
  2485. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: Cannot form path to external component INF."));
  2486. if (SUCCEEDED(hr))
  2487. {
  2488. LogCustomActionInfo(hInstall, szBuffer);
  2489. }
  2490. continue;
  2491. }
  2492. }
  2493. else
  2494. {
  2495. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: Cannot form path to external component INF."));
  2496. if (SUCCEEDED(hr))
  2497. {
  2498. LogCustomActionInfo(hInstall, szBuffer);
  2499. }
  2500. continue;
  2501. }
  2502. if (isInstall)
  2503. {
  2504. if (!ExecuteComponentINF(szComponentName, szCompInfFullPath, CompInstallSection, TRUE, hInstall))
  2505. {
  2506. // log an error and continue
  2507. 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);
  2508. if (SUCCEEDED(hr))
  2509. {
  2510. LogCustomActionInfo(hInstall, szBuffer);
  2511. }
  2512. continue;
  2513. }
  2514. }
  2515. else
  2516. {
  2517. if (!ExecuteComponentINF(szComponentName, szCompInfFullPath, CompUninstallSection, FALSE, hInstall) && result)
  2518. {
  2519. // try this again at an alternate location
  2520. hr = StringCchCopy(szCompInfAltFullPath, ARRAYSIZE(szCompInfAltFullPath), szWinDir);
  2521. if (SUCCEEDED(hr))
  2522. {
  2523. if (!((MUICchPathAppend(szCompInfAltFullPath, ARRAYSIZE(szCompInfAltFullPath), FALLBACKDIR, 13, hInstall)) &&
  2524. (MUICchPathAppend(szCompInfAltFullPath, ARRAYSIZE(szCompInfAltFullPath), pszLanguage, 5, hInstall)) &&
  2525. (MUICchPathAppend(szCompInfAltFullPath, ARRAYSIZE(szCompInfAltFullPath), CompINFFile, ARRAYSIZE(CompINFFile), hInstall))))
  2526. {
  2527. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: Cannot form path to alternate external component INF."));
  2528. if (SUCCEEDED(hr))
  2529. {
  2530. LogCustomActionInfo(hInstall, szBuffer);
  2531. }
  2532. continue;
  2533. }
  2534. }
  2535. else
  2536. {
  2537. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("InstallComponentInfs Failure: Cannot form path to alternate external component INF."));
  2538. if (SUCCEEDED(hr))
  2539. {
  2540. LogCustomActionInfo(hInstall, szBuffer);
  2541. }
  2542. continue;
  2543. }
  2544. if (!ExecuteComponentINF(szComponentName, szCompInfAltFullPath, CompUninstallSection, FALSE, hInstall) && result)
  2545. {
  2546. // log an error and continue
  2547. 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);
  2548. if (SUCCEEDED(hr))
  2549. {
  2550. LogCustomActionInfo(hInstall, szBuffer);
  2551. }
  2552. continue;
  2553. }
  2554. }
  2555. }
  2556. // Specify that an update of the progress bar's position in this
  2557. // case means to move it forward by one increment now that we have installed it.
  2558. if (!bRollback)
  2559. {
  2560. MsiRecordSetInteger(hProgressRec,1,2);
  2561. MsiRecordSetInteger(hProgressRec,2,COMP_TICK_INC);
  2562. MsiRecordSetInteger(hProgressRec,3,0);
  2563. iResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hProgressRec);
  2564. if (iResult == IDCANCEL)
  2565. {
  2566. SetupCloseInfFile(hInf);
  2567. return iResult;
  2568. }
  2569. }
  2570. //
  2571. // Install the next component.
  2572. //
  2573. } while (SetupFindNextLine(&InfContext, &InfContext));
  2574. }
  2575. SetupCloseInfFile(hInf);
  2576. return (IDOK);
  2577. }
  2578. ////////////////////////////////////////////////////////////////////////////////////
  2579. //
  2580. // GetMUIComponentsNumber
  2581. //
  2582. // Parameters:
  2583. // bInstall indicate whether this function is used for installing component infs or not
  2584. // this affects where it will look for mui.inf to get the component count.
  2585. // pszLangSourceDir The sub-directory name for a specific lanuage in the MUI CD-ROM.
  2586. // E.g. "jpn.MUI"
  2587. // pszLanguage The LCID for the specific language. E.g. "0404".
  2588. //
  2589. // Return:
  2590. // The number of MUI external components that need to be installed/uninstalled, if
  2591. // there is an error it will return 0, otherwise it returns the number of components
  2592. //
  2593. // Note:
  2594. // For the language resources stored in pszLangSourceDir, this function will enumerate
  2595. // the compoents listed in the [Components]
  2596. // (the real section is put in MUI_COMPONENTS_SECTION) section, and counts every entry in
  2597. // the [Components] section.
  2598. //
  2599. ////////////////////////////////////////////////////////////////////////////////////
  2600. UINT GetMUIComponentsNumber(PTSTR pszLanguage, MSIHANDLE hInstall)
  2601. {
  2602. UINT iResult = 0;
  2603. TCHAR szComponentName[BUFFER_SIZE] = {0};
  2604. TCHAR CompDir[MAX_PATH+1] = {0};
  2605. TCHAR CompINFFile[BUFFER_SIZE] = {0};
  2606. TCHAR szMuiInfPath[MAX_PATH+1] = {0};
  2607. TCHAR szBuffer[BUFFER_SIZE] = {0};
  2608. INFCONTEXT InfContext;
  2609. HRESULT hr = S_OK;
  2610. szMuiInfPath[0] = UNICODE_NULL;
  2611. // get path to the target mui.inf file
  2612. if (!GetMUIInfPath(szMuiInfPath, MAX_PATH+1, hInstall))
  2613. {
  2614. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("GetMUIComponentsNumber Failure: Unable to find installation temp file."));
  2615. if (SUCCEEDED(hr))
  2616. {
  2617. LogCustomActionInfo(hInstall, szBuffer);
  2618. }
  2619. return 0;
  2620. }
  2621. HINF hInf = SetupOpenInfFile(szMuiInfPath, NULL, INF_STYLE_WIN4, NULL);
  2622. if (hInf == INVALID_HANDLE_VALUE)
  2623. {
  2624. // return true here so that there won't be an error - but remember to log an error
  2625. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("GetMUIComponentsNumber: Unable to open installation temp file."));
  2626. if (SUCCEEDED(hr))
  2627. {
  2628. LogCustomActionInfo(hInstall, szBuffer);
  2629. }
  2630. return (iResult);
  2631. }
  2632. // Get the first comopnent to be installed.
  2633. if (SetupFindFirstLine(hInf, MUI_COMPONENTS_SECTION, NULL, &InfContext))
  2634. {
  2635. do
  2636. {
  2637. if (!SetupGetStringField(&InfContext, 0, szComponentName, ARRAYSIZE(szComponentName), NULL))
  2638. {
  2639. // return true here so that there won't be an error - but remember to log an error
  2640. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("GetMUIComponentsNumber: Error reading installation temp file, component name is missing."));
  2641. if (SUCCEEDED(hr))
  2642. {
  2643. LogCustomActionInfo(hInstall, szBuffer);
  2644. }
  2645. continue;
  2646. }
  2647. if (!SetupGetStringField(&InfContext, 1, CompDir, ARRAYSIZE(CompDir), NULL))
  2648. {
  2649. // return true here so that there won't be an error - but remember to log an error
  2650. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("GetMUIComponentsNumber: MUI files for component %s was not counted because of missing component direcotry."), szComponentName);
  2651. if (SUCCEEDED(hr))
  2652. {
  2653. LogCustomActionInfo(hInstall, szBuffer);
  2654. }
  2655. continue;
  2656. }
  2657. if (!SetupGetStringField(&InfContext, 2, CompINFFile, ARRAYSIZE(CompINFFile), NULL))
  2658. {
  2659. // return true here so that there won't be an error - but remember to log an error
  2660. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("GetMUIComponentsNumber: MUI files for component %s was not counted because of missing component INF filename."), szComponentName);
  2661. if (SUCCEEDED(hr))
  2662. {
  2663. LogCustomActionInfo(hInstall, szBuffer);
  2664. }
  2665. continue;
  2666. }
  2667. iResult++;
  2668. } while (SetupFindNextLine(&InfContext, &InfContext));
  2669. }
  2670. SetupCloseInfFile(hInf);
  2671. #ifdef MUI_DEBUG
  2672. hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("GetMUIComponentsNumber: Found %d components to install."), iResult);
  2673. if (SUCCEEDED(hr))
  2674. {
  2675. LogCustomActionInfo(hInstall, szBuffer);
  2676. }
  2677. #endif
  2678. return (iResult);
  2679. }
  2680. ////////////////////////////////////////////////////////////////////////////////////
  2681. //
  2682. // File Exists
  2683. //
  2684. // Returns TRUE if the file exists, FALSE if it does not.
  2685. //
  2686. ////////////////////////////////////////////////////////////////////////////////////
  2687. BOOL FileExists(LPTSTR szFile)
  2688. {
  2689. HANDLE hFile;
  2690. WIN32_FIND_DATA FindFileData;
  2691. HRESULT hr = S_OK;
  2692. size_t cch = 0;
  2693. if (NULL == szFile)
  2694. {
  2695. return FALSE;
  2696. }
  2697. // check for valid input, the path cannot be larger than MAX_PATH+1
  2698. hr = StringCchLength(szFile, MAX_PATH+1, &cch);
  2699. if (FAILED(hr) || cch > MAX_PATH)
  2700. {
  2701. return FALSE;
  2702. }
  2703. hFile = FindFirstFile(szFile, &FindFileData);
  2704. if (hFile == INVALID_HANDLE_VALUE)
  2705. {
  2706. return FALSE;
  2707. }
  2708. FindClose(hFile);
  2709. return TRUE;
  2710. }
  2711. ////////////////////////////////////////////////////////////////////////////////////
  2712. //
  2713. // LogCustomActionInfo
  2714. //
  2715. // This function sends an INFORMATION-type log message record to the opened
  2716. // windows installer session so that it can be logged by the installer
  2717. // if logging is enabled.
  2718. //
  2719. ////////////////////////////////////////////////////////////////////////////////////
  2720. void LogCustomActionInfo(MSIHANDLE hInstall, LPCTSTR szErrorMsg)
  2721. {
  2722. // 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
  2723. // to prevent it from showing up as an error and stopping the installation.
  2724. PMSIHANDLE hRecord = MsiCreateRecord(0);
  2725. // if can't create a msi record, just return
  2726. if ((NULL == hInstall) || (NULL == szErrorMsg) || (NULL == hRecord))
  2727. {
  2728. return;
  2729. }
  2730. if (ERROR_SUCCESS == MsiRecordSetString(hRecord, 0, szErrorMsg))
  2731. {
  2732. MsiProcessMessage(hInstall, INSTALLMESSAGE_INFO, hRecord);
  2733. }
  2734. }
  2735. ////////////////////////////////////////////////////////////////////////////////////
  2736. //
  2737. // GetLCID
  2738. //
  2739. // This function returns the 4-character LCID for the current installation package.
  2740. // We assume here that the passed in string array size is 5 TCHARs. If it is not,
  2741. // the function will fail.
  2742. //
  2743. // The behaviour is summarized as follows:
  2744. //
  2745. // 1. Immediate:
  2746. // a. Property "MuiLCID" is retrieved and tested from the current installation
  2747. // b. if LCID property can't be retrieved, returns FALSE.
  2748. //
  2749. // 2. Deferred/Rollback:
  2750. // a. Property "CustomActionData" is retrieved.
  2751. // b. Assumption is that LCID will be the first 4 character in the retrieved CustomActionData property.
  2752. // c. If property can't be retrieved, or if property testing fails, return FALSE.
  2753. //
  2754. // Parameters:
  2755. // szLanguage: This is a caller-allocated buffer of 5 TCHARS to store the LCID
  2756. // cchBufSize: This is the size of szLanguage, it has to be 5.
  2757. // hInstall: Current installation handle.
  2758. //
  2759. ////////////////////////////////////////////////////////////////////////////////////
  2760. BOOL GetLCID(TCHAR *szLanguage, UINT cchBufSize, MSIHANDLE hInstall)
  2761. {
  2762. HRESULT hr = S_OK;
  2763. TCHAR szLcid[5] = {0};
  2764. TCHAR szCustomActionData[BUFFER_SIZE] = {0};
  2765. TCHAR tcMessage[BUFFER_SIZE] = {0};
  2766. DWORD dwCount = 0;
  2767. if ((NULL == hInstall) || (NULL == szLanguage))
  2768. {
  2769. #ifdef MUI_DEBUG
  2770. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("GetLCID: Internal error 1."));
  2771. if (SUCCEEDED(hr))
  2772. {
  2773. LogCustomActionInfo(hInstall, tcMessage);
  2774. }
  2775. #endif
  2776. return FALSE;
  2777. }
  2778. if (cchBufSize != 5)
  2779. {
  2780. #ifdef MUI_DEBUG
  2781. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("GetLCID: Internal error 2."));
  2782. if (SUCCEEDED(hr))
  2783. {
  2784. LogCustomActionInfo(hInstall, tcMessage);
  2785. }
  2786. #endif
  2787. return FALSE;
  2788. }
  2789. if (!MsiGetMode(hInstall, MSIRUNMODE_SCHEDULED) &&
  2790. !MsiGetMode(hInstall, MSIRUNMODE_ROLLBACK)&&
  2791. !MsiGetMode(hInstall, MSIRUNMODE_COMMIT))
  2792. {
  2793. dwCount = 5;
  2794. if (ERROR_SUCCESS != MsiGetProperty(hInstall, TEXT("MuiLCID"), szLcid, &dwCount))
  2795. {
  2796. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("GetLCID: Failed to retrieve MuiLCID property."));
  2797. if (SUCCEEDED(hr))
  2798. {
  2799. LogCustomActionInfo(hInstall, tcMessage);
  2800. }
  2801. return FALSE;
  2802. }
  2803. // copy the Lcid to the output buffer
  2804. szLcid[4] = UNICODE_NULL;
  2805. hr = StringCchCopy(szLanguage, cchBufSize, szLcid);
  2806. if (FAILED(hr))
  2807. {
  2808. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("GetLCID: Failed to retrieve MuiLCID property."));
  2809. if (SUCCEEDED(hr))
  2810. {
  2811. LogCustomActionInfo(hInstall, tcMessage);
  2812. }
  2813. return FALSE;
  2814. }
  2815. }
  2816. else
  2817. {
  2818. dwCount = BUFFER_SIZE;
  2819. if (ERROR_SUCCESS != MsiGetProperty(hInstall, TEXT("CustomActionData"), szCustomActionData, &dwCount))
  2820. {
  2821. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("GetLCID: Failed to retrieve CustomActionData property."));
  2822. if (SUCCEEDED(hr))
  2823. {
  2824. LogCustomActionInfo(hInstall, tcMessage);
  2825. }
  2826. return FALSE;
  2827. }
  2828. // copy the Lcid to the output buffer
  2829. szCustomActionData[4] = UNICODE_NULL;
  2830. hr = StringCchCopy(szLanguage, cchBufSize, szCustomActionData);
  2831. if (FAILED(hr))
  2832. {
  2833. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("GetLCID: Failed to retrieve CustomActionData property."));
  2834. if (SUCCEEDED(hr))
  2835. {
  2836. LogCustomActionInfo(hInstall, tcMessage);
  2837. }
  2838. return FALSE;
  2839. }
  2840. }
  2841. szLanguage[4] = UNICODE_NULL;
  2842. return TRUE;
  2843. }
  2844. ////////////////////////////////////////////////////////////////////////////////////
  2845. //
  2846. // GetMUIInfPath
  2847. //
  2848. // This function returns the path to mui.inf to the calling function. This function
  2849. // is intended for use only by the exported functions of the custom action functions
  2850. // in this dll.
  2851. //
  2852. // Note that mui.inf is extracted to %windir% as mui.tmp during the installation
  2853. //
  2854. // The function expects the mui.tmp to be at %windir%\mui.tmp.
  2855. //
  2856. // Return Value:
  2857. // If the function successfully finds a file named mui.inf, it returns TRUE, otherwise it returns FALSE
  2858. // The full path to mui.inf is returned in the caller supplied buffer szMUIInfPath
  2859. //
  2860. // Parameters:
  2861. // szMUIInfPath -
  2862. // [out] This is the output buffer that will contain the path of the mui.tmp.
  2863. // cchBufSize -
  2864. // This indicates the size of the input/output buffer the caller allocated for us, it should be no longer
  2865. // than MAX_PATH+1 (validated in the function.
  2866. // hInstall -
  2867. // This is the handle passed to us from the windows installer - it is a handle to the current installation
  2868. //
  2869. ////////////////////////////////////////////////////////////////////////////////////
  2870. BOOL GetMUIInfPath(TCHAR *szMUIInfPath, UINT cchBufSize, MSIHANDLE hInstall)
  2871. {
  2872. TCHAR tcMessage[BUFFER_SIZE] = {0};
  2873. TCHAR szTempPath[MAX_PATH+1] = {0};
  2874. HRESULT hr = S_OK;
  2875. size_t cch = 0;
  2876. DWORD dwCount = 0;
  2877. if ((NULL == hInstall) || (NULL == szMUIInfPath))
  2878. {
  2879. return FALSE;
  2880. }
  2881. if ((cchBufSize > MAX_PATH+1) || (cchBufSize <= 8)) // 8 = mui.tmp + null terminator
  2882. {
  2883. return FALSE;
  2884. }
  2885. if (!GetSystemWindowsDirectory(szTempPath, MAX_PATH+1))
  2886. {
  2887. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("GetMUIInfPath: Unable to find the Windows directory, GetSystemWindowsDirectory returned %d."), GetLastError());
  2888. if (SUCCEEDED(hr))
  2889. {
  2890. LogCustomActionInfo(hInstall, tcMessage);
  2891. }
  2892. }
  2893. // check retrieved winpath, it needs to have space to append "mui.tmp" at the end
  2894. hr = StringCchLength(szTempPath, ARRAYSIZE(szTempPath), &cch);
  2895. if (FAILED(hr) || ((cch + 8) >= MAX_PATH+1))
  2896. {
  2897. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("GetMUIInfPath: cannot locate installation temp file."));
  2898. if (SUCCEEDED(hr))
  2899. {
  2900. LogCustomActionInfo(hInstall, tcMessage);
  2901. }
  2902. return FALSE;
  2903. }
  2904. // append mui.tmp
  2905. if (!MUICchPathAppend(szTempPath, ARRAYSIZE(szTempPath), TEXT("mui.tmp"), 8, hInstall))
  2906. {
  2907. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("GetMUIInfPath: cannot locate installation temp file."));
  2908. if (SUCCEEDED(hr))
  2909. {
  2910. LogCustomActionInfo(hInstall, tcMessage);
  2911. }
  2912. return FALSE;
  2913. }
  2914. // check if mui.tmp is there, if not, return failure
  2915. if (!FileExists(szTempPath))
  2916. {
  2917. // zero out the output buffer
  2918. ZeroMemory(szMUIInfPath, cchBufSize * sizeof(TCHAR));
  2919. return FALSE;
  2920. }
  2921. // copy result to output buffer
  2922. hr = StringCchCopy(szMUIInfPath, cchBufSize, szTempPath);
  2923. if (FAILED(hr))
  2924. {
  2925. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("GetMUIInfPath: cannot locate installation temp file."));
  2926. if (SUCCEEDED(hr))
  2927. {
  2928. LogCustomActionInfo(hInstall, tcMessage);
  2929. }
  2930. return FALSE;
  2931. }
  2932. return TRUE;
  2933. }
  2934. ////////////////////////////////////////////////////////////////////////////////////
  2935. //
  2936. // MUICchPathAppend
  2937. //
  2938. // This function is a simple pathappend-like function that does limited parameter checking and uses the
  2939. // safe string functions internally. It is used only internally within this custom action to append
  2940. // file names to the end of a path (such as current directory or windows system directory)
  2941. //
  2942. // If error occurs, the content of SzDestination is undefined and should not be used.
  2943. //
  2944. // Parameters:
  2945. // szDestination: the buffer where the result of the pathappend will be held.
  2946. // cchDestBufSize: the size of szDestination (number of characters, not byes!).
  2947. // szAppend: the buffer where the path to be appended is held.
  2948. // cchAppBufSize: the size of szAppend (number of characters, not byes!).
  2949. // hInstall: windows installer session, used for logging only
  2950. //
  2951. ////////////////////////////////////////////////////////////////////////////////////
  2952. BOOL MUICchPathAppend(LPTSTR szDestination, UINT cchDestBufSize, LPTSTR szAppend, UINT cchAppBufSize, MSIHANDLE hInstall)
  2953. {
  2954. size_t cch1 = 0;
  2955. size_t cch2 = 0;
  2956. HRESULT hr = S_OK;
  2957. TCHAR tcMessage[BUFFER_SIZE] = {0};
  2958. if ((NULL == szDestination) || (NULL == szAppend) || (NULL == hInstall))
  2959. {
  2960. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MUICchPathAppend: Invalid paths specified or invalid windows installer session."));
  2961. if (SUCCEEDED(hr))
  2962. {
  2963. LogCustomActionInfo(hInstall, tcMessage);
  2964. }
  2965. return FALSE;
  2966. }
  2967. // get length of both strings
  2968. hr = StringCchLength(szDestination, cchDestBufSize, &cch1);
  2969. if (FAILED(hr))
  2970. {
  2971. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MUICchPathAppend: Invalid destination path specified."));
  2972. if (SUCCEEDED(hr))
  2973. {
  2974. LogCustomActionInfo(hInstall, tcMessage);
  2975. }
  2976. return FALSE;
  2977. }
  2978. hr = StringCchLength(szAppend, cchAppBufSize, &cch2);
  2979. if (FAILED(hr))
  2980. {
  2981. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MUICchPathAppend: Invalid source path specified."));
  2982. if (SUCCEEDED(hr))
  2983. {
  2984. LogCustomActionInfo(hInstall, tcMessage);
  2985. }
  2986. return FALSE;
  2987. }
  2988. if ((cch1 + cch2 + 2) > cchDestBufSize) // null terminator and a possible backslash
  2989. {
  2990. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MUICchPathAppend: final path would be too long."));
  2991. if (SUCCEEDED(hr))
  2992. {
  2993. LogCustomActionInfo(hInstall, tcMessage);
  2994. }
  2995. return FALSE;
  2996. }
  2997. // check for slashes at the start of the string that we are appending
  2998. if (szAppend[0] == TEXT('\\'))
  2999. {
  3000. // check for slashes at the end of the string to be appended, add if it is there, remove it
  3001. if (szDestination[cch1-1] == TEXT('\\'))
  3002. {
  3003. szDestination[cch1-1] = UNICODE_NULL;
  3004. }
  3005. }
  3006. else
  3007. {
  3008. // check for slashes at the end of the string to be appended, add it if it is not there
  3009. if (szDestination[cch1-1] != TEXT('\\'))
  3010. {
  3011. szDestination[cch1] = TEXT('\\');
  3012. szDestination[cch1+1] = UNICODE_NULL;
  3013. }
  3014. }
  3015. hr = StringCchCat(szDestination, cchDestBufSize, szAppend);
  3016. if (FAILED(hr))
  3017. {
  3018. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MUICchPathAppend: Failed to form new path."));
  3019. if (SUCCEEDED(hr))
  3020. {
  3021. LogCustomActionInfo(hInstall, tcMessage);
  3022. }
  3023. return FALSE;
  3024. }
  3025. return TRUE;
  3026. }
  3027. ////////////////////////////////////////////////////////////////////////////////////
  3028. //
  3029. // MUIReportInfoEvent
  3030. //
  3031. // This function logs the supplied event message to the system event log
  3032. //
  3033. ////////////////////////////////////////////////////////////////////////////////////
  3034. BOOL MUIReportInfoEvent(DWORD dwEventID, TCHAR *szLanguage, UINT cchBufSize, MSIHANDLE hInstall)
  3035. {
  3036. HRESULT hr = S_OK;
  3037. size_t cch = 0;
  3038. HANDLE hLog = NULL;
  3039. TCHAR szUserName[UNLEN+1];
  3040. TCHAR *pszDomain = NULL;
  3041. PSID psidUser = NULL;
  3042. DWORD cbSid = 0;
  3043. DWORD cbDomain = 0;
  3044. DWORD cbUser = UNLEN + 1;
  3045. SID_NAME_USE snu;
  3046. BOOL bResult = TRUE;
  3047. // check input parameters
  3048. if ((NULL == hInstall) || (NULL == szLanguage) || (cchBufSize > BUFFER_SIZE))
  3049. {
  3050. bResult = FALSE;
  3051. goto Exit;
  3052. }
  3053. hr = StringCchLength(szLanguage, cchBufSize, &cch);
  3054. if (FAILED(hr))
  3055. {
  3056. bResult = FALSE;
  3057. goto Exit;
  3058. }
  3059. // check to see if the registry key exists for the event source we are going to use
  3060. // if it does not exist, we create it
  3061. if (!MUICheckEventSource(hInstall))
  3062. {
  3063. bResult = FALSE;
  3064. goto Exit;
  3065. }
  3066. // register the event source, first try not having written to the registry
  3067. hLog = RegisterEventSource(NULL, REGOPT_EVENTSOURCE_NAME);
  3068. if (NULL == hLog)
  3069. {
  3070. bResult = FALSE;
  3071. goto Exit;
  3072. }
  3073. // get the sid from the current thread token, this should be the current user who's
  3074. // running the installation
  3075. if (!GetUserName(szUserName, &cbUser))
  3076. {
  3077. bResult = FALSE;
  3078. goto Exit;
  3079. }
  3080. // convert user name to its security identifier, first time to get buffer size, second time
  3081. // to actually get the Sid
  3082. if (!LookupAccountName(NULL, szUserName, NULL, &cbSid, NULL, &cbDomain, &snu))
  3083. {
  3084. // allocate the buffers
  3085. psidUser = (PSID) LocalAlloc(LPTR, cbSid);
  3086. if (NULL == psidUser)
  3087. {
  3088. bResult = FALSE;
  3089. goto Exit;
  3090. }
  3091. pszDomain = (TCHAR*) LocalAlloc(LPTR, cbDomain * sizeof(TCHAR));
  3092. if (NULL == pszDomain)
  3093. {
  3094. bResult = FALSE;
  3095. goto Exit;
  3096. }
  3097. if (!LookupAccountName(NULL, szUserName, psidUser, &cbSid, pszDomain, &cbDomain, &snu))
  3098. {
  3099. bResult = FALSE;
  3100. goto Exit;
  3101. }
  3102. }
  3103. if (!ReportEvent(hLog,
  3104. EVENTLOG_INFORMATION_TYPE,
  3105. 0,
  3106. dwEventID,
  3107. psidUser,
  3108. 1,
  3109. 0,
  3110. (LPCWSTR *) &szLanguage,
  3111. NULL))
  3112. {
  3113. bResult = FALSE;
  3114. goto Exit;
  3115. }
  3116. Exit:
  3117. if (NULL != hLog)
  3118. {
  3119. if (!DeregisterEventSource(hLog))
  3120. {
  3121. bResult = FALSE;
  3122. }
  3123. }
  3124. if (psidUser)
  3125. {
  3126. if (LocalFree(psidUser))
  3127. {
  3128. bResult = FALSE;
  3129. }
  3130. }
  3131. if (pszDomain)
  3132. {
  3133. if (LocalFree(pszDomain))
  3134. {
  3135. bResult = FALSE;
  3136. }
  3137. }
  3138. return bResult;
  3139. }
  3140. ////////////////////////////////////////////////////////////////////////////////////
  3141. //
  3142. // MUICheckEventSource
  3143. //
  3144. // This function verifies that the intl.cpl is set up to report events, and
  3145. // returns TRUE if it is.
  3146. //
  3147. ////////////////////////////////////////////////////////////////////////////////////
  3148. BOOL MUICheckEventSource(MSIHANDLE hInstall)
  3149. {
  3150. HKEY hk;
  3151. DWORD dwData;
  3152. TCHAR tcMessage[BUFFER_SIZE] = {0};
  3153. TCHAR szPath[MAX_PATH+1] = {0};
  3154. HRESULT hr = S_OK;
  3155. size_t cch = 0;
  3156. size_t cb = 0;
  3157. if (!GetSystemWindowsDirectory(szPath, MAX_PATH+1))
  3158. {
  3159. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MUICheckEventSource: Unable to find the Windows directory, GetSystemWindowsDirectory returned %d."), GetLastError());
  3160. if (SUCCEEDED(hr))
  3161. {
  3162. LogCustomActionInfo(hInstall, tcMessage);
  3163. }
  3164. return FALSE;
  3165. }
  3166. // check retrieved winpath, it needs to have space to append "system32\intl.cpl" at the end
  3167. hr = StringCchLength(szPath, ARRAYSIZE(szPath), &cch);
  3168. if (FAILED(hr) || ((cch + 17) >= MAX_PATH+1))
  3169. {
  3170. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MUICheckEventSource: cannot find system windows path."));
  3171. if (SUCCEEDED(hr))
  3172. {
  3173. LogCustomActionInfo(hInstall, tcMessage);
  3174. }
  3175. return FALSE;
  3176. }
  3177. // append system32\intl.cpl
  3178. if (!MUICchPathAppend(szPath, ARRAYSIZE(szPath), TEXT("system32\\intl.cpl"), 18, hInstall))
  3179. {
  3180. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MUICheckEventSource: cannot form path to muisetup.exe."));
  3181. if (SUCCEEDED(hr))
  3182. {
  3183. LogCustomActionInfo(hInstall, tcMessage);
  3184. }
  3185. return FALSE;
  3186. }
  3187. // get the byte count for RegSetValueEx
  3188. hr = StringCbLength(szPath, MAX_PATH+1 * sizeof(TCHAR), &cb);
  3189. if (FAILED(hr))
  3190. {
  3191. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MUICheckEventSource: cannot form path to muisetup.exe."));
  3192. if (SUCCEEDED(hr))
  3193. {
  3194. LogCustomActionInfo(hInstall, tcMessage);
  3195. }
  3196. return FALSE;
  3197. }
  3198. // Add intl.cpl source name as a subkey under the System
  3199. // key in the EventLog registry key. This should be there already, but add it anyways if it is not.
  3200. if (ERROR_SUCCESS != RegCreateKeyEx(HKEY_LOCAL_MACHINE, REGOPT_EVENTSOURCE, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hk, NULL))
  3201. {
  3202. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MUICheckEventSource: cannot add Intl.cpl event source regkey."));
  3203. if (SUCCEEDED(hr))
  3204. {
  3205. LogCustomActionInfo(hInstall, tcMessage);
  3206. }
  3207. return FALSE;
  3208. }
  3209. // Add the name to the EventMessageFile subkey.
  3210. if (ERROR_SUCCESS != RegSetValueEx(hk, TEXT("EventMessageFile"), 0, REG_EXPAND_SZ, (LPBYTE) szPath, cb))
  3211. {
  3212. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MUICheckEventSource: cannot add event source Event message file information."));
  3213. if (SUCCEEDED(hr))
  3214. {
  3215. LogCustomActionInfo(hInstall, tcMessage);
  3216. }
  3217. RegCloseKey(hk);
  3218. return FALSE;
  3219. }
  3220. // Set the supported event types in the TypesSupported subkey.
  3221. dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
  3222. if (ERROR_SUCCESS != RegSetValueEx(hk, TEXT("TypesSupported"), 0, REG_DWORD, (LPBYTE) &dwData, sizeof(DWORD)))
  3223. {
  3224. hr = StringCchPrintf(tcMessage, ARRAYSIZE(tcMessage), TEXT("MUICheckEventSource: cannot add event source TypeSupported information."));
  3225. if (SUCCEEDED(hr))
  3226. {
  3227. LogCustomActionInfo(hInstall, tcMessage);
  3228. }
  3229. RegCloseKey(hk);
  3230. return FALSE;
  3231. }
  3232. RegCloseKey(hk);
  3233. return TRUE;
  3234. }
  3235. ////////////////////////////////////////////////////////////////////////////////////
  3236. //
  3237. // GetDotDefaultUILanguage
  3238. //
  3239. // Retrieve the UI language stored in the HKCU\.Default.
  3240. // This is the default UI language for new users.
  3241. // This function sends an INFORMATION-type log message record to the opened
  3242. // windows installer session so that it can be logged by the installer
  3243. // if logging is enabled.
  3244. //
  3245. ////////////////////////////////////////////////////////////////////////////////////
  3246. LANGID GetDotDefaultUILanguage(MSIHANDLE hInstall)
  3247. {
  3248. HKEY hKey;
  3249. DWORD dwKeyType;
  3250. DWORD dwSize;
  3251. BOOL success = FALSE;
  3252. TCHAR szBuffer[BUFFER_SIZE] = {0};
  3253. LANGID langID;
  3254. // Get the value in .DEFAULT.
  3255. if (RegOpenKeyEx( HKEY_USERS,
  3256. TEXT(".DEFAULT\\Control Panel\\Desktop"),
  3257. 0L,
  3258. KEY_READ,
  3259. &hKey ) == ERROR_SUCCESS)
  3260. {
  3261. dwSize = sizeof(szBuffer);
  3262. if (RegQueryValueEx( hKey,
  3263. TEXT("MultiUILanguageId"),
  3264. 0L,
  3265. &dwKeyType,
  3266. (LPBYTE)szBuffer,
  3267. &dwSize) == ERROR_SUCCESS)
  3268. {
  3269. if (dwKeyType == REG_SZ)
  3270. {
  3271. langID = (LANGID)_tcstol(szBuffer, NULL, 16);
  3272. success = TRUE;
  3273. }
  3274. }
  3275. RegCloseKey(hKey);
  3276. }
  3277. if (!success)
  3278. {
  3279. langID = GetSystemDefaultUILanguage();
  3280. }
  3281. return (langID);
  3282. }
  3283. ////////////////////////////////////////////////////////////////////////////////////
  3284. //
  3285. // IsOEMSystem
  3286. //
  3287. // Retrieve the Product ID stored in HKLM\Software\Microsoft\Windows NT\CurrentVersion
  3288. // If the product ID contains the string "OEM", it is determined to be an OEM system.
  3289. //
  3290. ////////////////////////////////////////////////////////////////////////////////////
  3291. BOOL IsOEMSystem()
  3292. {
  3293. HKEY hKey;
  3294. DWORD dwKeyType;
  3295. DWORD dwSize;
  3296. BOOL bRet = FALSE;
  3297. TCHAR szBuffer[BUFFER_SIZE] = {0};
  3298. TCHAR szOEM[] = TEXT("OEM");
  3299. if (RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  3300. TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion"),
  3301. 0L,
  3302. KEY_READ,
  3303. &hKey ) == ERROR_SUCCESS)
  3304. {
  3305. dwSize = sizeof(szBuffer);
  3306. if (RegQueryValueEx( hKey,
  3307. TEXT("ProductId"),
  3308. 0L,
  3309. &dwKeyType,
  3310. (LPBYTE)szBuffer,
  3311. &dwSize) == ERROR_SUCCESS)
  3312. {
  3313. if (dwKeyType == REG_SZ)
  3314. {
  3315. if (StrStrI((LPCTSTR)szBuffer, (LPCTSTR)szOEM) != NULL)
  3316. {
  3317. bRet = TRUE;
  3318. }
  3319. }
  3320. }
  3321. RegCloseKey(hKey);
  3322. }
  3323. return bRet;
  3324. }