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.
1361 lines
38 KiB
1361 lines
38 KiB
/*==========================================================================
|
|
*
|
|
* 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 <tchar.h>
|
|
#include <tlhelp32.h>
|
|
#include <winperf.h>
|
|
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
// 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; i<dwNumTasks; i++) {
|
|
//
|
|
// pointer to the process name
|
|
//
|
|
|
|
nameStr = (LPWSTR) ((DWORD_PTR)pInst + pInst->NameOffset);
|
|
|
|
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
|
|
|
|
|