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.
1161 lines
33 KiB
1161 lines
33 KiB
//========= 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(); |
|
}
|
|
|