785 lines
31 KiB
C++
785 lines
31 KiB
C++
|
//+----------------------------------------------------------------------------
|
||
|
//
|
||
|
// File: cmstp.cpp
|
||
|
//
|
||
|
// Module: CMSTP.EXE
|
||
|
//
|
||
|
// Synopsis: This file is the main function for the CM profile installer. This
|
||
|
// file basically processes command line switches for the installer and
|
||
|
// then launches the appropriate function.
|
||
|
//
|
||
|
// Copyright (c) 1998-1999 Microsoft Corporation
|
||
|
//
|
||
|
// Author: quintinb Created 07/13/98
|
||
|
//
|
||
|
//+----------------------------------------------------------------------------
|
||
|
|
||
|
#include "cmmaster.h"
|
||
|
#include "installerfuncs.h"
|
||
|
#include "cmstpex.h"
|
||
|
//
|
||
|
// Text Constants
|
||
|
//
|
||
|
static const TCHAR CMSTPMUTEXNAME[] = TEXT("Connection Manager Profile Installer Mutex");
|
||
|
|
||
|
|
||
|
//
|
||
|
// Global Dynamic Library Classes to hold the ras dll's and shell32. See
|
||
|
// the EnsureRasDllsLoaded and the EnsureShell32Loaded in common.cpp/common.h
|
||
|
//
|
||
|
CDynamicLibrary* g_pRasApi32 = NULL;
|
||
|
CDynamicLibrary* g_pRnaph = NULL;
|
||
|
CDynamicLibrary* g_pShell32 = NULL;
|
||
|
CDynamicLibrary* g_pNetShell = NULL;
|
||
|
|
||
|
|
||
|
//
|
||
|
// Function Headers
|
||
|
//
|
||
|
BOOL PromptUserToUninstallProfile(HINSTANCE hInstance, LPCTSTR pszInfFile); // from uninstall.cpp
|
||
|
BOOL PromptUserToUninstallCm(HINSTANCE hInstance); // from uninstallcm.cpp
|
||
|
|
||
|
|
||
|
//
|
||
|
// Enum for the LastManOut function which follows.
|
||
|
//
|
||
|
typedef enum _UNINSTALLTYPE
|
||
|
{
|
||
|
PROFILEUNINSTALL, // a profile is being uninstalled
|
||
|
CMUNINSTALL // the cm bits themselves are being uninstalled.
|
||
|
|
||
|
} UNINSTALLTYPE;
|
||
|
|
||
|
|
||
|
//+----------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: LastManOut
|
||
|
//
|
||
|
// Synopsis: This function determines if the current uninstall action is the
|
||
|
// last uninstall action which should then delete cmstp.exe. If the
|
||
|
// uninstall action is a profile uninstall we need to check that
|
||
|
// cm has already been uninstalled and that there is only one profile
|
||
|
// installed currently (the one we are about to delete). If the
|
||
|
// uninstall action is uninstalling CM then we need to make sure there
|
||
|
// are no other profiles on the machine. Notice that this function
|
||
|
// never returns TRUE on Native CM platforms. If it did, then cmstp.exe
|
||
|
// would be deleted inadvertently even though UninstallCm wouldn't
|
||
|
// actually delete the rest of CM.
|
||
|
//
|
||
|
// Arguments: UNINSTALLTYPE UninstallType - an enum value which tells if this is
|
||
|
// a profile uninstall or a CM uninstall.
|
||
|
//
|
||
|
// Returns: BOOL - TRUE if this install is the last one out and cmstp.exe should
|
||
|
// be deleted.
|
||
|
//
|
||
|
// History: quintinb Created 6/28/99
|
||
|
//
|
||
|
//+----------------------------------------------------------------------------
|
||
|
BOOL LastManOut(UNINSTALLTYPE UninstallType, LPCTSTR pszInfFile)
|
||
|
{
|
||
|
BOOL bReturn = FALSE;
|
||
|
|
||
|
//
|
||
|
// First check to make sure that remcmstp.inf doesn't exist in the system
|
||
|
// directory. If it does, then we know that Cmstp.exe has already determined
|
||
|
// that it is the last man and should delete itself. Thus it wrote the cmstp.exe
|
||
|
// command into remcmstp.inf and the inf engine will delete cmstp.exe when it is done.
|
||
|
// Thus we need to check for this file and if it exists return FALSE.
|
||
|
//
|
||
|
|
||
|
TCHAR szSystemDir[MAX_PATH+1];
|
||
|
TCHAR szTemp[MAX_PATH+1];
|
||
|
|
||
|
if (0 == GetSystemDirectory(szSystemDir, CELEMS(szSystemDir)))
|
||
|
{
|
||
|
CMASSERTMSG(FALSE, TEXT("LastManOut -- Unable to obtain a path to the System Directory"));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
wsprintf(szTemp, TEXT("%s\\remcmstp.inf"), szSystemDir);
|
||
|
|
||
|
if (FileExists(szTemp))
|
||
|
{
|
||
|
CMTRACE1(TEXT("\tDetected remcmstp.inf, not setting last man out -- Process ID is 0x%x "), GetCurrentProcessId());
|
||
|
Sleep(2000); // we sleep here to put a little delay in the processing to let any other copies
|
||
|
// of cmstp.exe clean themselves up. I found that on a system with several copies of
|
||
|
// cmstp.exe all deleting profiles and then a cmstp to delete CM, not all of the cmstps
|
||
|
// would clean up in time and thus cmstp.exe wouldn't get deleted. A sleep is hokey, but
|
||
|
// two seconds in the last man out situation only fixes it and it no down level user should
|
||
|
// ever have 8 profiles (which was home many I tested it with) let alone delete them
|
||
|
// all at once. It works fine for deleting two profiles and CM simultaneously either way.
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Make sure that we aren't trying to Remove cmstp.exe on a platform where CM is Native.
|
||
|
// If CM is Native, then always return FALSE because the CM uninstall function won't
|
||
|
// uninstall CM and we don't want to accidently delete cmstp.exe.
|
||
|
//
|
||
|
|
||
|
if (!CmIsNative())
|
||
|
{
|
||
|
if (PROFILEUNINSTALL == UninstallType)
|
||
|
{
|
||
|
//
|
||
|
// We are uninstalling a profile. We need to check to see if CM has been deleted and
|
||
|
// if there are any other profiles on the machine besides the one we are going to delete.
|
||
|
//
|
||
|
wsprintf(szTemp, TEXT("%s\\cmdial32.dll"), szSystemDir);
|
||
|
|
||
|
if (!FileExists(szTemp))
|
||
|
{
|
||
|
//
|
||
|
// Then we know that CM is already gone. We need to check and see if any other
|
||
|
// profiles exist besides the one we are about to delete.
|
||
|
//
|
||
|
HKEY hKey;
|
||
|
DWORD dwNumValues;
|
||
|
TCHAR szServiceName[MAX_PATH+1];
|
||
|
|
||
|
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_pszRegCmMappings, 0,
|
||
|
KEY_READ, &hKey))
|
||
|
{
|
||
|
if ((ERROR_SUCCESS == RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL,
|
||
|
&dwNumValues, NULL, NULL, NULL, NULL)) && (dwNumValues == 1))
|
||
|
{
|
||
|
//
|
||
|
// Then we have only the one profile mappings key, is it the correct one?
|
||
|
//
|
||
|
if (0 != GetPrivateProfileString(c_pszInfSectionStrings, c_pszCmEntryServiceName,
|
||
|
TEXT(""), szServiceName, MAX_PATH, pszInfFile))
|
||
|
{
|
||
|
DWORD dwSize = MAX_PATH;
|
||
|
LONG lResult = RegQueryValueEx(hKey, szServiceName, NULL,
|
||
|
NULL, (LPBYTE)szTemp, &dwSize);
|
||
|
|
||
|
if ((ERROR_SUCCESS == lResult) && (TEXT('\0') != szTemp[0]))
|
||
|
{
|
||
|
CMTRACE1(TEXT("\tDetected Last Man Out -- Process ID is 0x%x "), GetCurrentProcessId());
|
||
|
bReturn = TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
RegCloseKey(hKey);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (CMUNINSTALL == UninstallType)
|
||
|
{
|
||
|
//
|
||
|
// We are uninstalling CM. We want to make sure that we don't have any profiles
|
||
|
// still installed. If not, then we are the last man out.
|
||
|
//
|
||
|
if (!AllUserProfilesInstalled())
|
||
|
{
|
||
|
CMTRACE1(TEXT("\tDetected Last Man Out -- Process ID is 0x%x "), GetCurrentProcessId());
|
||
|
bReturn = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CMASSERTMSG(FALSE, TEXT("LastManOut -- Unknown Uninstall Type"));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return bReturn;
|
||
|
}
|
||
|
|
||
|
//+----------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: ExtractInfAndRelaunchCmstp
|
||
|
//
|
||
|
// Synopsis: This function is used to cleanup Cmstp.exe in the last man out
|
||
|
// scenario. In order to not leave cmstp.exe on a users machine,
|
||
|
// we must extract remcmstp.inf and write the uninstall command to it.
|
||
|
// That way, the inf will monitor the cmstp.exe process and when it is
|
||
|
// finished it can then delete cmstp.exe.
|
||
|
//
|
||
|
// Arguments: HINSTANCE hInstance - Instance handle to load resources
|
||
|
// DWORD dwFlags - Command line param flags
|
||
|
// LPCTSTR szInfPath - path to the inf file.
|
||
|
//
|
||
|
// Returns: BOOL -- TRUE if Successful
|
||
|
//
|
||
|
// History: quintinb Created 6/28/99
|
||
|
//
|
||
|
//+----------------------------------------------------------------------------
|
||
|
BOOL ExtractInfAndRelaunchCmstp(HINSTANCE hInstance, DWORD dwFlags, LPCTSTR pszInfPath)
|
||
|
{
|
||
|
|
||
|
//
|
||
|
// Check Parameters
|
||
|
//
|
||
|
|
||
|
if (0 == dwFlags || NULL == pszInfPath || TEXT('\0') == pszInfPath[0])
|
||
|
{
|
||
|
CMASSERTMSG(FALSE, TEXT("Invalid Paramater passed to ExtractInfAndRelaunchCmstp."));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get the Path to the System Directory
|
||
|
//
|
||
|
TCHAR szSystemDir[MAX_PATH+1];
|
||
|
if (0 == GetSystemDirectory(szSystemDir, CELEMS(szSystemDir)))
|
||
|
{
|
||
|
CMASSERTMSG(FALSE, TEXT("ExtractInfAndRelaunchCmstp -- Unable to obtain a path to the System Directory"));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Extract remcmstp.inf
|
||
|
//
|
||
|
HGLOBAL hRemCmstp = NULL;
|
||
|
LPTSTR pszRemCmstpInf = NULL;
|
||
|
HRSRC hResource = FindResource(hInstance, MAKEINTRESOURCE(IDT_REMCMSTP_INF), TEXT("REGINST"));
|
||
|
|
||
|
if (hResource)
|
||
|
{
|
||
|
hRemCmstp = LoadResource(hInstance, hResource);
|
||
|
|
||
|
if (hRemCmstp)
|
||
|
{
|
||
|
//
|
||
|
// Note that we don't need to call FreeResource, which is obsolete, this
|
||
|
// will be cleaned up when cmstp.exe exits.
|
||
|
//
|
||
|
pszRemCmstpInf = (LPTSTR)LockResource(hRemCmstp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Now that we have the remcmstp.inf file that is stored in the cmstp.exe resource
|
||
|
// loaded into memory and have a pointer to it, lets create the file that we are
|
||
|
// going to write it out to.
|
||
|
//
|
||
|
if (pszRemCmstpInf)
|
||
|
{
|
||
|
TCHAR szRemCmstpPath[MAX_PATH+1];
|
||
|
wsprintf(szRemCmstpPath, TEXT("%s\\remcmstp.inf"), szSystemDir);
|
||
|
|
||
|
HANDLE hFile = CreateFile(szRemCmstpPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS,
|
||
|
FILE_ATTRIBUTE_NORMAL, NULL);
|
||
|
|
||
|
if (INVALID_HANDLE_VALUE != hFile)
|
||
|
{
|
||
|
//
|
||
|
// Then we have the file, lets write the data to it.
|
||
|
//
|
||
|
DWORD cbWritten;
|
||
|
|
||
|
if (WriteFile(hFile, pszRemCmstpInf, lstrlen(pszRemCmstpInf)*sizeof(TCHAR),
|
||
|
&cbWritten, NULL))
|
||
|
{
|
||
|
//
|
||
|
// We launch the inf to delete cmstp right now. The inf has a PreSetupCommand that
|
||
|
// launches the cmstp.exe uninstall command with a /s switch (which we write in the
|
||
|
// inf after extracting it). The inf then launches the new cmstp, which forces the newly
|
||
|
// launched cmstp.exe to wait on the mutex of the current cmstp.exe until it is finished.
|
||
|
// Since profile installs will error on the mutex instead of waiting for it, we
|
||
|
// shouldn't get any installs until after the uninstall and the cleanup inf have run.
|
||
|
// Note that the inf will wait for the PreSetupCommands to finish before processing the inf.
|
||
|
// This is important because we could be waiting on User input (the OK dialog from
|
||
|
// deleting CM for instance).
|
||
|
//
|
||
|
CloseHandle(hFile);
|
||
|
|
||
|
//
|
||
|
// Now lets write the cmstp.exe command into remcmstp.inf
|
||
|
//
|
||
|
LPTSTR pszUninstallFlag = NULL;
|
||
|
if (dwFlags & c_dwUninstallCm)
|
||
|
{
|
||
|
pszUninstallFlag = c_pszUninstallCm;
|
||
|
}
|
||
|
else if (dwFlags & c_dwUninstall)
|
||
|
{
|
||
|
pszUninstallFlag = c_pszUninstall;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CMASSERTMSG(FALSE, TEXT("ExtractInfAndRelaunchCmstp -- Unknown Uninstall Type, exiting"));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
TCHAR szShortInfPath[MAX_PATH+1] = {0};
|
||
|
TCHAR szParams[2*MAX_PATH+1] = {0};
|
||
|
|
||
|
DWORD dwRet = GetShortPathName(pszInfPath, szShortInfPath, MAX_PATH);
|
||
|
|
||
|
if (0 == dwRet || MAX_PATH < dwRet)
|
||
|
{
|
||
|
CMASSERTMSG(FALSE, TEXT("ExtractInfAndRelaunchCmstp -- Unable to get the short path to the Inf, exiting"));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
wsprintf(szParams, TEXT("%s\\cmstp.exe %s %s %s"), szSystemDir, pszUninstallFlag, c_pszSilent, szShortInfPath);
|
||
|
|
||
|
WritePrivateProfileSection(TEXT("PreSetupCommandsSection"), szParams, szRemCmstpPath);
|
||
|
|
||
|
//
|
||
|
// Finally lets launch the inf uninstall with the new cmstp command in it.
|
||
|
//
|
||
|
wsprintf(szParams,
|
||
|
TEXT("advpack.dll,LaunchINFSection %s\\remcmstp.inf, Uninstall"),
|
||
|
szSystemDir);
|
||
|
|
||
|
SHELLEXECUTEINFO sei = {0};
|
||
|
|
||
|
sei.cbSize = sizeof(sei);
|
||
|
sei.fMask = SEE_MASK_FLAG_NO_UI;
|
||
|
sei.nShow = SW_SHOWNORMAL;
|
||
|
sei.lpFile = TEXT("Rundll32.exe");
|
||
|
sei.lpParameters = szParams;
|
||
|
sei.lpDirectory = szSystemDir;
|
||
|
|
||
|
if (!ShellExecuteEx(&sei))
|
||
|
{
|
||
|
CMTRACE1(TEXT("ExtractInfAndRelaunchCmstp -- ShellExecute Returned an error, GLE %d"), GetLastError());
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CloseHandle(hFile);
|
||
|
CMASSERTMSG(FALSE, TEXT("ExtractInfAndRelaunchCmstp -- Unable to write the file data to remcmstp.inf"));
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CMASSERTMSG(FALSE, TEXT("ExtractInfAndRelaunchCmstp -- Unable to Create remcmstp.inf in the system directory."));
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CMASSERTMSG(FALSE, TEXT("ExtractInfAndRelaunchCmstp -- Unable to load the remcmstp.inf custom resource."));
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//+----------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: IsInstall
|
||
|
//
|
||
|
// Synopsis: Wrapper function to check and see if this is an install or not.
|
||
|
//
|
||
|
// Arguments: DWORD dwFlags - the action flags parameter returned from the
|
||
|
// command line parsing class.
|
||
|
//
|
||
|
// Returns: BOOL - TRUE if this is an Install command
|
||
|
//
|
||
|
// History: quintinb Created Header 6/28/99
|
||
|
//
|
||
|
//+----------------------------------------------------------------------------
|
||
|
BOOL IsInstall(DWORD dwFlags)
|
||
|
{
|
||
|
return (0 == (dwFlags & 0xFF));
|
||
|
}
|
||
|
|
||
|
//+----------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: ProcessCmstpExtensionDll
|
||
|
//
|
||
|
// Synopsis: Processes the cmstp extension dll registry keys and calls out
|
||
|
// to the extension proc as necessary to modify the action behavior.
|
||
|
// Using the extension proc, we can modify the install, uninstall,
|
||
|
// etc. behavior that cmstp exhibits. This is most useful on platforms
|
||
|
// that have Native CM (or just a very new copy of CM) but an older
|
||
|
// profile is being installed. Since the cmstp.exe that is in the package
|
||
|
// does the actual installation, we can modify the installation parameters,
|
||
|
// modify the inf path, or even stop the install. Since we get called
|
||
|
// after the install as well, we can even take post-install or cleanup
|
||
|
// actions.
|
||
|
//
|
||
|
// Arguments: LPDWORD pdwFlags - pointer to the flags parameter, note that it
|
||
|
// can be modified by the extension proc
|
||
|
// LPTSTR pszInfPath - Inf path, note that it can be modified
|
||
|
// by the extension proc.
|
||
|
// HRESULT hrRet - current return value, this is only used on
|
||
|
// the post action proc call.
|
||
|
// EXTENSIONDLLPROCTIMES PreOrPost - if this is a Pre action
|
||
|
// call or a Post action call.
|
||
|
//
|
||
|
// Returns: BOOL - TRUE if cmstp.exe should continue, FALSE stops the action
|
||
|
// (install, uninstall, migration, whatever) without further
|
||
|
// action.
|
||
|
//
|
||
|
// History: quintinb Created Header 6/28/99
|
||
|
//
|
||
|
//+----------------------------------------------------------------------------
|
||
|
BOOL ProcessCmstpExtensionDll (LPDWORD pdwFlags, LPTSTR pszInfPath, HRESULT hrRet, EXTENSIONDLLPROCTIMES PreOrPost)
|
||
|
{
|
||
|
|
||
|
//
|
||
|
// Check for the CmstpExtensionDll reg key in Cm App Paths
|
||
|
//
|
||
|
const TCHAR* const c_pszRegCmstpExtensionDll = TEXT("CmstpExtensionDll");
|
||
|
const char* const c_pszCmstpExtensionProc = "CmstpExtensionProc"; // GetProcAddress takes ANSI strings -- quintinb
|
||
|
pfnCmstpExtensionProcSpec pfnCmstpExtensionProc = NULL;
|
||
|
|
||
|
HKEY hKey;
|
||
|
|
||
|
TCHAR szCmstpExtensionDllPath[MAX_PATH+1];
|
||
|
ZeroMemory(szCmstpExtensionDllPath, CELEMS(szCmstpExtensionDllPath));
|
||
|
|
||
|
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_pszRegCmAppPaths, 0, KEY_READ, &hKey))
|
||
|
{
|
||
|
DWORD dwSize = CELEMS(szCmstpExtensionDllPath);
|
||
|
DWORD dwType = REG_SZ;
|
||
|
|
||
|
if (ERROR_SUCCESS == RegQueryValueEx(hKey, c_pszRegCmstpExtensionDll, NULL, &dwType,
|
||
|
(LPBYTE)szCmstpExtensionDllPath, &dwSize))
|
||
|
{
|
||
|
CDynamicLibrary CmstpExtensionDll (szCmstpExtensionDllPath);
|
||
|
|
||
|
pfnCmstpExtensionProc = (pfnCmstpExtensionProcSpec)CmstpExtensionDll.GetProcAddress(c_pszCmstpExtensionProc);
|
||
|
if (NULL == pfnCmstpExtensionProc)
|
||
|
{
|
||
|
return TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return (pfnCmstpExtensionProc)(pdwFlags, pszInfPath, hrRet, PreOrPost);
|
||
|
}
|
||
|
}
|
||
|
RegCloseKey(hKey);
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//_____________________________________________________________________________
|
||
|
//
|
||
|
// Function: WinMain
|
||
|
//
|
||
|
// Synopsis: Processes command line switches -- see common\inc\cmstpex.h for full list
|
||
|
//
|
||
|
//
|
||
|
// Arguments: HINSTANCE hInstance -
|
||
|
// HINSTANCE hPrevInstance -
|
||
|
// PSTR szCmdLine - pass in the inf file name here
|
||
|
// int iCmdShow -
|
||
|
//
|
||
|
// Returns: int WINAPI -
|
||
|
//
|
||
|
// History: Re-created quintinb 7-13-98
|
||
|
//
|
||
|
//_____________________________________________________________________________
|
||
|
int WINAPI
|
||
|
WinMain (HINSTANCE, //hInstance
|
||
|
HINSTANCE, //hPrevInstance
|
||
|
PSTR, //szCmdLine
|
||
|
int //iCmdShow
|
||
|
)
|
||
|
{
|
||
|
CMTRACE(TEXT("====================================================="));
|
||
|
CMTRACE1(TEXT(" CMSTP.EXE - LOADING - Process ID is 0x%x "), GetCurrentProcessId());
|
||
|
CMTRACE(TEXT("====================================================="));
|
||
|
|
||
|
BOOL bUsageError = FALSE;
|
||
|
BOOL bAnotherInstanceRunning = FALSE;
|
||
|
HRESULT hrReturn = S_OK;
|
||
|
TCHAR szMsg[MAX_PATH+1];
|
||
|
TCHAR szTitle[MAX_PATH+1];
|
||
|
TCHAR szInfPath[MAX_PATH+1];
|
||
|
DWORD dwFlags = 0;
|
||
|
CPlatform plat;
|
||
|
CNamedMutex CmstpMutex; // keep this here so it doesn't get destructed until main ends.
|
||
|
// this gives us better control of when it is unlocked.
|
||
|
|
||
|
HINSTANCE hInstance = GetModuleHandleA(NULL);
|
||
|
LPTSTR szCmdLine = GetCommandLine();
|
||
|
|
||
|
//
|
||
|
// Check to make sure that we aren't an x86 version of cmstp running on an Alpha
|
||
|
//
|
||
|
#ifdef CMX86BUILD
|
||
|
if (plat.IsAlpha())
|
||
|
{
|
||
|
MYVERIFY(0 != LoadString(hInstance, IDS_CMSTP_TITLE, szTitle, MAX_PATH));
|
||
|
MYVERIFY(0 != LoadString(hInstance, IDS_BINARY_NOT_ALPHA, szMsg, MAX_PATH));
|
||
|
|
||
|
MessageBox(NULL, szMsg, szTitle, MB_OK);
|
||
|
return FALSE;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// Setup the Command Line Arguments
|
||
|
//
|
||
|
|
||
|
ZeroMemory(szInfPath, sizeof(szInfPath));
|
||
|
|
||
|
{ // Make sure ArgProcessor gets destructed properly and we don't leak mem
|
||
|
|
||
|
CProcessCmdLn ArgProcessor(c_NumArgs, (ArgStruct*)&Args, TRUE,
|
||
|
FALSE); //bSkipFirstToken == TRUE, bBlankCmdLnOkay == FALSE
|
||
|
|
||
|
if (ArgProcessor.GetCmdLineArgs(szCmdLine, &dwFlags, szInfPath, MAX_PATH))
|
||
|
{
|
||
|
|
||
|
//
|
||
|
// We want to wait indefinitely, unless this is an install. If it is an
|
||
|
// install then we want to return immediately and throw an error if we couldn't
|
||
|
// get the lock (NTRAID 261248). We also want to be able to launch two profiles
|
||
|
// simulaneously on NT5 (cmstp.exe takes the place of explorer.exe) thus we will
|
||
|
// pass the pointer to the CNamedMutex object to the install function so that
|
||
|
// it can release the mutex once the install is finished except for launching the
|
||
|
// profile (NTRAID 310478).
|
||
|
//
|
||
|
BOOL bWait = !IsInstall(dwFlags);
|
||
|
|
||
|
if (CmstpMutex.Lock(CMSTPMUTEXNAME, bWait, INFINITE))
|
||
|
{
|
||
|
//
|
||
|
// We got the mutex lock, so go ahead and process the command line
|
||
|
// arguments. First, however, check for a cmstp Dll listed in the
|
||
|
// app paths key of CM. If a dll is listed here, then we want to load
|
||
|
// the dll and pass it the inf path and the install flags. If the dll
|
||
|
// proc returns FALSE, then we want to exit. Otherwise continue with
|
||
|
// the install as normal.
|
||
|
// Of the install flags we first check for /x, ,/m, or /mp
|
||
|
// (these switches must be by themselves, we don't allow any
|
||
|
// modifier switches with these), the non-install commands. We now allow the uninstall
|
||
|
// command to take the Silent switch to silence our uninstall prompt.
|
||
|
//
|
||
|
|
||
|
if (ProcessCmstpExtensionDll(&dwFlags, szInfPath, S_OK, PRE))
|
||
|
{
|
||
|
CMTRACE2(TEXT("CMSTP.EXE -- Entering Flag Processing Loop, dwFlags = %u and szInfPath = %s"), dwFlags, szInfPath);
|
||
|
if (c_dwHelp & dwFlags)
|
||
|
{
|
||
|
bUsageError = TRUE;
|
||
|
}
|
||
|
else if (c_dwUninstall & dwFlags)
|
||
|
{
|
||
|
if (((c_dwUninstall == dwFlags) || ((c_dwUninstall | c_dwSilent) == dwFlags)) &&
|
||
|
(TEXT('\0') != szInfPath[0]))
|
||
|
{
|
||
|
BOOL bSilent = (dwFlags & c_dwSilent);
|
||
|
|
||
|
if (bSilent || PromptUserToUninstallProfile(hInstance, szInfPath))
|
||
|
{
|
||
|
//
|
||
|
// Okay, the user wants to uninstall. Now check to see if we are the last
|
||
|
// man out. If we are then we also need to delete cmstp.
|
||
|
//
|
||
|
|
||
|
if (LastManOut(PROFILEUNINSTALL, szInfPath))
|
||
|
{
|
||
|
ExtractInfAndRelaunchCmstp(hInstance, dwFlags, szInfPath);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hrReturn = UninstallProfile(hInstance, szInfPath, TRUE); // bCleanUpCreds == TRUE
|
||
|
MYVERIFY(SUCCEEDED(hrReturn));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bUsageError = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else if (c_dwOsMigration & dwFlags)
|
||
|
{
|
||
|
if ((c_dwOsMigration == dwFlags) && (TEXT('\0') == szInfPath[0]))
|
||
|
{
|
||
|
hrReturn = MigrateCmProfilesForWin2kUpgrade(hInstance);
|
||
|
MYVERIFY(SUCCEEDED(hrReturn));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bUsageError = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else if (c_dwProfileMigration & dwFlags)
|
||
|
{
|
||
|
if ((c_dwProfileMigration == dwFlags) && (TEXT('\0') == szInfPath[0]))
|
||
|
{
|
||
|
TCHAR szCurrentDir[MAX_PATH+1];
|
||
|
if (0 == GetCurrentDirectory(MAX_PATH, szCurrentDir))
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
lstrcat(szCurrentDir, TEXT("\\"));
|
||
|
|
||
|
hrReturn = MigrateOldCmProfilesForProfileInstall(hInstance, szCurrentDir);
|
||
|
MYVERIFY(SUCCEEDED(hrReturn));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bUsageError = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else if (c_dwUninstallCm & dwFlags)
|
||
|
{
|
||
|
if (((c_dwUninstallCm == dwFlags) || ((c_dwUninstallCm | c_dwSilent) == dwFlags)) &&
|
||
|
(TEXT('\0') != szInfPath[0]))
|
||
|
{
|
||
|
BOOL bNoBeginPrompt = (dwFlags & c_dwSilent);
|
||
|
|
||
|
if (bNoBeginPrompt || PromptUserToUninstallCm(hInstance))
|
||
|
{
|
||
|
//
|
||
|
// Okay, the user wants to uninstall. Now check to see if we are the last
|
||
|
// man out. If we are then we also need to delete cmstp.
|
||
|
//
|
||
|
|
||
|
if (LastManOut(CMUNINSTALL, szInfPath))
|
||
|
{
|
||
|
if (ExtractInfAndRelaunchCmstp(hInstance, dwFlags, szInfPath))
|
||
|
{
|
||
|
//
|
||
|
// We need to delete the Uninstall key so that we don't leave
|
||
|
// it in Add/Remove Programs (the refresh is keyed off of this
|
||
|
// executable ending not the relaunched cmstp.exe's ending).
|
||
|
// NTRAID 336249
|
||
|
//
|
||
|
HRESULT hrTemp = HrRegDeleteKeyTree(HKEY_LOCAL_MACHINE,
|
||
|
TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Connection Manager"));
|
||
|
MYDBGASSERT(SUCCEEDED(hrTemp));
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hrReturn = UninstallCm(hInstance, szInfPath);
|
||
|
MYVERIFY(SUCCEEDED(hrReturn));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bUsageError = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// Install, note that on NT5 we will release the CmstpMutex once
|
||
|
// we are finished installing and just want to launch the profile.
|
||
|
//
|
||
|
hrReturn = InstallInf(hInstance, szInfPath,
|
||
|
(dwFlags & c_dwNoSupportFiles), (dwFlags & c_dwNoLegacyIcon),
|
||
|
(dwFlags & c_dwNoNT5Shortcut), (dwFlags & c_dwSilent),
|
||
|
(dwFlags & c_dwSingleUser), (dwFlags & c_dwSetDefaultCon), &CmstpMutex);
|
||
|
|
||
|
if (FAILED(hrReturn))
|
||
|
{
|
||
|
CMTRACE2("Cmstp.exe -- InstallInf failed with error %d (0x%lx)", hrReturn, hrReturn);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Again call the Cmstp Extension Dll if one exists. We want to give it
|
||
|
// a chance to take post install actions if necessary.
|
||
|
|
||
|
ProcessCmstpExtensionDll(&dwFlags, szInfPath, hrReturn, POST);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bAnotherInstanceRunning = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bUsageError = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Clean up our Dll's
|
||
|
//
|
||
|
if (g_pRasApi32)
|
||
|
{
|
||
|
g_pRasApi32->Unload();
|
||
|
CmFree(g_pRasApi32);
|
||
|
}
|
||
|
|
||
|
if (g_pRnaph)
|
||
|
{
|
||
|
g_pRnaph->Unload();
|
||
|
CmFree(g_pRnaph);
|
||
|
}
|
||
|
|
||
|
if (g_pShell32)
|
||
|
{
|
||
|
g_pShell32->Unload();
|
||
|
CmFree(g_pShell32);
|
||
|
}
|
||
|
|
||
|
if (g_pNetShell)
|
||
|
{
|
||
|
g_pNetShell->Unload();
|
||
|
CmFree(g_pNetShell);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// UnLock the cmstp mutex, note that it may never have been locked or
|
||
|
// it could have been unlocked on Windows 2000 upon launching a profile,
|
||
|
// the named mutex class will handle this.
|
||
|
//
|
||
|
CmstpMutex.Unlock();
|
||
|
|
||
|
//
|
||
|
// Display any error messages after unlocking the mutex so that don't hold
|
||
|
// it in the Usage message case. Another instance running should only
|
||
|
// happen when an install tries to acquire the mutex while another cmstp
|
||
|
// is running, thus the mutex was never acquired but put the message code
|
||
|
// here to keep it in one place.
|
||
|
//
|
||
|
if (bUsageError)
|
||
|
{
|
||
|
CMTRACE("Cmstp.exe -- Usage Error!");
|
||
|
if (0 == (dwFlags & c_dwSilent))
|
||
|
{
|
||
|
const int c_MsgLen = 1024;
|
||
|
TCHAR* pszMsg = (TCHAR*)CmMalloc(sizeof(TCHAR)*(c_MsgLen+1));
|
||
|
if (pszMsg)
|
||
|
{
|
||
|
MYVERIFY(0 != LoadString(hInstance, IDS_CMSTP_TITLE, szTitle, MAX_PATH));
|
||
|
MYVERIFY(0 != LoadString(hInstance, IDS_USAGE_MSG, pszMsg, c_MsgLen));
|
||
|
|
||
|
MessageBox(NULL, pszMsg, szTitle, MB_OK | MB_ICONINFORMATION);
|
||
|
CmFree(pszMsg);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (bAnotherInstanceRunning)
|
||
|
{
|
||
|
MYVERIFY(0 != LoadString(hInstance, IDS_CMSTP_TITLE, szTitle, MAX_PATH));
|
||
|
MYVERIFY(0 != LoadString(hInstance, IDS_INUSE_MSG, szMsg, MAX_PATH));
|
||
|
|
||
|
MessageBox(NULL, szMsg, szTitle, MB_OK);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check for memory leaks
|
||
|
//
|
||
|
EndDebugMemory();
|
||
|
|
||
|
//
|
||
|
// get return value
|
||
|
//
|
||
|
BOOL bRet = SUCCEEDED(hrReturn) && !bUsageError && !bAnotherInstanceRunning;
|
||
|
|
||
|
//
|
||
|
// Since we don't link to libc, we need to do this ourselves.
|
||
|
//
|
||
|
CMTRACE(TEXT("====================================================="));
|
||
|
CMTRACE1(TEXT(" CMSTP.EXE - UNLOADING - Process ID is 0x%x "), GetCurrentProcessId());
|
||
|
CMTRACE(TEXT("====================================================="));
|
||
|
|
||
|
ExitProcess((UINT)bRet);
|
||
|
return bRet;
|
||
|
}
|
||
|
|