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.
317 lines
7.8 KiB
317 lines
7.8 KiB
5 years ago
|
//========= 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
|
||
|
}
|