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.
1043 lines
25 KiB
1043 lines
25 KiB
//========= 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; |
|
} |
|
} |
|
} |
|
|
|
|