summaryrefslogtreecommitdiff
path: root/utils/vmpi/vmpi_services_watch
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /utils/vmpi/vmpi_services_watch
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'utils/vmpi/vmpi_services_watch')
-rw-r--r--utils/vmpi/vmpi_services_watch/PatchTimeout.cpp50
-rw-r--r--utils/vmpi/vmpi_services_watch/PatchTimeout.h57
-rw-r--r--utils/vmpi/vmpi_services_watch/ServicesDlg.cpp1161
-rw-r--r--utils/vmpi/vmpi_services_watch/ServicesDlg.h150
-rw-r--r--utils/vmpi/vmpi_services_watch/SetPasswordDlg.cpp49
-rw-r--r--utils/vmpi/vmpi_services_watch/SetPasswordDlg.h54
-rw-r--r--utils/vmpi/vmpi_services_watch/StdAfx.cpp15
-rw-r--r--utils/vmpi/vmpi_services_watch/StdAfx.h35
-rw-r--r--utils/vmpi/vmpi_services_watch/res/vmpi.icobin0 -> 3310 bytes
-rw-r--r--utils/vmpi/vmpi_services_watch/res/vmpi_browser_services.icobin0 -> 1078 bytes
-rw-r--r--utils/vmpi/vmpi_services_watch/res/vmpi_browser_services.rc213
-rw-r--r--utils/vmpi/vmpi_services_watch/resource.h48
-rw-r--r--utils/vmpi/vmpi_services_watch/vmpi_browser_services.cpp81
-rw-r--r--utils/vmpi/vmpi_services_watch/vmpi_browser_services.h56
-rw-r--r--utils/vmpi/vmpi_services_watch/vmpi_browser_services.rc249
-rw-r--r--utils/vmpi/vmpi_services_watch/vmpi_services_watch.vpc72
16 files changed, 2090 insertions, 0 deletions
diff --git a/utils/vmpi/vmpi_services_watch/PatchTimeout.cpp b/utils/vmpi/vmpi_services_watch/PatchTimeout.cpp
new file mode 100644
index 0000000..df242d8
--- /dev/null
+++ b/utils/vmpi/vmpi_services_watch/PatchTimeout.cpp
@@ -0,0 +1,50 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// PatchTimeout.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "PatchTimeout.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CPatchTimeout dialog
+
+
+CPatchTimeout::CPatchTimeout(CWnd* pParent /*=NULL*/)
+ : CDialog(CPatchTimeout::IDD, pParent)
+{
+ //{{AFX_DATA_INIT(CPatchTimeout)
+ //}}AFX_DATA_INIT
+}
+
+
+void CPatchTimeout::DoDataExchange(CDataExchange* pDX)
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(CPatchTimeout)
+ DDX_Text(pDX, IDC_COMMAND_LINE, m_PatchDirectory);
+ DDX_Text(pDX, IDC_VMPI_TRANSFER_DIRECTORY, m_VMPITransferDirectory);
+ DDX_Check(pDX, IDC_FORCE_PATCH, m_bForcePatch);
+ //}}AFX_DATA_MAP
+}
+
+
+BEGIN_MESSAGE_MAP(CPatchTimeout, CDialog)
+ //{{AFX_MSG_MAP(CPatchTimeout)
+ // NOTE: the ClassWizard will add message map macros here
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CPatchTimeout message handlers
diff --git a/utils/vmpi/vmpi_services_watch/PatchTimeout.h b/utils/vmpi/vmpi_services_watch/PatchTimeout.h
new file mode 100644
index 0000000..1efb171
--- /dev/null
+++ b/utils/vmpi/vmpi_services_watch/PatchTimeout.h
@@ -0,0 +1,57 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#if !defined(AFX_PATCHTIMEOUT_H__2D87CBF2_AC88_4F23_BB43_CC8A5C248B64__INCLUDED_)
+#define AFX_PATCHTIMEOUT_H__2D87CBF2_AC88_4F23_BB43_CC8A5C248B64__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+// PatchTimeout.h : header file
+//
+
+#include "resource.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// CPatchTimeout dialog
+
+class CPatchTimeout : public CDialog
+{
+// Construction
+public:
+ CPatchTimeout(CWnd* pParent = NULL); // standard constructor
+
+// Dialog Data
+ //{{AFX_DATA(CPatchTimeout)
+ enum { IDD = IDD_TIMEOUT };
+ CString m_PatchDirectory;
+ CString m_VMPITransferDirectory;
+ int m_bForcePatch;
+ //}}AFX_DATA
+
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CPatchTimeout)
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+ //}}AFX_VIRTUAL
+
+// Implementation
+protected:
+
+ // Generated message map functions
+ //{{AFX_MSG(CPatchTimeout)
+ // NOTE: the ClassWizard will add member functions here
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_PATCHTIMEOUT_H__2D87CBF2_AC88_4F23_BB43_CC8A5C248B64__INCLUDED_)
diff --git a/utils/vmpi/vmpi_services_watch/ServicesDlg.cpp b/utils/vmpi/vmpi_services_watch/ServicesDlg.cpp
new file mode 100644
index 0000000..dd8ea5f
--- /dev/null
+++ b/utils/vmpi/vmpi_services_watch/ServicesDlg.cpp
@@ -0,0 +1,1161 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// ServicesDlg.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "ServicesDlg.h"
+#include "vmpi.h"
+#include "bitbuf.h"
+#include "tier1/strtools.h"
+#include "patchtimeout.h"
+#include "SetPasswordDlg.h"
+#include "vmpi_browser_helpers.h"
+#include <io.h>
+
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+#define SERVICE_OFF_TIMEOUT (20*1000) // If we haven't heard from a service in this long,
+ // then we assume the service is off.
+
+#define SERVICES_PING_INTERVAL (3*1000) // ping the services every so often
+
+#define SERVICE_MAX_UPDATE_INTERVAL (8*1000) // Update each service in the listbox at least this often.
+
+
+
+int V_AfxMessageBox( int mbType, const char *pFormat, ... )
+{
+ char msg[4096];
+ va_list marker;
+ va_start( marker, pFormat );
+ _vsnprintf( msg, sizeof( msg ), pFormat, marker );
+ va_end( marker );
+
+ return AfxMessageBox( msg, mbType );
+}
+
+
+bool CServiceInfo::IsOff() const
+{
+ return (Plat_MSTime() - m_LastPingTimeMS) > SERVICE_OFF_TIMEOUT;
+}
+
+
+// Returns the argument following pName.
+// If pName is the last argument on the command line, returns pEndArgDefault.
+// Returns NULL if there is no argument with pName.
+const char* FindArg( const char *pName, const char *pEndArgDefault="" )
+{
+ for ( int i=0; i < __argc; i++ )
+ {
+ if ( stricmp( pName, __argv[i] ) == 0 )
+ {
+ if ( (i+1) < __argc )
+ return __argv[i+1];
+ else
+ return pEndArgDefault;
+ }
+ }
+ return NULL;
+}
+
+void AppendStr( char *dest, int destSize, const char *pFormat, ... )
+{
+ char str[4096];
+ va_list marker;
+ va_start( marker, pFormat );
+ _vsnprintf( str, sizeof( str ), pFormat, marker );
+ va_end( marker );
+ str[sizeof( str ) - 1] = 0;
+
+ V_strncat( dest, str, destSize );
+}
+
+
+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;
+}
+
+const char* GetStatusString( CServiceInfo *pInfo )
+{
+ if ( pInfo->IsOff() )
+ return "off";
+ else if ( pInfo->m_iState == VMPI_STATE_BUSY )
+ return "busy";
+ else if ( pInfo->m_iState == VMPI_STATE_PATCHING )
+ return "patching";
+ else if ( pInfo->m_iState == VMPI_STATE_DISABLED )
+ return "disabled";
+ else if ( pInfo->m_iState == VMPI_STATE_SCREENSAVER_DISABLED )
+ return "disabled (screensaver)";
+ else if ( pInfo->m_iState == VMPI_STATE_DOWNLOADING )
+ return "downloading";
+ else
+ return "idle";
+}
+
+
+// --------------------------------------------------------------------------------------------------------- //
+// Column sort functions.
+// --------------------------------------------------------------------------------------------------------- //
+typedef int (CALLBACK *ServicesSortFn)( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam );
+
+static int CALLBACK SortByName( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
+{
+ CServiceInfo *pInfo1 = (CServiceInfo*)iItem1;
+ CServiceInfo *pInfo2 = (CServiceInfo*)iItem2;
+
+ return strcmp( pInfo1->m_ComputerName, pInfo2->m_ComputerName );
+}
+
+static int CALLBACK SortByStatus( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
+{
+ CServiceInfo *pInfo1 = (CServiceInfo*)iItem1;
+ CServiceInfo *pInfo2 = (CServiceInfo*)iItem2;
+
+ return -strcmp( GetStatusString( pInfo2 ), GetStatusString( pInfo1 ) );
+}
+
+static int CALLBACK SortByRunningTime( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
+{
+ CServiceInfo *pInfo1 = (CServiceInfo*)iItem1;
+ CServiceInfo *pInfo2 = (CServiceInfo*)iItem2;
+
+ int v1 = pInfo1->m_LiveTimeMS / (1000*60); // Sort on minutes so it doesn't constantly resort the list.
+ int v2 = pInfo2->m_LiveTimeMS / (1000*60);
+ if ( v2 > v1 )
+ return 1;
+ else if ( v2 < v1 )
+ return -1;
+ else
+ return 0;
+}
+
+static int CALLBACK SortByWorkerAppRunningTime( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
+{
+ CServiceInfo *pInfo1 = (CServiceInfo*)iItem1;
+ CServiceInfo *pInfo2 = (CServiceInfo*)iItem2;
+
+ int v1 = pInfo1->m_WorkerAppTimeMS / (1000*60); // Sort on minutes so it doesn't constantly resort the list.
+ int v2 = pInfo2->m_WorkerAppTimeMS / (1000*60);
+ if ( v2 > v1 )
+ return 1;
+ else if ( v2 < v1 )
+ return -1;
+ else
+ return 0;
+}
+
+static int CALLBACK SortByMasterName( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
+{
+ CServiceInfo *pInfo1 = (CServiceInfo*)iItem1;
+ CServiceInfo *pInfo2 = (CServiceInfo*)iItem2;
+
+ return -strcmp( pInfo2->m_MasterName, pInfo1->m_MasterName );
+}
+
+static int CALLBACK SortByProtocol( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
+{
+ CServiceInfo *pInfo1 = (CServiceInfo*)iItem1;
+ CServiceInfo *pInfo2 = (CServiceInfo*)iItem2;
+
+ return pInfo1->m_ProtocolVersion > pInfo2->m_ProtocolVersion;
+}
+
+static int CALLBACK SortByPassword( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
+{
+ CServiceInfo *pInfo1 = (CServiceInfo*)iItem1;
+ CServiceInfo *pInfo2 = (CServiceInfo*)iItem2;
+
+ return -strcmp( pInfo2->m_Password, pInfo1->m_Password );
+}
+
+static int CALLBACK SortByServiceVersion( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
+{
+ CServiceInfo *pInfo1 = (CServiceInfo*)iItem1;
+ CServiceInfo *pInfo2 = (CServiceInfo*)iItem2;
+
+ return -strcmp( pInfo2->m_ServiceVersion, pInfo1->m_ServiceVersion );
+}
+
+static int CALLBACK SortByExe( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
+{
+ CServiceInfo *pInfo1 = (CServiceInfo*)iItem1;
+ CServiceInfo *pInfo2 = (CServiceInfo*)iItem2;
+
+ return -strcmp( pInfo2->m_ExeName, pInfo1->m_ExeName );
+}
+
+static int CALLBACK SortByMap( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
+{
+ CServiceInfo *pInfo1 = (CServiceInfo*)iItem1;
+ CServiceInfo *pInfo2 = (CServiceInfo*)iItem2;
+
+ return -strcmp( pInfo2->m_MapName, pInfo1->m_MapName );
+}
+
+static int CALLBACK SortByCPUPercentage( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
+{
+ CServiceInfo *pInfo1 = (CServiceInfo*)iItem1;
+ CServiceInfo *pInfo2 = (CServiceInfo*)iItem2;
+
+ if ( pInfo2->m_CPUPercentage > pInfo1->m_CPUPercentage )
+ return 1;
+ else if ( pInfo2->m_CPUPercentage < pInfo1->m_CPUPercentage )
+ return -1;
+ else
+ return 0;
+}
+
+static int CALLBACK SortByMemUsage( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
+{
+ CServiceInfo *pInfo1 = (CServiceInfo*)iItem1;
+ CServiceInfo *pInfo2 = (CServiceInfo*)iItem2;
+
+ if ( pInfo2->m_MemUsageMB > pInfo1->m_MemUsageMB )
+ return 1;
+ else if ( pInfo2->m_MemUsageMB < pInfo1->m_MemUsageMB )
+ return -1;
+ else
+ return 0;
+}
+
+// --------------------------------------------------------------------------------------------------------- //
+// Column information.
+// --------------------------------------------------------------------------------------------------------- //
+
+struct
+{
+ char *pText;
+ int width;
+ ServicesSortFn sortFn;
+} g_ColumnInfos[] =
+{
+ {"Computer Name", 150, SortByName},
+ {"Status", 95, SortByStatus},
+ {"Service Run Time", 100, SortByRunningTime},
+ {"Worker App Run Time", 125, SortByWorkerAppRunningTime},
+ {"Master", 150, SortByMasterName},
+ {"Password", 60, SortByPassword},
+ {"Protocol", 60, SortByProtocol},
+ {"Version", 50, SortByServiceVersion},
+ {"CPU", 50, SortByCPUPercentage},
+ {"Memory (MB)", 80, SortByMemUsage},
+ {"Exe", 80, SortByExe},
+ {"Map", 90, SortByMap},
+};
+#define COLUMN_COMPUTER_NAME 0
+#define COLUMN_STATUS 1
+#define COLUMN_RUNNING_TIME 2
+#define COLUMN_WORKER_APP_RUNNING_TIME 3
+#define COLUMN_MASTER_NAME 4
+#define COLUMN_PASSWORD 5
+#define COLUMN_PROTOCOL_VERSION 6
+#define COLUMN_SERVICE_VERSION 7
+#define COLUMN_CPU_PERCENTAGE 8
+#define COLUMN_MEM_USAGE 9
+#define COLUMN_EXE_NAME 10
+#define COLUMN_MAP_NAME 11
+
+
+// Used to sort all the columns.
+// When they click on a column, we add that index to entry 0 in here.
+// Then when sorting, if 2 columns are equal, we move to the next sort function.
+CUtlVector<int> g_SortColumns;
+
+static void PushSortColumn( int iColumn )
+{
+ // First, get rid of all entries with this same index.
+ int iPrev = g_SortColumns.Find( iColumn );
+ if ( iPrev != g_SortColumns.InvalidIndex() )
+ g_SortColumns.Remove( iPrev );
+
+ // Now add this one.
+ g_SortColumns.AddToTail( iColumn );
+}
+
+static int CALLBACK MainSortFn( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
+{
+ int curStatus = 0;
+ for ( int i = (int)g_SortColumns.Count()-1; i >= 0; i-- )
+ {
+ curStatus = g_ColumnInfos[g_SortColumns[i]].sortFn( iItem1, iItem2, lpParam );
+ if ( curStatus != 0 )
+ break;
+ }
+ return curStatus;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// CServicesDlg dialog
+
+
+CServicesDlg::CServicesDlg(CWnd* pParent /*=NULL*/)
+ : CIdleDialog(CServicesDlg::IDD, pParent)
+{
+ //{{AFX_DATA_INIT(CServicesDlg)
+ // NOTE: the ClassWizard will add member initialization here
+ //}}AFX_DATA_INIT
+ m_pServicesPingSocket = NULL;
+}
+
+
+void CServicesDlg::DoDataExchange(CDataExchange* pDX)
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(CServicesDlg)
+ DDX_Control(pDX, IDC_NUM_SERVICES, m_NumServicesControl);
+ DDX_Control(pDX, IDC_NUM_DISABLED_SERVICES, m_NumDisabledServicesControl);
+ DDX_Control(pDX, IDC_NUM_WORKING_SERVICES, m_NumWorkingServicesControl);
+ DDX_Control(pDX, IDC_NUM_WAITING_SERVICES, m_NumWaitingServicesControl);
+ DDX_Control(pDX, IDC_NUM_OFF_SERVICES, m_NumOffServicesControl);
+ DDX_Control(pDX, IDC_CURRENT_PASSWORD, m_PasswordDisplay);
+ DDX_Control(pDX, IDC_SERVICES_LIST, m_ServicesList);
+ //}}AFX_DATA_MAP
+}
+
+
+BEGIN_MESSAGE_MAP(CServicesDlg, CIdleDialog)
+ //{{AFX_MSG_MAP(CServicesDlg)
+ ON_BN_CLICKED(ID_PATCH_SERVICES, OnPatchServices)
+ ON_BN_CLICKED(ID_STOP_SERVICES, OnStopServices)
+ ON_BN_CLICKED(ID_STOP_JOBS, OnStopJobs)
+ ON_BN_CLICKED(ID_FILTER_BY_PASSWORD, OnFilterByPassword)
+ ON_BN_CLICKED(ID_FORCE_PASSWORD, OnForcePassword)
+ ON_BN_CLICKED(ID_COPY_TO_CLIPBOARD, OnCopyToClipboard)
+ ON_NOTIFY(NM_DBLCLK, IDC_SERVICES_LIST, OnDblclkServicesList)
+ ON_WM_SIZE()
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CServicesDlg message handlers
+
+BOOL CServicesDlg::OnInitDialog()
+{
+ CDialog::OnInitDialog();
+
+ // Initially, just sort by computer name.
+ PushSortColumn( COLUMN_COMPUTER_NAME );
+
+ HICON hIcon = LoadIcon( AfxGetInstanceHandle(), MAKEINTRESOURCE( IDR_MAINFRAME ) );
+ SetIcon( hIcon, true );
+
+ m_ServicesList.SetExtendedStyle( LVS_EX_FULLROWSELECT );
+
+ // Setup the headers.
+ for ( int i=0; i < ARRAYSIZE( g_ColumnInfos ); i++ )
+ {
+ m_ServicesList.InsertColumn( i, g_ColumnInfos[i].pText, LVCFMT_LEFT, g_ColumnInfos[i].width, i );
+ }
+
+ m_pServicesPingSocket = CreateIPSocket();
+ if ( m_pServicesPingSocket )
+ {
+ m_pServicesPingSocket->BindToAny( 0 );
+ }
+
+ m_dwLastServicesPing = GetTickCount() - SERVICES_PING_INTERVAL;
+ StartIdleProcessing( 100 ); // get idle messages every half second
+
+ m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_NUM_SERVICES_LABEL ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
+ m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_NUM_SERVICES ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
+
+ m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_NUM_DISABLED_SERVICES_LABEL ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
+ m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_NUM_DISABLED_SERVICES ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
+
+ m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_NUM_WORKING_SERVICES_LABEL ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
+ m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_NUM_WORKING_SERVICES ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
+
+ m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_NUM_WAITING_SERVICES_LABEL ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
+ m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_NUM_WAITING_SERVICES ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
+
+ m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_NUM_OFF_SERVICES_LABEL ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
+ m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_NUM_OFF_SERVICES ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
+
+ m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_CURRENT_PASSWORD_LABEL ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
+ m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_CURRENT_PASSWORD ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
+ m_AnchorMgr.AddAnchor( this, GetDlgItem( ID_PATCH_SERVICES ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
+ m_AnchorMgr.AddAnchor( this, GetDlgItem( ID_STOP_SERVICES ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
+ m_AnchorMgr.AddAnchor( this, GetDlgItem( ID_STOP_JOBS ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
+ m_AnchorMgr.AddAnchor( this, GetDlgItem( ID_FORCE_PASSWORD ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
+ m_AnchorMgr.AddAnchor( this, GetDlgItem( ID_FILTER_BY_PASSWORD ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
+
+ m_AnchorMgr.AddAnchor( this, GetDlgItem( ID_COPY_TO_CLIPBOARD ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
+
+ m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_SERVICES_LIST ), ANCHOR_LEFT, ANCHOR_TOP, ANCHOR_RIGHT, ANCHOR_BOTTOM );
+
+ // Unless they specify admin mode, hide all the controls that can mess with the services.
+ if ( !FindArg( "-Admin" ) )
+ {
+ ::ShowWindow( ::GetDlgItem( m_hWnd, ID_PATCH_SERVICES ), SW_HIDE );
+ ::ShowWindow( ::GetDlgItem( m_hWnd, ID_STOP_SERVICES ), SW_HIDE );
+ ::ShowWindow( ::GetDlgItem( m_hWnd, ID_STOP_JOBS ), SW_HIDE );
+ ::ShowWindow( ::GetDlgItem( m_hWnd, ID_FORCE_PASSWORD ), SW_HIDE );
+ }
+
+ m_NetViewThread.Init();
+
+ return TRUE; // return TRUE unless you set the focus to a control
+ // EXCEPTION: OCX Property Pages should return FALSE
+}
+
+
+void CServicesDlg::BuildVMPIPingPacket( CUtlVector<char> &out, char cPacketID, unsigned char protocolVersion, bool bIgnorePassword )
+{
+ out.Purge();
+ out.AddToTail( protocolVersion );
+
+ const char *pPassword = m_Password;
+ if ( pPassword[0] == 0 || bIgnorePassword )
+ {
+ // If they haven't set a password to filter by, then we want all services to report in.
+ out.AddToTail( VMPI_PASSWORD_OVERRIDE );
+ out.AddToTail( 0 );
+ }
+ else
+ {
+ out.AddMultipleToTail( strlen( pPassword ) + 1, pPassword ); // password.
+ }
+
+ out.AddToTail( cPacketID );
+}
+
+
+void CServicesDlg::UpdateServicesFromNetMessages()
+{
+ while ( 1 )
+ {
+ char in[1024];
+ CIPAddr ipFrom;
+
+ int len = m_pServicesPingSocket->RecvFrom( in, sizeof( in ), &ipFrom );
+ if ( len < 4 )
+ break;
+
+ bf_read buf( in, len );
+ unsigned char protocolVersion = buf.ReadByte();
+ if ( protocolVersion == 4 || protocolVersion == VMPI_PROTOCOL_VERSION ) // Protocol version 4 is almost the same.
+ {
+ int packetID = buf.ReadByte();
+ if ( packetID == VMPI_PING_RESPONSE )
+ {
+ int iState = buf.ReadByte();
+ unsigned long liveTimeMS = (unsigned long)buf.ReadLong();
+
+ int iPort = buf.ReadLong();
+ char computerName[512];
+ buf.ReadString( computerName, sizeof( computerName ) );
+
+ char masterName[512];
+ if ( buf.GetNumBitsLeft() )
+ buf.ReadString( masterName, sizeof( masterName ) );
+ else
+ masterName[0] = 0;
+
+ unsigned long workerAppTimeMS = 0;
+ if ( buf.GetNumBitsLeft() )
+ workerAppTimeMS = buf.ReadLong();
+
+ char password[512] = {0};
+ if ( protocolVersion == VMPI_PROTOCOL_VERSION )
+ buf.ReadString( password, sizeof( password ) );
+
+ char serviceVersion[32];
+ if ( protocolVersion == VMPI_PROTOCOL_VERSION )
+ buf.ReadString( serviceVersion, sizeof( serviceVersion ) );
+ else
+ V_strncpy( serviceVersion, "old", sizeof( serviceVersion ) );
+
+ int cpuPercentage = -1;
+ if ( buf.GetNumBytesLeft() >= 1 )
+ cpuPercentage = buf.ReadByte();
+
+ char exeName[512];
+ if ( buf.GetNumBytesLeft() >= 1 )
+ buf.ReadString( exeName, sizeof( exeName ) );
+ else
+ V_strncpy( exeName, "-", sizeof( exeName ) );
+
+ short memUsage = -1;
+ if ( buf.GetNumBytesLeft() >= 2 )
+ memUsage = buf.ReadShort();
+
+ char mapName[256];
+ if ( buf.GetNumBytesLeft() >= 1 )
+ buf.ReadString( mapName, sizeof( mapName ) );
+ else
+ V_strncpy( mapName, "-", sizeof( mapName ) );
+
+
+ CServiceInfo *pInfo = FindServiceByComputerName( computerName );
+ if ( !pInfo )
+ {
+ pInfo = new CServiceInfo;
+ m_Services.AddToTail( pInfo );
+ pInfo->m_ComputerName = computerName;
+
+ pInfo->m_pLastStatusText = NULL;
+
+ pInfo->m_Addr.port = iPort;
+ pInfo->m_LastPingTimeMS = Plat_MSTime();
+ pInfo->m_MasterName = "?";
+
+ int iItem = m_ServicesList.InsertItem( COLUMN_COMPUTER_NAME, pInfo->m_ComputerName, NULL );
+ m_ServicesList.SetItemData( iItem, (DWORD)pInfo );
+
+ // Update the display of # of services.
+ UpdateServiceCountDisplay();
+ }
+
+ pInfo->m_ProtocolVersion = protocolVersion;
+ V_strncpy( pInfo->m_ServiceVersion, serviceVersion, sizeof( pInfo->m_ServiceVersion ) );
+ pInfo->m_Addr = ipFrom;
+ pInfo->m_CPUPercentage = cpuPercentage;
+ pInfo->m_MemUsageMB = memUsage;
+ pInfo->m_ExeName = exeName;
+ pInfo->m_MapName = mapName;
+ pInfo->m_MasterName = masterName;
+ pInfo->m_LiveTimeMS = liveTimeMS;
+ pInfo->m_WorkerAppTimeMS = workerAppTimeMS;
+ pInfo->m_iState = iState;
+ pInfo->m_LastPingTimeMS = Plat_MSTime();
+ pInfo->m_Password = password;
+
+ UpdateServiceInListbox( pInfo );
+ }
+ }
+ }
+}
+
+
+void CServicesDlg::UpdateServicesFromNetView()
+{
+ CUtlVector<char*> computerNames;
+ m_NetViewThread.GetComputerNames( computerNames );
+
+ for ( int i=0; i < computerNames.Count(); i++ )
+ {
+ CServiceInfo *pInfo = FindServiceByComputerName( computerNames[i] );
+ if ( !pInfo )
+ {
+ pInfo = new CServiceInfo;
+ m_Services.AddToTail( pInfo );
+ pInfo->m_ComputerName = computerNames[i];
+ pInfo->m_LastPingTimeMS = Plat_MSTime() - SERVICE_OFF_TIMEOUT*2; // so it's marked as "off"
+ pInfo->m_LastLiveTimeMS = Plat_MSTime();
+ pInfo->m_LiveTimeMS = 0;
+ pInfo->m_WorkerAppTimeMS = 0;
+ pInfo->m_ProtocolVersion = 0;
+ pInfo->m_ServiceVersion[0] = 0;
+ pInfo->m_CPUPercentage = -1;
+ pInfo->m_MemUsageMB = -1;
+
+ int iItem = m_ServicesList.InsertItem( COLUMN_COMPUTER_NAME, pInfo->m_ComputerName, NULL );
+ m_ServicesList.SetItemData( iItem, (DWORD)pInfo );
+
+ // Update the display of # of services.
+ UpdateServiceCountDisplay();
+
+ UpdateServiceInListbox( pInfo );
+ }
+ }
+
+ computerNames.PurgeAndDeleteElements();
+}
+
+
+void CServicesDlg::OnIdle()
+{
+ DWORD curTime = GetTickCount();
+
+ if ( !m_pServicesPingSocket )
+ return;
+
+ // Broadcast out to all the services?
+ if ( curTime - m_dwLastServicesPing >= SERVICES_PING_INTERVAL )
+ {
+ m_dwLastServicesPing = curTime;
+
+ for ( int i=VMPI_SERVICE_PORT; i <= VMPI_LAST_SERVICE_PORT; i++ )
+ {
+ CUtlVector<char> data;
+ BuildVMPIPingPacket( data, VMPI_PING_REQUEST );
+ m_pServicesPingSocket->Broadcast( data.Base(), data.Count(), i );
+
+ // Also send out a version 4 one because we understand a version 4 response.
+ BuildVMPIPingPacket( data, VMPI_PING_REQUEST, 4 );
+ m_pServicesPingSocket->Broadcast( data.Base(), data.Count(), i );
+ }
+
+ UpdateServiceCountDisplay();
+ }
+
+ m_ServicesList.SetRedraw( false );
+
+ // Check for messages from services.
+ UpdateServicesFromNetMessages();
+
+ // Issue a "net view" command, parse the output, and add any computers it lists.
+ // This lets us figure out which PCs on the network are not running the service.
+ UpdateServicesFromNetView();
+
+
+ FOR_EACH_LL( m_Services, iService )
+ {
+ CServiceInfo *pInfo = m_Services[iService];
+ if ( Plat_MSTime() - pInfo->m_LastUpdateTime > SERVICE_MAX_UPDATE_INTERVAL )
+ {
+ UpdateServiceInListbox( pInfo );
+ }
+ }
+
+ if ( m_bListChanged )
+ {
+ ResortItems();
+ m_bListChanged = false;
+ }
+
+ m_ServicesList.SetRedraw( true );
+}
+
+
+CServiceInfo* CServicesDlg::FindServiceByComputerName( const char *pComputerName )
+{
+ FOR_EACH_LL( m_Services, i )
+ {
+ if ( Q_stricmp( m_Services[i]->m_ComputerName, pComputerName ) == 0 )
+ return m_Services[i];
+ }
+ return NULL;
+}
+
+
+void CServicesDlg::SendToSelectedServices( const char *pData, int len )
+{
+ POSITION pos = m_ServicesList.GetFirstSelectedItemPosition();
+ while ( pos )
+ {
+ int iItem = m_ServicesList.GetNextSelectedItem( pos );
+
+ CServiceInfo *pInfo = (CServiceInfo*)m_ServicesList.GetItemData( iItem );
+ m_pServicesPingSocket->SendTo( &pInfo->m_Addr, pData, len );
+ }
+}
+
+
+void UpdateItemText( CListCtrl &ctrl, int iItem, int iColumn, const char *pNewVal )
+{
+ CString str = ctrl.GetItemText( iItem, iColumn );
+ if ( V_stricmp( str, pNewVal ) != 0 )
+ ctrl.SetItemText( iItem, iColumn, pNewVal );
+}
+
+
+void CServicesDlg::UpdateServiceInListbox( CServiceInfo *pInfo )
+{
+ // First, find this item in the listbox.
+ LVFINDINFO info;
+ info.flags = LVFI_PARAM;
+ info.lParam = (LPARAM)pInfo;
+ int iItem = m_ServicesList.FindItem( &info );
+ if ( iItem != -1 )
+ {
+ UpdateItemText( m_ServicesList, iItem, COLUMN_COMPUTER_NAME, pInfo->m_ComputerName );
+
+ const char *pText = GetStatusString( pInfo );
+ UpdateItemText( m_ServicesList, iItem, COLUMN_STATUS, pText );
+
+ char timeStr[512];
+ FormatTimeString( pInfo->m_LiveTimeMS / 1000, timeStr, sizeof( timeStr ) );
+ UpdateItemText( m_ServicesList, iItem, COLUMN_RUNNING_TIME, timeStr );
+
+ FormatTimeString( pInfo->m_WorkerAppTimeMS / 1000, timeStr, sizeof( timeStr ) );
+ UpdateItemText( m_ServicesList, iItem, COLUMN_WORKER_APP_RUNNING_TIME, timeStr );
+
+ UpdateItemText( m_ServicesList, iItem, COLUMN_MASTER_NAME, pInfo->m_MasterName );
+
+ char str[512];
+ V_snprintf( str, sizeof( str ), "%d", pInfo->m_ProtocolVersion );
+ UpdateItemText( m_ServicesList, iItem, COLUMN_PROTOCOL_VERSION, str );
+
+ UpdateItemText( m_ServicesList, iItem, COLUMN_PASSWORD, pInfo->m_Password );
+ UpdateItemText( m_ServicesList, iItem, COLUMN_SERVICE_VERSION, pInfo->m_ServiceVersion );
+
+ if ( pInfo->m_CPUPercentage == -1 )
+ V_snprintf( str, sizeof( str ), "-" );
+ else if ( pInfo->m_CPUPercentage == 101 )
+ V_snprintf( str, sizeof( str ), "(err)" );
+ else
+ V_snprintf( str, sizeof( str ), "%d%%", pInfo->m_CPUPercentage );
+ UpdateItemText( m_ServicesList, iItem, COLUMN_CPU_PERCENTAGE, str );
+
+ UpdateItemText( m_ServicesList, iItem, COLUMN_EXE_NAME, pInfo->m_ExeName );
+ UpdateItemText( m_ServicesList, iItem, COLUMN_MAP_NAME, pInfo->m_MapName );
+
+ if ( pInfo->m_MemUsageMB == -1 )
+ V_snprintf( str, sizeof( str ), "-" );
+ else
+ V_snprintf( str, sizeof( str ), "%d", pInfo->m_MemUsageMB );
+ UpdateItemText( m_ServicesList, iItem, COLUMN_MEM_USAGE, str );
+
+ pInfo->m_pLastStatusText = pText;
+ pInfo->m_LastLiveTimeMS = pInfo->m_LiveTimeMS;
+ pInfo->m_LastMasterName = pInfo->m_MasterName;
+ pInfo->m_LastUpdateTime = Plat_MSTime();
+
+ // Detect changes.
+ if ( !m_bListChanged && iItem > 0 )
+ {
+ CServiceInfo *pPrevItem = (CServiceInfo*)m_ServicesList.GetItemData( iItem-1 );
+ if ( pPrevItem && MainSortFn( (LPARAM)pPrevItem, (LPARAM)pInfo, NULL ) > 0 )
+ m_bListChanged = true;
+ }
+
+ if ( !m_bListChanged && (iItem+1) < m_ServicesList.GetItemCount() )
+ {
+ CServiceInfo *pNextItem = (CServiceInfo*)m_ServicesList.GetItemData( iItem+1 );
+ if ( pNextItem && MainSortFn( (LPARAM)pInfo, (LPARAM)pNextItem, NULL ) > 0 )
+ m_bListChanged = true;
+ }
+ }
+}
+
+
+void CServicesDlg::ResortItems()
+{
+ m_ServicesList.SortItems( MainSortFn, (LPARAM)this );
+}
+
+
+void CServicesDlg::UpdateServiceCountDisplay()
+{
+ char str[512];
+ Q_snprintf( str, sizeof( str ), "%d", m_Services.Count() );
+ m_NumServicesControl.SetWindowText( str );
+
+ // Now count the various types.
+ int nDisabled = 0, nWorking = 0, nWaiting = 0, nOff = 0;
+ FOR_EACH_LL( m_Services, i )
+ {
+ if ( m_Services[i]->IsOff() )
+ {
+ ++nOff;
+ }
+ else if ( m_Services[i]->m_iState == VMPI_STATE_BUSY || m_Services[i]->m_iState == VMPI_STATE_DOWNLOADING )
+ {
+ ++nWorking;
+ }
+ else if ( m_Services[i]->m_iState == VMPI_STATE_IDLE )
+ {
+ ++nWaiting;
+ }
+ else
+ {
+ ++nDisabled;
+ }
+ }
+
+ Q_snprintf( str, sizeof( str ), "%d", nDisabled );
+ m_NumDisabledServicesControl.SetWindowText( str );
+
+ Q_snprintf( str, sizeof( str ), "%d", nWorking );
+ m_NumWorkingServicesControl.SetWindowText( str );
+
+ Q_snprintf( str, sizeof( str ), "%d", nWaiting );
+ m_NumWaitingServicesControl.SetWindowText( str );
+
+ Q_snprintf( str, sizeof( str ), "%d", nOff );
+ m_NumOffServicesControl.SetWindowText( str );
+}
+
+
+// This monstrosity is here because of the way they bundle string resources into groups in an exe file.
+// See http://support.microsoft.com/kb/q196774/.
+bool FindStringResourceEx( HINSTANCE hinst, UINT uId, UINT langId, char *pStr, int outLen )
+{
+ // Convert the string ID into a bundle number
+ bool bRet = false;
+ HRSRC hrsrc = FindResourceEx(hinst, RT_STRING, MAKEINTRESOURCE(uId / 16 + 1), langId);
+ if (hrsrc)
+ {
+ HGLOBAL hglob = LoadResource(hinst, hrsrc);
+ if (hglob)
+ {
+ LPCWSTR pwsz = reinterpret_cast<LPCWSTR>( LockResource(hglob) );
+ if (pwsz)
+ {
+ // okay now walk the string table
+ for (UINT i = 0; i < (uId & 15); i++)
+ {
+ pwsz += 1 + (UINT)*pwsz;
+ }
+
+ // First word in the resource is the length and the rest is the data.
+ int nChars = min( (int)pwsz[0], outLen-1 );
+ ++pwsz;
+ V_wcstostr( pwsz, nChars, pStr, outLen );
+ pStr[nChars] = 0;
+ bRet = true;
+ }
+ FreeResource(hglob);
+ }
+ }
+ return bRet;
+}
+
+
+int CheckServiceVersion( const char *pPatchDir, char *pServiceVersion, int maxServiceVersionLen )
+{
+ char filename[MAX_PATH];
+ V_ComposeFileName( pPatchDir, "vmpi_service.exe", filename, sizeof( filename ) );
+
+ int ret = IDCANCEL;
+ HINSTANCE hInst = LoadLibrary( filename );
+ if ( hInst )
+ {
+ bool bFound = FindStringResourceEx( hInst, VMPI_SERVICE_IDS_VERSION_STRING, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), pServiceVersion, maxServiceVersionLen );
+ if ( bFound )
+ {
+ ret = V_AfxMessageBox( MB_YESNOCANCEL, "Service version in %s is %s.\n\nIs this correct?", filename, pServiceVersion );
+ }
+ else
+ {
+ V_AfxMessageBox( MB_OK, "Can't get IDS_VERSION_STRING resource from %s.", filename );
+ }
+
+ FreeLibrary( hInst );
+ }
+ else
+ {
+ V_AfxMessageBox( MB_OK, "Can't load %s to get service version.", filename );
+ }
+
+ return ret;
+}
+
+void CServicesDlg::OnPatchServices()
+{
+ // Inquire about the timeout.
+ CPatchTimeout dlg;
+ dlg.m_PatchDirectory = "\\\\fileserver\\vmpi\\testservice";
+ dlg.m_VMPITransferDirectory = dlg.m_PatchDirectory;
+ dlg.m_bForcePatch = false;
+
+TryAgain:;
+
+ if ( dlg.DoModal() == IDOK )
+ {
+ // Launch the transfer app.
+ char commandLine[32 * 1024] = {0};
+
+ char transferExe[MAX_PATH];
+ V_ComposeFileName( dlg.m_VMPITransferDirectory, "vmpi_transfer.exe", transferExe, sizeof( transferExe ) );
+ if ( _access( transferExe, 0 ) != 0 )
+ {
+ V_AfxMessageBox( MB_OK, "Can't find '%s' to run the patch.", transferExe );
+ goto TryAgain;
+ }
+
+ char strServiceVersion[64];
+ int ret = CheckServiceVersion( dlg.m_PatchDirectory, strServiceVersion, sizeof( strServiceVersion ) );
+ if ( ret == IDCANCEL )
+ return;
+ else if ( ret == IDNO )
+ goto TryAgain;
+
+ AppendStr( commandLine, sizeof( commandLine ), "\"%s\" -PatchHost", transferExe );
+ AppendStr( commandLine, sizeof( commandLine ), " -mpi_PatchVersion %s", strServiceVersion );
+ AppendStr( commandLine, sizeof( commandLine ), " -mpi_PatchDirectory \"%s\"", (const char*)dlg.m_PatchDirectory );
+
+ if ( dlg.m_bForcePatch )
+ AppendStr( commandLine, sizeof( commandLine ), " -mpi_ForcePatch" );
+
+ // Collect the list of addresses.
+ CUtlVector<CIPAddr> addrs;
+ POSITION pos = m_ServicesList.GetFirstSelectedItemPosition();
+ while ( pos )
+ {
+ int iItem = m_ServicesList.GetNextSelectedItem( pos );
+
+ CServiceInfo *pInfo = (CServiceInfo*)m_ServicesList.GetItemData( iItem );
+ if ( pInfo->m_Addr.ip[0] != 0 ) // "off" services won't have an IP
+ addrs.AddToTail( pInfo->m_Addr );
+ }
+ if ( addrs.Count() == 0 )
+ {
+ AfxMessageBox( "No workers selected, or they all are off." );
+ return;
+ }
+
+ AppendStr( commandLine, sizeof( commandLine ), " -mpi_PatchWorkers %d", addrs.Count() );
+ for ( int i=0; i < addrs.Count(); i++ )
+ {
+ AppendStr( commandLine, sizeof( commandLine ), " %d.%d.%d.%d", addrs[i].ip[0], addrs[i].ip[1], addrs[i].ip[2], addrs[i].ip[3] );
+ }
+
+ STARTUPINFO si;
+ memset( &si, 0, sizeof( si ) );
+ si.cb = sizeof( si );
+
+ PROCESS_INFORMATION pi;
+ memset( &pi, 0, sizeof( pi ) );
+
+ if ( CreateProcess( NULL, commandLine,
+ NULL, NULL, false,
+ 0,
+ NULL,
+ (const char *)dlg.m_PatchDirectory,
+ &si,
+ &pi ) )
+ {
+ CloseHandle( pi.hProcess );
+ CloseHandle( pi.hThread );
+
+ V_AfxMessageBox( MB_OK, "Patch master successfully started.\nServices patching now.\nClose the patch master console app when finished." );
+ }
+ else
+ {
+ V_AfxMessageBox( MB_OK, "Error starting patch master: %s", GetLastErrorString() );
+ }
+ }
+}
+
+
+void CServicesDlg::OnStopServices()
+{
+ if ( MessageBox( "Warning: if you stop these services, you won't be able to control them from this application, and must restart them manually. Contine?", "Warning", MB_YESNO ) == IDYES )
+ {
+ CUtlVector<char> data;
+ BuildVMPIPingPacket( data, VMPI_STOP_SERVICE );
+ SendToSelectedServices( data.Base(), data.Count() );
+ }
+}
+
+void CServicesDlg::OnStopJobs()
+{
+ CUtlVector<char> data;
+ BuildVMPIPingPacket( data, VMPI_KILL_PROCESS );
+ SendToSelectedServices( data.Base(), data.Count() );
+}
+
+
+void CServicesDlg::OnFilterByPassword()
+{
+ CSetPasswordDlg dlg( IDD_SET_PASSWORD );
+ dlg.m_Password = m_Password;
+
+ if ( dlg.DoModal() == IDOK )
+ {
+ m_Password = dlg.m_Password;
+ m_PasswordDisplay.SetWindowText( m_Password );
+
+ m_Services.PurgeAndDeleteElements();
+ m_ServicesList.DeleteAllItems();
+
+ UpdateServiceCountDisplay();
+
+ // Re-ping everyone immediately.
+ m_dwLastServicesPing = GetTickCount() - SERVICES_PING_INTERVAL;
+ }
+}
+
+// This sets a new password on the selected services.
+void CServicesDlg::OnForcePassword()
+{
+ CSetPasswordDlg dlg( IDD_FORCE_PASSWORD );
+ dlg.m_Password = "password";
+
+ if ( dlg.DoModal() == IDOK )
+ {
+ CUtlVector<char> data;
+
+ BuildVMPIPingPacket( data, VMPI_FORCE_PASSWORD_CHANGE, VMPI_PROTOCOL_VERSION, true );
+ const char *pNewPassword = dlg.m_Password;
+ data.AddMultipleToTail( V_strlen( pNewPassword ) + 1, pNewPassword );
+
+ SendToSelectedServices( data.Base(), data.Count() );
+ }
+}
+
+void CServicesDlg::OnDblclkServicesList(NMHDR* pNMHDR, LRESULT* pResult)
+{
+ POSITION pos = m_ServicesList.GetFirstSelectedItemPosition();
+ if ( pos )
+ {
+ int iItem = m_ServicesList.GetNextSelectedItem( pos );
+ if ( iItem != -1 )
+ {
+ CServiceInfo *pInfo = (CServiceInfo*)m_ServicesList.GetItemData( iItem );
+ if ( pInfo )
+ {
+ // Launch vmpi_browser_job_search and have it auto-select this worker.
+ char cmdLine[1024];
+ Q_snprintf( cmdLine, sizeof( cmdLine ), "vmpi_job_search -SelectWorker %s", (const char*)pInfo->m_ComputerName );
+
+ STARTUPINFO si;
+ memset( &si, 0, sizeof( si ) );
+ si.cb = sizeof( si );
+
+ PROCESS_INFORMATION pi;
+ memset( &pi, 0, sizeof( pi ) );
+
+ if ( !CreateProcess(
+ NULL,
+ (char*)(const char*)cmdLine,
+ NULL, // security
+ NULL,
+ TRUE,
+ 0, // flags
+ NULL, // environment
+ NULL, // current directory
+ &si,
+ &pi ) )
+ {
+ char err[512];
+ Q_snprintf( err, sizeof( err ), "Can't run '%s'", (LPCTSTR)cmdLine );
+ MessageBox( err, "Error", MB_OK );
+ }
+ }
+ }
+ }
+
+ *pResult = 0;
+}
+
+void CServicesDlg::OnSize(UINT nType, int cx, int cy)
+{
+ CIdleDialog::OnSize(nType, cx, cy);
+
+ m_AnchorMgr.UpdateAnchors( this );
+}
+
+
+BOOL CServicesDlg::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
+{
+ NMHDR *pHdr = (NMHDR*)lParam;
+ if ( pHdr->idFrom == IDC_SERVICES_LIST )
+ {
+ if ( pHdr->code == LVN_COLUMNCLICK )
+ {
+ LPNMLISTVIEW pListView = (LPNMLISTVIEW)lParam;
+
+ // Now sort by this column.
+ int iSortColumn = max( 0, min( pListView->iSubItem, (int)ARRAYSIZE( g_ColumnInfos ) - 1 ) );
+ PushSortColumn( iSortColumn );
+ ResortItems();
+ }
+ }
+
+ return CIdleDialog::OnNotify(wParam, lParam, pResult);
+}
+
+
+void CServicesDlg::BuildClipboardText( CUtlVector<char> &clipboardText )
+{
+ // Add the header information.
+ CHeaderCtrl *pHeader = m_ServicesList.GetHeaderCtrl();
+ for ( int i=0; i < pHeader->GetItemCount(); i++ )
+ {
+ char tempBuffer[512];
+ HDITEM item;
+ memset( &item, 0, sizeof( item ) );
+ item.mask = HDI_TEXT;
+ item.pszText = tempBuffer;
+ item.cchTextMax = sizeof( tempBuffer ) - 1;
+
+ if ( !pHeader->GetItem( i, &item ) )
+ item.pszText = "<bug>";
+
+ clipboardText.AddMultipleToTail( strlen( item.pszText ), item.pszText );
+ clipboardText.AddToTail( '\t' );
+ }
+ clipboardText.AddMultipleToTail( 2, "\r\n" );
+
+ // Now add each line of data.
+ int nItem = -1;
+ while ( (nItem = m_ServicesList.GetNextItem( nItem, LVNI_ALL )) != -1 )
+ {
+ char tempBuffer[512];
+ LVITEM item;
+ memset( &item, 0, sizeof( item ) );
+ item.mask = LVIF_TEXT;
+ item.iItem = nItem;
+ item.pszText = tempBuffer;
+ item.cchTextMax = sizeof( tempBuffer ) - 1;
+
+ for ( int i=0; i < pHeader->GetItemCount(); i++ )
+ {
+ item.iSubItem = i;
+ if ( !m_ServicesList.GetItem( &item ) )
+ {
+ item.pszText = "<bug>";
+ }
+
+ clipboardText.AddMultipleToTail( strlen( item.pszText ), item.pszText );
+ clipboardText.AddToTail( '\t' );
+ }
+ clipboardText.AddMultipleToTail( 2, "\r\n" );
+ }
+ clipboardText.AddToTail( 0 );
+}
+
+
+void CServicesDlg::OnCopyToClipboard()
+{
+ // Open and clear the clipboard.
+ if ( !OpenClipboard() )
+ return;
+
+ EmptyClipboard();
+
+ // Setup the clipboard text.
+ CUtlVector<char> clipboardText;
+ BuildClipboardText( clipboardText );
+
+ // Put the clipboard text into a global memory object.
+ HANDLE hMem = GlobalAlloc( GMEM_MOVEABLE, clipboardText.Count() );
+ void *ptr = GlobalLock( hMem );
+ memcpy( ptr, clipboardText.Base(), clipboardText.Count() );
+ GlobalUnlock( hMem );
+
+ // Put it in the clipboard.
+ SetClipboardData( CF_TEXT, hMem );
+
+ // Cleanup.
+ GlobalFree( hMem );
+ CloseClipboard();
+}
diff --git a/utils/vmpi/vmpi_services_watch/ServicesDlg.h b/utils/vmpi/vmpi_services_watch/ServicesDlg.h
new file mode 100644
index 0000000..8ff35e3
--- /dev/null
+++ b/utils/vmpi/vmpi_services_watch/ServicesDlg.h
@@ -0,0 +1,150 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#if !defined(AFX_SERVICESDLG_H__FD755233_0A7A_4CBB_BA5E_A5D0B3B5F830__INCLUDED_)
+#define AFX_SERVICESDLG_H__FD755233_0A7A_4CBB_BA5E_A5D0B3B5F830__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+// ServicesDlg.h : header file
+//
+
+#include "iphelpers.h"
+#include "idle_dialog.h"
+#include "utllinkedlist.h"
+#include "resource.h"
+#include "window_anchor_mgr.h"
+#include "net_view_thread.h"
+#include "vmpi_defs.h"
+
+
+class CServiceInfo
+{
+public:
+
+ bool IsOff() const; // Returns true if the time since we've heard from this guy is too long.
+
+
+public:
+
+ CString m_ComputerName;
+ CString m_MasterName;
+ CString m_Password;
+ int m_iState;
+
+ // Since the live time is always changing, we only update it every 10 seconds or so.
+ DWORD m_LiveTimeMS; // How long the service has been running (in milliseconds).
+
+ DWORD m_WorkerAppTimeMS; // How long the worker app has been running (0 if it's not running).
+
+ DWORD m_LastPingTimeMS; // Last time we heard from this machine. Used to detect if the service
+ // is off or not.
+
+ // Used to detect if we need to re-sort the list.
+ const char *m_pLastStatusText;
+ DWORD m_LastLiveTimeMS;
+ CString m_LastMasterName;
+
+ int m_CPUPercentage;
+ CString m_ExeName;
+ CString m_MapName;
+ int m_MemUsageMB;
+
+ // Last time we updated the service in the listbox.. used to make sure we update its on/off status
+ // every once in a while.
+ DWORD m_LastUpdateTime;
+
+ int m_ProtocolVersion; // i.e. the service's VMPI_SERVICE_PROTOCOL_VERSION.
+ char m_ServiceVersion[32]; // Version string.
+
+ CIPAddr m_Addr;
+};
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+// CServicesDlg dialog
+
+class CServicesDlg : public CIdleDialog
+{
+// Construction
+public:
+ CServicesDlg(CWnd* pParent = NULL); // standard constructor
+
+// Dialog Data
+ //{{AFX_DATA(CServicesDlg)
+ enum { IDD = IDD_SERVICES };
+ CStatic m_NumServicesControl;
+ CStatic m_NumDisabledServicesControl;
+ CStatic m_NumWorkingServicesControl;
+ CStatic m_NumWaitingServicesControl;
+ CStatic m_NumOffServicesControl;
+ CStatic m_PasswordDisplay;
+ CListCtrl m_ServicesList;
+ //}}AFX_DATA
+
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CServicesDlg)
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+ virtual BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult);
+ //}}AFX_VIRTUAL
+
+// Implementation
+protected:
+ void BuildVMPIPingPacket( CUtlVector<char> &out, char cPacketID, unsigned char cProtocolVersion=VMPI_PROTOCOL_VERSION, bool bIgnorePassword=false );
+
+ virtual void OnIdle();
+
+
+ CServiceInfo* FindServiceByComputerName( const char *pComputerName );
+ void SendToSelectedServices( const char *pData, int len );
+ void UpdateServiceInListbox( CServiceInfo *pInfo );
+ void UpdateServiceCountDisplay();
+ void ResortItems();
+
+ void UpdateServicesFromNetMessages();
+ void UpdateServicesFromNetView();
+ void BuildClipboardText( CUtlVector<char> &clipboardText );
+
+
+ ISocket *m_pServicesPingSocket;
+ DWORD m_dwLastServicesPing; // Last time we pinged all the services.
+
+ // Restricts the password so we only see a particular set of VMPI services.
+ CString m_Password;
+
+ CUtlLinkedList<CServiceInfo*, int> m_Services;
+ CNetViewThread m_NetViewThread;
+
+
+ CWindowAnchorMgr m_AnchorMgr;
+
+ bool m_bListChanged; // Used to detect if we need to re-sort the list.
+
+ // Generated message map functions
+ //{{AFX_MSG(CServicesDlg)
+ virtual BOOL OnInitDialog();
+ afx_msg void OnPatchServices();
+ afx_msg void OnStopServices();
+ afx_msg void OnStopJobs();
+ afx_msg void OnFilterByPassword();
+ afx_msg void OnForcePassword();
+ afx_msg void OnCopyToClipboard();
+ afx_msg void OnDblclkServicesList(NMHDR* pNMHDR, LRESULT* pResult);
+ afx_msg void OnSize(UINT nType, int cx, int cy);
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_SERVICESDLG_H__FD755233_0A7A_4CBB_BA5E_A5D0B3B5F830__INCLUDED_)
diff --git a/utils/vmpi/vmpi_services_watch/SetPasswordDlg.cpp b/utils/vmpi/vmpi_services_watch/SetPasswordDlg.cpp
new file mode 100644
index 0000000..ebe0b6a
--- /dev/null
+++ b/utils/vmpi/vmpi_services_watch/SetPasswordDlg.cpp
@@ -0,0 +1,49 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// SetPasswordDlg.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "SetPasswordDlg.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CSetPasswordDlg dialog
+
+
+CSetPasswordDlg::CSetPasswordDlg(int dlgID, CWnd* pParent /*=NULL*/)
+ : CDialog(dlgID, pParent)
+{
+ //{{AFX_DATA_INIT(CSetPasswordDlg)
+ m_Password = _T("");
+ //}}AFX_DATA_INIT
+}
+
+
+void CSetPasswordDlg::DoDataExchange(CDataExchange* pDX)
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(CSetPasswordDlg)
+ DDX_Text(pDX, IDC_PASSWORD, m_Password);
+ //}}AFX_DATA_MAP
+}
+
+
+BEGIN_MESSAGE_MAP(CSetPasswordDlg, CDialog)
+ //{{AFX_MSG_MAP(CSetPasswordDlg)
+ // NOTE: the ClassWizard will add message map macros here
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CSetPasswordDlg message handlers
diff --git a/utils/vmpi/vmpi_services_watch/SetPasswordDlg.h b/utils/vmpi/vmpi_services_watch/SetPasswordDlg.h
new file mode 100644
index 0000000..f738af1
--- /dev/null
+++ b/utils/vmpi/vmpi_services_watch/SetPasswordDlg.h
@@ -0,0 +1,54 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#if !defined(AFX_SETPASSWORDDLG_H__A349B943_49B8_4C7E_863B_BF1929AD5443__INCLUDED_)
+#define AFX_SETPASSWORDDLG_H__A349B943_49B8_4C7E_863B_BF1929AD5443__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+// SetPasswordDlg.h : header file
+//
+
+#include "resource.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// CSetPasswordDlg dialog
+
+class CSetPasswordDlg : public CDialog
+{
+// Construction
+public:
+ CSetPasswordDlg(int dlgID, CWnd* pParent = NULL); // standard constructor
+
+// Dialog Data
+ //{{AFX_DATA(CSetPasswordDlg)
+ CString m_Password;
+ //}}AFX_DATA
+
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CSetPasswordDlg)
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+ //}}AFX_VIRTUAL
+
+// Implementation
+protected:
+
+ // Generated message map functions
+ //{{AFX_MSG(CSetPasswordDlg)
+ // NOTE: the ClassWizard will add member functions here
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_SETPASSWORDDLG_H__A349B943_49B8_4C7E_863B_BF1929AD5443__INCLUDED_)
diff --git a/utils/vmpi/vmpi_services_watch/StdAfx.cpp b/utils/vmpi/vmpi_services_watch/StdAfx.cpp
new file mode 100644
index 0000000..5ee32bc
--- /dev/null
+++ b/utils/vmpi/vmpi_services_watch/StdAfx.cpp
@@ -0,0 +1,15 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// stdafx.cpp : source file that includes just the standard includes
+// vmpi_browser_services.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+
+
diff --git a/utils/vmpi/vmpi_services_watch/StdAfx.h b/utils/vmpi/vmpi_services_watch/StdAfx.h
new file mode 100644
index 0000000..a73914f
--- /dev/null
+++ b/utils/vmpi/vmpi_services_watch/StdAfx.h
@@ -0,0 +1,35 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#if !defined(AFX_STDAFX_H__BC4FB757_77BB_4E3A_8842_290307F41798__INCLUDED_)
+#define AFX_STDAFX_H__BC4FB757_77BB_4E3A_8842_290307F41798__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
+
+#include "tier0/wchartypes.h"
+#include <afxwin.h> // MFC core and standard components
+#include <afxext.h> // MFC extensions
+#include <afxdisp.h> // MFC Automation classes
+#include <afxdtctl.h> // MFC support for Internet Explorer 4 Common Controls
+#ifndef _AFX_NO_AFXCMN_SUPPORT
+#include <afxcmn.h> // MFC support for Windows Common Controls
+#endif // _AFX_NO_AFXCMN_SUPPORT
+
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_STDAFX_H__BC4FB757_77BB_4E3A_8842_290307F41798__INCLUDED_)
diff --git a/utils/vmpi/vmpi_services_watch/res/vmpi.ico b/utils/vmpi/vmpi_services_watch/res/vmpi.ico
new file mode 100644
index 0000000..09720c0
--- /dev/null
+++ b/utils/vmpi/vmpi_services_watch/res/vmpi.ico
Binary files differ
diff --git a/utils/vmpi/vmpi_services_watch/res/vmpi_browser_services.ico b/utils/vmpi/vmpi_services_watch/res/vmpi_browser_services.ico
new file mode 100644
index 0000000..7eef0bc
--- /dev/null
+++ b/utils/vmpi/vmpi_services_watch/res/vmpi_browser_services.ico
Binary files differ
diff --git a/utils/vmpi/vmpi_services_watch/res/vmpi_browser_services.rc2 b/utils/vmpi/vmpi_services_watch/res/vmpi_browser_services.rc2
new file mode 100644
index 0000000..a322a2e
--- /dev/null
+++ b/utils/vmpi/vmpi_services_watch/res/vmpi_browser_services.rc2
@@ -0,0 +1,13 @@
+//
+// VMPI_BROWSER_SERVICES.RC2 - resources Microsoft Visual C++ does not edit directly
+//
+
+#ifdef APSTUDIO_INVOKED
+ #error this file is not editable by Microsoft Visual C++
+#endif //APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Add manually edited resources here...
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/utils/vmpi/vmpi_services_watch/resource.h b/utils/vmpi/vmpi_services_watch/resource.h
new file mode 100644
index 0000000..1f172da
--- /dev/null
+++ b/utils/vmpi/vmpi_services_watch/resource.h
@@ -0,0 +1,48 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by vmpi_browser_services.rc
+//
+#define IDD_VMPI_BROWSER_SERVICES_DIALOG 102
+#define IDR_MAINFRAME 128
+#define IDD_SET_PASSWORD 130
+#define IDD_SERVICES 133
+#define IDD_TIMEOUT 134
+#define IDD_FORCE_PASSWORD 135
+#define IDC_CURRENT_PASSWORD_LABEL 1000
+#define IDC_PASSWORD 1001
+#define IDC_NUM_SERVICES_LABEL 1001
+#define IDC_NUM_DISABLED_SERVICES_LABEL 1002
+#define IDC_COMMAND_LINE 1002
+#define IDC_NUM_WORKING_SERVICES_LABEL 1003
+#define IDC_VMPI_TRANSFER_DIRECTORY 1003
+#define IDC_NUM_WAITING_SERVICES_LABEL 1004
+#define IDC_CHECK1 1004
+#define IDC_FORCE_PATCH 1004
+#define IDC_TIMEOUT_SECONDS 1005
+#define IDC_NUM_OFF_SERVICES_LABEL 1005
+#define IDC_SERVICES_LIST 1009
+#define ID_PATCH_SERVICES 1010
+#define ID_STOP_SERVICES 1011
+#define ID_STOP_JOBS 1012
+#define ID_CHANGE_PASSWORD 1013
+#define ID_FILTER_BY_PASSWORD 1013
+#define IDC_CURRENT_PASSWORD 1014
+#define ID_COPY_TO_CLIPBOARD 1015
+#define IDC_NUM_SERVICES 1016
+#define IDC_NUM_DISABLED_SERVICES 1017
+#define IDC_NUM_WORKING_SERVICES 1018
+#define IDC_NUM_WAITING_SERVICES 1019
+#define IDC_NUM_OFF_SERVICES 1020
+#define ID_FORCE_PASSWORD 1022
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 129
+#define _APS_NEXT_COMMAND_VALUE 32771
+#define _APS_NEXT_CONTROL_VALUE 1005
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/utils/vmpi/vmpi_services_watch/vmpi_browser_services.cpp b/utils/vmpi/vmpi_services_watch/vmpi_browser_services.cpp
new file mode 100644
index 0000000..168e4ef
--- /dev/null
+++ b/utils/vmpi/vmpi_services_watch/vmpi_browser_services.cpp
@@ -0,0 +1,81 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// vmpi_browser_services.cpp : Defines the class behaviors for the application.
+//
+
+#include "stdafx.h"
+#include "vmpi_browser_services.h"
+#include "ServicesDlg.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CVMPIBrowserServicesApp
+
+BEGIN_MESSAGE_MAP(CVMPIBrowserServicesApp, CWinApp)
+ //{{AFX_MSG_MAP(CVMPIBrowserServicesApp)
+ // NOTE - the ClassWizard will add and remove mapping macros here.
+ // DO NOT EDIT what you see in these blocks of generated code!
+ //}}AFX_MSG
+ ON_COMMAND(ID_HELP, CWinApp::OnHelp)
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CVMPIBrowserServicesApp construction
+
+CVMPIBrowserServicesApp::CVMPIBrowserServicesApp()
+{
+ // TODO: add construction code here,
+ // Place all significant initialization in InitInstance
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// The one and only CVMPIBrowserServicesApp object
+
+CVMPIBrowserServicesApp theApp;
+
+/////////////////////////////////////////////////////////////////////////////
+// CVMPIBrowserServicesApp initialization
+
+BOOL CVMPIBrowserServicesApp::InitInstance()
+{
+ AfxEnableControlContainer();
+
+ // Standard initialization
+ // If you are not using these features and wish to reduce the size
+ // of your final executable, you should remove from the following
+ // the specific initialization routines you do not need.
+
+#ifdef _AFXDLL
+ Enable3dControls(); // Call this when using MFC in a shared DLL
+#else
+ Enable3dControlsStatic(); // Call this when linking to MFC statically
+#endif
+
+ CServicesDlg dlg;
+ m_pMainWnd = &dlg;
+ int nResponse = dlg.DoModal();
+ if (nResponse == IDOK)
+ {
+ // TODO: Place code here to handle when the dialog is
+ // dismissed with OK
+ }
+ else if (nResponse == IDCANCEL)
+ {
+ // TODO: Place code here to handle when the dialog is
+ // dismissed with Cancel
+ }
+
+ // Since the dialog has been closed, return FALSE so that we exit the
+ // application, rather than start the application's message pump.
+ return FALSE;
+}
diff --git a/utils/vmpi/vmpi_services_watch/vmpi_browser_services.h b/utils/vmpi/vmpi_services_watch/vmpi_browser_services.h
new file mode 100644
index 0000000..6bfe0f8
--- /dev/null
+++ b/utils/vmpi/vmpi_services_watch/vmpi_browser_services.h
@@ -0,0 +1,56 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// vmpi_browser_services.h : main header file for the VMPI_BROWSER_SERVICES application
+//
+
+#if !defined(AFX_VMPI_BROWSER_SERVICES_H__B03E2165_4E70_48AC_A991_EB0289A3471E__INCLUDED_)
+#define AFX_VMPI_BROWSER_SERVICES_H__B03E2165_4E70_48AC_A991_EB0289A3471E__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#ifndef __AFXWIN_H__
+ #error include 'stdafx.h' before including this file for PCH
+#endif
+
+#include "resource.h" // main symbols
+
+/////////////////////////////////////////////////////////////////////////////
+// CVMPIBrowserServicesApp:
+// See vmpi_browser_services.cpp for the implementation of this class
+//
+
+class CVMPIBrowserServicesApp : public CWinApp
+{
+public:
+ CVMPIBrowserServicesApp();
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CVMPIBrowserServicesApp)
+ public:
+ virtual BOOL InitInstance();
+ //}}AFX_VIRTUAL
+
+// Implementation
+
+ //{{AFX_MSG(CVMPIBrowserServicesApp)
+ // NOTE - the ClassWizard will add and remove member functions here.
+ // DO NOT EDIT what you see in these blocks of generated code !
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_VMPI_BROWSER_SERVICES_H__B03E2165_4E70_48AC_A991_EB0289A3471E__INCLUDED_)
diff --git a/utils/vmpi/vmpi_services_watch/vmpi_browser_services.rc b/utils/vmpi/vmpi_services_watch/vmpi_browser_services.rc
new file mode 100644
index 0000000..e3e3245
--- /dev/null
+++ b/utils/vmpi/vmpi_services_watch/vmpi_browser_services.rc
@@ -0,0 +1,249 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "#define _AFX_NO_SPLITTER_RESOURCES\r\n"
+ "#define _AFX_NO_OLE_RESOURCES\r\n"
+ "#define _AFX_NO_TRACKER_RESOURCES\r\n"
+ "#define _AFX_NO_PROPERTY_RESOURCES\r\n"
+ "\r\n"
+ "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n"
+ "#ifdef _WIN32\r\n"
+ "LANGUAGE 9, 1\r\n"
+ "#pragma code_page(1252)\r\n"
+ "#endif //_WIN32\r\n"
+ "#include ""res\\vmpi_browser_services.rc2"" // non-Microsoft Visual C++ edited resources\r\n"
+ "#include ""afxres.rc"" // Standard components\r\n"
+ "#endif\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDR_MAINFRAME ICON "res\\vmpi.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,1
+ PRODUCTVERSION 1,0,0,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904B0"
+ BEGIN
+ VALUE "FileDescription", "vmpi_browser_services MFC Application"
+ VALUE "FileVersion", "1, 0, 0, 1"
+ VALUE "InternalName", "vmpi_browser_services"
+ VALUE "LegalCopyright", "Copyright (C) 2003"
+ VALUE "OriginalFilename", "vmpi_browser_services.EXE"
+ VALUE "ProductName", "vmpi_browser_services Application"
+ VALUE "ProductVersion", "1, 0, 0, 1"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_SERVICES, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 791
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 246
+ END
+
+ IDD_TIMEOUT, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 355
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 159
+ END
+
+ IDD_SET_PASSWORD, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 212
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 91
+ END
+
+ IDD_FORCE_PASSWORD, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 212
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 71
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_SERVICES DIALOGEX 0, 0, 798, 253
+STYLE DS_SETFONT | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
+CAPTION "VMPI Services"
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+ CONTROL "List1",IDC_SERVICES_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_SORTASCENDING | WS_BORDER | WS_TABSTOP,7,7,784,155
+ DEFPUSHBUTTON "Filter By Password",ID_FILTER_BY_PASSWORD,715,166,76,14
+ DEFPUSHBUTTON "Copy To Clipboard",ID_COPY_TO_CLIPBOARD,7,232,76,14
+ DEFPUSHBUTTON "Force Password",ID_FORCE_PASSWORD,184,232,76,14
+ DEFPUSHBUTTON "Stop Service(s)",ID_STOP_SERVICES,361,232,76,14
+ DEFPUSHBUTTON "Stop Job(s)",ID_STOP_JOBS,538,232,76,14
+ DEFPUSHBUTTON "Patch Services",ID_PATCH_SERVICES,715,232,76,14
+ LTEXT "Filtering By Password:",IDC_CURRENT_PASSWORD_LABEL,128,169,70,8
+ LTEXT "",IDC_CURRENT_PASSWORD,198,169,55,8
+ LTEXT "# Machines:",IDC_NUM_SERVICES_LABEL,7,169,40,8
+ LTEXT "2",IDC_NUM_SERVICES,47,169,55,8
+ LTEXT "# Disabled:",IDC_NUM_DISABLED_SERVICES_LABEL,7,181,37,8
+ LTEXT "2",IDC_NUM_DISABLED_SERVICES,47,181,55,8
+ LTEXT "# Working:",IDC_NUM_WORKING_SERVICES_LABEL,7,192,36,8
+ LTEXT "2",IDC_NUM_WORKING_SERVICES,47,192,55,8
+ LTEXT "# Waiting:",IDC_NUM_WAITING_SERVICES_LABEL,7,203,34,8
+ LTEXT "2",IDC_NUM_WAITING_SERVICES,47,203,55,8
+ LTEXT "# Off:",IDC_NUM_OFF_SERVICES_LABEL,7,214,19,8
+ LTEXT "2",IDC_NUM_OFF_SERVICES,47,214,55,8
+END
+
+IDD_TIMEOUT DIALOGEX 0, 0, 362, 166
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Patch"
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+ EDITTEXT IDC_COMMAND_LINE,93,78,262,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_VMPI_TRANSFER_DIRECTORY,93,98,262,14,ES_AUTOHSCROLL
+ CONTROL "Force Patch (otherwise, the service will ignore the patch if the new version is not higher)",IDC_FORCE_PATCH,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,121,291,10
+ DEFPUSHBUTTON "OK",IDOK,123,145,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,189,145,50,14
+ LTEXT "This will setup all the selected services to be patched.",IDC_STATIC,7,7,348,11
+ LTEXT "Patch Directory:",IDC_STATIC,7,81,52,8
+ LTEXT "First, it will run vmpi_transfer from the VMPI_Transfer directory. This is kept separate because the vmpi_transfer.exe in that directory might be a different version than the new one in the patch directory.",IDC_STATIC,7,21,348,18
+ LTEXT "VMPI_Transfer Directory:",IDC_STATIC,7,101,80,8
+ LTEXT "The selected services will download the patch files, then run vmpi_service_install.",IDC_STATIC,7,42,348,13
+END
+
+IDD_SET_PASSWORD DIALOGEX 0, 0, 219, 98
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Filter By Password"
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+ EDITTEXT IDC_PASSWORD,7,56,205,14,ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK",IDOK,7,77,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,162,77,50,14
+ LTEXT "This will cause the services browser to only show services that are using the specified password.\n\nIf you enter a blank password, then all services will be shown regardless of their password.",IDC_CURRENT_PASSWORD_LABEL,7,7,195,46
+END
+
+IDD_FORCE_PASSWORD DIALOGEX 0, 0, 219, 78
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Force Password"
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+ EDITTEXT IDC_PASSWORD,7,38,205,14,ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK",IDOK,7,57,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,162,57,50,14
+ LTEXT "This will set the specified password on all selected services. VMPI jobs must run with -mpi_pw <password> to use those services.",IDC_CURRENT_PASSWORD_LABEL,7,7,195,27
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#define _AFX_NO_SPLITTER_RESOURCES
+#define _AFX_NO_OLE_RESOURCES
+#define _AFX_NO_TRACKER_RESOURCES
+#define _AFX_NO_PROPERTY_RESOURCES
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE 9, 1
+#pragma code_page(1252)
+#endif //_WIN32
+#include "res\vmpi_browser_services.rc2" // non-Microsoft Visual C++ edited resources
+#include "afxres.rc" // Standard components
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/utils/vmpi/vmpi_services_watch/vmpi_services_watch.vpc b/utils/vmpi/vmpi_services_watch/vmpi_services_watch.vpc
new file mode 100644
index 0000000..a58e2a6
--- /dev/null
+++ b/utils/vmpi/vmpi_services_watch/vmpi_services_watch.vpc
@@ -0,0 +1,72 @@
+//-----------------------------------------------------------------------------
+// vmpi_services_watch.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR "..\..\.."
+$Macro OUTBINDIR "$SRCDIR\..\game\bin"
+$Macro OUTBINNAME "vmpi_services_watch"
+
+$Include "$SRCDIR\vpc_scripts\source_exe_base.vpc"
+
+$Configuration
+{
+ $Compiler
+ {
+ $AdditionalIncludeDirectories "$BASE;.\;..\;..\..\common"
+ $PreprocessorDefinitions "$BASE;PROTECTED_THINGS_DISABLE;WINVER=0x501;NO_WARN_MBCS_MFC_DEPRECATION"
+ $EnableC++Exceptions "Yes (/EHsc)"
+ }
+
+ $Linker
+ {
+ $AdditionalDependencies "$BASE ws2_32.lib"
+ }
+}
+
+$Project "vmpi_services_watch"
+{
+ $Folder "Source Files"
+ {
+ -$File "$SRCDIR\public\tier0\memoverride.cpp"
+
+ $File "..\idle_dialog.cpp"
+ $File "..\net_view_thread.cpp"
+ $File "PatchTimeout.cpp"
+ $File "ServicesDlg.cpp"
+ $File "SetPasswordDlg.cpp"
+ $File "..\vmpi_browser_helpers.cpp"
+ $File "vmpi_browser_services.cpp"
+ $File "vmpi_browser_services.rc"
+ $File "..\win_idle.cpp"
+ $File "..\window_anchor_mgr.cpp"
+ $File "StdAfx.cpp"
+ }
+
+ $Folder "Header Files"
+ {
+ $File "..\idle_dialog.h"
+ $File "..\net_view_thread.h"
+ $File "PatchTimeout.h"
+ $File "Resource.h"
+ $File "ServicesDlg.h"
+ $File "SetPasswordDlg.h"
+ $File "StdAfx.h"
+ $File "vmpi_browser_services.h"
+ $File "..\vmpi_defs.h"
+ $File "..\win_idle.h"
+ $File "..\window_anchor_mgr.h"
+ }
+
+ $Folder "Resources"
+ {
+ $File "res\vmpi_browser_services.ico"
+ $File "res\vmpi_browser_services.rc2"
+ }
+
+ $Folder "Link Libraries"
+ {
+ $Lib vmpi
+ }
+}