source-engine/engine/cl_check_process.cpp

317 lines
7.8 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Windows Only, does a check against all other processes to see how many other instances
// of this process is running concurrently
//
//=============================================================================//
//*********************************************************************************************
// Process Check
#include "cl_check_process.h"
#include "dbg.h"
#ifdef IS_WINDOWS_PC
#include <Windows.h>
#include <winternl.h>
#include <stdio.h>
#include <TlHelp32.h>
#include "strtools.h"
#include <Psapi.h>
#endif // IS_WINDOWS_PC
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#ifdef IS_WINDOWS_PC
#define HANDLE_QUERY_BUFFER_BLOCK_SIZE ( 1 * 1024 * 1024 )
#define SystemHandleInformation ( (SYSTEM_INFORMATION_CLASS)16 )
#define STATUS_INFO_LENGTH_MISMATCH ( (NTSTATUS)( 0xC0000004L ) )
typedef NTSTATUS (__stdcall *NtQuerySystemInformation1)
(
IN ULONG SysInfoClass,
IN OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG RetLen
);
typedef struct _HANDLE_INFORMATION
{
DWORD ProcessId;
BYTE ObjectType;
BYTE Flags;
USHORT Handle;
PVOID KernelObject;
ACCESS_MASK GrantedAccess;
} HANDLE_INFORMATION, *PHANDLE_INFORMATION;
typedef struct _SYSTEM_HANDLE_INFORMATION
{
ULONG HandleCount;
HANDLE_INFORMATION HandleInfoArray[ 1 ];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
//
// Checks Process Count with CreateToolhelp32Snapshot
//
int CheckOtherInstancesRunningWithSnapShot( const char *thisProcessNameShort )
{
DWORD nLength;
char otherProcessNameShort[ MAX_PATH ];
nLength = MAX_PATH;
int iSnapShotCount = 0;
//
HANDLE hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
if( hSnapshot )
{
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
if ( Process32First( hSnapshot, &pe32 ) )
{
do
{
V_FileBase( pe32.szExeFile, otherProcessNameShort, MAX_PATH );
if ( V_strcmp( thisProcessNameShort, otherProcessNameShort ) == 0 )
{
// DevMsg( "CreateToolhelp32Snapshot - Process Name [ %s ] - OtherName [ %s ] \n", thisProcessNameShort, pe32.szExeFile );
// We found an instance of this executable.
iSnapShotCount++;
}
} while( Process32Next( hSnapshot, &pe32 ) );
}
CloseHandle(hSnapshot);
}
return iSnapShotCount;
}
//
// Checks Process Count with QueryFullProcessImageName and OpenProcess
//
int CheckOtherInstancesWithEnumProcess( const char *thisProcessNameShort )
{
NTSTATUS status;
BOOL bStatus;
DWORD nLength;
DWORD nBufferSize;
DWORD nLastProcess;
DWORD i;
HANDLE process;
PSYSTEM_HANDLE_INFORMATION pHandleInfo = NULL;
char otherProcessName[ MAX_PATH ];
char otherProcessNameShort[ MAX_PATH ];
HINSTANCE hInst = NULL;
// Start with a count of zero, since we will find ourselves too.
int iProcessCount = 0;
// Get the path to the executable for this process.
nLength = MAX_PATH;
// Query all of the handles in the system. We have to do this in a loop, since we do
// not know how large of a buffer we need.
nBufferSize = 0;
// Load ntdll.dll so we can Query the system
/* load the ntdll.dll */
NtQuerySystemInformation1 NtQuerySystemInformation;
//PVOID Info;
HMODULE hModule = LoadLibrary( "ntdll.dll" );
if (!hModule)
{
iProcessCount = CHECK_PROCESS_UNSUPPORTED;
goto Cleanup;
}
while ( TRUE )
{
// Increase the buffer size and try the query.
if ( pHandleInfo != NULL )
{
free( pHandleInfo );
}
nBufferSize += HANDLE_QUERY_BUFFER_BLOCK_SIZE;
pHandleInfo = (PSYSTEM_HANDLE_INFORMATION)malloc( nBufferSize );
if ( pHandleInfo == NULL )
{
iProcessCount = CHECK_PROCESS_UNSUPPORTED;
goto Cleanup;
}
// Query the handles in the system.
NtQuerySystemInformation = (NtQuerySystemInformation1)GetProcAddress(hModule, "NtQuerySystemInformation");
if ( NtQuerySystemInformation == NULL )
{
iProcessCount = CHECK_PROCESS_UNSUPPORTED;
goto Cleanup;
}
status = NtQuerySystemInformation( SystemHandleInformation, pHandleInfo, nBufferSize, NULL );
// If our buffer was too small, try again.
if ( status == STATUS_INFO_LENGTH_MISMATCH )
{
continue;
}
// If the query failed, return the error.
if ( !NT_SUCCESS( status ) )
{
iProcessCount = CHECK_PROCESS_UNSUPPORTED;
goto Cleanup;
}
break;
}
//
// Walk all of the entries looking for process IDs we have not processed yet.
// Note that this code assumes that handles will be grouped by process, which is
// what Windows does. If that assumption ever turns out to be false, this code
// will have to be altered to keep a list of process IDs already seen.
//
// Check for the presence of GetModuleFileNameEx
hInst = LoadLibrary( "Psapi.dll" );
if ( !hInst )
return CHECK_PROCESS_UNSUPPORTED;
typedef DWORD (WINAPI *GetProcessImageFileNameFn)(HANDLE, LPTSTR, DWORD);
GetProcessImageFileNameFn fn = (GetProcessImageFileNameFn)GetProcAddress( hInst,
#ifdef UNICODE
"GetProcessImageFileNameW");
#else
"GetProcessImageFileNameA");
#endif
if ( !fn )
return CHECK_PROCESS_UNSUPPORTED;
nLastProcess = 0;
for ( i = 0; i < pHandleInfo->HandleCount; i++ )
{
if ( pHandleInfo->HandleInfoArray[ i ].ProcessId != nLastProcess )
{
//nLastProcess = pHandleInfo->HandleInfoArray[ i ].ProcessId;
nLastProcess = pHandleInfo->HandleInfoArray[ i ].ProcessId;
//
// Try to open a handle to this process. Note that we may not have
// access to all processes, so we ignore errors.
//
process = OpenProcess( PROCESS_QUERY_INFORMATION,
FALSE,
nLastProcess );
if ( process != NULL )
{
// Query the name of the executable for the process we opened. If the query
// fails, we ignore this process.
nLength = MAX_PATH;
bStatus = fn( process, otherProcessName, nLength );
if ( bStatus )
{
//
// We have the process name. See if it is the same name as our process.
//
V_FileBase( otherProcessName, otherProcessNameShort, MAX_PATH );
if ( V_strcmp( thisProcessNameShort, otherProcessNameShort ) == 0 )
{
// We found an instance of this executable.
// DevMsg( "EnumProcess - Process Name [ %s ] - OtherName [ %s ] \n", thisProcessNameShort, otherProcessName );
iProcessCount++;
}
}
CloseHandle( process );
}
}
}
Cleanup:
// Free allocated resources.
if ( pHandleInfo != NULL )
{
free( pHandleInfo );
}
if ( hInst != NULL )
{
FreeLibrary( hInst );
}
if ( hModule != NULL )
{
FreeLibrary( hModule );
}
return iProcessCount;
}
#endif // IS_WINDOWS_PC
int CheckOtherInstancesRunning( void )
{
#ifdef IS_WINDOWS_PC
BOOL bStatus = 0;
DWORD nLength = MAX_PATH;
char thisProcessName[ MAX_PATH ];
char thisProcessNameShort[ MAX_PATH ];
// Load the pspapi to get our current process' name
HINSTANCE hInst = LoadLibrary( "Psapi.dll" );
if ( hInst )
{
typedef DWORD (WINAPI *GetProcessImageFileNameFn)(HANDLE, LPTSTR, DWORD);
GetProcessImageFileNameFn fn = (GetProcessImageFileNameFn)GetProcAddress( hInst,
#ifdef UNICODE
"GetProcessImageFileNameW");
#else
"GetProcessImageFileNameA");
#endif
if ( fn )
{
bStatus = fn( GetCurrentProcess(), thisProcessName, nLength );
}
FreeLibrary( hInst );
}
if ( !bStatus )
{
return CHECK_PROCESS_UNSUPPORTED;
}
V_FileBase( thisProcessName, thisProcessNameShort, MAX_PATH );
// Msg( "Checking Other Instances Running : ProcessShortName [ %s - %s ] \n", thisProcessName, thisProcessNameShort );
int iSnapShotCount = CheckOtherInstancesRunningWithSnapShot( thisProcessNameShort );
if ( iSnapShotCount > 1 )
{
return iSnapShotCount;
}
int iEnumCount = CheckOtherInstancesWithEnumProcess( thisProcessNameShort );
return iEnumCount > iSnapShotCount ? iEnumCount : iSnapShotCount;
#endif // IS_WINDOWS_PC
return CHECK_PROCESS_UNSUPPORTED; // -1 UNSUPPORTED
}