/*========================================================================== * * Copyright (C) 1996-1997 Microsoft Corporation. All Rights Reserved. * * File: dplunk.c * Content: IUnknown implementation for dplobby * * History: * Date By Reason * ======= ======= ====== * 4/13/96 myronth Created it * 10/23/96 myronth Added client/server methods * 11/08/96 myronth Added PRV_GetDPLobbySPInterface * 11/20/96 myronth Added LogoffServer call to Release code * 2/12/97 myronth Mass DX5 changes * 2/26/97 myronth #ifdef'd out DPASYNCDATA stuff (removed dependency) * 3/12/97 myronth New release code for DPlay3 (order different) * 3/13/97 myronth Added FreeLibrary code for LP's * 3/17/97 myronth Cleanup map table * 3/24/97 kipo Added support for IDirectPlayLobby2 interface * 4/3/97 myronth Changed CALLSP macro to CALL_LP * 5/8/97 myronth Drop the lobby lock when calling the LP, Purged * dead code * 7/30/97 myronth Added request node cleanup for standard lobby messaging * 8/19/97 myronth Added PRV_GetLobbyObjectFromInterface * 8/19/97 myronth Removed PRV_GetLobbyObjectFromInterface (not needed) * 12/2/97 myronth Added IDirectPlayLobby3 interface * 2/2/99 aarono Added lobbies to refcount on DPLAY dll to avoid * accidental unload. ***************************************************************************/ #include "dplobpr.h" //-------------------------------------------------------------------------- // // Definitions // //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- // // Functions // //-------------------------------------------------------------------------- #undef DPF_MODNAME #define DPF_MODNAME "PRV_GetDPLobbySPInterface" LPDPLOBBYSP PRV_GetDPLobbySPInterface(LPDPLOBBYI_DPLOBJECT this) { LPDPLOBBYI_INTERFACE lpInt; ASSERT(this); // Get an IDPLobbySP interface if(FAILED(PRV_GetInterface(this, &lpInt, &dplCallbacksSP))) { DPF_ERR("Unable to get non-reference counted DPLobbySP Interface pointer"); ASSERT(FALSE); return NULL; } // Decrement the ref cnt on the interface lpInt->dwIntRefCnt--; // Return the interface pointer return (LPDPLOBBYSP)lpInt; } // PRV_GetDPLobbySPInterface // Find an interface with the pCallbacks vtbl on this object. // If one doesn't exist, create it, increment the ref count, // and return the interface #undef DPF_MODNAME #define DPF_MODNAME "PRV_GetInterface" HRESULT PRV_GetInterface(LPDPLOBBYI_DPLOBJECT this, LPDPLOBBYI_INTERFACE * ppInt, LPVOID lpCallbacks) { LPDPLOBBYI_INTERFACE lpCurrentInts = this->lpInterfaces; BOOL bFound = FALSE; DPF(7, "Entering PRV_GetInterface"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x", this, ppInt, lpCallbacks); ASSERT(ppInt); // See if there is already an interface while (lpCurrentInts && !bFound) { if (lpCurrentInts->lpVtbl == lpCallbacks) { bFound = TRUE; } else lpCurrentInts = lpCurrentInts->lpNextInterface; } // If there is one, return it if(bFound) { *ppInt = lpCurrentInts; (*ppInt)->dwIntRefCnt++; // we don't increment this->dwRefCnt, since it's one / interface object return DP_OK; } // Otherwise create one *ppInt = DPMEM_ALLOC(sizeof(DPLOBBYI_INTERFACE)); if (!(*ppInt)) { DPF_ERR("Could not alloc interface - out of memory"); return E_OUTOFMEMORY; } (*ppInt)->dwIntRefCnt = 1; (*ppInt)->lpDPLobby = this; (*ppInt)->lpNextInterface = this->lpInterfaces; (*ppInt)->lpVtbl = lpCallbacks; this->lpInterfaces = *ppInt; this->dwRefCnt++; // One time only for each interface object return DP_OK; } // PRV_GetInterface #undef DPF_MODNAME #define DPF_MODNAME "DPL_QueryInterface" HRESULT DPLAPI DPL_QueryInterface(LPDIRECTPLAYLOBBY lpDPL, REFIID riid, LPVOID * ppvObj) { LPDPLOBBYI_DPLOBJECT this; HRESULT hr; DPF(7, "Entering DPL_QueryInterface"); DPF(9, "Parameters: 0x%08x, refiid, 0x%08x", lpDPL, ppvObj); ENTER_DPLOBBY(); TRY { if( !VALID_DPLOBBY_INTERFACE( lpDPL )) { LEAVE_DPLOBBY(); return DPERR_INVALIDINTERFACE; } this = DPLOBJECT_FROM_INTERFACE(lpDPL); if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); return DPERR_INVALIDOBJECT; } if ( !VALID_READ_UUID_PTR(riid) ) { LEAVE_DPLOBBY(); return DPERR_INVALIDPARAMS; } if ((!VALID_UUID_PTR(ppvObj)) ) { LEAVE_DPLOBBY(); DPF_ERR("Object pointer is invalid!"); return DPERR_INVALIDPARAMS; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { DPF_ERR( "Exception encountered validating parameters" ); LEAVE_DPLOBBY(); return DPERR_INVALIDPARAMS; } *ppvObj=NULL; if( IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDirectPlayLobby) ) { // Get an IDirectPlayLobby Interface (Unicode) hr = PRV_GetInterface(this, (LPDPLOBBYI_INTERFACE *) ppvObj, &dplCallbacks); } else if( IsEqualIID(riid, &IID_IDirectPlayLobbyA) ) { // Get an IDirectPlayLobbyA Interface (ANSI) hr = PRV_GetInterface(this, (LPDPLOBBYI_INTERFACE *) ppvObj, &dplCallbacksA); } else if( IsEqualIID(riid, &IID_IDirectPlayLobby2) ) { // Get an IDirectPlayLobby2 Interface (Unicode) hr = PRV_GetInterface(this, (LPDPLOBBYI_INTERFACE *) ppvObj, &dplCallbacks2); } else if( IsEqualIID(riid, &IID_IDirectPlayLobby2A) ) { // Get an IDirectPlayLobby2A Interface (ANSI) hr = PRV_GetInterface(this, (LPDPLOBBYI_INTERFACE *) ppvObj, &dplCallbacks2A); } else if( IsEqualIID(riid, &IID_IDirectPlayLobby3) ) { // Get an IDirectPlayLobby3 Interface (Unicode) hr = PRV_GetInterface(this, (LPDPLOBBYI_INTERFACE *) ppvObj, &dplCallbacks3); } else if( IsEqualIID(riid, &IID_IDirectPlayLobby3A) ) { // Get an IDirectPlayLobby3A Interface (ANSI) hr = PRV_GetInterface(this, (LPDPLOBBYI_INTERFACE *) ppvObj, &dplCallbacks3A); } else { hr = E_NOINTERFACE; } LEAVE_DPLOBBY(); return hr; } //DPL_QueryInterface #undef DPF_MODNAME #define DPF_MODNAME "DPL_AddRef" ULONG DPLAPI DPL_AddRef(LPDIRECTPLAYLOBBY lpDPL) { LPDPLOBBYI_INTERFACE lpInt = (LPDPLOBBYI_INTERFACE)lpDPL; LPDPLOBBYI_DPLOBJECT this; DPF(7, "Entering DPL_AddRef"); DPF(9, "Parameters: 0x%08x", lpDPL); ENTER_DPLOBBY(); TRY { if( !VALID_DPLOBBY_INTERFACE( lpDPL )) { LEAVE_DPLOBBY(); return 0; } this = DPLOBJECT_FROM_INTERFACE(lpDPL); if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); return 0; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { DPF_ERR( "Exception encountered validating parameters" ); LEAVE_DPLOBBY(); return 0; } // Make sure someone isn't calling AddRef on our IDPLobbySP interface if(lpInt->lpVtbl == &dplCallbacksSP) { DPF_ERR("You cannot call AddRef on an IDPLobbySP interface"); ASSERT(FALSE); LEAVE_DPLOBBY(); return 0; } // Increment the interface's reference count lpInt->dwIntRefCnt++; LEAVE_DPLOBBY(); return (lpInt->dwIntRefCnt); } //DPL_AddRef #undef DPF_MODNAME #define DPF_MODNAME "PRV_DestroyDPLobby" HRESULT PRV_DestroyDPLobby(LPDPLOBBYI_DPLOBJECT this) { HRESULT hr = DP_OK; DPF(7, "Entering PRV_DestroyDPLobby"); DPF(9, "Parameters: 0x%08x", this); // Since we can now be called from the DPlay3 object's Release code, // make sure we don't have any interface objects when we go to // free our lobby object. Assert here if any interfaces exist. ASSERT(!this->lpInterfaces); // Walk the list of GameNodes, freeing them as you go while(this->lpgnHead) PRV_RemoveGameNodeFromList(this->lpgnHead); // Walk the list of pending lobby server requests and free them while(this->lprnHead) PRV_RemoveRequestNode(this, this->lprnHead); // Free our callback table if one exists if(this->pcbSPCallbacks) DPMEM_FREE(this->pcbSPCallbacks); // Free our ID Map Table if it exists if(this->lpMap) DPMEM_FREE(this->lpMap); // Free the dplobby object DPMEM_FREE(this); gnObjects--; ASSERT(((int)gnObjects) >= 0); return DP_OK; } // PRV_DestroyDPlayLobby #undef DPF_MODNAME #define DPF_MODNAME "PRV_DestroyDPLobbyInterface" HRESULT PRV_DestroyDPLobbyInterface(LPDPLOBBYI_DPLOBJECT this, LPDPLOBBYI_INTERFACE lpInterface) { LPDPLOBBYI_INTERFACE lpIntPrev; // The interface preceeding pInt in the list BOOL bFound = FALSE; DPF(7, "Entering PRV_DestroyDPLobbyInterface"); DPF(9, "Parameters: 0x%08x, 0x%08x", this, lpInterface); // Remove pInt from the list of interfaces if (this->lpInterfaces == lpInterface) { // It's the 1st one, just remove it this->lpInterfaces = lpInterface->lpNextInterface; } else { lpIntPrev = this->lpInterfaces; while (lpIntPrev && !bFound) { if (lpIntPrev->lpNextInterface == lpInterface) { bFound = TRUE; } else lpIntPrev = lpIntPrev->lpNextInterface; } if (!bFound) { ASSERT(FALSE); return E_UNEXPECTED; } // take pint out of the list lpIntPrev->lpNextInterface = lpInterface->lpNextInterface; } DPMEM_FREE(lpInterface); return DP_OK; } // PRV_DestroyDPLobbyInterface #undef DPF_MODNAME #define DPF_MODNAME "DPL_Release" ULONG PRV_Release(LPDPLOBBYI_DPLOBJECT this, LPDPLOBBYI_INTERFACE lpInterface) { HRESULT hr = DP_OK; SPDATA_SHUTDOWN sdd; DWORD dwError; DPF(7, "==> PRV_Release"); DPF(9, "Parameters: 0x%08x, 0x%08x", this, lpInterface); ENTER_DPLOBBY(); // Decrement the interface ref count if (0 == --(lpInterface->dwIntRefCnt)) { LPDPLOBBYI_GAMENODE lpgn; // Notifying apps we launched that we are releasing // our lobby interface. lpgn=this->lpgnHead; while(lpgn){ if(lpgn->dwFlags & GN_LOBBY_CLIENT) { hr=PRV_SendStandardSystemMessage((LPDIRECTPLAYLOBBY)lpInterface, DPLSYS_LOBBYCLIENTRELEASE, lpgn->dwGameProcessID); if(DP_OK != hr){ DPF(0,"Couldn't send system message to game pid %x, hr=%x",lpgn->dwGameProcessID,hr); } else { DPF(9,"Told Process %x we are releasing the lobby interface",lpgn->dwGameProcessID); } } lpgn=lpgn->lpgnNext; } DPF(7,"Lobby interface Refcount hit 0, freeing\n"); // Since we're destroying an interface, dec the object count this->dwRefCnt--; // If our object ref cnt just went to zero, we need to call // shutdown in the LP if one is loaded if(this->dwFlags & DPLOBBYPR_SPINTERFACE) { // Clear our stack-based structure memset(&sdd, 0, sizeof(SPDATA_SHUTDOWN)); // Call the Shutdown method in the SP if(CALLBACK_EXISTS(Shutdown)) { sdd.lpISP = PRV_GetDPLobbySPInterface(this); // Drop the lock so the lobby provider's receive thread can get back // in with other messages if they show up in the queue before our // CreatePlayer response (which always happens) LEAVE_DPLOBBY(); hr = CALL_LP(this, Shutdown, &sdd); ENTER_DPLOBBY(); } else { // All LP's should support Shutdown ASSERT(FALSE); hr = DPERR_UNAVAILABLE; } if (FAILED(hr)) { DPF_ERR("Could not invoke Shutdown method in the Service Provider"); } } // REVIEW!!!! -- Are we going to have the same problem dplay has // with SP's hanging around and crashing after we go away? We // need to make sure the LP goes away first. if(this->hInstanceLP) { DPF(7,"About to free lobby provider library, hInstance %x\n",this->hInstanceLP); if (!FreeLibrary(this->hInstanceLP)) { dwError = GetLastError(); DPF_ERRVAL("Unable to free Lobby Provider DLL, dwError = %lu", dwError); ASSERT(FALSE); } // Just to be safe this->hInstanceLP = NULL; } // If the interface is the IDPLobbySP interface, we had to have been // called from the DPlay3 release code, so clear the SP flag since // we are going to remove the IDPLobbySP interface just below here. this->dwFlags &= ~DPLOBBYPR_SPINTERFACE; // Take the interface out of the table hr = PRV_DestroyDPLobbyInterface(this, lpInterface); if (FAILED(hr)) { DPF(0,"Could not destroy DPLobby interface! hr = 0x%08lx\n", hr); ASSERT(FALSE); } // Now destroy the interface if the ref cnt is 0 if(0 == this->dwRefCnt) { // Destroy the DPLobby object DPF(0,"Destroying DirectPlayLobby object - ref cnt = 0!"); hr = PRV_DestroyDPLobby(this); if (FAILED(hr)) { DPF(0,"Could not destroy DPLobby! hr = 0x%08lx\n",hr); ASSERT(FALSE); } } // 0 == this->dwRefCnt LEAVE_DPLOBBY(); return 0; } //0 == pInt->dwIntRefCnt DPF(7, "<==PRV_Release, rc=%d\n",lpInterface->dwIntRefCnt); LEAVE_DPLOBBY(); return (lpInterface->dwIntRefCnt); } // PRV_Release #undef DPF_MODNAME #define DPF_MODNAME "DPL_Release" ULONG DPLAPI DPL_Release(LPDIRECTPLAYLOBBY lpDPL) { LPDPLOBBYI_INTERFACE lpInterface; LPDPLOBBYI_DPLOBJECT this; HRESULT hr = DP_OK; DPF(7, "Entering DPL_Release"); DPF(9, "Parameters: 0x%08x", lpDPL); TRY { lpInterface = (LPDPLOBBYI_INTERFACE)lpDPL; if( !VALID_DPLOBBY_INTERFACE( lpInterface )) { return 0; } this = DPLOBJECT_FROM_INTERFACE(lpDPL); if( !VALID_DPLOBBY_PTR( this ) ) { return 0; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { DPF_ERR( "Exception encountered validating parameters" ); return 0; } // Make sure someone isn't calling Release on our IDPLobbySP interface if(lpInterface->lpVtbl == &dplCallbacksSP) { DPF_ERR("You cannot call Release on an IDPLobbySP interface"); ASSERT(FALSE); return 0; } // Call our internal release function return PRV_Release(this, lpInterface); } //DPL_Release #undef DPF_MODNAME #define DPF_MODNAME "PRV_FreeAllLobbyObjects" void PRV_FreeAllLobbyObjects(LPDPLOBBYI_DPLOBJECT this) { DPF(7, "Entering PRV_FreeAllLobbyObjects"); DPF(9, "Parameters: 0x%08x", this); ASSERT(this); // If we have an SP interface, just call release on it if(this->dwFlags & DPLOBBYPR_SPINTERFACE) { // Assert if an interface doesn't exist, because it should ASSERT(this->lpInterfaces); PRV_Release(this, this->lpInterfaces); return; } // Otherwise, we should only have an uninitialized object, // which we should just be able to destroy PRV_DestroyDPLobby(this); }