#include #include #include #include "tchar.h" #include "shlwapi.h" LPTSTR pszTitle = _T("Microsoft Debugging Tools"); #define MSI_BUILD_VER_X86 1029 // Latest MSI version for Win2K x86 #define WIN2K_MIN_BUILD_X86 2183 // Win2K RC3 typedef struct _CommandArgs { BOOL QuietInstall; BOOL StressInstall; BOOL UIStressInstall; TCHAR szInstDir[ _MAX_PATH*sizeof(TCHAR) ]; TCHAR szMsiName[ _MAX_PATH*sizeof(TCHAR) ]; TCHAR szProductRegKey[ _MAX_PATH*sizeof(TCHAR) ]; } COMMAND_ARGS, *PCOMMAND_ARGS; // Function prototypes BOOL RunCommand( PTCHAR szCommandLine, HINSTANCE hInst ); BOOL GetCommandLineArgs( LPTSTR szCmdLine, PCOMMAND_ARGS pComArgs ); TCHAR szMSIInstFile[_MAX_PATH*sizeof(TCHAR)]; TCHAR szPkgInstFile[_MAX_PATH*sizeof(TCHAR)]; TCHAR szPkgInstCommand[_MAX_PATH*2*sizeof(TCHAR)]; // For stress installs, this command will be used to // remove the current package but don't remove its // files, if the current package with the same // product ID is already installed. TCHAR szPkgRemoveCommand[_MAX_PATH*2*sizeof(TCHAR)]; TCHAR szPkgRemoveCommand2[_MAX_PATH*2*sizeof(TCHAR)]; // If the first install fails, stress tries again without // the quiet switch before giving a pop-up TCHAR szPkgInstCommandNoQuiet[_MAX_PATH*2*sizeof(TCHAR)]; TCHAR szCommandFullPath[_MAX_PATH*sizeof(TCHAR)]; int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpszCmdLine, int nCmdShow ) { OSVERSIONINFO VersionInfo; SYSTEM_INFO SystemInfo; BOOL rc; BOOL MSIIsInstalled; PTCHAR ch; TCHAR szBuf[1000]; TCHAR szSystemDirectory[_MAX_PATH]; COMMAND_ARGS ComArgs; HKEY hKey; DWORD dwrc; DWORD dwSizeValue; DWORD dwType; HANDLE hFile; WIN32_FIND_DATA FindFileData; MSIIsInstalled=FALSE; // Get this info for later use VersionInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO ); GetVersionEx( &VersionInfo ); GetSystemInfo( &SystemInfo ); // Parse through the command line for the various arguments rc = GetCommandLineArgs(lpszCmdLine, &ComArgs ); if (!rc) { _stprintf( szBuf, _T("%s%s%s%s%s"), _T(" Usage: \n\n"), _T(" setup.exe [ /q [ /i ] ]\n\n"), _T(" /q\tGive pop-ups only for errors\n\n"), _T(" /i\tInstall to \n\n"), _T(" /n\tInstall \n\n") ); MessageBox( NULL, szBuf, pszTitle, 0 ); return (1); } // // Set the full path to this setup.exe // if (GetModuleFileName( NULL, szCommandFullPath, MAX_PATH ) == 0) { return(1); } // Put an end of string after the directory that this was // started from ch = szCommandFullPath + _tcslen(szCommandFullPath); while ( *ch != _T('\\') && ( ch > szCommandFullPath ) ) ch--; *ch=_T('\0'); // This will become the full path and name of the MSI file to install _tcscpy( szMSIInstFile, szCommandFullPath); // Set the full path and name of the msi package _tcscpy( szPkgInstFile, szCommandFullPath); _tcscat( szPkgInstFile, _T("\\") ); _tcscat( szPkgInstFile, ComArgs.szMsiName ); // See if the package exists hFile = FindFirstFile( szPkgInstFile, &FindFileData ); if ( hFile == INVALID_HANDLE_VALUE ) { _stprintf( szBuf, _T("%s%s%s%s"), _T("The Microsoft Debugging Tools package "), szPkgInstFile, _T(" does not exist.\n\nSetup cannot contine"), _T(" for this platform.") ); MessageBox(NULL, szBuf, pszTitle, 0); return(1); } FindClose(hFile); // Set the command for installing the package _tcscpy( szPkgInstCommand, _T("msiexec /i ") ); _tcscat( szPkgInstCommand, szPkgInstFile ); // Set the command for removing the current package // that is installed. _tcscpy( szBuf, _T("") ); dwrc = RegOpenKeyEx( HKEY_CURRENT_USER, ComArgs.szProductRegKey, 0, KEY_QUERY_VALUE, &hKey ); if ( dwrc == ERROR_SUCCESS ) { _tcscpy( szBuf, _T("") ); dwSizeValue=sizeof(szBuf); RegQueryValueEx ( hKey, _T("ProductCode"), 0, &dwType, (PBYTE)szBuf, &dwSizeValue ); RegCloseKey(hKey); } // Set the command to remove the current package // that has an Add/Remove link in the start menu _tcscpy(szPkgRemoveCommand2, _T("") ); if ( _tcslen(szBuf) > 0 ) { _tcscpy(szPkgRemoveCommand2, _T("msiexec /x ") ); _tcscat(szPkgRemoveCommand2, szBuf); _tcscat(szPkgRemoveCommand2, _T(" REMOVETHEFILES=0 /qn") ); } // Set the command to remove the current package so that // this program works like it used to. _tcscpy(szPkgRemoveCommand, _T("msiexec /x ") ); _tcscat(szPkgRemoveCommand, szPkgInstFile ); _tcscat(szPkgRemoveCommand, _T(" REMOVETHEFILES=0 /qn") ); // Add a user override installation directory if ( _tcslen(ComArgs.szInstDir) > 0 ) { _tcscat( szPkgInstCommand, _T(" INSTDIR=") ); _tcscat( szPkgInstCommand, ComArgs.szInstDir ); } else if ( ComArgs.UIStressInstall ) { GetSystemDirectory( szSystemDirectory, _MAX_PATH ); _tcscat( szPkgInstCommand, _T(" INSTDIR=") ); _tcscat( szPkgInstCommand, szSystemDirectory ); } // If this is an "undocumented" stress install // don't remove the files of the previous install // when you upgrade // FEATURESTOREMOVE should never actually need to be used, unless // the user has something screwed up on his system where the registry // key and products installed don't agree, or MSI thinks there's more // products installed than the registry key we look at. if ( ComArgs.StressInstall ) { _tcscat( szPkgInstCommand, _T(" FEATURESTOREMOVE=\"\"") ); } // If this is an "undocumented" UI stress install // only install the private extensions if ( ComArgs.UIStressInstall ) { _tcscat( szPkgInstCommand, _T(" ADDLOCAL=DBG.DbgExts.Internal,DBG.NtsdFix.Internal") ); } // Add the quiet switch // Save the command without a quiet switch _tcscpy( szPkgInstCommandNoQuiet, szPkgInstCommand); if ( ComArgs.QuietInstall ) { _tcscat( szPkgInstCommand, _T(" /qn") ); } // Do version checks for whether msi is already installed // // If this is Windows 2000 and build number is >= // WIN2K_MIN_BUILD_X86 then MSI is installed // Don't try to run instmsi.exe on Windows 2000 because // you will get file system protection pop-ups. // if ( (VersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) && (VersionInfo.dwMajorVersion >= 5.0 ) ) { switch (SystemInfo.wProcessorArchitecture) { case PROCESSOR_ARCHITECTURE_INTEL: if (VersionInfo.dwBuildNumber < WIN2K_MIN_BUILD_X86 ) { // The version of MSI that is on early builds of Windows // 2000 shouldn't be trusted for installs. MessageBox(NULL, _T("The Debugging Tools does not install on ") _T("this version of Windows 2000. Please upgrade ") _T("your system to a retail version of Windows ") _T("2000 before trying to install this package."), pszTitle, 0); return(1); } break; case PROCESSOR_ARCHITECTURE_AMD64: case PROCESSOR_ARCHITECTURE_IA64: break; default: MessageBox(NULL, _T("Unknown computer architecture."), pszTitle ,0); return(1); } MSIIsInstalled = TRUE; } else if ( SystemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) { // // For Intel OS's prior to Windows 2000, run instmsi.exe // // // NT4 X86 // if ( VersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ) { _tcscat( szMSIInstFile, _T("\\setup\\winnt\\i386\\instmsi.exe /q") ); } // // Win9x // else if ( VersionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) { _tcscat( szMSIInstFile, _T("\\setup\\win9x\\instmsi.exe /q") ); } else { MessageBox(NULL, _T("The Microsoft Debugging Tools does not install") _T(" on this system."), pszTitle, 0); return(1); } } else { MessageBox(NULL, _T("The Microsoft Debugging Tools cannot be installed") _T(" on this system."), pszTitle, 0); return(1); } // Install MSI if it is not already installed if ( !MSIIsInstalled ) { if ( RunCommand( szMSIInstFile, hInstance ) ) { MSIIsInstalled = TRUE; } if (!MSIIsInstalled) { MessageBox(NULL, _T("The Windows Installer could not be installed on ") _T("this system. This is required before installing") _T(" the Microsoft Debugging Tools package. Try ") _T("logging in as an administrator and try again."), pszTitle, 0); return(1); } } // // Now, if this is a stress install, // Try to remove the current package in case it is installed // if ( ComArgs.StressInstall ) { if ( _tcslen(szPkgRemoveCommand2) > 0 ) { RunCommand( szPkgRemoveCommand2, hInstance); } RunCommand( szPkgRemoveCommand, hInstance ); if ( !RunCommand( szPkgInstCommand, hInstance ) ) { // Try again without the quiet switch, so that the user will get // a pop-up from dbg.msi and quit calling us MessageBox(NULL, _T("There were errors when trying to install the ") _T("debuggers.\nClick OK to attempt an install of the") _T(" debuggers with\n the GUI and you will see the") _T(" correct error message."), pszTitle, 0); if ( !RunCommand( szPkgInstCommandNoQuiet, hInstance ) ) { MessageBox(NULL, _T("There were still errors in the install.\n") _T("Please see http://dbg/top10.html ") _T("for more help."), pszTitle, 0); return(1); } } return(0); } // // Now, install the package dbg.msi // if ( !RunCommand( szPkgInstCommand, hInstance ) ) { if (ComArgs.QuietInstall) { _stprintf( szBuf, _T("%s %s %s %s"), _T("There were errors in the Debugging Tools install."), _T(" Please run "), szPkgInstFile, _T("to receive more detailed error information.") ); MessageBox( NULL, szBuf, pszTitle,0); } return(1); } return(0); } // // RunCommand // // Purpose: Install MSI // // Return Values: // 0 error // 1 successful BOOL RunCommand( PTCHAR szCommandLine, HINSTANCE hInst) { BOOL rc; DWORD dwRet; PROCESS_INFORMATION ProcInfo = {0}; STARTUPINFO SI= {0}; // Spawn the command line specified by szCommandLine rc = CreateProcess(NULL, szCommandLine, NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE | NORMAL_PRIORITY_CLASS, NULL, NULL, &SI, &ProcInfo ); if ( (!rc) || (!ProcInfo.hProcess) ) { goto cleanup; } // // Wait for command to complete ... Give it 20 minutes // dwRet = WaitForSingleObject(ProcInfo.hProcess, 1200000); if (dwRet != WAIT_OBJECT_0) { rc = FALSE; goto cleanup; } // Get the process exit code rc = GetExitCodeProcess( ProcInfo.hProcess, &dwRet); if (dwRet == ERROR_SUCCESS ) { rc = 1; } else { rc = 0; } cleanup: if (ProcInfo.hProcess) CloseHandle(ProcInfo.hProcess); return (rc); } BOOL GetCommandLineArgs( LPTSTR szCmdLine, PCOMMAND_ARGS pComArgs ) { ULONG length; ULONG i,cur; BOOL SkippingSpaces=FALSE; BOOL QuotedString=FALSE; BOOL NeedSecond=FALSE; BOOL rc=TRUE; LPTSTR *argv; ULONG argc=0; LPTSTR szCmdLineTmp; TCHAR c; ZeroMemory(pComArgs, sizeof(COMMAND_ARGS)); // Create a line to use for temporary marking length=_tcslen(szCmdLine); szCmdLineTmp= (LPTSTR)malloc( (_tcslen(szCmdLine) + 1) * sizeof(TCHAR) ); if (szCmdLineTmp==NULL) { return FALSE; } _tcscpy(szCmdLineTmp, szCmdLine); // Count the number of arguments // Create a argv and argc SkippingSpaces=TRUE; QuotedString=FALSE; argc=0; for ( i=0; iQuietInstall=TRUE; break; case 'i': case 'I': NeedSecond=TRUE;; break; case 'n': case 'N': NeedSecond=TRUE; break; case 'z': case 'Z': pComArgs->StressInstall=TRUE; break; case 'u': case 'U': pComArgs->UIStressInstall=TRUE; pComArgs->StressInstall=TRUE; break; default: { rc=FALSE; goto CommandLineFinish; } } } else { NeedSecond = FALSE; switch ( c ) { case 'i': case 'I': _tcscpy(pComArgs->szInstDir,argv[i]); break; case 'n': case 'N': _tcscpy(pComArgs->szMsiName,argv[i]); break; default: { rc=FALSE; goto CommandLineFinish; } } } } if (pComArgs->szMsiName[0] == 0) { #ifdef BUILD_X86 _tcscpy(pComArgs->szMsiName, _T("dbg_x86.msi") ); _tcscpy(pComArgs->szProductRegKey, _T("Software\\Microsoft\\DebuggingTools\\AddRemove") ); #elif defined(BUILD_IA64) _tcscpy(pComArgs->szMsiName, _T("dbg_ia64.msi") ); _tcscpy(pComArgs->szProductRegKey, _T("Software\\Microsoft\\DebuggingTools64\\AddRemove") ); #elif defined(BUILD_AMD64) _tcscpy(pComArgs->szMsiName, _T("dbg_amd64.msi") ); _tcscpy(pComArgs->szProductRegKey, _T("Software\\Microsoft\\DebuggingTools64\\AddRemove") ); #endif } CommandLineFinish: free(szCmdLineTmp); free(argv); return (rc); }