Windows-Server-2003/sdktools/debuggers/setup/msisetup.c

721 lines
20 KiB
C

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#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 <InstDir> ] ]\n\n"),
_T(" /q\tGive pop-ups only for errors\n\n"),
_T(" /i\tInstall to <Instdir>\n\n"),
_T(" /n\tInstall <msi package Name>\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; i<length; i++ )
{
c=szCmdLineTmp[i];
switch (szCmdLineTmp[i]) {
case _T(' '):
case _T('\t'): if (QuotedString)
{
break;
}
if (!SkippingSpaces)
{
SkippingSpaces=TRUE;
}
break;
case _T('\"'): if (QuotedString)
{
// This is the end of a quoted string
// The next character to read in is a space
QuotedString=FALSE;
SkippingSpaces=TRUE;
if ( i < (length-1) &&
szCmdLineTmp[i+1] != _T(' ') &&
szCmdLineTmp[i+1] != _T('\t') )
{
// This is the end of a quote and its not
// followed by a space
rc=FALSE;
goto CommandLineFinish;
}
break;
}
if (SkippingSpaces) {
// This is the beginning of a quoted string
// Its a new argument and it follows spaces
argc++;
SkippingSpaces=FALSE;
QuotedString=TRUE;
break;
}
// This is an error -- This is a quote in the middle of a string
rc=FALSE;
goto CommandLineFinish;
break;
default: if (QuotedString) {
break;
}
if (SkippingSpaces) {
argc++;
SkippingSpaces=FALSE;
}
break;
}
}
if (QuotedString)
{
// Make sure that all the quotes got a finished pair
rc=FALSE;
goto CommandLineFinish;
}
// Now, create argv with the correct number of entries
argv=(LPTSTR*)malloc(argc * sizeof(LPTSTR) );
if (argv==NULL)
{
free(szCmdLineTmp);
return FALSE;
}
// Set argv to point to the correct place on szCmdLineTmp
// and put '\0' after each token.
SkippingSpaces=TRUE;
QuotedString=FALSE;
argc=0;
for ( i=0; i<length; i++ )
{
c=szCmdLineTmp[i];
switch (szCmdLineTmp[i]) {
case _T(' '):
case _T('\t'): if (QuotedString)
{
break;
}
if (!SkippingSpaces)
{
szCmdLineTmp[i]='\0';
SkippingSpaces=TRUE;
}
break;
case _T('\"'): if (QuotedString)
{
// This is the end of a quoted string
// The next character to read in is a space
QuotedString=FALSE;
SkippingSpaces=TRUE;
szCmdLineTmp[i+1]=_T('\0');
break;
}
if (SkippingSpaces) {
// This is the beginning of a quoted string
// Its a new argument and it follows spaces
argv[argc]=szCmdLineTmp+i;
argc++;
SkippingSpaces=FALSE;
QuotedString=TRUE;
break;
}
// This is an error -- This is a quote in the middle of a string
rc=FALSE;
goto CommandLineFinish;
break;
default: if (QuotedString)
{
break;
}
if (SkippingSpaces) {
argv[argc]=szCmdLineTmp+i;
argc++;
SkippingSpaces=FALSE;
}
break;
}
}
// Now, parse the arguments
NeedSecond=FALSE;
for (i=0; i<argc; i++) {
if (!NeedSecond)
{
if ( (argv[i][0] != '/') && (argv[i][0] != '-') )
{
rc=FALSE;
goto CommandLineFinish;
}
if ( _tcslen(argv[i]) != 2 )
{
rc=FALSE;
goto CommandLineFinish;
}
c=argv[i][1];
switch ( c )
{
case 'q':
case 'Q': pComArgs->QuietInstall=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);
}