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.

1676 lines
51 KiB

  1. //
  2. // Module: custom.cpp
  3. //
  4. // Purpose: TsClient MSI custom action code
  5. //
  6. // Copyright(C) Microsoft Corporation 1999-2000
  7. //
  8. // Author: nadima
  9. //
  10. //
  11. #include <custom.h>
  12. #include <stdio.h>
  13. #include <reglic.h>
  14. #include "setuplib.h"
  15. // Unicode wrapper
  16. #include "wraputl.h"
  17. #define TERMINAL_SERVER_CLIENT_REGKEY _T("Software\\Microsoft\\Terminal Server Client")
  18. #define TERMINAL_SERVER_CLIENT_BACKUP_REGKEY _T("Software\\Microsoft\\Terminal Server Client (Backup)")
  19. #define LOGFILE_STR _T("MsiLogFile")
  20. // MSI Folder Names
  21. #define SYSTEM32_IDENTIFIER _T("SystemFolder")
  22. #define PROGRAMMENUFOLDER_INDENTIFIER _T("ProgramMenuFolder")
  23. #define ACCESSORIES_IDENTIFIER _T("AccessoriesMenuFolder")
  24. #define COMMUNICATIONS_IDENTIFIER _T("CommunicationsMenuFolder")
  25. #define INSTALLATION_IDENTIFIER _T("INSTALLDIR")
  26. // MSI Properties
  27. #define ALLUSERS _T("ALLUSERS")
  28. // Assumed max length of shortcut file name.
  29. #define MAX_LNK_FILE_NAME_LEN 50
  30. // ERROR_SUCCESS will let MSI continue with the default value.
  31. // ERROR_INSTALL_FAILURE will block installation.
  32. #define NONCRITICAL_ERROR_RETURN ERROR_SUCCESS
  33. // Require comctl32.dll version 4.70 and above
  34. #define MIN_COMCTL32_VERSION MAKELONG(70,4)
  35. HINSTANCE g_hInstance = (HINSTANCE) NULL;
  36. HANDLE g_hLogFile = INVALID_HANDLE_VALUE;
  37. #ifdef UNIWRAP
  38. //It's ok to have a global unicode wrapper
  39. //class. All it does is sets up the g_bRunningOnNT
  40. //flag so it can be shared by multiple instances
  41. //also it is only used from DllMain so there
  42. //are no problems with re-entrancy
  43. CUnicodeWrapper g_uwrp;
  44. #endif
  45. void RestoreRegAcl(VOID);
  46. //
  47. // DllMain entry point
  48. //
  49. BOOL WINAPI DllMain(HANDLE hModule, DWORD ul_reason_for_call,
  50. LPVOID lpReserved)
  51. {
  52. if (NULL == g_hInstance)
  53. {
  54. g_hInstance = (HINSTANCE)hModule;
  55. }
  56. switch (ul_reason_for_call)
  57. {
  58. case DLL_PROCESS_ATTACH:
  59. {
  60. //
  61. // Open the tsclient registry key to get the log file name
  62. //
  63. LONG status;
  64. HKEY hKey;
  65. TCHAR buffer[MAX_PATH];
  66. DWORD bufLen;
  67. memset(buffer, 0, sizeof(buffer));
  68. bufLen = sizeof(buffer); //size in bytes needed
  69. #ifdef UNIWRAP
  70. //UNICODE Wrapper intialization has to happen first,
  71. //before anything ELSE. Even DC_BEGIN_FN, which does tracing
  72. g_uwrp.InitializeWrappers();
  73. #endif
  74. status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  75. TERMINAL_SERVER_CLIENT_REGKEY,
  76. 0, KEY_ALL_ACCESS, &hKey);
  77. if(ERROR_SUCCESS == status)
  78. {
  79. //
  80. // Query the tsclient optional logfile path
  81. //
  82. status = RegQueryValueEx(hKey, LOGFILE_STR,
  83. NULL, NULL, (BYTE *)buffer, &bufLen);
  84. if(ERROR_SUCCESS == status)
  85. {
  86. g_hLogFile = CreateFile(buffer,
  87. GENERIC_READ | GENERIC_WRITE,
  88. 0,
  89. NULL,
  90. OPEN_ALWAYS,
  91. FILE_ATTRIBUTE_NORMAL,
  92. NULL);
  93. if(g_hLogFile != INVALID_HANDLE_VALUE)
  94. {
  95. //Always append to the end of the log file
  96. SetFilePointer(g_hLogFile,
  97. 0,
  98. 0,
  99. FILE_END);
  100. }
  101. else
  102. {
  103. DBGMSG((_T("CreateFile for log file failed %d %d"),
  104. g_hLogFile, GetLastError()));
  105. }
  106. }
  107. else
  108. {
  109. DBGMSG((_T("RegQueryValueEx for log file failed %d %d"),
  110. status, GetLastError()));
  111. }
  112. RegCloseKey(hKey);
  113. }
  114. if(g_hLogFile != INVALID_HANDLE_VALUE)
  115. {
  116. DBGMSG((_T("Log file opened by new process attach")));
  117. DBGMSG((_T("-------------------------------------")));
  118. }
  119. DBGMSG((_T("custom.dll:Dllmain PROCESS_ATTACH")));
  120. }
  121. break;
  122. case DLL_THREAD_ATTACH:
  123. {
  124. DBGMSG((_T("custom.dll:Dllmain THREAD ATTACH")));
  125. }
  126. break;
  127. case DLL_THREAD_DETACH:
  128. {
  129. }
  130. break;
  131. case DLL_PROCESS_DETACH:
  132. {
  133. DBGMSG((_T("custom.dll:Dllmain THREAD DETACH. Closing log file")));
  134. CloseHandle(g_hLogFile);
  135. g_hLogFile = INVALID_HANDLE_VALUE;
  136. }
  137. break;
  138. }
  139. DBGMSG(((_T("In custom.dll DllMain. Reason: %d")), ul_reason_for_call));
  140. return TRUE;
  141. }
  142. //
  143. // Check if should be silent to UI
  144. //
  145. // Returns TRUE if it is OK to display UI
  146. BOOL AllowDisplayUI(MSIHANDLE hInstall)
  147. {
  148. UINT status;
  149. TCHAR szResult[3];
  150. DWORD cchResult = 3;
  151. BOOL fAllowDisplayUI = FALSE;
  152. DBGMSG((_T("Entering: AllowDisplayUI")));
  153. status = MsiGetProperty(hInstall, _T("UILevel"), szResult, &cchResult);
  154. if (ERROR_SUCCESS == status)
  155. {
  156. DBGMSG((_T("AllowDisplayUI: MsiGetProperty for UILevel succeeded, got %s"),
  157. szResult));
  158. if (szResult[0] != TEXT('2'))
  159. {
  160. fAllowDisplayUI = TRUE;
  161. }
  162. }
  163. else
  164. {
  165. DBGMSG((_T("AllowDisplayUI: MsiGetProperty for UILevel FAILED, Status: 0x%x"),
  166. status));
  167. }
  168. DBGMSG((_T("Leaving: AllowDisplayUI ret:%d"), fAllowDisplayUI));
  169. return fAllowDisplayUI;
  170. }
  171. /**PROC+************************************************************/
  172. /* Name: RDCSetupInit */
  173. /* */
  174. /* Type: Custom Action */
  175. /* */
  176. /* Purpose: Do any initialization for the setup here. */
  177. /* */
  178. /* Returns: Refer to MSI help. */
  179. /* */
  180. /* Params: Refer to MSI help. */
  181. /* */
  182. /**PROC-************************************************************/
  183. UINT __stdcall RDCSetupInit(MSIHANDLE hInstall)
  184. {
  185. DBGMSG((_T("Entering: RDCSetupInit")));
  186. DBGMSG((_T("Leaving : RDCSetupInit")));
  187. return ERROR_SUCCESS;
  188. }
  189. /**PROC+************************************************************/
  190. /* Name: RDCSetupCheckOsVer */
  191. /* */
  192. /* Type: Custom Action */
  193. /* */
  194. /* Purpose: Block install on certain OS's */
  195. /* */
  196. /* Returns: Refer to MSI help. */
  197. /* */
  198. /* Params: Refer to MSI help. */
  199. /* */
  200. /**PROC-************************************************************/
  201. UINT __stdcall RDCSetupCheckOsVer(MSIHANDLE hInstall)
  202. {
  203. DBGMSG((_T("Entering: RDCSetupCheckOsVer")));
  204. OSVERSIONINFO osVer;
  205. memset(&osVer, 0x0, sizeof(OSVERSIONINFO));
  206. osVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  207. if(0 == GetVersionEx(&osVer))
  208. {
  209. return ERROR_SUCCESS;
  210. }
  211. else
  212. {
  213. DBGMSG((_T("RDCSetupCheckOsVer. OS version OK to install")));
  214. DBGMSG((_T("RDCSetupCheckOsVer - now check comctl32 version")));
  215. if(!CheckComctl32Version())
  216. {
  217. DBGMSG((_T("RDCSetupCheckOsVer. comctl32.dll check failed. Block on this os")));
  218. TCHAR szBlockOnPlatform[MAX_PATH];
  219. TCHAR szError[MAX_PATH];
  220. LoadString(g_hInstance, IDS_BLOCKCOMCTL32,
  221. szBlockOnPlatform,SIZECHAR(szBlockOnPlatform));
  222. LoadString(g_hInstance, IDS_ERROR,
  223. szError, SIZECHAR(szError));
  224. if (AllowDisplayUI(hInstall))
  225. {
  226. MessageBox(NULL, szBlockOnPlatform, szError, MB_OK|MB_ICONSTOP);
  227. }
  228. else
  229. {
  230. DBGMSG((_T("AllowDisplayUI returned False, not displaying msg box!")));
  231. }
  232. //Return an error to make msi abort the install.
  233. return ERROR_INVALID_FUNCTION;
  234. }
  235. else
  236. {
  237. DBGMSG((_T("RDCSetupCheckOsVer - passed all tests. OK")));
  238. return ERROR_SUCCESS;
  239. }
  240. }
  241. DBGMSG((_T("Leaving : RDCSetupCheckOsVer")));
  242. }
  243. /**PROC+************************************************************/
  244. /* Name: RDCSetupCheckTcpIp */
  245. /* */
  246. /* Type: Custom Action */
  247. /* */
  248. /* Purpose: Check if TCP/IP is installed in the machine. */
  249. /* */
  250. /* Returns: Refer to MSI help. */
  251. /* */
  252. /* Params: Refer to MSI help. */
  253. /* */
  254. /**PROC-************************************************************/
  255. UINT __stdcall RDCSetupCheckTcpIp(MSIHANDLE hInstall)
  256. {
  257. DWORD dwVersion, dwWindowsMajorVersion;
  258. DWORD dwWindowsMinorVersion, dwBuild;
  259. HKEY hKey = NULL;
  260. LONG lRet = 0;
  261. TCHAR lpTcpMsg[MAX_PATH] = _T(""), szError[MAX_PATH] = _T("");
  262. OSVERSIONINFO osVer;
  263. DBGMSG((_T("Entering: RDCSetupCheckTcpIp")));
  264. memset(&osVer, 0x0, sizeof(OSVERSIONINFO));
  265. osVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  266. if(0 == GetVersionEx(&osVer))
  267. {
  268. return ERROR_SUCCESS;
  269. }
  270. //Now search the appropriate registry key.
  271. LoadString(g_hInstance, IDS_ERR_TCP, lpTcpMsg,SIZECHAR(lpTcpMsg));
  272. LoadString(g_hInstance, IDS_WARNING, szError, SIZECHAR(szError));
  273. if(VER_PLATFORM_WIN32_WINDOWS == osVer.dwPlatformId )
  274. {
  275. //
  276. // Win95 check for TCP/IP
  277. //
  278. lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  279. (_T("Enum\\Network\\MSTCP")),
  280. 0, KEY_READ, &hKey);
  281. if(ERROR_SUCCESS != lRet)
  282. {
  283. if(hKey)
  284. {
  285. RegCloseKey(hKey);
  286. }
  287. if (AllowDisplayUI(hInstall))
  288. {
  289. MessageBox(NULL, lpTcpMsg, szError, MB_OK|MB_ICONWARNING);
  290. }
  291. else
  292. {
  293. DBGMSG((_T("AllowDisplayUI returned false, not displaying TCP/IP warning")));
  294. }
  295. return ERROR_SUCCESS;
  296. }
  297. }
  298. else if((VER_PLATFORM_WIN32_NT == osVer.dwPlatformId) &&
  299. (osVer.dwMajorVersion <= 4))
  300. {
  301. //
  302. // NT4 and below check for TCP/IP
  303. //
  304. lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  305. (_T("SYSTEM\\CurrentControlSet\\Services\\Tcpip")),
  306. 0, KEY_READ, &hKey);
  307. if( ERROR_SUCCESS != lRet)
  308. {
  309. if(hKey)
  310. {
  311. RegCloseKey(hKey);
  312. }
  313. if (AllowDisplayUI(hInstall))
  314. {
  315. MessageBox(NULL, lpTcpMsg, szError, MB_OK|MB_ICONWARNING);
  316. }
  317. else
  318. {
  319. DBGMSG((_T("AllowDisplayUI returned false, not displaying TCP/IP warning")));
  320. }
  321. return ERROR_SUCCESS;
  322. }
  323. }
  324. else if((VER_PLATFORM_WIN32_NT == osVer.dwPlatformId) &&
  325. (osVer.dwMajorVersion >= 5))
  326. {
  327. //
  328. // NT5+ check for TCP/IP
  329. //
  330. HRESULT hr = CheckNt5TcpIpInstalled();
  331. if(S_FALSE == hr)
  332. {
  333. if (AllowDisplayUI(hInstall))
  334. {
  335. MessageBox(NULL, lpTcpMsg, szError, MB_OK|MB_ICONWARNING);
  336. }
  337. else
  338. {
  339. DBGMSG((_T("AllowDisplayUI returned false, not displaying TCP/IP warning")));
  340. }
  341. if(hKey)
  342. {
  343. RegCloseKey(hKey);
  344. }
  345. return ERROR_SUCCESS;
  346. }
  347. }
  348. if(hKey)
  349. {
  350. RegCloseKey(hKey);
  351. }
  352. DBGMSG((_T("Leaving: RDCSetupCheckTcpIp")));
  353. return ERROR_SUCCESS;
  354. }
  355. /**PROC+************************************************************/
  356. /* Name: CheckNt5TcpIpInstalled */
  357. /* */
  358. /* Purpose: Find if TCP/IP is installed and running. */
  359. /* This function should be called only NT 5 or greater. */
  360. /* */
  361. /* Returns: S_OK if TCP/IP is installed and running */
  362. /* S_FALSE if TCP/IP is not installed or running */
  363. /* E_FAIL if a failure occurs. */
  364. /* */
  365. /* Params: None */
  366. /* */
  367. /**PROC-************************************************************/
  368. HRESULT CheckNt5TcpIpInstalled()
  369. {
  370. INetCfg * pnetCfg = NULL;
  371. INetCfgClass * pNetCfgClass = NULL;
  372. INetCfgComponent * pNetCfgComponentprot = NULL;
  373. DWORD dwCharacteristics;
  374. ULONG count = 0;
  375. HRESULT hResult;
  376. WCHAR wsz [2*MAX_PATH] = L"";
  377. BOOL bInit = FALSE;
  378. DBGMSG((_T("Entering: CheckNt5TcpIpInstalled")));
  379. hResult = CoInitialize(NULL);
  380. if(FAILED(hResult))
  381. {
  382. DBGMSG( (_T("CoInitialize() failed.")));
  383. goto Cleanup;
  384. }
  385. else
  386. {
  387. bInit = TRUE;
  388. }
  389. hResult = CoCreateInstance(CLSID_CNetCfg, NULL, CLSCTX_SERVER,
  390. IID_INetCfg, (LPVOID *)&pnetCfg);
  391. if((NULL == pnetCfg) || FAILED(hResult))
  392. {
  393. DBGMSG( (_T("CoCreateInstance() failed.")));
  394. goto Cleanup;
  395. }
  396. hResult = pnetCfg->Initialize(NULL);
  397. if(FAILED(hResult))
  398. {
  399. DBGMSG( (_T("pnetCfg->Initialize() failed.")));
  400. goto Cleanup;
  401. }
  402. hResult = pnetCfg->QueryNetCfgClass(&GUID_DEVCLASS_NETTRANS,
  403. IID_INetCfgClass,
  404. (void **)&pNetCfgClass);
  405. if(FAILED(hResult))
  406. {
  407. DBGMSG( (_T("QueryNetCfgClass() failed.")));
  408. goto Cleanup;
  409. }
  410. #ifdef UNICODE
  411. lstrcpy(wsz, NETCFG_TRANS_CID_MS_TCPIP);
  412. #else //UNICODE
  413. if (0 == MultiByteToWideChar(CP_ACP, 0,
  414. (LPCSTR)NETCFG_TRANS_CID_MS_TCPIP,
  415. -1, wsz, sizeof(wsz)/sizeof(WCHAR)))
  416. {
  417. DBGMSG( (_T("MultiByteToWideChar() failed.")));
  418. hResult = E_FAIL;
  419. goto Cleanup;
  420. }
  421. #endif //UNICODE
  422. hResult = pNetCfgClass->FindComponent(wsz,
  423. &pNetCfgComponentprot);
  424. if(hResult == S_FALSE)
  425. {
  426. DBGMSG( (_T("FindComponent() failed.")));
  427. goto Cleanup;
  428. }
  429. Cleanup:
  430. if(bInit)
  431. {
  432. CoUninitialize();
  433. }
  434. if(pNetCfgComponentprot)
  435. {
  436. pNetCfgComponentprot->Release();
  437. pNetCfgComponentprot = NULL;
  438. }
  439. if(pNetCfgClass)
  440. {
  441. pNetCfgClass->Release();
  442. pNetCfgClass = NULL;
  443. }
  444. if(pnetCfg != NULL)
  445. {
  446. pnetCfg->Uninitialize();
  447. pnetCfg->Release();
  448. pnetCfg = NULL;
  449. }
  450. DBGMSG((_T("Leaving: IsTCPIPInstalled")));
  451. return hResult;
  452. }
  453. /**PROC+************************************************************/
  454. /* Name: RDCSetupPreInstall */
  455. /* */
  456. /* Type: Custom Action */
  457. /* */
  458. /* Purpose: Does cleanup work during install. */
  459. /* */
  460. /* Returns: Refer to MSI help. */
  461. /* */
  462. /* Params: Refer to MSI help. */
  463. /* */
  464. /**PROC-************************************************************/
  465. UINT __stdcall RDCSetupPreInstall(MSIHANDLE hInstall)
  466. {
  467. BOOL fInstalling = FALSE;
  468. UINT retVal = 0;
  469. DBGMSG((_T("Entering: RDCSetupPreInstall")));
  470. //Figure out if we're installing or removing
  471. ASSERT(hInstall);
  472. DBGMSG((_T("RDCSetupPreInstall: Modifying dirs")));
  473. retVal = RDCSetupModifyDir(hInstall);
  474. DBGMSG((_T("RDCSetupPreInstall: Modifying dirs. DONE: %d"),
  475. retVal));
  476. //
  477. // This is pre-install if the product
  478. // is not installed then we are installing
  479. //
  480. fInstalling = !IsProductInstalled(hInstall);
  481. if(fInstalling)
  482. {
  483. DBGMSG((_T("RDCSetupPreInstall: We're installing")));
  484. TCHAR szProgmanPath[MAX_PATH];
  485. TCHAR szOldProgmanPath[MAX_PATH];
  486. DBGMSG((_T("RDCSetupPreInstall: Delete desktop shortcuts. START")));
  487. DeleteTSCDesktopShortcuts();
  488. DBGMSG((_T("RDCSetupPreInstall: Delete desktop shortcuts. DONE")));
  489. //Acme uninstall
  490. LoadString(g_hInstance, IDS_PROGMAN_GROUP, szProgmanPath,
  491. sizeof(szProgmanPath) / sizeof(TCHAR));
  492. DBGMSG((_T("RDCSetupPreInstall: DeleteTSCFromStartMenu: %s. START"),
  493. szProgmanPath));
  494. DeleteTSCFromStartMenu(szProgmanPath);
  495. DBGMSG((_T("RDCSetupPreInstall: DeleteTSCFromStartMenu: %s. DONE"),
  496. szProgmanPath));
  497. LoadString(g_hInstance, IDS_OLD_NAME, szOldProgmanPath,
  498. sizeof(szOldProgmanPath) / sizeof(TCHAR));
  499. DBGMSG((_T("RDCSetupPreInstall: DeleteTSCFromStartMenu: %s. START"),
  500. szOldProgmanPath));
  501. DeleteTSCFromStartMenu(szOldProgmanPath);
  502. DBGMSG((_T("RDCSetupPreInstall: DeleteTSCFromStartMenu: %s. DONE"),
  503. szOldProgmanPath));
  504. DBGMSG((_T("RDCSetupPreInstall: Uninstall ACME program files. START")));
  505. DeleteTSCProgramFiles();
  506. DBGMSG((_T("RDCSetupPreInstall: Uninstall ACME program files. DONE")));
  507. DBGMSG((_T("RDCSetupPreInstall: Uninstall TSCLIENT registry keys. START")));
  508. DeleteTSCRegKeys();
  509. DBGMSG((_T("RDCSetupPreInstall: Uninstall TSCLIENT registry keys. DONE")));
  510. }
  511. else
  512. {
  513. if(MsiGetMode(hInstall, MSIRUNMODE_MAINTENANCE))
  514. {
  515. DBGMSG((_T("MsiGetMode: MSIRUNMODE_MAINTENANCE returned TRUE.")));
  516. DBGMSG((_T("RDCSetupPreInstall: We're in maintenance mode")));
  517. }
  518. else
  519. {
  520. DBGMSG((_T("MsiGetMode: MSIRUNMODE_MAINTENANCE returned FALSE.")));
  521. DBGMSG((_T("RDCSetupPreInstall: We're not installing (removing)")));
  522. }
  523. }
  524. DBGMSG((_T("Leaving: RDCSetupPreInstall")));
  525. return ERROR_SUCCESS;
  526. }
  527. //
  528. // Run migration 'mstsc /migrate'
  529. //
  530. // This will fail silenty if mstsc.exe is not present
  531. //
  532. BOOL RDCSetupRunMigration(MSIHANDLE hInstall)
  533. {
  534. BOOL fRet = TRUE;
  535. PROCESS_INFORMATION pinfo;
  536. STARTUPINFO sinfo;
  537. TCHAR szMigratePathLaunch[MAX_PATH];
  538. TCHAR szInstallPath[MAX_PATH];
  539. TCHAR szMigrateCmdLine[] = _T("mstsc.exe /migrate");
  540. DWORD cchInstallPath = SIZECHAR(szInstallPath);
  541. UINT uiResult;
  542. HRESULT hr;
  543. // Get the path to the installation directory.
  544. uiResult = MsiGetTargetPath(
  545. hInstall,
  546. INSTALLATION_IDENTIFIER,
  547. szInstallPath,
  548. &cchInstallPath);
  549. if (uiResult != ERROR_SUCCESS) {
  550. DBGMSG((_T("Error: MsiGetTargetPath returned 0x%x."), uiResult));
  551. fRet = FALSE;
  552. goto Exit;
  553. }
  554. DBGMSG((_T("Path to installation directory is %s"), szInstallPath));
  555. // Concatenate the installation directory and the mstsc /migrate command
  556. // so that we can call CreateProcess.
  557. hr = StringCchPrintf(
  558. szMigratePathLaunch,
  559. SIZECHAR(szMigratePathLaunch),
  560. _T("%s%s"),
  561. szInstallPath,
  562. _T("mstsc.exe"));
  563. if (FAILED(hr)) {
  564. DBGMSG((_T("Error: Failed to construct command line for CreateProcess. hr = 0x%x"), hr));
  565. goto Exit;
  566. }
  567. //
  568. // Start registry and connection file migration
  569. //
  570. ZeroMemory(&sinfo, sizeof(sinfo));
  571. sinfo.cb = sizeof(sinfo);
  572. fRet = CreateProcess(szMigratePathLaunch, // name of executable module
  573. szMigrateCmdLine, // command line string
  574. NULL, // SD
  575. NULL, // SD
  576. FALSE, // handle inheritance option
  577. CREATE_NEW_PROCESS_GROUP, // creation flags
  578. NULL, // new environment block
  579. NULL, // current directory name
  580. &sinfo, // startup information
  581. &pinfo); // process information
  582. if (fRet) {
  583. DBGMSG((_T("RDCSetupRunMigration: Started mstsc.exe /migrate")));
  584. }
  585. else {
  586. DBGMSG((_T("RDCSetupRunMigration: Failed to start mstsc.exe /migrate: %d"), GetLastError()));
  587. }
  588. Exit:
  589. return fRet;
  590. }
  591. /**PROC+************************************************************/
  592. /* Name: RDCSetupPostInstall */
  593. /* */
  594. /* Type: Custom Action */
  595. /* */
  596. /* Purpose: Do work after MSI has completed */
  597. /* could be after an uninstall completes, get MSI prop */
  598. /* to determine that */
  599. /* */
  600. /* Returns: Refer to MSI help. */
  601. /* */
  602. /* Params: Refer to MSI help. */
  603. /* */
  604. /**PROC-************************************************************/
  605. UINT __stdcall RDCSetupPostInstall(MSIHANDLE hInstall)
  606. {
  607. BOOL fInstalling = FALSE;
  608. DBGMSG((_T("Entering: RDCSetupPostInstall")));
  609. ASSERT(hInstall);
  610. //
  611. // This is post install if the product is installed
  612. // then we are 'installing' otherwise we were
  613. // removing.
  614. //
  615. fInstalling = IsProductInstalled(hInstall);
  616. if(fInstalling)
  617. {
  618. DBGMSG((_T("RDCSetupPostInstall: We're installing")));
  619. //Add the MsLicensingReg key and ACL it
  620. //This will only happen on NT (it's not needed on 9x)
  621. //and will not (cannot) work if you're not admin
  622. DBGMSG((_T("Setting up MSLicensing key...")));
  623. if(SetupMSLicensingKey())
  624. {
  625. DBGMSG((_T("Setting up MSLicensing key...SUCCEEDED")));
  626. }
  627. else
  628. {
  629. DBGMSG((_T("Setting up MSLicensing key...FAILED")));
  630. }
  631. //
  632. // Migrate user settings (will only run if MSTSC.EXE was successfully
  633. // installed).
  634. //
  635. if (RDCSetupRunMigration(hInstall))
  636. {
  637. DBGMSG((_T("RDCSetupRunMigration...SUCCEEDED")));
  638. }
  639. else
  640. {
  641. DBGMSG((_T("RDCSetupRunMigration...FAILED")));
  642. }
  643. }
  644. else
  645. {
  646. RestoreRegAcl();
  647. DBGMSG((_T("RDCSetupPostInstall: We're not installing (removing)")));
  648. //We're uninstalling
  649. //Delete the bitmap cache folder
  650. }
  651. DBGMSG((_T("Leaving: RDCSetupPostInstall")));
  652. return ERROR_SUCCESS;
  653. }
  654. //Return true if we're installing
  655. //False if we're uninstalling
  656. BOOL IsProductInstalled(MSIHANDLE hInstall)
  657. {
  658. ASSERT(hInstall);
  659. TCHAR szProdCode[MAX_PATH];
  660. DWORD dwCharCount = sizeof(szProdCode)/sizeof(TCHAR);
  661. UINT status;
  662. status = MsiGetProperty(hInstall,
  663. _T("ProductCode"),
  664. szProdCode,
  665. &dwCharCount);
  666. if(ERROR_SUCCESS == status)
  667. {
  668. DBGMSG((_T("MsiGetProperty returned product code %s"),
  669. szProdCode));
  670. INSTALLSTATE insState = MsiQueryProductState( szProdCode );
  671. DBGMSG((_T("MsiQueryProductState returned: %d"),
  672. (DWORD)insState));
  673. if(INSTALLSTATE_DEFAULT == insState)
  674. {
  675. DBGMSG((_T("Product installed. IsProductInstalled return TRUE")));
  676. return TRUE;
  677. }
  678. else
  679. {
  680. DBGMSG((_T("Product not installed. IsProductInstalled return FALSE")));
  681. return FALSE;
  682. }
  683. }
  684. else
  685. {
  686. DBGMSG((_T("MsiGetProperty for ProductCode failed: %d %d"),
  687. status, GetLastError()));
  688. return FALSE;
  689. }
  690. }
  691. //
  692. // Check that comctl32.dll has a sufficiently
  693. // high version number (4.70).
  694. //
  695. // Return - TRUE - version ok, allow install
  696. // FALSE - version bad (or fail) block install
  697. //
  698. BOOL CheckComctl32Version()
  699. {
  700. DWORD dwFileVerInfoSize;
  701. PBYTE pVerInfo = NULL;
  702. VS_FIXEDFILEINFO* pFixedFileInfo = NULL;
  703. BOOL bRetVal = FALSE;
  704. UINT len = 0;
  705. DBGMSG((_T("Entering: CheckComctl32Version")));
  706. //
  707. // USE Ansi versions of GetFileVersionInfo calls
  708. // because we don't have unicode wrappers for them
  709. //
  710. dwFileVerInfoSize = GetFileVersionInfoSizeA("comctl32.dll",
  711. NULL);
  712. if(!dwFileVerInfoSize)
  713. {
  714. DBGMSG((_T("GetFileVersionInfoSize for comctl32.dll failed: %d %d"),
  715. dwFileVerInfoSize, GetLastError()));
  716. }
  717. pVerInfo = (PBYTE) LocalAlloc(LPTR, dwFileVerInfoSize);
  718. if(pVerInfo)
  719. {
  720. if(GetFileVersionInfoA("comctl32.dll",
  721. NULL,
  722. dwFileVerInfoSize,
  723. (LPVOID)pVerInfo ))
  724. {
  725. DBGMSG((_T("GetFileVersionInfo: succeeded")));
  726. pFixedFileInfo = NULL;
  727. if(VerQueryValueA(pVerInfo,
  728. "\\", //get root version info block
  729. (LPVOID*)&pFixedFileInfo,
  730. &len ) && len)
  731. {
  732. DBGMSG((_T("comctl32.dll filever is 0x%x-0x%x"),
  733. pFixedFileInfo->dwFileVersionMS,
  734. pFixedFileInfo->dwFileVersionLS));
  735. if(pFixedFileInfo->dwFileVersionMS >= MIN_COMCTL32_VERSION)
  736. {
  737. DBGMSG((_T("Sufficently new comctl32.dll found. Allow install")))
  738. bRetVal = TRUE;
  739. }
  740. else
  741. {
  742. DBGMSG((_T("comctl32.dll too old block install")))
  743. bRetVal = FALSE;
  744. }
  745. }
  746. else
  747. {
  748. DBGMSG((_T("VerQueryValue: failed len:%d gle:%d"),
  749. len,
  750. GetLastError()));
  751. bRetVal = FALSE;
  752. goto BAIL_OUT;
  753. }
  754. }
  755. else
  756. {
  757. DBGMSG((_T("GetFileVersionInfo: failed %d"),
  758. GetLastError()));
  759. bRetVal = FALSE;
  760. goto BAIL_OUT;
  761. }
  762. }
  763. else
  764. {
  765. DBGMSG((_T("LocalAlloc for %d bytes of ver info failed"),
  766. dwFileVerInfoSize));
  767. bRetVal = FALSE;
  768. goto BAIL_OUT;
  769. }
  770. BAIL_OUT:
  771. DBGMSG((_T("Leaving: CheckComctl32Version")));
  772. if(pVerInfo)
  773. {
  774. LocalFree(pVerInfo);
  775. pVerInfo = NULL;
  776. }
  777. return bRetVal;
  778. }
  779. UINT RDCSetupModifyDir(MSIHANDLE hInstall)
  780. {
  781. UINT uReturn;
  782. int iAccessories;
  783. int iCommunications;
  784. TCHAR szAccessories[MAX_PATH];
  785. TCHAR szCommunications[MAX_PATH];
  786. TCHAR szProgram[MAX_PATH];
  787. TCHAR szFullAccessories[MAX_PATH];
  788. TCHAR szFullCommunications[MAX_PATH];
  789. OSVERSIONINFO osVer;
  790. DWORD dwSize;
  791. DBGMSG((_T("Entering: RDCSetupModifyDir")));
  792. //
  793. // OS Version
  794. //
  795. ZeroMemory( &osVer, sizeof( osVer ) );
  796. osVer.dwOSVersionInfoSize = sizeof( osVer );
  797. if (!GetVersionEx(&osVer))
  798. {
  799. DBGMSG( (TEXT("RDCSetupModifyDir: GetVersionEx failed.")) );
  800. return(NONCRITICAL_ERROR_RETURN);
  801. }
  802. if (osVer.dwMajorVersion >= 5)
  803. {
  804. DBGMSG((TEXT("RDCSetupModifyDir: Ver >= 5. No need to apply the altertnate path.")));
  805. return(ERROR_SUCCESS);
  806. }
  807. //
  808. // Get ProgramMenuFolder
  809. //
  810. dwSize = sizeof( szProgram ) / sizeof( TCHAR );
  811. uReturn = MsiGetProperty(hInstall,PROGRAMMENUFOLDER_INDENTIFIER,
  812. szProgram,&dwSize);
  813. if ( ERROR_SUCCESS != uReturn )
  814. {
  815. DBGMSG((TEXT("RDCSetupModifyDir: MsiGetProperty failed. %d"),
  816. uReturn));
  817. return NONCRITICAL_ERROR_RETURN;
  818. }
  819. //
  820. // Load String
  821. //
  822. iAccessories = LoadString(g_hInstance, IDS_ACCESSORIES, szAccessories,
  823. sizeof(szAccessories)/sizeof(TCHAR)-1);
  824. if (!iAccessories)
  825. {
  826. DBGMSG((TEXT("RDCSetupModifyDir: IDS_ACCESSORIES failed.")));
  827. return NONCRITICAL_ERROR_RETURN;
  828. }
  829. iCommunications = LoadString(g_hInstance, IDS_COMMUNICATIONS, szCommunications,
  830. sizeof(szCommunications)/sizeof(TCHAR)-1);
  831. if (!iCommunications)
  832. {
  833. DBGMSG((TEXT("RDCSetupModifyDir: IDS_COMMUNICATIONS failed.")));
  834. return NONCRITICAL_ERROR_RETURN;
  835. }
  836. //
  837. // Check Length
  838. //
  839. if (MAX_PATH < lstrlen( szProgram ) +
  840. iAccessories + 1 + iCommunications + 1 + MAX_LNK_FILE_NAME_LEN + 1 )
  841. {
  842. DBGMSG((TEXT( "RDCSetupModifyDir: Too long path." )));
  843. return NONCRITICAL_ERROR_RETURN;
  844. }
  845. //
  846. // Make Full Path
  847. //
  848. memset(szFullAccessories, 0, sizeof(szFullAccessories));
  849. memset(szFullCommunications, 0, sizeof(szFullCommunications));
  850. //
  851. // Use lstrcat as that has unicode wrappers
  852. //
  853. lstrcat(szFullAccessories, szProgram);
  854. lstrcat(szFullAccessories, szAccessories);
  855. lstrcat(szFullAccessories, _T("\\"));
  856. lstrcat(szFullCommunications, szFullAccessories);
  857. lstrcat(szFullCommunications, szCommunications);
  858. lstrcat(szFullCommunications, _T("\\"));
  859. //
  860. // Set Directory
  861. //
  862. uReturn = MsiSetTargetPath(hInstall,
  863. ACCESSORIES_IDENTIFIER,
  864. szFullAccessories);
  865. if (ERROR_SUCCESS != uReturn)
  866. {
  867. DBGMSG ((TEXT("RDCSetupModifyDir: SetTargetPathACCESSORIES_IDENTIFIER failed.")));
  868. return NONCRITICAL_ERROR_RETURN;
  869. }
  870. uReturn = MsiSetTargetPath(hInstall,
  871. COMMUNICATIONS_IDENTIFIER,
  872. szFullCommunications);
  873. if (ERROR_SUCCESS != uReturn)
  874. {
  875. DBGMSG( (TEXT( "RDCSetupModifyDir: COMMUNICATIONS_IDENTIFIER failed.")));
  876. return NONCRITICAL_ERROR_RETURN;
  877. }
  878. DBGMSG((_T("Leaving: RDCSetupModifyDir")));
  879. return ERROR_SUCCESS;
  880. }
  881. //*****************************************************************************
  882. //
  883. // CopyRegistryValues
  884. //
  885. // Copies all the values from a registry key to the other.
  886. //
  887. //*****************************************************************************
  888. HRESULT
  889. __stdcall
  890. CopyRegistryValues(
  891. IN HKEY hSourceKey,
  892. IN HKEY hTargetKey
  893. )
  894. {
  895. DWORD dwStatus = 0, cValues, cchValueName, cbData, dwType;
  896. BYTE rgbData[MAX_PATH];
  897. TCHAR szValueName[MAX_PATH];
  898. LONG lResult = 0;
  899. HRESULT hr = E_FAIL;
  900. // Determine how many values are in the registry key.
  901. lResult = RegQueryInfoKey(
  902. hSourceKey,
  903. NULL,
  904. NULL,
  905. NULL,
  906. NULL,
  907. NULL,
  908. NULL,
  909. &cValues,
  910. NULL,
  911. NULL,
  912. NULL,
  913. NULL);
  914. if (lResult != ERROR_SUCCESS) {
  915. hr = HRESULT_FROM_WIN32(lResult);
  916. DBGMSG((_T("RegQueryInfoKey failed getting the number of values in the source. hr = 0x%x"), hr));
  917. goto Exit;
  918. }
  919. // Loop through all of the values and copy them from the source key into
  920. // the target key.
  921. for (DWORD dwIndex = 0; dwIndex < cValues; dwIndex++) {
  922. cchValueName = SIZECHAR(szValueName);
  923. cbData = sizeof(rgbData);
  924. lResult = RegEnumValue(
  925. hSourceKey,
  926. dwIndex,
  927. szValueName,
  928. &cchValueName,
  929. NULL,
  930. &dwType,
  931. rgbData,
  932. &cbData);
  933. if (lResult != ERROR_SUCCESS) {
  934. hr = HRESULT_FROM_WIN32(lResult);
  935. DBGMSG((_T("RegEnumValue failed while obtaining source value. hr = 0x%x"), hr));
  936. goto Exit;
  937. }
  938. lResult = RegSetValueEx(
  939. hTargetKey,
  940. szValueName,
  941. NULL,
  942. dwType,
  943. rgbData,
  944. cbData);
  945. if (lResult != ERROR_SUCCESS) {
  946. hr = HRESULT_FROM_WIN32(lResult);
  947. DBGMSG((_T("RegSetValueEx failed while copying value into target. hr = 0x%x"), hr));
  948. goto Exit;
  949. }
  950. }
  951. hr = S_OK;
  952. Exit:
  953. return hr;
  954. }
  955. //*****************************************************************************
  956. //
  957. // CopyRegistryKey
  958. //
  959. // Copies the source registry key into the target registry key completely.
  960. //
  961. //*****************************************************************************
  962. HRESULT
  963. __stdcall
  964. CopyRegistryKey(
  965. IN HKEY hRootKey,
  966. IN TCHAR *szSourceKey,
  967. IN TCHAR *szTargetKey
  968. )
  969. {
  970. HKEY hSourceKey = NULL, hTargetKey = NULL;
  971. LONG lResult;
  972. DWORD cchSubSize = MAX_PATH, i = 0, dwDisposition = 0;
  973. TCHAR szSubKey[MAX_PATH];
  974. HRESULT hr = E_FAIL;
  975. // Open the source key.
  976. lResult = RegOpenKeyEx(
  977. hRootKey,
  978. szSourceKey,
  979. 0,
  980. KEY_READ,
  981. &hSourceKey);
  982. if(lResult != ERROR_SUCCESS) {
  983. hr = HRESULT_FROM_WIN32(lResult);
  984. DBGMSG((_T("Unable to open source registry key. hr = 0x%x"), hr));
  985. goto Exit;
  986. }
  987. // Create or open the target registry key.
  988. lResult = RegCreateKeyEx(
  989. hRootKey,
  990. szTargetKey,
  991. 0,
  992. NULL,
  993. REG_OPTION_NON_VOLATILE,
  994. KEY_ALL_ACCESS,
  995. NULL,
  996. &hTargetKey,
  997. &dwDisposition);
  998. if (lResult != ERROR_SUCCESS) {
  999. hr = HRESULT_FROM_WIN32(lResult);
  1000. DBGMSG((_T("Unable to create or open target registry key. hr = 0x%x"), hr));
  1001. goto Exit;
  1002. }
  1003. // Copy the values in the source key to the target key.
  1004. hr = CopyRegistryValues(hSourceKey, hTargetKey);
  1005. if (FAILED(hr)) {
  1006. DBGMSG((_T("Unable to copy registry values from source to target. hr = 0x%x"), hr));
  1007. goto Exit;
  1008. }
  1009. // Loop through the source's subkeys and copy each of these into the
  1010. // target key.
  1011. while (ERROR_SUCCESS == RegEnumKey(
  1012. hSourceKey,
  1013. i++,
  1014. szSubKey,
  1015. cchSubSize)) {
  1016. TCHAR szNewSubKey[MAX_PATH] = _T("");
  1017. TCHAR szOldSubKey[MAX_PATH] = _T("");
  1018. hr = StringCchPrintf(szOldSubKey, SIZECHAR(szOldSubKey), _T("%s\\%s"), szSourceKey, szSubKey);
  1019. if (FAILED(hr)) {
  1020. DBGMSG((_T("StringCchPrintf failed when constructing source registry key string. hr = 0x%x"), hr));
  1021. goto Exit;
  1022. }
  1023. StringCchPrintf(szNewSubKey, SIZECHAR(szNewSubKey), _T("%s\\%s"), szTargetKey, szSubKey);
  1024. if (FAILED(hr)) {
  1025. DBGMSG((_T("StringCchPrintf failed when constructing target registry key string. hr = 0x%x"), hr));
  1026. goto Exit;
  1027. }
  1028. hr = CopyRegistryKey(hRootKey, szOldSubKey, szNewSubKey);
  1029. if (FAILED(hr)) {
  1030. DBGMSG((_T("Failed to copy source subkey into target. hr = 0x%x"), hr));
  1031. goto Exit;
  1032. }
  1033. }
  1034. hr = S_OK;
  1035. Exit:
  1036. if (hTargetKey) {
  1037. RegCloseKey(hTargetKey);
  1038. }
  1039. if (hSourceKey) {
  1040. RegCloseKey(hSourceKey);
  1041. }
  1042. return hr;
  1043. }
  1044. //*****************************************************************************
  1045. //
  1046. // DeleteRegistryKey
  1047. //
  1048. // Deletes the registry key completely, including all subkeys. This is a bit
  1049. // tricky to do because Win9x and WinNT have different semantics for
  1050. // RegDeleteKey. From MSDN:
  1051. //
  1052. // Windows 95/98/Me: RegDeleteKey deletes all subkeys and values.
  1053. // Windows NT/2000/XP: The subkey to be deleted must not have subkeys.
  1054. //
  1055. // This function implements deletion for Windows NT and above only.
  1056. //
  1057. //*****************************************************************************
  1058. HRESULT
  1059. __stdcall
  1060. DeleteRegistryKey(
  1061. IN HKEY hRootKey,
  1062. IN LPTSTR pszDeleteKey
  1063. )
  1064. {
  1065. DWORD dwResult, cchSubKeyLength;
  1066. TCHAR szSubKey[MAX_PATH];
  1067. HKEY hDeleteKey = NULL;
  1068. HRESULT hr = E_FAIL;
  1069. // Open the key to delete so we can remove the subkeys.
  1070. dwResult = RegOpenKeyEx(
  1071. hRootKey,
  1072. pszDeleteKey,
  1073. 0,
  1074. KEY_READ,
  1075. &hDeleteKey);
  1076. if (dwResult != ERROR_SUCCESS) {
  1077. hr = HRESULT_FROM_WIN32(dwResult);
  1078. DBGMSG((_T("Error while opening deletion key. hr = 0x%x"), hr));
  1079. goto Exit;
  1080. }
  1081. // Enumerate through each of the subkeys and delete them in the process.
  1082. while (dwResult == ERROR_SUCCESS) {
  1083. cchSubKeyLength = SIZECHAR(szSubKey);
  1084. dwResult = RegEnumKeyEx(
  1085. hDeleteKey,
  1086. 0, // Always index zero.
  1087. szSubKey,
  1088. &cchSubKeyLength,
  1089. NULL,
  1090. NULL,
  1091. NULL,
  1092. NULL);
  1093. if (dwResult == ERROR_NO_MORE_ITEMS) {
  1094. // All of the subkeys have been deleted. So, just delete the
  1095. // deletion key.
  1096. RegCloseKey(hDeleteKey);
  1097. hDeleteKey = NULL;
  1098. dwResult = RegDeleteKey(hRootKey, pszDeleteKey);
  1099. hr = HRESULT_FROM_WIN32(dwResult);
  1100. goto Exit;
  1101. } else if (dwResult == ERROR_SUCCESS) {
  1102. // There are more subkeys to delete, so delete the current one
  1103. // recursively.
  1104. dwResult = DeleteRegistryKey(hDeleteKey, szSubKey);
  1105. } else {
  1106. // Some other error happened, so report a problem.
  1107. hr = HRESULT_FROM_WIN32(dwResult);
  1108. DBGMSG((_T("Error while enumerating subkeys. hr = 0x%x"), hr));
  1109. goto Exit;
  1110. }
  1111. }
  1112. Exit:
  1113. if (hDeleteKey) {
  1114. RegCloseKey(hDeleteKey);
  1115. }
  1116. return hr;
  1117. }
  1118. //*****************************************************************************
  1119. //
  1120. // RDCSetupBackupRegistry
  1121. //
  1122. // Copies the data in the Terminal Server Client registry key to a backup
  1123. // registry key so that this data can be restored when the client is removed
  1124. // at a later stage. This function is only necessary on Windows XP and above
  1125. // since they have built in clients which may rely on these registry keys, and
  1126. // because these clients take over after an uninstall, we have to make sure
  1127. // that they will work properly.
  1128. //
  1129. //*****************************************************************************
  1130. UINT
  1131. __stdcall
  1132. RDCSetupBackupRegistry(
  1133. IN MSIHANDLE hInstall
  1134. )
  1135. {
  1136. UINT uiResult;
  1137. TCHAR szAllUsers[MAX_PATH];
  1138. DWORD cchAllUsers = SIZECHAR(szAllUsers);
  1139. HKEY hRootKey = HKEY_LOCAL_MACHINE;
  1140. HRESULT hr = E_FAIL;
  1141. DBGMSG((_T("Entering: RDCSetupBackupRegistry")));
  1142. // Determine whether we will be using HKLM or HKCU. If it is a per user
  1143. // install use HKCU, otherwise, use HKLM.
  1144. uiResult = MsiGetProperty(
  1145. hInstall,
  1146. ALLUSERS,
  1147. szAllUsers,
  1148. &cchAllUsers);
  1149. if (uiResult != ERROR_SUCCESS) {
  1150. hr = HRESULT_FROM_WIN32(GetLastError());
  1151. DBGMSG((_T("Unable to get ALLUSERS property. hr = 0x%x."), hr));
  1152. goto Exit;
  1153. }
  1154. DBGMSG((_T("ALLUSERS = %s."), szAllUsers));
  1155. // If ALLUSERS[0] == NULL, then we are doing a per-user install.
  1156. if (szAllUsers[0] == NULL) {
  1157. hRootKey = HKEY_CURRENT_USER;
  1158. }
  1159. // Copy the source registry key into the backup key.
  1160. hr = CopyRegistryKey(
  1161. hRootKey,
  1162. TERMINAL_SERVER_CLIENT_REGKEY,
  1163. TERMINAL_SERVER_CLIENT_BACKUP_REGKEY);
  1164. if (FAILED(hr)) {
  1165. DBGMSG((_T("Unable to backup registry key. hr = 0x%x."), hr));
  1166. goto Exit;
  1167. }
  1168. hr = S_OK;
  1169. Exit:
  1170. DBGMSG((_T("Leaving: RDCSetupBackupRegistry")));
  1171. return ERROR_SUCCESS;
  1172. }
  1173. //*****************************************************************************
  1174. //
  1175. // RDCSetupRestoreRegistry
  1176. //
  1177. // Copies the data in the Terminal Server Client backup registry key back to
  1178. // the original key. Any data in the original key is deleted and the key is
  1179. // restored to exactly how it appeared when the backup was done.
  1180. //
  1181. //*****************************************************************************
  1182. UINT
  1183. __stdcall
  1184. RDCSetupRestoreRegistry(
  1185. IN MSIHANDLE hInstall
  1186. )
  1187. {
  1188. LONG lResult;
  1189. UINT uiResult;
  1190. TCHAR szAllUsers[MAX_PATH];
  1191. DWORD cchAllUsers = SIZECHAR(szAllUsers);
  1192. HKEY hRootKey = HKEY_LOCAL_MACHINE;
  1193. HRESULT hr = E_FAIL;
  1194. DBGMSG((_T("Entering: RDCSetupRestoreRegistry")));
  1195. // Determine whether we will be using HKLM or HKCU. If it is a per user
  1196. // install use HKCU, otherwise, use HKLM.
  1197. uiResult = MsiGetProperty(
  1198. hInstall,
  1199. ALLUSERS,
  1200. szAllUsers,
  1201. &cchAllUsers);
  1202. if (uiResult != ERROR_SUCCESS) {
  1203. hr = HRESULT_FROM_WIN32(GetLastError());
  1204. DBGMSG((_T("Unable to get ALLUSERS property. hr = 0x%x."), hr));
  1205. goto Exit;
  1206. }
  1207. DBGMSG((_T("ALLUSERS = %s."), szAllUsers));
  1208. // If ALLUSERS[0] == NULL, then we are doing a per-user uninstall.
  1209. if (szAllUsers[0] == NULL) {
  1210. hRootKey = HKEY_CURRENT_USER;
  1211. }
  1212. // Restore the registry key from the backup key.
  1213. hr = CopyRegistryKey(
  1214. hRootKey,
  1215. TERMINAL_SERVER_CLIENT_BACKUP_REGKEY,
  1216. TERMINAL_SERVER_CLIENT_REGKEY);
  1217. if (FAILED(hr)) {
  1218. DBGMSG((_T("Unable to restore registry key. hr = 0x%x."), hr));
  1219. goto Exit;
  1220. }
  1221. // Delete the restore source as we don't need it anymore.
  1222. hr = DeleteRegistryKey(
  1223. hRootKey,
  1224. TERMINAL_SERVER_CLIENT_BACKUP_REGKEY);
  1225. if (FAILED(hr)) {
  1226. DBGMSG((_T("Failed to delete backup registry key. hr = 0x%x"), hr));
  1227. goto Exit;
  1228. }
  1229. hr = S_OK;
  1230. Exit:
  1231. DBGMSG((_T("Leaving: RDCSetupRestoreRegistry")));
  1232. return ERROR_SUCCESS;
  1233. }
  1234. //*****************************************************************************
  1235. //
  1236. // CreateLinkFile
  1237. //
  1238. // Creates a shortcut named lpszLinkFile that points to the target lpszPath
  1239. // and contains the description given by lpszDescription.
  1240. //
  1241. //*****************************************************************************
  1242. HRESULT
  1243. __stdcall
  1244. CreateLinkFile(
  1245. IN LPTSTR lpszLinkFile,
  1246. IN LPCTSTR lpszPath,
  1247. IN LPCTSTR lpszDescription
  1248. )
  1249. {
  1250. IShellLink* psl;
  1251. HRESULT hr = E_FAIL;
  1252. // Get a pointer to the IShellLink interface.
  1253. hr = CoCreateInstance(
  1254. CLSID_ShellLink,
  1255. NULL,
  1256. CLSCTX_INPROC_SERVER,
  1257. IID_IShellLink,
  1258. (LPVOID*) &psl);
  1259. if (SUCCEEDED(hr)) {
  1260. IPersistFile* ppf;
  1261. // Get a pointer to the IPersistFile interface.
  1262. hr = psl->QueryInterface(IID_IPersistFile, (void**) &ppf);
  1263. if (SUCCEEDED(hr)) {
  1264. // Set the path to the link target.
  1265. hr = psl->SetPath(lpszPath);
  1266. if (SUCCEEDED(hr)) {
  1267. hr = psl->SetDescription(lpszDescription);
  1268. if (SUCCEEDED(hr)) {
  1269. #ifndef UNICODE
  1270. WCHAR wsz[MAX_PATH];
  1271. int cch;
  1272. // Ensure that the string is Unicode.
  1273. cch = MultiByteToWideChar(
  1274. CP_ACP, 0,
  1275. lpszLinkFile,
  1276. -1,
  1277. wsz,
  1278. MAX_PATH);
  1279. if (cch > 0) {
  1280. // Load the shortcut.
  1281. hr = ppf->Save(wsz, FALSE);
  1282. #else
  1283. // Load the shortcut.
  1284. hr = ppf->Save(lpszLinkFile, FALSE);
  1285. #endif
  1286. #ifndef UNICODE
  1287. }
  1288. #endif
  1289. }
  1290. }
  1291. // Release the pointer to the IPersistFile interface.
  1292. ppf->Release();
  1293. ppf = NULL;
  1294. }
  1295. // Release the pointer to the IShellLink interface.
  1296. psl->Release();
  1297. psl = NULL;
  1298. }
  1299. return hr;
  1300. }
  1301. //*****************************************************************************
  1302. //
  1303. // RDCSetupResetShortCut
  1304. //
  1305. // Reset the Remote Desktop Connection shortcut in the Communications submenu
  1306. // of the Start menu to point to the original Remote Desktop client.
  1307. //
  1308. //*****************************************************************************
  1309. UINT
  1310. __stdcall
  1311. RDCSetupResetShortCut(
  1312. IN MSIHANDLE hInstall
  1313. )
  1314. {
  1315. TCHAR szCommunicationsPath[MAX_PATH],
  1316. szSystem32Path[MAX_PATH],
  1317. szRdcShortCutTitle[MAX_PATH],
  1318. szRdcShortCutPath[MAX_PATH],
  1319. szMstscExecutableName[MAX_PATH],
  1320. szMstscPath[MAX_PATH],
  1321. szDescription[MAX_PATH];
  1322. DWORD cchCommunicationsPath = SIZECHAR(szCommunicationsPath),
  1323. cchSystem32Path = SIZECHAR(szSystem32Path);
  1324. UINT uiResult;
  1325. INT iResult;
  1326. HRESULT hr = E_FAIL;
  1327. DBGMSG((_T("Entering: RDCSetupResetShortCut")));
  1328. // Get the path to the Remote Desktop Connection shortcut.
  1329. uiResult = MsiGetTargetPath(
  1330. hInstall,
  1331. COMMUNICATIONS_IDENTIFIER,
  1332. szCommunicationsPath,
  1333. &cchCommunicationsPath);
  1334. if (uiResult != ERROR_SUCCESS) {
  1335. hr = HRESULT_FROM_WIN32(uiResult);
  1336. DBGMSG((_T("Error: MsiGetTargetPath returned hr = 0x%x."), hr));
  1337. goto Exit;
  1338. }
  1339. // Get the full path to the Remote Desktop shortcut.
  1340. iResult = LoadString(
  1341. g_hInstance,
  1342. IDS_RDC_SHORTCUT_FILE,
  1343. szRdcShortCutTitle,
  1344. SIZECHAR(szRdcShortCutTitle));
  1345. if (iResult == 0) {
  1346. DBGMSG((_T("Error: Resource IDS_RDC_SHORTCUT_FILE not found.")));
  1347. hr = HRESULT_FROM_WIN32(GetLastError());
  1348. goto Exit;
  1349. }
  1350. hr = StringCchPrintf(
  1351. szRdcShortCutPath,
  1352. SIZECHAR(szRdcShortCutPath),
  1353. _T("%s%s"),
  1354. szCommunicationsPath,
  1355. szRdcShortCutTitle);
  1356. if (FAILED(hr)) {
  1357. DBGMSG((_T("Error: Failed to construct the RDC shortcut path. hr = 0x%x"), hr));
  1358. goto Exit;
  1359. }
  1360. DBGMSG((_T("Path to RDC shortcut is %s"), szRdcShortCutPath));
  1361. // Get the path to the system32 directory.
  1362. uiResult = MsiGetTargetPath(
  1363. hInstall,
  1364. SYSTEM32_IDENTIFIER,
  1365. szSystem32Path,
  1366. &cchSystem32Path);
  1367. if (uiResult != ERROR_SUCCESS) {
  1368. hr = HRESULT_FROM_WIN32(uiResult);
  1369. DBGMSG((_T("Error: MsiGetTargetPath returned hr = 0x%x."), hr));
  1370. goto Exit;
  1371. }
  1372. // Get the full path to the mstsc executable.
  1373. iResult = LoadString(
  1374. g_hInstance,
  1375. IDS_MSTSC_EXE_FILE,
  1376. szMstscExecutableName,
  1377. SIZECHAR(szMstscExecutableName));
  1378. if (iResult == 0) {
  1379. DBGMSG((_T("Error: Resource IDS_MSTSC_EXE_FILE not found.")));
  1380. hr = HRESULT_FROM_WIN32(GetLastError());
  1381. goto Exit;
  1382. }
  1383. hr = StringCchPrintf(
  1384. szMstscPath,
  1385. SIZECHAR(szMstscPath),
  1386. _T("%s%s"),
  1387. szSystem32Path,
  1388. szMstscExecutableName);
  1389. if (FAILED(hr)) {
  1390. DBGMSG((_T("Error: Failed to construct mstsc executable path. hr = 0x%x"), hr));
  1391. goto Exit;
  1392. }
  1393. DBGMSG((_T("Path to mstsc executable is %s"), szMstscPath));
  1394. // Get the description text for the shortcut.
  1395. iResult = LoadString(
  1396. g_hInstance,
  1397. IDS_RDC_DESCRIPTION,
  1398. szDescription,
  1399. SIZECHAR(szDescription));
  1400. if (iResult == 0) {
  1401. DBGMSG((_T("Error: Resource IDS_RDC_DESCRIPTION not found.")));
  1402. hr = HRESULT_FROM_WIN32(GetLastError());
  1403. goto Exit;
  1404. }
  1405. // Create a shortcut that points back to the old remote desktop client
  1406. // in system32.
  1407. hr = CreateLinkFile(
  1408. szRdcShortCutPath,
  1409. szMstscPath,
  1410. szDescription);
  1411. if (FAILED(hr)) {
  1412. DBGMSG((_T("Error: Failed to set link file target. hr = 0x%x"), hr));
  1413. goto Exit;
  1414. }
  1415. hr = S_OK;
  1416. Exit:
  1417. DBGMSG((_T("Leaving: RDCSetupResetShortCut")));
  1418. return ERROR_SUCCESS;
  1419. }