Team Fortress 2 Source Code as on 22/4/2020
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.

316 lines
7.8 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Windows Only, does a check against all other processes to see how many other instances
  4. // of this process is running concurrently
  5. //
  6. //=============================================================================//
  7. //*********************************************************************************************
  8. // Process Check
  9. #include "cl_check_process.h"
  10. #include "dbg.h"
  11. #ifdef IS_WINDOWS_PC
  12. #include <Windows.h>
  13. #include <winternl.h>
  14. #include <stdio.h>
  15. #include <TlHelp32.h>
  16. #include "strtools.h"
  17. #include <Psapi.h>
  18. #endif // IS_WINDOWS_PC
  19. // memdbgon must be the last include file in a .cpp file!!!
  20. #include "tier0/memdbgon.h"
  21. #ifdef IS_WINDOWS_PC
  22. #define HANDLE_QUERY_BUFFER_BLOCK_SIZE ( 1 * 1024 * 1024 )
  23. #define SystemHandleInformation ( (SYSTEM_INFORMATION_CLASS)16 )
  24. #define STATUS_INFO_LENGTH_MISMATCH ( (NTSTATUS)( 0xC0000004L ) )
  25. typedef NTSTATUS (__stdcall *NtQuerySystemInformation1)
  26. (
  27. IN ULONG SysInfoClass,
  28. IN OUT PVOID SystemInformation,
  29. IN ULONG SystemInformationLength,
  30. OUT PULONG RetLen
  31. );
  32. typedef struct _HANDLE_INFORMATION
  33. {
  34. DWORD ProcessId;
  35. BYTE ObjectType;
  36. BYTE Flags;
  37. USHORT Handle;
  38. PVOID KernelObject;
  39. ACCESS_MASK GrantedAccess;
  40. } HANDLE_INFORMATION, *PHANDLE_INFORMATION;
  41. typedef struct _SYSTEM_HANDLE_INFORMATION
  42. {
  43. ULONG HandleCount;
  44. HANDLE_INFORMATION HandleInfoArray[ 1 ];
  45. } SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
  46. //
  47. // Checks Process Count with CreateToolhelp32Snapshot
  48. //
  49. int CheckOtherInstancesRunningWithSnapShot( const char *thisProcessNameShort )
  50. {
  51. DWORD nLength;
  52. char otherProcessNameShort[ MAX_PATH ];
  53. nLength = MAX_PATH;
  54. int iSnapShotCount = 0;
  55. //
  56. HANDLE hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
  57. if( hSnapshot )
  58. {
  59. PROCESSENTRY32 pe32;
  60. pe32.dwSize = sizeof(PROCESSENTRY32);
  61. if ( Process32First( hSnapshot, &pe32 ) )
  62. {
  63. do
  64. {
  65. V_FileBase( pe32.szExeFile, otherProcessNameShort, MAX_PATH );
  66. if ( V_strcmp( thisProcessNameShort, otherProcessNameShort ) == 0 )
  67. {
  68. // DevMsg( "CreateToolhelp32Snapshot - Process Name [ %s ] - OtherName [ %s ] \n", thisProcessNameShort, pe32.szExeFile );
  69. // We found an instance of this executable.
  70. iSnapShotCount++;
  71. }
  72. } while( Process32Next( hSnapshot, &pe32 ) );
  73. }
  74. CloseHandle(hSnapshot);
  75. }
  76. return iSnapShotCount;
  77. }
  78. //
  79. // Checks Process Count with QueryFullProcessImageName and OpenProcess
  80. //
  81. int CheckOtherInstancesWithEnumProcess( const char *thisProcessNameShort )
  82. {
  83. NTSTATUS status;
  84. BOOL bStatus;
  85. DWORD nLength;
  86. DWORD nBufferSize;
  87. DWORD nLastProcess;
  88. DWORD i;
  89. HANDLE process;
  90. PSYSTEM_HANDLE_INFORMATION pHandleInfo = NULL;
  91. char otherProcessName[ MAX_PATH ];
  92. char otherProcessNameShort[ MAX_PATH ];
  93. HINSTANCE hInst = NULL;
  94. // Start with a count of zero, since we will find ourselves too.
  95. int iProcessCount = 0;
  96. // Get the path to the executable for this process.
  97. nLength = MAX_PATH;
  98. // Query all of the handles in the system. We have to do this in a loop, since we do
  99. // not know how large of a buffer we need.
  100. nBufferSize = 0;
  101. // Load ntdll.dll so we can Query the system
  102. /* load the ntdll.dll */
  103. NtQuerySystemInformation1 NtQuerySystemInformation;
  104. //PVOID Info;
  105. HMODULE hModule = LoadLibrary( "ntdll.dll" );
  106. if (!hModule)
  107. {
  108. iProcessCount = CHECK_PROCESS_UNSUPPORTED;
  109. goto Cleanup;
  110. }
  111. while ( TRUE )
  112. {
  113. // Increase the buffer size and try the query.
  114. if ( pHandleInfo != NULL )
  115. {
  116. free( pHandleInfo );
  117. }
  118. nBufferSize += HANDLE_QUERY_BUFFER_BLOCK_SIZE;
  119. pHandleInfo = (PSYSTEM_HANDLE_INFORMATION)malloc( nBufferSize );
  120. if ( pHandleInfo == NULL )
  121. {
  122. iProcessCount = CHECK_PROCESS_UNSUPPORTED;
  123. goto Cleanup;
  124. }
  125. // Query the handles in the system.
  126. NtQuerySystemInformation = (NtQuerySystemInformation1)GetProcAddress(hModule, "NtQuerySystemInformation");
  127. if ( NtQuerySystemInformation == NULL )
  128. {
  129. iProcessCount = CHECK_PROCESS_UNSUPPORTED;
  130. goto Cleanup;
  131. }
  132. status = NtQuerySystemInformation( SystemHandleInformation, pHandleInfo, nBufferSize, NULL );
  133. // If our buffer was too small, try again.
  134. if ( status == STATUS_INFO_LENGTH_MISMATCH )
  135. {
  136. continue;
  137. }
  138. // If the query failed, return the error.
  139. if ( !NT_SUCCESS( status ) )
  140. {
  141. iProcessCount = CHECK_PROCESS_UNSUPPORTED;
  142. goto Cleanup;
  143. }
  144. break;
  145. }
  146. //
  147. // Walk all of the entries looking for process IDs we have not processed yet.
  148. // Note that this code assumes that handles will be grouped by process, which is
  149. // what Windows does. If that assumption ever turns out to be false, this code
  150. // will have to be altered to keep a list of process IDs already seen.
  151. //
  152. // Check for the presence of GetModuleFileNameEx
  153. hInst = LoadLibrary( "Psapi.dll" );
  154. if ( !hInst )
  155. return CHECK_PROCESS_UNSUPPORTED;
  156. typedef DWORD (WINAPI *GetProcessImageFileNameFn)(HANDLE, LPTSTR, DWORD);
  157. GetProcessImageFileNameFn fn = (GetProcessImageFileNameFn)GetProcAddress( hInst,
  158. #ifdef UNICODE
  159. "GetProcessImageFileNameW");
  160. #else
  161. "GetProcessImageFileNameA");
  162. #endif
  163. if ( !fn )
  164. return CHECK_PROCESS_UNSUPPORTED;
  165. nLastProcess = 0;
  166. for ( i = 0; i < pHandleInfo->HandleCount; i++ )
  167. {
  168. if ( pHandleInfo->HandleInfoArray[ i ].ProcessId != nLastProcess )
  169. {
  170. //nLastProcess = pHandleInfo->HandleInfoArray[ i ].ProcessId;
  171. nLastProcess = pHandleInfo->HandleInfoArray[ i ].ProcessId;
  172. //
  173. // Try to open a handle to this process. Note that we may not have
  174. // access to all processes, so we ignore errors.
  175. //
  176. process = OpenProcess( PROCESS_QUERY_INFORMATION,
  177. FALSE,
  178. nLastProcess );
  179. if ( process != NULL )
  180. {
  181. // Query the name of the executable for the process we opened. If the query
  182. // fails, we ignore this process.
  183. nLength = MAX_PATH;
  184. bStatus = fn( process, otherProcessName, nLength );
  185. if ( bStatus )
  186. {
  187. //
  188. // We have the process name. See if it is the same name as our process.
  189. //
  190. V_FileBase( otherProcessName, otherProcessNameShort, MAX_PATH );
  191. if ( V_strcmp( thisProcessNameShort, otherProcessNameShort ) == 0 )
  192. {
  193. // We found an instance of this executable.
  194. // DevMsg( "EnumProcess - Process Name [ %s ] - OtherName [ %s ] \n", thisProcessNameShort, otherProcessName );
  195. iProcessCount++;
  196. }
  197. }
  198. CloseHandle( process );
  199. }
  200. }
  201. }
  202. Cleanup:
  203. // Free allocated resources.
  204. if ( pHandleInfo != NULL )
  205. {
  206. free( pHandleInfo );
  207. }
  208. if ( hInst != NULL )
  209. {
  210. FreeLibrary( hInst );
  211. }
  212. if ( hModule != NULL )
  213. {
  214. FreeLibrary( hModule );
  215. }
  216. return iProcessCount;
  217. }
  218. #endif // IS_WINDOWS_PC
  219. int CheckOtherInstancesRunning( void )
  220. {
  221. #ifdef IS_WINDOWS_PC
  222. BOOL bStatus = 0;
  223. DWORD nLength = MAX_PATH;
  224. char thisProcessName[ MAX_PATH ];
  225. char thisProcessNameShort[ MAX_PATH ];
  226. // Load the pspapi to get our current process' name
  227. HINSTANCE hInst = LoadLibrary( "Psapi.dll" );
  228. if ( hInst )
  229. {
  230. typedef DWORD (WINAPI *GetProcessImageFileNameFn)(HANDLE, LPTSTR, DWORD);
  231. GetProcessImageFileNameFn fn = (GetProcessImageFileNameFn)GetProcAddress( hInst,
  232. #ifdef UNICODE
  233. "GetProcessImageFileNameW");
  234. #else
  235. "GetProcessImageFileNameA");
  236. #endif
  237. if ( fn )
  238. {
  239. bStatus = fn( GetCurrentProcess(), thisProcessName, nLength );
  240. }
  241. FreeLibrary( hInst );
  242. }
  243. if ( !bStatus )
  244. {
  245. return CHECK_PROCESS_UNSUPPORTED;
  246. }
  247. V_FileBase( thisProcessName, thisProcessNameShort, MAX_PATH );
  248. // Msg( "Checking Other Instances Running : ProcessShortName [ %s - %s ] \n", thisProcessName, thisProcessNameShort );
  249. int iSnapShotCount = CheckOtherInstancesRunningWithSnapShot( thisProcessNameShort );
  250. if ( iSnapShotCount > 1 )
  251. {
  252. return iSnapShotCount;
  253. }
  254. int iEnumCount = CheckOtherInstancesWithEnumProcess( thisProcessNameShort );
  255. return iEnumCount > iSnapShotCount ? iEnumCount : iSnapShotCount;
  256. #endif // IS_WINDOWS_PC
  257. return CHECK_PROCESS_UNSUPPORTED; // -1 UNSUPPORTED
  258. }