mirror of
git://source.winehq.org/git/wine.git
synced 2024-10-31 11:26:10 +00:00
1206 lines
32 KiB
C
1206 lines
32 KiB
C
/* dplayx.dll global data implementation.
|
|
*
|
|
* Copyright 1999,2000 - Peter Hunnisett
|
|
*
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*
|
|
*
|
|
* NOTES:
|
|
* o Implementation of all things which are associated with dplay on
|
|
* the computer - i.e. shared resources and such. Methods in this
|
|
* compilation unit should not call anything outside of this unit
|
|
* except base windows services and an interface to start the
|
|
* messaging thread.
|
|
* o Methods that begin with DPLAYX_ are used for dealing with
|
|
* dplayx.dll data which is accessible from all processes.
|
|
*
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
|
|
#define NONAMELESSUNION
|
|
#define NONAMELESSSTRUCT
|
|
#include "wine/debug.h"
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "winerror.h"
|
|
#include "wine/unicode.h"
|
|
|
|
#include "wingdi.h"
|
|
#include "winuser.h"
|
|
|
|
#include "dplayx_global.h"
|
|
#include "dplayx_messages.h" /* For CreateMessageReceptionThread only */
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(dplay);
|
|
|
|
/* FIXME: Need to do all that fun other dll referencing type of stuff */
|
|
|
|
/* Static data for all processes */
|
|
static const char lpszDplayxSemaName[] = "WINE_DPLAYX_SM";
|
|
static HANDLE hDplayxSema;
|
|
|
|
static const char lpszDplayxFileMapping[] = "WINE_DPLAYX_FM";
|
|
static HANDLE hDplayxSharedMem;
|
|
|
|
static LPVOID lpSharedStaticData = NULL;
|
|
|
|
|
|
#define DPLAYX_AcquireSemaphore() TRACE( "Waiting for DPLAYX semaphore\n" ); \
|
|
WaitForSingleObject( hDplayxSema, INFINITE );\
|
|
TRACE( "Through wait\n" )
|
|
|
|
#define DPLAYX_ReleaseSemaphore() ReleaseSemaphore( hDplayxSema, 1, NULL ); \
|
|
TRACE( "DPLAYX Semaphore released\n" ) /* FIXME: Is this correct? */
|
|
|
|
|
|
/* HACK for simple global data right now */
|
|
#define dwStaticSharedSize (128 * 1024) /* 128 KBytes */
|
|
#define dwDynamicSharedSize (512 * 1024) /* 512 KBytes */
|
|
#define dwTotalSharedSize ( dwStaticSharedSize + dwDynamicSharedSize )
|
|
|
|
|
|
/* FIXME: Is there no easier way? */
|
|
|
|
/* Pretend the entire dynamic area is carved up into 512 byte blocks.
|
|
* Each block has 4 bytes which are 0 unless used */
|
|
#define dwBlockSize 512
|
|
#define dwMaxBlock (dwDynamicSharedSize/dwBlockSize)
|
|
|
|
typedef struct
|
|
{
|
|
DWORD used;
|
|
DWORD data[dwBlockSize-sizeof(DWORD)];
|
|
} DPLAYX_MEM_SLICE;
|
|
|
|
static DPLAYX_MEM_SLICE* lpMemArea;
|
|
|
|
static void DPLAYX_PrivHeapFree( LPVOID addr )
|
|
{
|
|
LPVOID lpAddrStart;
|
|
DWORD dwBlockUsed;
|
|
|
|
/* Handle getting passed a NULL */
|
|
if( addr == NULL )
|
|
{
|
|
return;
|
|
}
|
|
|
|
lpAddrStart = (char*)addr - sizeof(DWORD); /* Find block header */
|
|
dwBlockUsed = ((BYTE*)lpAddrStart - (BYTE*)lpMemArea)/dwBlockSize;
|
|
|
|
lpMemArea[ dwBlockUsed ].used = 0;
|
|
}
|
|
|
|
static LPVOID DPLAYX_PrivHeapAlloc( DWORD flags, DWORD size )
|
|
{
|
|
LPVOID lpvArea = NULL;
|
|
UINT uBlockUsed;
|
|
|
|
if( size > (dwBlockSize - sizeof(DWORD)) )
|
|
{
|
|
FIXME( "Size exceeded. Request of 0x%08x\n", size );
|
|
size = dwBlockSize - sizeof(DWORD);
|
|
}
|
|
|
|
/* Find blank area */
|
|
uBlockUsed = 0;
|
|
while( ( lpMemArea[ uBlockUsed ].used != 0 ) && ( uBlockUsed <= dwMaxBlock ) ) { uBlockUsed++; }
|
|
|
|
if( uBlockUsed <= dwMaxBlock )
|
|
{
|
|
/* Set the area used */
|
|
lpMemArea[ uBlockUsed ].used = 1;
|
|
lpvArea = lpMemArea[ uBlockUsed ].data;
|
|
}
|
|
else
|
|
{
|
|
ERR( "No free block found\n" );
|
|
return NULL;
|
|
}
|
|
|
|
if( flags & HEAP_ZERO_MEMORY )
|
|
{
|
|
ZeroMemory( lpvArea, size );
|
|
}
|
|
|
|
return lpvArea;
|
|
}
|
|
|
|
|
|
enum { numSupportedLobbies = 32, numSupportedSessions = 32 };
|
|
typedef struct tagDPLAYX_LOBBYDATA
|
|
{
|
|
/* Points to lpConn + block of contiguous extra memory for dynamic parts
|
|
* of the struct directly following
|
|
*/
|
|
LPDPLCONNECTION lpConn;
|
|
|
|
/* Information for dplobby interfaces */
|
|
DWORD dwAppID;
|
|
DWORD dwAppLaunchedFromID;
|
|
|
|
/* Should this lobby app send messages to creator at important life
|
|
* stages
|
|
*/
|
|
HANDLE hInformOnAppStart;
|
|
HANDLE hInformOnAppDeath;
|
|
HANDLE hInformOnSettingRead;
|
|
|
|
/* Sundries */
|
|
BOOL bWaitForConnectionSettings;
|
|
DWORD dwLobbyMsgThreadId;
|
|
|
|
|
|
} DPLAYX_LOBBYDATA, *LPDPLAYX_LOBBYDATA;
|
|
|
|
static DPLAYX_LOBBYDATA* lobbyData = NULL;
|
|
/* static DPLAYX_LOBBYDATA lobbyData[ numSupportedLobbies ]; */
|
|
|
|
static DPSESSIONDESC2* sessionData = NULL;
|
|
/* static DPSESSIONDESC2* sessionData[ numSupportedSessions ]; */
|
|
|
|
|
|
static void DPLAYX_InitializeLobbyDataEntry( LPDPLAYX_LOBBYDATA lpData )
|
|
{
|
|
ZeroMemory( lpData, sizeof( *lpData ) );
|
|
}
|
|
|
|
/* NOTE: This must be called with the semaphore acquired.
|
|
* TRUE/FALSE with a pointer to it's data returned. Pointer data is
|
|
* is only valid if TRUE is returned.
|
|
*/
|
|
static BOOL DPLAYX_IsAppIdLobbied( DWORD dwAppID, LPDPLAYX_LOBBYDATA* lplpDplData )
|
|
{
|
|
UINT i;
|
|
|
|
*lplpDplData = NULL;
|
|
|
|
if( dwAppID == 0 )
|
|
{
|
|
dwAppID = GetCurrentProcessId();
|
|
TRACE( "Translated dwAppID == 0 into 0x%08x\n", dwAppID );
|
|
}
|
|
|
|
for( i=0; i < numSupportedLobbies; i++ )
|
|
{
|
|
if( lobbyData[ i ].dwAppID == dwAppID )
|
|
{
|
|
/* This process is lobbied */
|
|
TRACE( "Found 0x%08x @ %u\n", dwAppID, i );
|
|
*lplpDplData = &lobbyData[ i ];
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* Reserve a spot for the new application. TRUE means success and FALSE failure. */
|
|
BOOL DPLAYX_CreateLobbyApplication( DWORD dwAppID )
|
|
{
|
|
UINT i;
|
|
|
|
/* 0 is the marker for unused application data slots */
|
|
if( dwAppID == 0 )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
DPLAYX_AcquireSemaphore();
|
|
|
|
/* Find an empty space in the list and insert the data */
|
|
for( i=0; i < numSupportedLobbies; i++ )
|
|
{
|
|
if( lobbyData[ i ].dwAppID == 0 )
|
|
{
|
|
/* This process is now lobbied */
|
|
TRACE( "Setting lobbyData[%u] for (0x%08x,0x%08x)\n",
|
|
i, dwAppID, GetCurrentProcessId() );
|
|
|
|
lobbyData[ i ].dwAppID = dwAppID;
|
|
lobbyData[ i ].dwAppLaunchedFromID = GetCurrentProcessId();
|
|
|
|
/* FIXME: Where is the best place for this? In interface or here? */
|
|
lobbyData[ i ].hInformOnAppStart = 0;
|
|
lobbyData[ i ].hInformOnAppDeath = 0;
|
|
lobbyData[ i ].hInformOnSettingRead = 0;
|
|
|
|
DPLAYX_ReleaseSemaphore();
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
ERR( "No empty lobbies\n" );
|
|
|
|
DPLAYX_ReleaseSemaphore();
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL DPLAYX_SetLobbyHandles( DWORD dwAppID,
|
|
HANDLE hStart, HANDLE hDeath, HANDLE hConnRead )
|
|
{
|
|
LPDPLAYX_LOBBYDATA lpLData;
|
|
|
|
/* Need to explicitly give lobby application. Can't set for yourself */
|
|
if( dwAppID == 0 )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
DPLAYX_AcquireSemaphore();
|
|
|
|
if( !DPLAYX_IsAppIdLobbied( dwAppID, &lpLData ) )
|
|
{
|
|
DPLAYX_ReleaseSemaphore();
|
|
return FALSE;
|
|
}
|
|
|
|
lpLData->hInformOnAppStart = hStart;
|
|
lpLData->hInformOnAppDeath = hDeath;
|
|
lpLData->hInformOnSettingRead = hConnRead;
|
|
|
|
DPLAYX_ReleaseSemaphore();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL DPLAYX_GetThisLobbyHandles( LPHANDLE lphStart,
|
|
LPHANDLE lphDeath,
|
|
LPHANDLE lphConnRead,
|
|
BOOL bClearSetHandles )
|
|
{
|
|
LPDPLAYX_LOBBYDATA lpLData;
|
|
|
|
DPLAYX_AcquireSemaphore();
|
|
|
|
if( !DPLAYX_IsAppIdLobbied( 0, &lpLData ) )
|
|
{
|
|
DPLAYX_ReleaseSemaphore();
|
|
return FALSE;
|
|
}
|
|
|
|
if( lphStart != NULL )
|
|
{
|
|
if( lpLData->hInformOnAppStart == 0 )
|
|
{
|
|
DPLAYX_ReleaseSemaphore();
|
|
return FALSE;
|
|
}
|
|
|
|
*lphStart = lpLData->hInformOnAppStart;
|
|
|
|
if( bClearSetHandles )
|
|
{
|
|
CloseHandle( lpLData->hInformOnAppStart );
|
|
lpLData->hInformOnAppStart = 0;
|
|
}
|
|
}
|
|
|
|
if( lphDeath != NULL )
|
|
{
|
|
if( lpLData->hInformOnAppDeath == 0 )
|
|
{
|
|
DPLAYX_ReleaseSemaphore();
|
|
return FALSE;
|
|
}
|
|
|
|
*lphDeath = lpLData->hInformOnAppDeath;
|
|
|
|
if( bClearSetHandles )
|
|
{
|
|
CloseHandle( lpLData->hInformOnAppDeath );
|
|
lpLData->hInformOnAppDeath = 0;
|
|
}
|
|
}
|
|
|
|
if( lphConnRead != NULL )
|
|
{
|
|
if( lpLData->hInformOnSettingRead == 0 )
|
|
{
|
|
DPLAYX_ReleaseSemaphore();
|
|
return FALSE;
|
|
}
|
|
|
|
*lphConnRead = lpLData->hInformOnSettingRead;
|
|
|
|
if( bClearSetHandles )
|
|
{
|
|
CloseHandle( lpLData->hInformOnSettingRead );
|
|
lpLData->hInformOnSettingRead = 0;
|
|
}
|
|
}
|
|
|
|
DPLAYX_ReleaseSemaphore();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************
|
|
* Called to initialize the global data. This will only be used on the
|
|
* loading of the dll
|
|
***************************************************************************/
|
|
BOOL DPLAYX_ConstructData(void)
|
|
{
|
|
SECURITY_ATTRIBUTES s_attrib;
|
|
BOOL bInitializeSharedMemory = FALSE;
|
|
LPVOID lpDesiredMemoryMapStart = (LPVOID)0x50000000;
|
|
HANDLE hInformOnStart;
|
|
|
|
TRACE( "DPLAYX dll loaded - construct called\n" );
|
|
|
|
/* Create a semaphore to block access to DPLAYX global data structs */
|
|
|
|
s_attrib.bInheritHandle = TRUE;
|
|
s_attrib.lpSecurityDescriptor = NULL;
|
|
s_attrib.nLength = sizeof(s_attrib);
|
|
|
|
hDplayxSema = CreateSemaphoreA( &s_attrib, 0, 1, lpszDplayxSemaName );
|
|
|
|
/* First instance creates the semaphore. Others just use it */
|
|
if( GetLastError() == ERROR_SUCCESS )
|
|
{
|
|
TRACE( "Semaphore %p created\n", hDplayxSema );
|
|
|
|
/* The semaphore creator will also build the shared memory */
|
|
bInitializeSharedMemory = TRUE;
|
|
}
|
|
else if ( GetLastError() == ERROR_ALREADY_EXISTS )
|
|
{
|
|
TRACE( "Found semaphore handle %p\n", hDplayxSema );
|
|
DPLAYX_AcquireSemaphore();
|
|
}
|
|
else
|
|
{
|
|
ERR( ": semaphore error %d\n", GetLastError() );
|
|
return FALSE;
|
|
}
|
|
|
|
SetLastError( ERROR_SUCCESS );
|
|
|
|
hDplayxSharedMem = CreateFileMappingA( INVALID_HANDLE_VALUE,
|
|
&s_attrib,
|
|
PAGE_READWRITE | SEC_COMMIT,
|
|
0,
|
|
dwTotalSharedSize,
|
|
lpszDplayxFileMapping );
|
|
|
|
if( GetLastError() == ERROR_SUCCESS )
|
|
{
|
|
TRACE( "File mapped %p created\n", hDplayxSharedMem );
|
|
}
|
|
else if ( GetLastError() == ERROR_ALREADY_EXISTS )
|
|
{
|
|
TRACE( "Found FileMapping handle %p\n", hDplayxSharedMem );
|
|
}
|
|
else
|
|
{
|
|
ERR( ": unable to create shared memory (%d)\n", GetLastError() );
|
|
DPLAYX_ReleaseSemaphore();
|
|
return FALSE;
|
|
}
|
|
|
|
lpSharedStaticData = MapViewOfFileEx( hDplayxSharedMem,
|
|
FILE_MAP_WRITE,
|
|
0, 0, 0, lpDesiredMemoryMapStart );
|
|
|
|
if( lpSharedStaticData == NULL )
|
|
{
|
|
ERR( ": unable to map static data into process memory space (%d)\n",
|
|
GetLastError() );
|
|
DPLAYX_ReleaseSemaphore();
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
if( lpDesiredMemoryMapStart == lpSharedStaticData )
|
|
{
|
|
TRACE( "File mapped to %p\n", lpSharedStaticData );
|
|
}
|
|
else
|
|
{
|
|
/* Presently the shared data structures use pointers. If the
|
|
* files are not mapped into the same area, the pointers will no
|
|
* longer make any sense :(
|
|
* FIXME: In the future make the shared data structures have some
|
|
* sort of fixup to make them independent between data spaces.
|
|
* This will also require a rework of the session data stuff.
|
|
*/
|
|
ERR( "File mapped to %p (not %p). Expect failure\n",
|
|
lpSharedStaticData, lpDesiredMemoryMapStart );
|
|
}
|
|
}
|
|
|
|
/* Dynamic area starts just after the static area */
|
|
lpMemArea = (LPVOID)((BYTE*)lpSharedStaticData + dwStaticSharedSize);
|
|
|
|
/* FIXME: Crude hack */
|
|
lobbyData = lpSharedStaticData;
|
|
sessionData = (DPSESSIONDESC2*)((BYTE*)lpSharedStaticData + (dwStaticSharedSize/2));
|
|
|
|
/* Initialize shared data segments. */
|
|
if( bInitializeSharedMemory )
|
|
{
|
|
UINT i;
|
|
|
|
TRACE( "Initializing shared memory\n" );
|
|
|
|
/* Set all lobbies to be "empty" */
|
|
for( i=0; i < numSupportedLobbies; i++ )
|
|
{
|
|
DPLAYX_InitializeLobbyDataEntry( &lobbyData[ i ] );
|
|
}
|
|
|
|
/* Set all sessions to be "empty" */
|
|
for( i=0; i < numSupportedSessions; i++ )
|
|
{
|
|
sessionData[i].dwSize = 0;
|
|
}
|
|
|
|
/* Zero out the dynamic area */
|
|
ZeroMemory( lpMemArea, dwDynamicSharedSize );
|
|
|
|
/* Just for fun sync the whole data area */
|
|
FlushViewOfFile( lpSharedStaticData, dwTotalSharedSize );
|
|
}
|
|
|
|
DPLAYX_ReleaseSemaphore();
|
|
|
|
/* Everything was created correctly. Signal the lobby client that
|
|
* we started up correctly
|
|
*/
|
|
if( DPLAYX_GetThisLobbyHandles( &hInformOnStart, NULL, NULL, FALSE ) &&
|
|
hInformOnStart
|
|
)
|
|
{
|
|
BOOL bSuccess;
|
|
bSuccess = SetEvent( hInformOnStart );
|
|
TRACE( "Signalling lobby app start event %p %s\n",
|
|
hInformOnStart, bSuccess ? "succeed" : "failed" );
|
|
|
|
/* Close out handle */
|
|
DPLAYX_GetThisLobbyHandles( &hInformOnStart, NULL, NULL, TRUE );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************
|
|
* Called to destroy all global data. This will only be used on the
|
|
* unloading of the dll
|
|
***************************************************************************/
|
|
BOOL DPLAYX_DestructData(void)
|
|
{
|
|
HANDLE hInformOnDeath;
|
|
|
|
TRACE( "DPLAYX dll unloaded - destruct called\n" );
|
|
|
|
/* If required, inform that this app is dying */
|
|
if( DPLAYX_GetThisLobbyHandles( NULL, &hInformOnDeath, NULL, FALSE ) &&
|
|
hInformOnDeath
|
|
)
|
|
{
|
|
BOOL bSuccess;
|
|
bSuccess = SetEvent( hInformOnDeath );
|
|
TRACE( "Signalling lobby app death event %p %s\n",
|
|
hInformOnDeath, bSuccess ? "succeed" : "failed" );
|
|
|
|
/* Close out handle */
|
|
DPLAYX_GetThisLobbyHandles( NULL, &hInformOnDeath, NULL, TRUE );
|
|
}
|
|
|
|
/* DO CLEAN UP (LAST) */
|
|
|
|
/* Delete the semaphore */
|
|
CloseHandle( hDplayxSema );
|
|
|
|
/* Delete shared memory file mapping */
|
|
UnmapViewOfFile( lpSharedStaticData );
|
|
CloseHandle( hDplayxSharedMem );
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/* Assumption: Enough contiguous space was allocated at dest */
|
|
static void DPLAYX_CopyConnStructA( LPDPLCONNECTION dest, const DPLCONNECTION *src )
|
|
{
|
|
BYTE* lpStartOfFreeSpace;
|
|
|
|
*dest = *src;
|
|
|
|
lpStartOfFreeSpace = ((BYTE*)dest) + sizeof( DPLCONNECTION );
|
|
|
|
/* Copy the LPDPSESSIONDESC2 structure if it exists */
|
|
if( src->lpSessionDesc )
|
|
{
|
|
dest->lpSessionDesc = (LPDPSESSIONDESC2)lpStartOfFreeSpace;
|
|
lpStartOfFreeSpace += sizeof( DPSESSIONDESC2 );
|
|
*dest->lpSessionDesc = *src->lpSessionDesc;
|
|
|
|
/* Session names may or may not exist */
|
|
if( src->lpSessionDesc->u1.lpszSessionNameA )
|
|
{
|
|
strcpy( (LPSTR)lpStartOfFreeSpace, src->lpSessionDesc->u1.lpszSessionNameA );
|
|
dest->lpSessionDesc->u1.lpszSessionNameA = (LPSTR)lpStartOfFreeSpace;
|
|
lpStartOfFreeSpace +=
|
|
strlen( dest->lpSessionDesc->u1.lpszSessionNameA ) + 1;
|
|
}
|
|
|
|
if( src->lpSessionDesc->u2.lpszPasswordA )
|
|
{
|
|
strcpy( (LPSTR)lpStartOfFreeSpace, src->lpSessionDesc->u2.lpszPasswordA );
|
|
dest->lpSessionDesc->u2.lpszPasswordA = (LPSTR)lpStartOfFreeSpace;
|
|
lpStartOfFreeSpace +=
|
|
strlen( dest->lpSessionDesc->u2.lpszPasswordA ) + 1;
|
|
}
|
|
}
|
|
|
|
/* DPNAME structure is optional */
|
|
if( src->lpPlayerName )
|
|
{
|
|
dest->lpPlayerName = (LPDPNAME)lpStartOfFreeSpace;
|
|
lpStartOfFreeSpace += sizeof( DPNAME );
|
|
*dest->lpPlayerName = *src->lpPlayerName;
|
|
|
|
if( src->lpPlayerName->u1.lpszShortNameA )
|
|
{
|
|
strcpy( (LPSTR)lpStartOfFreeSpace, src->lpPlayerName->u1.lpszShortNameA );
|
|
dest->lpPlayerName->u1.lpszShortNameA = (LPSTR)lpStartOfFreeSpace;
|
|
lpStartOfFreeSpace +=
|
|
strlen( dest->lpPlayerName->u1.lpszShortNameA ) + 1;
|
|
}
|
|
|
|
if( src->lpPlayerName->u2.lpszLongNameA )
|
|
{
|
|
strcpy( (LPSTR)lpStartOfFreeSpace, src->lpPlayerName->u2.lpszLongNameA );
|
|
dest->lpPlayerName->u2.lpszLongNameA = (LPSTR)lpStartOfFreeSpace;
|
|
lpStartOfFreeSpace +=
|
|
strlen( (LPSTR)dest->lpPlayerName->u2.lpszLongName ) + 1 ;
|
|
}
|
|
|
|
}
|
|
|
|
/* Copy address if it exists */
|
|
if( src->lpAddress )
|
|
{
|
|
dest->lpAddress = lpStartOfFreeSpace;
|
|
CopyMemory( lpStartOfFreeSpace, src->lpAddress, src->dwAddressSize );
|
|
/* No need to advance lpStartOfFreeSpace as there is no more "dynamic" data */
|
|
}
|
|
}
|
|
|
|
/* Assumption: Enough contiguous space was allocated at dest */
|
|
static void DPLAYX_CopyConnStructW( LPDPLCONNECTION dest, const DPLCONNECTION *src )
|
|
{
|
|
BYTE* lpStartOfFreeSpace;
|
|
|
|
*dest = *src;
|
|
|
|
lpStartOfFreeSpace = ( (BYTE*)dest) + sizeof( DPLCONNECTION );
|
|
|
|
/* Copy the LPDPSESSIONDESC2 structure if it exists */
|
|
if( src->lpSessionDesc )
|
|
{
|
|
dest->lpSessionDesc = (LPDPSESSIONDESC2)lpStartOfFreeSpace;
|
|
lpStartOfFreeSpace += sizeof( DPSESSIONDESC2 );
|
|
*dest->lpSessionDesc = *src->lpSessionDesc;
|
|
|
|
/* Session names may or may not exist */
|
|
if( src->lpSessionDesc->u1.lpszSessionName )
|
|
{
|
|
strcpyW( (LPWSTR)lpStartOfFreeSpace, src->lpSessionDesc->u1.lpszSessionName );
|
|
dest->lpSessionDesc->u1.lpszSessionName = (LPWSTR)lpStartOfFreeSpace;
|
|
lpStartOfFreeSpace += sizeof(WCHAR) *
|
|
( strlenW( dest->lpSessionDesc->u1.lpszSessionName ) + 1 );
|
|
}
|
|
|
|
if( src->lpSessionDesc->u2.lpszPassword )
|
|
{
|
|
strcpyW( (LPWSTR)lpStartOfFreeSpace, src->lpSessionDesc->u2.lpszPassword );
|
|
dest->lpSessionDesc->u2.lpszPassword = (LPWSTR)lpStartOfFreeSpace;
|
|
lpStartOfFreeSpace += sizeof(WCHAR) *
|
|
( strlenW( dest->lpSessionDesc->u2.lpszPassword ) + 1 );
|
|
}
|
|
}
|
|
|
|
/* DPNAME structure is optional */
|
|
if( src->lpPlayerName )
|
|
{
|
|
dest->lpPlayerName = (LPDPNAME)lpStartOfFreeSpace;
|
|
lpStartOfFreeSpace += sizeof( DPNAME );
|
|
*dest->lpPlayerName = *src->lpPlayerName;
|
|
|
|
if( src->lpPlayerName->u1.lpszShortName )
|
|
{
|
|
strcpyW( (LPWSTR)lpStartOfFreeSpace, src->lpPlayerName->u1.lpszShortName );
|
|
dest->lpPlayerName->u1.lpszShortName = (LPWSTR)lpStartOfFreeSpace;
|
|
lpStartOfFreeSpace += sizeof(WCHAR) *
|
|
( strlenW( dest->lpPlayerName->u1.lpszShortName ) + 1 );
|
|
}
|
|
|
|
if( src->lpPlayerName->u2.lpszLongName )
|
|
{
|
|
strcpyW( (LPWSTR)lpStartOfFreeSpace, src->lpPlayerName->u2.lpszLongName );
|
|
dest->lpPlayerName->u2.lpszLongName = (LPWSTR)lpStartOfFreeSpace;
|
|
lpStartOfFreeSpace += sizeof(WCHAR) *
|
|
( strlenW( dest->lpPlayerName->u2.lpszLongName ) + 1 );
|
|
}
|
|
|
|
}
|
|
|
|
/* Copy address if it exists */
|
|
if( src->lpAddress )
|
|
{
|
|
dest->lpAddress = lpStartOfFreeSpace;
|
|
CopyMemory( lpStartOfFreeSpace, src->lpAddress, src->dwAddressSize );
|
|
/* No need to advance lpStartOfFreeSpace as there is no more "dynamic" data */
|
|
}
|
|
|
|
}
|
|
|
|
static DWORD DPLAYX_SizeOfLobbyDataA( const DPLCONNECTION *lpConn )
|
|
{
|
|
DWORD dwTotalSize = sizeof( DPLCONNECTION );
|
|
|
|
/* Just a safety check */
|
|
if( lpConn == NULL )
|
|
{
|
|
ERR( "lpConn is NULL\n" );
|
|
return 0;
|
|
}
|
|
|
|
if( lpConn->lpSessionDesc != NULL )
|
|
{
|
|
dwTotalSize += sizeof( DPSESSIONDESC2 );
|
|
|
|
if( lpConn->lpSessionDesc->u1.lpszSessionNameA )
|
|
{
|
|
dwTotalSize += strlen( lpConn->lpSessionDesc->u1.lpszSessionNameA ) + 1;
|
|
}
|
|
|
|
if( lpConn->lpSessionDesc->u2.lpszPasswordA )
|
|
{
|
|
dwTotalSize += strlen( lpConn->lpSessionDesc->u2.lpszPasswordA ) + 1;
|
|
}
|
|
}
|
|
|
|
if( lpConn->lpPlayerName != NULL )
|
|
{
|
|
dwTotalSize += sizeof( DPNAME );
|
|
|
|
if( lpConn->lpPlayerName->u1.lpszShortNameA )
|
|
{
|
|
dwTotalSize += strlen( lpConn->lpPlayerName->u1.lpszShortNameA ) + 1;
|
|
}
|
|
|
|
if( lpConn->lpPlayerName->u2.lpszLongNameA )
|
|
{
|
|
dwTotalSize += strlen( lpConn->lpPlayerName->u2.lpszLongNameA ) + 1;
|
|
}
|
|
|
|
}
|
|
|
|
dwTotalSize += lpConn->dwAddressSize;
|
|
|
|
return dwTotalSize;
|
|
}
|
|
|
|
static DWORD DPLAYX_SizeOfLobbyDataW( const DPLCONNECTION *lpConn )
|
|
{
|
|
DWORD dwTotalSize = sizeof( DPLCONNECTION );
|
|
|
|
/* Just a safety check */
|
|
if( lpConn == NULL )
|
|
{
|
|
ERR( "lpConn is NULL\n" );
|
|
return 0;
|
|
}
|
|
|
|
if( lpConn->lpSessionDesc != NULL )
|
|
{
|
|
dwTotalSize += sizeof( DPSESSIONDESC2 );
|
|
|
|
if( lpConn->lpSessionDesc->u1.lpszSessionName )
|
|
{
|
|
dwTotalSize += sizeof( WCHAR ) *
|
|
( strlenW( lpConn->lpSessionDesc->u1.lpszSessionName ) + 1 );
|
|
}
|
|
|
|
if( lpConn->lpSessionDesc->u2.lpszPassword )
|
|
{
|
|
dwTotalSize += sizeof( WCHAR ) *
|
|
( strlenW( lpConn->lpSessionDesc->u2.lpszPassword ) + 1 );
|
|
}
|
|
}
|
|
|
|
if( lpConn->lpPlayerName != NULL )
|
|
{
|
|
dwTotalSize += sizeof( DPNAME );
|
|
|
|
if( lpConn->lpPlayerName->u1.lpszShortName )
|
|
{
|
|
dwTotalSize += sizeof( WCHAR ) *
|
|
( strlenW( lpConn->lpPlayerName->u1.lpszShortName ) + 1 );
|
|
}
|
|
|
|
if( lpConn->lpPlayerName->u2.lpszLongName )
|
|
{
|
|
dwTotalSize += sizeof( WCHAR ) *
|
|
( strlenW( lpConn->lpPlayerName->u2.lpszLongName ) + 1 );
|
|
}
|
|
|
|
}
|
|
|
|
dwTotalSize += lpConn->dwAddressSize;
|
|
|
|
return dwTotalSize;
|
|
}
|
|
|
|
HRESULT DPLAYX_GetConnectionSettingsA
|
|
( DWORD dwAppID,
|
|
LPVOID lpData,
|
|
LPDWORD lpdwDataSize )
|
|
{
|
|
LPDPLAYX_LOBBYDATA lpDplData;
|
|
DWORD dwRequiredDataSize = 0;
|
|
HANDLE hInformOnSettingRead;
|
|
|
|
DPLAYX_AcquireSemaphore();
|
|
|
|
if ( ! DPLAYX_IsAppIdLobbied( dwAppID, &lpDplData ) )
|
|
{
|
|
DPLAYX_ReleaseSemaphore();
|
|
|
|
TRACE( "Application 0x%08x is not lobbied\n", dwAppID );
|
|
return DPERR_NOTLOBBIED;
|
|
}
|
|
|
|
dwRequiredDataSize = DPLAYX_SizeOfLobbyDataA( lpDplData->lpConn );
|
|
|
|
/* Do they want to know the required buffer size or is the provided buffer
|
|
* big enough?
|
|
*/
|
|
if ( ( lpData == NULL ) ||
|
|
( *lpdwDataSize < dwRequiredDataSize )
|
|
)
|
|
{
|
|
DPLAYX_ReleaseSemaphore();
|
|
|
|
*lpdwDataSize = DPLAYX_SizeOfLobbyDataA( lpDplData->lpConn );
|
|
|
|
return DPERR_BUFFERTOOSMALL;
|
|
}
|
|
|
|
DPLAYX_CopyConnStructA( lpData, lpDplData->lpConn );
|
|
|
|
DPLAYX_ReleaseSemaphore();
|
|
|
|
/* They have gotten the information - signal the event if required */
|
|
if( DPLAYX_GetThisLobbyHandles( NULL, NULL, &hInformOnSettingRead, FALSE ) &&
|
|
hInformOnSettingRead
|
|
)
|
|
{
|
|
BOOL bSuccess;
|
|
bSuccess = SetEvent( hInformOnSettingRead );
|
|
TRACE( "Signalling setting read event %p %s\n",
|
|
hInformOnSettingRead, bSuccess ? "succeed" : "failed" );
|
|
|
|
/* Close out handle */
|
|
DPLAYX_GetThisLobbyHandles( NULL, NULL, &hInformOnSettingRead, TRUE );
|
|
}
|
|
|
|
return DP_OK;
|
|
}
|
|
|
|
HRESULT DPLAYX_GetConnectionSettingsW
|
|
( DWORD dwAppID,
|
|
LPVOID lpData,
|
|
LPDWORD lpdwDataSize )
|
|
{
|
|
LPDPLAYX_LOBBYDATA lpDplData;
|
|
DWORD dwRequiredDataSize = 0;
|
|
HANDLE hInformOnSettingRead;
|
|
|
|
DPLAYX_AcquireSemaphore();
|
|
|
|
if ( ! DPLAYX_IsAppIdLobbied( dwAppID, &lpDplData ) )
|
|
{
|
|
DPLAYX_ReleaseSemaphore();
|
|
return DPERR_NOTLOBBIED;
|
|
}
|
|
|
|
dwRequiredDataSize = DPLAYX_SizeOfLobbyDataW( lpDplData->lpConn );
|
|
|
|
/* Do they want to know the required buffer size or is the provided buffer
|
|
* big enough?
|
|
*/
|
|
if ( ( lpData == NULL ) ||
|
|
( *lpdwDataSize < dwRequiredDataSize )
|
|
)
|
|
{
|
|
DPLAYX_ReleaseSemaphore();
|
|
|
|
*lpdwDataSize = DPLAYX_SizeOfLobbyDataW( lpDplData->lpConn );
|
|
|
|
return DPERR_BUFFERTOOSMALL;
|
|
}
|
|
|
|
DPLAYX_CopyConnStructW( lpData, lpDplData->lpConn );
|
|
|
|
DPLAYX_ReleaseSemaphore();
|
|
|
|
/* They have gotten the information - signal the event if required */
|
|
if( DPLAYX_GetThisLobbyHandles( NULL, NULL, &hInformOnSettingRead, FALSE ) &&
|
|
hInformOnSettingRead
|
|
)
|
|
{
|
|
BOOL bSuccess;
|
|
bSuccess = SetEvent( hInformOnSettingRead );
|
|
TRACE( "Signalling setting read event %p %s\n",
|
|
hInformOnSettingRead, bSuccess ? "succeed" : "failed" );
|
|
|
|
/* Close out handle */
|
|
DPLAYX_GetThisLobbyHandles( NULL, NULL, &hInformOnSettingRead, TRUE );
|
|
}
|
|
|
|
return DP_OK;
|
|
}
|
|
|
|
/* Store the structure into the shared data structure. Ensure that allocs for
|
|
* variable length strings come from the shared data structure.
|
|
* FIXME: We need to free information as well.
|
|
*/
|
|
HRESULT DPLAYX_SetConnectionSettingsA
|
|
( DWORD dwFlags,
|
|
DWORD dwAppID,
|
|
const DPLCONNECTION *lpConn )
|
|
{
|
|
LPDPLAYX_LOBBYDATA lpDplData;
|
|
|
|
/* Parameter check */
|
|
if( dwFlags || !lpConn )
|
|
{
|
|
ERR("invalid parameters.\n");
|
|
return DPERR_INVALIDPARAMS;
|
|
}
|
|
|
|
/* Store information */
|
|
if( lpConn->dwSize != sizeof(DPLCONNECTION) )
|
|
{
|
|
ERR(": old/new DPLCONNECTION type? Size=%08x\n", lpConn->dwSize );
|
|
|
|
return DPERR_INVALIDPARAMS;
|
|
}
|
|
|
|
DPLAYX_AcquireSemaphore();
|
|
|
|
if ( ! DPLAYX_IsAppIdLobbied( dwAppID, &lpDplData ) )
|
|
{
|
|
DPLAYX_ReleaseSemaphore();
|
|
|
|
return DPERR_NOTLOBBIED;
|
|
}
|
|
|
|
if( (!lpConn->lpSessionDesc ) ||
|
|
( lpConn->lpSessionDesc->dwSize != sizeof( DPSESSIONDESC2 ) )
|
|
)
|
|
{
|
|
DPLAYX_ReleaseSemaphore();
|
|
|
|
ERR("DPSESSIONDESC passed in? Size=%u\n",
|
|
lpConn->lpSessionDesc?lpConn->lpSessionDesc->dwSize:0 );
|
|
|
|
return DPERR_INVALIDPARAMS;
|
|
}
|
|
|
|
/* Free the existing memory */
|
|
DPLAYX_PrivHeapFree( lpDplData->lpConn );
|
|
|
|
lpDplData->lpConn = DPLAYX_PrivHeapAlloc( HEAP_ZERO_MEMORY,
|
|
DPLAYX_SizeOfLobbyDataA( lpConn ) );
|
|
|
|
DPLAYX_CopyConnStructA( lpDplData->lpConn, lpConn );
|
|
|
|
|
|
DPLAYX_ReleaseSemaphore();
|
|
|
|
/* FIXME: Send a message - I think */
|
|
|
|
return DP_OK;
|
|
}
|
|
|
|
/* Store the structure into the shared data structure. Ensure that allocs for
|
|
* variable length strings come from the shared data structure.
|
|
* FIXME: We need to free information as well
|
|
*/
|
|
HRESULT DPLAYX_SetConnectionSettingsW
|
|
( DWORD dwFlags,
|
|
DWORD dwAppID,
|
|
const DPLCONNECTION *lpConn )
|
|
{
|
|
LPDPLAYX_LOBBYDATA lpDplData;
|
|
|
|
/* Parameter check */
|
|
if( dwFlags || !lpConn )
|
|
{
|
|
ERR("invalid parameters.\n");
|
|
return DPERR_INVALIDPARAMS;
|
|
}
|
|
|
|
/* Store information */
|
|
if( lpConn->dwSize != sizeof(DPLCONNECTION) )
|
|
{
|
|
ERR(": old/new DPLCONNECTION type? Size=%u\n", lpConn->dwSize );
|
|
|
|
return DPERR_INVALIDPARAMS;
|
|
}
|
|
|
|
DPLAYX_AcquireSemaphore();
|
|
|
|
if ( ! DPLAYX_IsAppIdLobbied( dwAppID, &lpDplData ) )
|
|
{
|
|
DPLAYX_ReleaseSemaphore();
|
|
|
|
return DPERR_NOTLOBBIED;
|
|
}
|
|
|
|
/* Free the existing memory */
|
|
DPLAYX_PrivHeapFree( lpDplData->lpConn );
|
|
|
|
lpDplData->lpConn = DPLAYX_PrivHeapAlloc( HEAP_ZERO_MEMORY,
|
|
DPLAYX_SizeOfLobbyDataW( lpConn ) );
|
|
|
|
DPLAYX_CopyConnStructW( lpDplData->lpConn, lpConn );
|
|
|
|
|
|
DPLAYX_ReleaseSemaphore();
|
|
|
|
/* FIXME: Send a message - I think */
|
|
|
|
return DP_OK;
|
|
}
|
|
|
|
BOOL DPLAYX_WaitForConnectionSettings( BOOL bWait )
|
|
{
|
|
LPDPLAYX_LOBBYDATA lpLobbyData;
|
|
|
|
DPLAYX_AcquireSemaphore();
|
|
|
|
if( !DPLAYX_IsAppIdLobbied( 0, &lpLobbyData ) )
|
|
{
|
|
DPLAYX_ReleaseSemaphore();
|
|
return FALSE;
|
|
}
|
|
|
|
lpLobbyData->bWaitForConnectionSettings = bWait;
|
|
|
|
DPLAYX_ReleaseSemaphore();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL DPLAYX_AnyLobbiesWaitingForConnSettings(void)
|
|
{
|
|
UINT i;
|
|
BOOL bFound = FALSE;
|
|
|
|
DPLAYX_AcquireSemaphore();
|
|
|
|
for( i=0; i < numSupportedLobbies; i++ )
|
|
{
|
|
if( ( lobbyData[ i ].dwAppID != 0 ) && /* lobby initialized */
|
|
( lobbyData[ i ].bWaitForConnectionSettings ) /* Waiting */
|
|
)
|
|
{
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
DPLAYX_ReleaseSemaphore();
|
|
|
|
return bFound;
|
|
}
|
|
|
|
BOOL DPLAYX_SetLobbyMsgThreadId( DWORD dwAppId, DWORD dwThreadId )
|
|
{
|
|
LPDPLAYX_LOBBYDATA lpLobbyData;
|
|
|
|
DPLAYX_AcquireSemaphore();
|
|
|
|
if( !DPLAYX_IsAppIdLobbied( dwAppId, &lpLobbyData ) )
|
|
{
|
|
DPLAYX_ReleaseSemaphore();
|
|
return FALSE;
|
|
}
|
|
|
|
lpLobbyData->dwLobbyMsgThreadId = dwThreadId;
|
|
|
|
DPLAYX_ReleaseSemaphore();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* NOTE: This is potentially not thread safe. You are not guaranteed to end up
|
|
with the correct string printed in the case where the HRESULT is not
|
|
known. You will just get the last hr passed in. This can change
|
|
over time if this method is used a lot :) */
|
|
LPCSTR DPLAYX_HresultToString(HRESULT hr)
|
|
{
|
|
static char szTempStr[12];
|
|
|
|
switch (hr)
|
|
{
|
|
case DP_OK:
|
|
return "DP_OK";
|
|
case DPERR_ALREADYINITIALIZED:
|
|
return "DPERR_ALREADYINITIALIZED";
|
|
case DPERR_ACCESSDENIED:
|
|
return "DPERR_ACCESSDENIED";
|
|
case DPERR_ACTIVEPLAYERS:
|
|
return "DPERR_ACTIVEPLAYERS";
|
|
case DPERR_BUFFERTOOSMALL:
|
|
return "DPERR_BUFFERTOOSMALL";
|
|
case DPERR_CANTADDPLAYER:
|
|
return "DPERR_CANTADDPLAYER";
|
|
case DPERR_CANTCREATEGROUP:
|
|
return "DPERR_CANTCREATEGROUP";
|
|
case DPERR_CANTCREATEPLAYER:
|
|
return "DPERR_CANTCREATEPLAYER";
|
|
case DPERR_CANTCREATESESSION:
|
|
return "DPERR_CANTCREATESESSION";
|
|
case DPERR_CAPSNOTAVAILABLEYET:
|
|
return "DPERR_CAPSNOTAVAILABLEYET";
|
|
case DPERR_EXCEPTION:
|
|
return "DPERR_EXCEPTION";
|
|
case DPERR_GENERIC:
|
|
return "DPERR_GENERIC";
|
|
case DPERR_INVALIDFLAGS:
|
|
return "DPERR_INVALIDFLAGS";
|
|
case DPERR_INVALIDOBJECT:
|
|
return "DPERR_INVALIDOBJECT";
|
|
case DPERR_INVALIDPARAMS:
|
|
return "DPERR_INVALIDPARAMS";
|
|
case DPERR_INVALIDPLAYER:
|
|
return "DPERR_INVALIDPLAYER";
|
|
case DPERR_INVALIDGROUP:
|
|
return "DPERR_INVALIDGROUP";
|
|
case DPERR_NOCAPS:
|
|
return "DPERR_NOCAPS";
|
|
case DPERR_NOCONNECTION:
|
|
return "DPERR_NOCONNECTION";
|
|
case DPERR_OUTOFMEMORY:
|
|
return "DPERR_OUTOFMEMORY";
|
|
case DPERR_NOMESSAGES:
|
|
return "DPERR_NOMESSAGES";
|
|
case DPERR_NONAMESERVERFOUND:
|
|
return "DPERR_NONAMESERVERFOUND";
|
|
case DPERR_NOPLAYERS:
|
|
return "DPERR_NOPLAYERS";
|
|
case DPERR_NOSESSIONS:
|
|
return "DPERR_NOSESSIONS";
|
|
case DPERR_PENDING:
|
|
return "DPERR_PENDING";
|
|
case DPERR_SENDTOOBIG:
|
|
return "DPERR_SENDTOOBIG";
|
|
case DPERR_TIMEOUT:
|
|
return "DPERR_TIMEOUT";
|
|
case DPERR_UNAVAILABLE:
|
|
return "DPERR_UNAVAILABLE";
|
|
case DPERR_UNSUPPORTED:
|
|
return "DPERR_UNSUPPORTED";
|
|
case DPERR_BUSY:
|
|
return "DPERR_BUSY";
|
|
case DPERR_USERCANCEL:
|
|
return "DPERR_USERCANCEL";
|
|
case DPERR_NOINTERFACE:
|
|
return "DPERR_NOINTERFACE";
|
|
case DPERR_CANNOTCREATESERVER:
|
|
return "DPERR_CANNOTCREATESERVER";
|
|
case DPERR_PLAYERLOST:
|
|
return "DPERR_PLAYERLOST";
|
|
case DPERR_SESSIONLOST:
|
|
return "DPERR_SESSIONLOST";
|
|
case DPERR_UNINITIALIZED:
|
|
return "DPERR_UNINITIALIZED";
|
|
case DPERR_NONEWPLAYERS:
|
|
return "DPERR_NONEWPLAYERS";
|
|
case DPERR_INVALIDPASSWORD:
|
|
return "DPERR_INVALIDPASSWORD";
|
|
case DPERR_CONNECTING:
|
|
return "DPERR_CONNECTING";
|
|
case DPERR_CONNECTIONLOST:
|
|
return "DPERR_CONNECTIONLOST";
|
|
case DPERR_UNKNOWNMESSAGE:
|
|
return "DPERR_UNKNOWNMESSAGE";
|
|
case DPERR_CANCELFAILED:
|
|
return "DPERR_CANCELFAILED";
|
|
case DPERR_INVALIDPRIORITY:
|
|
return "DPERR_INVALIDPRIORITY";
|
|
case DPERR_NOTHANDLED:
|
|
return "DPERR_NOTHANDLED";
|
|
case DPERR_CANCELLED:
|
|
return "DPERR_CANCELLED";
|
|
case DPERR_ABORTED:
|
|
return "DPERR_ABORTED";
|
|
case DPERR_BUFFERTOOLARGE:
|
|
return "DPERR_BUFFERTOOLARGE";
|
|
case DPERR_CANTCREATEPROCESS:
|
|
return "DPERR_CANTCREATEPROCESS";
|
|
case DPERR_APPNOTSTARTED:
|
|
return "DPERR_APPNOTSTARTED";
|
|
case DPERR_INVALIDINTERFACE:
|
|
return "DPERR_INVALIDINTERFACE";
|
|
case DPERR_NOSERVICEPROVIDER:
|
|
return "DPERR_NOSERVICEPROVIDER";
|
|
case DPERR_UNKNOWNAPPLICATION:
|
|
return "DPERR_UNKNOWNAPPLICATION";
|
|
case DPERR_NOTLOBBIED:
|
|
return "DPERR_NOTLOBBIED";
|
|
case DPERR_SERVICEPROVIDERLOADED:
|
|
return "DPERR_SERVICEPROVIDERLOADED";
|
|
case DPERR_ALREADYREGISTERED:
|
|
return "DPERR_ALREADYREGISTERED";
|
|
case DPERR_NOTREGISTERED:
|
|
return "DPERR_NOTREGISTERED";
|
|
case DPERR_AUTHENTICATIONFAILED:
|
|
return "DPERR_AUTHENTICATIONFAILED";
|
|
case DPERR_CANTLOADSSPI:
|
|
return "DPERR_CANTLOADSSPI";
|
|
case DPERR_ENCRYPTIONFAILED:
|
|
return "DPERR_ENCRYPTIONFAILED";
|
|
case DPERR_SIGNFAILED:
|
|
return "DPERR_SIGNFAILED";
|
|
case DPERR_CANTLOADSECURITYPACKAGE:
|
|
return "DPERR_CANTLOADSECURITYPACKAGE";
|
|
case DPERR_ENCRYPTIONNOTSUPPORTED:
|
|
return "DPERR_ENCRYPTIONNOTSUPPORTED";
|
|
case DPERR_CANTLOADCAPI:
|
|
return "DPERR_CANTLOADCAPI";
|
|
case DPERR_NOTLOGGEDIN:
|
|
return "DPERR_NOTLOGGEDIN";
|
|
case DPERR_LOGONDENIED:
|
|
return "DPERR_LOGONDENIED";
|
|
default:
|
|
/* For errors not in the list, return HRESULT as a string
|
|
This part is not thread safe */
|
|
WARN( "Unknown error 0x%08x\n", hr );
|
|
wsprintfA( szTempStr, "0x%08lx", hr );
|
|
return szTempStr;
|
|
}
|
|
}
|