1793 lines
58 KiB
C++
1793 lines
58 KiB
C++
|
/*==========================================================================
|
||
|
*
|
||
|
* Copyright (C) 2000 Microsoft Corporation. All Rights Reserved.
|
||
|
*
|
||
|
* File: DNLClient.cpp
|
||
|
* Content: DirectNet Lobby Client Functions
|
||
|
*@@BEGIN_MSINTERNAL
|
||
|
* History:
|
||
|
* Date By Reason
|
||
|
* ==== == ======
|
||
|
* 02/21/00 mjn Created
|
||
|
* 03/22/2000 jtk Changed interface names
|
||
|
* 04/05/2000 jtk Changed GetValueSize to GetValueLength
|
||
|
* 04/13/00 rmt First pass param validation
|
||
|
* 04/25/2000 rmt Bug #s 33138, 33145, 33150
|
||
|
* 04/26/00 mjn Removed dwTimeOut from Send() API call
|
||
|
* 05/01/2000 rmt Bug #33678
|
||
|
* 05/03/00 rmt Bug #33879 -- Status messsage missing from field
|
||
|
* 05/30/00 rmt Bug #35618 -- ConnectApp with ShortTimeout returns DPN_OK
|
||
|
* 06/07/00 rmt Bug #36452 -- Calling ConnectApplication twice could result in disconnection
|
||
|
* 06/15/00 rmt Bug #33617 - Must provide method for providing automatic launch of DirectPlay instances
|
||
|
* 07/06/00 rmt Updated for new registry parameters
|
||
|
* 07/08/2000 rmt Bug #38725 - Need to provide method to detect if app was lobby launched
|
||
|
* rmt Bug #38757 - Callback messages for connections may return AFTER WaitForConnection returns
|
||
|
* rmt Bug #38755 - No way to specify player name in Connection Settings
|
||
|
* rmt Bug #38758 - DPLOBBY8.H has incorrect comments
|
||
|
* rmt Bug #38783 - pvUserApplicationContext is only partially implemented
|
||
|
* rmt Added DPLHANDLE_ALLCONNECTIONS and dwFlags (reserved field to couple of funcs).
|
||
|
* 07/14/2000 rmt Bug #39257 - LobbyClient::ReleaseApp returns E_OUTOFMEMORY when called when no one connected
|
||
|
* 07/21/2000 rmt Bug #39578 - LobbyClient sample errors and quits -- memory corruption due to length vs. size problem
|
||
|
* 08/05/2000 RichGr IA64: Use %p format specifier in DPFs for 32/64-bit pointers and handles.
|
||
|
* 12/15/2000 rmt Bug #48445 - Specifying empty launcher name results in error
|
||
|
* 04/19/2001 simonpow Bug #369842 - Altered CreateProcess calls to take app name and cmd
|
||
|
* line as 2 separate arguments rather than one.
|
||
|
* 06/16/2001 rodtoll WINBUG #416983 - RC1: World has full control to HKLM\Software\Microsoft\DirectPlay\Applications on Personal
|
||
|
* Implementing mirror of keys into HKCU. Algorithm is now:
|
||
|
* - Read of entries tries HKCU first, then HKLM
|
||
|
* - Enum of entires is combination of HKCU and HKLM entries with duplicates removed. HKCU takes priority.
|
||
|
* - Write of entries is HKLM and HKCU. (HKLM may fail, but is ignored).
|
||
|
*@@END_MSINTERNAL
|
||
|
*
|
||
|
***************************************************************************/
|
||
|
|
||
|
#include "dnlobbyi.h"
|
||
|
|
||
|
|
||
|
//**********************************************************************
|
||
|
// Constant definitions
|
||
|
//**********************************************************************
|
||
|
|
||
|
//**********************************************************************
|
||
|
// Macro definitions
|
||
|
//**********************************************************************
|
||
|
|
||
|
//**********************************************************************
|
||
|
// Structure definitions
|
||
|
//**********************************************************************
|
||
|
|
||
|
//**********************************************************************
|
||
|
// Variable definitions
|
||
|
//**********************************************************************
|
||
|
|
||
|
typedef STDMETHODIMP ClientQueryInterface(IDirectPlay8LobbyClient *pInterface,REFIID ridd,PVOID *ppvObj);
|
||
|
typedef STDMETHODIMP_(ULONG) ClientAddRef(IDirectPlay8LobbyClient *pInterface);
|
||
|
typedef STDMETHODIMP_(ULONG) ClientRelease(IDirectPlay8LobbyClient *pInterface);
|
||
|
typedef STDMETHODIMP ClientRegisterMessageHandler(IDirectPlay8LobbyClient *pInterface,const PVOID pvUserContext,const PFNDPNMESSAGEHANDLER pfn,const DWORD dwFlags);
|
||
|
typedef STDMETHODIMP ClientSend(IDirectPlay8LobbyClient *pInterface,const DPNHANDLE hTarget,BYTE *const pBuffer,const DWORD pBufferSize,const DWORD dwFlags);
|
||
|
typedef STDMETHODIMP ClientClose(IDirectPlay8LobbyClient *pInterface,const DWORD dwFlags);
|
||
|
typedef STDMETHODIMP ClientGetConnectionSettings(IDirectPlay8LobbyClient *pInterface, const DPNHANDLE hLobbyClient, DPL_CONNECTION_SETTINGS * const pdplSessionInfo, DWORD *pdwInfoSize, const DWORD dwFlags );
|
||
|
typedef STDMETHODIMP ClientSetConnectionSettings(IDirectPlay8LobbyClient *pInterface, const DPNHANDLE hTarget, const DPL_CONNECTION_SETTINGS * const pdplSessionInfo, const DWORD dwFlags );
|
||
|
|
||
|
IDirectPlay8LobbyClientVtbl DPL_Lobby8ClientVtbl =
|
||
|
{
|
||
|
(ClientQueryInterface*) DPL_QueryInterface,
|
||
|
(ClientAddRef*) DPL_AddRef,
|
||
|
(ClientRelease*) DPL_Release,
|
||
|
(ClientRegisterMessageHandler*) DPL_RegisterMessageHandlerClient,
|
||
|
DPL_EnumLocalPrograms,
|
||
|
DPL_ConnectApplication,
|
||
|
(ClientSend*) DPL_Send,
|
||
|
DPL_ReleaseApplication,
|
||
|
(ClientClose*) DPL_Close,
|
||
|
(ClientGetConnectionSettings*) DPL_GetConnectionSettings,
|
||
|
(ClientSetConnectionSettings*) DPL_SetConnectionSettings
|
||
|
};
|
||
|
|
||
|
|
||
|
//**********************************************************************
|
||
|
// Function prototypes
|
||
|
//**********************************************************************
|
||
|
|
||
|
//**********************************************************************
|
||
|
// Function definitions
|
||
|
//**********************************************************************
|
||
|
|
||
|
#define DPL_ENUM_APPGUID_BUFFER_INITIAL 8
|
||
|
#define DPL_ENUM_APPGUID_BUFFER_GROWBY 4
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "DPL_EnumLocalPrograms"
|
||
|
|
||
|
STDMETHODIMP DPL_EnumLocalPrograms(IDirectPlay8LobbyClient *pInterface,
|
||
|
GUID *const pGuidApplication,
|
||
|
BYTE *const pEnumData,
|
||
|
DWORD *const pdwEnumDataSize,
|
||
|
DWORD *const pdwEnumDataItems,
|
||
|
const DWORD dwFlags )
|
||
|
{
|
||
|
HRESULT hResultCode;
|
||
|
CMessageQueue MessageQueue;
|
||
|
CPackedBuffer PackedBuffer;
|
||
|
CRegistry RegistryEntry;
|
||
|
CRegistry SubEntry;
|
||
|
DWORD dwSizeRequired;
|
||
|
DWORD dwMaxKeyLen;
|
||
|
PWSTR pwszKeyName = NULL;
|
||
|
|
||
|
// Application name variables
|
||
|
PWSTR pwszApplicationName = NULL;
|
||
|
DWORD dwMaxApplicationNameLength; // Includes null terminator
|
||
|
DWORD dwApplicationNameLength; // Includes null terminator
|
||
|
|
||
|
// Executable name variables
|
||
|
PWSTR pwszExecutableFilename = NULL;
|
||
|
DWORD dwMaxExecutableFilenameLength; // Includes null terminator
|
||
|
DWORD dwExecutableFilenameLength; // Includes null terminator
|
||
|
|
||
|
DWORD *pdwPID;
|
||
|
DWORD dwMaxPID;
|
||
|
DWORD dwNumPID;
|
||
|
DWORD dwEnumIndex;
|
||
|
DWORD dwEnumCount;
|
||
|
DWORD dwKeyLen;
|
||
|
DWORD dw;
|
||
|
DPL_APPLICATION_INFO dplAppInfo;
|
||
|
DIRECTPLAYLOBBYOBJECT *pdpLobbyObject;
|
||
|
GUID *pAppLoadedList = NULL; // List of GUIDs of app's we've enumerated
|
||
|
DWORD dwSizeAppLoadedList = 0; // size of list pAppLoadedList
|
||
|
DWORD dwLengthAppLoadedList = 0; // # of elements in list
|
||
|
|
||
|
HKEY hkCurrentBranch = HKEY_LOCAL_MACHINE;
|
||
|
|
||
|
DPFX(DPFPREP, 3,"Parameters: pInterface [0x%p], pGuidApplication [0x%p], pEnumData [0x%p], pdwEnumDataSize [0x%p], pdwEnumDataItems [0x%p], dwFlags [0x%lx]",
|
||
|
pInterface,pGuidApplication,pEnumData,pdwEnumDataSize,pdwEnumDataItems,dwFlags);
|
||
|
|
||
|
TRY
|
||
|
{
|
||
|
pdpLobbyObject = static_cast<DIRECTPLAYLOBBYOBJECT*>(GET_OBJECT_FROM_INTERFACE(pInterface));
|
||
|
|
||
|
if( pdpLobbyObject->dwFlags & DPL_OBJECT_FLAG_PARAMVALIDATION )
|
||
|
{
|
||
|
if( FAILED( hResultCode = DPL_ValidateEnumLocalPrograms( pInterface, pGuidApplication, pEnumData, pdwEnumDataSize, pdwEnumDataItems, dwFlags ) ) )
|
||
|
{
|
||
|
DPFX(DPFPREP, 0, "Error validating enum local programs params hr=[0x%lx]", hResultCode );
|
||
|
DPF_RETURN( hResultCode );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Ensure we've been initialized
|
||
|
if (pdpLobbyObject->pReceiveQueue == NULL)
|
||
|
{
|
||
|
DPFERR("Not initialized");
|
||
|
DPF_RETURN(DPNERR_UNINITIALIZED);
|
||
|
}
|
||
|
}
|
||
|
EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
||
|
{
|
||
|
DPFERR("Invalid object" );
|
||
|
DPF_RETURN(DPNERR_INVALIDOBJECT);
|
||
|
}
|
||
|
|
||
|
dwSizeRequired = *pdwEnumDataSize;
|
||
|
PackedBuffer.Initialize(pEnumData,dwSizeRequired);
|
||
|
pwszApplicationName = NULL;
|
||
|
pwszExecutableFilename = NULL;
|
||
|
pdwPID = NULL;
|
||
|
dwMaxPID = 0;
|
||
|
|
||
|
dwLengthAppLoadedList = 0;
|
||
|
dwSizeAppLoadedList = DPL_ENUM_APPGUID_BUFFER_INITIAL;
|
||
|
pAppLoadedList = static_cast<GUID*>(DNMalloc(sizeof(GUID)*dwSizeAppLoadedList));
|
||
|
|
||
|
if( !pAppLoadedList )
|
||
|
{
|
||
|
DPFERR("Failed allocating memory" );
|
||
|
hResultCode = DPNERR_OUTOFMEMORY;
|
||
|
goto EXIT_DPL_EnumLocalPrograms;
|
||
|
}
|
||
|
|
||
|
dwEnumCount = 0;
|
||
|
|
||
|
for( DWORD dwIndex = 0; dwIndex < 2; dwIndex++ )
|
||
|
{
|
||
|
if( dwIndex == 0 )
|
||
|
{
|
||
|
hkCurrentBranch = HKEY_CURRENT_USER;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hkCurrentBranch = HKEY_LOCAL_MACHINE;
|
||
|
}
|
||
|
|
||
|
if (!RegistryEntry.Open(hkCurrentBranch,DPL_REG_LOCAL_APPL_SUBKEY,TRUE,FALSE,TRUE,DPL_REGISTRY_READ_ACCESS))
|
||
|
{
|
||
|
DPFX(DPFPREP,1,"On pass %i could not find app key", dwIndex);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Set up to enumerate
|
||
|
if (!RegistryEntry.GetMaxKeyLen(dwMaxKeyLen))
|
||
|
{
|
||
|
DPFERR("RegistryEntry.GetMaxKeyLen() failed");
|
||
|
hResultCode = DPNERR_GENERIC;
|
||
|
goto EXIT_DPL_EnumLocalPrograms;
|
||
|
}
|
||
|
dwMaxKeyLen++; // Null terminator
|
||
|
DPFX(DPFPREP, 7,"dwMaxKeyLen = %ld",dwMaxKeyLen);
|
||
|
if ((pwszKeyName = static_cast<WCHAR*>(DNMalloc(dwMaxKeyLen*sizeof(WCHAR)))) == NULL)
|
||
|
{
|
||
|
DPFERR("DNMalloc() failed");
|
||
|
hResultCode = DPNERR_OUTOFMEMORY;
|
||
|
goto EXIT_DPL_EnumLocalPrograms;
|
||
|
}
|
||
|
dwMaxApplicationNameLength = dwMaxKeyLen * sizeof(WCHAR);
|
||
|
dwMaxExecutableFilenameLength = dwMaxApplicationNameLength;
|
||
|
|
||
|
if ((pwszApplicationName = static_cast<WCHAR*>(DNMalloc(dwMaxApplicationNameLength*sizeof(WCHAR)))) == NULL) // Seed Application name size
|
||
|
{
|
||
|
DPFERR("DNMalloc() failed");
|
||
|
hResultCode = DPNERR_OUTOFMEMORY;
|
||
|
goto EXIT_DPL_EnumLocalPrograms;
|
||
|
}
|
||
|
if ((pwszExecutableFilename = static_cast<WCHAR*>(DNMalloc(dwMaxExecutableFilenameLength*sizeof(WCHAR)))) == NULL)
|
||
|
{
|
||
|
DPFERR("DNMalloc() failed");
|
||
|
hResultCode = DPNERR_OUTOFMEMORY;
|
||
|
goto EXIT_DPL_EnumLocalPrograms;
|
||
|
}
|
||
|
dwEnumIndex = 0;
|
||
|
dwKeyLen = dwMaxKeyLen;
|
||
|
|
||
|
// Enumerate !
|
||
|
while (RegistryEntry.EnumKeys(pwszKeyName,&dwKeyLen,dwEnumIndex))
|
||
|
{
|
||
|
DPFX(DPFPREP, 7,"%ld - %S (%ld)",dwEnumIndex,pwszKeyName,dwKeyLen);
|
||
|
|
||
|
// Get Application name and GUID from each sub key
|
||
|
if (!SubEntry.Open(RegistryEntry,pwszKeyName,TRUE,FALSE))
|
||
|
{
|
||
|
DPFX(DPFPREP, 7,"skipping %S",pwszKeyName);
|
||
|
goto LOOP_END;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Minara, double-check size vs. length for names
|
||
|
//
|
||
|
if (!SubEntry.GetValueLength(DPL_REG_KEYNAME_APPLICATIONNAME,&dwApplicationNameLength))
|
||
|
{
|
||
|
DPFX(DPFPREP, 7,"Could not get ApplicationName size. Skipping [%S]",pwszKeyName);
|
||
|
goto LOOP_END;
|
||
|
}
|
||
|
|
||
|
// To include null terminator
|
||
|
dwApplicationNameLength++;
|
||
|
|
||
|
if (dwApplicationNameLength > dwMaxApplicationNameLength)
|
||
|
{
|
||
|
// grow buffer (taking into account that the reg functions always return WCHAR) and try again
|
||
|
DPFX(DPFPREP, 7,"Need to grow pwszApplicationName from %ld to %ld",dwMaxApplicationNameLength,dwApplicationNameLength);
|
||
|
if (pwszApplicationName != NULL)
|
||
|
{
|
||
|
DNFree(pwszApplicationName);
|
||
|
pwszApplicationName = NULL;
|
||
|
}
|
||
|
if ((pwszApplicationName = static_cast<WCHAR*>(DNMalloc(dwApplicationNameLength*sizeof(WCHAR)))) == NULL)
|
||
|
{
|
||
|
DPFERR("DNMalloc() failed");
|
||
|
hResultCode = DPNERR_OUTOFMEMORY;
|
||
|
goto EXIT_DPL_EnumLocalPrograms;
|
||
|
}
|
||
|
dwMaxApplicationNameLength = dwApplicationNameLength;
|
||
|
}
|
||
|
|
||
|
if (!SubEntry.ReadString(DPL_REG_KEYNAME_APPLICATIONNAME,pwszApplicationName,&dwApplicationNameLength))
|
||
|
{
|
||
|
DPFX(DPFPREP, 7,"Could not read ApplicationName. Skipping [%S]",pwszKeyName);
|
||
|
goto LOOP_END;
|
||
|
}
|
||
|
|
||
|
DPFX(DPFPREP, 7,"ApplicationName = %S (%ld WCHARs)",pwszApplicationName,dwApplicationNameLength);
|
||
|
|
||
|
if (!SubEntry.ReadGUID(DPL_REG_KEYNAME_GUID,dplAppInfo.guidApplication))
|
||
|
{
|
||
|
DPFERR("SubEntry.ReadGUID failed - skipping entry");
|
||
|
goto LOOP_END;
|
||
|
}
|
||
|
|
||
|
for( DWORD dwGuidSearchIndex = 0; dwGuidSearchIndex < dwLengthAppLoadedList; dwGuidSearchIndex++ )
|
||
|
{
|
||
|
if( pAppLoadedList[dwGuidSearchIndex] == dplAppInfo.guidApplication )
|
||
|
{
|
||
|
DPFX(DPFPREP, 1, "Ignoring local machine entry for current user version of entry [%S]", pwszApplicationName );
|
||
|
goto LOOP_END;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((pGuidApplication == NULL) || (*pGuidApplication == dplAppInfo.guidApplication))
|
||
|
{
|
||
|
// Get process count - need executable filename
|
||
|
|
||
|
//
|
||
|
// Minara, check size vs. length
|
||
|
//
|
||
|
if (!SubEntry.GetValueLength(DPL_REG_KEYNAME_EXECUTABLEFILENAME,&dwExecutableFilenameLength))
|
||
|
{
|
||
|
DPFX(DPFPREP, 7,"Could not get ExecutableFilename size. Skipping [%S]",pwszKeyName);
|
||
|
goto LOOP_END;
|
||
|
}
|
||
|
|
||
|
// So we include null terminator
|
||
|
dwExecutableFilenameLength++;
|
||
|
|
||
|
if (dwExecutableFilenameLength > dwMaxExecutableFilenameLength)
|
||
|
{
|
||
|
// grow buffer (noting that all strings from the registry are WCHAR) and try again
|
||
|
DPFX(DPFPREP, 7,"Need to grow pwszExecutableFilename from %ld to %ld",dwMaxExecutableFilenameLength,dwExecutableFilenameLength);
|
||
|
if (pwszExecutableFilename != NULL)
|
||
|
{
|
||
|
DNFree(pwszExecutableFilename);
|
||
|
pwszExecutableFilename = NULL;
|
||
|
}
|
||
|
if ((pwszExecutableFilename = static_cast<WCHAR*>(DNMalloc(dwExecutableFilenameLength*sizeof(WCHAR)))) == NULL)
|
||
|
{
|
||
|
DPFERR("DNMalloc() failed");
|
||
|
hResultCode = DPNERR_OUTOFMEMORY;
|
||
|
goto EXIT_DPL_EnumLocalPrograms;
|
||
|
}
|
||
|
dwMaxExecutableFilenameLength = dwExecutableFilenameLength;
|
||
|
}
|
||
|
if (!SubEntry.ReadString(DPL_REG_KEYNAME_EXECUTABLEFILENAME,pwszExecutableFilename,&dwExecutableFilenameLength))
|
||
|
{
|
||
|
DPFX(DPFPREP, 7,"Could not read ExecutableFilename. Skipping [%S]",pwszKeyName);
|
||
|
goto LOOP_END;
|
||
|
}
|
||
|
DPFX(DPFPREP, 7,"ExecutableFilename [%S]",pwszExecutableFilename);
|
||
|
|
||
|
// Count running apps
|
||
|
dwNumPID = dwMaxPID;
|
||
|
while ((hResultCode = DPLGetProcessList(pwszExecutableFilename,pdwPID,&dwNumPID,
|
||
|
pdpLobbyObject->bIsUnicodePlatform)) == DPNERR_BUFFERTOOSMALL)
|
||
|
{
|
||
|
if (pdwPID)
|
||
|
{
|
||
|
DNFree(pdwPID);
|
||
|
pdwPID = NULL;
|
||
|
}
|
||
|
dwMaxPID = dwNumPID;
|
||
|
if ((pdwPID = static_cast<DWORD*>(DNMalloc(dwNumPID*sizeof(DWORD)))) == NULL)
|
||
|
{
|
||
|
DPFERR("DNMalloc() failed");
|
||
|
hResultCode = DPNERR_OUTOFMEMORY;
|
||
|
goto EXIT_DPL_EnumLocalPrograms;
|
||
|
}
|
||
|
}
|
||
|
if (hResultCode != DPN_OK)
|
||
|
{
|
||
|
DPFERR("DPLGetProcessList() failed");
|
||
|
DisplayDNError(0,hResultCode);
|
||
|
hResultCode = DPNERR_GENERIC;
|
||
|
goto EXIT_DPL_EnumLocalPrograms;
|
||
|
}
|
||
|
|
||
|
// Count waiting apps
|
||
|
dplAppInfo.dwNumWaiting = 0;
|
||
|
for (dw = 0 ; dw < dwNumPID ; dw++)
|
||
|
{
|
||
|
if ((hResultCode = MessageQueue.Open( pdwPID[dw],
|
||
|
DPL_MSGQ_OBJECT_SUFFIX_APPLICATION,
|
||
|
DPL_MSGQ_SIZE,
|
||
|
DPL_MSGQ_OPEN_FLAG_NO_CREATE, INFINITE)) == DPN_OK)
|
||
|
{
|
||
|
if (MessageQueue.IsAvailable())
|
||
|
{
|
||
|
dplAppInfo.dwNumWaiting++;
|
||
|
}
|
||
|
MessageQueue.Close();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
hResultCode = PackedBuffer.AddWCHARStringToBack(pwszApplicationName);
|
||
|
dplAppInfo.pwszApplicationName = (PWSTR)(PackedBuffer.GetTailAddress());
|
||
|
dplAppInfo.dwFlags = 0;
|
||
|
dplAppInfo.dwNumRunning = dwNumPID;
|
||
|
hResultCode = PackedBuffer.AddToFront(&dplAppInfo,sizeof(DPL_APPLICATION_INFO));
|
||
|
|
||
|
if( dwLengthAppLoadedList+1 > dwSizeAppLoadedList )
|
||
|
{
|
||
|
GUID *pTmpArray = NULL;
|
||
|
|
||
|
pTmpArray = static_cast<GUID*>(DNMalloc(sizeof(GUID)*(dwSizeAppLoadedList+DPL_ENUM_APPGUID_BUFFER_GROWBY)));
|
||
|
|
||
|
if( !pTmpArray )
|
||
|
{
|
||
|
DPFERR("DNMalloc() failed");
|
||
|
hResultCode = DPNERR_OUTOFMEMORY;
|
||
|
goto EXIT_DPL_EnumLocalPrograms;
|
||
|
}
|
||
|
|
||
|
memcpy( pTmpArray, pAppLoadedList, sizeof(GUID)*dwLengthAppLoadedList);
|
||
|
|
||
|
dwSizeAppLoadedList += DPL_ENUM_APPGUID_BUFFER_GROWBY;
|
||
|
|
||
|
DNFree(pAppLoadedList);
|
||
|
pAppLoadedList = pTmpArray;
|
||
|
}
|
||
|
|
||
|
pAppLoadedList[dwLengthAppLoadedList] = dplAppInfo.guidApplication;
|
||
|
dwLengthAppLoadedList++;
|
||
|
|
||
|
dwEnumCount++;
|
||
|
}
|
||
|
|
||
|
LOOP_END:
|
||
|
SubEntry.Close();
|
||
|
dwEnumIndex++;
|
||
|
dwKeyLen = dwMaxKeyLen;
|
||
|
}
|
||
|
|
||
|
RegistryEntry.Close();
|
||
|
|
||
|
if( pwszKeyName )
|
||
|
{
|
||
|
DNFree(pwszKeyName);
|
||
|
pwszKeyName= NULL;
|
||
|
}
|
||
|
|
||
|
if( pwszApplicationName )
|
||
|
{
|
||
|
DNFree(pwszApplicationName);
|
||
|
pwszApplicationName = NULL;
|
||
|
}
|
||
|
|
||
|
if( pwszExecutableFilename )
|
||
|
{
|
||
|
DNFree(pwszExecutableFilename);
|
||
|
pwszExecutableFilename = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
dwSizeRequired = PackedBuffer.GetSizeRequired();
|
||
|
if (dwSizeRequired > *pdwEnumDataSize)
|
||
|
{
|
||
|
DPFX(DPFPREP, 7,"Buffer too small");
|
||
|
*pdwEnumDataSize = dwSizeRequired;
|
||
|
hResultCode = DPNERR_BUFFERTOOSMALL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*pdwEnumDataItems = dwEnumCount;
|
||
|
}
|
||
|
|
||
|
if( pGuidApplication != NULL && dwEnumCount == 0 )
|
||
|
{
|
||
|
DPFX(DPFPREP, 0, "Specified application was not registered" );
|
||
|
hResultCode = DPNERR_DOESNOTEXIST;
|
||
|
}
|
||
|
|
||
|
EXIT_DPL_EnumLocalPrograms:
|
||
|
|
||
|
if (pwszKeyName != NULL)
|
||
|
DNFree(pwszKeyName);
|
||
|
if (pwszApplicationName != NULL)
|
||
|
DNFree(pwszApplicationName);
|
||
|
if (pwszExecutableFilename != NULL)
|
||
|
DNFree(pwszExecutableFilename);
|
||
|
if (pdwPID != NULL)
|
||
|
DNFree(pdwPID);
|
||
|
if( pAppLoadedList )
|
||
|
DNFree(pAppLoadedList);
|
||
|
|
||
|
DPF_RETURN(hResultCode);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// DPL_ConnectApplication
|
||
|
//
|
||
|
// Try to connect to a lobbied application. Based on DPL_CONNECT_INFO flags,
|
||
|
// we may have to launch an application.
|
||
|
//
|
||
|
// If we have to launch an application, we will need to handshake the PID of the
|
||
|
// application (as it may be ripple launched). We will pass the LobbyClient's PID on the
|
||
|
// command line to the application launcher and expect it to be passed down to the
|
||
|
// application. The application will open a named shared memory block using the PID and
|
||
|
// write its PID there, and then signal a named event (using the LobbyClient's PID again).
|
||
|
// When the waiting LobbyClient is signaled by this event, it continues its connection
|
||
|
// process as if this was an existing running and available application.
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "DPL_ConnectApplication"
|
||
|
|
||
|
STDMETHODIMP DPL_ConnectApplication(IDirectPlay8LobbyClient *pInterface,
|
||
|
DPL_CONNECT_INFO *const pdplConnectInfo,
|
||
|
void *pvConnectionContext,
|
||
|
DPNHANDLE *const hApplication,
|
||
|
const DWORD dwTimeOut,
|
||
|
const DWORD dwFlags)
|
||
|
{
|
||
|
HRESULT hResultCode = DPN_OK;
|
||
|
DWORD dwSize = 0;
|
||
|
BYTE *pBuffer = NULL;
|
||
|
DPL_PROGRAM_DESC *pdplProgramDesc;
|
||
|
DWORD *pdwProcessList = NULL;
|
||
|
DWORD dwNumProcesses = 0;
|
||
|
DWORD dwPID = 0;
|
||
|
DWORD dw = 0;
|
||
|
DPNHANDLE handle = NULL;
|
||
|
DPL_CONNECTION *pdplConnection = NULL;
|
||
|
DIRECTPLAYLOBBYOBJECT *pdpLobbyObject = NULL;
|
||
|
|
||
|
DPFX(DPFPREP, 3,"Parameters: pdplConnectInfo [0x%p], pvUserAppContext [0x%p], hApplication [0x%lx], dwFlags [0x%lx]",
|
||
|
pdplConnectInfo,pvConnectionContext,hApplication,dwFlags);
|
||
|
|
||
|
TRY
|
||
|
{
|
||
|
pdpLobbyObject = static_cast<DIRECTPLAYLOBBYOBJECT*>(GET_OBJECT_FROM_INTERFACE(pInterface));
|
||
|
|
||
|
if( pdpLobbyObject->dwFlags & DPL_OBJECT_FLAG_PARAMVALIDATION )
|
||
|
{
|
||
|
if( FAILED( hResultCode = DPL_ValidateConnectApplication( pInterface, pdplConnectInfo, pvConnectionContext, hApplication, dwTimeOut, dwFlags ) ) )
|
||
|
{
|
||
|
DPFX(DPFPREP, 0, "Error validating connect application params hr=[0x%lx]", hResultCode );
|
||
|
DPF_RETURN( hResultCode );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Ensure we've been initialized
|
||
|
if (pdpLobbyObject->pReceiveQueue == NULL)
|
||
|
{
|
||
|
DPFERR("Not initialized");
|
||
|
DPF_RETURN(DPNERR_UNINITIALIZED);
|
||
|
}
|
||
|
}
|
||
|
EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
||
|
{
|
||
|
DPFERR("Invalid object" );
|
||
|
DPF_RETURN(DPNERR_INVALIDOBJECT);
|
||
|
}
|
||
|
|
||
|
// Get program description
|
||
|
dwSize = 0;
|
||
|
pBuffer = NULL;
|
||
|
hResultCode = DPLGetProgramDesc(&pdplConnectInfo->guidApplication,pBuffer,&dwSize);
|
||
|
if (hResultCode != DPNERR_BUFFERTOOSMALL)
|
||
|
{
|
||
|
DPFERR("Could not get Program Description");
|
||
|
goto EXIT_DPL_ConnectApplication;
|
||
|
}
|
||
|
if ((pBuffer = static_cast<BYTE*>(DNMalloc(dwSize))) == NULL)
|
||
|
{
|
||
|
DPFERR("Could not allocate space for buffer");
|
||
|
hResultCode = DPNERR_OUTOFMEMORY;
|
||
|
goto EXIT_DPL_ConnectApplication;
|
||
|
}
|
||
|
if ((hResultCode = DPLGetProgramDesc(&pdplConnectInfo->guidApplication,pBuffer,&dwSize)) != DPN_OK)
|
||
|
{
|
||
|
DPFERR("Could not get Program Description");
|
||
|
DisplayDNError(0,hResultCode);
|
||
|
goto EXIT_DPL_ConnectApplication;
|
||
|
}
|
||
|
|
||
|
pdplProgramDesc = reinterpret_cast<DPL_PROGRAM_DESC*>(pBuffer);
|
||
|
dwPID = 0;
|
||
|
dwNumProcesses = 0;
|
||
|
pdwProcessList = NULL;
|
||
|
|
||
|
if (!(pdplConnectInfo->dwFlags & DPLCONNECT_LAUNCHNEW)) // Only if not forcing launch
|
||
|
{
|
||
|
// Get process list
|
||
|
hResultCode = DPLGetProcessList(pdplProgramDesc->pwszExecutableFilename,NULL,&dwNumProcesses,
|
||
|
pdpLobbyObject->bIsUnicodePlatform);
|
||
|
if (hResultCode != DPN_OK && hResultCode != DPNERR_BUFFERTOOSMALL)
|
||
|
{
|
||
|
DPFERR("Could not retrieve process list");
|
||
|
DisplayDNError(0,hResultCode);
|
||
|
goto EXIT_DPL_ConnectApplication;
|
||
|
}
|
||
|
if (hResultCode == DPNERR_BUFFERTOOSMALL)
|
||
|
{
|
||
|
if ((pdwProcessList = static_cast<DWORD*>(DNMalloc(dwNumProcesses*sizeof(DWORD)))) == NULL)
|
||
|
{
|
||
|
DPFERR("Could not create process list buffer");
|
||
|
hResultCode = DPNERR_OUTOFMEMORY;
|
||
|
goto EXIT_DPL_ConnectApplication;
|
||
|
}
|
||
|
if ((hResultCode = DPLGetProcessList(pdplProgramDesc->pwszExecutableFilename,pdwProcessList,
|
||
|
&dwNumProcesses,pdpLobbyObject->bIsUnicodePlatform)) != DPN_OK)
|
||
|
{
|
||
|
DPFERR("Could not get process list");
|
||
|
DisplayDNError(0,hResultCode);
|
||
|
goto EXIT_DPL_ConnectApplication;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// Try to connect to an already running application
|
||
|
for (dw = 0 ; dw < dwNumProcesses ; dw++)
|
||
|
{
|
||
|
if ((hResultCode = DPLMakeApplicationUnavailable(pdwProcessList[dw])) == DPN_OK)
|
||
|
{
|
||
|
DPFX(DPFPREP, 1, "Found Existing Process=%d", pdwProcessList[dw] );
|
||
|
dwPID = pdwProcessList[dw];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pdwProcessList)
|
||
|
{
|
||
|
DNFree(pdwProcessList);
|
||
|
pdwProcessList = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Launch application if none are ready to connect
|
||
|
if ((dwPID == 0) && (pdplConnectInfo->dwFlags & (DPLCONNECT_LAUNCHNEW | DPLCONNECT_LAUNCHNOTFOUND)))
|
||
|
{
|
||
|
if ((hResultCode = DPLLaunchApplication(pdpLobbyObject,pdplProgramDesc,&dwPID,dwTimeOut)) != DPN_OK)
|
||
|
{
|
||
|
DPFERR("Could not launch application");
|
||
|
DisplayDNError(0,hResultCode);
|
||
|
goto EXIT_DPL_ConnectApplication;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DPFX(DPFPREP, 1, "Launched process dwID=%d", dwPID );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (dwPID == 0) // Could not make any connection
|
||
|
{
|
||
|
DPFERR("Could not connect to an existing application or launch a new one");
|
||
|
hResultCode = DPNERR_NOCONNECTION;
|
||
|
DisplayDNError( 0, hResultCode );
|
||
|
goto EXIT_DPL_ConnectApplication;
|
||
|
}
|
||
|
|
||
|
handle = NULL;
|
||
|
|
||
|
// Create connection
|
||
|
if ((hResultCode = DPLConnectionNew(pdpLobbyObject,&handle,&pdplConnection)) != DPN_OK)
|
||
|
{
|
||
|
DPFERR("Could not create connection entry");
|
||
|
DisplayDNError(0,hResultCode);
|
||
|
goto EXIT_DPL_ConnectApplication;
|
||
|
}
|
||
|
|
||
|
pdplConnection->dwTargetPID = dwPID;
|
||
|
|
||
|
DPFX(DPFPREP, 0, "PID=%d", dwPID );
|
||
|
|
||
|
// Set the context for this connection
|
||
|
if ((hResultCode = DPLConnectionSetContext( pdpLobbyObject, handle, pvConnectionContext )) != DPN_OK )
|
||
|
{
|
||
|
DPFERR( "Could not set contect for connection" );
|
||
|
DisplayDNError(0,hResultCode);
|
||
|
goto EXIT_DPL_ConnectApplication;
|
||
|
}
|
||
|
|
||
|
// Connect to selected application instance
|
||
|
if ((hResultCode = DPLConnectionConnect(pdpLobbyObject,handle,dwPID,TRUE)) != DPN_OK)
|
||
|
{
|
||
|
DPFERR("Could not connect to application");
|
||
|
DisplayDNError(0,hResultCode);
|
||
|
goto EXIT_DPL_ConnectApplication;
|
||
|
}
|
||
|
|
||
|
ResetEvent(pdplConnection->hConnectEvent);
|
||
|
|
||
|
// Pass lobby client info to application
|
||
|
|
||
|
if ((hResultCode = DPLConnectionSendREQ(pdpLobbyObject,handle,pdpLobbyObject->dwPID,
|
||
|
pdplConnectInfo)) != DPN_OK)
|
||
|
{
|
||
|
DPFERR("Could not send connection request");
|
||
|
DisplayDNError(0,hResultCode);
|
||
|
goto EXIT_DPL_ConnectApplication;
|
||
|
}
|
||
|
|
||
|
if (WaitForSingleObject(pdplConnection->hConnectEvent,INFINITE) != WAIT_OBJECT_0)
|
||
|
{
|
||
|
DPFERR("Wait for connection terminated");
|
||
|
hResultCode = DPNERR_GENERIC;
|
||
|
goto EXIT_DPL_ConnectApplication;
|
||
|
}
|
||
|
|
||
|
*hApplication = handle;
|
||
|
|
||
|
hResultCode = DPN_OK;
|
||
|
|
||
|
EXIT_DPL_ConnectApplication:
|
||
|
|
||
|
if( FAILED(hResultCode) && handle )
|
||
|
{
|
||
|
DPLConnectionDisconnect(pdpLobbyObject,handle);
|
||
|
DPLConnectionRelease(pdpLobbyObject,handle);
|
||
|
}
|
||
|
|
||
|
if (pBuffer)
|
||
|
DNFree(pBuffer);
|
||
|
|
||
|
if (pdwProcessList)
|
||
|
DNFree(pdwProcessList);
|
||
|
|
||
|
DPF_RETURN(hResultCode);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "DPL_ReleaseApplication"
|
||
|
|
||
|
STDMETHODIMP DPL_ReleaseApplication(IDirectPlay8LobbyClient *pInterface,
|
||
|
const DPNHANDLE hApplication,
|
||
|
const DWORD dwFlags )
|
||
|
{
|
||
|
HRESULT hResultCode;
|
||
|
DIRECTPLAYLOBBYOBJECT *pdpLobbyObject;
|
||
|
DPNHANDLE *hTargets = NULL;
|
||
|
DWORD dwNumTargets = 0;
|
||
|
DWORD dwTargetIndex = 0;
|
||
|
|
||
|
DPFX(DPFPREP, 3,"Parameters: hApplication [0x%lx]",hApplication);
|
||
|
|
||
|
TRY
|
||
|
{
|
||
|
pdpLobbyObject = static_cast<DIRECTPLAYLOBBYOBJECT*>(GET_OBJECT_FROM_INTERFACE(pInterface));
|
||
|
|
||
|
if( pdpLobbyObject->dwFlags & DPL_OBJECT_FLAG_PARAMVALIDATION )
|
||
|
{
|
||
|
if( FAILED( hResultCode = DPL_ValidateReleaseApplication( pInterface, hApplication, dwFlags ) ) )
|
||
|
{
|
||
|
DPFX(DPFPREP, 0, "Error validating release application params hr=[0x%lx]", hResultCode );
|
||
|
DPF_RETURN( hResultCode );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Ensure we've been initialized
|
||
|
if (pdpLobbyObject->pReceiveQueue == NULL)
|
||
|
{
|
||
|
DPFERR("Not initialized");
|
||
|
DPF_RETURN(DPNERR_UNINITIALIZED);
|
||
|
}
|
||
|
}
|
||
|
EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
||
|
{
|
||
|
DPFERR("Invalid object" );
|
||
|
DPF_RETURN(DPNERR_INVALIDOBJECT);
|
||
|
}
|
||
|
|
||
|
if( hApplication == DPLHANDLE_ALLCONNECTIONS )
|
||
|
{
|
||
|
dwNumTargets = 0;
|
||
|
|
||
|
// We need loop so if someone adds a connection during our run
|
||
|
// it gets added to our list
|
||
|
//
|
||
|
while( 1 )
|
||
|
{
|
||
|
hResultCode = DPLConnectionEnum( pdpLobbyObject, hTargets, &dwNumTargets );
|
||
|
|
||
|
if( hResultCode == DPNERR_BUFFERTOOSMALL )
|
||
|
{
|
||
|
if( hTargets )
|
||
|
{
|
||
|
delete [] hTargets;
|
||
|
}
|
||
|
|
||
|
hTargets = new DPNHANDLE[dwNumTargets];
|
||
|
|
||
|
if( hTargets == NULL )
|
||
|
{
|
||
|
DPFERR("Error allocating memory" );
|
||
|
hResultCode = DPNERR_OUTOFMEMORY;
|
||
|
dwNumTargets = 0;
|
||
|
goto EXIT_AND_CLEANUP;
|
||
|
}
|
||
|
|
||
|
memset( hTargets, 0x00, sizeof(DPNHANDLE)*dwNumTargets);
|
||
|
|
||
|
continue;
|
||
|
}
|
||
|
else if( FAILED( hResultCode ) )
|
||
|
{
|
||
|
DPFX(DPFPREP, 0, "Error getting list of connections hr=0x%x", hResultCode );
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Failed getting connection information
|
||
|
if( FAILED( hResultCode ) )
|
||
|
{
|
||
|
if( hTargets )
|
||
|
{
|
||
|
delete [] hTargets;
|
||
|
hTargets = NULL;
|
||
|
}
|
||
|
dwNumTargets = 0;
|
||
|
goto EXIT_AND_CLEANUP;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hTargets = new DPNHANDLE[1]; // We use array delete below so we need array new
|
||
|
|
||
|
if( hTargets == NULL )
|
||
|
{
|
||
|
DPFERR("Error allocating memory" );
|
||
|
hResultCode = DPNERR_OUTOFMEMORY;
|
||
|
dwNumTargets = 0;
|
||
|
goto EXIT_AND_CLEANUP;
|
||
|
}
|
||
|
|
||
|
dwNumTargets = 1;
|
||
|
hTargets[0] = hApplication;
|
||
|
}
|
||
|
|
||
|
for( dwTargetIndex = 0; dwTargetIndex < dwNumTargets; dwTargetIndex++ )
|
||
|
{
|
||
|
hResultCode = DPLConnectionDisconnect(pdpLobbyObject,hTargets[dwTargetIndex]);
|
||
|
|
||
|
if( FAILED( hResultCode ) )
|
||
|
{
|
||
|
DPFX(DPFPREP, 0, "Error disconnecting connection 0x%x hr=0x%x", hTargets[dwTargetIndex], hResultCode );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
EXIT_AND_CLEANUP:
|
||
|
|
||
|
if( hTargets )
|
||
|
delete [] hTargets;
|
||
|
|
||
|
DPF_RETURN(hResultCode);
|
||
|
}
|
||
|
|
||
|
|
||
|
// DPLLaunchApplication
|
||
|
//
|
||
|
// Launch the application with a command-line argument of:
|
||
|
// DPLID=PIDn PID=Lobby Client PID, n=launch counter (each launch increases it)
|
||
|
// Wait for the application to signal the event (or die)
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "DPLLaunchApplication"
|
||
|
|
||
|
HRESULT DPLLaunchApplication(DIRECTPLAYLOBBYOBJECT *const pdpLobbyObject,
|
||
|
DPL_PROGRAM_DESC *const pdplProgramDesc,
|
||
|
DWORD *const pdwPID,
|
||
|
const DWORD dwTimeOut)
|
||
|
{
|
||
|
HRESULT hResultCode;
|
||
|
DWORD dwAppNameLen=0; //Length of the application full name (path+exe)
|
||
|
PWSTR pwszAppName=NULL; //Unicode version of application full name
|
||
|
DWORD dwCmdLineLen=0; //Length of the command line string
|
||
|
PWSTR pwszCmdLine=NULL; //Unicode version of command line to supply
|
||
|
CHAR * pszAppName=NULL; //Ascii version of application full name
|
||
|
CHAR * pszCmdLine=NULL; //Acii version of command line string
|
||
|
LONG lc;
|
||
|
STARTUPINFOW siW; // Unicode startup info (place holder)
|
||
|
STARTUPINFOA siA;
|
||
|
PROCESS_INFORMATION pi;
|
||
|
DWORD dwError;
|
||
|
HANDLE hSyncEvents[2] = { NULL, NULL };
|
||
|
WCHAR pwszObjectName[(sizeof(DWORD)*2)*2 + 1];
|
||
|
CHAR pszObjectName[(sizeof(DWORD)*2)*2 + 1 + 1];
|
||
|
DPL_SHARED_CONNECT_BLOCK *pSharedBlock = NULL;
|
||
|
HANDLE hFileMap = NULL;
|
||
|
DWORD dwPID;
|
||
|
CHAR *pszDefaultDir = NULL;
|
||
|
WCHAR *wszToLaunchPath = NULL;
|
||
|
WCHAR *wszToLaunchExecutable = NULL;
|
||
|
DWORD dwToLaunchPathLen;
|
||
|
|
||
|
|
||
|
// Are we launching the launcher or the executable?
|
||
|
if( !pdplProgramDesc->pwszLauncherFilename || wcslen(pdplProgramDesc->pwszLauncherFilename) == 0 )
|
||
|
{
|
||
|
wszToLaunchPath = pdplProgramDesc->pwszExecutablePath;
|
||
|
wszToLaunchExecutable = pdplProgramDesc->pwszExecutableFilename;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
wszToLaunchPath = pdplProgramDesc->pwszLauncherPath;
|
||
|
wszToLaunchExecutable = pdplProgramDesc->pwszLauncherFilename;
|
||
|
}
|
||
|
|
||
|
DPFX(DPFPREP, 3,"Parameters: pdplProgramDesc [0x%p]",pdplProgramDesc);
|
||
|
|
||
|
DNASSERT(pdplProgramDesc != NULL);
|
||
|
|
||
|
// Increment launch count
|
||
|
lc = InterlockedIncrement(&pdpLobbyObject->lLaunchCount);
|
||
|
|
||
|
// Synchronization event and shared memory names
|
||
|
swprintf(pwszObjectName,L"%lx%lx",pdpLobbyObject->dwPID,lc);
|
||
|
sprintf(pszObjectName,"-%lx%lx",pdpLobbyObject->dwPID,lc);
|
||
|
|
||
|
// Compute the size of the full application name string (combination of path and exe name)
|
||
|
if (wszToLaunchPath)
|
||
|
dwAppNameLen += (wcslen(wszToLaunchPath) + 1);
|
||
|
if (wszToLaunchExecutable)
|
||
|
dwAppNameLen += (wcslen(wszToLaunchExecutable) + 1);
|
||
|
|
||
|
// Compute the size of the command line string
|
||
|
dwCmdLineLen=dwAppNameLen+1;
|
||
|
if (pdplProgramDesc->pwszCommandLine)
|
||
|
dwCmdLineLen += wcslen(pdplProgramDesc->pwszCommandLine);
|
||
|
dwCmdLineLen += (1 + wcslen(DPL_ID_STR_W) + (sizeof(DWORD)*2*2) + 1);
|
||
|
|
||
|
DPFX(DPFPREP, 5,"Application full name string length [%ld] WCHARs", dwAppNameLen);
|
||
|
DPFX(DPFPREP, 5,"Command Line string length [%ld] WCHARs", dwCmdLineLen);
|
||
|
|
||
|
// Allocate memory to hold the full app name and command line + check allocation was OK
|
||
|
pwszAppName=static_cast<WCHAR *>(DNMalloc(dwAppNameLen * sizeof(WCHAR)));
|
||
|
pwszCmdLine=static_cast<WCHAR *>(DNMalloc(dwCmdLineLen * sizeof(WCHAR)));
|
||
|
if (pwszAppName==NULL || pwszCmdLine==NULL)
|
||
|
{
|
||
|
DPFERR("Could not allocate strings for app name and command line");
|
||
|
hResultCode = DPNERR_OUTOFMEMORY;
|
||
|
goto CLEANUP_DPLLaunch;
|
||
|
}
|
||
|
|
||
|
// Build the application full name by combining launch path with exe name
|
||
|
*pwszAppName = L'\0';
|
||
|
if (wszToLaunchPath)
|
||
|
{
|
||
|
dwToLaunchPathLen = wcslen(wszToLaunchPath);
|
||
|
if (dwToLaunchPathLen > 0)
|
||
|
{
|
||
|
wcscat(pwszAppName,wszToLaunchPath);
|
||
|
if (wszToLaunchPath[dwToLaunchPathLen - 1] != L'\\')
|
||
|
{
|
||
|
wcscat(pwszAppName,L"\\");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (wszToLaunchExecutable)
|
||
|
{
|
||
|
wcscat(pwszAppName,wszToLaunchExecutable);
|
||
|
}
|
||
|
|
||
|
//Build the command line from app name, program description and the lobby related parameters
|
||
|
wcscpy(pwszCmdLine, pwszAppName);
|
||
|
wcscat(pwszCmdLine,L" ");
|
||
|
if (pdplProgramDesc->pwszCommandLine)
|
||
|
{
|
||
|
wcscat(pwszCmdLine,pdplProgramDesc->pwszCommandLine);
|
||
|
wcscat(pwszCmdLine,L" ");
|
||
|
}
|
||
|
wcscat(pwszCmdLine,DPL_ID_STR_W);
|
||
|
wcscat(pwszCmdLine,pwszObjectName);
|
||
|
|
||
|
DPFX(DPFPREP, 5,"Application full name string [%S]",pwszAppName);
|
||
|
DPFX(DPFPREP, 5,"Command Line string [%S]",pwszCmdLine);
|
||
|
|
||
|
|
||
|
// Create shared connect block to receive Application's PID
|
||
|
*pszObjectName = DPL_MSGQ_OBJECT_IDCHAR_FILEMAP;
|
||
|
hFileMap = CreateFileMappingA(INVALID_HANDLE_VALUE,(LPSECURITY_ATTRIBUTES) NULL,
|
||
|
PAGE_READWRITE,(DWORD)0,sizeof(DPL_SHARED_CONNECT_BLOCK),pszObjectName);
|
||
|
if (hFileMap == NULL)
|
||
|
{
|
||
|
dwError = GetLastError();
|
||
|
DPFX(DPFPREP, 0, "CreateFileMapping() failed dwLastError [0x%lx]", dwError );
|
||
|
hResultCode = DPNERR_CANTLAUNCHAPPLICATION;
|
||
|
goto CLEANUP_DPLLaunch;
|
||
|
}
|
||
|
|
||
|
// Map file
|
||
|
pSharedBlock = reinterpret_cast<DPL_SHARED_CONNECT_BLOCK*>(MapViewOfFile(hFileMap,FILE_MAP_ALL_ACCESS,0,0,0));
|
||
|
if (pSharedBlock == NULL)
|
||
|
{
|
||
|
dwError = GetLastError();
|
||
|
DPFX(DPFPREP, 0,"MapViewOfFile() failed dwLastError [0x%lx]", dwError);
|
||
|
hResultCode = DPNERR_CANTLAUNCHAPPLICATION;
|
||
|
goto CLEANUP_DPLLaunch;
|
||
|
}
|
||
|
|
||
|
// Create synchronization event
|
||
|
*pszObjectName = DPL_MSGQ_OBJECT_IDCHAR_EVENT;
|
||
|
if ((hSyncEvents[0] = CreateEventA(NULL,TRUE,FALSE,pszObjectName)) == NULL)
|
||
|
{
|
||
|
dwError = GetLastError();
|
||
|
DPFX(DPFPREP, 0, "Create Event Failed dwLastError [0x%lx]", dwError );
|
||
|
hResultCode = DPNERR_CANTLAUNCHAPPLICATION;
|
||
|
goto CLEANUP_DPLLaunch;
|
||
|
}
|
||
|
|
||
|
if( DNGetOSType() == VER_PLATFORM_WIN32_NT )
|
||
|
{
|
||
|
// More setup
|
||
|
siW.cb = sizeof(STARTUPINFO);
|
||
|
siW.lpReserved = NULL;
|
||
|
siW.lpDesktop = NULL;
|
||
|
siW.lpTitle = NULL;
|
||
|
siW.dwFlags = 0;
|
||
|
siW.cbReserved2 = 0;
|
||
|
siW.lpReserved2 = NULL;
|
||
|
|
||
|
// Launch !
|
||
|
if (CreateProcessW(pwszAppName, pwszCmdLine, NULL,NULL,FALSE,NORMAL_PRIORITY_CLASS,NULL,
|
||
|
pdplProgramDesc->pwszCurrentDirectory,&siW,&pi) == 0)
|
||
|
{
|
||
|
dwError = GetLastError();
|
||
|
DPFX(DPFPREP, 0, "CreateProcess Failed dwLastError [0x%lx]", dwError );
|
||
|
hResultCode = DPNERR_CANTLAUNCHAPPLICATION;
|
||
|
goto CLEANUP_DPLLaunch;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// More setup
|
||
|
siA.cb = sizeof(STARTUPINFO);
|
||
|
siA.lpReserved = NULL;
|
||
|
siA.lpDesktop = NULL;
|
||
|
siA.lpTitle = NULL;
|
||
|
siA.dwFlags = 0;
|
||
|
siA.cbReserved2 = 0;
|
||
|
siA.lpReserved2 = NULL;
|
||
|
|
||
|
DPFX(DPFPREP, 1, "Detected 9x, Doing Ansi launch" );
|
||
|
|
||
|
//Convert full app name, command line and default dir from unicode to ascii format
|
||
|
if( FAILED( hResultCode = STR_AllocAndConvertToANSI( &pszAppName, pwszAppName ) ) )
|
||
|
{
|
||
|
dwError = GetLastError();
|
||
|
DPFX(DPFPREP, 0, "String conversion failed dwError = [0x%lx]", dwError );
|
||
|
hResultCode = DPNERR_CONVERSION;
|
||
|
goto CLEANUP_DPLLaunch;
|
||
|
}
|
||
|
if( FAILED( hResultCode = STR_AllocAndConvertToANSI( &pszCmdLine, pwszCmdLine ) ) )
|
||
|
{
|
||
|
dwError = GetLastError();
|
||
|
DPFX(DPFPREP, 0, "String conversion failed dwError = [0x%lx]", dwError );
|
||
|
hResultCode = DPNERR_CONVERSION;
|
||
|
goto CLEANUP_DPLLaunch;
|
||
|
}
|
||
|
if( FAILED( hResultCode = STR_AllocAndConvertToANSI( &pszDefaultDir, pdplProgramDesc->pwszCurrentDirectory ) ) )
|
||
|
{
|
||
|
dwError = GetLastError();
|
||
|
DPFX(DPFPREP, 0, "String conversion failed dwError = [0x%lx]", dwError );
|
||
|
hResultCode = DPNERR_CONVERSION;
|
||
|
goto CLEANUP_DPLLaunch;
|
||
|
}
|
||
|
|
||
|
// Launch !
|
||
|
if (CreateProcessA(pszAppName,pszCmdLine,NULL,NULL,FALSE,NORMAL_PRIORITY_CLASS,NULL,
|
||
|
pszDefaultDir,&siA,&pi) == 0)
|
||
|
{
|
||
|
dwError = GetLastError();
|
||
|
DPFX(DPFPREP, 0, "CreateProcess Failed dwLastError [0x%lx]", dwError );
|
||
|
hResultCode = DPNERR_CANTLAUNCHAPPLICATION;
|
||
|
goto CLEANUP_DPLLaunch;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
hSyncEvents[1] = pi.hProcess;
|
||
|
|
||
|
// Wait for connection or application termination
|
||
|
dwError = WaitForMultipleObjects(2,hSyncEvents,FALSE,dwTimeOut);
|
||
|
|
||
|
// Immediately clean up
|
||
|
dwPID = pSharedBlock->dwPID;
|
||
|
/* CloseHandle(hSyncEvents[0]);
|
||
|
UnmapViewOfFile(pSharedBlock);
|
||
|
CloseHandle(hFileMap); */
|
||
|
|
||
|
// Ensure we can continue
|
||
|
if (dwError - WAIT_OBJECT_0 > 1)
|
||
|
{
|
||
|
if (dwError == WAIT_TIMEOUT)
|
||
|
{
|
||
|
DPFERR("Wait for application connection timed out");
|
||
|
hResultCode = DPNERR_TIMEDOUT;
|
||
|
goto CLEANUP_DPLLaunch;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DPFERR("Wait for application connection terminated mysteriously");
|
||
|
hResultCode = DPNERR_CANTLAUNCHAPPLICATION;
|
||
|
goto CLEANUP_DPLLaunch;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check if application terminated
|
||
|
if (dwError == 1)
|
||
|
{
|
||
|
DPFERR("Application was terminated");
|
||
|
hResultCode = DPNERR_CANTLAUNCHAPPLICATION;
|
||
|
goto CLEANUP_DPLLaunch;
|
||
|
}
|
||
|
|
||
|
*pdwPID = dwPID;
|
||
|
|
||
|
hResultCode = DPN_OK;
|
||
|
|
||
|
CLEANUP_DPLLaunch:
|
||
|
|
||
|
if( hSyncEvents[0] != NULL )
|
||
|
CloseHandle( hSyncEvents[0] );
|
||
|
|
||
|
if( pSharedBlock != NULL )
|
||
|
UnmapViewOfFile(pSharedBlock);
|
||
|
|
||
|
if( hFileMap != NULL )
|
||
|
CloseHandle( hFileMap );
|
||
|
|
||
|
if( pwszAppName != NULL )
|
||
|
DNFree( pwszAppName );
|
||
|
|
||
|
if (pwszCmdLine!=NULL)
|
||
|
DNFree( pwszCmdLine );
|
||
|
|
||
|
if( pszAppName != NULL )
|
||
|
delete[] pszAppName;
|
||
|
|
||
|
if (pszCmdLine!=NULL)
|
||
|
delete[] pszCmdLine;
|
||
|
|
||
|
if( pszDefaultDir != NULL )
|
||
|
delete [] pszDefaultDir;
|
||
|
|
||
|
DPF_RETURN(hResultCode);
|
||
|
}
|
||
|
|
||
|
HRESULT DPLUpdateAppStatus(DIRECTPLAYLOBBYOBJECT *const pdpLobbyObject,
|
||
|
const DPNHANDLE hSender,
|
||
|
BYTE *const pBuffer)
|
||
|
{
|
||
|
HRESULT hResultCode;
|
||
|
DPL_INTERNAL_MESSAGE_UPDATE_STATUS *pStatus;
|
||
|
DPL_MESSAGE_SESSION_STATUS MsgStatus;
|
||
|
|
||
|
DPFX(DPFPREP, 3,"Parameters: pBuffer [0x%p]",pBuffer);
|
||
|
|
||
|
DNASSERT(pdpLobbyObject != NULL);
|
||
|
DNASSERT(pBuffer != NULL);
|
||
|
|
||
|
pStatus = reinterpret_cast<DPL_INTERNAL_MESSAGE_UPDATE_STATUS*>(pBuffer);
|
||
|
|
||
|
MsgStatus.dwSize = sizeof(DPL_MESSAGE_SESSION_STATUS);
|
||
|
MsgStatus.dwStatus = pStatus->dwStatus;
|
||
|
MsgStatus.hSender = hSender;
|
||
|
|
||
|
// Return code is irrelevant, at this point we're going to indicate regardless
|
||
|
hResultCode = DPLConnectionGetContext( pdpLobbyObject, hSender, &MsgStatus.pvConnectionContext );
|
||
|
|
||
|
if( FAILED( hResultCode ) )
|
||
|
{
|
||
|
DPFX(DPFPREP, 0, "Error getting connection context for 0x%x hr=0x%x", hSender, hResultCode );
|
||
|
}
|
||
|
|
||
|
hResultCode = (pdpLobbyObject->pfnMessageHandler)(pdpLobbyObject->pvUserContext,
|
||
|
DPL_MSGID_SESSION_STATUS,
|
||
|
reinterpret_cast<BYTE*>(&MsgStatus));
|
||
|
|
||
|
DPFX(DPFPREP, 3,"Returning: [0x%lx]",hResultCode);
|
||
|
return(hResultCode);
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------
|
||
|
#if 0
|
||
|
|
||
|
// HRESULT DNL_RegisterLcMessageHandler
|
||
|
// LPVOID lpv Interface pointer
|
||
|
// LPFNDNLCMESSAGEHANDLER lpfn Pointer to user supplied lobby client message handler function
|
||
|
// DWORD dwFlags Not Used
|
||
|
//
|
||
|
// Returns
|
||
|
// DPN_OK If the message handler was registered without incident
|
||
|
// DPNERR_INVALIDPARAM If there was an invalid parameter
|
||
|
// DPNERR_GENERIC If there were any problems
|
||
|
//
|
||
|
// Notes
|
||
|
// This function registers a user supplied lobby client message handler function. This function should
|
||
|
// only be called once, when the lobby client is launched.
|
||
|
//
|
||
|
// This will set up the required message queues, and spawn the lobby client's receive message queue thread.
|
||
|
|
||
|
|
||
|
|
||
|
// HRESULT DNL_EnumLocalPrograms
|
||
|
// LPVOID lpv Interface pointer
|
||
|
// LPGUID lpGuidApplication GUID of application to enumerate (optional)
|
||
|
// LPVOID lpvEnumData Buffer to be filled with DNAPPINFO structs and Unicode strings
|
||
|
// LPDWORD lpdwEnumData Size of lpvEnumData and number of bytes needed on return
|
||
|
// LPDWORD lpdwItems Number of DNAPPINFO structs in lpvEnumData
|
||
|
//
|
||
|
// Returns
|
||
|
// DPN_OK If the key was added successfully
|
||
|
// DPNERR_INVALIDPARAM If there was a problem with a parameter
|
||
|
// DPNERR_OUTOFMEMORY If it could not allocate memory
|
||
|
// DPNERR_DOESNOTEXIST If there was a problem opening reading a registry key
|
||
|
//
|
||
|
// Notes
|
||
|
// - This is ugly because of the need to support both WinNT and Win9x at runtime.
|
||
|
// WinNT registry is kept in Unicode, whereas Win9x is in ANSI. Application root key
|
||
|
// names should be ANSI compatible (or their GUIDs are used instead) i.e. Will be stored
|
||
|
// in Unicode under WinNT, but only if convertable to ANSI
|
||
|
// - Strings are placed in the buffer, starting at the end of the buffer and working
|
||
|
// backwards.
|
||
|
// - In most cases, errors will cause the current app being enumerated to be ignored
|
||
|
// and not included in the enumeration buffer.
|
||
|
|
||
|
|
||
|
|
||
|
// HRESULT DNL_ConnectApplication
|
||
|
// LPVOID lpv Interface pointer
|
||
|
// LPDNCONNECTINFO lpdnConnectionInfo Pointer to connection info structure
|
||
|
// LPVOID lpvUserApplicationContext User supplied application context value
|
||
|
// LPDWORD lpdwAppId Pointer to receive application ID (handle) in
|
||
|
// DWORD dwFlags Flags
|
||
|
//
|
||
|
// Returns
|
||
|
// DPN_OK If the application was connected to without incident
|
||
|
// DPNERR_INVALIDPARAM If there was an invalid parameter
|
||
|
// DPNERR_OUTOFMEMORY If there were any memory allocation problems
|
||
|
// DPNERR_GENERIC If there were any problems
|
||
|
//
|
||
|
// Notes
|
||
|
// This function connects the lobby client to a user specified application. If successfull, the
|
||
|
// DNLobby assigned application ID will be returned in lpdwAppId.
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "DNL_ConnectApplication"
|
||
|
|
||
|
STDMETHODIMP DNL_ConnectApplication(LPVOID lpv,LPDNCONNECTINFO lpdnConnectionInfo,
|
||
|
LPVOID lpvUserApplicationContext,LPDWORD lpdwAppId, DWORD dwFlags)
|
||
|
{
|
||
|
LPDIRECTPLAYLOBBYOBJECT lpdnLobbyObject;
|
||
|
HRESULT hResultCode = DPN_OK;
|
||
|
DNPROGRAMDESC dnProgramDesc; // Program descriptor for launching applications
|
||
|
DWORD dwNumApps; // Number of applications currently running
|
||
|
LPDWORD lpdwProcessList = NULL; // Pointer to process list
|
||
|
DWORD dwProcessId; // Process ID of target applications
|
||
|
DWORD dw; // Counter
|
||
|
DWORD dwStrLen; // Various string lengths
|
||
|
LPWSTR lpwszCommandLine = NULL; // Expanded Unicode command line
|
||
|
LPWSTR lpwszCurrentDirectory = NULL; // Expanded Unicode current directory
|
||
|
LPSTR lpszCommandLine = NULL; // Expanded ANSI command line
|
||
|
LPSTR lpszCurrentDirectory = NULL; // Expanded ANSI current directory
|
||
|
LPWSTR lpwszUnexpanded = NULL; // Unexpanded command line
|
||
|
LPSTR lpszUnexpanded = NULL; // Unexpanded ANSI strings converted from Unicode
|
||
|
STARTUPINFOA siA; // ANSI startup info (place holder)
|
||
|
STARTUPINFOW siW; // Unicode startup info (place holder)
|
||
|
PROCESS_INFORMATION pi;
|
||
|
UUID uuid;
|
||
|
RPC_STATUS rpcStatus;
|
||
|
DWORD dwHandle;
|
||
|
LPDN_APP_HANDLE_ENTRY lpdnAppHandleEntry;
|
||
|
LPDN_MESSAGE_STRUCT lpdnMsg = NULL;
|
||
|
|
||
|
DPFX(DPFPREP, 9,"Parameters: lpv [%p], lpdnConnectionInfo [%p], lpvUserApplicationContext [%p], lpdwAppId [%p], dwFlags [%lx]",
|
||
|
lpv,lpdnConnectionInfo,lpvUserApplicationContext,lpdwAppId,dwFlags);
|
||
|
|
||
|
// Parameter validation
|
||
|
TRY
|
||
|
{
|
||
|
if (lpv == NULL || lpdnConnectionInfo == NULL || lpdwAppId == NULL)
|
||
|
return(DPNERR_INVALIDPARAM);
|
||
|
}
|
||
|
EXCEPT (EXCEPTION_EXECUTE_HANDLER)
|
||
|
{
|
||
|
DPFERR("Exception encountered validating parameters");
|
||
|
return(DPNERR_INVALIDPARAM);
|
||
|
}
|
||
|
|
||
|
lpdnLobbyObject = (LPDIRECTPLAYLOBBYOBJECT) GET_OBJECT_FROM_INTERFACE(lpv);
|
||
|
|
||
|
// Get program description from registry
|
||
|
if ((hResultCode = DnGetProgramDesc(lpdnConnectionInfo->lpGuidApplication,&dnProgramDesc))
|
||
|
!= DPN_OK)
|
||
|
{
|
||
|
goto EXIT_DNL_ConnectApplication;
|
||
|
}
|
||
|
|
||
|
hResultCode = DnGetProcessListW(dnProgramDesc.lpwszFilename,NULL,&dwNumApps); // Num apps
|
||
|
if (hResultCode != DPN_OK && hResultCode != DPNERR_BUFFERTOOSMALL)
|
||
|
{
|
||
|
DnFreeProgramDesc(&dnProgramDesc);
|
||
|
goto EXIT_DNL_ConnectApplication;
|
||
|
}
|
||
|
while (hResultCode == DPNERR_BUFFERTOOSMALL) // New processes may get launched !
|
||
|
{
|
||
|
if (lpdwProcessList != NULL)
|
||
|
GlobalFree(lpdwProcessList);
|
||
|
|
||
|
if ((lpdwProcessList = (LPDWORD)GLOBALALLOC(dwNumApps*sizeof(DWORD))) == NULL)
|
||
|
{
|
||
|
hResultCode = DPNERR_OUTOFMEMORY;
|
||
|
DnFreeProgramDesc(&dnProgramDesc);
|
||
|
goto EXIT_DNL_ConnectApplication;
|
||
|
}
|
||
|
hResultCode = DnGetProcessListW(dnProgramDesc.lpwszFilename,lpdwProcessList,&dwNumApps);
|
||
|
}
|
||
|
|
||
|
if (hResultCode != DPN_OK) // Some sort of error occurred
|
||
|
{
|
||
|
DnFreeProgramDesc(&dnProgramDesc);
|
||
|
goto EXIT_DNL_ConnectApplication;
|
||
|
}
|
||
|
|
||
|
dwProcessId = 0;
|
||
|
for (dw = 0; dw < dwNumApps ; dw++)
|
||
|
{
|
||
|
if (DnCheckMsgQueueWaiting(lpdwProcessList[dw]))
|
||
|
{
|
||
|
if (DnSetMsgQueueWaiting(lpdwProcessList[dw],FALSE,TRUE) == DPN_OK)
|
||
|
{
|
||
|
// This is the process ID of the existing app to connect to !
|
||
|
dwProcessId = lpdwProcessList[dw];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (dwProcessId == 0) // Could not find an app to connect to - launch a new one
|
||
|
{
|
||
|
dwStrLen = wcslen(dnProgramDesc.lpwszPath) + 1
|
||
|
+ wcslen(dnProgramDesc.lpwszApplicationLauncher) + 1
|
||
|
+ wcslen(dnProgramDesc.lpwszCommandLine) + 1 + DN_LC_ID_STR_LEN + DN_GUID_STR_LEN + 1;
|
||
|
if ((lpwszUnexpanded = (LPWSTR)GLOBALALLOC(dwStrLen*sizeof(WCHAR))) == NULL)
|
||
|
{
|
||
|
DnFreeProgramDesc(&dnProgramDesc);
|
||
|
hResultCode = DPNERR_OUTOFMEMORY;
|
||
|
goto EXIT_DNL_ConnectApplication;
|
||
|
}
|
||
|
|
||
|
*lpwszUnexpanded = L'\0';
|
||
|
if (wcslen(dnProgramDesc.lpwszPath) > 0)
|
||
|
{
|
||
|
wcscpy(lpwszUnexpanded,dnProgramDesc.lpwszPath);
|
||
|
wcscat(lpwszUnexpanded,L"\\");
|
||
|
}
|
||
|
wcscat(lpwszUnexpanded,dnProgramDesc.lpwszApplicationLauncher);
|
||
|
wcscat(lpwszUnexpanded,L" ");
|
||
|
wcscat(lpwszUnexpanded,dnProgramDesc.lpwszCommandLine);
|
||
|
wcscat(lpwszUnexpanded,L" ");
|
||
|
wcscat(lpwszUnexpanded,DN_LC_ID_WSTR);
|
||
|
rpcStatus = CoCreateGuid(&uuid);
|
||
|
if (rpcStatus != RPC_S_OK && rpcStatus != RPC_S_UUID_LOCAL_ONLY)
|
||
|
{
|
||
|
DnFreeProgramDesc(&dnProgramDesc);
|
||
|
hResultCode = DPNERR_GENERIC;
|
||
|
goto EXIT_DNL_ConnectApplication;
|
||
|
}
|
||
|
StringFromGUID(&uuid,lpwszUnexpanded+wcslen(lpwszUnexpanded),DN_GUID_STR_LEN+1);
|
||
|
|
||
|
if (DN_RUNNING_NT) // Unicode on WinNT
|
||
|
{
|
||
|
// Expand environment strings
|
||
|
if ((hResultCode = DnExpandEnvStringW(lpwszUnexpanded,&lpwszCommandLine)) != DPN_OK)
|
||
|
{
|
||
|
DPFX(DPFPREP, 0,"DnExpandEnvStringW() failed [%lX]",hResultCode);
|
||
|
DnFreeProgramDesc(&dnProgramDesc);
|
||
|
goto EXIT_DNL_ConnectApplication;
|
||
|
}
|
||
|
if ((hResultCode = DnExpandEnvStringW(dnProgramDesc.lpwszCurrentDirectory,
|
||
|
&lpwszCurrentDirectory)) != DPN_OK)
|
||
|
{
|
||
|
DPFX(DPFPREP, 0,"DnExpandEnvStringW() failed [%lX]",hResultCode);
|
||
|
DnFreeProgramDesc(&dnProgramDesc);
|
||
|
goto EXIT_DNL_ConnectApplication;
|
||
|
}
|
||
|
|
||
|
// Set up to Launch application
|
||
|
siW.cb = sizeof(STARTUPINFO);
|
||
|
siW.lpReserved = NULL;
|
||
|
siW.lpDesktop = NULL;
|
||
|
siW.lpTitle = NULL;
|
||
|
siW.dwFlags = (DWORD)NULL;
|
||
|
siW.cbReserved2 = (BYTE)NULL;
|
||
|
siW.lpReserved2 = NULL;
|
||
|
|
||
|
// Launch application (finally !)
|
||
|
if (CreateProcessW(NULL,lpwszCommandLine,NULL,NULL,FALSE,NORMAL_PRIORITY_CLASS,NULL,
|
||
|
lpwszCurrentDirectory,&siW,&pi) == 0)
|
||
|
{
|
||
|
hResultCode = DPNERR_CANTLAUNCHAPPLICATION;
|
||
|
DnFreeProgramDesc(&dnProgramDesc);
|
||
|
goto EXIT_DNL_ConnectApplication;
|
||
|
}
|
||
|
}
|
||
|
else // ANSI on Win9x
|
||
|
{
|
||
|
// Convert command line from Unicode to ANSI
|
||
|
if ((lpszUnexpanded = (LPSTR)GLOBALALLOC(dwStrLen)) == NULL)
|
||
|
{
|
||
|
DnFreeProgramDesc(&dnProgramDesc);
|
||
|
hResultCode = DPNERR_OUTOFMEMORY;
|
||
|
goto EXIT_DNL_ConnectApplication;
|
||
|
}
|
||
|
WideToAnsi(lpszUnexpanded,lpwszUnexpanded,dwStrLen);
|
||
|
DPFX(DPFPREP, 7,"Unexpanded Command Line [%s]",lpszUnexpanded);
|
||
|
|
||
|
// Expand command line environment strings
|
||
|
if ((hResultCode = DnExpandEnvStringA(lpszUnexpanded,&lpszCommandLine)) != DPN_OK)
|
||
|
{
|
||
|
DPFX(DPFPREP, 0,"DnExpandEnvStringA() failed [%lX]",hResultCode);
|
||
|
DnFreeProgramDesc(&dnProgramDesc);
|
||
|
goto EXIT_DNL_ConnectApplication;
|
||
|
}
|
||
|
DPFX(DPFPREP, 7,"Expanded Command Line [%s]",lpszCommandLine);
|
||
|
|
||
|
// Convert current directory from Unicode to ANSI
|
||
|
GlobalFree(lpszUnexpanded);
|
||
|
lpszUnexpanded = NULL;
|
||
|
dwStrLen = wcslen(dnProgramDesc.lpwszCurrentDirectory);
|
||
|
if ((lpszUnexpanded = (LPSTR)GLOBALALLOC(dwStrLen+1)) == NULL)
|
||
|
{
|
||
|
DnFreeProgramDesc(&dnProgramDesc);
|
||
|
hResultCode = DPNERR_OUTOFMEMORY;
|
||
|
goto EXIT_DNL_ConnectApplication;
|
||
|
}
|
||
|
WideToAnsi(lpszUnexpanded,dnProgramDesc.lpwszCurrentDirectory,dwStrLen+1);
|
||
|
DPFX(DPFPREP, 7,"Unexpanded Current Directory [%s]",lpszUnexpanded);
|
||
|
|
||
|
// Expand current directory environment strings
|
||
|
if ((hResultCode = DnExpandEnvStringA(lpszUnexpanded,&lpszCurrentDirectory)) != DPN_OK)
|
||
|
{
|
||
|
DPFX(DPFPREP, 0,"DnExpandEnvStringA() failed [%lX]",hResultCode);
|
||
|
DnFreeProgramDesc(&dnProgramDesc);
|
||
|
goto EXIT_DNL_ConnectApplication;
|
||
|
}
|
||
|
DPFX(DPFPREP, 7,"Expanded Current Directory [%s]",lpszCurrentDirectory);
|
||
|
|
||
|
// Set up to Launch application
|
||
|
siA.cb = sizeof(STARTUPINFO);
|
||
|
siA.lpReserved = NULL;
|
||
|
siA.lpDesktop = NULL;
|
||
|
siA.lpTitle = NULL;
|
||
|
siA.dwFlags = (DWORD)NULL;
|
||
|
siA.cbReserved2 = (BYTE)NULL;
|
||
|
siA.lpReserved2 = NULL;
|
||
|
|
||
|
// Launch application (finally !)
|
||
|
if (CreateProcessA(NULL,lpszCommandLine,NULL,NULL,FALSE,NORMAL_PRIORITY_CLASS,NULL,
|
||
|
lpszCurrentDirectory,&siA,&pi) == 0)
|
||
|
{
|
||
|
DPFERR("CreateProcess() failed !");
|
||
|
hResultCode = DPNERR_CANTLAUNCHAPPLICATION;
|
||
|
DnFreeProgramDesc(&dnProgramDesc);
|
||
|
goto EXIT_DNL_ConnectApplication;
|
||
|
}
|
||
|
}
|
||
|
// Get Application Process ID
|
||
|
if ((hResultCode = DnHandShakeAppPid(TRUE,&uuid,&dwProcessId)) != DPN_OK)
|
||
|
{
|
||
|
DPFERR("Hand shake to retrieve App PID failed !");
|
||
|
DnFreeProgramDesc(&dnProgramDesc);
|
||
|
goto EXIT_DNL_ConnectApplication;
|
||
|
}
|
||
|
DPFX(DPFPREP, 0,"Application PID = [0x%08lX]",dwProcessId);
|
||
|
}
|
||
|
DnFreeProgramDesc(&dnProgramDesc);
|
||
|
|
||
|
// Create application handle
|
||
|
if ((lpdnAppHandleEntry = (LPDN_APP_HANDLE_ENTRY)GLOBALALLOC(
|
||
|
sizeof(DN_APP_HANDLE_ENTRY))) == NULL)
|
||
|
{
|
||
|
hResultCode = DPNERR_OUTOFMEMORY;
|
||
|
goto EXIT_DNL_ConnectApplication;
|
||
|
}
|
||
|
lpdnAppHandleEntry->dwAppProcessId = dwProcessId;
|
||
|
|
||
|
if ((lpdnAppHandleEntry->lpmqSendMsgQueue =
|
||
|
(DN_MSG_QUEUE_STRUCT *)GLOBALALLOC(sizeof(DN_MSG_QUEUE_STRUCT))) == NULL)
|
||
|
{
|
||
|
hResultCode = DPNERR_OUTOFMEMORY;
|
||
|
goto EXIT_DNL_ConnectApplication;
|
||
|
}
|
||
|
if ((hResultCode = CreateHandle(lpdnLobbyObject->lphsApplicationHandles,&dwHandle,lpdnAppHandleEntry)) != DPN_OK)
|
||
|
{
|
||
|
goto EXIT_DNL_ConnectApplication;
|
||
|
}
|
||
|
lpdnAppHandleEntry->dwHandle = dwHandle;
|
||
|
lpdnAppHandleEntry->lpvUserApplicationContext = lpvUserApplicationContext;
|
||
|
*lpdwAppId = dwHandle;
|
||
|
|
||
|
// Connect to Application's receive message queue
|
||
|
if ((hResultCode = DnOpenMsgQueue(lpdnAppHandleEntry->lpmqSendMsgQueue,dwProcessId,
|
||
|
DN_MSG_OBJECT_SUFFIX_APPLICATION,DN_MSG_QUEUE_SIZE,(DWORD)NULL)) != DPN_OK)
|
||
|
{
|
||
|
goto EXIT_DNL_ConnectApplication;
|
||
|
}
|
||
|
|
||
|
// Pass lobby client connection info to application
|
||
|
if ((hResultCode = DnSendLcInfo(lpdnAppHandleEntry->lpmqSendMsgQueue,dwHandle,
|
||
|
GetCurrentProcessId())) != DPN_OK)
|
||
|
{
|
||
|
goto EXIT_DNL_ConnectApplication;
|
||
|
}
|
||
|
|
||
|
// Pass connection info - TODO check if valid
|
||
|
DPFX(DPFPREP, 9,"Sending Connection Info ...");
|
||
|
|
||
|
// Get string lengths
|
||
|
dwStrLen = ((wcslen(lpdnConnectionInfo->lpwszPlayerName)+ 1 ) * sizeof(WCHAR))
|
||
|
+ strlen(lpdnConnectionInfo->lpszDNAddressLocalSettings) + 1
|
||
|
+ strlen(lpdnConnectionInfo->lpszDNAddressRemote) + 1
|
||
|
+ sizeof(GUID);
|
||
|
dw = sizeof(DN_MESSAGE_STRUCT) + dwStrLen;
|
||
|
|
||
|
// Create message
|
||
|
if ((lpdnMsg = (LPDN_MESSAGE_STRUCT)GLOBALALLOC(dw)) == NULL)
|
||
|
{
|
||
|
hResultCode = DPNERR_OUTOFMEMORY;
|
||
|
goto EXIT_DNL_ConnectApplication;
|
||
|
}
|
||
|
|
||
|
lpdnMsg->dwMessageType = DN_MSGTYPE_CONNECTION_SETTINGS;
|
||
|
lpdnMsg->dwParam1 = 0;
|
||
|
lpdnMsg->dwParam2 = (wcslen(lpdnConnectionInfo->lpwszPlayerName) + 1) * sizeof(WCHAR);
|
||
|
lpdnMsg->dwParam3 = lpdnMsg->dwParam2 + strlen(lpdnConnectionInfo->lpszDNAddressRemote) + 1;
|
||
|
lpdnMsg->dwParam4 = lpdnMsg->dwParam3 + strlen(lpdnConnectionInfo->lpszDNAddressLocalSettings) + 1;
|
||
|
lpdnMsg->dwTagLen = dwStrLen;
|
||
|
|
||
|
memcpy(&lpdnMsg->cTags + lpdnMsg->dwParam1,lpdnConnectionInfo->lpwszPlayerName,
|
||
|
lpdnMsg->dwParam2);
|
||
|
memcpy(&lpdnMsg->cTags + lpdnMsg->dwParam2,lpdnConnectionInfo->lpszDNAddressRemote,
|
||
|
lpdnMsg->dwParam3-lpdnMsg->dwParam2);
|
||
|
memcpy(&lpdnMsg->cTags + lpdnMsg->dwParam3,lpdnConnectionInfo->lpszDNAddressLocalSettings,
|
||
|
lpdnMsg->dwParam4-lpdnMsg->dwParam3);
|
||
|
memcpy(&lpdnMsg->cTags + lpdnMsg->dwParam4,lpdnConnectionInfo->lpGuidApplication,
|
||
|
dwStrLen-lpdnMsg->dwParam4);
|
||
|
|
||
|
if ((hResultCode = DnSendMsg(lpdnAppHandleEntry->lpmqSendMsgQueue,lpdnMsg,dw,FALSE,
|
||
|
0)) != DPN_OK)
|
||
|
{
|
||
|
GlobalFree(lpdnMsg);
|
||
|
goto EXIT_DNL_ConnectApplication;
|
||
|
}
|
||
|
|
||
|
GlobalFree(lpdnMsg);
|
||
|
|
||
|
EXIT_DNL_ConnectApplication:
|
||
|
|
||
|
if (lpwszUnexpanded != NULL)
|
||
|
GlobalFree(lpwszUnexpanded);
|
||
|
if (lpwszCommandLine != NULL)
|
||
|
GlobalFree(lpwszCommandLine);
|
||
|
if (lpwszCurrentDirectory != NULL)
|
||
|
GlobalFree(lpwszCurrentDirectory);
|
||
|
if (lpszUnexpanded != NULL)
|
||
|
GlobalFree(lpszUnexpanded);
|
||
|
if (lpszCommandLine != NULL)
|
||
|
GlobalFree(lpszCommandLine);
|
||
|
if (lpszCurrentDirectory != NULL)
|
||
|
GlobalFree(lpszCurrentDirectory);
|
||
|
if (lpdwProcessList != NULL)
|
||
|
GlobalFree(lpdwProcessList);
|
||
|
|
||
|
return(hResultCode);
|
||
|
}
|
||
|
|
||
|
|
||
|
// HRESULT DnProcessLcMessage
|
||
|
// LPDIRECTNETLOBBYOBJECT lpdnLobbyObject Pointer to lobby object
|
||
|
// LPVOID lpvUserHandler Pointer to user message handler routine
|
||
|
// LPVOID lpvMsgBuff Pointer to message buffer
|
||
|
// DWORD dwMsgLen Length of message buffer
|
||
|
// DWORD dwId Application ID which sent the message
|
||
|
// LPVOID lpvContext User context value associated with the app ID
|
||
|
//
|
||
|
// Returns
|
||
|
// DPN_OK If the message was processed without incident
|
||
|
// DPNERR_GENERIC If there were any problems from the user supplied handler
|
||
|
//
|
||
|
// Notes
|
||
|
// This function processes messages received by the lobby client from the applications it is connected to.
|
||
|
// One Lobby Client may be connected to one or more applications. Internal messages are processed here,
|
||
|
// and all others are passed to the user supplied message handler.
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "DnProcessLcMessage"
|
||
|
|
||
|
HRESULT DnProcessLcMessage(LPDIRECTNETLOBBYOBJECT lpdnLobbyObject,LPVOID lpvUserHandler,
|
||
|
LPVOID lpvMsgBuff, DWORD dwMsgLen,DWORD dwId,LPVOID lpvContext)
|
||
|
{
|
||
|
HRESULT hResultCode = DPN_OK;
|
||
|
LPDN_MESSAGE_STRUCT lpdnMsg;
|
||
|
|
||
|
DPFX(DPFPREP, 9,"Parameters: lpdnLobbyObject [0x%p], lpvUserHandler [0x%p], lpvMsgBuff [0x%p], dwMsgLen [%lx], dwId [%lx], lpvContext [0x%p]",
|
||
|
lpdnLobbyObject,lpvUserHandler,lpvMsgBuff,dwMsgLen,dwId,lpvContext);
|
||
|
|
||
|
lpdnMsg = (LPDN_MESSAGE_STRUCT)lpvMsgBuff;
|
||
|
|
||
|
switch(lpdnMsg->dwMessageType)
|
||
|
{
|
||
|
case DN_MSGTYPE_TERMINATE:
|
||
|
DPFX(DPFPREP, 9,"Received Terminate");
|
||
|
ExitThread(0);
|
||
|
break;
|
||
|
|
||
|
default: // Pass message to user message handler
|
||
|
DPFX(DPFPREP, 9,"User Message ...");
|
||
|
hResultCode = ((LPFNDNLCMESSAGEHANDLER)lpvUserHandler)(dwId,lpvContext,lpdnMsg->dwMessageType,lpdnMsg->dwUserToken,
|
||
|
lpdnMsg->dwParam1,lpdnMsg->dwParam2,lpdnMsg->dwParam3,lpdnMsg->dwParam4,
|
||
|
lpdnMsg->dwTagLen,&(lpdnMsg->cTags));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return(hResultCode);
|
||
|
}
|
||
|
|
||
|
|
||
|
// HRESULT DNL_SendToApplication
|
||
|
// LPVOID lpv Interface pointer
|
||
|
// DWORD dwAppId Application ID to send message to
|
||
|
// DWORD dwMessageId Message ID code
|
||
|
// DWORD dwUserToken User supplied token
|
||
|
// DWORD dwParam1
|
||
|
// DWORD dwParam2
|
||
|
// DWORD dwParam3
|
||
|
// DWORD dwParam4
|
||
|
// DWORD dwFlags
|
||
|
// DWORD dwTagLen Length of tags field (in bytes)
|
||
|
// LPVOID lpvTags Tag field
|
||
|
//
|
||
|
// Returns
|
||
|
// DPN_OK If the message was sent without incident
|
||
|
// DPNERR_INVALIDPARAM If there was an invalid parameter
|
||
|
// DPNERR_OUTOFMEMORY If there were any memory allocation problems
|
||
|
// DPNERR_GENERIC If there were any problems
|
||
|
//
|
||
|
// Notes
|
||
|
// This function sends messages from the lobby client to a specified application.
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "DNL_SendToApplication"
|
||
|
|
||
|
STDMETHODIMP DNL_SendToApplication(LPVOID lpv,DWORD dwAppId,DWORD dwMessageId,DWORD dwUserToken,
|
||
|
DWORD dwParam1,DWORD dwParam2,DWORD dwParam3,DWORD dwParam4,
|
||
|
DWORD dwFlags,DWORD dwTagLen,LPVOID lpvTags)
|
||
|
{
|
||
|
LPDIRECTNETLOBBYOBJECT lpdnLobbyObject;
|
||
|
HRESULT hResultCode = DPN_OK;
|
||
|
LPDN_APP_HANDLE_ENTRY lpdnAppHandleEntry;
|
||
|
LPDN_MESSAGE_STRUCT lpdnMessage;
|
||
|
DN_MESSAGE_STRUCT dnMessage;
|
||
|
DWORD dwMsgLen;
|
||
|
|
||
|
DPFX(DPFPREP, 9,"Parameters: lpv [%p], dwAppId [%lx], dwMessageId [%lx], dwUserToken [%lx], dwParams [%lx], [%lx], [%lx], [%lx], dwFlags [%lx], dwTagLen [%lx], lpvTags [%p]",
|
||
|
lpv,dwAppId,dwMessageId,dwUserToken,dwParam1,dwParam2,dwParam3,dwParam4,dwFlags,dwTagLen,lpvTags);
|
||
|
|
||
|
// Parameter validation
|
||
|
TRY
|
||
|
{
|
||
|
if (lpv == NULL || dwAppId == 0)
|
||
|
return(DPNERR_INVALIDPARAM);
|
||
|
if (dwFlags != 0)
|
||
|
return(DPNERR_INVALIDFLAGS);
|
||
|
}
|
||
|
EXCEPT (EXCEPTION_EXECUTE_HANDLER)
|
||
|
{
|
||
|
DPFERR("Exception encountered validating parameters");
|
||
|
return(DPNERR_INVALIDPARAM);
|
||
|
}
|
||
|
|
||
|
lpdnLobbyObject = (LPDIRECTNETLOBBYOBJECT) GET_OBJECT_FROM_INTERFACE(lpv);
|
||
|
|
||
|
// Ensure app ID is valid, and then retrieve connection (message queue) information for it
|
||
|
if (!IsHandleValid(lpdnLobbyObject->lphsApplicationHandles,dwAppId))
|
||
|
{
|
||
|
hResultCode = DPNERR_INVALIDPARAM;
|
||
|
goto EXIT_DNL_SendToApplication;
|
||
|
}
|
||
|
if ((hResultCode = RetrieveHandleData(lpdnLobbyObject->lphsApplicationHandles,dwAppId,
|
||
|
(LPVOID *)&lpdnAppHandleEntry)) != DPN_OK)
|
||
|
{
|
||
|
goto EXIT_DNL_SendToApplication;
|
||
|
}
|
||
|
|
||
|
dwMsgLen = sizeof(DN_MESSAGE_STRUCT);
|
||
|
if (dwTagLen > 0) // Create buffer if there are tags
|
||
|
{
|
||
|
dwMsgLen += dwTagLen;
|
||
|
if ((lpdnMessage = (LPDN_MESSAGE_STRUCT)GLOBALALLOC(dwMsgLen)) == NULL)
|
||
|
{
|
||
|
hResultCode = DPNERR_OUTOFMEMORY;
|
||
|
goto EXIT_DNL_SendToApplication;
|
||
|
}
|
||
|
}
|
||
|
else // Use default static buffer (avoid malloc call)
|
||
|
{
|
||
|
lpdnMessage = &dnMessage;
|
||
|
}
|
||
|
lpdnMessage->dwMessageType = dwMessageId;
|
||
|
lpdnMessage->dwUserToken = dwUserToken;
|
||
|
lpdnMessage->dwParam1 = dwParam1;
|
||
|
lpdnMessage->dwParam2 = dwParam2;
|
||
|
lpdnMessage->dwParam3 = dwParam3;
|
||
|
lpdnMessage->dwParam4 = dwParam4;
|
||
|
lpdnMessage->dwTagLen = dwTagLen;
|
||
|
if (dwTagLen > 0)
|
||
|
{
|
||
|
memcpy(&(lpdnMessage->cTags),lpvTags,dwTagLen);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
lpdnMessage->cTags = '\0';
|
||
|
}
|
||
|
|
||
|
// Send message
|
||
|
hResultCode = DnSendMsg(lpdnAppHandleEntry->lpmqSendMsgQueue,lpdnMessage,dwMsgLen,FALSE,
|
||
|
(DWORD)NULL);
|
||
|
|
||
|
// Free buffer if it was malloc'd
|
||
|
if (dwTagLen > 0)
|
||
|
GlobalFree(lpdnMessage);
|
||
|
|
||
|
EXIT_DNL_SendToApplication:
|
||
|
|
||
|
return(hResultCode);
|
||
|
}
|
||
|
|
||
|
|
||
|
// HRESULT DNL_ReleaseApplication
|
||
|
// LPVOID lpv Interface pointer
|
||
|
// DWORD dwAppId Application ID to release
|
||
|
//
|
||
|
// Returns
|
||
|
// DPN_OK If the message was sent without incident
|
||
|
// DPNERR_INVALIDPARAM If there was an invalid parameter
|
||
|
// DPNERR_INVALIDHANDLE If the application ID is not valid
|
||
|
// DPNERR_GENERIC If there were any problems
|
||
|
//
|
||
|
// Notes
|
||
|
// This function causes the lobby client to terminate it's association with a lobbied
|
||
|
// application (whether the lobby client launched the application or not). This allows the
|
||
|
// application to either terminate gracefully (if still running) and then be made available
|
||
|
// for re-connection to another (or the same) lobby client later. This should be called
|
||
|
// whenever a lobby client is finished with a lobbied application.
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "DNL_ReleaseApplication"
|
||
|
|
||
|
STDMETHODIMP DNL_ReleaseApplication(LPVOID lpv,DWORD dwAppId)
|
||
|
{
|
||
|
LPDIRECTNETLOBBYOBJECT lpdnLobbyObject;
|
||
|
HRESULT hResultCode = DPN_OK;
|
||
|
LPDN_APP_HANDLE_ENTRY lph;
|
||
|
|
||
|
DPFX(DPFPREP, 9,"Parameters: lpv [%lx], dwAppId [%lx]", lpv,dwAppId);
|
||
|
|
||
|
// Parameter validation
|
||
|
TRY
|
||
|
{
|
||
|
if (lpv == NULL || dwAppId == 0)
|
||
|
return(DPNERR_INVALIDPARAM);
|
||
|
}
|
||
|
EXCEPT (EXCEPTION_EXECUTE_HANDLER)
|
||
|
{
|
||
|
DPFERR("Exception encountered validating parameters");
|
||
|
return(DPNERR_INVALIDPARAM);
|
||
|
}
|
||
|
|
||
|
lpdnLobbyObject = (LPDIRECTNETLOBBYOBJECT) GET_OBJECT_FROM_INTERFACE(lpv);
|
||
|
|
||
|
// TODO - wait for messages to be processed
|
||
|
|
||
|
if ((hResultCode = RetrieveHandleData(lpdnLobbyObject->lphsApplicationHandles,dwAppId,&lph)) == DPN_OK)
|
||
|
{
|
||
|
if ((hResultCode = DnCloseMsgQueue(lph->lpmqSendMsgQueue)) == DPN_OK)
|
||
|
{
|
||
|
if ((hResultCode = DestroyHandle(lpdnLobbyObject->lphsApplicationHandles,dwAppId)) == DPN_OK)
|
||
|
{
|
||
|
}
|
||
|
}
|
||
|
GlobalFree(lph);
|
||
|
}
|
||
|
|
||
|
//EXIT_DNL_ReleaseApplication:
|
||
|
|
||
|
return(hResultCode);
|
||
|
}
|
||
|
|
||
|
#endif
|