/*========================================================================== * * Copyright (C) 1996-1997 Microsoft Corporation. All Rights Reserved. * * File: dplgame.c * Content: Methods for game management * * History: * Date By Reason * ======= ======= ====== * 4/13/96 myronth Created it * 6/24/96 kipo changed guidGame to guidApplication. * 10/23/96 myronth added client/server methods * 12/12/96 myronth Fixed DPLCONNECTION validation * 2/12/97 myronth Mass DX5 changes * 2/26/97 myronth #ifdef'd out DPASYNCDATA stuff (removed dependency) * 4/3/97 myronth #ifdef'd out DPLC_StartGame (Nov. spec related) * 5/8/97 myronth Purged dead code * 5/22/97 myronth Changed error code processing of RunApplication which * was calling the wrong cleanup function (#8871) * 6/4/97 myronth Fixed handle leak (#9458) * 6/19/97 myronth Fixed handle leak (#10063) * 7/30/97 myronth Added support for standard lobby messaging and fixed * additional backslash on current directory bug (#10592) * 1/20/98 myronth Added WaitForConnectionSettings * 1/26/98 myronth Added OS_CompareString function for Win95 * 7/07/98 kipo Define and use PROCESSENTRY32A to avoid passing * Unicode data structures to Win95 functions expecting ANSI * 7/09/99 aarono Cleaning up GetLastError misuse, must call right away, * before calling anything else, including DPF. * 7/12/00 aarono fix GUIDs for IPC to be fully significant, otherwise won't IPC. * ***************************************************************************/ #include "dplobpr.h" #include #include #include //-------------------------------------------------------------------------- // // Functions // //-------------------------------------------------------------------------- #undef DPF_MODNAME #define DPF_MODNAME "PRV_FindGameInRegistry" BOOL PRV_FindGameInRegistry(LPGUID lpguid, LPWSTR lpwszAppName, DWORD dwNameSize, HKEY * lphkey) { HKEY hkeyDPApps, hkeyApp; DWORD dwIndex = 0; WCHAR wszGuidStr[GUID_STRING_SIZE]; DWORD dwGuidStrSize = GUID_STRING_SIZE; DWORD dwType = REG_SZ; GUID guidApp; LONG lReturn; BOOL bFound = FALSE; DWORD dwSaveNameSize = dwNameSize; DPF(7, "Entering PRV_FindGameInRegistry"); DPF(9, "Parameters: 0x%08x, 0x%08x, %lu, 0x%08x", lpguid, lpwszAppName, dwNameSize, lphkey); // Open the Applications key lReturn = OS_RegOpenKeyEx(HKEY_LOCAL_MACHINE, SZ_DPLAY_APPS_KEY, 0, KEY_READ, &hkeyDPApps); if(lReturn != ERROR_SUCCESS) { DPF_ERR("Unable to open DPlay Applications registry key!"); return FALSE; } // Walk the list of DPlay games in the registry, looking for // the app with the right GUID while(!bFound) { // Open the next application key dwSaveNameSize = dwNameSize; dwGuidStrSize = GUID_STRING_SIZE; lReturn = OS_RegEnumKeyEx(hkeyDPApps, dwIndex++, lpwszAppName, &dwSaveNameSize, NULL, NULL, NULL, NULL); // If the enum returns no more apps, we want to bail if(lReturn != ERROR_SUCCESS) break; // Open the application key lReturn = OS_RegOpenKeyEx(hkeyDPApps, lpwszAppName, 0, KEY_READ, &hkeyApp); if(lReturn != ERROR_SUCCESS) { DPF_ERR("Unable to open app key!"); continue; } // Get the GUID of the Game lReturn = OS_RegQueryValueEx(hkeyApp, SZ_GUID, NULL, &dwType, (LPBYTE)wszGuidStr, &dwGuidStrSize); if(lReturn != ERROR_SUCCESS) { RegCloseKey(hkeyApp); DPF_ERR("Unable to query GUID key value!"); continue; } // Convert the string to a real GUID & Compare it to the passed in one GUIDFromString(wszGuidStr, &guidApp); if(IsEqualGUID(&guidApp, lpguid)) { bFound = TRUE; break; } // Close the App key RegCloseKey(hkeyApp); } // Close the DPApps key RegCloseKey(hkeyDPApps); if(bFound) *lphkey = hkeyApp; return bFound; } // PRV_FindGameInRegistry #undef DPF_MODNAME #define DPF_MODNAME "PRV_GetKeyStringValue" BOOL PRV_GetKeyStringValue(HKEY hkeyApp, LPWSTR lpwszKey, LPWSTR * lplpwszValue) { DWORD dwType = REG_SZ; DWORD dwSize=0; LPWSTR lpwszTemp = NULL; LONG lReturn; DPF(7, "Entering PRV_GetKeyStringValue"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x", hkeyApp, lpwszKey, lplpwszValue); ASSERT(lplpwszValue); // Get the size of the buffer for the Path lReturn = OS_RegQueryValueEx(hkeyApp, lpwszKey, NULL, &dwType, NULL, &dwSize); if(lReturn != ERROR_SUCCESS) { DPF_ERR("Error getting size of key value!"); return FALSE; } // If the size is 2, then it is an empty string (only contains a // null terminator). Treat this the same as a NULL string or a // missing key and fail it. if(dwSize <= 2) return FALSE; // Alloc the buffer for the Path lpwszTemp = DPMEM_ALLOC(dwSize); if(!lpwszTemp) { DPF_ERR("Unable to allocate temporary string for Path!"); return FALSE; } // Get the value itself lReturn = OS_RegQueryValueEx(hkeyApp, lpwszKey, NULL, &dwType, (LPBYTE)lpwszTemp, &dwSize); if(lReturn != ERROR_SUCCESS) { DPMEM_FREE(lpwszTemp); DPF_ERR("Unable to get key value!"); return FALSE; } *lplpwszValue = lpwszTemp; return TRUE; } // PRV_GetKeyStringValue #undef DPF_MODNAME #define DPF_MODNAME "PRV_FreeConnectInfo" BOOL PRV_FreeConnectInfo(LPCONNECTINFO lpci) { DPF(7, "Entering PRV_FreeConnectInfo"); DPF(9, "Parameters: 0x%08x", lpci); if(!lpci) return TRUE; if(lpci->lpszName) DPMEM_FREE(lpci->lpszName); if(lpci->lpszPath) DPMEM_FREE(lpci->lpszPath); if(lpci->lpszFile) DPMEM_FREE(lpci->lpszFile); if(lpci->lpszCommandLine) DPMEM_FREE(lpci->lpszCommandLine); if(lpci->lpszCurrentDir) DPMEM_FREE(lpci->lpszCurrentDir); if(lpci->lpszAppLauncherName) DPMEM_FREE(lpci->lpszAppLauncherName); return TRUE; } // PRV_FreeConnectInfo #undef DPF_MODNAME #define DPF_MODNAME "PRV_GetConnectInfoFromRegistry" BOOL PRV_GetConnectInfoFromRegistry(LPCONNECTINFO lpci) { LPWSTR lpwszAppName; HKEY hkeyApp = NULL; LPWSTR lpwszTemp; DWORD dwSize = 0; DWORD dwError; GUID guidApp; BOOL bReturn; DPF(7, "Entering PRV_GetConnectInfoFromRegistry"); DPF(9, "Parameters: 0x%08x", lpci); // Clear our ConnectInfo structure since we will be overwriting // whatever is in it, and we are making assumptions that the // string pointers are NULL to start with. However, we need // the Application guid, so save it off guidApp = lpci->guidApplication; memset(lpci, 0, sizeof(CONNECTINFO)); lpci->guidApplication = guidApp; // Allocate memory for the App Name lpwszAppName = DPMEM_ALLOC(DPLOBBY_REGISTRY_NAMELEN*sizeof(WCHAR)); if(!lpwszAppName) { DPF_ERR("Unable to allocate memory for App Name!"); return FALSE; } // Open the registry key for the App if(!PRV_FindGameInRegistry(&(lpci->guidApplication), lpwszAppName, DPLOBBY_REGISTRY_NAMELEN, &hkeyApp)) { DPMEM_FREE(lpwszAppName); DPF_ERR("Unable to find game in registry!"); return FALSE; } lpci->lpszName = lpwszAppName; // Get the string value for the Path. If this fails, we // can use a NULL path, which represents the default path // on the CreateProcess call if(PRV_GetKeyStringValue(hkeyApp, SZ_PATH, &lpwszTemp)) { lpci->lpszPath = lpwszTemp; } // Get the string value for the File if(!PRV_GetKeyStringValue(hkeyApp, SZ_FILE, &lpwszTemp)) { DPF_ERR("Error getting value for File key!"); bReturn = FALSE; goto EXIT_GETCONNECTINFO; } lpci->lpszFile = lpwszTemp; // Get the string value for the CommandLine. If this fails, // we can pass a NULL command line to the CreateProcess call if(PRV_GetKeyStringValue(hkeyApp, SZ_COMMANDLINE, &lpwszTemp)) { lpci->lpszCommandLine = lpwszTemp; } // Get the string value for the AppLauncherName. If this fails, // then we assume there is no launcher application. if(PRV_GetKeyStringValue(hkeyApp, SZ_LAUNCHER, &lpwszTemp)) { lpci->lpszAppLauncherName = lpwszTemp; } // Get the string value for the CurrentDir. If this fails, just // use the value returned by GetCurrentDirectory. if(!PRV_GetKeyStringValue(hkeyApp, SZ_CURRENTDIR, &lpwszTemp)) { // Get the size of the string dwSize = OS_GetCurrentDirectory(0, NULL); if(!dwSize) { dwError = GetLastError(); // WARNING: this last error value may not be correct in debug // since OS_GetCurrentDirectory may have called another function. DPF(0, "GetCurrentDirectory returned an error! dwError = %d", dwError); bReturn = FALSE; goto EXIT_GETCONNECTINFO; } lpwszTemp = DPMEM_ALLOC(dwSize * sizeof(WCHAR)); if(!lpwszTemp) { DPF_ERR("Unable to allocate temporary string for CurrentDirectory!"); bReturn = FALSE; goto EXIT_GETCONNECTINFO; } if(!OS_GetCurrentDirectory(dwSize, lpwszTemp)) { DPF_ERR("Unable to get CurrentDirectory!"); bReturn = FALSE; goto EXIT_GETCONNECTINFO; } } lpci->lpszCurrentDir = lpwszTemp; bReturn = TRUE; EXIT_GETCONNECTINFO: // Free any string we allocated if we failed if(!bReturn) PRV_FreeConnectInfo(lpci); // Close the Apps key if(hkeyApp) RegCloseKey(hkeyApp); return bReturn; } // PRV_GetConnectInfoFromRegistry #undef DPF_MODNAME #define DPF_MODNAME "PRV_CreateGameProcess" HRESULT PRV_CreateGameProcess(LPCONNECTINFO lpci, LPPROCESS_INFORMATION lppi) { STARTUPINFO si; HRESULT hr; LPWSTR lpwszPathAndFile = NULL; LPWSTR lpwszTemp = NULL; LPWSTR lpwszCommandLine = NULL; LPWSTR lpwszFileToRun; DWORD dwPathSize, dwFileSize, dwCurrentDirSize, dwPathAndFileSize, dwCommandLineSize, dwIPCSwitchSize, dwError; DPF(7, "Entering PRV_CreateGameProcess"); DPF(9, "Parameters: 0x%08x, 0x%08x", lpci, lppi); // Allocate enough memory for the Path, File, an additional backslash, // and the null termination // Note: the following two OS_StrLen calls with count the null terms // on the end of each string. Since this comes to two characters // (4 bytes), this will account for our null terminator and the // possible additional backslash after concatenation. Thus, the // size here is big enough for the concatenated string. dwPathSize = OS_StrLen(lpci->lpszPath); if(lpci->lpszAppLauncherName){ // when launching with an applauncher, we need a GUID. OS_CreateGuid(&lpci->guidIPC); lpci->guidIPC.Data1 |= 0x10000000; // make life easy by having fully byte significant first dword. lpwszFileToRun = lpci->lpszAppLauncherName; } else { lpwszFileToRun = lpci->lpszFile; } dwFileSize = OS_StrLen(lpwszFileToRun); dwPathAndFileSize = dwPathSize + dwFileSize; lpwszPathAndFile = DPMEM_ALLOC(dwPathAndFileSize * sizeof(WCHAR)); if(!lpwszPathAndFile) { DPF_ERR("Couldn't allocate memory for temporary string!"); hr = DPERR_OUTOFMEMORY; goto ERROR_CREATE_GAME_PROCESS; } // Concatenate the path & file together if(dwPathSize) { memcpy(lpwszPathAndFile, lpci->lpszPath, (dwPathSize * sizeof(WCHAR))); lpwszTemp = lpwszPathAndFile + dwPathSize - 1; // Only add a backslash if one doesn't already exists if(memcmp((lpwszTemp - 1), SZ_BACKSLASH, sizeof(WCHAR))) memcpy(lpwszTemp++, SZ_BACKSLASH, sizeof(WCHAR)); else // since we didn't add a backslash, the actual used // size is one WCHAR less than the full allocated size so // we need to reduce it so when we calculate the spot for // the command line we aren't after a NULL. dwPathAndFileSize--; } else lpwszTemp = lpwszPathAndFile; memcpy(lpwszTemp, lpwszFileToRun, (dwFileSize * sizeof(WCHAR))); // Allocate memory for temporary command line string // Note: Since the OS_StrLen function counts the null terminator, // we will be large enough to include the extra space when we // concatenate the two strings together. dwCommandLineSize = OS_StrLen(lpci->lpszCommandLine); if(lpci->lpszAppLauncherName){ // leave space for GUID on the command line dwIPCSwitchSize = sizeof(SZ_DP_IPC_GUID SZ_GUID_PROTOTYPE)/sizeof(WCHAR); } else { dwIPCSwitchSize = 0; } lpwszCommandLine = DPMEM_ALLOC(((dwCommandLineSize + dwPathAndFileSize+dwIPCSwitchSize) * sizeof(WCHAR))); if(!lpwszCommandLine) { // REVIEW!!!! -- We should fix these error paths post-DX3 DPF_ERR("Couldn't allocate memory for temporary command line string!"); hr = DPERR_OUTOFMEMORY; goto ERROR_CREATE_GAME_PROCESS; } // Concatenate the path & file string with the rest of the command line memcpy(lpwszCommandLine, lpwszPathAndFile, (dwPathAndFileSize * sizeof(WCHAR))); // Add the rest of the command line if it exists lpwszTemp = lpwszCommandLine + dwPathAndFileSize; if(dwCommandLineSize) { // First change the null terminator to a space lpwszTemp -= 1; memcpy(lpwszTemp++, SZ_SPACE, sizeof(WCHAR)); // Now copy in the command line memcpy(lpwszTemp, lpci->lpszCommandLine, (dwCommandLineSize * sizeof(WCHAR))); } if(dwIPCSwitchSize){ // add switch with a GUID on the command line for IPC when // application is started by a launcher lpwszTemp += dwCommandLineSize-1; // change NULL terminator to a space memcpy(lpwszTemp++, SZ_SPACE, sizeof(WCHAR)); // copy /dplay_ipc_guid: but skip the NULL memcpy(lpwszTemp, SZ_DP_IPC_GUID, sizeof(SZ_DP_IPC_GUID)-sizeof(WCHAR)); lpwszTemp+=(sizeof(SZ_DP_IPC_GUID)-sizeof(WCHAR))/sizeof(WCHAR); // Copy the GUID directly into the target StringFromGUID(&lpci->guidIPC,lpwszTemp,GUID_STRING_SIZE); } // Make sure the CurrentDirectory string doesn't have a trailing backslash // (This will cause CreateProcess to not use the right directory) dwCurrentDirSize = OS_StrLen(lpci->lpszCurrentDir); if(dwCurrentDirSize > 2) { lpwszTemp = lpci->lpszCurrentDir + dwCurrentDirSize - 2; if(!(memcmp((lpwszTemp), SZ_BACKSLASH, sizeof(WCHAR)))) memset(lpwszTemp, 0, sizeof(WCHAR)); } // Create the game's process in a suspended state memset(&si, 0, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); if(!OS_CreateProcess(lpwszPathAndFile, lpwszCommandLine, NULL, NULL, FALSE, (CREATE_SUSPENDED | CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE), NULL, lpci->lpszCurrentDir, &si, lppi)) { dwError = GetLastError(); // WARNING Last error produced here may not be correct since OS_CreateProcess // may call out to other functions (like DPF) before returning. DPF_ERR("Couldn't create game process"); DPF(0, "CreateProcess error = 0x%08x, (WARNING Error may not be correct)", dwError); hr = DPERR_CANTCREATEPROCESS; goto ERROR_CREATE_GAME_PROCESS; } hr = DP_OK; // Fall through ERROR_CREATE_GAME_PROCESS: if(lpwszPathAndFile) DPMEM_FREE(lpwszPathAndFile); if(lpwszCommandLine) DPMEM_FREE(lpwszCommandLine); return hr; } // PRV_CreateGameProcess #undef DPF_MODNAME #define DPF_MODNAME "PRV_IsAppInWaitMode" BOOL PRV_IsAppInWaitMode(DWORD dwProcessID) { DPLOBBYI_GAMENODE gn; LPDPLOBBYI_CONNCONTROL lpConnControl = NULL; SECURITY_ATTRIBUTES sa; WCHAR szName[MAX_MMFILENAME_LENGTH * sizeof(WCHAR)]; HRESULT hr; HANDLE hFile = NULL; HANDLE hMutex = NULL; BOOL bReturn = FALSE; DWORD dwError; DPF(7, "Entering PRV_IsAppInWaitMode"); DPF(9, "Parameters: %lu", dwProcessID); // Setup a temporary gamenode structure memset(&gn, 0, sizeof(DPLOBBYI_GAMENODE)); gn.dwFlags = GN_LOBBY_CLIENT; gn.dwGameProcessID = dwProcessID; // Get the name of the shared connection settings buffer hr = PRV_GetInternalName(&gn, TYPE_CONNECT_DATA_FILE, (LPWSTR)szName); if(FAILED(hr)) { DPF(5, "Unable to get name for shared conn settings buffer"); goto EXIT_ISAPPINWAITMODE; } // Map the file into our process hFile = OS_OpenFileMapping(FILE_MAP_ALL_ACCESS, TRUE, (LPWSTR)szName); if(!hFile) { dwError = GetLastError(); // WARNING: Error may not be correct since OpenFileMapping calls out to other functions before returning. DPF(5, "Couldn't get a handle to the shared local memory, dwError = %lu (ERROR MAY NOT BE CORRECT)", dwError); goto EXIT_ISAPPINWAITMODE; } // Map a View of the file lpConnControl = MapViewOfFile(hFile, FILE_MAP_ALL_ACCESS, 0, 0, 0); if(!lpConnControl) { dwError = GetLastError(); DPF(5, "Unable to get pointer to shared local memory, dwError = %lu", dwError); goto EXIT_ISAPPINWAITMODE; } // Get the name of the connection settings buffer mutex hr = PRV_GetInternalName(&gn, TYPE_CONNECT_DATA_MUTEX, (LPWSTR)szName); if(FAILED(hr)) { DPF(5, "Unable to get name for shared conn settings buffer mutex"); goto EXIT_ISAPPINWAITMODE; } // Set up the security attributes (so that our objects can // be inheritable) memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES)); sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = TRUE; // Open the Mutex hMutex = OS_CreateMutex(&sa, FALSE, (LPWSTR)szName); if(!hMutex) { DPF(5, "Unable to create shared conn settings buffer mutex"); goto EXIT_ISAPPINWAITMODE; } // Now grab the mutex and see if the app is in wait mode (and // it is not in pending mode) WaitForSingleObject(hMutex, INFINITE); if((lpConnControl->dwFlags & BC_WAIT_MODE) && !(lpConnControl->dwFlags & BC_PENDING_CONNECT)) { // Put the app in pending mode lpConnControl->dwFlags |= BC_PENDING_CONNECT; // Set the return code to true bReturn = TRUE; } // Release the mutex ReleaseMutex(hMutex); // Fall through EXIT_ISAPPINWAITMODE: if(lpConnControl) UnmapViewOfFile(lpConnControl); if(hFile) CloseHandle(hFile); if(hMutex) CloseHandle(hMutex); return bReturn; } // PRV_IsAppInWaitMode #undef DPF_MODNAME #define DPF_MODNAME "PRV_FindRunningAppNT" #define INITIAL_SIZE 51200 #define EXTEND_SIZE 25600 #define REGKEY_PERF _T("software\\microsoft\\windows nt\\currentversion\\perflib") #define REGSUBKEY_COUNTERS _T("Counters") #define PROCESS_COUNTER _T("process") #define PROCESSID_COUNTER _T("id process") #if 0 HRESULT PRV_FindRunningAppNT(LPCONNECTINFO lpci, LPPROCESS_INFORMATION lppi) { HANDLE hProcess = NULL; DWORD dwProcessID = 0; DWORD dwError; HRESULT hr = -1; DWORD rc; HKEY hKeyNames; DWORD dwType; DWORD dwSize; LPBYTE buf = NULL; TCHAR szSubKey[1024]; LANGID lid; LPTSTR p; LPTSTR p2; LPWSTR nameStr; PPERF_DATA_BLOCK pPerf; PPERF_OBJECT_TYPE pObj; PPERF_INSTANCE_DEFINITION pInst; PPERF_COUNTER_BLOCK pCounter; PPERF_COUNTER_DEFINITION pCounterDef; DWORD i; DWORD dwProcessIdTitle; DWORD dwProcessIdCounter; DWORD dwNumTasks; INT ccStrFind; INT ccStrMatch; // on Whistler, the string to match in the process table has changed from // being the name of the process without the ".exe" at the end to being // the name of the process followed by an '_' and the processid, so we // build that string too to compare and accept either when finding the app. WCHAR procString[64];//name concated with proc id for Whistler WCHAR *procStringBaseNameEnd; INT ccStrFindProcBased; // // Look for the list of counters. Always use the neutral // English version, regardless of the local language. We // are looking for some particular keys, and we are always // going to do our looking in English. We are not going // to show the user the counter names, so there is no need // to go find the corresponding name in the local language. // lid = MAKELANGID( LANG_ENGLISH, SUBLANG_NEUTRAL ); wsprintf( szSubKey, _T("%s\\%03x"), REGKEY_PERF, lid ); rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE, szSubKey, 0, KEY_READ, &hKeyNames ); if (rc != ERROR_SUCCESS) { goto exit; } // // get the buffer size for the counter names // rc = RegQueryValueEx( hKeyNames, REGSUBKEY_COUNTERS, NULL, &dwType, NULL, &dwSize ); if (rc != ERROR_SUCCESS) { goto exit; } // // allocate the counter names buffer // buf = (LPBYTE) DPMEM_ALLOC( dwSize ); if (buf == NULL) { goto exit; } memset( buf, 0, dwSize ); // // read the counter names from the registry // rc = RegQueryValueEx( hKeyNames, REGSUBKEY_COUNTERS, NULL, &dwType, buf, &dwSize ); if (rc != ERROR_SUCCESS) { goto exit; } // // now loop thru the counter names looking for the following counters: // // 1. "Process" process name // 2. "ID Process" process id // // the buffer contains multiple null terminated strings and then // finally null terminated at the end. the strings are in pairs of // counter number and counter name. // // convert the string to ansi because we can't use _wtoi p = (LPTSTR) buf; while (*p) { if (p > (LPTSTR) buf) { for( p2=p-2; _istdigit(*p2); p2--) ; } if (_tcsicmp(p, PROCESS_COUNTER) == 0) { // // look backwards for the counter number // for( p2=p-2; _istdigit(*p2); p2--) ; _tcscpy( szSubKey, p2+1 ); } else if (_tcsicmp(p, PROCESSID_COUNTER) == 0) { // // look backwards for the counter number // for( p2=p-2; _istdigit(*p2); p2--) ; dwProcessIdTitle = _ttoi( p2+1 ); } // // next string // p += (_tcslen(p) + 1); } // // free the counter names buffer // DPMEM_FREE( buf ); // // allocate the initial buffer for the performance data // dwSize = INITIAL_SIZE; buf = DPMEM_ALLOC( dwSize ); if (buf == NULL) { goto exit; } memset( buf, 0, dwSize ); while (TRUE) { rc = RegQueryValueEx( HKEY_PERFORMANCE_DATA, szSubKey, NULL, &dwType, buf, &dwSize ); pPerf = (PPERF_DATA_BLOCK) buf; // // check for success and valid perf data block signature // if ((rc == ERROR_SUCCESS) && (dwSize > 0) && (pPerf)->Signature[0] == (WCHAR)'P' && (pPerf)->Signature[1] == (WCHAR)'E' && (pPerf)->Signature[2] == (WCHAR)'R' && (pPerf)->Signature[3] == (WCHAR)'F' ) { break; } // // if buffer is not big enough, reallocate and try again // if (rc == ERROR_MORE_DATA) { dwSize += EXTEND_SIZE; buf = DPMEM_REALLOC( buf, dwSize ); memset( buf, 0, dwSize ); } else { goto exit; } } // // set the perf_object_type pointer // pObj = (PPERF_OBJECT_TYPE) ((DWORD_PTR)pPerf + pPerf->HeaderLength); // // loop thru the performance counter definition records looking // for the process id counter and then save its offset // pCounterDef = (PPERF_COUNTER_DEFINITION) ((DWORD_PTR)pObj + pObj->HeaderLength); for (i=0; i<(DWORD)pObj->NumCounters; i++) { if (pCounterDef->CounterNameTitleIndex == dwProcessIdTitle) { dwProcessIdCounter = pCounterDef->CounterOffset; break; } pCounterDef++; } dwNumTasks = (DWORD)pObj->NumInstances; pInst = (PPERF_INSTANCE_DEFINITION) ((DWORD_PTR)pObj + pObj->DefinitionLength); // // loop thru the performance instance data extracting each process name // and process id // ccStrFind=(WSTRLEN(lpci->lpszFile)-1)-4; // don't include .exe in compare if(ccStrFind > 15){ ccStrFind=15; } wcsncpy(procString, lpci->lpszFile, ccStrFind); procString[ccStrFind]=L'_'; procStringBaseNameEnd=&procString[ccStrFind+1]; for (i=0; iNameOffset); pCounter = (PPERF_COUNTER_BLOCK) ((DWORD_PTR)pInst + pInst->ByteLength); // Compare the process name with the executable name we are // looking for dwProcessID = *((LPDWORD) ((DWORD_PTR)pCounter + dwProcessIdCounter)); // tack processid onto end of base name to test on Whistler _itow(dwProcessID, procStringBaseNameEnd, 10); ccStrFindProcBased=WSTRLEN(procString)-1; ccStrMatch=WSTRLEN(nameStr)-1; // 1 for NULL if(ccStrMatch == 16){ // when it is 16, it included a trailing . so strip it. ccStrMatch--; } if((CSTR_EQUAL == OS_CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, nameStr, ccStrMatch, lpci->lpszFile, ccStrFind)) || (CSTR_EQUAL == OS_CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, nameStr, ccStrMatch, procString, ccStrFindProcBased)) ) { // See if the process is in wait mode if(PRV_IsAppInWaitMode(dwProcessID)) { hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessID); if(!hProcess) { dwError = GetLastError(); DPF_ERRVAL("Unable to open running process, dwError = %lu", dwError); goto exit; } else { // Save off the stuff we need lppi->dwProcessId = dwProcessID; lppi->hProcess = hProcess; hr = DP_OK; goto exit; } } // IsAppInWaitMode } // Are Filenames Equal // // next process // pInst = (PPERF_INSTANCE_DEFINITION) ((DWORD_PTR)pCounter + pCounter->ByteLength); } exit: if (buf) { DPMEM_FREE( buf ); } RegCloseKey( hKeyNames ); RegCloseKey( HKEY_PERFORMANCE_DATA ); return hr; } // PRV_FindRunningAppNT #endif // If you build with the UNICODE flag set, the headers will redefine PROCESSENTRY32 // to be PROCESSENTRY32W. Unfortunately, passing PROCESSENTRY32W to Win9x functions // will cause them to fail (because of the embedded Unicode string). // // Fix is to define our own PROCESSENTRY32A which is guaranteed to have an ANSI // embedded string which Win9x will always accept. typedef struct tagPROCESSENTRY32 PROCESSENTRY32A; typedef PROCESSENTRY32A *LPPROCESSENTRY32A; #undef DPF_MODNAME #define DPF_MODNAME "PRV_FindRunningAppWin9x" HRESULT PRV_FindRunningAppWin9x(LPCONNECTINFO lpci, LPPROCESS_INFORMATION lppi) { HANDLE hSnapShot = NULL; PROCESSENTRY32A procentry; BOOL bFlag; HRESULT hr = DPERR_UNAVAILABLE; LPBYTE lpbTemp = NULL; DWORD dwStrSize; LPWSTR lpszFile = NULL; HANDLE hProcess = NULL; DWORD dwError; HANDLE hInstLib = NULL; HRESULT hrTemp; // ToolHelp Function Pointers. HANDLE (WINAPI *lpfCreateToolhelp32Snapshot)(DWORD,DWORD); BOOL (WINAPI *lpfProcess32First)(HANDLE,LPPROCESSENTRY32A); BOOL (WINAPI *lpfProcess32Next)(HANDLE,LPPROCESSENTRY32A); DPF(7, "Entering PRV_FindRunningAppWin9x"); DPF(9, "Parameters: 0x%08x, 0x%08x", lpci, lppi); // Load library and get the procedures explicitly. We do // this so that we can load the entry points dynamically, // which allows us to build correctly under WinNT even // though the NT kernel32 doesn't have these entry points hInstLib = LoadLibraryA( "Kernel32.DLL" ); if(hInstLib == NULL) { DPF_ERR("Unable to load Kernel32.DLL"); goto EXIT_FIND_RUNNING_APP_WIN9X; } // Get procedure addresses. // We are linking to these functions of Kernel32 // explicitly, because otherwise a module using // this code would fail to load under Windows NT, // which does not have the Toolhelp32 // functions in the Kernel 32. lpfCreateToolhelp32Snapshot=(HANDLE(WINAPI *)(DWORD,DWORD)) GetProcAddress( hInstLib, "CreateToolhelp32Snapshot" ); lpfProcess32First=(BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32A)) GetProcAddress( hInstLib, "Process32First" ); lpfProcess32Next=(BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32A)) GetProcAddress( hInstLib, "Process32Next" ); if( lpfProcess32Next == NULL || lpfProcess32First == NULL || lpfCreateToolhelp32Snapshot == NULL ) { DPF_ERR("Unable to get needed entry points in PSAPI.DLL"); goto EXIT_FIND_RUNNING_APP_WIN9X; } // Get a handle to a Toolhelp snapshot of the systems processes. hSnapShot = lpfCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if(hSnapShot == INVALID_HANDLE_VALUE) { DPF_ERR("Unable to get snapshot of system processes"); goto EXIT_FIND_RUNNING_APP_WIN9X; } // Get the first process' information. procentry.dwSize = sizeof(PROCESSENTRY32A); bFlag = lpfProcess32First(hSnapShot, &procentry); // While there are processes, keep looping. while(bFlag) { // Walk the path and filename string (guaranteed to be ANSI) // looking for the final backslash (\). Once we find it, // convert the filename to Unicode so we can compare it. dwStrSize = lstrlenA((LPBYTE)procentry.szExeFile); lpbTemp = (LPBYTE)procentry.szExeFile + dwStrSize - 1; while(--dwStrSize) { if(lpbTemp[0] == '\\') { lpbTemp++; break; } else lpbTemp--; } hrTemp = GetWideStringFromAnsi(&lpszFile, (LPSTR)lpbTemp); if(FAILED(hrTemp)) { DPF_ERR("Failed making temporary copy of filename string"); goto EXIT_FIND_RUNNING_APP_WIN9X; } // Compare the process name with the executable name we are // looking for if(CSTR_EQUAL == OS_CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, lpszFile, -1, lpci->lpszFile, -1)) { // See if the process is in wait mode if(PRV_IsAppInWaitMode(procentry.th32ProcessID)) { // Open the process since Windows9x doesn't do // it for us. hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procentry.th32ProcessID); if(!hProcess) { dwError = GetLastError(); DPF_ERRVAL("Unable to open running process, dwError = %lu", dwError); bFlag = FALSE; } else { // Save off the stuff we need lppi->dwProcessId = procentry.th32ProcessID; lppi->hProcess = hProcess; hr = DP_OK; bFlag = FALSE; } } // IsAppInWaitMode } // Are Filenames Equal // Free our temporary string DPMEM_FREE(lpszFile); // If we haven't found it, and we didn't error, then move to // the next process if(bFlag) { // Move to the next process procentry.dwSize = sizeof(PROCESSENTRY32A); bFlag = lpfProcess32Next(hSnapShot, &procentry); } } EXIT_FIND_RUNNING_APP_WIN9X: if(hSnapShot) CloseHandle(hSnapShot); if(hInstLib) FreeLibrary(hInstLib) ; return hr; } // PRV_FindRunningAppWin9x #undef DPF_MODNAME #define DPF_MODNAME "PRV_FindRunningApp" HRESULT PRV_FindRunningApp(LPCONNECTINFO lpci, LPPROCESS_INFORMATION lppi) { OSVERSIONINFOA ver; HRESULT hr = DPERR_UNAVAILABLE; DPF(7, "Entering PRV_FindRunningApp"); DPF(9, "Parameters: 0x%08x, 0x%08x", lpci, lppi); ASSERT(lpci); ASSERT(lppi); // Clear our structure since it's on the stack memset(&ver, 0, sizeof(OSVERSIONINFOA)); ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); // Figure out which platform we are running on and // call the appropriate process enumerating function if(!GetVersionExA(&ver)) { DPF_ERR("Unable to determinte platform -- not looking for running app"); return DPERR_UNAVAILABLE; } switch(ver.dwPlatformId) { case VER_PLATFORM_WIN32_NT: case VER_PLATFORM_WIN32_WINDOWS: // Call the Win9x version of FindRunningApp hr = PRV_FindRunningAppWin9x(lpci, lppi); break; #if 0 case VER_PLATFORM_WIN32_NT: hr = PRV_FindRunningAppNT(lpci, lppi); break; #endif default: DPF_ERR("Unable to determinte platform -- not looking for running app"); hr = DPERR_UNAVAILABLE; break; } return hr; } // PRV_FindRunningApp #undef DPF_MODNAME #define DPF_MODNAME "DPL_RunApplication" HRESULT DPLAPI DPL_RunApplication(LPDIRECTPLAYLOBBY lpDPL, DWORD dwFlags, LPDWORD lpdwGameID, LPDPLCONNECTION lpConn, HANDLE hReceiveEvent) { LPDPLOBBYI_DPLOBJECT this; HRESULT hr; PROCESS_INFORMATION pi; LPDPLOBBYI_GAMENODE lpgn = NULL; CONNECTINFO ci; HANDLE hDupReceiveEvent = NULL; HANDLE hReceiveThread = NULL; HANDLE hTerminateThread = NULL; HANDLE hKillReceiveThreadEvent = NULL; HANDLE hKillTermThreadEvent = NULL; DWORD dwThreadID; BOOL bCreatedProcess = FALSE; GUID *lpguidIPC = NULL; DPF(7, "Entering DPL_RunApplication"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x", lpDPL, dwFlags, lpdwGameID, lpConn, hReceiveEvent); ENTER_DPLOBBY(); TRY { if( !VALID_DPLOBBY_INTERFACE( lpDPL )) { LEAVE_DPLOBBY(); return DPERR_INVALIDINTERFACE; } this = DPLOBJECT_FROM_INTERFACE(lpDPL); if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); return DPERR_INVALIDOBJECT; } // Validate the DPLCONNECTION structure and it's members hr = PRV_ValidateDPLCONNECTION(lpConn, FALSE); if(FAILED(hr)) { LEAVE_DPLOBBY(); return hr; } if( !VALID_DWORD_PTR( lpdwGameID ) ) { LEAVE_DPLOBBY(); return DPERR_INVALIDPARAMS; } // We haven't defined any flags for this release if( (dwFlags) ) { LEAVE_DPLOBBY(); return DPERR_INVALIDFLAGS; } // Validate the handle if(hReceiveEvent) { if(!OS_IsValidHandle(hReceiveEvent)) { LEAVE_DPLOBBY(); DPF_ERR("Invalid hReceiveEvent handle"); return DPERR_INVALIDPARAMS; } } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLOBBY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // Clear the CONNECTINFO structure since it's on the stack memset(&ci, 0, sizeof(CONNECTINFO)); // Get the guid of the game we want to launch if(lpConn && lpConn->lpSessionDesc) ci.guidApplication = lpConn->lpSessionDesc->guidApplication; else { LEAVE_DPLOBBY(); return DPERR_UNKNOWNAPPLICATION; } // Get the information out the registry based on the GUID if(!PRV_GetConnectInfoFromRegistry(&ci)) { LEAVE_DPLOBBY(); return DPERR_UNKNOWNAPPLICATION; } // Clear the PROCESS_INFORMATION structure since it's on the stack memset(&pi, 0, sizeof(PROCESS_INFORMATION)); // Look to see if this game is already running AND is in wait mode // waiting for new connection settings. If it is, we want to // send the connection settings to it. hr = PRV_FindRunningApp(&ci, &pi); if(FAILED(hr)) { // It isn't waiting, so create the game's process & suspend it hr = PRV_CreateGameProcess(&ci, &pi); if(FAILED(hr)) { LEAVE_DPLOBBY(); return hr; } if(!(IsEqualGUID(&ci.guidIPC,&GUID_NULL))){ lpguidIPC=&ci.guidIPC; } // Set our created flag bCreatedProcess = TRUE; } // Create a game node hr = PRV_AddNewGameNode(this, &lpgn, pi.dwProcessId, pi.hProcess, TRUE, lpguidIPC); if(FAILED(hr)) { DPF_ERR("Couldn't create new game node"); goto RUN_APP_ERROR_EXIT; } // If the ConnectionSettings are from a StartSession message (lobby launched), // we need to set the flag if(lpConn->lpSessionDesc->dwReserved1) { // Set the flag that says we were lobby client launched lpgn->dwFlags |= GN_CLIENT_LAUNCHED; } // Write the connection settings in the shared memory buffer hr = PRV_WriteConnectionSettings(lpgn, lpConn, TRUE); if(FAILED(hr)) { DPF_ERR("Unable to write the connection settings!"); goto RUN_APP_ERROR_EXIT; } // Send the app a message that the new connection settings are available // but only if we've sent the settings to a running app if(!bCreatedProcess) PRV_SendStandardSystemMessage(lpDPL, DPLSYS_NEWCONNECTIONSETTINGS, pi.dwProcessId); // Duplicate the Receive Event handle to use a signal to the // lobby client that the game has sent game settings to it. if(hReceiveEvent) { hDupReceiveEvent = PRV_DuplicateHandle(hReceiveEvent); if(!hDupReceiveEvent) { DPF_ERR("Unable to duplicate ReceiveEvent handle"); hr = DPERR_OUTOFMEMORY; goto RUN_APP_ERROR_EXIT; } } lpgn->hDupReceiveEvent = hDupReceiveEvent; // Create the kill thread event for the monitor thread hKillTermThreadEvent = OS_CreateEvent(NULL, FALSE, FALSE, NULL); if(!hKillTermThreadEvent) { DPF_ERR("Unable to create kill thread event"); hr = DPERR_OUTOFMEMORY; goto RUN_APP_ERROR_EXIT; } lpgn->hKillTermThreadEvent = hKillTermThreadEvent; // Spawn off a terminate monitor thread hTerminateThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) PRV_ClientTerminateNotification, lpgn, 0, &dwThreadID); if(!hTerminateThread) { DPF_ERR("Unable to create Terminate Monitor Thread!"); hr = DPERR_OUTOFMEMORY; goto RUN_APP_ERROR_EXIT; } lpgn->hTerminateThread = hTerminateThread; // Resume the game's process & let it run, then // free the thread handle since we won't use it anymore if(bCreatedProcess) { ResumeThread(pi.hThread); CloseHandle(pi.hThread); } // Set the output pointer *lpdwGameID = pi.dwProcessId; // Free the strings in the connect info struct PRV_FreeConnectInfo(&ci); LEAVE_DPLOBBY(); return DP_OK; RUN_APP_ERROR_EXIT: if(pi.hThread && bCreatedProcess) CloseHandle(pi.hThread); if(bCreatedProcess && pi.hProcess) TerminateProcess(pi.hProcess, 0L); if(lpgn) PRV_RemoveGameNodeFromList(lpgn); PRV_FreeConnectInfo(&ci); LEAVE_DPLOBBY(); return hr; } // DPL_RunApplication