Sweden-Number/dlls/dplayx/dplayx_global.c

1291 lines
35 KiB
C
Raw Normal View History

/* dplayx.dll global data implementation.
*
2000-05-27 00:26:30 +02:00
* 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
2008-04-02 22:10:47 +02:00
* 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.
*
2002-06-01 01:06:46 +02:00
*/
#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 */
2006-12-14 15:47:05 +01:00
static const char lpszDplayxSemaName[] = "WINE_DPLAYX_SM";
static HANDLE hDplayxSema;
2006-12-14 15:47:05 +01:00
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? */
2002-06-01 01:06:46 +02:00
/* 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)
2002-06-01 01:06:46 +02:00
typedef struct
{
DWORD used;
DWORD data[dwBlockSize-sizeof(DWORD)];
} DPLAYX_MEM_SLICE;
static DPLAYX_MEM_SLICE* lpMemArea;
2008-12-01 13:26:05 +01:00
static void DPLAYX_PrivHeapFree( LPVOID addr )
{
LPVOID lpAddrStart;
DWORD dwBlockUsed;
/* Handle getting passed a NULL */
if( addr == NULL )
{
return;
2002-06-01 01:06:46 +02:00
}
lpAddrStart = (char*)addr - sizeof(DWORD); /* Find block header */
dwBlockUsed = ((BYTE*)lpAddrStart - (BYTE*)lpMemArea)/dwBlockSize;
2002-06-01 01:06:46 +02:00
lpMemArea[ dwBlockUsed ].used = 0;
}
2008-12-01 13:26:05 +01:00
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);
2002-06-01 01:06:46 +02:00
}
/* Find blank area */
2002-06-01 01:06:46 +02:00
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;
}
2002-06-01 01:06:46 +02:00
if( flags & HEAP_ZERO_MEMORY )
{
ZeroMemory( lpvArea, size );
}
return lpvArea;
}
2000-05-27 00:26:30 +02:00
enum { numSupportedLobbies = 32, numSupportedSessions = 32 };
typedef struct tagDPLAYX_LOBBYDATA
{
/* Points to lpConn + block of contiguous extra memory for dynamic parts
2002-06-01 01:06:46 +02:00
* of the struct directly following
*/
LPDPLCONNECTION lpConn;
2000-05-27 00:26:30 +02:00
/* Information for dplobby interfaces */
DWORD dwAppID;
DWORD dwAppLaunchedFromID;
2000-05-27 00:26:30 +02:00
/* Should this lobby app send messages to creator at important life
* stages
*/
HANDLE hInformOnAppStart;
HANDLE hInformOnAppDeath;
HANDLE hInformOnSettingRead;
/* Sundries */
BOOL bWaitForConnectionSettings;
DWORD dwLobbyMsgThreadId;
2002-06-01 01:06:46 +02:00
} DPLAYX_LOBBYDATA, *LPDPLAYX_LOBBYDATA;
static DPLAYX_LOBBYDATA* lobbyData = NULL;
/* static DPLAYX_LOBBYDATA lobbyData[ numSupportedLobbies ]; */
2002-06-01 01:06:46 +02:00
static DPSESSIONDESC2* sessionData = NULL;
/* static DPSESSIONDESC2* sessionData[ numSupportedSessions ]; */
2000-05-27 00:26:30 +02:00
/* Function prototypes */
2007-04-26 23:45:27 +02:00
static DWORD DPLAYX_SizeOfLobbyDataA( const DPLCONNECTION *lpDplData );
static DWORD DPLAYX_SizeOfLobbyDataW( const DPLCONNECTION *lpDplData );
static void DPLAYX_CopyConnStructA( LPDPLCONNECTION dest, const DPLCONNECTION *src );
static void DPLAYX_CopyConnStructW( LPDPLCONNECTION dest, const DPLCONNECTION *src );
2007-01-06 20:31:56 +01:00
static BOOL DPLAYX_IsAppIdLobbied( DWORD dwAppId, LPDPLAYX_LOBBYDATA* dplData );
static void DPLAYX_InitializeLobbyDataEntry( LPDPLAYX_LOBBYDATA lpData );
static BOOL DPLAYX_CopyIntoSessionDesc2A( LPDPSESSIONDESC2 lpSessionDest,
LPCDPSESSIONDESC2 lpSessionSrc );
static BOOL DPLAYX_GetThisLobbyHandles( LPHANDLE lphStart, LPHANDLE lphDeath,
LPHANDLE lphConnRead, BOOL bClearSetHandles );
2002-06-01 01:06:46 +02:00
/***************************************************************************
* 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,
2002-06-01 01:06:46 +02:00
0,
dwTotalSharedSize,
2002-06-01 01:06:46 +02:00
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;
}
2002-06-01 01:06:46 +02:00
lpSharedStaticData = MapViewOfFileEx( hDplayxSharedMem,
FILE_MAP_WRITE,
0, 0, 0, lpDesiredMemoryMapStart );
if( lpSharedStaticData == NULL )
2000-05-27 00:26:30 +02:00
{
ERR( ": unable to map static data into process memory space (%d)\n",
GetLastError() );
DPLAYX_ReleaseSemaphore();
return FALSE;
2000-05-27 00:26:30 +02:00
}
else
{
if( lpDesiredMemoryMapStart == lpSharedStaticData )
{
TRACE( "File mapped to %p\n", lpSharedStaticData );
}
else
{
/* Presently the shared data structures use pointers. If the
* files are no 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;
2000-05-27 00:26:30 +02:00
TRACE( "Initializing shared memory\n" );
2002-06-01 01:06:46 +02:00
/* 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;
}
2008-04-02 22:10:47 +02:00
/* Zero out the dynamic area */
ZeroMemory( lpMemArea, dwDynamicSharedSize );
/* Just for fun sync the whole data area */
FlushViewOfFile( lpSharedStaticData, dwTotalSharedSize );
}
2002-06-01 01:06:46 +02:00
DPLAYX_ReleaseSemaphore();
/* Everything was created correctly. Signal the lobby client that
* we started up correctly
*/
if( DPLAYX_GetThisLobbyHandles( &hInformOnStart, NULL, NULL, FALSE ) &&
2002-06-01 01:06:46 +02:00
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;
}
2002-06-01 01:06:46 +02:00
/***************************************************************************
* 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 ) &&
2002-06-01 01:06:46 +02:00
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 );
2002-06-01 01:06:46 +02:00
/* Delete shared memory file mapping */
UnmapViewOfFile( lpSharedStaticData );
CloseHandle( hDplayxSharedMem );
return FALSE;
}
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.
*/
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 )
{
2002-06-01 01:06:46 +02:00
/* This process is lobbied */
TRACE( "Found 0x%08x @ %u\n", dwAppID, i );
*lplpDplData = &lobbyData[ i ];
return TRUE;
}
}
return FALSE;
}
2008-04-02 22:10:47 +02:00
/* 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;
2002-06-01 01:06:46 +02:00
}
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? */
2002-06-01 01:06:46 +02:00
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;
}
/* I'm not sure when I'm going to need this, but here it is */
2002-06-01 01:06:46 +02:00
BOOL DPLAYX_DestroyLobbyApplication( DWORD dwAppID )
{
UINT i;
DPLAYX_AcquireSemaphore();
/* Find an empty space in the list and insert the data */
for( i=0; i < numSupportedLobbies; i++ )
{
if( lobbyData[ i ].dwAppID == dwAppID )
{
/* FIXME: Should free up anything unused. Tisk tisk :0 */
/* Mark this entry unused */
TRACE( "Marking lobbyData[%u] unused\n", i );
DPLAYX_InitializeLobbyDataEntry( &lobbyData[ i ] );
DPLAYX_ReleaseSemaphore();
return TRUE;
}
}
DPLAYX_ReleaseSemaphore();
ERR( "Unable to find global entry for application\n" );
return FALSE;
}
BOOL DPLAYX_SetLobbyHandles( DWORD dwAppID,
HANDLE hStart, HANDLE hDeath, HANDLE hConnRead )
{
LPDPLAYX_LOBBYDATA lpLData;
2008-04-02 22:10:47 +02:00
/* 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;
}
2007-01-06 20:31:56 +01:00
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();
2002-06-01 01:06:46 +02:00
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;
}
}
2002-06-01 01:06:46 +02:00
DPLAYX_ReleaseSemaphore();
return TRUE;
}
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
2002-06-01 01:06:46 +02:00
* 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 ) &&
2002-06-01 01:06:46 +02:00
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;
}
/* Assumption: Enough contiguous space was allocated at dest */
2007-04-26 23:45:27 +02:00
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;
2002-06-01 01:06:46 +02:00
lpStartOfFreeSpace +=
2007-12-31 22:38:06 +01:00
strlen( dest->lpSessionDesc->u1.lpszSessionNameA ) + 1;
}
if( src->lpSessionDesc->u2.lpszPasswordA )
{
strcpy( (LPSTR)lpStartOfFreeSpace, src->lpSessionDesc->u2.lpszPasswordA );
dest->lpSessionDesc->u2.lpszPasswordA = (LPSTR)lpStartOfFreeSpace;
2002-06-01 01:06:46 +02:00
lpStartOfFreeSpace +=
2007-12-31 22:38:06 +01:00
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;
2002-06-01 01:06:46 +02:00
lpStartOfFreeSpace +=
2007-12-31 22:38:06 +01:00
strlen( dest->lpPlayerName->u1.lpszShortNameA ) + 1;
}
if( src->lpPlayerName->u2.lpszLongNameA )
{
strcpy( (LPSTR)lpStartOfFreeSpace, src->lpPlayerName->u2.lpszLongNameA );
dest->lpPlayerName->u2.lpszLongNameA = (LPSTR)lpStartOfFreeSpace;
2002-06-01 01:06:46 +02:00
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 */
}
}
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
2002-06-01 01:06:46 +02:00
* 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 ) &&
2002-06-01 01:06:46 +02:00
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;
}
/* Assumption: Enough contiguous space was allocated at dest */
2007-04-26 23:45:27 +02:00
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 )
{
2007-04-25 22:41:14 +02:00
strcpyW( (LPWSTR)lpStartOfFreeSpace, src->lpSessionDesc->u1.lpszSessionName );
dest->lpSessionDesc->u1.lpszSessionName = (LPWSTR)lpStartOfFreeSpace;
lpStartOfFreeSpace += sizeof(WCHAR) *
2007-12-31 22:38:06 +01:00
( 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) *
2007-12-31 22:38:06 +01:00
( strlenW( dest->lpSessionDesc->u2.lpszPassword ) + 1 );
}
}
/* DPNAME structure is optional */
if( src->lpPlayerName )
{
dest->lpPlayerName = (LPDPNAME)lpStartOfFreeSpace;
lpStartOfFreeSpace += sizeof( DPNAME );
*dest->lpPlayerName = *src->lpPlayerName;
2002-06-01 01:06:46 +02:00
if( src->lpPlayerName->u1.lpszShortName )
{
strcpyW( (LPWSTR)lpStartOfFreeSpace, src->lpPlayerName->u1.lpszShortName );
dest->lpPlayerName->u1.lpszShortName = (LPWSTR)lpStartOfFreeSpace;
lpStartOfFreeSpace += sizeof(WCHAR) *
2007-12-31 22:38:06 +01:00
( 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) *
2007-12-31 22:38:06 +01:00
( strlenW( dest->lpPlayerName->u2.lpszLongName ) + 1 );
}
}
/* Copy address if it exists */
if( src->lpAddress )
{
dest->lpAddress = lpStartOfFreeSpace;
2002-06-01 01:06:46 +02:00
CopyMemory( lpStartOfFreeSpace, src->lpAddress, src->dwAddressSize );
/* No need to advance lpStartOfFreeSpace as there is no more "dynamic" data */
}
}
2008-04-02 22:10:47 +02:00
/* Store the structure into the shared data structure. Ensure that allocs for
* variable length strings come from the shared data structure.
2008-04-02 22:10:47 +02:00
* FIXME: We need to free information as well.
*/
HRESULT DPLAYX_SetConnectionSettingsA
( DWORD dwFlags,
DWORD dwAppID,
2007-04-26 23:45:27 +02:00
const DPLCONNECTION *lpConn )
{
LPDPLAYX_LOBBYDATA lpDplData;
2003-06-18 05:30:39 +02:00
/* 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 );
2002-06-01 01:06:46 +02:00
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;
}
2008-04-04 21:23:01 +02:00
/* Store the structure into the shared data structure. Ensure that allocs for
* variable length strings come from the shared data structure.
2002-06-01 01:06:46 +02:00
* FIXME: We need to free information as well
*/
HRESULT DPLAYX_SetConnectionSettingsW
( DWORD dwFlags,
DWORD dwAppID,
2007-04-26 23:45:27 +02:00
const DPLCONNECTION *lpConn )
{
LPDPLAYX_LOBBYDATA lpDplData;
2003-06-18 05:30:39 +02:00
/* 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;
}
2007-04-26 23:45:27 +02:00
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;
}
2002-06-01 01:06:46 +02:00
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;
}
2007-04-26 23:45:27 +02:00
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;
}
2000-05-27 00:26:30 +02:00
/* Copy an ANSI session desc structure to the given buffer */
BOOL DPLAYX_CopyIntoSessionDesc2A( LPDPSESSIONDESC2 lpSessionDest,
LPCDPSESSIONDESC2 lpSessionSrc )
{
CopyMemory( lpSessionDest, lpSessionSrc, sizeof( *lpSessionSrc ) );
2000-05-27 00:26:30 +02:00
if( lpSessionSrc->u1.lpszSessionNameA )
2000-05-27 00:26:30 +02:00
{
if ((lpSessionDest->u1.lpszSessionNameA = HeapAlloc( GetProcessHeap(), 0,
strlen(lpSessionSrc->u1.lpszSessionNameA)+1 )))
strcpy( lpSessionDest->u1.lpszSessionNameA, lpSessionSrc->u1.lpszSessionNameA );
2000-05-27 00:26:30 +02:00
}
if( lpSessionSrc->u2.lpszPasswordA )
2000-05-27 00:26:30 +02:00
{
if ((lpSessionDest->u2.lpszPasswordA = HeapAlloc( GetProcessHeap(), 0,
strlen(lpSessionSrc->u2.lpszPasswordA)+1 )))
strcpy( lpSessionDest->u2.lpszPasswordA, lpSessionSrc->u2.lpszPasswordA );
2000-05-27 00:26:30 +02:00
}
return TRUE;
}
void DPLAYX_SetLocalSession( LPCDPSESSIONDESC2 lpsd )
{
UINT i;
/* FIXME: Is this an error if it exists already? */
2002-06-01 01:06:46 +02:00
2000-05-27 00:26:30 +02:00
/* Crude/wrong implementation for now. Just always add to first empty spot */
for( i=0; i < numSupportedSessions; i++ )
{
/* Is this one empty? */
if( sessionData[i].dwSize == 0 )
{
2002-06-01 01:06:46 +02:00
DPLAYX_CopyIntoSessionDesc2A( &sessionData[i], lpsd );
2000-05-27 00:26:30 +02:00
break;
}
}
}
BOOL DPLAYX_WaitForConnectionSettings( BOOL bWait )
{
LPDPLAYX_LOBBYDATA lpLobbyData;
DPLAYX_AcquireSemaphore();
2002-06-01 01:06:46 +02:00
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 */
)
2002-06-01 01:06:46 +02:00
{
bFound = TRUE;
2002-06-01 01:06:46 +02:00
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;
}
2002-06-01 01:06:46 +02:00
/* 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
2005-01-26 22:09:04 +01:00
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)
{
2002-06-01 01:06:46 +02:00
case DP_OK:
return "DP_OK";
2002-06-01 01:06:46 +02:00
case DPERR_ALREADYINITIALIZED:
return "DPERR_ALREADYINITIALIZED";
2002-06-01 01:06:46 +02:00
case DPERR_ACCESSDENIED:
return "DPERR_ACCESSDENIED";
2002-06-01 01:06:46 +02:00
case DPERR_ACTIVEPLAYERS:
return "DPERR_ACTIVEPLAYERS";
2002-06-01 01:06:46 +02:00
case DPERR_BUFFERTOOSMALL:
return "DPERR_BUFFERTOOSMALL";
2002-06-01 01:06:46 +02:00
case DPERR_CANTADDPLAYER:
return "DPERR_CANTADDPLAYER";
2002-06-01 01:06:46 +02:00
case DPERR_CANTCREATEGROUP:
return "DPERR_CANTCREATEGROUP";
2002-06-01 01:06:46 +02:00
case DPERR_CANTCREATEPLAYER:
return "DPERR_CANTCREATEPLAYER";
2002-06-01 01:06:46 +02:00
case DPERR_CANTCREATESESSION:
return "DPERR_CANTCREATESESSION";
2002-06-01 01:06:46 +02:00
case DPERR_CAPSNOTAVAILABLEYET:
return "DPERR_CAPSNOTAVAILABLEYET";
2002-06-01 01:06:46 +02:00
case DPERR_EXCEPTION:
return "DPERR_EXCEPTION";
2002-06-01 01:06:46 +02:00
case DPERR_GENERIC:
return "DPERR_GENERIC";
2002-06-01 01:06:46 +02:00
case DPERR_INVALIDFLAGS:
return "DPERR_INVALIDFLAGS";
2002-06-01 01:06:46 +02:00
case DPERR_INVALIDOBJECT:
return "DPERR_INVALIDOBJECT";
2002-06-01 01:06:46 +02:00
case DPERR_INVALIDPARAMS:
return "DPERR_INVALIDPARAMS";
2002-06-01 01:06:46 +02:00
case DPERR_INVALIDPLAYER:
return "DPERR_INVALIDPLAYER";
2002-06-01 01:06:46 +02:00
case DPERR_INVALIDGROUP:
return "DPERR_INVALIDGROUP";
2002-06-01 01:06:46 +02:00
case DPERR_NOCAPS:
return "DPERR_NOCAPS";
2002-06-01 01:06:46 +02:00
case DPERR_NOCONNECTION:
return "DPERR_NOCONNECTION";
2002-06-01 01:06:46 +02:00
case DPERR_OUTOFMEMORY:
return "DPERR_OUTOFMEMORY";
2002-06-01 01:06:46 +02:00
case DPERR_NOMESSAGES:
return "DPERR_NOMESSAGES";
2002-06-01 01:06:46 +02:00
case DPERR_NONAMESERVERFOUND:
return "DPERR_NONAMESERVERFOUND";
2002-06-01 01:06:46 +02:00
case DPERR_NOPLAYERS:
return "DPERR_NOPLAYERS";
2002-06-01 01:06:46 +02:00
case DPERR_NOSESSIONS:
return "DPERR_NOSESSIONS";
2002-06-01 01:06:46 +02:00
case DPERR_PENDING:
return "DPERR_PENDING";
2002-06-01 01:06:46 +02:00
case DPERR_SENDTOOBIG:
return "DPERR_SENDTOOBIG";
2002-06-01 01:06:46 +02:00
case DPERR_TIMEOUT:
return "DPERR_TIMEOUT";
2002-06-01 01:06:46 +02:00
case DPERR_UNAVAILABLE:
return "DPERR_UNAVAILABLE";
2002-06-01 01:06:46 +02:00
case DPERR_UNSUPPORTED:
return "DPERR_UNSUPPORTED";
2002-06-01 01:06:46 +02:00
case DPERR_BUSY:
return "DPERR_BUSY";
2002-06-01 01:06:46 +02:00
case DPERR_USERCANCEL:
return "DPERR_USERCANCEL";
2002-06-01 01:06:46 +02:00
case DPERR_NOINTERFACE:
return "DPERR_NOINTERFACE";
2002-06-01 01:06:46 +02:00
case DPERR_CANNOTCREATESERVER:
return "DPERR_CANNOTCREATESERVER";
2002-06-01 01:06:46 +02:00
case DPERR_PLAYERLOST:
return "DPERR_PLAYERLOST";
2002-06-01 01:06:46 +02:00
case DPERR_SESSIONLOST:
return "DPERR_SESSIONLOST";
2002-06-01 01:06:46 +02:00
case DPERR_UNINITIALIZED:
return "DPERR_UNINITIALIZED";
2002-06-01 01:06:46 +02:00
case DPERR_NONEWPLAYERS:
return "DPERR_NONEWPLAYERS";
2002-06-01 01:06:46 +02:00
case DPERR_INVALIDPASSWORD:
return "DPERR_INVALIDPASSWORD";
2002-06-01 01:06:46 +02:00
case DPERR_CONNECTING:
return "DPERR_CONNECTING";
2002-06-01 01:06:46 +02:00
case DPERR_CONNECTIONLOST:
return "DPERR_CONNECTIONLOST";
case DPERR_UNKNOWNMESSAGE:
return "DPERR_UNKNOWNMESSAGE";
2002-06-01 01:06:46 +02:00
case DPERR_CANCELFAILED:
return "DPERR_CANCELFAILED";
2002-06-01 01:06:46 +02:00
case DPERR_INVALIDPRIORITY:
return "DPERR_INVALIDPRIORITY";
2002-06-01 01:06:46 +02:00
case DPERR_NOTHANDLED:
return "DPERR_NOTHANDLED";
2002-06-01 01:06:46 +02:00
case DPERR_CANCELLED:
return "DPERR_CANCELLED";
2002-06-01 01:06:46 +02:00
case DPERR_ABORTED:
return "DPERR_ABORTED";
2002-06-01 01:06:46 +02:00
case DPERR_BUFFERTOOLARGE:
return "DPERR_BUFFERTOOLARGE";
2002-06-01 01:06:46 +02:00
case DPERR_CANTCREATEPROCESS:
return "DPERR_CANTCREATEPROCESS";
2002-06-01 01:06:46 +02:00
case DPERR_APPNOTSTARTED:
return "DPERR_APPNOTSTARTED";
2002-06-01 01:06:46 +02:00
case DPERR_INVALIDINTERFACE:
return "DPERR_INVALIDINTERFACE";
2002-06-01 01:06:46 +02:00
case DPERR_NOSERVICEPROVIDER:
return "DPERR_NOSERVICEPROVIDER";
2002-06-01 01:06:46 +02:00
case DPERR_UNKNOWNAPPLICATION:
return "DPERR_UNKNOWNAPPLICATION";
2002-06-01 01:06:46 +02:00
case DPERR_NOTLOBBIED:
return "DPERR_NOTLOBBIED";
2002-06-01 01:06:46 +02:00
case DPERR_SERVICEPROVIDERLOADED:
return "DPERR_SERVICEPROVIDERLOADED";
2002-06-01 01:06:46 +02:00
case DPERR_ALREADYREGISTERED:
return "DPERR_ALREADYREGISTERED";
2002-06-01 01:06:46 +02:00
case DPERR_NOTREGISTERED:
return "DPERR_NOTREGISTERED";
2002-06-01 01:06:46 +02:00
case DPERR_AUTHENTICATIONFAILED:
return "DPERR_AUTHENTICATIONFAILED";
2002-06-01 01:06:46 +02:00
case DPERR_CANTLOADSSPI:
return "DPERR_CANTLOADSSPI";
2002-06-01 01:06:46 +02:00
case DPERR_ENCRYPTIONFAILED:
return "DPERR_ENCRYPTIONFAILED";
2002-06-01 01:06:46 +02:00
case DPERR_SIGNFAILED:
return "DPERR_SIGNFAILED";
2002-06-01 01:06:46 +02:00
case DPERR_CANTLOADSECURITYPACKAGE:
return "DPERR_CANTLOADSECURITYPACKAGE";
2002-06-01 01:06:46 +02:00
case DPERR_ENCRYPTIONNOTSUPPORTED:
return "DPERR_ENCRYPTIONNOTSUPPORTED";
2002-06-01 01:06:46 +02:00
case DPERR_CANTLOADCAPI:
return "DPERR_CANTLOADCAPI";
2002-06-01 01:06:46 +02:00
case DPERR_NOTLOGGEDIN:
return "DPERR_NOTLOGGEDIN";
2002-06-01 01:06:46 +02:00
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;
}
}