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.

720 lines
21 KiB

  1. #include <windows.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include "tchar.h"
  5. #include "shlwapi.h"
  6. LPTSTR pszTitle = _T("Microsoft Debugging Tools");
  7. #define MSI_BUILD_VER_X86 1029 // Latest MSI version for Win2K x86
  8. #define WIN2K_MIN_BUILD_X86 2183 // Win2K RC3
  9. typedef struct _CommandArgs {
  10. BOOL QuietInstall;
  11. BOOL StressInstall;
  12. BOOL UIStressInstall;
  13. TCHAR szInstDir[ _MAX_PATH*sizeof(TCHAR) ];
  14. TCHAR szMsiName[ _MAX_PATH*sizeof(TCHAR) ];
  15. TCHAR szProductRegKey[ _MAX_PATH*sizeof(TCHAR) ];
  16. } COMMAND_ARGS, *PCOMMAND_ARGS;
  17. // Function prototypes
  18. BOOL
  19. RunCommand(
  20. PTCHAR szCommandLine,
  21. HINSTANCE hInst
  22. );
  23. BOOL
  24. GetCommandLineArgs(
  25. LPTSTR szCmdLine,
  26. PCOMMAND_ARGS pComArgs
  27. );
  28. TCHAR szMSIInstFile[_MAX_PATH*sizeof(TCHAR)];
  29. TCHAR szPkgInstFile[_MAX_PATH*sizeof(TCHAR)];
  30. TCHAR szPkgInstCommand[_MAX_PATH*2*sizeof(TCHAR)];
  31. // For stress installs, this command will be used to
  32. // remove the current package but don't remove its
  33. // files, if the current package with the same
  34. // product ID is already installed.
  35. TCHAR szPkgRemoveCommand[_MAX_PATH*2*sizeof(TCHAR)];
  36. TCHAR szPkgRemoveCommand2[_MAX_PATH*2*sizeof(TCHAR)];
  37. // If the first install fails, stress tries again without
  38. // the quiet switch before giving a pop-up
  39. TCHAR szPkgInstCommandNoQuiet[_MAX_PATH*2*sizeof(TCHAR)];
  40. TCHAR szCommandFullPath[_MAX_PATH*sizeof(TCHAR)];
  41. int WINAPI WinMain(
  42. HINSTANCE hInstance,
  43. HINSTANCE hPrevInstance,
  44. LPTSTR lpszCmdLine,
  45. int nCmdShow
  46. )
  47. {
  48. OSVERSIONINFO VersionInfo;
  49. SYSTEM_INFO SystemInfo;
  50. BOOL rc;
  51. BOOL MSIIsInstalled;
  52. PTCHAR ch;
  53. TCHAR szBuf[1000];
  54. TCHAR szSystemDirectory[_MAX_PATH];
  55. COMMAND_ARGS ComArgs;
  56. HKEY hKey;
  57. DWORD dwrc;
  58. DWORD dwSizeValue;
  59. DWORD dwType;
  60. HANDLE hFile;
  61. WIN32_FIND_DATA FindFileData;
  62. MSIIsInstalled=FALSE;
  63. // Get this info for later use
  64. VersionInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
  65. GetVersionEx( &VersionInfo );
  66. GetSystemInfo( &SystemInfo );
  67. // Parse through the command line for the various arguments
  68. rc = GetCommandLineArgs(lpszCmdLine, &ComArgs );
  69. if (!rc) {
  70. _stprintf( szBuf, _T("%s%s%s%s%s"),
  71. _T(" Usage: \n\n"),
  72. _T(" setup.exe [ /q [ /i <InstDir> ] ]\n\n"),
  73. _T(" /q\tGive pop-ups only for errors\n\n"),
  74. _T(" /i\tInstall to <Instdir>\n\n"),
  75. _T(" /n\tInstall <msi package Name>\n\n")
  76. );
  77. MessageBox( NULL, szBuf, pszTitle, 0 );
  78. return (1);
  79. }
  80. //
  81. // Set the full path to this setup.exe
  82. //
  83. if (GetModuleFileName( NULL, szCommandFullPath, MAX_PATH ) == 0) {
  84. return(1);
  85. }
  86. // Put an end of string after the directory that this was
  87. // started from
  88. ch = szCommandFullPath + _tcslen(szCommandFullPath);
  89. while ( *ch != _T('\\') && ( ch > szCommandFullPath ) ) ch--;
  90. *ch=_T('\0');
  91. // This will become the full path and name of the MSI file to install
  92. _tcscpy( szMSIInstFile, szCommandFullPath);
  93. // Set the full path and name of the msi package
  94. _tcscpy( szPkgInstFile, szCommandFullPath);
  95. _tcscat( szPkgInstFile, _T("\\") );
  96. _tcscat( szPkgInstFile, ComArgs.szMsiName );
  97. // See if the package exists
  98. hFile = FindFirstFile( szPkgInstFile, &FindFileData );
  99. if ( hFile == INVALID_HANDLE_VALUE ) {
  100. _stprintf( szBuf, _T("%s%s%s%s"),
  101. _T("The Microsoft Debugging Tools package "),
  102. szPkgInstFile,
  103. _T(" does not exist.\n\nSetup cannot contine"),
  104. _T(" for this platform.")
  105. );
  106. MessageBox(NULL, szBuf, pszTitle, 0);
  107. return(1);
  108. }
  109. FindClose(hFile);
  110. // Set the command for installing the package
  111. _tcscpy( szPkgInstCommand, _T("msiexec /i ") );
  112. _tcscat( szPkgInstCommand, szPkgInstFile );
  113. // Set the command for removing the current package
  114. // that is installed.
  115. _tcscpy( szBuf, _T("") );
  116. dwrc = RegOpenKeyEx( HKEY_CURRENT_USER,
  117. ComArgs.szProductRegKey,
  118. 0,
  119. KEY_QUERY_VALUE,
  120. &hKey
  121. );
  122. if ( dwrc == ERROR_SUCCESS ) {
  123. _tcscpy( szBuf, _T("") );
  124. dwSizeValue=sizeof(szBuf);
  125. RegQueryValueEx ( hKey,
  126. _T("ProductCode"),
  127. 0,
  128. &dwType,
  129. (PBYTE)szBuf,
  130. &dwSizeValue
  131. );
  132. RegCloseKey(hKey);
  133. }
  134. // Set the command to remove the current package
  135. // that has an Add/Remove link in the start menu
  136. _tcscpy(szPkgRemoveCommand2, _T("") );
  137. if ( _tcslen(szBuf) > 0 ) {
  138. _tcscpy(szPkgRemoveCommand2, _T("msiexec /x ") );
  139. _tcscat(szPkgRemoveCommand2, szBuf);
  140. _tcscat(szPkgRemoveCommand2, _T(" REMOVETHEFILES=0 /qn") );
  141. }
  142. // Set the command to remove the current package so that
  143. // this program works like it used to.
  144. _tcscpy(szPkgRemoveCommand, _T("msiexec /x ") );
  145. _tcscat(szPkgRemoveCommand, szPkgInstFile );
  146. _tcscat(szPkgRemoveCommand, _T(" REMOVETHEFILES=0 /qn") );
  147. // Add a user override installation directory
  148. if ( _tcslen(ComArgs.szInstDir) > 0 ) {
  149. _tcscat( szPkgInstCommand, _T(" INSTDIR=") );
  150. _tcscat( szPkgInstCommand, ComArgs.szInstDir );
  151. } else if ( ComArgs.UIStressInstall ) {
  152. GetSystemDirectory( szSystemDirectory, _MAX_PATH );
  153. _tcscat( szPkgInstCommand, _T(" INSTDIR=") );
  154. _tcscat( szPkgInstCommand, szSystemDirectory );
  155. }
  156. // If this is an "undocumented" stress install
  157. // don't remove the files of the previous install
  158. // when you upgrade
  159. // FEATURESTOREMOVE should never actually need to be used, unless
  160. // the user has something screwed up on his system where the registry
  161. // key and products installed don't agree, or MSI thinks there's more
  162. // products installed than the registry key we look at.
  163. if ( ComArgs.StressInstall ) {
  164. _tcscat( szPkgInstCommand, _T(" FEATURESTOREMOVE=\"\"") );
  165. }
  166. // If this is an "undocumented" UI stress install
  167. // only install the private extensions
  168. if ( ComArgs.UIStressInstall ) {
  169. _tcscat( szPkgInstCommand,
  170. _T(" ADDLOCAL=DBG.DbgExts.Internal,DBG.NtsdFix.Internal") );
  171. }
  172. // Add the quiet switch
  173. // Save the command without a quiet switch
  174. _tcscpy( szPkgInstCommandNoQuiet, szPkgInstCommand);
  175. if ( ComArgs.QuietInstall ) {
  176. _tcscat( szPkgInstCommand, _T(" /qn") );
  177. }
  178. // Do version checks for whether msi is already installed
  179. //
  180. // If this is Windows 2000 and build number is >=
  181. // WIN2K_MIN_BUILD_X86 then MSI is installed
  182. // Don't try to run instmsi.exe on Windows 2000 because
  183. // you will get file system protection pop-ups.
  184. //
  185. if ( (VersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
  186. (VersionInfo.dwMajorVersion >= 5.0 ) ) {
  187. switch (SystemInfo.wProcessorArchitecture) {
  188. case PROCESSOR_ARCHITECTURE_INTEL:
  189. if (VersionInfo.dwBuildNumber < WIN2K_MIN_BUILD_X86 ) {
  190. // The version of MSI that is on early builds of Windows
  191. // 2000 shouldn't be trusted for installs.
  192. MessageBox(NULL,
  193. _T("The Debugging Tools does not install on ")
  194. _T("this version of Windows 2000. Please upgrade ")
  195. _T("your system to a retail version of Windows ")
  196. _T("2000 before trying to install this package."),
  197. pszTitle,
  198. 0);
  199. return(1);
  200. }
  201. break;
  202. case PROCESSOR_ARCHITECTURE_AMD64:
  203. case PROCESSOR_ARCHITECTURE_IA64:
  204. break;
  205. default:
  206. MessageBox(NULL, _T("Unknown computer architecture."), pszTitle ,0);
  207. return(1);
  208. }
  209. MSIIsInstalled = TRUE;
  210. } else if ( SystemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) {
  211. //
  212. // For Intel OS's prior to Windows 2000, run instmsi.exe
  213. //
  214. //
  215. // NT4 X86
  216. //
  217. if ( VersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ) {
  218. _tcscat( szMSIInstFile,
  219. _T("\\setup\\winnt\\i386\\instmsi.exe /q") );
  220. }
  221. //
  222. // Win9x
  223. //
  224. else if ( VersionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) {
  225. _tcscat( szMSIInstFile,
  226. _T("\\setup\\win9x\\instmsi.exe /q") );
  227. } else {
  228. MessageBox(NULL,
  229. _T("The Microsoft Debugging Tools does not install")
  230. _T(" on this system."),
  231. pszTitle,
  232. 0);
  233. return(1);
  234. }
  235. } else {
  236. MessageBox(NULL,
  237. _T("The Microsoft Debugging Tools cannot be installed")
  238. _T(" on this system."),
  239. pszTitle,
  240. 0);
  241. return(1);
  242. }
  243. // Install MSI if it is not already installed
  244. if ( !MSIIsInstalled ) {
  245. if ( RunCommand( szMSIInstFile, hInstance ) ) {
  246. MSIIsInstalled = TRUE;
  247. }
  248. if (!MSIIsInstalled) {
  249. MessageBox(NULL,
  250. _T("The Windows Installer could not be installed on ")
  251. _T("this system. This is required before installing")
  252. _T(" the Microsoft Debugging Tools package. Try ")
  253. _T("logging in as an administrator and try again."),
  254. pszTitle,
  255. 0);
  256. return(1);
  257. }
  258. }
  259. //
  260. // Now, if this is a stress install,
  261. // Try to remove the current package in case it is installed
  262. //
  263. if ( ComArgs.StressInstall ) {
  264. if ( _tcslen(szPkgRemoveCommand2) > 0 ) {
  265. RunCommand( szPkgRemoveCommand2, hInstance);
  266. }
  267. RunCommand( szPkgRemoveCommand, hInstance );
  268. if ( !RunCommand( szPkgInstCommand, hInstance ) ) {
  269. // Try again without the quiet switch, so that the user will get
  270. // a pop-up from dbg.msi and quit calling us
  271. MessageBox(NULL,
  272. _T("There were errors when trying to install the ")
  273. _T("debuggers.\nClick OK to attempt an install of the")
  274. _T(" debuggers with\n the GUI and you will see the")
  275. _T(" correct error message."),
  276. pszTitle,
  277. 0);
  278. if ( !RunCommand( szPkgInstCommandNoQuiet, hInstance ) ) {
  279. MessageBox(NULL,
  280. _T("There were still errors in the install.\n")
  281. _T("Please see http://dbg/top10.html ")
  282. _T("for more help."),
  283. pszTitle,
  284. 0);
  285. return(1);
  286. }
  287. }
  288. return(0);
  289. }
  290. //
  291. // Now, install the package dbg.msi
  292. //
  293. if ( !RunCommand( szPkgInstCommand, hInstance ) ) {
  294. if (ComArgs.QuietInstall) {
  295. _stprintf( szBuf, _T("%s %s %s %s"),
  296. _T("There were errors in the Debugging Tools install."),
  297. _T(" Please run "),
  298. szPkgInstFile,
  299. _T("to receive more detailed error information.")
  300. );
  301. MessageBox( NULL, szBuf, pszTitle,0);
  302. }
  303. return(1);
  304. }
  305. return(0);
  306. }
  307. //
  308. // RunCommand
  309. //
  310. // Purpose: Install MSI
  311. //
  312. // Return Values:
  313. // 0 error
  314. // 1 successful
  315. BOOL
  316. RunCommand( PTCHAR szCommandLine,
  317. HINSTANCE hInst)
  318. {
  319. BOOL rc;
  320. DWORD dwRet;
  321. PROCESS_INFORMATION ProcInfo = {0};
  322. STARTUPINFO SI= {0};
  323. // Spawn the command line specified by szCommandLine
  324. rc = CreateProcess(NULL,
  325. szCommandLine,
  326. NULL,
  327. NULL,
  328. FALSE,
  329. CREATE_DEFAULT_ERROR_MODE | NORMAL_PRIORITY_CLASS,
  330. NULL,
  331. NULL,
  332. &SI,
  333. &ProcInfo );
  334. if ( (!rc) || (!ProcInfo.hProcess) ) {
  335. goto cleanup;
  336. }
  337. //
  338. // Wait for command to complete ... Give it 20 minutes
  339. //
  340. dwRet = WaitForSingleObject(ProcInfo.hProcess, 1200000);
  341. if (dwRet != WAIT_OBJECT_0) {
  342. rc = FALSE;
  343. goto cleanup;
  344. }
  345. // Get the process exit code
  346. rc = GetExitCodeProcess( ProcInfo.hProcess, &dwRet);
  347. if (dwRet == ERROR_SUCCESS ) {
  348. rc = 1;
  349. } else {
  350. rc = 0;
  351. }
  352. cleanup:
  353. if (ProcInfo.hProcess)
  354. CloseHandle(ProcInfo.hProcess);
  355. return (rc);
  356. }
  357. BOOL
  358. GetCommandLineArgs(
  359. LPTSTR szCmdLine,
  360. PCOMMAND_ARGS pComArgs
  361. )
  362. {
  363. ULONG length;
  364. ULONG i,cur;
  365. BOOL SkippingSpaces=FALSE;
  366. BOOL QuotedString=FALSE;
  367. BOOL NeedSecond=FALSE;
  368. BOOL rc=TRUE;
  369. LPTSTR *argv;
  370. ULONG argc=0;
  371. LPTSTR szCmdLineTmp;
  372. TCHAR c;
  373. ZeroMemory(pComArgs, sizeof(COMMAND_ARGS));
  374. // Create a line to use for temporary marking
  375. length=_tcslen(szCmdLine);
  376. szCmdLineTmp= (LPTSTR)malloc( (_tcslen(szCmdLine) + 1) * sizeof(TCHAR) );
  377. if (szCmdLineTmp==NULL)
  378. {
  379. return FALSE;
  380. }
  381. _tcscpy(szCmdLineTmp, szCmdLine);
  382. // Count the number of arguments
  383. // Create a argv and argc
  384. SkippingSpaces=TRUE;
  385. QuotedString=FALSE;
  386. argc=0;
  387. for ( i=0; i<length; i++ )
  388. {
  389. c=szCmdLineTmp[i];
  390. switch (szCmdLineTmp[i]) {
  391. case _T(' '):
  392. case _T('\t'): if (QuotedString)
  393. {
  394. break;
  395. }
  396. if (!SkippingSpaces)
  397. {
  398. SkippingSpaces=TRUE;
  399. }
  400. break;
  401. case _T('\"'): if (QuotedString)
  402. {
  403. // This is the end of a quoted string
  404. // The next character to read in is a space
  405. QuotedString=FALSE;
  406. SkippingSpaces=TRUE;
  407. if ( i < (length-1) &&
  408. szCmdLineTmp[i+1] != _T(' ') &&
  409. szCmdLineTmp[i+1] != _T('\t') )
  410. {
  411. // This is the end of a quote and its not
  412. // followed by a space
  413. rc=FALSE;
  414. goto CommandLineFinish;
  415. }
  416. break;
  417. }
  418. if (SkippingSpaces) {
  419. // This is the beginning of a quoted string
  420. // Its a new argument and it follows spaces
  421. argc++;
  422. SkippingSpaces=FALSE;
  423. QuotedString=TRUE;
  424. break;
  425. }
  426. // This is an error -- This is a quote in the middle of a string
  427. rc=FALSE;
  428. goto CommandLineFinish;
  429. break;
  430. default: if (QuotedString) {
  431. break;
  432. }
  433. if (SkippingSpaces) {
  434. argc++;
  435. SkippingSpaces=FALSE;
  436. }
  437. break;
  438. }
  439. }
  440. if (QuotedString)
  441. {
  442. // Make sure that all the quotes got a finished pair
  443. rc=FALSE;
  444. goto CommandLineFinish;
  445. }
  446. // Now, create argv with the correct number of entries
  447. argv=(LPTSTR*)malloc(argc * sizeof(LPTSTR) );
  448. if (argv==NULL)
  449. {
  450. free(szCmdLineTmp);
  451. return FALSE;
  452. }
  453. // Set argv to point to the correct place on szCmdLineTmp
  454. // and put '\0' after each token.
  455. SkippingSpaces=TRUE;
  456. QuotedString=FALSE;
  457. argc=0;
  458. for ( i=0; i<length; i++ )
  459. {
  460. c=szCmdLineTmp[i];
  461. switch (szCmdLineTmp[i]) {
  462. case _T(' '):
  463. case _T('\t'): if (QuotedString)
  464. {
  465. break;
  466. }
  467. if (!SkippingSpaces)
  468. {
  469. szCmdLineTmp[i]='\0';
  470. SkippingSpaces=TRUE;
  471. }
  472. break;
  473. case _T('\"'): if (QuotedString)
  474. {
  475. // This is the end of a quoted string
  476. // The next character to read in is a space
  477. QuotedString=FALSE;
  478. SkippingSpaces=TRUE;
  479. szCmdLineTmp[i+1]=_T('\0');
  480. break;
  481. }
  482. if (SkippingSpaces) {
  483. // This is the beginning of a quoted string
  484. // Its a new argument and it follows spaces
  485. argv[argc]=szCmdLineTmp+i;
  486. argc++;
  487. SkippingSpaces=FALSE;
  488. QuotedString=TRUE;
  489. break;
  490. }
  491. // This is an error -- This is a quote in the middle of a string
  492. rc=FALSE;
  493. goto CommandLineFinish;
  494. break;
  495. default: if (QuotedString)
  496. {
  497. break;
  498. }
  499. if (SkippingSpaces) {
  500. argv[argc]=szCmdLineTmp+i;
  501. argc++;
  502. SkippingSpaces=FALSE;
  503. }
  504. break;
  505. }
  506. }
  507. // Now, parse the arguments
  508. NeedSecond=FALSE;
  509. for (i=0; i<argc; i++) {
  510. if (!NeedSecond)
  511. {
  512. if ( (argv[i][0] != '/') && (argv[i][0] != '-') )
  513. {
  514. rc=FALSE;
  515. goto CommandLineFinish;
  516. }
  517. if ( _tcslen(argv[i]) != 2 )
  518. {
  519. rc=FALSE;
  520. goto CommandLineFinish;
  521. }
  522. c=argv[i][1];
  523. switch ( c )
  524. {
  525. case 'q':
  526. case 'Q': pComArgs->QuietInstall=TRUE;
  527. break;
  528. case 'i':
  529. case 'I': NeedSecond=TRUE;;
  530. break;
  531. case 'n':
  532. case 'N': NeedSecond=TRUE;
  533. break;
  534. case 'z':
  535. case 'Z': pComArgs->StressInstall=TRUE;
  536. break;
  537. case 'u':
  538. case 'U': pComArgs->UIStressInstall=TRUE;
  539. pComArgs->StressInstall=TRUE;
  540. break;
  541. default: {
  542. rc=FALSE;
  543. goto CommandLineFinish;
  544. }
  545. }
  546. } else {
  547. NeedSecond = FALSE;
  548. switch ( c )
  549. {
  550. case 'i':
  551. case 'I': _tcscpy(pComArgs->szInstDir,argv[i]);
  552. break;
  553. case 'n':
  554. case 'N': _tcscpy(pComArgs->szMsiName,argv[i]);
  555. break;
  556. default: {
  557. rc=FALSE;
  558. goto CommandLineFinish;
  559. }
  560. }
  561. }
  562. }
  563. if (pComArgs->szMsiName[0] == 0)
  564. {
  565. #ifdef BUILD_X86
  566. _tcscpy(pComArgs->szMsiName, _T("dbg_x86.msi") );
  567. _tcscpy(pComArgs->szProductRegKey, _T("Software\\Microsoft\\DebuggingTools\\AddRemove") );
  568. #elif defined(BUILD_IA64)
  569. _tcscpy(pComArgs->szMsiName, _T("dbg_ia64.msi") );
  570. _tcscpy(pComArgs->szProductRegKey, _T("Software\\Microsoft\\DebuggingTools64\\AddRemove") );
  571. #elif defined(BUILD_AMD64)
  572. _tcscpy(pComArgs->szMsiName, _T("dbg_amd64.msi") );
  573. _tcscpy(pComArgs->szProductRegKey, _T("Software\\Microsoft\\DebuggingTools64\\AddRemove") );
  574. #endif
  575. }
  576. CommandLineFinish:
  577. free(szCmdLineTmp);
  578. free(argv);
  579. return (rc);
  580. }