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.
4241 lines
112 KiB
4241 lines
112 KiB
/*==========================================================================
|
|
*
|
|
* Copyright (C) 2000-2002 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
* File: Async.cpp
|
|
* Content: Async operation FPM routines
|
|
*@@BEGIN_MSINTERNAL
|
|
* History:
|
|
* Date By Reason
|
|
* ==== == ======
|
|
* 07/27/99 mjn Created
|
|
* 12/23/99 mjn Added HOST_MIGRATE functionality
|
|
* 12/28/99 mjn Moved Async Op stuff to Async.h
|
|
* 12/29/99 mjn Reformed DN_ASYNC_OP to use hParentOp instead of lpvUserContext
|
|
* 01/06/00 mjn Moved NameTable stuff to NameTable.h
|
|
* 01/07/00 mjn Handle NULL buffer descriptors during sends
|
|
* 01/09/00 mjn Transfer Application Description at connect
|
|
* 01/10/00 mjn Added support for DN_MSG_INTERNAL_UPDATE_APPLICATION_DESC
|
|
* 01/12/00 jtk Added simple code to handle enum and enum response messages.
|
|
* 01/11/00 mjn Moved AppDesc stuff to AppDesc.h
|
|
* Moved connect/disconnect stuff to Connect.h
|
|
* 01/14/00 mjn Added pvUserContext to DN_PerformListen
|
|
* 01/15/00 mjn Replaced DN_COUNT_BUFFER with CRefCountBuffer
|
|
* 01/16/00 mjn Moved User callback stuff to User.h
|
|
* 01/17/00 mjn Fixed ConnectToPeer function names
|
|
* 01/17/00 mjn Implemented send time
|
|
* 01/19/00 mjn Fixed Parent Op refCount bug in MultiSend
|
|
* 01/19/00 mjn Replaced DN_SYNC_EVENT with CSyncEvent
|
|
* 01/20/00 mjn Route NameTable operations through NameTable operation list
|
|
* 01/21/00 mjn Added DNProcessInternalOperation
|
|
* 01/23/00 mjn Added support for DN_MSG_INTERNAL_HOST_DESTROY_PLAYER
|
|
* 01/24/00 mjn Added support for DN_MSG_INTERNAL_NAMETABLE_VERSION
|
|
* and DN_MSG_INTERNAL_RESYNC_VERSION
|
|
* 01/25/00 mjn Added support for DN_MSG_INTERNAL_HOST_MIGRATE_COMPLETE
|
|
* 01/26/00 mjn Implemented NameTable re-sync at host migration
|
|
* 01/27/00 mjn Cleaned up switch/case statements
|
|
* 01/27/00 mjn Added support for retention of receive buffers
|
|
* 02/09/00 mjn Implemented DNSEND_COMPLETEONPROCESS
|
|
* 02/18/00 mjn Converted DNADDRESS to IDirectPlayAddress8
|
|
* 03/23/00 mjn Added phrSync and pvInternal
|
|
* 03/24/00 mjn Add guidSP to DN_ASYNC_OP
|
|
* 03/25/00 rmt Added code to unregister ourselves when listens are terminated
|
|
* 04/04/00 rmt Added check for DPNSVR disable before attempting to unregister
|
|
* 04/04/00 mjn Added DNProcessTerminateSession and related code
|
|
* 04/06/00 rmt Added code to complete nocopy voice sends
|
|
* 04/10/00 mjn Use CAsyncOp for CONNECTs, LISTENs and DISCONNECTs
|
|
* 04/11/00 mjn Use CAsyncOp for ENUMs
|
|
* 04/13/00 mjn Use Protocol Interface VTBL (replaces some functions)
|
|
* 04/14/00 mjn DNPerformListen performs synchronous LISTENs
|
|
* 04/16/00 mjn Use CAsyncOp for SENDs
|
|
* 04/17/00 mjn Replaced BUFFERDESC with DPN_BUFFER_DESC
|
|
* 04/17/00 mjn Added DNCompleteAsyncHandle
|
|
* 04/20/00 mjn DNPerformChildSend set's child op flags to the parent's op flags
|
|
* 04/21/00 mjn Added DNPerformDisconnect
|
|
* 04/23/00 mjn Optionally return child AsyncOp in DNPerformChildSend()
|
|
* mjn Removed DNSendCompleteOnProcess (better implementation)
|
|
* 04/24/00 mjn Added DNCreateUserHandle()
|
|
* 04/26/00 mjn Removed DN_ASYNC_OP and related functions
|
|
* 04/28/00 mjn Clear unused buffer descriptions in DN_SendTo()
|
|
* 05/02/00 mjn Keep a reference on the Connection during SEND's
|
|
* 05/05/00 mjn Return DPN_OK from DNReceiveCompleteOnProcess() to prevent holding the buffer
|
|
* 05/08/00 vpo Removed asserts when protocol returns non PENDING
|
|
* 06/05/00 mjn Removed assert in DNSendMessage
|
|
* 06/21/00 mjn Modified DNSendMessage() and DNCreateSendParent() to use protocol voice bit
|
|
* 06/24/00 mjn Added CONNECT completions and fixed DN_MSG_INTERNAL_CONNECT_FAILED processing
|
|
* mjn Added code to process DN_MSG_INTERNAL_INSTRUCTED_CONNECT_FAILED
|
|
* 07/02/00 mjn Added DNSendGroupMessage() *@@END_MSINTERNAL
|
|
* 07/05/00 mjn Removed references to DN_MSG_INTERNAL_ENUM_WITH_APPLICATION_GUID,DN_MSG_INTERNAL_ENUM,DN_MSG_INTERNAL_ENUM_RESPONSE
|
|
* 07/06/00 mjn Only use CONNECTED connections in group sends
|
|
* mjn Use SP handle instead of interface
|
|
* 07/10/00 mjn Added DNPerformEnumQuery()
|
|
* mjn Correctly flag parent ops in groups sends and added DPNIDs to async ops for better tracking
|
|
* 07/11/00 mjn Added fNoLoopBack to DNSendGroupMessage()
|
|
* mjn Added DNPerformNextEnumQuery(),DNPerformSPListen(),DNPerformNextListen(),DNEnumAdapterGuids(),DNPerformNextConnect()
|
|
* 07/20/00 mjn Fixed DN_TerminateAllListens() to better use locks
|
|
* mjn Fixed connect completions and added DNCompleteConnectOperation() and DNCompleteSendConnectInfo()
|
|
* mjn Changed DNPerformDisconnect() to take a CConnection and hEndPt
|
|
* mjn Revamped CONNECT process and associated refcounts
|
|
* 07/21/00 mjn Process DN_MSG_INTERNAL_CONNECT_ATTEMPT_FAILED
|
|
* 07/25/00 mjn Save result code on parent only if failure in DNCompleteSendConnectInfo()
|
|
* 07/26/00 mjn DNPerformSPListen() fails if no valid device adapters
|
|
* 07/26/00 mjn Fixed locking problem with CAsyncOp::MakeChild()
|
|
* 07/28/00 mjn Added code to track send queue info on CConnection
|
|
* 07/29/00 mjn Use DNUserConnectionTerminated() rather than DN_TerminateSession()
|
|
* mjn Added HRESULT to DNUserReturnBuffer()
|
|
* mjn Added fUseCachedCaps to DN_SPEnsureLoaded()
|
|
* 07/30/00 mjn Use DNUserTerminateSession() rather than DNUserConnectionTerminated()
|
|
* 07/31/00 mjn Removed DN_MSG_INTERNAL_HOST_DESTROY_PLAYER
|
|
* 07/31/00 mjn Change DN_MSG_INTERNAL_DELETE_PLAYER to DN_MSG_INTERNAL_DESTROY_PLAYER
|
|
* 08/02/00 mjn Added dwFlags to DNReceiveUserData()
|
|
* 08/05/00 RichGr IA64: Use %p format specifier in DPFs for 32/64-bit pointers and handles.
|
|
* 08/05/00 mjn Added pParent to DNSendGroupMessage and DNSendMessage()
|
|
* mjn Ensure cancelled operations don't proceed
|
|
* mjn Added m_bilinkActiveList to CAsyncOp
|
|
* mjn Added fInternal to DNPerformChildSend()
|
|
* mjn Removed DN_TerminateAllListens()
|
|
* mjn Added DNProcessFailedRequest()
|
|
* mjn Added DNCompleteRequest()
|
|
* 08/07/00 mjn Added code to handle peer-peer integrity checking
|
|
* 08/08/00 mjn Perform LISTENs on worker thread in DNPerformNextListen()
|
|
* 08/14/00 mjn Handle failed LISTENs in DNPerformListen()
|
|
* 08/15/00 mjn Changed registration with DPNSVR
|
|
* mjn Allow NULL CConnection object pointer for DNPerformRequest()
|
|
* 08/20/00 mjn Removed fUseCachedCaps from DN_SPEnsureLoaded()
|
|
* 08/24/00 mjn Replace DN_NAMETABLE_OP with CNameTableOp
|
|
* mjn DN_MSG_INTERNAL_INSTRUCT_CONNECT gets routed through DNNTAddOperation() in DNProcessInternalOperation()
|
|
* 08/31/00 mjn Release DirectNetLock for failure cases in DNPerformRequest()
|
|
* 09/04/00 mjn Added CApplicationDesc
|
|
* 09/06/00 mjn Fixed register with DPNSVR problem
|
|
* 09/14/2000 rmt Bug #44625: DPLAY8: CORE: Multihomed machines cannot always be enumerated
|
|
* Moved registration to after point where listen completes.
|
|
* 09/14/00 mjn AddRef Protocol refcount when invoking protocol
|
|
* 09/21/00 mjn Allow NULL CConnection in DNPerformDisconnect()
|
|
* 09/23/00 mjn Added CSyncEvent to DN_LISTEN_OP_DATA
|
|
* 09/27/00 mjn Inform lobby of successfull connects from DNCompleteConnectOperation()
|
|
* 10/11/00 mjn Save protocol handle on AsyncOp earlier in DNPerformListen()
|
|
* mjn Clean up DirectNet object in failure cases in DNCompleteConnectToHost() and DNCompleteSendConnectInfo()
|
|
* 10/17/00 mjn Fixed clean up for unreachable players
|
|
* 12/11/00 mjn Added verification of internal messages
|
|
* 01/10/01 mjn DNCompleteUserConnect() cancels ENUMs with DPNERR_CONNECTING
|
|
* 01/22/01 mjn Set connection as INVALID in DNPerformDisconnect()
|
|
* 01/25/01 mjn Fixed 64-bit alignment problem in received messages
|
|
* 01/30/00 mjn Avoid sending requests during host migration in DNPerformRequest()
|
|
* 02/11/01 mjn Allow complete on process requests during host migration in DNPerformRequest()
|
|
* mjn Fixed CConnection::GetEndPt() to track calling thread
|
|
* 03/30/01 mjn Changes to prevent multiple loading/unloading of SP's
|
|
* 04/05/01 mjn Added DPNID parameter to DNProcessHostMigration3()
|
|
* 04/11/01 mjn Propegate LISTEN flags from listen parents in DNPerformSPListen() and DNPerformListen()
|
|
* 04/13/01 mjn Add requests to the request list in DNPerformRequest()
|
|
* Remove requests from request list in DNCompleteSendRequest() and DNReceiveCompleteOnProcessReply()
|
|
* 05/07/01 vpo Whistler 384350: "DPLAY8: CORE: Messages from server can be indicated before connect completes"
|
|
* 05/11/01 mjn Ensure sends not canceled before storing protocol handle in DNSendMessage()
|
|
* 05/14/01 mjn Fix client error handling when completing connect if server not available
|
|
* 05/17/01 mjn Track number of threads performing NameTable operations
|
|
* 05/22/01 mjn Properly set DirectNetObject as CONNECTED for successful client connect
|
|
* 05/23/01 mjn Prevent LISTEN's from being cancelled before completing in DNPerformListen()
|
|
* 06/03/01 mjn Make DISCONNECT's children of failed connect's in DNPerformDisconnect()
|
|
* 06/08/01 mjn Disconnect connection to host if connect was rejected in DNConnectToHostFailed()
|
|
* 07/22/01 mjn Added DPNBUILD_NOHOSTMIGRATE compile flag
|
|
*@@END_MSINTERNAL
|
|
*
|
|
***************************************************************************/
|
|
|
|
#include "dncorei.h"
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNCreateUserHandle"
|
|
|
|
HRESULT DNCreateUserHandle(DIRECTNETOBJECT *const pdnObject,
|
|
CAsyncOp **const ppAsyncOp)
|
|
{
|
|
HRESULT hResultCode;
|
|
CAsyncOp *pAsyncOp;
|
|
DPNHANDLE handle;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: ppAsyncOp [0x%p]",ppAsyncOp);
|
|
|
|
DNASSERT(pdnObject != NULL);
|
|
DNASSERT(ppAsyncOp != NULL);
|
|
|
|
pAsyncOp = NULL;
|
|
|
|
if ((hResultCode = AsyncOpNew(pdnObject,&pAsyncOp)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not create AsyncOp");
|
|
DisplayDNError(0,hResultCode);
|
|
goto Failure;
|
|
}
|
|
pAsyncOp->SetOpType( ASYNC_OP_USER_HANDLE );
|
|
pAsyncOp->MakeParent();
|
|
|
|
if ((hResultCode = pdnObject->HandleTable.Create(pAsyncOp, &handle)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not create Handle");
|
|
DisplayDNError(0,hResultCode);
|
|
goto Failure;
|
|
}
|
|
else
|
|
{
|
|
// Add a reference for the HandleTable
|
|
pAsyncOp->AddRef();
|
|
pAsyncOp->Lock();
|
|
pAsyncOp->SetHandle(handle);
|
|
pAsyncOp->Unlock();
|
|
}
|
|
|
|
pAsyncOp->AddRef();
|
|
*ppAsyncOp = pAsyncOp;
|
|
|
|
pAsyncOp->Release();
|
|
pAsyncOp = NULL;
|
|
|
|
hResultCode = DPN_OK;
|
|
|
|
Exit:
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx] (handle = 0x%p)",hResultCode,*ppAsyncOp);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (pAsyncOp)
|
|
{
|
|
pAsyncOp->Release();
|
|
pAsyncOp = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
#ifndef DPNBUILD_ONLYONEADAPTER
|
|
|
|
// DNEnumAdapterGuids
|
|
//
|
|
// Generate a list of adapter GUIDs for multiple ENUMs,LISTENs,CONNECTs
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNEnumAdapterGuids"
|
|
|
|
HRESULT DNEnumAdapterGuids(DIRECTNETOBJECT *const pdnObject,
|
|
GUID *const pguidSP,
|
|
const DWORD dwMatchFlags,
|
|
GUID **const ppAdapterList,
|
|
DWORD *const pdwNumAdapters)
|
|
{
|
|
HRESULT hResultCode;
|
|
GUID *pguid;
|
|
DWORD dw;
|
|
DWORD dwAdapterBufferSize;
|
|
DWORD dwAdapterBufferCount;
|
|
DWORD dwNumAdapters;
|
|
void *pvAdapterBuffer;
|
|
void *pvBlock;
|
|
DPN_SERVICE_PROVIDER_INFO *pSPInfo;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: pguidSP [0x%p], dwMatchFlags [0x%x], ppAdapterList [0x%p], pdwNumAdapters [0x%p]",
|
|
pguidSP,dwMatchFlags,ppAdapterList,pdwNumAdapters);
|
|
|
|
DNASSERT(pdnObject != NULL);
|
|
DNASSERT(ppAdapterList != NULL);
|
|
DNASSERT(pdwNumAdapters != NULL);
|
|
|
|
pvBlock = NULL;
|
|
pvAdapterBuffer = NULL;
|
|
dwAdapterBufferSize = 0;
|
|
dwAdapterBufferCount = 0;
|
|
dwNumAdapters = 0;
|
|
|
|
hResultCode = DN_EnumAdapters( pdnObject,
|
|
0,
|
|
pguidSP,
|
|
NULL,
|
|
reinterpret_cast<DPN_SERVICE_PROVIDER_INFO*>(pvAdapterBuffer),
|
|
&dwAdapterBufferSize,
|
|
&dwAdapterBufferCount);
|
|
if ((hResultCode == DPNERR_BUFFERTOOSMALL) && (dwAdapterBufferSize > 0))
|
|
{
|
|
if ((pvAdapterBuffer = DNMalloc(dwAdapterBufferSize)) == NULL)
|
|
{
|
|
DPFERR("Could not allocate space for adapter list");
|
|
hResultCode = DPNERR_OUTOFMEMORY;
|
|
goto Failure;
|
|
}
|
|
|
|
if ((pvBlock = MemoryBlockAlloc(pdnObject,dwAdapterBufferCount * sizeof(GUID))) == NULL)
|
|
{
|
|
DPFERR("Could not allocate MemoryBlock");
|
|
hResultCode = DPNERR_OUTOFMEMORY;
|
|
goto Failure;
|
|
}
|
|
|
|
pguid = reinterpret_cast<GUID*>(pvBlock);
|
|
|
|
hResultCode = DN_EnumAdapters( pdnObject,
|
|
0,
|
|
pguidSP,
|
|
NULL,
|
|
reinterpret_cast<DPN_SERVICE_PROVIDER_INFO*>(pvAdapterBuffer),
|
|
&dwAdapterBufferSize,
|
|
&dwAdapterBufferCount);
|
|
if (hResultCode != DPN_OK)
|
|
{
|
|
DPFERR("Could not enumerate adapters");
|
|
DisplayDNError(0,hResultCode);
|
|
goto Failure;
|
|
}
|
|
DPFX(DPFPREP, 7,"dwAdapterBufferCount [%ld]",dwAdapterBufferCount);
|
|
|
|
pSPInfo = reinterpret_cast<DPN_SERVICE_PROVIDER_INFO*>(pvAdapterBuffer);
|
|
for ( dw = 0 ; dw < dwAdapterBufferCount ; dw++ )
|
|
{
|
|
static const GUID InvalidGuid = { 0 };
|
|
|
|
if (!memcmp(&InvalidGuid,&pSPInfo->guid,sizeof(GUID)))
|
|
{
|
|
DPFX(DPFPREP, 1,"Ignoring invalid adapter GUID %u.",dw);
|
|
pSPInfo++;
|
|
continue;
|
|
}
|
|
|
|
if ((dwMatchFlags != 0) && (! (pSPInfo->dwFlags & dwMatchFlags)))
|
|
{
|
|
DPFX(DPFPREP, 1,"Ignoring adapter %u with invalid flags (0x%x doesn't match 0x%x).",dw,pSPInfo->dwFlags,dwMatchFlags);
|
|
pSPInfo++;
|
|
continue;
|
|
}
|
|
|
|
memcpy(pguid,&pSPInfo->guid,sizeof(GUID));
|
|
pguid++;
|
|
dwNumAdapters++;
|
|
pSPInfo++;
|
|
}
|
|
DNFree(pvAdapterBuffer);
|
|
pvAdapterBuffer = NULL;
|
|
|
|
DPFX(DPFPREP, 7,"Number of adapters [%ld]",dwNumAdapters);
|
|
}
|
|
|
|
*pdwNumAdapters = dwNumAdapters;
|
|
*ppAdapterList = reinterpret_cast<GUID*>(pvBlock);
|
|
pvBlock = NULL;
|
|
|
|
hResultCode = DPN_OK;
|
|
|
|
Exit:
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (pvBlock)
|
|
{
|
|
MemoryBlockFree(pdnObject,pvBlock);
|
|
pvBlock = NULL;
|
|
}
|
|
if (pvAdapterBuffer)
|
|
{
|
|
DNFree(pvAdapterBuffer);
|
|
pvAdapterBuffer = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
#endif // ! DPNBUILD_ONLYONEADAPTER
|
|
|
|
|
|
// DNPerformSPListen
|
|
//
|
|
// LISTEN on a particular SP
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNPerformSPListen"
|
|
|
|
HRESULT DNPerformSPListen(DIRECTNETOBJECT *const pdnObject,
|
|
IDirectPlay8Address *const pDeviceAddr,
|
|
CAsyncOp *const pListenParent,
|
|
CAsyncOp **const ppParent)
|
|
{
|
|
HRESULT hResultCode;
|
|
CAsyncOp *pParent;
|
|
#ifndef DPNBUILD_ONLYONESP
|
|
GUID guidSP;
|
|
#endif // ! DPNBUILD_ONLYONESP
|
|
#ifndef DPNBUILD_ONLYONEADAPTER
|
|
GUID guidAdapter;
|
|
BOOL fEnumAdapters;
|
|
DWORD dwMatchFlags;
|
|
#endif // ! DPNBUILD_ONLYONEADAPTER
|
|
DPN_SP_CAPS dnSPCaps;
|
|
CServiceProvider *pSP;
|
|
CSyncEvent *pSyncEvent;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: pDeviceAddr [0x%p], pListenParent [0x%p], ppParent [0x%p]",
|
|
pDeviceAddr,pListenParent,ppParent);
|
|
|
|
DNASSERT(pdnObject != NULL);
|
|
|
|
pParent = NULL;
|
|
pSP = NULL;
|
|
pSyncEvent = NULL;
|
|
|
|
#if ((defined(DPNBUILD_ONLYONESP)) && (defined(DPNBUILD_LIBINTERFACE)))
|
|
DNASSERT(pdnObject->pOnlySP != NULL);
|
|
pdnObject->pOnlySP->AddRef();
|
|
pSP = pdnObject->pOnlySP;
|
|
#else // ! DPNBUILD_ONLYONESP or ! DPNBUILD_LIBINTERFACE
|
|
#ifndef DPNBUILD_ONLYONESP
|
|
//
|
|
// Extract SP guid as we will probably need it
|
|
//
|
|
if ((hResultCode = IDirectPlay8Address_GetSP(pDeviceAddr,&guidSP)) != DPN_OK)
|
|
{
|
|
DPFERR("SP not specified in Device address");
|
|
DisplayDNError(0,hResultCode);
|
|
goto Failure;
|
|
}
|
|
#endif // ! DPNBUILD_ONLYONESP
|
|
|
|
//
|
|
// Ensure SP specified in Device address is loaded
|
|
//
|
|
hResultCode = DN_SPEnsureLoaded(pdnObject,
|
|
#ifndef DPNBUILD_ONLYONESP
|
|
&guidSP,
|
|
#endif // ! DPNBUILD_ONLYONESP
|
|
#ifndef DPNBUILD_LIBINTERFACE
|
|
NULL,
|
|
#endif // ! DPNBUILD_LIBINTERFACE
|
|
&pSP);
|
|
if (hResultCode != DPN_OK)
|
|
{
|
|
DPFERR("Could not ensure SP is loaded!" );
|
|
DisplayDNError(0,hResultCode);
|
|
goto Failure;
|
|
}
|
|
#endif // ! DPNBUILD_ONLYONESP or ! DPNBUILD_LIBINTERFACE
|
|
|
|
//
|
|
// Get SP caps (to later see if we can ENUM on all adapters)
|
|
//
|
|
if ((hResultCode = DNGetActualSPCaps(pSP,&dnSPCaps)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not get SP caps");
|
|
DisplayDNError(0,hResultCode);
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Create a parent op for LISTENs on this SP
|
|
//
|
|
if ((hResultCode = AsyncOpNew(pdnObject,&pParent)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not create SP parent listen op");
|
|
DisplayDNError(0,hResultCode);
|
|
goto Failure;
|
|
}
|
|
pParent->SetOpType( pListenParent->GetOpType() );
|
|
pParent->SetCompletion( DNCompleteListen );
|
|
pParent->SetOpFlags( pListenParent->GetOpFlags() );
|
|
pParent->MakeParent();
|
|
|
|
if (pListenParent)
|
|
{
|
|
pListenParent->Lock();
|
|
if (pListenParent->IsCancelled())
|
|
{
|
|
pListenParent->Unlock();
|
|
pParent->SetResult( DPNERR_USERCANCEL );
|
|
hResultCode = DPNERR_USERCANCEL;
|
|
goto Failure;
|
|
}
|
|
pParent->MakeChild( pListenParent );
|
|
pListenParent->Unlock();
|
|
}
|
|
|
|
//
|
|
// Set SP on parent
|
|
//
|
|
pParent->SetSP( pSP );
|
|
|
|
#ifndef DPNBUILD_ONLYONEADAPTER
|
|
//
|
|
// If there is no adapter specified in the device address,
|
|
// we will attempt to enum on each individual adapter if the SP supports it
|
|
//
|
|
fEnumAdapters = FALSE;
|
|
dwMatchFlags = 0;
|
|
if ((hResultCode = IDirectPlay8Address_GetDevice( pDeviceAddr, &guidAdapter )) != DPN_OK)
|
|
{
|
|
DPFX(DPFPREP,1,"Could not determine adapter");
|
|
DisplayDNError(1,hResultCode);
|
|
|
|
if (dnSPCaps.dwFlags & DPNSPCAPS_SUPPORTSALLADAPTERS)
|
|
{
|
|
DPFX(DPFPREP, 3,"SP supports ENUMing on all adapters");
|
|
fEnumAdapters = TRUE;
|
|
|
|
#ifndef DPNBUILD_NOMULTICAST
|
|
//
|
|
// For multicast listens where the user did not specify an adapter,
|
|
// let the SP tell us which of its devices would be the best to use.
|
|
//
|
|
if (pListenParent->GetOpType() == ASYNC_OP_LISTEN_MULTICAST)
|
|
{
|
|
dwMatchFlags = DPNSPINFO_DEFAULTMULTICASTDEVICE;
|
|
}
|
|
#endif // ! DPNBUILD_NOMULTICAST
|
|
}
|
|
}
|
|
#endif // ! DPNBUILD_ONLYONEADAPTER
|
|
|
|
pSP->Release();
|
|
pSP = NULL;
|
|
|
|
#ifndef DPNBUILD_ONLYONEADAPTER
|
|
if(fEnumAdapters)
|
|
{
|
|
DWORD dwNumAdapters;
|
|
GUID *pAdapterList = NULL;;
|
|
DN_LISTEN_OP_DATA *pListenOpData = NULL;
|
|
|
|
if ((hResultCode = DNEnumAdapterGuids( pdnObject,
|
|
&guidSP,
|
|
dwMatchFlags,
|
|
&pAdapterList,
|
|
&dwNumAdapters)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not enum adapters for this SP");
|
|
DisplayDNError(0,hResultCode);
|
|
goto Failure;
|
|
}
|
|
if (dwNumAdapters == 0)
|
|
{
|
|
DPFERR("No adapters were found for this SP");
|
|
hResultCode = DPNERR_INVALIDDEVICEADDRESS;
|
|
goto Failure;
|
|
}
|
|
|
|
pListenOpData = pParent->GetLocalListenOpData();
|
|
pListenOpData->dwNumAdapters = dwNumAdapters;
|
|
pListenOpData->dwCurrentAdapter = 0;
|
|
pListenOpData->dwCompleteAdapters = 0;
|
|
|
|
//
|
|
// Choose first adapter for initial LISTEN call
|
|
//
|
|
if ((hResultCode = IDirectPlay8Address_SetDevice(pDeviceAddr,pAdapterList)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not set device adapter");
|
|
DisplayDNError(0,hResultCode);
|
|
MemoryBlockFree(pdnObject,pAdapterList);
|
|
goto Failure;
|
|
}
|
|
pListenOpData->dwCurrentAdapter++;
|
|
pParent->SetOpData( pAdapterList );
|
|
pAdapterList = NULL;
|
|
|
|
//
|
|
// Create a SyncEvent for multiple LISTENs
|
|
//
|
|
if ((hResultCode = SyncEventNew(pdnObject,&pSyncEvent)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not create SyncEvent");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(FALSE);
|
|
goto Failure;
|
|
}
|
|
pListenOpData->pSyncEvent = pSyncEvent;
|
|
pListenOpData = NULL;
|
|
}
|
|
#endif // ! DPNBUILD_ONLYONEADAPTER
|
|
|
|
|
|
hResultCode = DNPerformListen(pdnObject,pDeviceAddr,pParent);
|
|
if (hResultCode != DPNERR_PENDING)
|
|
{
|
|
DPFERR("Could not perform LISTEN");
|
|
DisplayDNError(0,hResultCode);
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// If there is a SyncEvent, wait for it to be set and then return it
|
|
//
|
|
if (pSyncEvent)
|
|
{
|
|
pSyncEvent->WaitForEvent();
|
|
pSyncEvent->ReturnSelfToPool();
|
|
pSyncEvent = NULL;
|
|
}
|
|
|
|
//
|
|
// Save enum frame size
|
|
//
|
|
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
|
|
if ((pdnObject->dwMaxFrameSize == 0) || (pdnObject->dwMaxFrameSize > (dnSPCaps.dwMaxEnumPayloadSize + sizeof(DN_ENUM_QUERY_PAYLOAD))))
|
|
{
|
|
pdnObject->dwMaxFrameSize = dnSPCaps.dwMaxEnumPayloadSize + sizeof(DN_ENUM_QUERY_PAYLOAD);
|
|
}
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
|
|
if (ppParent)
|
|
{
|
|
pParent->AddRef();
|
|
*ppParent = pParent;
|
|
}
|
|
|
|
pParent->Release();
|
|
pParent = NULL;
|
|
|
|
hResultCode = DPN_OK;
|
|
|
|
Exit:
|
|
DNASSERT( pParent == NULL );
|
|
DNASSERT( pSP == NULL );
|
|
DNASSERT( pSyncEvent == NULL );
|
|
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (pParent)
|
|
{
|
|
pParent->Release();
|
|
pParent = NULL;
|
|
}
|
|
if (pSP)
|
|
{
|
|
pSP->Release();
|
|
pSP = NULL;
|
|
}
|
|
if (pSyncEvent)
|
|
{
|
|
pSyncEvent->ReturnSelfToPool();
|
|
pSyncEvent = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
// DNPerformListen
|
|
//
|
|
// IDirectPlayAddress8 *pDeviceInfo
|
|
// CAsyncOp *pParent
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNPerformListen"
|
|
|
|
HRESULT DNPerformListen(DIRECTNETOBJECT *const pdnObject,
|
|
IDirectPlay8Address *const pDeviceInfo,
|
|
CAsyncOp *const pParent)
|
|
{
|
|
HANDLE hProtocol;
|
|
HRESULT hResultCode;
|
|
CAsyncOp *pAsyncOp;
|
|
CSyncEvent *pSyncEvent;
|
|
HRESULT hrListen;
|
|
#ifdef DBG
|
|
TCHAR DP8ABuffer[512] = {0};
|
|
DWORD DP8ASize = 512;
|
|
#endif // DBG
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: pDeviceInfo [0x%p], pParent [0x%p]",pDeviceInfo,pParent);
|
|
|
|
DNASSERT(pdnObject != NULL);
|
|
DNASSERT(pParent != NULL);
|
|
|
|
hProtocol = NULL;
|
|
pAsyncOp = NULL;
|
|
pSyncEvent = NULL;
|
|
|
|
// Try an initial check (might get lucky :)
|
|
if (!(pdnObject->dwFlags & DN_OBJECT_FLAG_INITIALIZED))
|
|
{
|
|
DPFERR("Not initialized");
|
|
return(DPNERR_UNINITIALIZED);
|
|
}
|
|
|
|
#ifdef DBG
|
|
IDirectPlay8Address_GetURL(pDeviceInfo,DP8ABuffer,&DP8ASize);
|
|
DPFX(DPFPREP, 7,"Device Info [%s]",DP8ABuffer);
|
|
#endif // DBG
|
|
//
|
|
// Set up for LISTEN
|
|
//
|
|
|
|
// HRESULT
|
|
hrListen = DPNERR_GENERIC;
|
|
|
|
// SyncEvent
|
|
if ((hResultCode = SyncEventNew(pdnObject,&pSyncEvent)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not create SyncEvent");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(FALSE);
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Async op for LISTEN
|
|
//
|
|
if ((hResultCode = AsyncOpNew(pdnObject,&pAsyncOp)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not create AsyncOp");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(FALSE);
|
|
goto Failure;
|
|
}
|
|
pAsyncOp->SetOpType( pParent->GetOpType() );
|
|
pAsyncOp->SetSyncEvent(pSyncEvent);
|
|
pAsyncOp->SetResultPointer( &hrListen );
|
|
|
|
//
|
|
// We will set the LISTEN as not cancellable (part of our contract with the Protocol)
|
|
// We will set it as cancellable if the LISTEN completes successfully.
|
|
//
|
|
pAsyncOp->SetCannotCancel();
|
|
|
|
pParent->Lock();
|
|
if (pParent->IsCancelled())
|
|
{
|
|
pParent->Unlock();
|
|
pAsyncOp->SetResult( DPNERR_USERCANCEL );
|
|
hResultCode = DPNERR_USERCANCEL;
|
|
goto Failure;
|
|
}
|
|
pAsyncOp->MakeChild(pParent);
|
|
pParent->Unlock();
|
|
|
|
//
|
|
// Add to active async op list
|
|
//
|
|
DNEnterCriticalSection(&pdnObject->csActiveList);
|
|
pAsyncOp->m_bilinkActiveList.InsertBefore(&pdnObject->m_bilinkActiveList);
|
|
DNLeaveCriticalSection(&pdnObject->csActiveList);
|
|
|
|
//
|
|
// Perform LISTEN
|
|
//
|
|
pAsyncOp->AddRef();
|
|
hResultCode = DNPListen(pdnObject->pdnProtocolData,
|
|
pDeviceInfo,
|
|
pParent->GetSP()->GetHandle(),
|
|
pParent->GetOpFlags(),
|
|
static_cast<void*>(pAsyncOp),
|
|
pdnObject->ApplicationDesc.GetReservedData(),
|
|
pdnObject->ApplicationDesc.GetReservedDataSize(),
|
|
&hProtocol);
|
|
if (hResultCode != DPNERR_PENDING)
|
|
{
|
|
DPFERR("Could not Listen at Protocol layer !");
|
|
DisplayDNError(0,hResultCode);
|
|
pAsyncOp->Release();
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Save Protocol handle
|
|
//
|
|
pAsyncOp->Lock();
|
|
if (pAsyncOp->IsCancelled() && !pAsyncOp->IsComplete())
|
|
{
|
|
HRESULT hrCancel;
|
|
|
|
pAsyncOp->Unlock();
|
|
DPFX(DPFPREP, 7,"Operation marked for cancel");
|
|
if ((hrCancel = DNPCancelCommand(pdnObject->pdnProtocolData,hProtocol)) == DPN_OK)
|
|
{
|
|
hResultCode = DPNERR_USERCANCEL;
|
|
goto Failure;
|
|
}
|
|
DPFERR("Could not cancel operation");
|
|
DisplayDNError(0,hrCancel);
|
|
pAsyncOp->Lock();
|
|
}
|
|
pAsyncOp->SetProtocolHandle(hProtocol);
|
|
pAsyncOp->Unlock();
|
|
|
|
//
|
|
// Wait for LISTEN to complete.
|
|
// DNPICompleteListen() will set pSyncEvent and hrListen.
|
|
// Clean up.
|
|
//
|
|
pSyncEvent->WaitForEvent();
|
|
pAsyncOp->SetSyncEvent(NULL);
|
|
pAsyncOp->SetResultPointer(NULL);
|
|
|
|
if (hrListen != DPN_OK)
|
|
{
|
|
DPFERR("LISTEN did not succeed");
|
|
DisplayDNError(0,hrListen);
|
|
hResultCode = hrListen;
|
|
goto Failure;
|
|
}
|
|
|
|
pAsyncOp->Release();
|
|
pAsyncOp = NULL;
|
|
|
|
pSyncEvent->ReturnSelfToPool();
|
|
pSyncEvent = NULL;
|
|
|
|
//
|
|
// Flag object as LISTENing
|
|
//
|
|
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
|
|
pdnObject->dwFlags |= DN_OBJECT_FLAG_LISTENING;
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
|
|
DNASSERT( hResultCode == DPNERR_PENDING );
|
|
|
|
hResultCode = DPNERR_PENDING;
|
|
|
|
Exit:
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (pAsyncOp)
|
|
{
|
|
DNEnterCriticalSection(&pdnObject->csActiveList);
|
|
pAsyncOp->m_bilinkActiveList.RemoveFromList();
|
|
DNLeaveCriticalSection(&pdnObject->csActiveList);
|
|
pAsyncOp->Release();
|
|
pAsyncOp = NULL;
|
|
}
|
|
if (pSyncEvent)
|
|
{
|
|
pSyncEvent->ReturnSelfToPool();
|
|
pSyncEvent = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
#ifndef DPNBUILD_ONLYONEADAPTER
|
|
|
|
// DNPerformNextListen
|
|
//
|
|
// This will attempt to perform the next LISTEN if multiple adapters were requested
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNPerformNextListen"
|
|
|
|
HRESULT DNPerformNextListen(DIRECTNETOBJECT *const pdnObject,
|
|
CAsyncOp *const pAsyncOp,
|
|
IDirectPlay8Address *const pDeviceAddr)
|
|
{
|
|
HRESULT hResultCode;
|
|
CAsyncOp *pParent;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: pAsyncOp [0x%p], pDeviceAddr [0x%p]",pAsyncOp,pDeviceAddr);
|
|
|
|
pParent = NULL;
|
|
|
|
pAsyncOp->Lock();
|
|
if (pAsyncOp->GetParent())
|
|
{
|
|
pAsyncOp->GetParent()->AddRef();
|
|
pParent = pAsyncOp->GetParent();
|
|
}
|
|
pAsyncOp->Unlock();
|
|
|
|
//
|
|
// If there are any LISTENs left to perform, we will move on to the next one
|
|
//
|
|
if (pParent)
|
|
{
|
|
DN_LISTEN_OP_DATA *pListenOpData;
|
|
|
|
pListenOpData = pParent->GetLocalListenOpData();
|
|
if ((pListenOpData->dwCurrentAdapter < pListenOpData->dwNumAdapters) && pParent->GetOpData())
|
|
{
|
|
GUID *pguid;
|
|
CWorkerJob *pWorkerJob;
|
|
|
|
pguid = reinterpret_cast<GUID*>(pParent->GetOpData());
|
|
pguid += pListenOpData->dwCurrentAdapter;
|
|
if ((hResultCode = IDirectPlay8Address_SetDevice(pDeviceAddr,pguid)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not set device for next adapter");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(FALSE);
|
|
goto Failure;
|
|
}
|
|
pListenOpData->dwCurrentAdapter++;
|
|
|
|
//
|
|
// Perform LISTEN on worker thread
|
|
//
|
|
if ((hResultCode = WorkerJobNew(pdnObject,&pWorkerJob)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not create WorkerJob");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(FALSE);
|
|
goto Failure;
|
|
}
|
|
pWorkerJob->SetJobType( WORKER_JOB_PERFORM_LISTEN );
|
|
pWorkerJob->SetAddress( pDeviceAddr );
|
|
pWorkerJob->SetAsyncOp( pParent );
|
|
DNQueueWorkerJob(pdnObject,pWorkerJob);
|
|
}
|
|
|
|
pParent->Release();
|
|
pParent = NULL;
|
|
}
|
|
|
|
hResultCode = DPN_OK;
|
|
|
|
Exit:
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (pParent)
|
|
{
|
|
pParent->Release();
|
|
pParent = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
#endif // ! DPNBUILD_ONLYONEADAPTER
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNCompleteListen"
|
|
|
|
void DNCompleteListen(DIRECTNETOBJECT *const pdnObject,
|
|
CAsyncOp *const pAsyncOp)
|
|
{
|
|
DNASSERT(pdnObject != NULL);
|
|
DNASSERT(pAsyncOp != NULL);
|
|
|
|
#ifndef DPNBUILD_ONLYONEADAPTER
|
|
memset( pAsyncOp->GetLocalListenOpData(),0x00,sizeof(DN_LISTEN_OP_DATA) );
|
|
#endif // ! DPNBUILD_ONLYONEADAPTER
|
|
if (pAsyncOp->GetOpData() != NULL)
|
|
{
|
|
MemoryBlockFree(pdnObject,pAsyncOp->GetOpData());
|
|
pAsyncOp->SetOpData( NULL );
|
|
}
|
|
|
|
if (pAsyncOp->IsChild())
|
|
{
|
|
DNASSERT(pAsyncOp->GetParent() != NULL);
|
|
pAsyncOp->Orphan();
|
|
}
|
|
else
|
|
{
|
|
if (pAsyncOp->IsParent())
|
|
{
|
|
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
|
|
pdnObject->dwFlags &= (~DN_OBJECT_FLAG_LISTENING);
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// DNPerformEnumQuery
|
|
//
|
|
// Initiate an ENUM and take care of the book keeping
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNPerformEnumQuery"
|
|
|
|
HRESULT DNPerformEnumQuery(DIRECTNETOBJECT *const pdnObject,
|
|
IDirectPlay8Address *const pHost,
|
|
IDirectPlay8Address *const pDevice,
|
|
const HANDLE hSPHandle,
|
|
const DWORD dwFlags,
|
|
void *const pvContext,
|
|
CAsyncOp *const pParent)
|
|
{
|
|
HRESULT hResultCode;
|
|
CAsyncOp *pAsyncOp;
|
|
HANDLE hProtocol;
|
|
DN_ENUM_QUERY_OP_DATA *pEnumQueryOpData;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: pHost [0x%p], pDevice [0x%p], hSPHandle [0x%p], dwFlags [0x%x], pvContext [0x%p], pParent [0x%p]",
|
|
pHost,pDevice,hSPHandle,dwFlags,pvContext,pParent);
|
|
|
|
DNASSERT(pdnObject != NULL);
|
|
DNASSERT(hSPHandle != NULL);
|
|
|
|
pAsyncOp = NULL;
|
|
|
|
//
|
|
// Create AsyncOp for ENUM
|
|
//
|
|
if ((hResultCode = AsyncOpNew(pdnObject,&pAsyncOp)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not create AsyncOp");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(FALSE);
|
|
goto Failure;
|
|
}
|
|
pAsyncOp->SetOpType( ASYNC_OP_ENUM_QUERY );
|
|
pAsyncOp->SetResult( DPNERR_GENERIC );
|
|
pAsyncOp->SetCompletion( DNCompleteEnumQuery );
|
|
pAsyncOp->SetContext( pvContext );
|
|
|
|
DNASSERT(pParent != NULL);
|
|
pParent->Lock();
|
|
if (pParent->IsCancelled())
|
|
{
|
|
pParent->Unlock();
|
|
pAsyncOp->SetResult( DPNERR_USERCANCEL );
|
|
hResultCode = DPNERR_USERCANCEL;
|
|
goto Failure;
|
|
}
|
|
pAsyncOp->MakeChild(pParent);
|
|
pParent->Unlock();
|
|
pEnumQueryOpData = pParent->GetLocalEnumQueryOpData();
|
|
|
|
//
|
|
// Add to active AsyncOp list
|
|
//
|
|
DNEnterCriticalSection(&pdnObject->csActiveList);
|
|
pAsyncOp->m_bilinkActiveList.InsertBefore(&pdnObject->m_bilinkActiveList);
|
|
DNLeaveCriticalSection(&pdnObject->csActiveList);
|
|
|
|
//
|
|
// AddRef Protocol so that it won't go away until this completes
|
|
//
|
|
DNProtocolAddRef(pdnObject);
|
|
|
|
pAsyncOp->AddRef();
|
|
hResultCode = DNPEnumQuery(pdnObject->pdnProtocolData,
|
|
pHost,
|
|
pDevice,
|
|
hSPHandle,
|
|
&pEnumQueryOpData->BufferDesc[DN_ENUM_BUFFERDESC_QUERY_DN_PAYLOAD],
|
|
pEnumQueryOpData->dwBufferCount,
|
|
pEnumQueryOpData->dwRetryCount, // count of enumerations to send
|
|
pEnumQueryOpData->dwRetryInterval, // interval between enumerations
|
|
pEnumQueryOpData->dwTimeOut, // linger time after last enumeration is sent
|
|
dwFlags,
|
|
reinterpret_cast<void*>(pAsyncOp),
|
|
((pEnumQueryOpData->dwAppDescReservedDataSize > 0) ? pEnumQueryOpData->AppDescReservedData : NULL),
|
|
pEnumQueryOpData->dwAppDescReservedDataSize,
|
|
&hProtocol);
|
|
if ( hResultCode != DPNERR_PENDING )
|
|
{
|
|
DPFERR( "Failed to start enuming!" );
|
|
pAsyncOp->Release();
|
|
DNProtocolRelease(pdnObject);
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Setup for proper clean-up
|
|
//
|
|
pAsyncOp->Lock();
|
|
if (pAsyncOp->IsCancelled() && !pAsyncOp->IsComplete())
|
|
{
|
|
HRESULT hrCancel;
|
|
|
|
pAsyncOp->Unlock();
|
|
DPFX(DPFPREP, 7,"Operation marked for cancel");
|
|
if ((hrCancel = DNPCancelCommand(pdnObject->pdnProtocolData,hProtocol)) == DPN_OK)
|
|
{
|
|
hResultCode = DPNERR_USERCANCEL;
|
|
goto Failure;
|
|
}
|
|
DPFERR("Could not cancel operation");
|
|
DisplayDNError(0,hrCancel);
|
|
pAsyncOp->Lock();
|
|
}
|
|
pAsyncOp->SetProtocolHandle( hProtocol );
|
|
pAsyncOp->Unlock();
|
|
|
|
pAsyncOp->Release();
|
|
pAsyncOp = NULL;
|
|
|
|
hResultCode = DPN_OK;
|
|
|
|
Exit:
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (pAsyncOp)
|
|
{
|
|
DNEnterCriticalSection(&pdnObject->csActiveList);
|
|
pAsyncOp->m_bilinkActiveList.RemoveFromList();
|
|
DNLeaveCriticalSection(&pdnObject->csActiveList);
|
|
pAsyncOp->Release();
|
|
pAsyncOp = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
// DNPerformNextEnumQuery
|
|
//
|
|
// This will attempt to perform the next ENUM if multiple adapters were requested
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNPerformNextEnumQuery"
|
|
|
|
HRESULT DNPerformNextEnumQuery(DIRECTNETOBJECT *const pdnObject,
|
|
CAsyncOp *const pAsyncOp,
|
|
IDirectPlay8Address *const pHostAddr,
|
|
IDirectPlay8Address *const pDeviceAddr)
|
|
{
|
|
HRESULT hResultCode;
|
|
CAsyncOp *pParent;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: pAsyncOp [0x%p], pHostAddr [0x%p], pDeviceAddr [0x%p]",pAsyncOp,pHostAddr,pDeviceAddr);
|
|
|
|
pParent = NULL;
|
|
|
|
pAsyncOp->Lock();
|
|
if (pAsyncOp->GetParent())
|
|
{
|
|
pAsyncOp->GetParent()->AddRef();
|
|
pParent = pAsyncOp->GetParent();
|
|
}
|
|
pAsyncOp->Unlock();
|
|
|
|
//
|
|
// If there are any ENUMs left to perform, we will move on to the next one
|
|
//
|
|
if (pParent)
|
|
{
|
|
#ifndef DPNBUILD_ONLYONEADAPTER
|
|
DN_ENUM_QUERY_OP_DATA *pEnumQueryOpData;
|
|
|
|
pEnumQueryOpData = pParent->GetLocalEnumQueryOpData();
|
|
if ((pEnumQueryOpData->dwCurrentAdapter < pEnumQueryOpData->dwNumAdapters) && pParent->GetOpData())
|
|
{
|
|
GUID *pguid;
|
|
DWORD dwMultiplexFlag;
|
|
|
|
pguid = reinterpret_cast<GUID*>(pParent->GetOpData());
|
|
pguid += pEnumQueryOpData->dwCurrentAdapter;
|
|
if ((hResultCode = IDirectPlay8Address_SetDevice(pDeviceAddr,pguid)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not set device for next adapter");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(FALSE);
|
|
goto Failure;
|
|
}
|
|
pEnumQueryOpData->dwCurrentAdapter++;
|
|
|
|
if (pEnumQueryOpData->dwCurrentAdapter < pEnumQueryOpData->dwNumAdapters)
|
|
{
|
|
dwMultiplexFlag = DN_ENUMQUERYFLAGS_ADDITIONALMULTIPLEXADAPTERS;
|
|
}
|
|
else
|
|
{
|
|
dwMultiplexFlag = 0;
|
|
}
|
|
|
|
hResultCode = DNPerformEnumQuery( pdnObject,
|
|
pHostAddr,
|
|
pDeviceAddr,
|
|
pParent->GetSP()->GetHandle(),
|
|
pParent->GetOpFlags() | dwMultiplexFlag,
|
|
pParent->GetContext(),
|
|
pParent );
|
|
if (hResultCode != DPN_OK)
|
|
{
|
|
DPFERR("Could not start ENUM");
|
|
DisplayDNError(0,hResultCode);
|
|
goto Failure;
|
|
}
|
|
}
|
|
#endif // ! DPNBUILD_ONLYONEADAPTER
|
|
|
|
pParent->Release();
|
|
pParent = NULL;
|
|
}
|
|
|
|
hResultCode = DPN_OK;
|
|
|
|
#ifndef DPNBUILD_ONLYONEADAPTER
|
|
Exit:
|
|
#endif // ! DPNBUILD_ONLYONEADAPTER
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
#ifndef DPNBUILD_ONLYONEADAPTER
|
|
Failure:
|
|
if (pParent)
|
|
{
|
|
pParent->Release();
|
|
pParent = NULL;
|
|
}
|
|
goto Exit;
|
|
#endif // ! DPNBUILD_ONLYONEADAPTER
|
|
}
|
|
|
|
|
|
// DNCompleteEnumQuery
|
|
//
|
|
// Completion for AsyncOps for EnumQuery.
|
|
// This will:
|
|
// - free up the EnumQuery memory block associated with this AsyncOp
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNCompleteEnumQuery"
|
|
|
|
void DNCompleteEnumQuery(DIRECTNETOBJECT *const pdnObject,
|
|
CAsyncOp *const pAsyncOp)
|
|
{
|
|
DNASSERT(pdnObject != NULL);
|
|
DNASSERT(pAsyncOp != NULL);
|
|
|
|
memset(pAsyncOp->GetLocalEnumQueryOpData(),0x00,sizeof(DN_ENUM_QUERY_OP_DATA));
|
|
if (pAsyncOp->GetOpData())
|
|
{
|
|
MemoryBlockFree(pdnObject,pAsyncOp->GetOpData());
|
|
pAsyncOp->SetOpData( NULL );
|
|
}
|
|
|
|
if ( pAsyncOp->IsChild() )
|
|
{
|
|
DNASSERT(pAsyncOp->GetParent() != NULL);
|
|
|
|
pAsyncOp->GetParent()->Lock();
|
|
|
|
//
|
|
// Save HRESULT
|
|
//
|
|
if (pAsyncOp->GetParent()->GetResult() != DPN_OK)
|
|
{
|
|
pAsyncOp->GetParent()->SetResult( pAsyncOp->GetResult() );
|
|
}
|
|
|
|
//
|
|
// Release parent Handle if it exists
|
|
//
|
|
if (SUCCEEDED(pdnObject->HandleTable.Destroy( pAsyncOp->GetParent()->GetHandle(), NULL )))
|
|
{
|
|
// Release the HandleTable reference
|
|
pAsyncOp->GetParent()->Release();
|
|
}
|
|
|
|
pAsyncOp->GetParent()->Unlock();
|
|
}
|
|
}
|
|
|
|
|
|
// DNCompleteEnumResponse
|
|
//
|
|
// Completion for AsyncOps for EnumResponse.
|
|
// This will:
|
|
// - generate a RETURN_BUFFER message if there was a user payload
|
|
// - free up the EnumResponse memory block associated with this AsyncOp
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNCompleteEnumResponse"
|
|
|
|
void DNCompleteEnumResponse(DIRECTNETOBJECT *const pdnObject,
|
|
CAsyncOp *const pAsyncOp)
|
|
{
|
|
DN_ENUM_RESPONSE_OP_DATA *pEnumResponseOpData;
|
|
|
|
DNASSERT(pdnObject != NULL);
|
|
DNASSERT(pAsyncOp != NULL);
|
|
|
|
pEnumResponseOpData = pAsyncOp->GetLocalEnumResponseOpData();
|
|
|
|
if (pEnumResponseOpData->BufferDesc[DN_ENUM_BUFFERDESC_RESPONSE_USER_PAYLOAD].pBufferData != NULL)
|
|
{
|
|
DNUserReturnBuffer( pdnObject,
|
|
DPN_OK,
|
|
pEnumResponseOpData->BufferDesc[DN_ENUM_BUFFERDESC_RESPONSE_USER_PAYLOAD].pBufferData,
|
|
pEnumResponseOpData->pvUserContext);
|
|
}
|
|
|
|
memset(pEnumResponseOpData,0x00,sizeof(DN_ENUM_RESPONSE_OP_DATA));
|
|
}
|
|
|
|
|
|
|
|
// DNPerformConnect
|
|
//
|
|
// Initiate a connection and take care of the book keeping (create handle).
|
|
//
|
|
// DPNID dpnid Target player (for ExistingPlayers connect calls)
|
|
// IDirectPlayAddress8 *pDeviceInfo (may be NULL - no connect performed)
|
|
// IDirectPlayAddress8 *pRemoteAddr (may be NULL - no connect performed)
|
|
// DWORD dwFlags CONNECT op flags
|
|
// CAsyncOp *pParent Parent Async Op (if it exists)
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNPerformConnect"
|
|
|
|
HRESULT DNPerformConnect(DIRECTNETOBJECT *const pdnObject,
|
|
const DPNID dpnid,
|
|
IDirectPlay8Address *const pDeviceInfo,
|
|
IDirectPlay8Address *const pRemoteAddr,
|
|
CServiceProvider *const pSP,
|
|
const DWORD dwConnectFlags,
|
|
CAsyncOp *const pParent)
|
|
{
|
|
HANDLE hProtocol;
|
|
HRESULT hResultCode;
|
|
CAsyncOp *pAsyncOp;
|
|
#ifdef DBG
|
|
TCHAR DP8ABuffer[512] = {0};
|
|
DWORD DP8ASize;
|
|
#endif // DBG
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: dpnid [0x%p], pDeviceInfo [0x%p], pRemoteAddr [0x%p], dwConnectFlags [0x%lx], pParent [0x%p]",
|
|
dpnid,pDeviceInfo,pRemoteAddr,dwConnectFlags,pParent);
|
|
|
|
DNASSERT(pdnObject != NULL);
|
|
DNASSERT(pDeviceInfo != NULL);
|
|
DNASSERT(pRemoteAddr != NULL);
|
|
DNASSERT(pSP != NULL);
|
|
|
|
pAsyncOp = NULL;
|
|
|
|
#ifdef DBG
|
|
DP8ASize = 512;
|
|
IDirectPlay8Address_GetURL(pRemoteAddr,DP8ABuffer,&DP8ASize);
|
|
DPFX(DPFPREP, 7,"Remote Address [%s]",DP8ABuffer);
|
|
|
|
DP8ASize = 512;
|
|
IDirectPlay8Address_GetURL(pDeviceInfo,DP8ABuffer,&DP8ASize);
|
|
DPFX(DPFPREP, 7,"Device Info [%s]",DP8ABuffer);
|
|
#endif // DBG
|
|
|
|
//
|
|
// Create AsyncOp for CONNECT
|
|
//
|
|
if ((hResultCode = AsyncOpNew(pdnObject,&pAsyncOp)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not create AsyncOp");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(FALSE);
|
|
goto Failure;
|
|
}
|
|
pAsyncOp->SetDPNID( dpnid );
|
|
pAsyncOp->SetOpFlags( dwConnectFlags );
|
|
pAsyncOp->SetSP(pSP);
|
|
pAsyncOp->SetResult( DPNERR_NOCONNECTION );
|
|
pAsyncOp->SetCompletion( DNCompleteConnect );
|
|
|
|
if (pParent)
|
|
{
|
|
pAsyncOp->SetOpType( pParent->GetOpType() );
|
|
pParent->Lock();
|
|
if (pParent->IsCancelled())
|
|
{
|
|
pParent->Unlock();
|
|
pAsyncOp->SetResult( DPNERR_USERCANCEL );
|
|
hResultCode = DPNERR_USERCANCEL;
|
|
goto Failure;
|
|
}
|
|
pAsyncOp->MakeChild(pParent);
|
|
pParent->Unlock();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Assume it's a regular connect.
|
|
//
|
|
pAsyncOp->SetOpType( ASYNC_OP_CONNECT );
|
|
}
|
|
|
|
//
|
|
// Add to active async op list
|
|
//
|
|
DNEnterCriticalSection(&pdnObject->csActiveList);
|
|
pAsyncOp->m_bilinkActiveList.InsertBefore(&pdnObject->m_bilinkActiveList);
|
|
DNLeaveCriticalSection(&pdnObject->csActiveList);
|
|
|
|
//
|
|
// AddRef Protocol so that it won't go away until this completes
|
|
//
|
|
DNProtocolAddRef(pdnObject);
|
|
|
|
//
|
|
// Perform CONNECT
|
|
//
|
|
DPFX(DPFPREP, 7,"Performing connect");
|
|
pAsyncOp->AddRef();
|
|
hResultCode = DNPConnect( pdnObject->pdnProtocolData,
|
|
pDeviceInfo,
|
|
pRemoteAddr,
|
|
pSP->GetHandle(),
|
|
dwConnectFlags,
|
|
pAsyncOp,
|
|
pdnObject->ApplicationDesc.GetReservedData(),
|
|
pdnObject->ApplicationDesc.GetReservedDataSize(),
|
|
&hProtocol);
|
|
if (hResultCode != DPNERR_PENDING)
|
|
{
|
|
DPFERR("Could not CONNECT");
|
|
DisplayDNError(0,hResultCode);
|
|
pAsyncOp->Release();
|
|
DNProtocolRelease(pdnObject);
|
|
goto Failure;
|
|
}
|
|
|
|
pAsyncOp->Lock();
|
|
if (pAsyncOp->IsCancelled() && !pAsyncOp->IsComplete())
|
|
{
|
|
HRESULT hrCancel;
|
|
|
|
pAsyncOp->Unlock();
|
|
DPFX(DPFPREP, 7,"Operation marked for cancel");
|
|
if ((hrCancel = DNPCancelCommand(pdnObject->pdnProtocolData,hProtocol)) == DPN_OK)
|
|
{
|
|
//
|
|
// If the parent has DPN_OK, then another connect succeeded and we don't want to fail.
|
|
// This may happen in a dual (or more) adapter case.
|
|
//
|
|
if (pParent && pParent->GetResult() == DPN_OK)
|
|
{
|
|
hResultCode = DPN_OK;
|
|
}
|
|
else
|
|
{
|
|
hResultCode = DPNERR_USERCANCEL;
|
|
}
|
|
goto Failure;
|
|
}
|
|
DPFERR("Could not cancel operation");
|
|
DisplayDNError(0,hrCancel);
|
|
pAsyncOp->Lock();
|
|
}
|
|
pAsyncOp->SetProtocolHandle(hProtocol);
|
|
pAsyncOp->Unlock();
|
|
|
|
pAsyncOp->Release();
|
|
pAsyncOp = NULL;
|
|
|
|
hResultCode = DPN_OK;
|
|
|
|
Exit:
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (pAsyncOp)
|
|
{
|
|
DNEnterCriticalSection(&pdnObject->csActiveList);
|
|
pAsyncOp->m_bilinkActiveList.RemoveFromList();
|
|
DNLeaveCriticalSection(&pdnObject->csActiveList);
|
|
pAsyncOp->Release();
|
|
pAsyncOp = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
// DNPerformNextConnect
|
|
//
|
|
// This will attempt to perform the next CONNECT if multiple adapters were requested
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNPerformNextConnect"
|
|
|
|
HRESULT DNPerformNextConnect(DIRECTNETOBJECT *const pdnObject,
|
|
CAsyncOp *const pAsyncOp,
|
|
IDirectPlay8Address *const pHostAddr,
|
|
IDirectPlay8Address *const pDeviceAddr)
|
|
{
|
|
HRESULT hResultCode;
|
|
CAsyncOp *pParent;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: pAsyncOp [0x%p], pHostAddr [0x%p], pDeviceAddr [0x%p]",pAsyncOp,pHostAddr,pDeviceAddr);
|
|
|
|
pParent = NULL;
|
|
|
|
pAsyncOp->Lock();
|
|
if (pAsyncOp->GetParent())
|
|
{
|
|
pAsyncOp->GetParent()->AddRef();
|
|
pParent = pAsyncOp->GetParent();
|
|
}
|
|
pAsyncOp->Unlock();
|
|
|
|
//
|
|
// If there are any CONNECTs left to perform, we will move on to the next one
|
|
//
|
|
if (pParent)
|
|
{
|
|
#ifndef DPNBUILD_ONLYONEADAPTER
|
|
DN_CONNECT_OP_DATA *pConnectOpData;
|
|
|
|
pConnectOpData = pParent->GetLocalConnectOpData();
|
|
if ((pConnectOpData->dwCurrentAdapter < pConnectOpData->dwNumAdapters) && pParent->GetOpData())
|
|
{
|
|
GUID *pguid;
|
|
DWORD dwMultiplexFlag;
|
|
|
|
pguid = reinterpret_cast<GUID*>(pParent->GetOpData());
|
|
pguid += pConnectOpData->dwCurrentAdapter;
|
|
if ((hResultCode = IDirectPlay8Address_SetDevice(pDeviceAddr,pguid)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not set device for next adapter");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(FALSE);
|
|
goto Failure;
|
|
}
|
|
pConnectOpData->dwCurrentAdapter++;
|
|
|
|
if (pConnectOpData->dwCurrentAdapter < pConnectOpData->dwNumAdapters)
|
|
{
|
|
dwMultiplexFlag = DN_CONNECTFLAGS_ADDITIONALMULTIPLEXADAPTERS;
|
|
}
|
|
else
|
|
{
|
|
dwMultiplexFlag = 0;
|
|
}
|
|
|
|
if ((hResultCode = DNPerformConnect(pdnObject,
|
|
NULL,
|
|
pDeviceAddr,
|
|
pHostAddr,
|
|
pParent->GetSP(),
|
|
pParent->GetOpFlags() | dwMultiplexFlag,
|
|
pParent)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not perform CONNECT");
|
|
DisplayDNError(0,hResultCode);
|
|
goto Failure;
|
|
}
|
|
}
|
|
#endif // ! DPNBUILD_ONLYONEADAPTER
|
|
|
|
pParent->Release();
|
|
pParent = NULL;
|
|
}
|
|
|
|
hResultCode = DPN_OK;
|
|
|
|
#ifndef DPNBUILD_ONLYONEADAPTER
|
|
Exit:
|
|
#endif // ! DPNBUILD_ONLYONEADAPTER
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
#ifndef DPNBUILD_ONLYONEADAPTER
|
|
Failure:
|
|
if (pParent)
|
|
{
|
|
pParent->Release();
|
|
pParent = NULL;
|
|
}
|
|
goto Exit;
|
|
#endif // ! DPNBUILD_ONLYONEADAPTER
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNCompleteConnect"
|
|
|
|
void DNCompleteConnect(DIRECTNETOBJECT *const pdnObject,
|
|
CAsyncOp *const pAsyncOp)
|
|
{
|
|
//
|
|
// Save the result code on the parent if it hasn't already been set (DPN_OK or DPNERR_HOSTREJECTEDCONNECTION)
|
|
//
|
|
if (pAsyncOp->GetParent() && (pAsyncOp->GetResult() != DPNERR_NOCONNECTION))
|
|
{
|
|
pAsyncOp->GetParent()->Lock();
|
|
if ((pAsyncOp->GetParent()->GetResult() != DPN_OK)
|
|
&& (pAsyncOp->GetParent()->GetResult() != DPNERR_HOSTREJECTEDCONNECTION))
|
|
{
|
|
pAsyncOp->GetParent()->SetResult( pAsyncOp->GetResult() );
|
|
}
|
|
pAsyncOp->GetParent()->Unlock();
|
|
}
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNCompleteConnectToHost"
|
|
|
|
void DNCompleteConnectToHost(DIRECTNETOBJECT *const pdnObject,
|
|
CAsyncOp *const pAsyncOp)
|
|
{
|
|
//
|
|
// Save the result code on the parent if there was a problem (this should be the connect operation parent)
|
|
//
|
|
if ((pAsyncOp->GetResult() != DPN_OK) && (pAsyncOp->GetResult() != DPNERR_NOCONNECTION))
|
|
{
|
|
if (pAsyncOp->GetParent())
|
|
{
|
|
pAsyncOp->GetParent()->Lock();
|
|
if ((pAsyncOp->GetParent()->GetResult() != DPN_OK) && (pAsyncOp->GetParent()->GetResult() != DPNERR_HOSTREJECTEDCONNECTION))
|
|
{
|
|
pAsyncOp->GetParent()->SetResult( pAsyncOp->GetResult() );
|
|
}
|
|
pAsyncOp->GetParent()->Unlock();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Clean up DirectNet object if this fails
|
|
//
|
|
if (pAsyncOp->GetResult() != DPN_OK)
|
|
{
|
|
CAsyncOp *pConnectParent;
|
|
|
|
pConnectParent = NULL;
|
|
|
|
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
|
|
pdnObject->dwFlags &= (~(DN_OBJECT_FLAG_CONNECTED
|
|
| DN_OBJECT_FLAG_CONNECTING
|
|
| DN_OBJECT_FLAG_HOST_CONNECTED));
|
|
if (pdnObject->pConnectParent)
|
|
{
|
|
pConnectParent = pdnObject->pConnectParent;
|
|
pdnObject->pConnectParent = NULL;
|
|
}
|
|
if( pdnObject->pIDP8ADevice )
|
|
{
|
|
IDirectPlay8Address_Release( pdnObject->pIDP8ADevice );
|
|
pdnObject->pIDP8ADevice = NULL;
|
|
}
|
|
if( pdnObject->pConnectAddress )
|
|
{
|
|
IDirectPlay8Address_Release( pdnObject->pConnectAddress );
|
|
pdnObject->pConnectAddress = NULL;
|
|
}
|
|
if( pdnObject->pConnectSP )
|
|
{
|
|
pdnObject->pConnectSP->Release();
|
|
pdnObject->pConnectSP = NULL;
|
|
}
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
|
|
if (pConnectParent)
|
|
{
|
|
pConnectParent->Release();
|
|
pConnectParent = NULL;
|
|
}
|
|
|
|
DNASSERT(pConnectParent == NULL);
|
|
}
|
|
|
|
//
|
|
// Clean up CONNECT op data
|
|
//
|
|
#ifndef DPNBUILD_ONLYONEADAPTER
|
|
memset( pAsyncOp->GetLocalConnectOpData(),0x00,sizeof(DN_CONNECT_OP_DATA) );
|
|
#endif // ! DPNBUILD_ONLYONEADAPTER
|
|
if (pAsyncOp->GetOpData() != NULL)
|
|
{
|
|
MemoryBlockFree(pdnObject,pAsyncOp->GetOpData());
|
|
pAsyncOp->SetOpData( NULL );
|
|
}
|
|
|
|
//
|
|
// Detach from parent
|
|
// I'm not sure why we need to do this !
|
|
//
|
|
DNASSERT(pAsyncOp->IsChild());
|
|
DNASSERT(pAsyncOp->GetParent());
|
|
pAsyncOp->Orphan();
|
|
}
|
|
|
|
|
|
//
|
|
// Completion for connect parent
|
|
//
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNCompleteConnectOperation"
|
|
|
|
void DNCompleteConnectOperation(DIRECTNETOBJECT *const pdnObject,
|
|
CAsyncOp *const pAsyncOp)
|
|
{
|
|
//
|
|
// Save the result code on the parent (if it exists - it will be the CONNECT handle)
|
|
//
|
|
if (pAsyncOp->GetParent())
|
|
{
|
|
pAsyncOp->GetParent()->Lock();
|
|
pAsyncOp->GetParent()->SetResult( pAsyncOp->GetResult() );
|
|
pAsyncOp->GetParent()->SetRefCountBuffer( pAsyncOp->GetRefCountBuffer() );
|
|
pAsyncOp->GetParent()->Unlock();
|
|
|
|
if (SUCCEEDED(pdnObject->HandleTable.Destroy( pAsyncOp->GetParent()->GetHandle(), NULL )))
|
|
{
|
|
// Release the HandleTable reference
|
|
pAsyncOp->GetParent()->Release();
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the OpData of this AsyncOp is set it is a pointer to a RefCountBuffer pointer
|
|
// (sync connect call) and we will fill it in
|
|
//
|
|
if (pAsyncOp->GetOpData())
|
|
{
|
|
if (pAsyncOp->GetRefCountBuffer())
|
|
{
|
|
pAsyncOp->GetRefCountBuffer()->AddRef();
|
|
*(static_cast<CRefCountBuffer**>(pAsyncOp->GetOpData())) = pAsyncOp->GetRefCountBuffer();
|
|
}
|
|
pAsyncOp->SetOpData( NULL );
|
|
}
|
|
|
|
#ifndef DPNBUILD_NOLOBBY
|
|
//
|
|
// If this connect succeeded, we will inform the lobby
|
|
//
|
|
if (pAsyncOp->GetResult() == DPN_OK)
|
|
{
|
|
DNUpdateLobbyStatus(pdnObject,DPLSESSION_CONNECTED);
|
|
}
|
|
else
|
|
{
|
|
DNUpdateLobbyStatus(pdnObject,DPLSESSION_COULDNOTCONNECT);
|
|
}
|
|
#endif // ! DPNBUILD_NOLOBBY
|
|
|
|
//
|
|
// Clear DISCONNECTING flag (in case this was aborted)
|
|
//
|
|
DPFX(DPFPREP, 8,"Clearing DISCONNECTING flag");
|
|
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
|
|
pdnObject->dwFlags &= (~DN_OBJECT_FLAG_DISCONNECTING);
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
}
|
|
|
|
|
|
//
|
|
// Completion for connect handle given to user if Connect was called asynchronously
|
|
//
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNCompleteUserConnect"
|
|
|
|
void DNCompleteUserConnect(DIRECTNETOBJECT *const pdnObject,
|
|
CAsyncOp *const pAsyncOp)
|
|
{
|
|
CNameTableEntry *pHostPlayer;
|
|
|
|
pHostPlayer = NULL;
|
|
|
|
//
|
|
// No longer connecting
|
|
//
|
|
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
|
|
pdnObject->dwFlags &= (~DN_OBJECT_FLAG_CONNECTING);
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
|
|
//
|
|
// Clients need to release all sends from the server that are
|
|
// queued once the CONNECT_COMPLETE gets indicated.
|
|
// We prepare to do that now.
|
|
//
|
|
if ((pAsyncOp->GetResult() == DPN_OK) && (pdnObject->dwFlags & DN_OBJECT_FLAG_CLIENT))
|
|
{
|
|
if (pdnObject->NameTable.GetHostPlayerRef( &pHostPlayer ) == DPN_OK)
|
|
{
|
|
pHostPlayer->Lock();
|
|
pHostPlayer->MakeAvailable();
|
|
pHostPlayer->NotifyAddRef();
|
|
pHostPlayer->SetInUse();
|
|
pHostPlayer->Unlock();
|
|
|
|
//
|
|
// We are now connected
|
|
//
|
|
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
|
|
pdnObject->dwFlags |= DN_OBJECT_FLAG_CONNECTED;
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If we couldn't get a reference on the server (host player),
|
|
// then either the server has disconnected, or we are being shut down.
|
|
// In either case, we should return an error
|
|
//
|
|
DPFX(DPFPREP, 0, "Couldn't get host player reference, failing CONNECT!");
|
|
pAsyncOp->SetResult( DPNERR_NOCONNECTION );
|
|
if (pAsyncOp->GetRefCountBuffer())
|
|
{
|
|
pAsyncOp->GetRefCountBuffer()->Release();
|
|
pAsyncOp->SetRefCountBuffer( NULL );
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Generate connect completion for player
|
|
//
|
|
DNUserConnectComplete( pdnObject,
|
|
pAsyncOp->GetHandle(),
|
|
pAsyncOp->GetContext(),
|
|
pAsyncOp->GetResult(),
|
|
pAsyncOp->GetRefCountBuffer() );
|
|
|
|
//
|
|
// Cancel ENUMs if the CONNECT succeeded and unload SP's
|
|
//
|
|
if (pAsyncOp->GetResult() == DPN_OK)
|
|
{
|
|
DNCancelActiveCommands(pdnObject,DN_CANCEL_FLAG_ENUM_QUERY,NULL,TRUE,DPNERR_CONNECTING);
|
|
|
|
#if ((! defined(DPNBUILD_LIBINTERFACE)) || (! defined(DPNBUILD_ONLYONESP)))
|
|
DN_SPReleaseAll(pdnObject);
|
|
#endif // ! DPNBUILD_LIBINTERFACE or ! DPNBUILD_ONLYONESP
|
|
|
|
|
|
//
|
|
// Actually release queued messages if necessary
|
|
//
|
|
if (pHostPlayer != NULL)
|
|
{
|
|
pHostPlayer->PerformQueuedOperations();
|
|
|
|
pHostPlayer->Release();
|
|
pHostPlayer = NULL;
|
|
}
|
|
}
|
|
|
|
DNASSERT( pHostPlayer == NULL );
|
|
}
|
|
|
|
|
|
//
|
|
// Completion for NewPlayer sending connect data to the Host
|
|
//
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNCompleteSendConnectInfo"
|
|
|
|
void DNCompleteSendConnectInfo(DIRECTNETOBJECT *const pdnObject,
|
|
CAsyncOp *const pAsyncOp)
|
|
{
|
|
//
|
|
// Update result of this send on the CONNECT operation parent
|
|
//
|
|
DNASSERT(pAsyncOp->GetParent() != NULL);
|
|
pAsyncOp->GetParent()->Lock();
|
|
if (pAsyncOp->GetResult() != DPN_OK)
|
|
{
|
|
pAsyncOp->GetParent()->SetResult( pAsyncOp->GetResult() );
|
|
//
|
|
// Clean up CONNECT response buffer from Host
|
|
//
|
|
if (pAsyncOp->GetParent()->GetRefCountBuffer())
|
|
{
|
|
pAsyncOp->GetParent()->GetRefCountBuffer()->Release();
|
|
pAsyncOp->GetParent()->SetRefCountBuffer( NULL );
|
|
}
|
|
}
|
|
pAsyncOp->GetParent()->Unlock();
|
|
|
|
//
|
|
// Clean up DirectNet object if this fails
|
|
//
|
|
if (pAsyncOp->GetResult() != DPN_OK)
|
|
{
|
|
CAsyncOp *pConnectParent;
|
|
|
|
pConnectParent = NULL;
|
|
|
|
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
|
|
pdnObject->dwFlags &= (~(DN_OBJECT_FLAG_CONNECTED
|
|
| DN_OBJECT_FLAG_CONNECTING
|
|
| DN_OBJECT_FLAG_HOST_CONNECTED));
|
|
if (pdnObject->pConnectParent)
|
|
{
|
|
pConnectParent = pdnObject->pConnectParent;
|
|
pdnObject->pConnectParent = NULL;
|
|
}
|
|
if( pdnObject->pIDP8ADevice )
|
|
{
|
|
IDirectPlay8Address_Release( pdnObject->pIDP8ADevice );
|
|
pdnObject->pIDP8ADevice = NULL;
|
|
}
|
|
if( pdnObject->pConnectAddress )
|
|
{
|
|
IDirectPlay8Address_Release( pdnObject->pConnectAddress );
|
|
pdnObject->pConnectAddress = NULL;
|
|
}
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
|
|
if (pConnectParent)
|
|
{
|
|
pConnectParent->Release();
|
|
pConnectParent = NULL;
|
|
}
|
|
|
|
DNASSERT(pConnectParent == NULL);
|
|
}
|
|
|
|
//
|
|
// Clean up op data
|
|
//
|
|
if (pAsyncOp->GetOpData())
|
|
{
|
|
MemoryBlockFree(pdnObject,pAsyncOp->GetOpData());
|
|
pAsyncOp->SetOpData( NULL );
|
|
}
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNPerformDisconnect"
|
|
|
|
HRESULT DNPerformDisconnect(DIRECTNETOBJECT *const pdnObject,
|
|
CConnection *const pConnection,
|
|
const HANDLE hEndPt,
|
|
const BOOL fImmediate)
|
|
{
|
|
HRESULT hResultCode;
|
|
HANDLE hProtocol;
|
|
CAsyncOp *pAsyncOp;
|
|
CAsyncOp *pConnectParent;
|
|
DWORD dwProtocolFlags;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: pConnection [0x%p], hEndPt [0x%lx], fImmediate [%d]",pConnection,hEndPt,fImmediate);
|
|
|
|
DNASSERT(pdnObject != NULL);
|
|
|
|
pAsyncOp = NULL;
|
|
pConnectParent = NULL;
|
|
dwProtocolFlags = 0;
|
|
|
|
if (hEndPt == NULL)
|
|
{
|
|
DPFERR("Ignoring NULL endpoint");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Create AsyncOp for this operation
|
|
//
|
|
if ((hResultCode = AsyncOpNew(pdnObject,&pAsyncOp)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not create AsyncOp");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(FALSE);
|
|
goto Failure;
|
|
}
|
|
pAsyncOp->SetOpType( ASYNC_OP_DISCONNECT );
|
|
pAsyncOp->SetConnection( pConnection );
|
|
pAsyncOp->SetCannotCancel(); // Cannot cancel DISCONNECT's
|
|
|
|
//
|
|
// AddRef Protocol so that it won't go away until this completes
|
|
//
|
|
DNProtocolAddRef(pdnObject);
|
|
|
|
//
|
|
// If there is a connect parent op, and it's hResultCode is not DPN_OK,
|
|
// then a connect is failing for some reason, and to prevent it from
|
|
// completing before this disconnect is complete, we will set the connect parent
|
|
// as the parent of this disconnect.
|
|
//
|
|
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
|
|
if (pdnObject->pConnectParent && (pdnObject->pConnectParent->GetResult() != DPN_OK))
|
|
{
|
|
pdnObject->pConnectParent->AddRef();
|
|
pConnectParent = pdnObject->pConnectParent;
|
|
}
|
|
if ((pdnObject->dwFlags & DN_OBJECT_FLAG_CLOSING_IMMEDIATE) || (fImmediate))
|
|
{
|
|
//
|
|
// If Close() was called with DPNCLOSE_IMMEDIATE then DN_OBJET_FLAG_CLOSING_IMMEDIATE will be set
|
|
// and all disconnects should be done in immediate mode
|
|
//
|
|
dwProtocolFlags |= DN_DISCONNECTFLAGS_IMMEDIATE;
|
|
}
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
if (pConnectParent)
|
|
{
|
|
pAsyncOp->MakeChild( pConnectParent );
|
|
pConnectParent->Release();
|
|
pConnectParent = NULL;
|
|
}
|
|
|
|
//
|
|
// Set connection as INVALID so that no other operations may use it anymore
|
|
//
|
|
pConnection->Lock();
|
|
pConnection->SetStatus( INVALID );
|
|
pConnection->Unlock();
|
|
|
|
//
|
|
// Perform DISCONNECT
|
|
//
|
|
pAsyncOp->AddRef();
|
|
hResultCode = DNPDisconnectEndPoint(pdnObject->pdnProtocolData,
|
|
hEndPt,
|
|
static_cast<void*>(pAsyncOp),
|
|
&hProtocol,
|
|
dwProtocolFlags);
|
|
if ((hResultCode != DPN_OK) && (hResultCode != DPNERR_PENDING))
|
|
{
|
|
DPFERR("Could not issue DISCONNECT");
|
|
DisplayDNError(0,hResultCode);
|
|
DNProtocolRelease(pdnObject);
|
|
pAsyncOp->Release();
|
|
goto Failure;
|
|
}
|
|
|
|
pAsyncOp->Lock();
|
|
pAsyncOp->SetProtocolHandle( hProtocol );
|
|
pAsyncOp->Unlock();
|
|
|
|
pAsyncOp->Release();
|
|
pAsyncOp = NULL;
|
|
|
|
Exit:
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (pAsyncOp)
|
|
{
|
|
pAsyncOp->Release();
|
|
pAsyncOp = NULL;
|
|
}
|
|
if (pConnectParent)
|
|
{
|
|
pConnectParent->Release();
|
|
pConnectParent = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNCompleteAsyncHandle"
|
|
|
|
void DNCompleteAsyncHandle(DIRECTNETOBJECT *const pdnObject,
|
|
CAsyncOp *const pAsyncOp)
|
|
{
|
|
DNASSERT(pdnObject != NULL);
|
|
DNASSERT(pAsyncOp != NULL);
|
|
|
|
DNUserAsyncComplete(pdnObject,
|
|
pAsyncOp->GetHandle(),
|
|
pAsyncOp->GetContext(),
|
|
pAsyncOp->GetResult() );
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNCompleteSendHandle"
|
|
|
|
void DNCompleteSendHandle(DIRECTNETOBJECT *const pdnObject,
|
|
CAsyncOp *const pAsyncOp)
|
|
{
|
|
DNASSERT(pdnObject != NULL);
|
|
DNASSERT(pAsyncOp != NULL);
|
|
|
|
DNUserSendComplete( pdnObject,
|
|
pAsyncOp->GetHandle(),
|
|
pAsyncOp->GetContext(),
|
|
pAsyncOp->GetStartTime(),
|
|
pAsyncOp->GetResult(),
|
|
pAsyncOp->GetFirstFrameRTT(),
|
|
pAsyncOp->GetFirstFrameRetryCount());
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNCompleteSendAsyncOp"
|
|
|
|
void DNCompleteSendAsyncOp(DIRECTNETOBJECT *const pdnObject,
|
|
CAsyncOp *const pAsyncOp)
|
|
{
|
|
DNASSERT(pdnObject != NULL);
|
|
DNASSERT(pAsyncOp != NULL);
|
|
|
|
//
|
|
// Update outstanding queue info - this is only performed on children or stand-alone ops (i.e. no parent)
|
|
//
|
|
if ( !pAsyncOp->IsParent() && (pAsyncOp->GetConnection() != NULL))
|
|
{
|
|
DN_SEND_OP_DATA *pSendOpData = NULL;
|
|
|
|
if (pAsyncOp->IsUseParentOpData())
|
|
{
|
|
if (pAsyncOp->IsChild() && pAsyncOp->GetParent())
|
|
{
|
|
pSendOpData = pAsyncOp->GetParent()->GetLocalSendOpData();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pSendOpData = pAsyncOp->GetLocalSendOpData();
|
|
}
|
|
|
|
if (pSendOpData && pSendOpData->dwMsgId == DN_MSG_USER_SEND)
|
|
{
|
|
DWORD dw;
|
|
|
|
DNASSERT( pAsyncOp->GetConnection() != NULL );
|
|
|
|
pAsyncOp->GetConnection()->Lock();
|
|
for ( dw = 0 ; dw < pSendOpData->dwNumBuffers ; dw++ )
|
|
{
|
|
if (pAsyncOp->GetOpFlags() & DN_SENDFLAGS_HIGH_PRIORITY)
|
|
{
|
|
pAsyncOp->GetConnection()->RemoveFromHighQueue( pSendOpData->BufferDesc[dw].dwBufferSize );
|
|
}
|
|
else if (pAsyncOp->GetOpFlags() & DN_SENDFLAGS_LOW_PRIORITY)
|
|
{
|
|
pAsyncOp->GetConnection()->RemoveFromLowQueue( pSendOpData->BufferDesc[dw].dwBufferSize );
|
|
}
|
|
else
|
|
{
|
|
pAsyncOp->GetConnection()->RemoveFromNormalQueue( pSendOpData->BufferDesc[dw].dwBufferSize );
|
|
}
|
|
}
|
|
pAsyncOp->GetConnection()->Unlock();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Clean up
|
|
//
|
|
if (!pAsyncOp->IsUseParentOpData())
|
|
{
|
|
DN_SEND_OP_DATA *pSendOpData;
|
|
DWORD dw;
|
|
|
|
pSendOpData = pAsyncOp->GetLocalSendOpData();
|
|
for ( dw = 0 ; dw < pSendOpData->dwNumBuffers ; dw++ )
|
|
{
|
|
pSendOpData->BufferDesc[dw].dwBufferSize = 0;
|
|
pSendOpData->BufferDesc[dw].pBufferData = NULL;
|
|
}
|
|
pSendOpData->dwMsgId = 0;
|
|
pSendOpData->dwNumBuffers = 0;
|
|
}
|
|
|
|
if ( pAsyncOp->IsChild() )
|
|
{
|
|
DNASSERT(pAsyncOp->GetParent() != NULL);
|
|
|
|
pAsyncOp->GetParent()->Lock();
|
|
|
|
//
|
|
// Save HRESULT. Overwrite the parent's error while it's not DPN_OK.
|
|
//
|
|
if (pAsyncOp->GetParent()->GetResult() != DPN_OK)
|
|
{
|
|
pAsyncOp->GetParent()->SetResult( pAsyncOp->GetResult() );
|
|
}
|
|
|
|
//
|
|
// Release parent Handle if it exists
|
|
//
|
|
if (pAsyncOp->GetParent()->GetOpType() == ASYNC_OP_USER_HANDLE)
|
|
{
|
|
if (SUCCEEDED(pdnObject->HandleTable.Destroy( pAsyncOp->GetParent()->GetHandle(), NULL )))
|
|
{
|
|
// Release the HandleTable reference
|
|
pAsyncOp->GetParent()->Release();
|
|
}
|
|
}
|
|
|
|
pAsyncOp->GetParent()->Unlock();
|
|
}
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNCompleteRequest"
|
|
|
|
void DNCompleteRequest(DIRECTNETOBJECT *const pdnObject,
|
|
CAsyncOp *const pAsyncOp)
|
|
{
|
|
DWORD dw;
|
|
DN_SEND_OP_DATA *pSendOpData;
|
|
|
|
DNASSERT(pdnObject != NULL);
|
|
DNASSERT(pAsyncOp != NULL);
|
|
|
|
//
|
|
// Clean up op data
|
|
//
|
|
pSendOpData = pAsyncOp->GetLocalSendOpData();
|
|
for ( dw = 0 ; dw < pSendOpData->dwNumBuffers ; dw++ )
|
|
{
|
|
pSendOpData->BufferDesc[dw].dwBufferSize = 0;
|
|
pSendOpData->BufferDesc[dw].pBufferData = NULL;
|
|
}
|
|
pSendOpData->dwMsgId = 0;
|
|
pSendOpData->dwNumBuffers = 0;
|
|
pSendOpData = NULL;
|
|
|
|
//
|
|
// If the parent exists, copy the result up, and then remove them from the HandleTable
|
|
//
|
|
if (pAsyncOp->GetParent() != NULL)
|
|
{
|
|
pAsyncOp->GetParent()->Lock();
|
|
pAsyncOp->GetParent()->SetResult( pAsyncOp->GetResult() );
|
|
pAsyncOp->GetParent()->Unlock();
|
|
|
|
if (SUCCEEDED(pdnObject->HandleTable.Destroy( pAsyncOp->GetParent()->GetHandle(), NULL )))
|
|
{
|
|
// Release the HandleTable reference
|
|
pAsyncOp->GetParent()->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNCompleteSendRequest"
|
|
|
|
void DNCompleteSendRequest(DIRECTNETOBJECT *const pdnObject,
|
|
CAsyncOp *const pAsyncOp)
|
|
{
|
|
DNASSERT(pdnObject != NULL);
|
|
DNASSERT(pAsyncOp != NULL);
|
|
|
|
DNASSERT(pAsyncOp->GetParent() != NULL);
|
|
if (pAsyncOp->GetResult() != DPN_OK)
|
|
{
|
|
//
|
|
// If this operation was cancelled or was NOT internal (i.e. complete on process send)
|
|
// remove the parent (RequestChild) AsyncOp from the HandleTable and from the request list
|
|
//
|
|
if ((pAsyncOp->GetResult() == DPNERR_USERCANCEL) || !pAsyncOp->IsInternal())
|
|
{
|
|
DNASSERT(pAsyncOp->GetParent()->GetHandle() != 0);
|
|
DNASSERT(pAsyncOp->GetParent()->GetOpType() == ASYNC_OP_REQUEST);
|
|
DNEnterCriticalSection(&pdnObject->csActiveList);
|
|
pAsyncOp->GetParent()->m_bilinkActiveList.RemoveFromList();
|
|
DNLeaveCriticalSection(&pdnObject->csActiveList);
|
|
|
|
if (SUCCEEDED(pdnObject->HandleTable.Destroy( pAsyncOp->GetParent()->GetHandle(), NULL )))
|
|
{
|
|
// Release the HandleTable reference
|
|
pAsyncOp->GetParent()->Release();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Mark this operation as PLAYERLOST. If we do get an operation complete message,
|
|
// this result will get overwritten.
|
|
//
|
|
pAsyncOp->GetParent()->Lock();
|
|
if (pAsyncOp->GetParent()->GetResult() == DPNERR_GENERIC)
|
|
{
|
|
pAsyncOp->GetParent()->SetResult( DPNERR_PLAYERLOST );
|
|
}
|
|
pAsyncOp->GetParent()->Unlock();
|
|
}
|
|
}
|
|
|
|
|
|
// DNSendMessage
|
|
//
|
|
// Send structured message to given endpoint. Internally generated sends have a header
|
|
// associated with them. The first few bytes of this header are a signature, indicating
|
|
// an internal message. User generated sends do not have a header, unless they contain
|
|
// the signature. In this case, the user message is escaped with a header indicating this.
|
|
//
|
|
// For internal sends:
|
|
// - Create message header (dwMsgId,dwParam1,dwParam2)
|
|
// - Send message header and supplied data buffer (lpBuffDesc)
|
|
// - Save Async Operation info to be unwound when send completes
|
|
// - If pdnCountBuffer is specified, pBuffDesc may or may not point to its contents.
|
|
//
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNSendMessage"
|
|
|
|
HRESULT DNSendMessage(DIRECTNETOBJECT *const pdnObject,
|
|
CConnection *const pConnection, // Connection to send to
|
|
const DWORD dwMsgId, // Message ID
|
|
const DPNID dpnidTarget, // Target of this send (may be NULL)
|
|
const DPN_BUFFER_DESC *const pdnBufferDesc, // Array of buffer desc's
|
|
const DWORD cBufferDesc, // Number of buffer desc's
|
|
CRefCountBuffer *const pRefCountBuffer, // RefCountBuffer (may be NULL)
|
|
const DWORD dwTimeOut, // Time out
|
|
const DWORD dwSendFlags, // Send flags
|
|
CAsyncOp *const pParent, // Parent of this send
|
|
CAsyncOp **const ppAsyncOp) // CAsyncOp created for this send
|
|
{
|
|
DWORD dw;
|
|
HANDLE hProtocol;
|
|
HRESULT hResultCode;
|
|
HANDLE hEndPt;
|
|
CAsyncOp *pAsyncOp;
|
|
DN_SEND_OP_DATA *pSendOpData;
|
|
CCallbackThread CallbackThread;
|
|
DWORD dwStart;
|
|
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: pConnection [0x%p], dwMsgId [0x%x], dpnidTarget [0x%x], pdnBufferDesc [0x%p], cBufferDesc [%ld], pRefCountBuffer [0x%p], dwTimeOut [%ld], dwSendFlags [0x%lx], pParent [0x%p], ppAsyncOp [0x%p]",
|
|
pConnection,dwMsgId,dpnidTarget,pdnBufferDesc,cBufferDesc,pRefCountBuffer,dwTimeOut,dwSendFlags,pParent,ppAsyncOp);
|
|
|
|
DNASSERT(pdnObject != NULL);
|
|
DNASSERT(pConnection != NULL);
|
|
|
|
pAsyncOp = NULL;
|
|
pSendOpData = NULL;
|
|
CallbackThread.Initialize();
|
|
|
|
//
|
|
// Create AsyncOp for SEND
|
|
//
|
|
if ((hResultCode = AsyncOpNew(pdnObject,&pAsyncOp)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not create AsyncOp");
|
|
DisplayDNError(0,hResultCode);
|
|
goto Failure;
|
|
}
|
|
pAsyncOp->SetOpType( ASYNC_OP_SEND );
|
|
pAsyncOp->SetDPNID( dpnidTarget );
|
|
pAsyncOp->SetRefCountBuffer( pRefCountBuffer );
|
|
pAsyncOp->SetStartTime( GETTIMESTAMP() );
|
|
if (dwMsgId & DN_MSG_INTERNAL)
|
|
{
|
|
//
|
|
// We have to set this early (before it's in the active list or a child) so that it won't
|
|
// be prematurely cancelled (before we get a chance to mark it INTERNAL).
|
|
//
|
|
pAsyncOp->SetInternal();
|
|
}
|
|
|
|
//
|
|
// Make child if parent was supplied
|
|
//
|
|
if (pParent)
|
|
{
|
|
pParent->Lock();
|
|
if (pParent->IsCancelled())
|
|
{
|
|
pParent->Unlock();
|
|
pAsyncOp->SetResult( DPNERR_USERCANCEL );
|
|
hResultCode = DPNERR_USERCANCEL;
|
|
goto Failure;
|
|
}
|
|
pAsyncOp->MakeChild( pParent );
|
|
pParent->Unlock();
|
|
}
|
|
|
|
//
|
|
// Add to active async op list
|
|
//
|
|
DNEnterCriticalSection(&pdnObject->csActiveList);
|
|
pAsyncOp->m_bilinkActiveList.InsertBefore(&pdnObject->m_bilinkActiveList);
|
|
DNLeaveCriticalSection(&pdnObject->csActiveList);
|
|
|
|
//
|
|
// Set-up SEND op data block
|
|
//
|
|
pSendOpData = pAsyncOp->GetLocalSendOpData();
|
|
pSendOpData->dwMsgId = dwMsgId;
|
|
if (dwMsgId & DN_MSG_INTERNAL)
|
|
{
|
|
#ifndef DPNBUILD_NOVOICE
|
|
if (dwMsgId == DN_MSG_INTERNAL_VOICE_SEND)
|
|
{
|
|
DNASSERT(pdnBufferDesc != NULL);
|
|
|
|
dwStart = 0;
|
|
pAsyncOp->SetOpFlags( dwSendFlags | DN_SENDFLAGS_SET_USER_FLAG | DN_SENDFLAGS_SET_USER_FLAG_TWO | DN_SENDFLAGS_COALESCE );
|
|
}
|
|
else
|
|
#endif // DPNBUILD_NOVOICE
|
|
{
|
|
pSendOpData->BufferDesc[0].pBufferData = reinterpret_cast<BYTE*>( &(pSendOpData->dwMsgId) );
|
|
pSendOpData->BufferDesc[0].dwBufferSize = sizeof( DWORD );
|
|
|
|
dwStart = 1;
|
|
pAsyncOp->SetOpFlags( dwSendFlags | DN_SENDFLAGS_SET_USER_FLAG | DN_SENDFLAGS_COALESCE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DNASSERT(pdnBufferDesc != NULL);
|
|
|
|
dwStart = 0;
|
|
pAsyncOp->SetOpFlags( dwSendFlags );
|
|
}
|
|
for ( dw = 0 ; dw < cBufferDesc ; dw++ )
|
|
{
|
|
pSendOpData->BufferDesc[dwStart+dw].pBufferData = pdnBufferDesc[dw].pBufferData;
|
|
pSendOpData->BufferDesc[dwStart+dw].dwBufferSize = pdnBufferDesc[dw].dwBufferSize;
|
|
}
|
|
pSendOpData->dwNumBuffers = cBufferDesc + dwStart;
|
|
|
|
if ((hResultCode = pConnection->GetEndPt(&hEndPt,&CallbackThread)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not get end point from connection");
|
|
DisplayDNError(0,hResultCode);
|
|
hResultCode = DPNERR_CONNECTIONLOST; // re-map this
|
|
goto Failure;
|
|
}
|
|
pAsyncOp->SetConnection( pConnection );
|
|
|
|
if (hEndPt == NULL) // Message for local player - Put on local message queue
|
|
{
|
|
//
|
|
// INTERNAL Message
|
|
//
|
|
DPFX(DPFPREP, 5,"INTERNAL Message");
|
|
|
|
//
|
|
// AddRef Protocol as SendComplete will release this reference
|
|
//
|
|
DNProtocolAddRef(pdnObject);
|
|
|
|
pAsyncOp->AddRef();
|
|
hResultCode = DNWTSendInternal( pdnObject,
|
|
pAsyncOp );
|
|
|
|
pConnection->ReleaseEndPt(&CallbackThread);
|
|
|
|
DNASSERT( hResultCode == DPNERR_PENDING );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// EXTERNAL Message
|
|
//
|
|
DPFX(DPFPREP, 5,"EXTERNAL Message");
|
|
|
|
//
|
|
// AddRef Protocol so that it won't go away until this completes
|
|
//
|
|
DNProtocolAddRef(pdnObject);
|
|
|
|
pAsyncOp->AddRef();
|
|
hResultCode = DNPSendData( pdnObject->pdnProtocolData,
|
|
hEndPt,
|
|
pSendOpData->dwNumBuffers,
|
|
&pSendOpData->BufferDesc[0],
|
|
dwTimeOut,
|
|
pAsyncOp->GetOpFlags(),
|
|
reinterpret_cast<void*>(pAsyncOp),
|
|
&hProtocol);
|
|
|
|
pConnection->ReleaseEndPt(&CallbackThread);
|
|
|
|
if (hResultCode != DPNERR_PENDING)
|
|
{
|
|
if (hResultCode != DPN_OK)
|
|
{
|
|
DPFERR("Could not send data");
|
|
DisplayDNError(0,hResultCode);
|
|
}
|
|
pAsyncOp->Release();
|
|
DNProtocolRelease(pdnObject);
|
|
goto Failure;
|
|
}
|
|
|
|
pAsyncOp->Lock();
|
|
if (pAsyncOp->IsCancelled() && !pAsyncOp->IsComplete())
|
|
{
|
|
HRESULT hrCancel;
|
|
|
|
pAsyncOp->Unlock();
|
|
DPFX(DPFPREP, 7,"Operation marked for cancel");
|
|
if ((hrCancel = DNPCancelCommand(pdnObject->pdnProtocolData,hProtocol)) == DPN_OK)
|
|
{
|
|
hResultCode = DPNERR_USERCANCEL;
|
|
goto Failure;
|
|
}
|
|
DPFERR("Could not cancel operation");
|
|
DisplayDNError(0,hrCancel);
|
|
pAsyncOp->Lock();
|
|
}
|
|
pAsyncOp->SetProtocolHandle( hProtocol );
|
|
pAsyncOp->Unlock();
|
|
}
|
|
|
|
DNASSERT( hResultCode == DPNERR_PENDING );
|
|
|
|
//
|
|
// Update outstanding queue info for USER sends (now guaranteed to be cleaned up by the completion)
|
|
// and set completion
|
|
//
|
|
if (!(dwMsgId & DN_MSG_INTERNAL))
|
|
{
|
|
pConnection->Lock();
|
|
for ( dw = 0 ; dw < pSendOpData->dwNumBuffers ; dw++ )
|
|
{
|
|
if (dwSendFlags & DN_SENDFLAGS_HIGH_PRIORITY)
|
|
{
|
|
pConnection->AddToHighQueue( pSendOpData->BufferDesc[dw].dwBufferSize );
|
|
}
|
|
else if (dwSendFlags & DN_SENDFLAGS_LOW_PRIORITY)
|
|
{
|
|
pConnection->AddToLowQueue( pSendOpData->BufferDesc[dw].dwBufferSize );
|
|
}
|
|
else
|
|
{
|
|
pConnection->AddToNormalQueue( pSendOpData->BufferDesc[dw].dwBufferSize );
|
|
}
|
|
}
|
|
pConnection->Unlock();
|
|
}
|
|
pAsyncOp->SetCompletion( DNCompleteSendAsyncOp );
|
|
pSendOpData = NULL;
|
|
|
|
if (ppAsyncOp != NULL)
|
|
{
|
|
pAsyncOp->AddRef();
|
|
*ppAsyncOp = pAsyncOp;
|
|
}
|
|
|
|
pAsyncOp->Release();
|
|
pAsyncOp = NULL;
|
|
|
|
Exit:
|
|
|
|
CallbackThread.Deinitialize();
|
|
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (pAsyncOp)
|
|
{
|
|
DNEnterCriticalSection(&pdnObject->csActiveList);
|
|
pAsyncOp->m_bilinkActiveList.RemoveFromList();
|
|
DNLeaveCriticalSection(&pdnObject->csActiveList);
|
|
pAsyncOp->Release();
|
|
pAsyncOp = NULL;
|
|
}
|
|
if (pSendOpData)
|
|
{
|
|
for ( dw = 0 ; dw < pSendOpData->dwNumBuffers ; dw++ )
|
|
{
|
|
pSendOpData->BufferDesc[dw].dwBufferSize = 0;
|
|
pSendOpData->BufferDesc[dw].pBufferData = NULL;
|
|
}
|
|
pSendOpData->dwMsgId = 0;
|
|
pSendOpData->dwNumBuffers = 0;
|
|
pSendOpData = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNSendGroupMessage"
|
|
|
|
HRESULT DNSendGroupMessage(DIRECTNETOBJECT *const pdnObject,
|
|
CNameTableEntry *const pGroup,
|
|
const DWORD dwMsgId,
|
|
const DPN_BUFFER_DESC *const pdnBufferDesc,
|
|
const DWORD cBufferDesc,
|
|
CRefCountBuffer *const pRefCountBuffer,
|
|
const DWORD dwTimeOut,
|
|
const DWORD dwSendFlags,
|
|
const BOOL fNoLoopBack,
|
|
const BOOL fRequest,
|
|
CAsyncOp *const pParent,
|
|
CAsyncOp **const ppGroupSendParent)
|
|
{
|
|
HRESULT hResultCode;
|
|
CAsyncOp *pAsyncOp;
|
|
CAsyncOp *pGroupSendParent;
|
|
CBilink *pBilink;
|
|
CConnection *pConnection;
|
|
CGroupConnection *pGroupConnection;
|
|
DN_GROUP_SEND_OP *pSendOp;
|
|
DN_GROUP_SEND_OP *pTemp;
|
|
DPNID dpnidLocalPlayer;
|
|
|
|
DPFX(DPFPREP, 4,"Parameters: pGroup [0x%p], dwMsgId [0x%lx], pdnBufferDesc [0x%p], pRefCountBuffer [0x%p], dwTimeOut [%ld], dwSendFlags [0x%lx], fNoLoopBack [%ld], fRequest [%ld], pParent [0x%p], ppGroupSendParent [0x%p]",
|
|
pGroup,dwMsgId,pdnBufferDesc,pRefCountBuffer,dwTimeOut,dwSendFlags,fNoLoopBack,fRequest,pParent,ppGroupSendParent);
|
|
|
|
DNASSERT(pdnObject != NULL);
|
|
DNASSERT(pGroup != NULL);
|
|
DNASSERT(ppGroupSendParent != NULL);
|
|
|
|
pAsyncOp = NULL;
|
|
pGroupSendParent = NULL;
|
|
pConnection = NULL;
|
|
pGroupConnection = NULL;
|
|
|
|
if (fNoLoopBack)
|
|
{
|
|
CNameTableEntry *pLocalPlayer;
|
|
|
|
pLocalPlayer = NULL;
|
|
if ((hResultCode = pdnObject->NameTable.GetLocalPlayerRef( &pLocalPlayer )) != DPN_OK)
|
|
{
|
|
DPFERR("Could not get local player reference");
|
|
DisplayDNError(0,hResultCode);
|
|
goto Failure;
|
|
}
|
|
dpnidLocalPlayer = pLocalPlayer->GetDPNID();
|
|
pLocalPlayer->Release();
|
|
pLocalPlayer = NULL;
|
|
}
|
|
else
|
|
{
|
|
dpnidLocalPlayer = 0;
|
|
}
|
|
|
|
//
|
|
// Create group send target list
|
|
//
|
|
pSendOp = NULL;
|
|
pGroup->Lock();
|
|
pBilink = pGroup->m_bilinkConnections.GetNext();
|
|
while (pBilink != &pGroup->m_bilinkConnections)
|
|
{
|
|
pGroupConnection = CONTAINING_OBJECT(pBilink,CGroupConnection,m_bilink);
|
|
if ((hResultCode = pGroupConnection->GetConnectionRef( &pConnection )) == DPN_OK)
|
|
{
|
|
//
|
|
// We will only use CONNECTED connections (not CONNECTING,DISCONNECTING, or INVALID ones)
|
|
//
|
|
if (pConnection->IsConnected())
|
|
{
|
|
if ((!fNoLoopBack) || (pConnection->GetDPNID() != dpnidLocalPlayer))
|
|
{
|
|
//
|
|
// Save this connection
|
|
//
|
|
pTemp = static_cast<DN_GROUP_SEND_OP*>(MemoryBlockAlloc(pdnObject,sizeof(DN_GROUP_SEND_OP)));
|
|
if (pTemp == NULL)
|
|
{
|
|
pGroup->Unlock();
|
|
DPFERR("Could not create DN_GROUP_SEND_OP");
|
|
hResultCode = DPNERR_OUTOFMEMORY;
|
|
goto Failure;
|
|
}
|
|
pConnection->AddRef();
|
|
pTemp->pConnection = pConnection;
|
|
pTemp->pNext = pSendOp;
|
|
pSendOp = pTemp;
|
|
}
|
|
}
|
|
pConnection->Release();
|
|
pConnection = NULL;
|
|
}
|
|
pBilink = pBilink->GetNext();
|
|
}
|
|
pGroup->Unlock();
|
|
|
|
//
|
|
// Create group send parent
|
|
//
|
|
if ((hResultCode = DNCreateSendParent( pdnObject,
|
|
dwMsgId,
|
|
pdnBufferDesc,
|
|
cBufferDesc,
|
|
dwSendFlags,
|
|
&pGroupSendParent)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not create SEND parent");
|
|
DisplayDNError(0,hResultCode);
|
|
goto Failure;
|
|
}
|
|
if (pRefCountBuffer)
|
|
{
|
|
pGroupSendParent->SetRefCountBuffer( pRefCountBuffer );
|
|
}
|
|
pGroupSendParent->SetDPNID( pGroup->GetDPNID() );
|
|
|
|
//
|
|
// Make child if parent specified
|
|
//
|
|
if (pParent)
|
|
{
|
|
pParent->Lock();
|
|
if (pParent->IsCancelled())
|
|
{
|
|
pParent->Unlock();
|
|
pGroupSendParent->SetResult( DPNERR_USERCANCEL );
|
|
hResultCode = DPNERR_USERCANCEL;
|
|
goto Failure;
|
|
}
|
|
pGroupSendParent->MakeChild( pParent );
|
|
pParent->Unlock();
|
|
}
|
|
|
|
//
|
|
// Preset error code on send parent to DPNSUCCESS_NORECEIVERS
|
|
//
|
|
pGroupSendParent->Lock();
|
|
if (pGroupSendParent->GetResult() == DPNERR_GENERIC)
|
|
{
|
|
pGroupSendParent->SetResult( DPNSUCCESS_NOPLAYERSINGROUP );
|
|
}
|
|
pGroupSendParent->Unlock();
|
|
|
|
//
|
|
// Traverse send list and perform sends
|
|
//
|
|
while (pSendOp)
|
|
{
|
|
if (fRequest)
|
|
{
|
|
hResultCode = DNPerformRequest( pdnObject,
|
|
DN_MSG_INTERNAL_REQ_PROCESS_COMPLETION,
|
|
pdnBufferDesc,
|
|
pSendOp->pConnection,
|
|
pGroupSendParent,
|
|
&pAsyncOp);
|
|
}
|
|
else
|
|
{
|
|
hResultCode = DNPerformChildSend( pdnObject,
|
|
pGroupSendParent,
|
|
pSendOp->pConnection,
|
|
dwTimeOut,
|
|
&pAsyncOp,
|
|
FALSE);
|
|
}
|
|
|
|
if (pAsyncOp != NULL)
|
|
{
|
|
pAsyncOp->SetDPNID( pSendOp->pConnection->GetDPNID() );
|
|
pAsyncOp->Release();
|
|
pAsyncOp = NULL;
|
|
}
|
|
|
|
//
|
|
// Destroy old group send op
|
|
//
|
|
pTemp = pSendOp;
|
|
pSendOp = pSendOp->pNext;
|
|
pTemp->pConnection->Release();
|
|
pTemp->pConnection = NULL;
|
|
MemoryBlockFree(pdnObject,pTemp);
|
|
}
|
|
|
|
//
|
|
// Pass back group send parent
|
|
//
|
|
*ppGroupSendParent = pGroupSendParent;
|
|
pGroupSendParent = NULL;
|
|
|
|
hResultCode = DPN_OK;
|
|
|
|
Exit:
|
|
DPFX(DPFPREP, 4,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (pAsyncOp)
|
|
{
|
|
pAsyncOp->Release();
|
|
pAsyncOp = NULL;
|
|
}
|
|
if (pGroupSendParent)
|
|
{
|
|
pGroupSendParent->Release();
|
|
pGroupSendParent = NULL;
|
|
}
|
|
if (pConnection)
|
|
{
|
|
pConnection->Release();
|
|
pConnection = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNCreateSendParent"
|
|
|
|
HRESULT DNCreateSendParent(DIRECTNETOBJECT *const pdnObject,
|
|
const DWORD dwMsgId,
|
|
const DPN_BUFFER_DESC *const pdnBufferDesc,
|
|
const DWORD cBufferDesc,
|
|
const DWORD dwSendFlags,
|
|
CAsyncOp **const ppParent)
|
|
{
|
|
HRESULT hResultCode;
|
|
CAsyncOp *pAsyncOp;
|
|
DN_SEND_OP_DATA *pSendOpData;
|
|
DWORD dwStart;
|
|
DWORD dw;
|
|
|
|
DPFX(DPFPREP, 4,"Parameters: dwMsgId [0x%lx], pdnBufferDesc [0x%p], cBufferDesc [%ld], dwSendFlags [0x%lx], ppParent [0x%p]",
|
|
dwMsgId,pdnBufferDesc,cBufferDesc,dwSendFlags,ppParent);
|
|
|
|
DNASSERT(pdnObject != NULL);
|
|
DNASSERT(ppParent != NULL);
|
|
|
|
pAsyncOp = NULL;
|
|
pSendOpData = NULL;
|
|
|
|
if ((hResultCode = AsyncOpNew(pdnObject,&pAsyncOp)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not create AsyncOp");
|
|
DisplayDNError(0,hResultCode);
|
|
goto Failure;
|
|
}
|
|
|
|
pAsyncOp->SetOpType( ASYNC_OP_SEND );
|
|
pAsyncOp->MakeParent();
|
|
|
|
pSendOpData = pAsyncOp->GetLocalSendOpData();
|
|
pSendOpData->dwMsgId = dwMsgId;
|
|
|
|
if (dwMsgId & DN_MSG_INTERNAL)
|
|
{
|
|
#ifndef DPNBUILD_NOVOICE
|
|
if (dwMsgId == DN_MSG_INTERNAL_VOICE_SEND)
|
|
{
|
|
DNASSERT(pdnBufferDesc != NULL);
|
|
|
|
dwStart = 0;
|
|
pAsyncOp->SetOpFlags( dwSendFlags | DN_SENDFLAGS_SET_USER_FLAG | DN_SENDFLAGS_SET_USER_FLAG_TWO | DN_SENDFLAGS_COALESCE );
|
|
}
|
|
else
|
|
#endif // DPNBUILD_NOVOICE
|
|
{
|
|
pSendOpData->BufferDesc[0].pBufferData = reinterpret_cast<BYTE*>( &(pSendOpData->dwMsgId) );
|
|
pSendOpData->BufferDesc[0].dwBufferSize = sizeof( DWORD );
|
|
|
|
dwStart = 1;
|
|
pAsyncOp->SetOpFlags( dwSendFlags | DN_SENDFLAGS_SET_USER_FLAG | DN_SENDFLAGS_COALESCE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DNASSERT(pdnBufferDesc != NULL);
|
|
|
|
dwStart = 0;
|
|
pAsyncOp->SetOpFlags( dwSendFlags );
|
|
}
|
|
for ( dw = 0 ; dw < cBufferDesc ; dw++ )
|
|
{
|
|
pSendOpData->BufferDesc[dwStart+dw].pBufferData = pdnBufferDesc[dw].pBufferData;
|
|
pSendOpData->BufferDesc[dwStart+dw].dwBufferSize = pdnBufferDesc[dw].dwBufferSize;
|
|
}
|
|
pSendOpData->dwNumBuffers = cBufferDesc + dwStart;
|
|
|
|
//
|
|
// Completion
|
|
//
|
|
pAsyncOp->SetCompletion( DNCompleteSendAsyncOp );
|
|
pSendOpData = NULL;
|
|
|
|
*ppParent = pAsyncOp;
|
|
pAsyncOp = NULL;
|
|
|
|
Exit:
|
|
DPFX(DPFPREP, 4,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (pAsyncOp)
|
|
{
|
|
pAsyncOp->Release();
|
|
pAsyncOp = NULL;
|
|
}
|
|
if (pSendOpData)
|
|
{
|
|
for ( dw = 0 ; dw < pSendOpData->dwNumBuffers ; dw++ )
|
|
{
|
|
pSendOpData->BufferDesc[dw].dwBufferSize = 0;
|
|
pSendOpData->BufferDesc[dw].pBufferData = NULL;
|
|
}
|
|
pSendOpData->dwMsgId = 0;
|
|
pSendOpData->dwNumBuffers = 0;
|
|
pSendOpData = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNPerformChildSend"
|
|
|
|
HRESULT DNPerformChildSend(DIRECTNETOBJECT *const pdnObject,
|
|
CAsyncOp *const pParent,
|
|
CConnection *const pConnection,
|
|
const DWORD dwTimeOut,
|
|
CAsyncOp **const ppChild,
|
|
const BOOL fInternal)
|
|
{
|
|
HRESULT hResultCode;
|
|
DWORD dw;
|
|
CAsyncOp *pAsyncOp;
|
|
DN_SEND_OP_DATA *pSendOpData;
|
|
HANDLE hEndPt;
|
|
HANDLE hProtocol;
|
|
CCallbackThread CallbackThread;
|
|
|
|
DPFX(DPFPREP, 4,"Parameters: pParent [0x%p], pConnection [0x%p], dwTimeOut [%ld]",
|
|
pParent,pConnection,dwTimeOut);
|
|
|
|
DNASSERT(pdnObject != NULL);
|
|
DNASSERT(pParent != NULL);
|
|
DNASSERT(pConnection != NULL);
|
|
|
|
pAsyncOp = NULL;
|
|
CallbackThread.Initialize();
|
|
|
|
if ((hResultCode = AsyncOpNew(pdnObject,&pAsyncOp)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not create AsyncOp");
|
|
DisplayDNError(0,hResultCode);
|
|
goto Failure;
|
|
}
|
|
pAsyncOp->SetOpType( ASYNC_OP_SEND );
|
|
pAsyncOp->SetCompletion( DNCompleteSendAsyncOp );
|
|
pAsyncOp->SetOpFlags( pParent->GetOpFlags() );
|
|
if (fInternal)
|
|
{
|
|
pAsyncOp->SetInternal();
|
|
}
|
|
|
|
pParent->Lock();
|
|
if (pParent->IsCancelled())
|
|
{
|
|
pParent->Unlock();
|
|
pAsyncOp->SetResult( DPNERR_USERCANCEL );
|
|
hResultCode = DPNERR_USERCANCEL;
|
|
goto Failure;
|
|
}
|
|
pAsyncOp->MakeChild( pParent );
|
|
pParent->Unlock();
|
|
|
|
//
|
|
// Add to active async op list
|
|
//
|
|
DNEnterCriticalSection(&pdnObject->csActiveList);
|
|
pAsyncOp->m_bilinkActiveList.InsertBefore(&pdnObject->m_bilinkActiveList);
|
|
DNLeaveCriticalSection(&pdnObject->csActiveList);
|
|
|
|
//
|
|
// Get parent's send op data block
|
|
//
|
|
pSendOpData = pParent->GetLocalSendOpData();
|
|
|
|
//
|
|
// Save connection and get end point
|
|
//
|
|
if ((hResultCode = pConnection->GetEndPt(&hEndPt,&CallbackThread)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not retrieve EndPt");
|
|
DisplayDNError(0,hResultCode);
|
|
goto Failure;
|
|
}
|
|
pAsyncOp->SetConnection( pConnection );
|
|
|
|
//
|
|
// Update outstanding queue info (now guaranteed to be cleaned up by the completion once UseParentOpData is set)
|
|
//
|
|
if (pSendOpData->dwMsgId == DN_MSG_USER_SEND)
|
|
{
|
|
for ( dw = 0 ; dw < pSendOpData->dwNumBuffers ; dw++ )
|
|
{
|
|
pConnection->Lock();
|
|
if (pAsyncOp->GetOpFlags() & DN_SENDFLAGS_HIGH_PRIORITY)
|
|
{
|
|
pConnection->AddToHighQueue( pSendOpData->BufferDesc[dw].dwBufferSize );
|
|
}
|
|
else if (pAsyncOp->GetOpFlags() & DN_SENDFLAGS_LOW_PRIORITY)
|
|
{
|
|
pConnection->AddToLowQueue( pSendOpData->BufferDesc[dw].dwBufferSize );
|
|
}
|
|
else
|
|
{
|
|
pConnection->AddToNormalQueue( pSendOpData->BufferDesc[dw].dwBufferSize );
|
|
}
|
|
pConnection->Unlock();
|
|
}
|
|
}
|
|
pAsyncOp->SetUseParentOpData();
|
|
|
|
if (hEndPt == NULL)
|
|
{
|
|
//
|
|
// INTERNAL Message
|
|
//
|
|
DPFX(DPFPREP, 5,"INTERNAL Message");
|
|
|
|
//
|
|
// AddRef Protocol as SendComplete will release this reference
|
|
//
|
|
DNProtocolAddRef(pdnObject);
|
|
|
|
pAsyncOp->AddRef();
|
|
hResultCode = DNWTSendInternal( pdnObject,
|
|
pAsyncOp );
|
|
|
|
pConnection->ReleaseEndPt(&CallbackThread);
|
|
|
|
#pragma BUGBUG(minara, "Handle out-of-memory case correctly")
|
|
DNASSERT(hResultCode == DPNERR_PENDING);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// EXTERNAL Message
|
|
//
|
|
DPFX(DPFPREP, 5,"EXTERNAL Message");
|
|
|
|
//
|
|
// AddRef Protocol so that it won't go away until this completes
|
|
//
|
|
DNProtocolAddRef(pdnObject);
|
|
|
|
pAsyncOp->AddRef();
|
|
hResultCode = DNPSendData( pdnObject->pdnProtocolData,
|
|
hEndPt,
|
|
pSendOpData->dwNumBuffers,
|
|
&pSendOpData->BufferDesc[0],
|
|
dwTimeOut,
|
|
pParent->GetOpFlags(),
|
|
reinterpret_cast<void*>(pAsyncOp),
|
|
&hProtocol);
|
|
pConnection->ReleaseEndPt(&CallbackThread);
|
|
|
|
if (hResultCode != DPNERR_PENDING)
|
|
{
|
|
if (hResultCode != DPN_OK)
|
|
{
|
|
DPFERR("SEND failed at Protocol layer");
|
|
DisplayDNError(0,hResultCode);
|
|
}
|
|
pAsyncOp->Release();
|
|
DNProtocolRelease(pdnObject);
|
|
goto Failure;
|
|
}
|
|
|
|
pAsyncOp->Lock();
|
|
if (pAsyncOp->IsCancelled() && !pAsyncOp->IsComplete())
|
|
{
|
|
HRESULT hrCancel;
|
|
|
|
pAsyncOp->Unlock();
|
|
DPFX(DPFPREP, 7,"Operation marked for cancel");
|
|
if ((hrCancel = DNPCancelCommand(pdnObject->pdnProtocolData,hProtocol)) == DPN_OK)
|
|
{
|
|
hResultCode = DPNERR_USERCANCEL;
|
|
goto Failure;
|
|
}
|
|
DPFERR("Could not cancel operation");
|
|
DisplayDNError(0,hrCancel);
|
|
pAsyncOp->Lock();
|
|
}
|
|
pAsyncOp->SetProtocolHandle( hProtocol );
|
|
pAsyncOp->Unlock();
|
|
}
|
|
|
|
//
|
|
// If the caller wants a reference on this operation, give it to them
|
|
//
|
|
if (ppChild != NULL)
|
|
{
|
|
pAsyncOp->AddRef();
|
|
*ppChild = pAsyncOp;
|
|
}
|
|
|
|
pAsyncOp->Release();
|
|
pAsyncOp = NULL;
|
|
|
|
Exit:
|
|
|
|
CallbackThread.Deinitialize();
|
|
|
|
DPFX(DPFPREP, 4,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (pAsyncOp)
|
|
{
|
|
DNEnterCriticalSection(&pdnObject->csActiveList);
|
|
pAsyncOp->m_bilinkActiveList.RemoveFromList();
|
|
DNLeaveCriticalSection(&pdnObject->csActiveList);
|
|
pAsyncOp->Release();
|
|
pAsyncOp = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
// DN_ProcessInternalOperation
|
|
//
|
|
// Process an internal operation
|
|
//
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DN_ProcessInternalOperation"
|
|
|
|
HRESULT DNProcessInternalOperation(DIRECTNETOBJECT *const pdnObject,
|
|
const DWORD dwMsgId,
|
|
void *const pOpBuffer,
|
|
const DWORD dwOpBufferSize,
|
|
CConnection *const pConnection,
|
|
const HANDLE hProtocol,
|
|
CRefCountBuffer *const pRefCountBuffer)
|
|
{
|
|
HRESULT hResultCode;
|
|
CRefCountBuffer *pRCBuffer;
|
|
CWorkerJob *pWorkerJob;
|
|
BOOL fDecRunningOpCount;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: dwMsgId [0x%lx], pOpBuffer [0x%p], dwOpBufferSize [%ld], pConnection [0x%p], hProtocol [0x%p], pRefCountBuffer [0x%p]",
|
|
dwMsgId,pOpBuffer,dwOpBufferSize,pConnection,hProtocol,pRefCountBuffer);
|
|
|
|
hResultCode = DPN_OK;
|
|
pRCBuffer = NULL;
|
|
pWorkerJob = NULL;
|
|
fDecRunningOpCount = FALSE;
|
|
|
|
//
|
|
// We will track the number of running operations (i.e. threads running through this function).
|
|
// At the start of host migration, we will need to send the new host the latest name table version,
|
|
// so we will need to let running operations finish, before sending in the name table version.
|
|
// If another thread is waiting, we will not perform any operations (other than host migration)
|
|
//
|
|
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
|
|
if ((pdnObject->dwFlags & DN_OBJECT_FLAG_HOST_MIGRATING_WAIT) && (dwMsgId != DN_MSG_INTERNAL_HOST_MIGRATE))
|
|
{
|
|
DPFX(DPFPREP,7,"Already waiting for running operations - ignoring this operation");
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
if (dwMsgId != DN_MSG_INTERNAL_HOST_MIGRATE)
|
|
{
|
|
pdnObject->dwRunningOpCount++;
|
|
fDecRunningOpCount = TRUE;
|
|
}
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
|
|
switch(dwMsgId)
|
|
{
|
|
case DN_MSG_INTERNAL_PLAYER_CONNECT_INFO:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_PLAYER_CONNECT_INFO");
|
|
|
|
if (DNVerifyPlayerConnectInfo(pOpBuffer,dwOpBufferSize) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Perform validations and send name table to player
|
|
//
|
|
hResultCode = DNHostConnect1(pdnObject,pOpBuffer,dwOpBufferSize,pConnection);
|
|
|
|
hResultCode = DPN_OK; // Ignore return code
|
|
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_SEND_CONNECT_INFO:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_SEND_CONNECT_INFO");
|
|
|
|
if (DNVerifyConnectInfo(pOpBuffer,dwOpBufferSize) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// We will pass this stage off to the worker thread since there is a threading
|
|
// issue, and we cannot keep the SP's thread which was passed up to here.
|
|
//
|
|
|
|
if ((hResultCode = RefCountBufferNew(pdnObject,dwOpBufferSize,MemoryBlockAlloc,MemoryBlockFree,&pRCBuffer)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not allocate RefCountBuffer");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(FALSE);
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
memcpy(pRCBuffer->GetBufferAddress(),pOpBuffer,dwOpBufferSize);
|
|
|
|
if ((hResultCode = WorkerJobNew(pdnObject,&pWorkerJob)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not create WorkerJob");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(FALSE);
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
pWorkerJob->SetJobType( WORKER_JOB_INSTALL_NAMETABLE );
|
|
pWorkerJob->SetConnection( pConnection );
|
|
pWorkerJob->SetRefCountBuffer( pRCBuffer );
|
|
|
|
DNQueueWorkerJob(pdnObject,pWorkerJob);
|
|
pWorkerJob = NULL;
|
|
|
|
pRCBuffer->Release();
|
|
pRCBuffer = NULL;
|
|
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_ACK_CONNECT_INFO:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_ACK_CONNECT_INFO");
|
|
|
|
//
|
|
// No verification as there is no payload with this message
|
|
//
|
|
|
|
//
|
|
// Process connect info acknowledge by host
|
|
//
|
|
hResultCode = DNHostConnect2(pdnObject,pConnection); // Ignore errors
|
|
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_SEND_PLAYER_DNID:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_SEND_PLAYER_DNID");
|
|
|
|
if (DNVerifySendPlayerDPNID(pOpBuffer,dwOpBufferSize) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Send this player's DNID to the connecting player to enable name table entry
|
|
//
|
|
hResultCode = DNPlayerConnect1(pdnObject,pOpBuffer,pConnection); // Ignore errors
|
|
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_CONNECT_FAILED:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_CONNECT_FAILED");
|
|
|
|
if (DNVerifyConnectFailed(pOpBuffer,dwOpBufferSize) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Save reply buffer and clean up
|
|
//
|
|
DNConnectToHostFailed(pdnObject,pOpBuffer,dwOpBufferSize,pConnection);
|
|
|
|
hResultCode = DPN_OK;
|
|
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_INSTRUCT_CONNECT:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_INSTRUCT_CONNECT");
|
|
|
|
if (DNVerifyInstructConnect(pOpBuffer,dwOpBufferSize) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
hResultCode = DNNTAddOperation( pdnObject,
|
|
DN_MSG_INTERNAL_INSTRUCT_CONNECT,
|
|
pOpBuffer,
|
|
dwOpBufferSize,
|
|
hProtocol,
|
|
pConnection->GetSP());
|
|
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_INSTRUCTED_CONNECT_FAILED:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_INSTRUCTED_CONNECT_FAILED");
|
|
|
|
if (DNVerifyInstructedConnectFailed(pOpBuffer,dwOpBufferSize) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
hResultCode = DNHostDropPlayer( pdnObject,
|
|
pConnection->GetDPNID(),
|
|
pOpBuffer); // Ignore errors
|
|
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_CONNECT_ATTEMPT_FAILED:
|
|
{
|
|
UNALIGNED DN_INTERNAL_MESSAGE_CONNECT_ATTEMPT_FAILED *pInfo;
|
|
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_CONNECT_ATTEMPT_FAILED");
|
|
|
|
if (DNVerifyConnectAttemptFailed(pOpBuffer,dwOpBufferSize) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
pInfo = static_cast<DN_INTERNAL_MESSAGE_CONNECT_ATTEMPT_FAILED*>(pOpBuffer);
|
|
DPFX(DPFPREP, 7,"Player [0x%lx] could not connect to us",pInfo->dpnid);
|
|
|
|
hResultCode = DNAbortConnect(pdnObject,DPNERR_PLAYERNOTREACHABLE); // Ignore errors
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_NAMETABLE_VERSION:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_NAMETABLE_VERSION");
|
|
#ifndef DPNBUILD_NOHOSTMIGRATE
|
|
if (DNVerifyNameTableVersion(pOpBuffer,dwOpBufferSize) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
hResultCode = DNNTHostReceiveVersion(pdnObject,pConnection->GetDPNID(),pOpBuffer);
|
|
#else
|
|
DNASSERT(!"Should never get here!");
|
|
#endif // DPNBUILD_NOHOSTMIGRATE
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_RESYNC_VERSION:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_RESYNC_VERSION");
|
|
#ifndef DPNBUILD_NOHOSTMIGRATE
|
|
if (DNVerifyResyncVersion(pOpBuffer,dwOpBufferSize) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
hResultCode = DNNTPlayerResyncVersion(pdnObject,pOpBuffer);
|
|
#else
|
|
DNASSERT(!"Should never get here!");
|
|
#endif // DPNBUILD_NOHOSTMIGRATE
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_REQ_NAMETABLE_OP:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_REQ_NAMETABLE_OP");
|
|
#ifndef DPNBUILD_NOHOSTMIGRATE
|
|
if (DNVerifyReqNameTableOp(pOpBuffer,dwOpBufferSize) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
hResultCode = DNProcessHostMigration2(pdnObject,pOpBuffer);
|
|
#else
|
|
DNASSERT(!"Should never get here!");
|
|
#endif // DPNBUILD_NOHOSTMIGRATE
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_ACK_NAMETABLE_OP:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_ACK_NAMETABLE_OP");
|
|
#ifndef DPNBUILD_NOHOSTMIGRATE
|
|
if (DNVerifyAckNameTableOp(pOpBuffer,dwOpBufferSize) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
hResultCode = DNPerformHostMigration3(pdnObject,pOpBuffer);
|
|
#else
|
|
DNASSERT(!"Should never get here!");
|
|
#endif // DPNBUILD_NOHOSTMIGRATE
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_HOST_MIGRATE:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_HOST_MIGRATE");
|
|
#ifndef DPNBUILD_NOHOSTMIGRATE
|
|
if (DNVerifyHostMigrate(pOpBuffer,dwOpBufferSize) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
hResultCode = DNProcessHostMigration1(pdnObject,pOpBuffer);
|
|
#else
|
|
DNASSERT(!"Should never get here!");
|
|
#endif // DPNBUILD_NOHOSTMIGRATE
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_HOST_MIGRATE_COMPLETE:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_HOST_MIGRATE_COMPLETE");
|
|
#ifndef DPNBUILD_NOHOSTMIGRATE
|
|
//
|
|
// No verification as there is no payload with this message
|
|
//
|
|
hResultCode = DNProcessHostMigration3(pdnObject,pConnection->GetDPNID());
|
|
#else
|
|
DNASSERT(!"Should never get here!");
|
|
#endif // DPNBUILD_NOHOSTMIGRATE
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_UPDATE_APPLICATION_DESC:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_UPDATE_APPLICATION_DESC");
|
|
|
|
if (DNVerifyApplicationDescInfo(pOpBuffer,dwOpBufferSize,pOpBuffer) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
hResultCode = DNProcessUpdateAppDesc(pdnObject,static_cast<DPN_APPLICATION_DESC_INFO*>(pOpBuffer));
|
|
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_ADD_PLAYER:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_ADD_PLAYER");
|
|
|
|
if (DNVerifyNameTableEntryInfo(pOpBuffer,dwOpBufferSize,pOpBuffer) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
hResultCode = DNNTAddOperation( pdnObject,
|
|
DN_MSG_INTERNAL_ADD_PLAYER,
|
|
pOpBuffer,
|
|
dwOpBufferSize,
|
|
hProtocol,
|
|
pConnection->GetSP());
|
|
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_DESTROY_PLAYER:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_DESTROY_PLAYER");
|
|
|
|
if (DNVerifyDestroyPlayer(pOpBuffer,dwOpBufferSize) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
hResultCode = DNNTAddOperation( pdnObject,
|
|
DN_MSG_INTERNAL_DESTROY_PLAYER,
|
|
pOpBuffer,
|
|
dwOpBufferSize,
|
|
hProtocol,
|
|
pConnection->GetSP());
|
|
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_CREATE_GROUP:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_CREATE_GROUP");
|
|
|
|
if (DNVerifyCreateGroup(pOpBuffer,dwOpBufferSize,pOpBuffer) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
hResultCode = DNNTAddOperation( pdnObject,
|
|
DN_MSG_INTERNAL_CREATE_GROUP,
|
|
pOpBuffer,
|
|
dwOpBufferSize,
|
|
hProtocol,
|
|
pConnection->GetSP());
|
|
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_DESTROY_GROUP:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_DESTROY_GROUP");
|
|
|
|
if (DNVerifyDestroyGroup(pOpBuffer,dwOpBufferSize) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
hResultCode = DNNTAddOperation( pdnObject,
|
|
DN_MSG_INTERNAL_DESTROY_GROUP,
|
|
pOpBuffer,
|
|
dwOpBufferSize,
|
|
hProtocol,
|
|
pConnection->GetSP());
|
|
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_ADD_PLAYER_TO_GROUP:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_ADD_PLAYER_TO_GROUP");
|
|
|
|
if (DNVerifyAddPlayerToGroup(pOpBuffer,dwOpBufferSize) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
hResultCode = DNNTAddOperation( pdnObject,
|
|
DN_MSG_INTERNAL_ADD_PLAYER_TO_GROUP,
|
|
pOpBuffer,
|
|
dwOpBufferSize,
|
|
hProtocol,
|
|
pConnection->GetSP());
|
|
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_DELETE_PLAYER_FROM_GROUP:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_DELETE_PLAYER_FROM_GROUP");
|
|
|
|
if (DNVerifyDeletePlayerFromGroup(pOpBuffer,dwOpBufferSize) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
hResultCode = DNNTAddOperation( pdnObject,
|
|
DN_MSG_INTERNAL_DELETE_PLAYER_FROM_GROUP,
|
|
pOpBuffer,
|
|
dwOpBufferSize,
|
|
hProtocol,
|
|
pConnection->GetSP());
|
|
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_UPDATE_INFO:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_UPDATE_INFO");
|
|
|
|
if (DNVerifyUpdateInfo(pOpBuffer,dwOpBufferSize,pOpBuffer) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
hResultCode = DNNTAddOperation( pdnObject,
|
|
DN_MSG_INTERNAL_UPDATE_INFO,
|
|
pOpBuffer,
|
|
dwOpBufferSize,
|
|
hProtocol,
|
|
pConnection->GetSP());
|
|
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_REQ_CREATE_GROUP:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_REQ_CREATE_GROUP");
|
|
|
|
if (DNVerifyReqCreateGroup(pOpBuffer,dwOpBufferSize,pOpBuffer) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
hResultCode = DNHostProcessRequest(pdnObject,DN_MSG_INTERNAL_REQ_CREATE_GROUP,
|
|
pOpBuffer,pConnection->GetDPNID());
|
|
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_REQ_DESTROY_GROUP:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_REQ_DESTROY_GROUP");
|
|
|
|
if (DNVerifyReqDestroyGroup(pOpBuffer,dwOpBufferSize) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
hResultCode = DNHostProcessRequest(pdnObject,DN_MSG_INTERNAL_REQ_DESTROY_GROUP,
|
|
pOpBuffer,pConnection->GetDPNID());
|
|
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_REQ_ADD_PLAYER_TO_GROUP:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_REQ_ADD_PLAYER_TO_GROUP");
|
|
|
|
if (DNVerifyReqAddPlayerToGroup(pOpBuffer,dwOpBufferSize) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
hResultCode = DNHostProcessRequest(pdnObject,
|
|
DN_MSG_INTERNAL_REQ_ADD_PLAYER_TO_GROUP,pOpBuffer,pConnection->GetDPNID());
|
|
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_REQ_DELETE_PLAYER_FROM_GROUP:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_REQ_DELETE_PLAYER_FROM_GROUP");
|
|
|
|
if (DNVerifyReqDeletePlayerFromGroup(pOpBuffer,dwOpBufferSize) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
hResultCode = DNHostProcessRequest(pdnObject,
|
|
DN_MSG_INTERNAL_REQ_DELETE_PLAYER_FROM_GROUP,pOpBuffer,pConnection->GetDPNID());
|
|
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_REQ_UPDATE_INFO:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_REQ_UPDATE_INFO");
|
|
|
|
if (DNVerifyReqUpdateInfo(pOpBuffer,dwOpBufferSize,pOpBuffer) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
hResultCode = DNHostProcessRequest(pdnObject,DN_MSG_INTERNAL_REQ_UPDATE_INFO,
|
|
pOpBuffer,pConnection->GetDPNID());
|
|
|
|
break;
|
|
}
|
|
|
|
#ifndef DPNBUILD_NOVOICE
|
|
case DN_MSG_INTERNAL_VOICE_SEND:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_VOICE_SEND");
|
|
|
|
hResultCode = Voice_Receive( pdnObject, pConnection->GetDPNID(), 0, pOpBuffer, dwOpBufferSize );
|
|
|
|
break;
|
|
}
|
|
#endif // DPNBUILD_NOVOICE
|
|
|
|
case DN_MSG_INTERNAL_BUFFER_IN_USE:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_BUFFER_IN_USE - INVALID !");
|
|
DNASSERT(FALSE);
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_REQUEST_FAILED:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_REQUEST_FAILED - INVALID !");
|
|
|
|
if (DNVerifyRequestFailed(pOpBuffer,dwOpBufferSize) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
hResultCode = DNProcessFailedRequest(pdnObject,pOpBuffer);
|
|
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_TERMINATE_SESSION:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_TERMINATE_SESSION");
|
|
|
|
if (DNVerifyTerminateSession(pOpBuffer,dwOpBufferSize) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
hResultCode = DNProcessTerminateSession(pdnObject,pOpBuffer,dwOpBufferSize);
|
|
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_REQ_PROCESS_COMPLETION:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_REQ_PROCESS_COMPLETION");
|
|
|
|
if (DNVerifyReqProcessCompletion(pOpBuffer,dwOpBufferSize) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Ensure requesting player in NameTable
|
|
//
|
|
hResultCode = DNReceiveCompleteOnProcess(pdnObject,pConnection,pOpBuffer,
|
|
dwOpBufferSize,hProtocol,pRefCountBuffer);
|
|
|
|
if (hResultCode != DPNERR_PENDING)
|
|
{
|
|
hResultCode = DPN_OK; // Ignore errors
|
|
}
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_PROCESS_COMPLETION:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_PROCESS_COMPLETION");
|
|
|
|
if (DNVerifyProcessCompletion(pOpBuffer,dwOpBufferSize) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
hResultCode = DNReceiveCompleteOnProcessReply(pdnObject,pOpBuffer,dwOpBufferSize);
|
|
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_REQ_INTEGRITY_CHECK:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_REQ_INTEGRITY_CHECK");
|
|
|
|
if (DNVerifyReqIntegrityCheck(pOpBuffer,dwOpBufferSize) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
hResultCode = DNHostProcessRequest(pdnObject,DN_MSG_INTERNAL_REQ_INTEGRITY_CHECK,
|
|
pOpBuffer,pConnection->GetDPNID());
|
|
|
|
hResultCode = DPN_OK;
|
|
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_INTEGRITY_CHECK:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_INTEGRITY_CHECK");
|
|
|
|
if (DNVerifyIntegrityCheck(pOpBuffer,dwOpBufferSize) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
hResultCode = DNProcessCheckIntegrity(pdnObject,pOpBuffer);
|
|
|
|
hResultCode = DPN_OK; // Ignore errors
|
|
|
|
break;
|
|
}
|
|
|
|
case DN_MSG_INTERNAL_INTEGRITY_CHECK_RESPONSE:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL_INTEGRITY_CHECK_RESPONSE");
|
|
|
|
if (DNVerifyIntegrityCheckResponse(pOpBuffer,dwOpBufferSize) != DPN_OK)
|
|
{
|
|
DPFERR("Invalid message - ignoring");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
hResultCode = DNHostFixIntegrity(pdnObject,pOpBuffer);
|
|
|
|
hResultCode = DPN_OK; // Ignore errors
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DPFX(DPFPREP, 7,"Received: DN_MSG_INTERNAL (UNKNOWN!)");
|
|
DNASSERT(FALSE);
|
|
hResultCode = DPNERR_UNSUPPORTED;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
|
|
if (fDecRunningOpCount)
|
|
{
|
|
pdnObject->dwRunningOpCount--;
|
|
}
|
|
if ((pdnObject->dwRunningOpCount == 0) && (pdnObject->dwFlags & DN_OBJECT_FLAG_HOST_MIGRATING_WAIT))
|
|
{
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
DNSetEvent(pdnObject->hRunningOpEvent);
|
|
}
|
|
else
|
|
{
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
}
|
|
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (pRCBuffer)
|
|
{
|
|
pRCBuffer->Release();
|
|
pRCBuffer = NULL;
|
|
}
|
|
if (pWorkerJob)
|
|
{
|
|
pWorkerJob->ReturnSelfToPool();
|
|
pWorkerJob = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNPerformRequest"
|
|
|
|
HRESULT DNPerformRequest(DIRECTNETOBJECT *const pdnObject,
|
|
const DWORD dwMsgId,
|
|
const DPN_BUFFER_DESC *const pBufferDesc,
|
|
CConnection *const pConnection,
|
|
CAsyncOp *const pParent,
|
|
CAsyncOp **const ppRequest)
|
|
{
|
|
DWORD dwFlags;
|
|
HRESULT hResultCode;
|
|
BOOL fInternal;
|
|
BOOL fReleaseLock;
|
|
CAsyncOp *pRequest;
|
|
CAsyncOp *pSend;
|
|
CRefCountBuffer *pRefCountBuffer;
|
|
DN_SEND_OP_DATA *pSendOpData;
|
|
DPNHANDLE handle;
|
|
DN_INTERNAL_MESSAGE_REQ_PROCESS_COMPLETION *pMsg;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: dwMsgId [0x%lx], pBufferDesc [0x%p], pConnection [0x%p], pParent [0x%p], ppRequest [0x%p]",
|
|
dwMsgId,pBufferDesc,pConnection,pParent,ppRequest);
|
|
|
|
DNASSERT(pdnObject != NULL);
|
|
DNASSERT(pBufferDesc != NULL);
|
|
|
|
pRequest = NULL;
|
|
pSend = NULL;
|
|
pSendOpData = NULL;
|
|
pRefCountBuffer = NULL;
|
|
fReleaseLock = FALSE;
|
|
|
|
//
|
|
// Create RefCountBuffer for this operation
|
|
//
|
|
hResultCode = RefCountBufferNew(pdnObject,
|
|
pBufferDesc->dwBufferSize + sizeof(DN_INTERNAL_MESSAGE_REQ_PROCESS_COMPLETION),
|
|
MemoryBlockAlloc,
|
|
MemoryBlockFree,
|
|
&pRefCountBuffer);
|
|
if (hResultCode != DPN_OK)
|
|
{
|
|
DPFERR("Could not create RefCountBuffer");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(FALSE);
|
|
goto Failure;
|
|
}
|
|
pMsg = reinterpret_cast<DN_INTERNAL_MESSAGE_REQ_PROCESS_COMPLETION*>(pRefCountBuffer->GetBufferAddress());
|
|
memcpy(pMsg + 1,pBufferDesc->pBufferData,pBufferDesc->dwBufferSize);
|
|
|
|
//
|
|
// Keep DirectNetObject from vanishing under us !
|
|
//
|
|
if ((hResultCode = DNAddRefLock(pdnObject)) != DPN_OK)
|
|
{
|
|
DPFERR("Aborting request - object is closing");
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
fReleaseLock = TRUE;
|
|
|
|
//
|
|
// Create REQUEST
|
|
//
|
|
if ((hResultCode = AsyncOpNew(pdnObject,&pRequest)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not create AsyncOp");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(FALSE);
|
|
goto Failure;
|
|
}
|
|
pRequest->SetOpType( ASYNC_OP_REQUEST );
|
|
if (pParent)
|
|
{
|
|
pParent->Lock();
|
|
if (pParent->IsCancelled())
|
|
{
|
|
pParent->Unlock();
|
|
pRequest->SetResult( DPNERR_USERCANCEL );
|
|
hResultCode = DPNERR_USERCANCEL;
|
|
goto Failure;
|
|
}
|
|
pRequest->MakeChild( pParent );
|
|
pParent->Unlock();
|
|
}
|
|
pRequest->MakeParent();
|
|
pRequest->SetRefCountBuffer( pRefCountBuffer );
|
|
|
|
//
|
|
// Need a handle for this op (to be sent to the other side who will pass it back)
|
|
//
|
|
if ((hResultCode = pdnObject->HandleTable.Create(pRequest,&handle)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not create handle for this operation");
|
|
DisplayDNError(0,hResultCode);
|
|
goto Failure;
|
|
}
|
|
else
|
|
{
|
|
// Add a reference for the HandleTable
|
|
pRequest->AddRef();
|
|
pRequest->Lock();
|
|
pRequest->SetHandle(handle);
|
|
pRequest->Unlock();
|
|
}
|
|
DNASSERT(pRequest->GetHandle() != 0);
|
|
|
|
//
|
|
// Set up SEND op data
|
|
//
|
|
pSendOpData = pRequest->GetLocalSendOpData();
|
|
pSendOpData->dwMsgId = dwMsgId;
|
|
pSendOpData->BufferDesc[0].pBufferData = reinterpret_cast<BYTE*>(&pSendOpData->dwMsgId);
|
|
pSendOpData->BufferDesc[0].dwBufferSize = sizeof(DWORD);
|
|
pSendOpData->BufferDesc[1].pBufferData = pRefCountBuffer->GetBufferAddress();
|
|
pSendOpData->BufferDesc[1].dwBufferSize = pRefCountBuffer->GetBufferSize();
|
|
pSendOpData->dwNumBuffers = 2;
|
|
|
|
#pragma TODO(vanceo, "This does not preserve non-sequential & coalesce flags when this is a COMPLETEONPROCESS send request")
|
|
pRequest->SetOpFlags( DN_SENDFLAGS_RELIABLE | DN_SENDFLAGS_SET_USER_FLAG );
|
|
pRequest->SetResult( DPNERR_PLAYERLOST );
|
|
pRequest->SetCompletion( DNCompleteRequest );
|
|
pSendOpData = NULL;
|
|
|
|
pMsg->hCompletionOp = pRequest->GetHandle();
|
|
|
|
pRefCountBuffer->Release();
|
|
pRefCountBuffer = NULL;
|
|
|
|
//
|
|
// User messages sent with COMPLETE_ON_SEND should be allowed to be cancelled
|
|
//
|
|
if (dwMsgId == DN_MSG_INTERNAL_REQ_PROCESS_COMPLETION)
|
|
{
|
|
fInternal = FALSE;
|
|
}
|
|
else
|
|
{
|
|
fInternal = TRUE;
|
|
}
|
|
|
|
//
|
|
// We will always hand back the request, even if the send fails
|
|
//
|
|
if (ppRequest)
|
|
{
|
|
pRequest->AddRef();
|
|
*ppRequest = pRequest;
|
|
}
|
|
|
|
//
|
|
// Unlock DirectNetObject
|
|
//
|
|
DNDecRefLock(pdnObject);
|
|
fReleaseLock = FALSE;
|
|
|
|
//
|
|
// Add request to the request list
|
|
//
|
|
DNEnterCriticalSection(&pdnObject->csActiveList);
|
|
pRequest->m_bilinkActiveList.InsertBefore(&pdnObject->m_bilinkRequestList);
|
|
DNLeaveCriticalSection(&pdnObject->csActiveList);
|
|
|
|
//
|
|
// Avoid operations while host migration is taking place. This will get picked up
|
|
// after host migration, when completing outstanding operations
|
|
//
|
|
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
|
|
dwFlags = pdnObject->dwFlags;
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
if (!(dwFlags & DN_OBJECT_FLAG_HOST_MIGRATING) || !fInternal)
|
|
{
|
|
|
|
//
|
|
// SEND REQUEST
|
|
//
|
|
if (pConnection)
|
|
{
|
|
hResultCode = DNPerformChildSend( pdnObject,
|
|
pRequest,
|
|
pConnection,
|
|
0,
|
|
&pSend,
|
|
fInternal );
|
|
if (hResultCode != DPNERR_PENDING)
|
|
{
|
|
DPFERR("Could not perform child SEND");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(hResultCode != DPN_OK); // it was sent guaranteed, it should not return immediately
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Reset SEND AsyncOp to complete apropriately.
|
|
//
|
|
pSend->SetCompletion( DNCompleteSendRequest );
|
|
|
|
pSend->Release();
|
|
pSend = NULL;
|
|
}
|
|
}
|
|
|
|
pRequest->Release();
|
|
pRequest = NULL;
|
|
|
|
Exit:
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (fReleaseLock)
|
|
{
|
|
DNDecRefLock(pdnObject);
|
|
fReleaseLock = FALSE;
|
|
}
|
|
if (pRequest)
|
|
{
|
|
pRequest->Release();
|
|
pRequest = NULL;
|
|
}
|
|
if (pSend)
|
|
{
|
|
pSend->Release();
|
|
pSend = NULL;
|
|
}
|
|
if (pRefCountBuffer)
|
|
{
|
|
pRefCountBuffer->Release();
|
|
pRefCountBuffer = NULL;
|
|
}
|
|
if (pSendOpData)
|
|
{
|
|
DWORD dw;
|
|
|
|
for ( dw = 0 ; dw < pSendOpData->dwNumBuffers ; dw++ )
|
|
{
|
|
pSendOpData->BufferDesc[dw].dwBufferSize = 0;
|
|
pSendOpData->BufferDesc[dw].pBufferData = NULL;
|
|
}
|
|
pSendOpData->dwMsgId = 0;
|
|
pSendOpData->dwNumBuffers = 0;
|
|
pSendOpData = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
// DNReceiveCompleteOnProcess
|
|
//
|
|
// Receive a CompleteOnProcess message
|
|
// Pass the message up to the application and then return a special completion message
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNReceiveCompleteOnProcess"
|
|
|
|
HRESULT DNReceiveCompleteOnProcess(DIRECTNETOBJECT *const pdnObject,
|
|
CConnection *const pConnection,
|
|
void *const pBufferData,
|
|
const DWORD dwBufferSize,
|
|
const HANDLE hProtocol,
|
|
CRefCountBuffer *const pRefCountBuffer)
|
|
{
|
|
HRESULT hResultCode;
|
|
PVOID pvData;
|
|
DWORD dwDataSize;
|
|
UNALIGNED DN_INTERNAL_MESSAGE_REQ_PROCESS_COMPLETION *pReq;
|
|
|
|
DPFX(DPFPREP, 4,"Parameters: pConnection [0x%p], pBufferData [0x%p], dwBufferSize [%ld], hProtocol [0x%p], pRefCountBuffer [0x%p]",
|
|
pConnection,pBufferData,dwBufferSize,hProtocol,pRefCountBuffer);
|
|
|
|
DNASSERT(pdnObject != NULL);
|
|
DNASSERT(pConnection != NULL);
|
|
|
|
//
|
|
// Extract message
|
|
//
|
|
pReq = static_cast<DN_INTERNAL_MESSAGE_REQ_PROCESS_COMPLETION*>(pBufferData);
|
|
pvData = static_cast<void*>(pReq + 1);
|
|
dwDataSize = dwBufferSize - sizeof(DN_INTERNAL_MESSAGE_REQ_PROCESS_COMPLETION);
|
|
|
|
//
|
|
// Pass data to application
|
|
//
|
|
hResultCode = DNReceiveUserData(pdnObject,
|
|
pConnection,
|
|
static_cast<BYTE*>(pvData),
|
|
dwDataSize,
|
|
hProtocol,
|
|
pRefCountBuffer,
|
|
pReq->hCompletionOp,
|
|
0);
|
|
|
|
DPFX(DPFPREP, 4,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
}
|
|
|
|
|
|
// DNReceiveCompleteOnProcessReply
|
|
//
|
|
// Receive a CompleteOnProcess COMPLETION message
|
|
// Complete the outstanding CompleteOnProcess operation
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNReceiveCompleteOnProcessReply"
|
|
|
|
HRESULT DNReceiveCompleteOnProcessReply(DIRECTNETOBJECT *const pdnObject,
|
|
void *const pBufferData,
|
|
const DWORD dwBufferSize)
|
|
{
|
|
HRESULT hResultCode;
|
|
CAsyncOp *pAsyncOp;
|
|
UNALIGNED DN_INTERNAL_MESSAGE_PROCESS_COMPLETION *pMsg;
|
|
|
|
DPFX(DPFPREP, 4,"Parameters: pBufferData [0x%p], dwBufferSize [%ld]",pBufferData,dwBufferSize);
|
|
|
|
DNASSERT(pBufferData != NULL);
|
|
|
|
pAsyncOp = NULL;
|
|
|
|
pMsg = static_cast<DN_INTERNAL_MESSAGE_PROCESS_COMPLETION*>(pBufferData);
|
|
|
|
//
|
|
// Remove async op from HandleTable and from request list
|
|
//
|
|
DPFX(DPFPREP, 5,"Release completion operation [0x%lx]",pMsg->hCompletionOp);
|
|
if ((hResultCode = pdnObject->HandleTable.Destroy(pMsg->hCompletionOp,(PVOID*)&pAsyncOp)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not find handle in HandleTable");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(FALSE);
|
|
goto Failure;
|
|
}
|
|
|
|
DNASSERT(pAsyncOp->GetOpType() == ASYNC_OP_REQUEST);
|
|
DNEnterCriticalSection(&pdnObject->csActiveList);
|
|
pAsyncOp->m_bilinkActiveList.RemoveFromList();
|
|
DNLeaveCriticalSection(&pdnObject->csActiveList);
|
|
|
|
//
|
|
// Mark this operation as completing okay
|
|
//
|
|
pAsyncOp->SetResult( DPN_OK );
|
|
|
|
//
|
|
// This release should be the final release of the Request Child Async Op. and should
|
|
// DecRef the Request Parent Async Op.
|
|
//
|
|
pAsyncOp->Release();
|
|
pAsyncOp = NULL;
|
|
|
|
hResultCode = DPN_OK;
|
|
|
|
Exit:
|
|
DPFX(DPFPREP, 4,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (pAsyncOp)
|
|
{
|
|
pAsyncOp->Release();
|
|
pAsyncOp = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
// DNProcessTerminateSession
|
|
//
|
|
// Process a TERMINATE_SESSION message from the Host player
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNProcessTerminateSession"
|
|
|
|
HRESULT DNProcessTerminateSession(DIRECTNETOBJECT *const pdnObject,
|
|
void *const pvBuffer,
|
|
const DWORD dwBufferSize)
|
|
{
|
|
HRESULT hResultCode;
|
|
void *pvTerminateData;
|
|
UNALIGNED DN_INTERNAL_MESSAGE_TERMINATE_SESSION *pMsg;
|
|
|
|
DPFX(DPFPREP, 4,"Parameters: pvBuffer [0x%p], dwBufferSize [%ld]",pvBuffer,dwBufferSize);
|
|
|
|
DNASSERT(pdnObject != NULL);
|
|
DNASSERT(pvBuffer != NULL);
|
|
|
|
pMsg = static_cast<DN_INTERNAL_MESSAGE_TERMINATE_SESSION*>(pvBuffer);
|
|
if (pMsg->dwTerminateDataOffset)
|
|
{
|
|
pvTerminateData = static_cast<void*>(static_cast<BYTE*>(pvBuffer) + pMsg->dwTerminateDataOffset);
|
|
}
|
|
else
|
|
{
|
|
pvTerminateData = NULL;
|
|
}
|
|
|
|
//
|
|
// Inform user of termination
|
|
//
|
|
hResultCode = DNUserTerminateSession(pdnObject,DPNERR_HOSTTERMINATEDSESSION,pvTerminateData,pMsg->dwTerminateDataSize);
|
|
|
|
// Terminate session
|
|
hResultCode = DNTerminateSession(pdnObject,DPNERR_HOSTTERMINATEDSESSION);
|
|
|
|
DPFX(DPFPREP, 4,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
}
|