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.

794 lines
32 KiB

  1. //+----------------------------------------------------------------------------
  2. //
  3. // File: cmstp.cpp
  4. //
  5. // Module: CMSTP.EXE
  6. //
  7. // Synopsis: This file is the main function for the CM profile installer. This
  8. // file basically processes command line switches for the installer and
  9. // then launches the appropriate function.
  10. //
  11. // Copyright (c) 1998-1999 Microsoft Corporation
  12. //
  13. // Author: quintinb Created 07/13/98
  14. //
  15. //+----------------------------------------------------------------------------
  16. #include "cmmaster.h"
  17. #include "installerfuncs.h"
  18. #include "cmstpex.h"
  19. //
  20. // Text Constants
  21. //
  22. static const TCHAR CMSTPMUTEXNAME[] = TEXT("Connection Manager Profile Installer Mutex");
  23. //
  24. // Global Dynamic Library Classes to hold the ras dll's and shell32. See
  25. // the EnsureRasDllsLoaded and the EnsureShell32Loaded in common.cpp/common.h
  26. //
  27. CDynamicLibrary* g_pRasApi32 = NULL;
  28. CDynamicLibrary* g_pRnaph = NULL;
  29. CDynamicLibrary* g_pShell32 = NULL;
  30. CDynamicLibrary* g_pNetShell = NULL;
  31. //
  32. // Function Headers
  33. //
  34. BOOL PromptUserToUninstallProfile(HINSTANCE hInstance, LPCTSTR pszInfFile); // from uninstall.cpp
  35. BOOL PromptUserToUninstallCm(HINSTANCE hInstance); // from uninstallcm.cpp
  36. //
  37. // Enum for the LastManOut function which follows.
  38. //
  39. typedef enum _UNINSTALLTYPE
  40. {
  41. PROFILEUNINSTALL, // a profile is being uninstalled
  42. CMUNINSTALL // the cm bits themselves are being uninstalled.
  43. } UNINSTALLTYPE;
  44. //+----------------------------------------------------------------------------
  45. //
  46. // Function: LastManOut
  47. //
  48. // Synopsis: This function determines if the current uninstall action is the
  49. // last uninstall action which should then delete cmstp.exe. If the
  50. // uninstall action is a profile uninstall we need to check that
  51. // cm has already been uninstalled and that there is only one profile
  52. // installed currently (the one we are about to delete). If the
  53. // uninstall action is uninstalling CM then we need to make sure there
  54. // are no other profiles on the machine. Notice that this function
  55. // never returns TRUE on Native CM platforms. If it did, then cmstp.exe
  56. // would be deleted inadvertently even though UninstallCm wouldn't
  57. // actually delete the rest of CM.
  58. //
  59. // Arguments: UNINSTALLTYPE UninstallType - an enum value which tells if this is
  60. // a profile uninstall or a CM uninstall.
  61. //
  62. // Returns: BOOL - TRUE if this install is the last one out and cmstp.exe should
  63. // be deleted.
  64. //
  65. // History: quintinb Created 6/28/99
  66. //
  67. //+----------------------------------------------------------------------------
  68. BOOL LastManOut(UNINSTALLTYPE UninstallType, LPCTSTR pszInfFile)
  69. {
  70. BOOL bReturn = FALSE;
  71. //
  72. // First check to make sure that remcmstp.inf doesn't exist in the system
  73. // directory. If it does, then we know that Cmstp.exe has already determined
  74. // that it is the last man and should delete itself. Thus it wrote the cmstp.exe
  75. // command into remcmstp.inf and the inf engine will delete cmstp.exe when it is done.
  76. // Thus we need to check for this file and if it exists return FALSE.
  77. //
  78. TCHAR szSystemDir[MAX_PATH+1];
  79. TCHAR szTemp[MAX_PATH+1];
  80. if (0 == GetSystemDirectory(szSystemDir, CELEMS(szSystemDir)))
  81. {
  82. CMASSERTMSG(FALSE, TEXT("LastManOut -- Unable to obtain a path to the System Directory"));
  83. return FALSE;
  84. }
  85. wsprintf(szTemp, TEXT("%s\\remcmstp.inf"), szSystemDir);
  86. if (FileExists(szTemp))
  87. {
  88. CMTRACE1(TEXT("\tDetected remcmstp.inf, not setting last man out -- Process ID is 0x%x "), GetCurrentProcessId());
  89. Sleep(2000); // we sleep here to put a little delay in the processing to let any other copies
  90. // of cmstp.exe clean themselves up. I found that on a system with several copies of
  91. // cmstp.exe all deleting profiles and then a cmstp to delete CM, not all of the cmstps
  92. // would clean up in time and thus cmstp.exe wouldn't get deleted. A sleep is hokey, but
  93. // two seconds in the last man out situation only fixes it and it no down level user should
  94. // ever have 8 profiles (which was home many I tested it with) let alone delete them
  95. // all at once. It works fine for deleting two profiles and CM simultaneously either way.
  96. return FALSE;
  97. }
  98. //
  99. // Make sure that we aren't trying to Remove cmstp.exe on a platform where CM is Native.
  100. // If CM is Native, then always return FALSE because the CM uninstall function won't
  101. // uninstall CM and we don't want to accidently delete cmstp.exe.
  102. //
  103. if (!CmIsNative())
  104. {
  105. if (PROFILEUNINSTALL == UninstallType)
  106. {
  107. //
  108. // We are uninstalling a profile. We need to check to see if CM has been deleted and
  109. // if there are any other profiles on the machine besides the one we are going to delete.
  110. //
  111. wsprintf(szTemp, TEXT("%s\\cmdial32.dll"), szSystemDir);
  112. if (!FileExists(szTemp))
  113. {
  114. //
  115. // Then we know that CM is already gone. We need to check and see if any other
  116. // profiles exist besides the one we are about to delete.
  117. //
  118. HKEY hKey;
  119. DWORD dwNumValues;
  120. TCHAR szServiceName[MAX_PATH+1];
  121. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_pszRegCmMappings, 0,
  122. KEY_READ, &hKey))
  123. {
  124. if ((ERROR_SUCCESS == RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL,
  125. &dwNumValues, NULL, NULL, NULL, NULL)) && (dwNumValues == 1))
  126. {
  127. //
  128. // Then we have only the one profile mappings key, is it the correct one?
  129. //
  130. if (0 != GetPrivateProfileString(c_pszInfSectionStrings, c_pszCmEntryServiceName,
  131. TEXT(""), szServiceName, MAX_PATH, pszInfFile))
  132. {
  133. DWORD dwSize = MAX_PATH;
  134. LONG lResult = RegQueryValueEx(hKey, szServiceName, NULL,
  135. NULL, (LPBYTE)szTemp, &dwSize);
  136. if ((ERROR_SUCCESS == lResult) && (TEXT('\0') != szTemp[0]))
  137. {
  138. CMTRACE1(TEXT("\tDetected Last Man Out -- Process ID is 0x%x "), GetCurrentProcessId());
  139. bReturn = TRUE;
  140. }
  141. }
  142. }
  143. RegCloseKey(hKey);
  144. }
  145. }
  146. }
  147. else if (CMUNINSTALL == UninstallType)
  148. {
  149. //
  150. // We are uninstalling CM. We want to make sure that we don't have any profiles
  151. // still installed. If not, then we are the last man out.
  152. //
  153. if (!AllUserProfilesInstalled())
  154. {
  155. CMTRACE1(TEXT("\tDetected Last Man Out -- Process ID is 0x%x "), GetCurrentProcessId());
  156. bReturn = TRUE;
  157. }
  158. }
  159. else
  160. {
  161. CMASSERTMSG(FALSE, TEXT("LastManOut -- Unknown Uninstall Type"));
  162. }
  163. }
  164. return bReturn;
  165. }
  166. //+----------------------------------------------------------------------------
  167. //
  168. // Function: ExtractInfAndRelaunchCmstp
  169. //
  170. // Synopsis: This function is used to cleanup Cmstp.exe in the last man out
  171. // scenario. In order to not leave cmstp.exe on a users machine,
  172. // we must extract remcmstp.inf and write the uninstall command to it.
  173. // That way, the inf will monitor the cmstp.exe process and when it is
  174. // finished it can then delete cmstp.exe.
  175. //
  176. // Arguments: HINSTANCE hInstance - Instance handle to load resources
  177. // DWORD dwFlags - Command line param flags
  178. // LPCTSTR szInfPath - path to the inf file.
  179. //
  180. // Returns: BOOL -- TRUE if Successful
  181. //
  182. // History: quintinb Created 6/28/99
  183. //
  184. //+----------------------------------------------------------------------------
  185. BOOL ExtractInfAndRelaunchCmstp(HINSTANCE hInstance, DWORD dwFlags, LPCTSTR pszInfPath)
  186. {
  187. //
  188. // Check Parameters
  189. //
  190. if (0 == dwFlags || NULL == pszInfPath || TEXT('\0') == pszInfPath[0])
  191. {
  192. CMASSERTMSG(FALSE, TEXT("Invalid Paramater passed to ExtractInfAndRelaunchCmstp."));
  193. return FALSE;
  194. }
  195. //
  196. // Get the Path to the System Directory
  197. //
  198. TCHAR szSystemDir[MAX_PATH+1];
  199. if (0 == GetSystemDirectory(szSystemDir, CELEMS(szSystemDir)))
  200. {
  201. CMASSERTMSG(FALSE, TEXT("ExtractInfAndRelaunchCmstp -- Unable to obtain a path to the System Directory"));
  202. return FALSE;
  203. }
  204. //
  205. // Extract remcmstp.inf
  206. //
  207. HGLOBAL hRemCmstp = NULL;
  208. LPTSTR pszRemCmstpInf = NULL;
  209. HRSRC hResource = FindResource(hInstance, MAKEINTRESOURCE(IDT_REMCMSTP_INF), TEXT("REGINST"));
  210. if (hResource)
  211. {
  212. hRemCmstp = LoadResource(hInstance, hResource);
  213. if (hRemCmstp)
  214. {
  215. //
  216. // Note that we don't need to call FreeResource, which is obsolete, this
  217. // will be cleaned up when cmstp.exe exits.
  218. //
  219. pszRemCmstpInf = (LPTSTR)LockResource(hRemCmstp);
  220. }
  221. }
  222. //
  223. // Now that we have the remcmstp.inf file that is stored in the cmstp.exe resource
  224. // loaded into memory and have a pointer to it, lets create the file that we are
  225. // going to write it out to.
  226. //
  227. if (pszRemCmstpInf)
  228. {
  229. TCHAR szRemCmstpPath[MAX_PATH+1];
  230. wsprintf(szRemCmstpPath, TEXT("%s\\remcmstp.inf"), szSystemDir);
  231. HANDLE hFile = CreateFile(szRemCmstpPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS,
  232. FILE_ATTRIBUTE_NORMAL, NULL);
  233. if (INVALID_HANDLE_VALUE != hFile)
  234. {
  235. //
  236. // Then we have the file, lets write the data to it.
  237. //
  238. DWORD cbWritten;
  239. if (WriteFile(hFile, pszRemCmstpInf, lstrlen(pszRemCmstpInf)*sizeof(TCHAR),
  240. &cbWritten, NULL))
  241. {
  242. //
  243. // We launch the inf to delete cmstp right now. The inf has a PreSetupCommand that
  244. // launches the cmstp.exe uninstall command with a /s switch (which we write in the
  245. // inf after extracting it). The inf then launches the new cmstp, which forces the newly
  246. // launched cmstp.exe to wait on the mutex of the current cmstp.exe until it is finished.
  247. // Since profile installs will error on the mutex instead of waiting for it, we
  248. // shouldn't get any installs until after the uninstall and the cleanup inf have run.
  249. // Note that the inf will wait for the PreSetupCommands to finish before processing the inf.
  250. // This is important because we could be waiting on User input (the OK dialog from
  251. // deleting CM for instance).
  252. //
  253. CloseHandle(hFile);
  254. //
  255. // Now lets write the cmstp.exe command into remcmstp.inf
  256. //
  257. LPTSTR pszUninstallFlag = NULL;
  258. if (dwFlags & c_dwUninstallCm)
  259. {
  260. pszUninstallFlag = c_pszUninstallCm;
  261. }
  262. else if (dwFlags & c_dwUninstall)
  263. {
  264. pszUninstallFlag = c_pszUninstall;
  265. }
  266. else
  267. {
  268. CMASSERTMSG(FALSE, TEXT("ExtractInfAndRelaunchCmstp -- Unknown Uninstall Type, exiting"));
  269. return FALSE;
  270. }
  271. TCHAR szShortInfPath[MAX_PATH+1] = {0};
  272. TCHAR szParams[2*MAX_PATH+1] = {0};
  273. DWORD dwRet = GetShortPathName(pszInfPath, szShortInfPath, MAX_PATH);
  274. if (0 == dwRet || MAX_PATH < dwRet)
  275. {
  276. CMASSERTMSG(FALSE, TEXT("ExtractInfAndRelaunchCmstp -- Unable to get the short path to the Inf, exiting"));
  277. return FALSE;
  278. }
  279. wsprintf(szParams, TEXT("%s\\cmstp.exe %s %s %s"), szSystemDir, pszUninstallFlag, c_pszSilent, szShortInfPath);
  280. WritePrivateProfileSection(TEXT("PreSetupCommandsSection"), szParams, szRemCmstpPath);
  281. //
  282. // Finally lets launch the inf uninstall with the new cmstp command in it.
  283. //
  284. wsprintf(szParams,
  285. TEXT("advpack.dll,LaunchINFSection %s\\remcmstp.inf, Uninstall"),
  286. szSystemDir);
  287. SHELLEXECUTEINFO sei = {0};
  288. sei.cbSize = sizeof(sei);
  289. sei.fMask = SEE_MASK_FLAG_NO_UI;
  290. sei.nShow = SW_SHOWNORMAL;
  291. sei.lpFile = TEXT("Rundll32.exe");
  292. sei.lpParameters = szParams;
  293. sei.lpDirectory = szSystemDir;
  294. if (!ShellExecuteEx(&sei))
  295. {
  296. CMTRACE1(TEXT("ExtractInfAndRelaunchCmstp -- ShellExecute Returned an error, GLE %d"), GetLastError());
  297. }
  298. else
  299. {
  300. return TRUE;
  301. }
  302. }
  303. else
  304. {
  305. CloseHandle(hFile);
  306. CMASSERTMSG(FALSE, TEXT("ExtractInfAndRelaunchCmstp -- Unable to write the file data to remcmstp.inf"));
  307. }
  308. }
  309. else
  310. {
  311. CMASSERTMSG(FALSE, TEXT("ExtractInfAndRelaunchCmstp -- Unable to Create remcmstp.inf in the system directory."));
  312. }
  313. }
  314. else
  315. {
  316. CMASSERTMSG(FALSE, TEXT("ExtractInfAndRelaunchCmstp -- Unable to load the remcmstp.inf custom resource."));
  317. }
  318. return FALSE;
  319. }
  320. //+----------------------------------------------------------------------------
  321. //
  322. // Function: IsInstall
  323. //
  324. // Synopsis: Wrapper function to check and see if this is an install or not.
  325. //
  326. // Arguments: DWORD dwFlags - the action flags parameter returned from the
  327. // command line parsing class.
  328. //
  329. // Returns: BOOL - TRUE if this is an Install command
  330. //
  331. // History: quintinb Created Header 6/28/99
  332. //
  333. //+----------------------------------------------------------------------------
  334. BOOL IsInstall(DWORD dwFlags)
  335. {
  336. return (0 == (dwFlags & 0xFF));
  337. }
  338. //+----------------------------------------------------------------------------
  339. //
  340. // Function: ProcessCmstpExtensionDll
  341. //
  342. // Synopsis: Processes the cmstp extension dll registry keys and calls out
  343. // to the extension proc as necessary to modify the action behavior.
  344. // Using the extension proc, we can modify the install, uninstall,
  345. // etc. behavior that cmstp exhibits. This is most useful on platforms
  346. // that have Native CM (or just a very new copy of CM) but an older
  347. // profile is being installed. Since the cmstp.exe that is in the package
  348. // does the actual installation, we can modify the installation parameters,
  349. // modify the inf path, or even stop the install. Since we get called
  350. // after the install as well, we can even take post-install or cleanup
  351. // actions.
  352. //
  353. // Arguments: LPDWORD pdwFlags - pointer to the flags parameter, note that it
  354. // can be modified by the extension proc
  355. // LPTSTR pszInfPath - Inf path, note that it can be modified
  356. // by the extension proc.
  357. // HRESULT hrRet - current return value, this is only used on
  358. // the post action proc call.
  359. // EXTENSIONDLLPROCTIMES PreOrPost - if this is a Pre action
  360. // call or a Post action call.
  361. //
  362. // Returns: BOOL - TRUE if cmstp.exe should continue, FALSE stops the action
  363. // (install, uninstall, migration, whatever) without further
  364. // action.
  365. //
  366. // History: quintinb Created Header 6/28/99
  367. //
  368. //+----------------------------------------------------------------------------
  369. BOOL ProcessCmstpExtensionDll (LPDWORD pdwFlags, LPTSTR pszInfPath, HRESULT hrRet, EXTENSIONDLLPROCTIMES PreOrPost)
  370. {
  371. //
  372. // Check for the CmstpExtensionDll reg key in Cm App Paths
  373. //
  374. const TCHAR* const c_pszRegCmstpExtensionDll = TEXT("CmstpExtensionDll");
  375. const char* const c_pszCmstpExtensionProc = "CmstpExtensionProc"; // GetProcAddress takes ANSI strings -- quintinb
  376. pfnCmstpExtensionProcSpec pfnCmstpExtensionProc = NULL;
  377. HKEY hKey;
  378. TCHAR szCmstpExtensionDllPath[MAX_PATH+1];
  379. ZeroMemory(szCmstpExtensionDllPath, CELEMS(szCmstpExtensionDllPath));
  380. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_pszRegCmAppPaths, 0, KEY_READ, &hKey))
  381. {
  382. DWORD dwSize = CELEMS(szCmstpExtensionDllPath);
  383. DWORD dwType = REG_SZ;
  384. if (ERROR_SUCCESS == RegQueryValueEx(hKey, c_pszRegCmstpExtensionDll, NULL, &dwType,
  385. (LPBYTE)szCmstpExtensionDllPath, &dwSize))
  386. {
  387. CDynamicLibrary CmstpExtensionDll (szCmstpExtensionDllPath);
  388. pfnCmstpExtensionProc = (pfnCmstpExtensionProcSpec)CmstpExtensionDll.GetProcAddress(c_pszCmstpExtensionProc);
  389. if (NULL == pfnCmstpExtensionProc)
  390. {
  391. return TRUE;
  392. }
  393. else
  394. {
  395. return (pfnCmstpExtensionProc)(pdwFlags, pszInfPath, hrRet, PreOrPost);
  396. }
  397. }
  398. RegCloseKey(hKey);
  399. }
  400. return TRUE;
  401. }
  402. //_____________________________________________________________________________
  403. //
  404. // Function: WinMain
  405. //
  406. // Synopsis: Processes command line switches -- see common\inc\cmstpex.h for full list
  407. //
  408. //
  409. // Arguments: HINSTANCE hInstance -
  410. // HINSTANCE hPrevInstance -
  411. // PSTR szCmdLine - pass in the inf file name here
  412. // int iCmdShow -
  413. //
  414. // Returns: int WINAPI -
  415. //
  416. // History: Re-created quintinb 7-13-98
  417. //
  418. //_____________________________________________________________________________
  419. int WINAPI
  420. WinMain (HINSTANCE, //hInstance
  421. HINSTANCE, //hPrevInstance
  422. PSTR, //szCmdLine
  423. int //iCmdShow
  424. )
  425. {
  426. CMTRACE(TEXT("====================================================="));
  427. CMTRACE1(TEXT(" CMSTP.EXE - LOADING - Process ID is 0x%x "), GetCurrentProcessId());
  428. CMTRACE(TEXT("====================================================="));
  429. BOOL bUsageError = FALSE;
  430. BOOL bAnotherInstanceRunning = FALSE;
  431. HRESULT hrReturn = S_OK;
  432. TCHAR szMsg[MAX_PATH+1];
  433. TCHAR szTitle[MAX_PATH+1];
  434. TCHAR szInfPath[MAX_PATH+1];
  435. DWORD dwFlags = 0;
  436. CPlatform plat;
  437. CNamedMutex CmstpMutex; // keep this here so it doesn't get destructed until main ends.
  438. // this gives us better control of when it is unlocked.
  439. HINSTANCE hInstance = GetModuleHandleA(NULL);
  440. LPTSTR szCmdLine = GetCommandLine();
  441. //
  442. // Check to make sure that we aren't an x86 version of cmstp running on an Alpha
  443. //
  444. #ifdef CMX86BUILD
  445. if (plat.IsAlpha())
  446. {
  447. MYVERIFY(0 != LoadString(hInstance, IDS_CMSTP_TITLE, szTitle, MAX_PATH));
  448. MYVERIFY(0 != LoadString(hInstance, IDS_BINARY_NOT_ALPHA, szMsg, MAX_PATH));
  449. MessageBox(NULL, szMsg, szTitle, MB_OK);
  450. return FALSE;
  451. }
  452. #endif
  453. //
  454. // Setup the Command Line Arguments
  455. //
  456. ZeroMemory(szInfPath, sizeof(szInfPath));
  457. { // Make sure ArgProcessor gets destructed properly and we don't leak mem
  458. CProcessCmdLn ArgProcessor(c_NumArgs, (ArgStruct*)&Args, TRUE,
  459. FALSE); //bSkipFirstToken == TRUE, bBlankCmdLnOkay == FALSE
  460. if (ArgProcessor.GetCmdLineArgs(szCmdLine, &dwFlags, szInfPath, MAX_PATH))
  461. {
  462. //
  463. // We want to wait indefinitely, unless this is an install. If it is an
  464. // install then we want to return immediately and throw an error if we couldn't
  465. // get the lock (NTRAID 261248). We also want to be able to launch two profiles
  466. // simulaneously on NT5 (cmstp.exe takes the place of explorer.exe) thus we will
  467. // pass the pointer to the CNamedMutex object to the install function so that
  468. // it can release the mutex once the install is finished except for launching the
  469. // profile (NTRAID 310478).
  470. //
  471. BOOL bWait = !IsInstall(dwFlags);
  472. if (CmstpMutex.Lock(CMSTPMUTEXNAME, bWait, INFINITE))
  473. {
  474. //
  475. // We got the mutex lock, so go ahead and process the command line
  476. // arguments. First, however, check for a cmstp Dll listed in the
  477. // app paths key of CM. If a dll is listed here, then we want to load
  478. // the dll and pass it the inf path and the install flags. If the dll
  479. // proc returns FALSE, then we want to exit. Otherwise continue with
  480. // the install as normal.
  481. // Of the install flags we first check for /x, ,/m, or /mp
  482. // (these switches must be by themselves, we don't allow any
  483. // modifier switches with these), the non-install commands. We now allow the uninstall
  484. // command to take the Silent switch to silence our uninstall prompt.
  485. //
  486. if (ProcessCmstpExtensionDll(&dwFlags, szInfPath, S_OK, PRE))
  487. {
  488. CMTRACE2(TEXT("CMSTP.EXE -- Entering Flag Processing Loop, dwFlags = %u and szInfPath = %s"), dwFlags, szInfPath);
  489. if (c_dwHelp & dwFlags)
  490. {
  491. bUsageError = TRUE;
  492. }
  493. else if (c_dwUninstall & dwFlags)
  494. {
  495. if (((c_dwUninstall == dwFlags) || ((c_dwUninstall | c_dwSilent) == dwFlags)) &&
  496. (TEXT('\0') != szInfPath[0]))
  497. {
  498. BOOL bSilent = (dwFlags & c_dwSilent);
  499. if (bSilent || PromptUserToUninstallProfile(hInstance, szInfPath))
  500. {
  501. //
  502. // Okay, the user wants to uninstall. Now check to see if we are the last
  503. // man out. If we are then we also need to delete cmstp.
  504. //
  505. if (LastManOut(PROFILEUNINSTALL, szInfPath))
  506. {
  507. ExtractInfAndRelaunchCmstp(hInstance, dwFlags, szInfPath);
  508. }
  509. else
  510. {
  511. hrReturn = UninstallProfile(hInstance, szInfPath, TRUE); // bCleanUpCreds == TRUE
  512. MYVERIFY(SUCCEEDED(hrReturn));
  513. }
  514. }
  515. }
  516. else
  517. {
  518. bUsageError = TRUE;
  519. }
  520. }
  521. else if (c_dwOsMigration & dwFlags)
  522. {
  523. if ((c_dwOsMigration == dwFlags) && (TEXT('\0') == szInfPath[0]))
  524. {
  525. hrReturn = MigrateCmProfilesForWin2kUpgrade(hInstance);
  526. MYVERIFY(SUCCEEDED(hrReturn));
  527. }
  528. else
  529. {
  530. bUsageError = TRUE;
  531. }
  532. }
  533. else if (c_dwProfileMigration & dwFlags)
  534. {
  535. if ((c_dwProfileMigration == dwFlags) && (TEXT('\0') == szInfPath[0]))
  536. {
  537. TCHAR szCurrentDir[MAX_PATH+1];
  538. if (0 == GetCurrentDirectory(MAX_PATH, szCurrentDir))
  539. {
  540. return FALSE;
  541. }
  542. lstrcat(szCurrentDir, TEXT("\\"));
  543. hrReturn = MigrateOldCmProfilesForProfileInstall(hInstance, szCurrentDir);
  544. MYVERIFY(SUCCEEDED(hrReturn));
  545. }
  546. else
  547. {
  548. bUsageError = TRUE;
  549. }
  550. }
  551. else if (c_dwUninstallCm & dwFlags)
  552. {
  553. if (((c_dwUninstallCm == dwFlags) || ((c_dwUninstallCm | c_dwSilent) == dwFlags)) &&
  554. (TEXT('\0') != szInfPath[0]))
  555. {
  556. BOOL bNoBeginPrompt = (dwFlags & c_dwSilent);
  557. if (bNoBeginPrompt || PromptUserToUninstallCm(hInstance))
  558. {
  559. //
  560. // Okay, the user wants to uninstall. Now check to see if we are the last
  561. // man out. If we are then we also need to delete cmstp.
  562. //
  563. if (LastManOut(CMUNINSTALL, szInfPath))
  564. {
  565. if (ExtractInfAndRelaunchCmstp(hInstance, dwFlags, szInfPath))
  566. {
  567. //
  568. // We need to delete the Uninstall key so that we don't leave
  569. // it in Add/Remove Programs (the refresh is keyed off of this
  570. // executable ending not the relaunched cmstp.exe's ending).
  571. // NTRAID 336249
  572. //
  573. HRESULT hrTemp = HrRegDeleteKeyTree(HKEY_LOCAL_MACHINE,
  574. TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Connection Manager"));
  575. MYDBGASSERT(SUCCEEDED(hrTemp));
  576. }
  577. }
  578. else
  579. {
  580. hrReturn = UninstallCm(hInstance, szInfPath);
  581. MYVERIFY(SUCCEEDED(hrReturn));
  582. }
  583. }
  584. }
  585. else
  586. {
  587. bUsageError = TRUE;
  588. }
  589. }
  590. else
  591. {
  592. //
  593. // Figure out if the command line flags told us to install single user or
  594. // all user. Note that we have command line flags for both now (we used to
  595. // default to all user but we now default to single user even for Admins). If
  596. // the caller specified the all user flag and the single user flag then the
  597. // single user flag takes precedence. Otherwise, if the all user flag is
  598. // specified we will honor it.
  599. //
  600. BOOL bSingleUser = ((dwFlags & c_dwSingleUser) || !(dwFlags & c_dwAllUser));
  601. //
  602. // Install, note that on NT5 we will release the CmstpMutex once
  603. // we are finished installing and just want to launch the profile.
  604. //
  605. hrReturn = InstallInf(hInstance, szInfPath,
  606. (dwFlags & c_dwNoSupportFiles), (dwFlags & c_dwNoLegacyIcon),
  607. (dwFlags & c_dwNoNT5Shortcut), (dwFlags & c_dwSilent),
  608. bSingleUser, (dwFlags & c_dwSetDefaultCon), &CmstpMutex);
  609. if (FAILED(hrReturn))
  610. {
  611. CMTRACE2("Cmstp.exe -- InstallInf failed with error %d (0x%lx)", hrReturn, hrReturn);
  612. }
  613. }
  614. //
  615. // Again call the Cmstp Extension Dll if one exists. We want to give it
  616. // a chance to take post install actions if necessary.
  617. ProcessCmstpExtensionDll(&dwFlags, szInfPath, hrReturn, POST);
  618. }
  619. }
  620. else
  621. {
  622. bAnotherInstanceRunning = TRUE;
  623. }
  624. }
  625. else
  626. {
  627. bUsageError = TRUE;
  628. }
  629. }
  630. //
  631. // Clean up our Dll's
  632. //
  633. if (g_pRasApi32)
  634. {
  635. g_pRasApi32->Unload();
  636. CmFree(g_pRasApi32);
  637. }
  638. if (g_pRnaph)
  639. {
  640. g_pRnaph->Unload();
  641. CmFree(g_pRnaph);
  642. }
  643. if (g_pShell32)
  644. {
  645. g_pShell32->Unload();
  646. CmFree(g_pShell32);
  647. }
  648. if (g_pNetShell)
  649. {
  650. g_pNetShell->Unload();
  651. CmFree(g_pNetShell);
  652. }
  653. //
  654. // UnLock the cmstp mutex, note that it may never have been locked or
  655. // it could have been unlocked on Windows 2000 upon launching a profile,
  656. // the named mutex class will handle this.
  657. //
  658. CmstpMutex.Unlock();
  659. //
  660. // Display any error messages after unlocking the mutex so that don't hold
  661. // it in the Usage message case. Another instance running should only
  662. // happen when an install tries to acquire the mutex while another cmstp
  663. // is running, thus the mutex was never acquired but put the message code
  664. // here to keep it in one place.
  665. //
  666. if (bUsageError)
  667. {
  668. CMTRACE("Cmstp.exe -- Usage Error!");
  669. if (0 == (dwFlags & c_dwSilent))
  670. {
  671. const int c_MsgLen = 2024;
  672. TCHAR* pszMsg = (TCHAR*)CmMalloc(sizeof(TCHAR)*(c_MsgLen+1));
  673. if (pszMsg)
  674. {
  675. MYVERIFY(0 != LoadString(hInstance, IDS_CMSTP_TITLE, szTitle, MAX_PATH));
  676. MYVERIFY(0 != LoadString(hInstance, IDS_USAGE_MSG, pszMsg, c_MsgLen));
  677. MessageBox(NULL, pszMsg, szTitle, MB_OK | MB_ICONINFORMATION);
  678. CmFree(pszMsg);
  679. }
  680. }
  681. }
  682. else if (bAnotherInstanceRunning)
  683. {
  684. MYVERIFY(0 != LoadString(hInstance, IDS_CMSTP_TITLE, szTitle, MAX_PATH));
  685. MYVERIFY(0 != LoadString(hInstance, IDS_INUSE_MSG, szMsg, MAX_PATH));
  686. MessageBox(NULL, szMsg, szTitle, MB_OK);
  687. }
  688. //
  689. // Check for memory leaks
  690. //
  691. EndDebugMemory();
  692. //
  693. // get return value
  694. //
  695. BOOL bRet = SUCCEEDED(hrReturn) && !bUsageError && !bAnotherInstanceRunning;
  696. //
  697. // Since we don't link to libc, we need to do this ourselves.
  698. //
  699. CMTRACE(TEXT("====================================================="));
  700. CMTRACE1(TEXT(" CMSTP.EXE - UNLOADING - Process ID is 0x%x "), GetCurrentProcessId());
  701. CMTRACE(TEXT("====================================================="));
  702. ExitProcess((UINT)bRet);
  703. return bRet;
  704. }