//========= 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 #include #include #include #include "strtools.h" #include #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 }