source-engine/utils/vmpi/vmpi_service_install/ServiceInstallDlg.cpp
FluorescentCIAAfricanAmerican 3bf9df6b27 1
2020-04-22 12:56:21 -04:00

1044 lines
25 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
// JobWatchDlg.cpp : implementation file
//
#include "stdafx.h"
#include "ServiceInstallDlg.h"
#include "tier1/strtools.h"
#define DEFAULT_INSTALL_LOCATION "C:\\Program Files\\Valve\\vmpi_service"
#define HLKM_WINDOWS_RUN_KEY "Software\\Microsoft\\Windows\\CurrentVersion\\Run"
#define VMPI_SERVICE_VALUE_NAME "VMPI Service"
#define VMPI_SERVICE_UI_VALUE_NAME "VMPI Service UI"
// These are the files required for installation.
char *g_pInstallFiles[] =
{
"vmpi_service.exe",
"vmpi_service_ui.exe",
"WaitAndRestart.exe",
"vmpi_service_install.exe",
"tier0.dll",
"vmpi_transfer.exe",
"filesystem_stdio.dll",
"vstdlib.dll"
};
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
HWND g_hMessageControl = NULL;
HKEY g_hVMPIKey = NULL; // hklm/software/valve/vmpi.
bool g_bNoOutput = false;
bool g_bDontTouchUI = false;
bool g_bReinstalling = false;
FILE *g_fpLog = NULL;
char* FindArg( int argc, char **argv, const char *pArgName, char *pDefaultValue="" )
{
for ( int i=0; i < argc; i++ )
{
if ( stricmp( argv[i], pArgName ) == 0 )
{
if ( (i+1) >= argc )
return pDefaultValue;
else
return argv[i+1];
}
}
return NULL;
}
void CloseLog()
{
if ( g_fpLog )
{
fflush( g_fpLog );
fclose( g_fpLog );
flushall();
g_fpLog = NULL;
}
}
void OpenLog()
{
CloseLog();
g_fpLog = fopen( "c:\\vmpi_service_install.log", "wt" );
}
void AddToLog( const char *pMsg )
{
if ( g_fpLog )
{
fprintf( g_fpLog, "%s", pMsg );
}
}
SpewRetval_t MySpewOutputFunc( SpewType_t spewType, const tchar *pMsg )
{
AddToLog( pMsg );
if ( spewType == SPEW_MESSAGE || spewType == SPEW_WARNING )
{
// Format the message and send it to the control.
CUtlVector<char> msg;
msg.SetSize( V_strlen( pMsg )*2 + 1 );
char *pOut = msg.Base();
const char *pIn = pMsg;
while ( *pIn )
{
if ( *pIn == '\n' )
{
*pOut++ = '\r';
*pOut++ = '\n';
}
else
{
*pOut++ = *pIn;
}
++pIn;
}
*pOut = 0;
int nLen = (int)SendMessage( g_hMessageControl, EM_GETLIMITTEXT, 0, 0 );
SendMessage( g_hMessageControl, EM_SETSEL, nLen, nLen );
SendMessage( g_hMessageControl, EM_REPLACESEL, FALSE, (LPARAM)msg.Base() );
}
// Show a message box for warnings and errors.
if ( spewType == SPEW_ERROR || spewType == SPEW_WARNING )
{
if ( !g_bNoOutput )
AfxMessageBox( pMsg, MB_OK );
}
if ( spewType == SPEW_ERROR )
{
CloseLog();
TerminateProcess( GetCurrentProcess(), 2 );
}
return SPEW_CONTINUE;
}
void ScanDirectory( const char *pDirName, CUtlVector<CString> &subDirs, CUtlVector<CString> &files )
{
subDirs.Purge();
files.Purge();
char strPattern[MAX_PATH];
V_ComposeFileName( pDirName, "*.*", strPattern, sizeof( strPattern ) );
WIN32_FIND_DATA fileInfo; // File information
HANDLE hFile = ::FindFirstFile( strPattern, &fileInfo );
if ( hFile == INVALID_HANDLE_VALUE )
return;
do
{
if ( fileInfo.cFileName[0] == '.' )
continue;
if ( fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
subDirs.AddToTail( fileInfo.cFileName );
else
files.AddToTail( fileInfo.cFileName );
} while( ::FindNextFile(hFile, &fileInfo) );
::FindClose( hFile );
}
int DeleteDirectory( const char *pRootDir, bool bDeleteSubdirectories, char errorFile[MAX_PATH] )
{
errorFile[0] = 0;
CUtlVector<CString> subDirs, files;
ScanDirectory( pRootDir, subDirs, files );
// First nuke any subdirectories.
if ( bDeleteSubdirectories && !g_bReinstalling )
{
for ( int i=0; i < subDirs.Count(); i++ )
{
char fullName[MAX_PATH];
V_ComposeFileName( pRootDir, subDirs[i], fullName, sizeof( fullName ) );
// Delete subdirectory
int iRC = DeleteDirectory( fullName, bDeleteSubdirectories, errorFile );
if ( iRC )
return iRC;
}
}
for ( int i=0; i < files.Count(); i++ )
{
char fullName[MAX_PATH];
V_ComposeFileName( pRootDir, files[i], fullName, sizeof( fullName ) );
// Set file attributes
if ( !SetFileAttributes( fullName, FILE_ATTRIBUTE_NORMAL ) )
return GetLastError();
// Delete file
if ( !DeleteFile( fullName ) )
{
V_strncpy( errorFile, fullName, MAX_PATH );
return GetLastError();
}
}
if( !g_bReinstalling )
{
// Set directory attributes
if ( !SetFileAttributes( pRootDir, FILE_ATTRIBUTE_NORMAL ) )
return GetLastError();
// Delete directory
if ( !RemoveDirectory( pRootDir ) )
return GetLastError();
}
return 0;
}
bool CreateDirectory_R( const char *pDirName )
{
char chPrevDir[MAX_PATH];
V_strncpy( chPrevDir, pDirName, sizeof( chPrevDir ) );
if ( V_StripLastDir( chPrevDir, sizeof( chPrevDir ) ) )
{
if ( V_stricmp( chPrevDir, ".\\" ) != 0 && V_stricmp( chPrevDir, "./" ) != 0 )
if ( !CreateDirectory_R( chPrevDir ) )
return false;
}
if ( _access( pDirName, 0 ) == 0 )
return true;
return CreateDirectory( pDirName, NULL ) || GetLastError() == ERROR_ALREADY_EXISTS;
}
bool SetupStartMenuSubFolderName( const char *pSubFolderName, char *pOut, int outLen )
{
LPITEMIDLIST pidl;
// Get a pointer to an item ID list that represents the path of a special folder
HRESULT hr = SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_PROGRAMS, &pidl);
if ( hr != S_OK )
return false;
// Convert the item ID list's binary representation into a file system path
char szPath[_MAX_PATH];
BOOL f = SHGetPathFromIDList(pidl, szPath);
// Free the LPITEMIDLIST they gave us.
LPMALLOC pMalloc;
hr = SHGetMalloc(&pMalloc);
pMalloc->Free(pidl);
pMalloc->Release();
if ( f )
{
V_ComposeFileName( szPath, pSubFolderName, pOut, outLen );
return true;
}
else
{
return false;
}
}
bool CreateStartMenuLink( const char *pSubFolderName, const char *pLinkName, const char *pLinkTarget, const char *pArguments )
{
char fullFolderName[MAX_PATH];
if ( !SetupStartMenuSubFolderName( pSubFolderName, fullFolderName, sizeof( fullFolderName ) ) )
return false;
// Create the folder if necessary.
if ( !CreateDirectory_R( fullFolderName ) )
{
Msg( "CreateStartMenuLink failed - can't create directory %s.\n", fullFolderName );
return false;
}
IShellLink* psl = NULL;
// Get a pointer to the IShellLink interface.
bool bRet = false;
CoInitialize( NULL );
HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, reinterpret_cast<void**>(&psl));
if (SUCCEEDED(hres))
{
psl->SetPath( pLinkTarget ); // Set the path to the shortcut target
if ( pArguments )
psl->SetArguments( pArguments );
// Query IShellLink for the IPersistFile interface for saving
//the shortcut in persistent storage.
IPersistFile* ppf = NULL;
hres = psl->QueryInterface( IID_IPersistFile, reinterpret_cast<void**>(&ppf) );
if (SUCCEEDED(hres))
{
// Setup the filename for the link.
char linkFilename[MAX_PATH];
V_ComposeFileName( fullFolderName, pLinkName, linkFilename, sizeof( linkFilename ) );
V_strncat( linkFilename, ".lnk", sizeof( linkFilename ) );
// Ensure that the string is ANSI.
WCHAR wsz[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, linkFilename, -1, wsz, MAX_PATH);
// Save the link by calling IPersistFile::Save.
hres = ppf->Save( wsz, TRUE );
ppf->Release();
bRet = true;
}
psl->Release();
}
CoUninitialize();
return bRet;
}
char* GetLastErrorString()
{
static char err[2048];
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL
);
strncpy( err, (char*)lpMsgBuf, sizeof( err ) );
LocalFree( lpMsgBuf );
err[ sizeof( err ) - 1 ] = 0;
return err;
}
bool LaunchApp( char *pCommandLine, const char *pBaseDir )
{
STARTUPINFO si;
memset( &si, 0, sizeof( si ) );
si.cb = sizeof( si );
PROCESS_INFORMATION pi;
memset( &pi, 0, sizeof( pi ) );
return CreateProcess( NULL, pCommandLine, NULL, NULL, FALSE, 0, NULL, pBaseDir, &si, &pi ) != 0;
}
bool StartVMPIServiceUI( const char *pInstallLocation )
{
if ( g_bDontTouchUI )
{
Msg( "StartVMPIServiceUI: Ignoring due to -DontTouchUI.\n" );
return true;
}
char cmdLine[MAX_PATH];
V_ComposeFileName( pInstallLocation, "vmpi_service_ui.exe", cmdLine, sizeof( cmdLine ) );
return LaunchApp( cmdLine, pInstallLocation );
}
bool StartVMPIService( SC_HANDLE hSCManager )
{
bool bRet = true;
// First, get rid of an old service with the same name.
SC_HANDLE hMyService = OpenService( hSCManager, VMPI_SERVICE_NAME_INTERNAL, SERVICE_START );
if ( hMyService )
{
if ( StartService( hMyService, NULL, NULL ) )
{
Msg( "Started!\n" );
}
else
{
Error( "Can't start the service.\n" );
bRet = false;
}
}
else
{
Error( "Can't open service: %s\n", VMPI_SERVICE_NAME_INTERNAL );
bRet = false;
}
CloseServiceHandle( hMyService );
return bRet;
}
bool StopRunningApp()
{
if ( g_bDontTouchUI )
{
Msg( "StopRunningApp: -DontTouchUI was specified, so exiting before stopping the app.\n" );
return true;
}
// Send the
ISocket *pSocket = CreateIPSocket();
if ( pSocket )
{
if ( pSocket->BindToAny( 0 ) )
{
CUtlVector<char> protocolVersions;
protocolVersions.AddToTail( VMPI_PROTOCOL_VERSION );
if ( VMPI_PROTOCOL_VERSION == 5 )
protocolVersions.AddToTail( 4 ); // We want this installer to kill the previous services too.
for ( int iProtocolVersion=0; iProtocolVersion < protocolVersions.Count(); iProtocolVersion++ )
{
char cPacket[4] =
{
protocolVersions[iProtocolVersion],
VMPI_PASSWORD_OVERRIDE, // (force it to accept this message).
0,
VMPI_STOP_SERVICE
};
CIPAddr addr( 127, 0, 0, 1, 0 );
for ( int iPort=VMPI_SERVICE_PORT; iPort <= VMPI_LAST_SERVICE_PORT; iPort++ )
{
addr.port = iPort;
pSocket->SendTo( &addr, cPacket, sizeof( cPacket ) );
}
}
// Give it a sec to get the message and shutdown in case we're restarting.
Sleep( 2000 );
// This is the overkill method. If it didn't shutdown gracefully, kill it.
HMODULE hInst = LoadLibrary( "psapi.dll" );
if ( hInst )
{
typedef BOOL (WINAPI *EnumProcessesFn)(DWORD *lpidProcess, DWORD cb, DWORD *cbNeeded);
typedef BOOL (WINAPI *EnumProcessModulesFn)(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded );
typedef DWORD (WINAPI *GetModuleBaseNameFn)( HANDLE hProcess, HMODULE hModule, LPTSTR lpBaseName, DWORD nSize );
EnumProcessesFn EnumProcesses = (EnumProcessesFn)GetProcAddress( hInst, "EnumProcesses" );
EnumProcessModulesFn EnumProcessModules = (EnumProcessModulesFn)GetProcAddress( hInst, "EnumProcessModules" );
GetModuleBaseNameFn GetModuleBaseName = (GetModuleBaseNameFn)GetProcAddress( hInst, "GetModuleBaseNameA" );
if ( EnumProcessModules && EnumProcesses )
{
// Now just to make sure, kill the processes we're interested in.
DWORD procIDs[1024];
DWORD nBytes;
if ( EnumProcesses( procIDs, sizeof( procIDs ), &nBytes ) )
{
DWORD nProcs = nBytes / sizeof( procIDs[0] );
for ( DWORD i=0; i < nProcs; i++ )
{
HANDLE hProc = OpenProcess( PROCESS_ALL_ACCESS, FALSE, procIDs[i] );
if ( hProc )
{
HMODULE hModules[1024];
if ( EnumProcessModules( hProc, hModules, sizeof( hModules ), &nBytes ) )
{
DWORD nModules = nBytes / sizeof( hModules[0] );
for ( DWORD iModule=0; iModule < nModules; iModule++ )
{
char filename[512];
if ( GetModuleBaseName( hProc, hModules[iModule], filename, sizeof( filename ) ) )
{
if ( Q_stristr( filename, "vmpi_service.exe" ) || Q_stristr( filename, "vmpi_service_ui.exe" ) )
{
TerminateProcess( hProc, 1 );
CloseHandle( hProc );
hProc = NULL;
break;
}
}
}
}
CloseHandle( hProc );
}
}
}
}
FreeLibrary( hInst );
}
}
pSocket->Release();
}
return true;
}
bool StopOrDeleteService( SC_HANDLE hSCManager, bool bDelete )
{
bool bRet = true;
// First, get rid of an old service with the same name.
SC_HANDLE hOldService = OpenService( hSCManager, VMPI_SERVICE_NAME_INTERNAL, SERVICE_STOP | DELETE );
if ( hOldService )
{
// Stop the service.
Msg( "Found the service already running.\n" );
Msg( "Stopping service...\n" );
SERVICE_STATUS status;
ControlService( hOldService, SERVICE_CONTROL_STOP, &status );
if ( bDelete )
{
Msg( "Deleting service...\n" );
bool bExitedNicely = false;
DWORD startTime = GetTickCount();
while ( 1 )
{
BOOL bRet = DeleteService( hOldService );
if ( !bRet || bRet == ERROR_SERVICE_MARKED_FOR_DELETE )
{
Msg( "Deleted old service.\n" );
bExitedNicely = true;
break;
}
// Wait for the service to stop for 8 seconds.
if ( GetTickCount() - startTime > 8000 )
break;
}
if ( !bExitedNicely )
{
Error( "Couldn't delete the old '%s' service! Error: %s.\n", VMPI_SERVICE_NAME, GetLastErrorString() );
bRet = false;
}
}
CloseServiceHandle( hOldService );
}
return bRet;
}
bool GetExistingInstallationLocation( CString &strInstallLocation )
{
char buf[1024];
DWORD bufSize = sizeof( buf );
DWORD dwType;
if ( RegQueryValueEx( g_hVMPIKey, SERVICE_INSTALL_LOCATION_KEY, NULL, &dwType, (LPBYTE)buf, &bufSize ) == ERROR_SUCCESS )
{
if ( dwType == REG_SZ )
{
strInstallLocation = buf;
return true;
}
}
return false;
}
void RemoveRegistryKeys()
{
// Delete the run values (that tells it to run the app when the user logs in).
HKEY hKey = NULL;
RegCreateKey( HKEY_LOCAL_MACHINE, HLKM_WINDOWS_RUN_KEY, &hKey );
RegDeleteValue( hKey, VMPI_SERVICE_VALUE_NAME );
RegDeleteValue( hKey, VMPI_SERVICE_UI_VALUE_NAME );
// Get rid of the "InstallLocation" value.
RegDeleteValue( g_hVMPIKey, SERVICE_INSTALL_LOCATION_KEY );
}
bool IsAnInstallFile( const char *pName )
{
for ( int i=0; i < ARRAYSIZE( g_pInstallFiles ); i++ )
{
if ( V_stricmp( g_pInstallFiles[i], pName ) == 0 )
return true;
}
return false;
}
bool AnyNonInstallFilesInDirectory( const char *strInstallLocation )
{
char searchStr[MAX_PATH];
V_ComposeFileName( strInstallLocation, "*.*", searchStr, sizeof( searchStr ) );
_finddata_t data;
long handle = _findfirst( searchStr, &data );
if ( handle != -1 )
{
do
{
if ( data.name[0] == '.' || (data.attrib & _A_SUBDIR) != 0 )
continue;
if ( !IsAnInstallFile( data.name ) )
return true;
} while( _findnext( handle, &data ) == 0 );
_findclose( handle );
}
return false;
}
/////////////////////////////////////////////////////////////////////////////
// CServiceInstallDlg dialog
CServiceInstallDlg::CServiceInstallDlg(CWnd* pParent /*=NULL*/)
: CDialog(CServiceInstallDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CServiceInstallDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
}
CServiceInstallDlg::~CServiceInstallDlg()
{
}
void CServiceInstallDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CServiceInstallDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CServiceInstallDlg, CDialog)
//{{AFX_MSG_MAP(CServiceInstallDlg)
ON_BN_CLICKED(IDC_CANCEL_BUTTON, OnCancel)
ON_BN_CLICKED(IDC_INSTALL_BUTTON, OnInstall)
ON_BN_CLICKED(IDC_UNINSTALL_BUTTON2, OnUninstall)
ON_BN_CLICKED(IDC_START_EXISTING_BUTTON, OnStartExisting)
ON_BN_CLICKED(IDC_STOP_EXISTING_BUTTON, OnStopExisting)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CServiceInstallDlg message handlers
const char* FindArg( const char *pArgName, const char *pDefault="" )
{
for ( int i=1; i < __argc; i++ )
{
if ( Q_stricmp( pArgName, __argv[i] ) == 0 )
{
if ( (i+1) < __argc )
return __argv[i+1];
else
return pDefault;
}
}
return NULL;
}
BOOL CServiceInstallDlg::OnInitDialog()
{
CDialog::OnInitDialog();
HICON hIcon = LoadIcon( AfxGetInstanceHandle(), MAKEINTRESOURCE( IDR_MAINFRAME ) );
SetIcon( hIcon, true );
OpenLog();
// Setup the registry key for the install location.
if ( RegCreateKey( HKEY_LOCAL_MACHINE, VMPI_SERVICE_KEY, &g_hVMPIKey ) != ERROR_SUCCESS )
{
Error( "Can't open registry key: %s.", VMPI_SERVICE_KEY );
return FALSE;
}
VerifyInstallFiles();
g_hMessageControl = ::GetDlgItem( GetSafeHwnd(), IDC_TEXTOUTPUT );
SpewOutputFunc( MySpewOutputFunc );
// Init the service manager.
m_hSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
if ( !m_hSCManager )
{
Error( "OpenSCManager failed (%s)!\n", GetLastErrorString() );
return FALSE;
}
// See if there is a previous installation.
CString strInstallLocation;
if ( GetExistingInstallationLocation( strInstallLocation ) )
SetDlgItemText( IDC_INSTALL_LOCATION, strInstallLocation );
else
SetDlgItemText( IDC_INSTALL_LOCATION, DEFAULT_INSTALL_LOCATION );
// Now, if they passed in a command line option .
if ( FindArg( __argc, __argv, "-install_quiet" ) )
{
g_bReinstalling = true;
g_bNoOutput = true;
g_bDontTouchUI = (FindArg( __argc, __argv, "-DontTouchUI", NULL ) != 0);
OnInstall();
EndDialog( 0 );
}
else if ( FindArg( __argc, __argv, "-uninstall_quiet" ) )
{
g_bNoOutput = true;
DoUninstall( false );
EndDialog( 0 );
}
else if ( FindArg( __argc, __argv, "-start" ) )
{
OnStartExisting();
}
else if ( FindArg( __argc, __argv, "-stop" ) )
{
OnStopExisting();
}
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
void CServiceInstallDlg::OnCancel(void)
{
EndDialog( 1 );
}
// This function registers the service with the service manager.
bool InstallService( SC_HANDLE hSCManager, const char *pBaseDir )
{
char filename[512], uiFilename[512];
V_ComposeFileName( pBaseDir, "vmpi_service.exe", filename, sizeof( filename ) );
V_ComposeFileName( pBaseDir, "vmpi_service_ui.exe", uiFilename, sizeof( uiFilename ) );
// Try a to reinstall the service for up to 5 seconds.
Msg( "Creating new service...\n" );
SC_HANDLE hMyService = NULL;
DWORD startTime = GetTickCount();
while ( GetTickCount() - startTime < 5000 )
{
// Now reinstall it.
hMyService = CreateService(
hSCManager, // Service Control Manager database.
VMPI_SERVICE_NAME_INTERNAL, // Service name.
VMPI_SERVICE_NAME, // Display name.
SERVICE_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS,
SERVICE_AUTO_START, // Start automatically on system bootup.
SERVICE_ERROR_NORMAL,
filename, // Executable to register for the service.
NULL, // no load ordering group
NULL, // no tag identifier
NULL, // no dependencies
NULL, // account
NULL // password
);
if ( hMyService )
break;
else
Sleep( 300 );
}
if ( !hMyService )
{
Warning( "CreateService failed (%s)!\n", GetLastErrorString() );
CloseServiceHandle( hSCManager );
return false;
}
// Now setup the UI executable to run when their system starts.
HKEY hUIKey = NULL;
RegCreateKey( HKEY_LOCAL_MACHINE, HLKM_WINDOWS_RUN_KEY, &hUIKey );
if ( !hUIKey || RegSetValueEx( hUIKey, VMPI_SERVICE_UI_VALUE_NAME, 0, REG_SZ, (unsigned char*)uiFilename, strlen( uiFilename) + 1 ) != ERROR_SUCCESS )
{
Warning( "Can't install registry key for %s\n", uiFilename );
return false;
}
CloseServiceHandle( hMyService );
return true;
}
void SetupStartMenuLinks( const char *pInstallerFilename )
{
CreateStartMenuLink( "Valve\\VMPI", "Start VMPI Service", pInstallerFilename, "-start" );
CreateStartMenuLink( "Valve\\VMPI", "Stop VMPI Service", pInstallerFilename, "-stop" );
CreateStartMenuLink( "Valve\\VMPI", "Uninstall VMPI", pInstallerFilename, NULL );
}
void RemoveStartMenuLinks()
{
char fullFolderName[MAX_PATH];
if ( !SetupStartMenuSubFolderName( "Valve\\VMPI", fullFolderName, sizeof( fullFolderName ) ) )
return;
char errorFile[MAX_PATH];
if ( !DeleteDirectory( fullFolderName, true, errorFile ) )
{
Msg( "Unable to remove Start Menu items in %s.\n", fullFolderName );
}
}
void CServiceInstallDlg::OnInstall()
{
// Get the install location.
Msg( "Verifying install location.\n" );
CString strInstallLocation;
if ( !GetDlgItemText( IDC_INSTALL_LOCATION, strInstallLocation ) )
{
Error( "Can't get install location." );
return;
}
if ( strchr( strInstallLocation, ':' ) == NULL )
{
Warning( "Install location must be an absolute path (include a colon)." );
return;
}
// Stop the existing service.
if ( !DoUninstall( false ) )
return;
// Create the directory.
Msg( "Creating install directory %s.\n", strInstallLocation );
if ( !CreateDirectory_R( strInstallLocation ) )
{
Warning( "Unable to create directory: %s.", (const char*)strInstallLocation );
return;
}
// Copy the files down.
Msg( "Copying files.\n" );
char chDir[MAX_PATH];
GetModuleFileName( NULL, chDir, sizeof( chDir ) );
V_StripFilename( chDir );
for ( int i=0; i < ARRAYSIZE( g_pInstallFiles ); i++ )
{
char srcFilename[MAX_PATH], destFilename[MAX_PATH];
V_ComposeFileName( chDir, g_pInstallFiles[i], srcFilename, sizeof( srcFilename ) );
V_ComposeFileName( strInstallLocation, g_pInstallFiles[i], destFilename, sizeof( destFilename ) );
if ( !CopyFile( srcFilename, destFilename, FALSE ) )
{
Sleep( 2000 );
if ( !CopyFile( srcFilename, destFilename, FALSE ) )
{
Error( "CopyFile() failed.\nSrc: %s\nDest: %s\n%s", srcFilename, destFilename, GetLastErrorString() );
return;
}
}
}
// Register the service.
if ( !InstallService( m_hSCManager, strInstallLocation ) )
return;
// Write the new location to the registry.
Msg( "Updating registry.\n" );
if ( RegSetValueEx( g_hVMPIKey, SERVICE_INSTALL_LOCATION_KEY, 0, REG_SZ, (BYTE*)(const char*)strInstallLocation, V_strlen( strInstallLocation ) + 1 ) != ERROR_SUCCESS )
{
Error( "RegSetValueEx( %s, %s ) failed.", SERVICE_INSTALL_LOCATION_KEY, (const char*)strInstallLocation );
return;
}
// Setup start menu links.
char installerFilename[MAX_PATH];
V_ComposeFileName( strInstallLocation, "vmpi_service_install.exe", installerFilename, sizeof( installerFilename ) );
SetupStartMenuLinks( installerFilename );
// Start the new service.
Msg( "Starting new service.\n" );
if ( DoStartExisting() )
{
Warning( "Installed successfully!" );
}
}
bool CServiceInstallDlg::DoUninstall( bool bShowMessage )
{
// Figure out where to uninstall from.
CString strInstallLocation;
if ( !GetDlgItemText( IDC_INSTALL_LOCATION, strInstallLocation ) )
{
Error( "Can't get install location." );
return false;
}
if ( _access( strInstallLocation, 0 ) == 0 && !g_bNoOutput )
{
// Don't ask if they care if we delete all the files in that directory if the only exes in there are the install exes.
if ( AnyNonInstallFilesInDirectory( strInstallLocation ) )
{
char str[512];
V_snprintf( str, sizeof( str ), "Warning: this will delete all files under this directory: \n%s\nContinue?", strInstallLocation );
if ( AfxMessageBox( str, MB_YESNO ) != IDYES )
return false;
}
}
// Stop both the service and the win app.
bool bDone = StopRunningApp() && StopOrDeleteService( m_hSCManager, true );
if ( !bDone )
return false;
bool bSuccess = true;
RemoveRegistryKeys();
char errorFile[MAX_PATH];
if ( !NukeDirectory( strInstallLocation, errorFile ) )
{
// When reinstalling, the service may not be done exiting, so give it a sec.
Sleep( 2000 );
if ( !NukeDirectory( strInstallLocation, errorFile ) )
{
if ( errorFile[0] )
Msg( "NukeDirectory( %s ) failed.\nError on file: %s\n", strInstallLocation, errorFile );
else
Msg( "NukeDirectory( %s ) failed.\n", strInstallLocation );
Msg( "Uninstall complete, but files are left over in %s.\n", strInstallLocation );
bSuccess = false;
}
}
RemoveStartMenuLinks();
if ( bShowMessage && bSuccess )
AfxMessageBox( "Uninstall successful." );
return true;
}
void CServiceInstallDlg::OnUninstall()
{
DoUninstall( true );
}
void CServiceInstallDlg::OnStartExisting()
{
if ( DoStartExisting() )
AfxMessageBox( "Started successfully." );
}
bool CServiceInstallDlg::DoStartExisting()
{
StopRunningApp();
StopOrDeleteService( m_hSCManager, false );
CString strInstallLocation;
if ( !GetExistingInstallationLocation( strInstallLocation ) )
{
Error( "The VMPI service is not installed." );
return false;
}
if ( StartVMPIService( m_hSCManager ) )
{
return StartVMPIServiceUI( strInstallLocation );
}
else
{
return false;
}
}
void CServiceInstallDlg::OnStopExisting()
{
// Stop the app but don't delete it.
bool bDone = StopRunningApp() && StopOrDeleteService( m_hSCManager, false );
if ( bDone )
{
AfxMessageBox( "Service successfully stopped." );
}
}
bool CServiceInstallDlg::NukeDirectory( const char *pDir, char errorFile[MAX_PATH] )
{
// If the directory doesn't exist anyways, then return true..
if ( _access( pDir, 0 ) != 0 )
return true;
return DeleteDirectory( pDir, true, errorFile ) == 0;
}
void CServiceInstallDlg::VerifyInstallFiles()
{
char chDir[MAX_PATH];
GetModuleFileName( NULL, chDir, sizeof( chDir ) );
V_StripFilename( chDir );
for ( int i=0; i < ARRAYSIZE( g_pInstallFiles ); i++ )
{
char filename[MAX_PATH];
V_ComposeFileName( chDir, g_pInstallFiles[i], filename, sizeof( filename ) );
if ( _access( filename, 0 ) != 0 )
{
char szErrorMessage[MAX_PATH];
V_snprintf( szErrorMessage, sizeof( szErrorMessage ), "Required installation file missing: %s", filename );
AfxMessageBox( szErrorMessage );
return;
}
}
}