Team Fortress 2 Source Code as on 22/4/2020
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

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. // JobWatchDlg.cpp : implementation file
  9. //
  10. #include "stdafx.h"
  11. #include "ServiceInstallDlg.h"
  12. #include "tier1/strtools.h"
  13. #define DEFAULT_INSTALL_LOCATION "C:\\Program Files\\Valve\\vmpi_service"
  14. #define HLKM_WINDOWS_RUN_KEY "Software\\Microsoft\\Windows\\CurrentVersion\\Run"
  15. #define VMPI_SERVICE_VALUE_NAME "VMPI Service"
  16. #define VMPI_SERVICE_UI_VALUE_NAME "VMPI Service UI"
  17. // These are the files required for installation.
  18. char *g_pInstallFiles[] =
  19. {
  20. "vmpi_service.exe",
  21. "vmpi_service_ui.exe",
  22. "WaitAndRestart.exe",
  23. "vmpi_service_install.exe",
  24. "tier0.dll",
  25. "vmpi_transfer.exe",
  26. "filesystem_stdio.dll",
  27. "vstdlib.dll"
  28. };
  29. #ifdef _DEBUG
  30. #define new DEBUG_NEW
  31. #undef THIS_FILE
  32. static char THIS_FILE[] = __FILE__;
  33. #endif
  34. HWND g_hMessageControl = NULL;
  35. HKEY g_hVMPIKey = NULL; // hklm/software/valve/vmpi.
  36. bool g_bNoOutput = false;
  37. bool g_bDontTouchUI = false;
  38. bool g_bReinstalling = false;
  39. FILE *g_fpLog = NULL;
  40. char* FindArg( int argc, char **argv, const char *pArgName, char *pDefaultValue="" )
  41. {
  42. for ( int i=0; i < argc; i++ )
  43. {
  44. if ( stricmp( argv[i], pArgName ) == 0 )
  45. {
  46. if ( (i+1) >= argc )
  47. return pDefaultValue;
  48. else
  49. return argv[i+1];
  50. }
  51. }
  52. return NULL;
  53. }
  54. void CloseLog()
  55. {
  56. if ( g_fpLog )
  57. {
  58. fflush( g_fpLog );
  59. fclose( g_fpLog );
  60. flushall();
  61. g_fpLog = NULL;
  62. }
  63. }
  64. void OpenLog()
  65. {
  66. CloseLog();
  67. g_fpLog = fopen( "c:\\vmpi_service_install.log", "wt" );
  68. }
  69. void AddToLog( const char *pMsg )
  70. {
  71. if ( g_fpLog )
  72. {
  73. fprintf( g_fpLog, "%s", pMsg );
  74. }
  75. }
  76. SpewRetval_t MySpewOutputFunc( SpewType_t spewType, const tchar *pMsg )
  77. {
  78. AddToLog( pMsg );
  79. if ( spewType == SPEW_MESSAGE || spewType == SPEW_WARNING )
  80. {
  81. // Format the message and send it to the control.
  82. CUtlVector<char> msg;
  83. msg.SetSize( V_strlen( pMsg )*2 + 1 );
  84. char *pOut = msg.Base();
  85. const char *pIn = pMsg;
  86. while ( *pIn )
  87. {
  88. if ( *pIn == '\n' )
  89. {
  90. *pOut++ = '\r';
  91. *pOut++ = '\n';
  92. }
  93. else
  94. {
  95. *pOut++ = *pIn;
  96. }
  97. ++pIn;
  98. }
  99. *pOut = 0;
  100. int nLen = (int)SendMessage( g_hMessageControl, EM_GETLIMITTEXT, 0, 0 );
  101. SendMessage( g_hMessageControl, EM_SETSEL, nLen, nLen );
  102. SendMessage( g_hMessageControl, EM_REPLACESEL, FALSE, (LPARAM)msg.Base() );
  103. }
  104. // Show a message box for warnings and errors.
  105. if ( spewType == SPEW_ERROR || spewType == SPEW_WARNING )
  106. {
  107. if ( !g_bNoOutput )
  108. AfxMessageBox( pMsg, MB_OK );
  109. }
  110. if ( spewType == SPEW_ERROR )
  111. {
  112. CloseLog();
  113. TerminateProcess( GetCurrentProcess(), 2 );
  114. }
  115. return SPEW_CONTINUE;
  116. }
  117. void ScanDirectory( const char *pDirName, CUtlVector<CString> &subDirs, CUtlVector<CString> &files )
  118. {
  119. subDirs.Purge();
  120. files.Purge();
  121. char strPattern[MAX_PATH];
  122. V_ComposeFileName( pDirName, "*.*", strPattern, sizeof( strPattern ) );
  123. WIN32_FIND_DATA fileInfo; // File information
  124. HANDLE hFile = ::FindFirstFile( strPattern, &fileInfo );
  125. if ( hFile == INVALID_HANDLE_VALUE )
  126. return;
  127. do
  128. {
  129. if ( fileInfo.cFileName[0] == '.' )
  130. continue;
  131. if ( fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
  132. subDirs.AddToTail( fileInfo.cFileName );
  133. else
  134. files.AddToTail( fileInfo.cFileName );
  135. } while( ::FindNextFile(hFile, &fileInfo) );
  136. ::FindClose( hFile );
  137. }
  138. int DeleteDirectory( const char *pRootDir, bool bDeleteSubdirectories, char errorFile[MAX_PATH] )
  139. {
  140. errorFile[0] = 0;
  141. CUtlVector<CString> subDirs, files;
  142. ScanDirectory( pRootDir, subDirs, files );
  143. // First nuke any subdirectories.
  144. if ( bDeleteSubdirectories && !g_bReinstalling )
  145. {
  146. for ( int i=0; i < subDirs.Count(); i++ )
  147. {
  148. char fullName[MAX_PATH];
  149. V_ComposeFileName( pRootDir, subDirs[i], fullName, sizeof( fullName ) );
  150. // Delete subdirectory
  151. int iRC = DeleteDirectory( fullName, bDeleteSubdirectories, errorFile );
  152. if ( iRC )
  153. return iRC;
  154. }
  155. }
  156. for ( int i=0; i < files.Count(); i++ )
  157. {
  158. char fullName[MAX_PATH];
  159. V_ComposeFileName( pRootDir, files[i], fullName, sizeof( fullName ) );
  160. // Set file attributes
  161. if ( !SetFileAttributes( fullName, FILE_ATTRIBUTE_NORMAL ) )
  162. return GetLastError();
  163. // Delete file
  164. if ( !DeleteFile( fullName ) )
  165. {
  166. V_strncpy( errorFile, fullName, MAX_PATH );
  167. return GetLastError();
  168. }
  169. }
  170. if( !g_bReinstalling )
  171. {
  172. // Set directory attributes
  173. if ( !SetFileAttributes( pRootDir, FILE_ATTRIBUTE_NORMAL ) )
  174. return GetLastError();
  175. // Delete directory
  176. if ( !RemoveDirectory( pRootDir ) )
  177. return GetLastError();
  178. }
  179. return 0;
  180. }
  181. bool CreateDirectory_R( const char *pDirName )
  182. {
  183. char chPrevDir[MAX_PATH];
  184. V_strncpy( chPrevDir, pDirName, sizeof( chPrevDir ) );
  185. if ( V_StripLastDir( chPrevDir, sizeof( chPrevDir ) ) )
  186. {
  187. if ( V_stricmp( chPrevDir, ".\\" ) != 0 && V_stricmp( chPrevDir, "./" ) != 0 )
  188. if ( !CreateDirectory_R( chPrevDir ) )
  189. return false;
  190. }
  191. if ( _access( pDirName, 0 ) == 0 )
  192. return true;
  193. return CreateDirectory( pDirName, NULL ) || GetLastError() == ERROR_ALREADY_EXISTS;
  194. }
  195. bool SetupStartMenuSubFolderName( const char *pSubFolderName, char *pOut, int outLen )
  196. {
  197. LPITEMIDLIST pidl;
  198. // Get a pointer to an item ID list that represents the path of a special folder
  199. HRESULT hr = SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_PROGRAMS, &pidl);
  200. if ( hr != S_OK )
  201. return false;
  202. // Convert the item ID list's binary representation into a file system path
  203. char szPath[_MAX_PATH];
  204. BOOL f = SHGetPathFromIDList(pidl, szPath);
  205. // Free the LPITEMIDLIST they gave us.
  206. LPMALLOC pMalloc;
  207. hr = SHGetMalloc(&pMalloc);
  208. pMalloc->Free(pidl);
  209. pMalloc->Release();
  210. if ( f )
  211. {
  212. V_ComposeFileName( szPath, pSubFolderName, pOut, outLen );
  213. return true;
  214. }
  215. else
  216. {
  217. return false;
  218. }
  219. }
  220. bool CreateStartMenuLink( const char *pSubFolderName, const char *pLinkName, const char *pLinkTarget, const char *pArguments )
  221. {
  222. char fullFolderName[MAX_PATH];
  223. if ( !SetupStartMenuSubFolderName( pSubFolderName, fullFolderName, sizeof( fullFolderName ) ) )
  224. return false;
  225. // Create the folder if necessary.
  226. if ( !CreateDirectory_R( fullFolderName ) )
  227. {
  228. Msg( "CreateStartMenuLink failed - can't create directory %s.\n", fullFolderName );
  229. return false;
  230. }
  231. IShellLink* psl = NULL;
  232. // Get a pointer to the IShellLink interface.
  233. bool bRet = false;
  234. CoInitialize( NULL );
  235. HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, reinterpret_cast<void**>(&psl));
  236. if (SUCCEEDED(hres))
  237. {
  238. psl->SetPath( pLinkTarget ); // Set the path to the shortcut target
  239. if ( pArguments )
  240. psl->SetArguments( pArguments );
  241. // Query IShellLink for the IPersistFile interface for saving
  242. //the shortcut in persistent storage.
  243. IPersistFile* ppf = NULL;
  244. hres = psl->QueryInterface( IID_IPersistFile, reinterpret_cast<void**>(&ppf) );
  245. if (SUCCEEDED(hres))
  246. {
  247. // Setup the filename for the link.
  248. char linkFilename[MAX_PATH];
  249. V_ComposeFileName( fullFolderName, pLinkName, linkFilename, sizeof( linkFilename ) );
  250. V_strncat( linkFilename, ".lnk", sizeof( linkFilename ) );
  251. // Ensure that the string is ANSI.
  252. WCHAR wsz[MAX_PATH];
  253. MultiByteToWideChar(CP_ACP, 0, linkFilename, -1, wsz, MAX_PATH);
  254. // Save the link by calling IPersistFile::Save.
  255. hres = ppf->Save( wsz, TRUE );
  256. ppf->Release();
  257. bRet = true;
  258. }
  259. psl->Release();
  260. }
  261. CoUninitialize();
  262. return bRet;
  263. }
  264. char* GetLastErrorString()
  265. {
  266. static char err[2048];
  267. LPVOID lpMsgBuf;
  268. FormatMessage(
  269. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  270. FORMAT_MESSAGE_FROM_SYSTEM |
  271. FORMAT_MESSAGE_IGNORE_INSERTS,
  272. NULL,
  273. GetLastError(),
  274. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  275. (LPTSTR) &lpMsgBuf,
  276. 0,
  277. NULL
  278. );
  279. strncpy( err, (char*)lpMsgBuf, sizeof( err ) );
  280. LocalFree( lpMsgBuf );
  281. err[ sizeof( err ) - 1 ] = 0;
  282. return err;
  283. }
  284. bool LaunchApp( char *pCommandLine, const char *pBaseDir )
  285. {
  286. STARTUPINFO si;
  287. memset( &si, 0, sizeof( si ) );
  288. si.cb = sizeof( si );
  289. PROCESS_INFORMATION pi;
  290. memset( &pi, 0, sizeof( pi ) );
  291. return CreateProcess( NULL, pCommandLine, NULL, NULL, FALSE, 0, NULL, pBaseDir, &si, &pi ) != 0;
  292. }
  293. bool StartVMPIServiceUI( const char *pInstallLocation )
  294. {
  295. if ( g_bDontTouchUI )
  296. {
  297. Msg( "StartVMPIServiceUI: Ignoring due to -DontTouchUI.\n" );
  298. return true;
  299. }
  300. char cmdLine[MAX_PATH];
  301. V_ComposeFileName( pInstallLocation, "vmpi_service_ui.exe", cmdLine, sizeof( cmdLine ) );
  302. return LaunchApp( cmdLine, pInstallLocation );
  303. }
  304. bool StartVMPIService( SC_HANDLE hSCManager )
  305. {
  306. bool bRet = true;
  307. // First, get rid of an old service with the same name.
  308. SC_HANDLE hMyService = OpenService( hSCManager, VMPI_SERVICE_NAME_INTERNAL, SERVICE_START );
  309. if ( hMyService )
  310. {
  311. if ( StartService( hMyService, NULL, NULL ) )
  312. {
  313. Msg( "Started!\n" );
  314. }
  315. else
  316. {
  317. Error( "Can't start the service.\n" );
  318. bRet = false;
  319. }
  320. }
  321. else
  322. {
  323. Error( "Can't open service: %s\n", VMPI_SERVICE_NAME_INTERNAL );
  324. bRet = false;
  325. }
  326. CloseServiceHandle( hMyService );
  327. return bRet;
  328. }
  329. bool StopRunningApp()
  330. {
  331. if ( g_bDontTouchUI )
  332. {
  333. Msg( "StopRunningApp: -DontTouchUI was specified, so exiting before stopping the app.\n" );
  334. return true;
  335. }
  336. // Send the
  337. ISocket *pSocket = CreateIPSocket();
  338. if ( pSocket )
  339. {
  340. if ( pSocket->BindToAny( 0 ) )
  341. {
  342. CUtlVector<char> protocolVersions;
  343. protocolVersions.AddToTail( VMPI_PROTOCOL_VERSION );
  344. if ( VMPI_PROTOCOL_VERSION == 5 )
  345. protocolVersions.AddToTail( 4 ); // We want this installer to kill the previous services too.
  346. for ( int iProtocolVersion=0; iProtocolVersion < protocolVersions.Count(); iProtocolVersion++ )
  347. {
  348. char cPacket[4] =
  349. {
  350. protocolVersions[iProtocolVersion],
  351. VMPI_PASSWORD_OVERRIDE, // (force it to accept this message).
  352. 0,
  353. VMPI_STOP_SERVICE
  354. };
  355. CIPAddr addr( 127, 0, 0, 1, 0 );
  356. for ( int iPort=VMPI_SERVICE_PORT; iPort <= VMPI_LAST_SERVICE_PORT; iPort++ )
  357. {
  358. addr.port = iPort;
  359. pSocket->SendTo( &addr, cPacket, sizeof( cPacket ) );
  360. }
  361. }
  362. // Give it a sec to get the message and shutdown in case we're restarting.
  363. Sleep( 2000 );
  364. // This is the overkill method. If it didn't shutdown gracefully, kill it.
  365. HMODULE hInst = LoadLibrary( "psapi.dll" );
  366. if ( hInst )
  367. {
  368. typedef BOOL (WINAPI *EnumProcessesFn)(DWORD *lpidProcess, DWORD cb, DWORD *cbNeeded);
  369. typedef BOOL (WINAPI *EnumProcessModulesFn)(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded );
  370. typedef DWORD (WINAPI *GetModuleBaseNameFn)( HANDLE hProcess, HMODULE hModule, LPTSTR lpBaseName, DWORD nSize );
  371. EnumProcessesFn EnumProcesses = (EnumProcessesFn)GetProcAddress( hInst, "EnumProcesses" );
  372. EnumProcessModulesFn EnumProcessModules = (EnumProcessModulesFn)GetProcAddress( hInst, "EnumProcessModules" );
  373. GetModuleBaseNameFn GetModuleBaseName = (GetModuleBaseNameFn)GetProcAddress( hInst, "GetModuleBaseNameA" );
  374. if ( EnumProcessModules && EnumProcesses )
  375. {
  376. // Now just to make sure, kill the processes we're interested in.
  377. DWORD procIDs[1024];
  378. DWORD nBytes;
  379. if ( EnumProcesses( procIDs, sizeof( procIDs ), &nBytes ) )
  380. {
  381. DWORD nProcs = nBytes / sizeof( procIDs[0] );
  382. for ( DWORD i=0; i < nProcs; i++ )
  383. {
  384. HANDLE hProc = OpenProcess( PROCESS_ALL_ACCESS, FALSE, procIDs[i] );
  385. if ( hProc )
  386. {
  387. HMODULE hModules[1024];
  388. if ( EnumProcessModules( hProc, hModules, sizeof( hModules ), &nBytes ) )
  389. {
  390. DWORD nModules = nBytes / sizeof( hModules[0] );
  391. for ( DWORD iModule=0; iModule < nModules; iModule++ )
  392. {
  393. char filename[512];
  394. if ( GetModuleBaseName( hProc, hModules[iModule], filename, sizeof( filename ) ) )
  395. {
  396. if ( Q_stristr( filename, "vmpi_service.exe" ) || Q_stristr( filename, "vmpi_service_ui.exe" ) )
  397. {
  398. TerminateProcess( hProc, 1 );
  399. CloseHandle( hProc );
  400. hProc = NULL;
  401. break;
  402. }
  403. }
  404. }
  405. }
  406. CloseHandle( hProc );
  407. }
  408. }
  409. }
  410. }
  411. FreeLibrary( hInst );
  412. }
  413. }
  414. pSocket->Release();
  415. }
  416. return true;
  417. }
  418. bool StopOrDeleteService( SC_HANDLE hSCManager, bool bDelete )
  419. {
  420. bool bRet = true;
  421. // First, get rid of an old service with the same name.
  422. SC_HANDLE hOldService = OpenService( hSCManager, VMPI_SERVICE_NAME_INTERNAL, SERVICE_STOP | DELETE );
  423. if ( hOldService )
  424. {
  425. // Stop the service.
  426. Msg( "Found the service already running.\n" );
  427. Msg( "Stopping service...\n" );
  428. SERVICE_STATUS status;
  429. ControlService( hOldService, SERVICE_CONTROL_STOP, &status );
  430. if ( bDelete )
  431. {
  432. Msg( "Deleting service...\n" );
  433. bool bExitedNicely = false;
  434. DWORD startTime = GetTickCount();
  435. while ( 1 )
  436. {
  437. BOOL bRet = DeleteService( hOldService );
  438. if ( !bRet || bRet == ERROR_SERVICE_MARKED_FOR_DELETE )
  439. {
  440. Msg( "Deleted old service.\n" );
  441. bExitedNicely = true;
  442. break;
  443. }
  444. // Wait for the service to stop for 8 seconds.
  445. if ( GetTickCount() - startTime > 8000 )
  446. break;
  447. }
  448. if ( !bExitedNicely )
  449. {
  450. Error( "Couldn't delete the old '%s' service! Error: %s.\n", VMPI_SERVICE_NAME, GetLastErrorString() );
  451. bRet = false;
  452. }
  453. }
  454. CloseServiceHandle( hOldService );
  455. }
  456. return bRet;
  457. }
  458. bool GetExistingInstallationLocation( CString &strInstallLocation )
  459. {
  460. char buf[1024];
  461. DWORD bufSize = sizeof( buf );
  462. DWORD dwType;
  463. if ( RegQueryValueEx( g_hVMPIKey, SERVICE_INSTALL_LOCATION_KEY, NULL, &dwType, (LPBYTE)buf, &bufSize ) == ERROR_SUCCESS )
  464. {
  465. if ( dwType == REG_SZ )
  466. {
  467. strInstallLocation = buf;
  468. return true;
  469. }
  470. }
  471. return false;
  472. }
  473. void RemoveRegistryKeys()
  474. {
  475. // Delete the run values (that tells it to run the app when the user logs in).
  476. HKEY hKey = NULL;
  477. RegCreateKey( HKEY_LOCAL_MACHINE, HLKM_WINDOWS_RUN_KEY, &hKey );
  478. RegDeleteValue( hKey, VMPI_SERVICE_VALUE_NAME );
  479. RegDeleteValue( hKey, VMPI_SERVICE_UI_VALUE_NAME );
  480. // Get rid of the "InstallLocation" value.
  481. RegDeleteValue( g_hVMPIKey, SERVICE_INSTALL_LOCATION_KEY );
  482. }
  483. bool IsAnInstallFile( const char *pName )
  484. {
  485. for ( int i=0; i < ARRAYSIZE( g_pInstallFiles ); i++ )
  486. {
  487. if ( V_stricmp( g_pInstallFiles[i], pName ) == 0 )
  488. return true;
  489. }
  490. return false;
  491. }
  492. bool AnyNonInstallFilesInDirectory( const char *strInstallLocation )
  493. {
  494. char searchStr[MAX_PATH];
  495. V_ComposeFileName( strInstallLocation, "*.*", searchStr, sizeof( searchStr ) );
  496. _finddata_t data;
  497. long handle = _findfirst( searchStr, &data );
  498. if ( handle != -1 )
  499. {
  500. do
  501. {
  502. if ( data.name[0] == '.' || (data.attrib & _A_SUBDIR) != 0 )
  503. continue;
  504. if ( !IsAnInstallFile( data.name ) )
  505. return true;
  506. } while( _findnext( handle, &data ) == 0 );
  507. _findclose( handle );
  508. }
  509. return false;
  510. }
  511. /////////////////////////////////////////////////////////////////////////////
  512. // CServiceInstallDlg dialog
  513. CServiceInstallDlg::CServiceInstallDlg(CWnd* pParent /*=NULL*/)
  514. : CDialog(CServiceInstallDlg::IDD, pParent)
  515. {
  516. //{{AFX_DATA_INIT(CServiceInstallDlg)
  517. // NOTE: the ClassWizard will add member initialization here
  518. //}}AFX_DATA_INIT
  519. }
  520. CServiceInstallDlg::~CServiceInstallDlg()
  521. {
  522. }
  523. void CServiceInstallDlg::DoDataExchange(CDataExchange* pDX)
  524. {
  525. CDialog::DoDataExchange(pDX);
  526. //{{AFX_DATA_MAP(CServiceInstallDlg)
  527. //}}AFX_DATA_MAP
  528. }
  529. BEGIN_MESSAGE_MAP(CServiceInstallDlg, CDialog)
  530. //{{AFX_MSG_MAP(CServiceInstallDlg)
  531. ON_BN_CLICKED(IDC_CANCEL_BUTTON, OnCancel)
  532. ON_BN_CLICKED(IDC_INSTALL_BUTTON, OnInstall)
  533. ON_BN_CLICKED(IDC_UNINSTALL_BUTTON2, OnUninstall)
  534. ON_BN_CLICKED(IDC_START_EXISTING_BUTTON, OnStartExisting)
  535. ON_BN_CLICKED(IDC_STOP_EXISTING_BUTTON, OnStopExisting)
  536. //}}AFX_MSG_MAP
  537. END_MESSAGE_MAP()
  538. /////////////////////////////////////////////////////////////////////////////
  539. // CServiceInstallDlg message handlers
  540. const char* FindArg( const char *pArgName, const char *pDefault="" )
  541. {
  542. for ( int i=1; i < __argc; i++ )
  543. {
  544. if ( Q_stricmp( pArgName, __argv[i] ) == 0 )
  545. {
  546. if ( (i+1) < __argc )
  547. return __argv[i+1];
  548. else
  549. return pDefault;
  550. }
  551. }
  552. return NULL;
  553. }
  554. BOOL CServiceInstallDlg::OnInitDialog()
  555. {
  556. CDialog::OnInitDialog();
  557. HICON hIcon = LoadIcon( AfxGetInstanceHandle(), MAKEINTRESOURCE( IDR_MAINFRAME ) );
  558. SetIcon( hIcon, true );
  559. OpenLog();
  560. // Setup the registry key for the install location.
  561. if ( RegCreateKey( HKEY_LOCAL_MACHINE, VMPI_SERVICE_KEY, &g_hVMPIKey ) != ERROR_SUCCESS )
  562. {
  563. Error( "Can't open registry key: %s.", VMPI_SERVICE_KEY );
  564. return FALSE;
  565. }
  566. VerifyInstallFiles();
  567. g_hMessageControl = ::GetDlgItem( GetSafeHwnd(), IDC_TEXTOUTPUT );
  568. SpewOutputFunc( MySpewOutputFunc );
  569. // Init the service manager.
  570. m_hSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
  571. if ( !m_hSCManager )
  572. {
  573. Error( "OpenSCManager failed (%s)!\n", GetLastErrorString() );
  574. return FALSE;
  575. }
  576. // See if there is a previous installation.
  577. CString strInstallLocation;
  578. if ( GetExistingInstallationLocation( strInstallLocation ) )
  579. SetDlgItemText( IDC_INSTALL_LOCATION, strInstallLocation );
  580. else
  581. SetDlgItemText( IDC_INSTALL_LOCATION, DEFAULT_INSTALL_LOCATION );
  582. // Now, if they passed in a command line option .
  583. if ( FindArg( __argc, __argv, "-install_quiet" ) )
  584. {
  585. g_bReinstalling = true;
  586. g_bNoOutput = true;
  587. g_bDontTouchUI = (FindArg( __argc, __argv, "-DontTouchUI", NULL ) != 0);
  588. OnInstall();
  589. EndDialog( 0 );
  590. }
  591. else if ( FindArg( __argc, __argv, "-uninstall_quiet" ) )
  592. {
  593. g_bNoOutput = true;
  594. DoUninstall( false );
  595. EndDialog( 0 );
  596. }
  597. else if ( FindArg( __argc, __argv, "-start" ) )
  598. {
  599. OnStartExisting();
  600. }
  601. else if ( FindArg( __argc, __argv, "-stop" ) )
  602. {
  603. OnStopExisting();
  604. }
  605. return TRUE; // return TRUE unless you set the focus to a control
  606. // EXCEPTION: OCX Property Pages should return FALSE
  607. }
  608. void CServiceInstallDlg::OnCancel(void)
  609. {
  610. EndDialog( 1 );
  611. }
  612. // This function registers the service with the service manager.
  613. bool InstallService( SC_HANDLE hSCManager, const char *pBaseDir )
  614. {
  615. char filename[512], uiFilename[512];
  616. V_ComposeFileName( pBaseDir, "vmpi_service.exe", filename, sizeof( filename ) );
  617. V_ComposeFileName( pBaseDir, "vmpi_service_ui.exe", uiFilename, sizeof( uiFilename ) );
  618. // Try a to reinstall the service for up to 5 seconds.
  619. Msg( "Creating new service...\n" );
  620. SC_HANDLE hMyService = NULL;
  621. DWORD startTime = GetTickCount();
  622. while ( GetTickCount() - startTime < 5000 )
  623. {
  624. // Now reinstall it.
  625. hMyService = CreateService(
  626. hSCManager, // Service Control Manager database.
  627. VMPI_SERVICE_NAME_INTERNAL, // Service name.
  628. VMPI_SERVICE_NAME, // Display name.
  629. SERVICE_ALL_ACCESS,
  630. SERVICE_WIN32_OWN_PROCESS,
  631. SERVICE_AUTO_START, // Start automatically on system bootup.
  632. SERVICE_ERROR_NORMAL,
  633. filename, // Executable to register for the service.
  634. NULL, // no load ordering group
  635. NULL, // no tag identifier
  636. NULL, // no dependencies
  637. NULL, // account
  638. NULL // password
  639. );
  640. if ( hMyService )
  641. break;
  642. else
  643. Sleep( 300 );
  644. }
  645. if ( !hMyService )
  646. {
  647. Warning( "CreateService failed (%s)!\n", GetLastErrorString() );
  648. CloseServiceHandle( hSCManager );
  649. return false;
  650. }
  651. // Now setup the UI executable to run when their system starts.
  652. HKEY hUIKey = NULL;
  653. RegCreateKey( HKEY_LOCAL_MACHINE, HLKM_WINDOWS_RUN_KEY, &hUIKey );
  654. if ( !hUIKey || RegSetValueEx( hUIKey, VMPI_SERVICE_UI_VALUE_NAME, 0, REG_SZ, (unsigned char*)uiFilename, strlen( uiFilename) + 1 ) != ERROR_SUCCESS )
  655. {
  656. Warning( "Can't install registry key for %s\n", uiFilename );
  657. return false;
  658. }
  659. CloseServiceHandle( hMyService );
  660. return true;
  661. }
  662. void SetupStartMenuLinks( const char *pInstallerFilename )
  663. {
  664. CreateStartMenuLink( "Valve\\VMPI", "Start VMPI Service", pInstallerFilename, "-start" );
  665. CreateStartMenuLink( "Valve\\VMPI", "Stop VMPI Service", pInstallerFilename, "-stop" );
  666. CreateStartMenuLink( "Valve\\VMPI", "Uninstall VMPI", pInstallerFilename, NULL );
  667. }
  668. void RemoveStartMenuLinks()
  669. {
  670. char fullFolderName[MAX_PATH];
  671. if ( !SetupStartMenuSubFolderName( "Valve\\VMPI", fullFolderName, sizeof( fullFolderName ) ) )
  672. return;
  673. char errorFile[MAX_PATH];
  674. if ( !DeleteDirectory( fullFolderName, true, errorFile ) )
  675. {
  676. Msg( "Unable to remove Start Menu items in %s.\n", fullFolderName );
  677. }
  678. }
  679. void CServiceInstallDlg::OnInstall()
  680. {
  681. // Get the install location.
  682. Msg( "Verifying install location.\n" );
  683. CString strInstallLocation;
  684. if ( !GetDlgItemText( IDC_INSTALL_LOCATION, strInstallLocation ) )
  685. {
  686. Error( "Can't get install location." );
  687. return;
  688. }
  689. if ( strchr( strInstallLocation, ':' ) == NULL )
  690. {
  691. Warning( "Install location must be an absolute path (include a colon)." );
  692. return;
  693. }
  694. // Stop the existing service.
  695. if ( !DoUninstall( false ) )
  696. return;
  697. // Create the directory.
  698. Msg( "Creating install directory %s.\n", strInstallLocation );
  699. if ( !CreateDirectory_R( strInstallLocation ) )
  700. {
  701. Warning( "Unable to create directory: %s.", (const char*)strInstallLocation );
  702. return;
  703. }
  704. // Copy the files down.
  705. Msg( "Copying files.\n" );
  706. char chDir[MAX_PATH];
  707. GetModuleFileName( NULL, chDir, sizeof( chDir ) );
  708. V_StripFilename( chDir );
  709. for ( int i=0; i < ARRAYSIZE( g_pInstallFiles ); i++ )
  710. {
  711. char srcFilename[MAX_PATH], destFilename[MAX_PATH];
  712. V_ComposeFileName( chDir, g_pInstallFiles[i], srcFilename, sizeof( srcFilename ) );
  713. V_ComposeFileName( strInstallLocation, g_pInstallFiles[i], destFilename, sizeof( destFilename ) );
  714. if ( !CopyFile( srcFilename, destFilename, FALSE ) )
  715. {
  716. Sleep( 2000 );
  717. if ( !CopyFile( srcFilename, destFilename, FALSE ) )
  718. {
  719. Error( "CopyFile() failed.\nSrc: %s\nDest: %s\n%s", srcFilename, destFilename, GetLastErrorString() );
  720. return;
  721. }
  722. }
  723. }
  724. // Register the service.
  725. if ( !InstallService( m_hSCManager, strInstallLocation ) )
  726. return;
  727. // Write the new location to the registry.
  728. Msg( "Updating registry.\n" );
  729. if ( RegSetValueEx( g_hVMPIKey, SERVICE_INSTALL_LOCATION_KEY, 0, REG_SZ, (BYTE*)(const char*)strInstallLocation, V_strlen( strInstallLocation ) + 1 ) != ERROR_SUCCESS )
  730. {
  731. Error( "RegSetValueEx( %s, %s ) failed.", SERVICE_INSTALL_LOCATION_KEY, (const char*)strInstallLocation );
  732. return;
  733. }
  734. // Setup start menu links.
  735. char installerFilename[MAX_PATH];
  736. V_ComposeFileName( strInstallLocation, "vmpi_service_install.exe", installerFilename, sizeof( installerFilename ) );
  737. SetupStartMenuLinks( installerFilename );
  738. // Start the new service.
  739. Msg( "Starting new service.\n" );
  740. if ( DoStartExisting() )
  741. {
  742. Warning( "Installed successfully!" );
  743. }
  744. }
  745. bool CServiceInstallDlg::DoUninstall( bool bShowMessage )
  746. {
  747. // Figure out where to uninstall from.
  748. CString strInstallLocation;
  749. if ( !GetDlgItemText( IDC_INSTALL_LOCATION, strInstallLocation ) )
  750. {
  751. Error( "Can't get install location." );
  752. return false;
  753. }
  754. if ( _access( strInstallLocation, 0 ) == 0 && !g_bNoOutput )
  755. {
  756. // 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.
  757. if ( AnyNonInstallFilesInDirectory( strInstallLocation ) )
  758. {
  759. char str[512];
  760. V_snprintf( str, sizeof( str ), "Warning: this will delete all files under this directory: \n%s\nContinue?", strInstallLocation );
  761. if ( AfxMessageBox( str, MB_YESNO ) != IDYES )
  762. return false;
  763. }
  764. }
  765. // Stop both the service and the win app.
  766. bool bDone = StopRunningApp() && StopOrDeleteService( m_hSCManager, true );
  767. if ( !bDone )
  768. return false;
  769. bool bSuccess = true;
  770. RemoveRegistryKeys();
  771. char errorFile[MAX_PATH];
  772. if ( !NukeDirectory( strInstallLocation, errorFile ) )
  773. {
  774. // When reinstalling, the service may not be done exiting, so give it a sec.
  775. Sleep( 2000 );
  776. if ( !NukeDirectory( strInstallLocation, errorFile ) )
  777. {
  778. if ( errorFile[0] )
  779. Msg( "NukeDirectory( %s ) failed.\nError on file: %s\n", strInstallLocation, errorFile );
  780. else
  781. Msg( "NukeDirectory( %s ) failed.\n", strInstallLocation );
  782. Msg( "Uninstall complete, but files are left over in %s.\n", strInstallLocation );
  783. bSuccess = false;
  784. }
  785. }
  786. RemoveStartMenuLinks();
  787. if ( bShowMessage && bSuccess )
  788. AfxMessageBox( "Uninstall successful." );
  789. return true;
  790. }
  791. void CServiceInstallDlg::OnUninstall()
  792. {
  793. DoUninstall( true );
  794. }
  795. void CServiceInstallDlg::OnStartExisting()
  796. {
  797. if ( DoStartExisting() )
  798. AfxMessageBox( "Started successfully." );
  799. }
  800. bool CServiceInstallDlg::DoStartExisting()
  801. {
  802. StopRunningApp();
  803. StopOrDeleteService( m_hSCManager, false );
  804. CString strInstallLocation;
  805. if ( !GetExistingInstallationLocation( strInstallLocation ) )
  806. {
  807. Error( "The VMPI service is not installed." );
  808. return false;
  809. }
  810. if ( StartVMPIService( m_hSCManager ) )
  811. {
  812. return StartVMPIServiceUI( strInstallLocation );
  813. }
  814. else
  815. {
  816. return false;
  817. }
  818. }
  819. void CServiceInstallDlg::OnStopExisting()
  820. {
  821. // Stop the app but don't delete it.
  822. bool bDone = StopRunningApp() && StopOrDeleteService( m_hSCManager, false );
  823. if ( bDone )
  824. {
  825. AfxMessageBox( "Service successfully stopped." );
  826. }
  827. }
  828. bool CServiceInstallDlg::NukeDirectory( const char *pDir, char errorFile[MAX_PATH] )
  829. {
  830. // If the directory doesn't exist anyways, then return true..
  831. if ( _access( pDir, 0 ) != 0 )
  832. return true;
  833. return DeleteDirectory( pDir, true, errorFile ) == 0;
  834. }
  835. void CServiceInstallDlg::VerifyInstallFiles()
  836. {
  837. char chDir[MAX_PATH];
  838. GetModuleFileName( NULL, chDir, sizeof( chDir ) );
  839. V_StripFilename( chDir );
  840. for ( int i=0; i < ARRAYSIZE( g_pInstallFiles ); i++ )
  841. {
  842. char filename[MAX_PATH];
  843. V_ComposeFileName( chDir, g_pInstallFiles[i], filename, sizeof( filename ) );
  844. if ( _access( filename, 0 ) != 0 )
  845. {
  846. char szErrorMessage[MAX_PATH];
  847. V_snprintf( szErrorMessage, sizeof( szErrorMessage ), "Required installation file missing: %s", filename );
  848. AfxMessageBox( szErrorMessage );
  849. return;
  850. }
  851. }
  852. }