Removed the cleanup_window_queue request.
[wine] / dlls / dplayx / dplayx_global.c
1 /* dplayx.dll global data implementation.
2  *
3  * Copyright 1999,2000 - Peter Hunnisett
4  *
5  * <presently under construction - contact hunnise@nortelnetworks.com>
6  *
7  */
8
9 /* NOTE: Methods that begin with DPLAYX_ are used for dealing with 
10  *       dplayx.dll data which is accessible from all processes.
11  */ 
12
13 #include <string.h>
14 #include "debugtools.h"
15 #include "winbase.h"
16 #include "winerror.h"
17 #include "wine/unicode.h"
18
19 #include "wingdi.h"
20 #include "winuser.h"
21
22 #include "dplayx_global.h"
23 #include "dplayx_messages.h" /* For CreateMessageReceptionThread only */
24
25 DEFAULT_DEBUG_CHANNEL(dplay);
26
27 /* FIXME: Need to do all that fun other dll referencing type of stuff */
28
29 /* Static data for all processes */
30 static LPCSTR lpszDplayxSemaName = "WINE_DPLAYX_SM";
31 static HANDLE hDplayxSema;
32
33 static LPCSTR lpszDplayxFileMapping = "WINE_DPLAYX_FM";
34 static HANDLE hDplayxSharedMem;
35
36 static LPVOID lpSharedStaticData = NULL;
37
38
39 #define DPLAYX_AquireSemaphore()  TRACE( "Waiting for DPLAYX semaphore\n" ); \
40                                   WaitForSingleObject( hDplayxSema, INFINITE );\
41                                   TRACE( "Through wait\n" )
42
43 #define DPLAYX_ReleaseSemaphore() ReleaseSemaphore( hDplayxSema, 1, NULL ); \
44                                   TRACE( "DPLAYX Semaphore released\n" ) /* FIXME: Is this correct? */
45
46
47 /* HACK for simple global data right now */ 
48 #define dwStaticSharedSize (128 * 1024) /* 128 KBytes */
49 #define dwDynamicSharedSize (512 * 1024) /* 512 KBytes */
50 #define dwTotalSharedSize  ( dwStaticSharedSize + dwDynamicSharedSize )
51
52
53 /* FIXME: Is there no easier way? */
54
55 /* Pretend the entire dynamic area is carved up into 512 byte blocks.
56  * Each block has 4 bytes which are 0 unless used */
57 #define dwBlockSize 512
58 #define dwMaxBlock  (dwDynamicSharedSize/dwBlockSize)
59
60 typedef struct 
61 {
62   DWORD used;
63   DWORD data[dwBlockSize-sizeof(DWORD)];
64 } DPLAYX_MEM_SLICE;
65
66 static DPLAYX_MEM_SLICE* lpMemArea;
67
68 void DPLAYX_PrivHeapFree( LPVOID addr );
69 void DPLAYX_PrivHeapFree( LPVOID addr )
70 {
71   LPVOID lpAddrStart;
72   DWORD dwBlockUsed;
73
74   /* Handle getting passed a NULL */
75   if( addr == NULL )
76   {
77     return;
78   } 
79
80   lpAddrStart = addr - sizeof(DWORD); /* Find block header */
81   dwBlockUsed =  ((BYTE*)lpAddrStart - (BYTE*)lpMemArea)/dwBlockSize;
82   
83   lpMemArea[ dwBlockUsed ].used = 0;
84 }
85
86 /* FIXME: This should be static, but is being used for a hack right now */
87 LPVOID DPLAYX_PrivHeapAlloc( DWORD flags, DWORD size );
88 LPVOID DPLAYX_PrivHeapAlloc( DWORD flags, DWORD size )
89 {
90   LPVOID lpvArea = NULL;
91   UINT   uBlockUsed;
92
93   if( size > (dwBlockSize - sizeof(DWORD)) )
94   {
95     FIXME( "Size exceeded. Request of 0x%08lx\n", size );
96     size = dwBlockSize - sizeof(DWORD);
97   } 
98
99   /* Find blank area */
100   uBlockUsed = 0; 
101   while( ( lpMemArea[ uBlockUsed ].used != 0 ) && ( uBlockUsed <= dwMaxBlock ) ) { uBlockUsed++; } 
102
103   if( uBlockUsed <= dwMaxBlock )
104   {
105     /* Set the area used */
106     lpMemArea[ uBlockUsed ].used = 1;
107     lpvArea = &(lpMemArea[ uBlockUsed ].data);
108   }
109   else
110   {
111     ERR( "No free block found\n" );
112     return NULL;
113   }
114   
115   if( flags & HEAP_ZERO_MEMORY )
116   {
117     ZeroMemory( lpvArea, size );
118   }
119
120   return lpvArea;
121 }
122
123 LPSTR DPLAYX_strdupA( DWORD flags, LPCSTR str );
124 LPSTR DPLAYX_strdupA( DWORD flags, LPCSTR str )
125 {
126   LPSTR p = DPLAYX_PrivHeapAlloc( flags, strlen(str) + 1 );
127   if(p) {
128     strcpy( p, str );
129   }
130   return p;
131 }
132
133 LPWSTR DPLAYX_strdupW( DWORD flags, LPCWSTR str );
134 LPWSTR DPLAYX_strdupW( DWORD flags, LPCWSTR str )
135 {                   
136   INT len = strlenW(str) + 1;
137   LPWSTR p = DPLAYX_PrivHeapAlloc( flags, len * sizeof(WCHAR) );
138   if(p) {
139     strcpyW( p, str );
140   }
141   return p;
142 }
143
144
145 enum { numSupportedLobbies = 32, numSupportedSessions = 32 };
146 typedef struct tagDPLAYX_LOBBYDATA
147 {
148   /* Points to lpConn + block of contiguous extra memory for dynamic parts
149    * of the struct directly following 
150    */
151   LPDPLCONNECTION lpConn;
152
153   /* Information for dplobby interfaces */
154   DWORD           dwAppID;
155   DWORD           dwAppLaunchedFromID;
156
157   /* Should this lobby app send messages to creator at important life
158    * stages
159    */
160   HANDLE hInformOnAppStart;
161   HANDLE hInformOnAppDeath;
162   HANDLE hInformOnSettingRead;
163
164   /* Sundries */
165   BOOL bWaitForConnectionSettings;
166   DWORD dwLobbyMsgThreadId;
167   
168
169 } DPLAYX_LOBBYDATA, *LPDPLAYX_LOBBYDATA;
170
171 static DPLAYX_LOBBYDATA* lobbyData = NULL;
172 /* static DPLAYX_LOBBYDATA lobbyData[ numSupportedLobbies ]; */
173
174 static DPSESSIONDESC2* sessionData = NULL; 
175 /* static DPSESSIONDESC2* sessionData[ numSupportedSessions ]; */
176
177 /* Function prototypes */
178 DWORD DPLAYX_SizeOfLobbyDataA( LPDPLCONNECTION lpDplData );
179 DWORD DPLAYX_SizeOfLobbyDataW( LPDPLCONNECTION lpDplData );
180 void DPLAYX_CopyConnStructA( LPDPLCONNECTION dest, LPDPLCONNECTION src );
181 void DPLAYX_CopyConnStructW( LPDPLCONNECTION dest, LPDPLCONNECTION src );
182 BOOL DPLAYX_IsAppIdLobbied( DWORD dwAppId, LPDPLAYX_LOBBYDATA* dplData );
183 void DPLAYX_InitializeLobbyDataEntry( LPDPLAYX_LOBBYDATA lpData );
184 BOOL DPLAYX_CopyIntoSessionDesc2A( LPDPSESSIONDESC2  lpSessionDest,
185                                    LPCDPSESSIONDESC2 lpSessionSrc );
186
187
188
189 /*************************************************************************** 
190  * Called to initialize the global data. This will only be used on the 
191  * loading of the dll 
192  ***************************************************************************/ 
193 BOOL DPLAYX_ConstructData(void)
194 {
195   SECURITY_ATTRIBUTES s_attrib;
196   BOOL                bInitializeSharedMemory = FALSE;
197   LPVOID              lpDesiredMemoryMapStart = (LPVOID)0x50000000;
198   HANDLE              hInformOnStart;
199
200   TRACE( "DPLAYX dll loaded - construct called\n" );
201
202   /* Create a semaphore to block access to DPLAYX global data structs */
203
204   s_attrib.bInheritHandle       = TRUE;
205   s_attrib.lpSecurityDescriptor = NULL;
206   s_attrib.nLength              = sizeof(s_attrib);
207
208   hDplayxSema = CreateSemaphoreA( &s_attrib, 1, 1, lpszDplayxSemaName ); 
209
210   /* First instance creates the semaphore. Others just use it */
211   if( GetLastError() == ERROR_SUCCESS )
212   {
213     TRACE( "Semaphore %u created\n", hDplayxSema );
214
215     /* The semaphore creator will also build the shared memory */
216     bInitializeSharedMemory = TRUE;
217   }
218   else if ( GetLastError() == ERROR_ALREADY_EXISTS )
219   {
220     TRACE( "Found semaphore handle %u\n", hDplayxSema );
221   }
222   else
223   {
224     ERR( ": semaphore error 0x%08lx\n", GetLastError() );
225     return FALSE;
226   }
227
228   SetLastError( ERROR_SUCCESS );
229
230   DPLAYX_AquireSemaphore();
231  
232   hDplayxSharedMem = CreateFileMappingA( INVALID_HANDLE_VALUE,
233                                          &s_attrib,
234                                          PAGE_READWRITE | SEC_COMMIT,
235                                          0, 
236                                          dwTotalSharedSize,
237                                          lpszDplayxFileMapping ); 
238
239   if( GetLastError() == ERROR_SUCCESS )
240   {
241     TRACE( "File mapped %u created\n", hDplayxSharedMem );
242   }
243   else if ( GetLastError() == ERROR_ALREADY_EXISTS )
244   {
245     TRACE( "Found FileMapping handle %u\n", hDplayxSharedMem );
246   }
247   else
248   {
249     ERR( ": unable to create shared memory 0x%08lx\n", GetLastError() );
250     return FALSE;
251   }
252
253   lpSharedStaticData = MapViewOfFileEx( hDplayxSharedMem, 
254                                         FILE_MAP_WRITE, 
255                                         0, 0, 0, lpDesiredMemoryMapStart );
256
257   if( lpSharedStaticData == NULL )
258   {
259     ERR( ": unable to map static data into process memory space: 0x%08lx\n", 
260          GetLastError() );
261     return FALSE;
262   }
263   else
264   {
265     if( lpDesiredMemoryMapStart == lpSharedStaticData )
266     {
267       TRACE( "File mapped to %p\n", lpSharedStaticData );
268     }
269     else
270     {
271       /* Presently the shared data structures use pointers. If the
272        * files are no mapped into the same area, the pointers will no
273        * longer make any sense :(
274        * FIXME: In the future make the shared data structures have some
275        *        sort of fixup to make them independent between data spaces.
276        *        This will also require a rework of the session data stuff.
277        */
278       ERR( "File mapped to %p (not %p). Expect failure\n",
279             lpSharedStaticData, lpDesiredMemoryMapStart );
280     }
281   }
282
283   /* Dynamic area starts just after the static area */
284   lpMemArea = (LPVOID)((BYTE*)lpSharedStaticData + dwStaticSharedSize);
285
286   /* FIXME: Crude hack */
287   lobbyData   = (DPLAYX_LOBBYDATA*)lpSharedStaticData;
288   sessionData = (DPSESSIONDESC2*)((BYTE*)lpSharedStaticData + (dwStaticSharedSize/2));
289
290   /* Initialize shared data segments. */
291   if( bInitializeSharedMemory )
292   {
293     UINT i;
294
295     TRACE( "Initializing shared memory\n" );
296
297     /* Set all lobbies to be "empty" */ 
298     for( i=0; i < numSupportedLobbies; i++ )
299     {
300       DPLAYX_InitializeLobbyDataEntry( &lobbyData[ i ] );
301     }
302
303     /* Set all sessions to be "empty" */
304     for( i=0; i < numSupportedSessions; i++ )
305     {
306       sessionData[i].dwSize = 0;
307     }
308
309     /* Zero out the dynmaic area */
310     ZeroMemory( lpMemArea, dwDynamicSharedSize );
311
312     /* Just for fun sync the whole data area */
313     FlushViewOfFile( lpSharedStaticData, dwTotalSharedSize );
314   }
315   
316   DPLAYX_ReleaseSemaphore();
317
318   /* Everything was created correctly. Signal the lobby client that
319    * we started up correctly
320    */
321   if( DPLAYX_GetThisLobbyHandles( &hInformOnStart, NULL, NULL, FALSE ) &&
322       hInformOnStart 
323     )
324   {
325     BOOL bSuccess;
326     bSuccess = SetEvent( hInformOnStart );
327     TRACE( "Signalling lobby app start event %u %s\n", 
328              hInformOnStart, bSuccess ? "succeed" : "failed" );
329
330     /* Close out handle */
331     DPLAYX_GetThisLobbyHandles( &hInformOnStart, NULL, NULL, TRUE );
332   }
333
334   return TRUE;
335 }
336
337 /*************************************************************************** 
338  * Called to destroy all global data. This will only be used on the 
339  * unloading of the dll 
340  ***************************************************************************/ 
341 BOOL DPLAYX_DestructData(void)
342 {
343   HANDLE hInformOnDeath;
344
345   TRACE( "DPLAYX dll unloaded - destruct called\n" );
346
347   /* If required, inform that this app is dying */
348   if( DPLAYX_GetThisLobbyHandles( NULL, &hInformOnDeath, NULL, FALSE ) &&
349       hInformOnDeath 
350     )
351   {
352     BOOL bSuccess;
353     bSuccess = SetEvent( hInformOnDeath );
354     TRACE( "Signalling lobby app death event %u %s\n", 
355              hInformOnDeath, bSuccess ? "succeed" : "failed" );
356
357     /* Close out handle */
358     DPLAYX_GetThisLobbyHandles( NULL, &hInformOnDeath, NULL, TRUE );
359   }
360
361   /* DO CLEAN UP (LAST) */
362
363   /* Delete the semaphore */
364   CloseHandle( hDplayxSema );
365  
366   /* Delete shared memory file mapping */
367   UnmapViewOfFile( lpSharedStaticData );
368   CloseHandle( hDplayxSharedMem );
369
370   return FALSE;
371 }
372
373
374 void DPLAYX_InitializeLobbyDataEntry( LPDPLAYX_LOBBYDATA lpData )
375 {
376   ZeroMemory( lpData, sizeof( *lpData ) );
377 }
378
379 /* NOTE: This must be called with the semaphore aquired. 
380  * TRUE/FALSE with a pointer to it's data returned. Pointer data is
381  * is only valid if TRUE is returned.
382  */
383 BOOL DPLAYX_IsAppIdLobbied( DWORD dwAppID, LPDPLAYX_LOBBYDATA* lplpDplData )
384 {
385   UINT i;
386
387   *lplpDplData = NULL;
388
389   if( dwAppID == 0 )
390   {
391     dwAppID = GetCurrentProcessId();
392     TRACE( "Translated dwAppID == 0 into 0x%08lx\n", dwAppID );
393   }
394
395   for( i=0; i < numSupportedLobbies; i++ )
396   {
397     if( lobbyData[ i ].dwAppID == dwAppID )
398     {
399       /* This process is lobbied */ 
400       TRACE( "Found 0x%08lx @ %u\n", dwAppID, i );
401       *lplpDplData = &lobbyData[ i ];
402       return TRUE;
403     }
404   }
405
406   return FALSE;
407 }
408
409 /* Reserve a spot for the new appliction. TRUE means success and FALSE failure.  */
410 BOOL DPLAYX_CreateLobbyApplication( DWORD dwAppID )
411 {
412   UINT i;
413
414   /* 0 is the marker for unused application data slots */
415   if( dwAppID == 0 )
416   {
417     return FALSE;
418   } 
419
420   DPLAYX_AquireSemaphore();
421
422   /* Find an empty space in the list and insert the data */
423   for( i=0; i < numSupportedLobbies; i++ )
424   {
425     if( lobbyData[ i ].dwAppID == 0 )
426     {
427       /* This process is now lobbied */
428       TRACE( "Setting lobbyData[%u] for (0x%08lx,0x%08lx)\n",
429               i, dwAppID, GetCurrentProcessId() );
430
431       lobbyData[ i ].dwAppID              = dwAppID;
432       lobbyData[ i ].dwAppLaunchedFromID  = GetCurrentProcessId();
433
434       /* FIXME: Where is the best place for this? In interface or here? */
435       lobbyData[ i ].hInformOnAppStart = 0; 
436       lobbyData[ i ].hInformOnAppDeath = 0;
437       lobbyData[ i ].hInformOnSettingRead = 0;
438
439       DPLAYX_ReleaseSemaphore();
440       return TRUE;
441     }
442   }
443
444   ERR( "No empty lobbies\n" );
445
446   DPLAYX_ReleaseSemaphore();
447   return FALSE;
448 }
449
450 /* I'm not sure when I'm going to need this, but here it is */
451 BOOL DPLAYX_DestroyLobbyApplication( DWORD dwAppID ) 
452 {
453   UINT i;
454
455   DPLAYX_AquireSemaphore();
456
457   /* Find an empty space in the list and insert the data */
458   for( i=0; i < numSupportedLobbies; i++ )
459   {
460     if( lobbyData[ i ].dwAppID == dwAppID )
461     {
462       /* FIXME: Should free up anything unused. Tisk tisk :0 */
463       /* Mark this entry unused */
464       TRACE( "Marking lobbyData[%u] unused\n", i );
465       DPLAYX_InitializeLobbyDataEntry( &lobbyData[ i ] );
466
467       DPLAYX_ReleaseSemaphore();
468       return TRUE;
469     }
470   }
471
472   DPLAYX_ReleaseSemaphore();
473   ERR( "Unable to find global entry for application\n" );
474   return FALSE;
475 }
476
477 BOOL DPLAYX_SetLobbyHandles( DWORD dwAppID,
478                              HANDLE hStart, HANDLE hDeath, HANDLE hConnRead )
479 {
480   LPDPLAYX_LOBBYDATA lpLData;
481
482   /* Need to explictly give lobby application. Can't set for yourself */
483   if( dwAppID == 0 )
484   {
485     return FALSE;
486   }
487
488   DPLAYX_AquireSemaphore();
489
490   if( !DPLAYX_IsAppIdLobbied( dwAppID, &lpLData ) )
491   {
492     DPLAYX_ReleaseSemaphore();
493     return FALSE;
494   }
495
496   lpLData->hInformOnAppStart    = hStart;
497   lpLData->hInformOnAppDeath    = hDeath;
498   lpLData->hInformOnSettingRead = hConnRead;
499
500   DPLAYX_ReleaseSemaphore();
501
502   return TRUE;
503 }
504
505 BOOL DPLAYX_GetThisLobbyHandles( LPHANDLE lphStart, 
506                                  LPHANDLE lphDeath,
507                                  LPHANDLE lphConnRead,
508                                  BOOL     bClearSetHandles )
509 {
510   LPDPLAYX_LOBBYDATA lpLData;
511
512   DPLAYX_AquireSemaphore();
513
514   if( !DPLAYX_IsAppIdLobbied( 0, &lpLData ) )
515   {
516     DPLAYX_ReleaseSemaphore();
517     return FALSE;
518   }
519
520   if( lphStart != NULL )
521   {
522     if( lpLData->hInformOnAppStart == 0 )
523     {
524       DPLAYX_ReleaseSemaphore();
525       return FALSE; 
526     }
527
528     *lphStart = lpLData->hInformOnAppStart;
529
530     if( bClearSetHandles )
531     {
532       CloseHandle( lpLData->hInformOnAppStart );
533       lpLData->hInformOnAppStart = 0;
534     }
535   }
536
537   if( lphDeath != NULL )
538   {
539     if( lpLData->hInformOnAppDeath == 0 )
540     {
541       DPLAYX_ReleaseSemaphore();
542       return FALSE;
543     }
544
545     *lphDeath = lpLData->hInformOnAppDeath;
546
547     if( bClearSetHandles )
548     {
549       CloseHandle( lpLData->hInformOnAppDeath );
550       lpLData->hInformOnAppDeath = 0;
551     }
552   }
553
554   if( lphConnRead != NULL )
555   {
556     if( lpLData->hInformOnSettingRead == 0 )
557     {
558       DPLAYX_ReleaseSemaphore();
559       return FALSE;
560     }
561
562     *lphConnRead = lpLData->hInformOnSettingRead;
563
564     if( bClearSetHandles )
565     {
566       CloseHandle( lpLData->hInformOnSettingRead );
567       lpLData->hInformOnSettingRead = 0;
568     }
569   }
570  
571   DPLAYX_ReleaseSemaphore();
572
573   return TRUE;
574 }
575
576
577 HRESULT DPLAYX_GetConnectionSettingsA
578 ( DWORD dwAppID,
579   LPVOID lpData,
580   LPDWORD lpdwDataSize )
581 {
582   LPDPLAYX_LOBBYDATA lpDplData;
583   DWORD              dwRequiredDataSize = 0;
584   HANDLE             hInformOnSettingRead;
585
586   DPLAYX_AquireSemaphore();
587
588   if ( ! DPLAYX_IsAppIdLobbied( dwAppID, &lpDplData ) )
589   {
590     DPLAYX_ReleaseSemaphore();
591
592     TRACE( "Application 0x%08lx is not lobbied\n", dwAppID );
593     return DPERR_NOTLOBBIED;
594   }
595
596   dwRequiredDataSize = DPLAYX_SizeOfLobbyDataA( lpDplData->lpConn );
597
598   /* Do they want to know the required buffer size or is the provided buffer
599    * big enough? 
600    */
601   if ( ( lpData == NULL ) ||
602        ( *lpdwDataSize < dwRequiredDataSize )
603      )
604   {
605     DPLAYX_ReleaseSemaphore();
606
607     *lpdwDataSize = DPLAYX_SizeOfLobbyDataA( lpDplData->lpConn );
608
609     return DPERR_BUFFERTOOSMALL;
610   }
611
612   DPLAYX_CopyConnStructA( (LPDPLCONNECTION)lpData, lpDplData->lpConn );
613
614   DPLAYX_ReleaseSemaphore();
615
616   /* They have gotten the information - signal the event if required */
617   if( DPLAYX_GetThisLobbyHandles( NULL, NULL, &hInformOnSettingRead, FALSE ) &&
618       hInformOnSettingRead 
619     )
620   {
621     BOOL bSuccess;
622     bSuccess = SetEvent( hInformOnSettingRead );
623     TRACE( "Signalling setting read event %u %s\n", 
624              hInformOnSettingRead, bSuccess ? "succeed" : "failed" );
625
626     /* Close out handle */
627     DPLAYX_GetThisLobbyHandles( NULL, NULL, &hInformOnSettingRead, TRUE );
628   }
629
630   return DP_OK;
631 }
632
633 /* Assumption: Enough contiguous space was allocated at dest */
634 void DPLAYX_CopyConnStructA( LPDPLCONNECTION dest, LPDPLCONNECTION src )
635 {
636   BYTE* lpStartOfFreeSpace;
637
638   CopyMemory( dest, src, sizeof( DPLCONNECTION ) );
639
640   lpStartOfFreeSpace = ((BYTE*)dest) + sizeof( DPLCONNECTION );
641
642   /* Copy the LPDPSESSIONDESC2 structure if it exists */
643   if( src->lpSessionDesc )
644   {
645     dest->lpSessionDesc = (LPDPSESSIONDESC2)lpStartOfFreeSpace;
646     lpStartOfFreeSpace += sizeof( DPSESSIONDESC2 );
647     CopyMemory( dest->lpSessionDesc, src->lpSessionDesc, sizeof( DPSESSIONDESC2 ) );
648
649     /* Session names may or may not exist */
650     if( src->lpSessionDesc->u1.lpszSessionNameA )
651     {
652       strcpy( (LPSTR)lpStartOfFreeSpace, src->lpSessionDesc->u1.lpszSessionNameA );
653       dest->lpSessionDesc->u1.lpszSessionNameA = (LPSTR)lpStartOfFreeSpace;
654       lpStartOfFreeSpace +=  
655         strlen( (LPSTR)dest->lpSessionDesc->u1.lpszSessionNameA ) + 1;
656     }
657
658     if( src->lpSessionDesc->u2.lpszPasswordA )
659     {
660       strcpy( (LPSTR)lpStartOfFreeSpace, src->lpSessionDesc->u2.lpszPasswordA );
661       dest->lpSessionDesc->u2.lpszPasswordA = (LPSTR)lpStartOfFreeSpace;
662       lpStartOfFreeSpace += 
663         strlen( (LPSTR)dest->lpSessionDesc->u2.lpszPasswordA ) + 1;
664     }
665   }
666
667   /* DPNAME structure is optional */
668   if( src->lpPlayerName )
669   {
670     dest->lpPlayerName = (LPDPNAME)lpStartOfFreeSpace;
671     lpStartOfFreeSpace += sizeof( DPNAME );
672     CopyMemory( dest->lpPlayerName, src->lpPlayerName, sizeof( DPNAME ) );
673
674     if( src->lpPlayerName->u1.lpszShortNameA )
675     {
676       strcpy( (LPSTR)lpStartOfFreeSpace, src->lpPlayerName->u1.lpszShortNameA );
677       dest->lpPlayerName->u1.lpszShortNameA = (LPSTR)lpStartOfFreeSpace;
678       lpStartOfFreeSpace +=  
679         strlen( (LPSTR)dest->lpPlayerName->u1.lpszShortNameA ) + 1;
680     }
681
682     if( src->lpPlayerName->u2.lpszLongNameA )
683     {
684       strcpy( (LPSTR)lpStartOfFreeSpace, src->lpPlayerName->u2.lpszLongNameA );
685       dest->lpPlayerName->u2.lpszLongNameA = (LPSTR)lpStartOfFreeSpace;
686       lpStartOfFreeSpace +=  
687         strlen( (LPSTR)dest->lpPlayerName->u2.lpszLongName ) + 1 ;
688     }
689
690   }
691
692   /* Copy address if it exists */
693   if( src->lpAddress )
694   {
695     dest->lpAddress = (LPVOID)lpStartOfFreeSpace;
696     CopyMemory( lpStartOfFreeSpace, src->lpAddress, src->dwAddressSize );
697     /* No need to advance lpStartOfFreeSpace as there is no more "dynamic" data */
698   }
699 }
700
701 HRESULT DPLAYX_GetConnectionSettingsW
702 ( DWORD dwAppID,
703   LPVOID lpData,
704   LPDWORD lpdwDataSize )
705 {
706   LPDPLAYX_LOBBYDATA lpDplData;
707   DWORD              dwRequiredDataSize = 0;
708   HANDLE             hInformOnSettingRead;
709
710   DPLAYX_AquireSemaphore();
711
712   if ( ! DPLAYX_IsAppIdLobbied( dwAppID, &lpDplData ) )
713   {
714     DPLAYX_ReleaseSemaphore();
715     return DPERR_NOTLOBBIED;
716   }
717
718   dwRequiredDataSize = DPLAYX_SizeOfLobbyDataW( lpDplData->lpConn );
719
720   /* Do they want to know the required buffer size or is the provided buffer
721    * big enough? 
722    */
723   if ( ( lpData == NULL ) ||
724        ( *lpdwDataSize < dwRequiredDataSize )
725      )
726   {
727     DPLAYX_ReleaseSemaphore();
728
729     *lpdwDataSize = DPLAYX_SizeOfLobbyDataW( lpDplData->lpConn );
730
731     return DPERR_BUFFERTOOSMALL;
732   }
733
734   DPLAYX_CopyConnStructW( (LPDPLCONNECTION)lpData, lpDplData->lpConn );
735
736   DPLAYX_ReleaseSemaphore();
737
738   /* They have gotten the information - signal the event if required */
739   if( DPLAYX_GetThisLobbyHandles( NULL, NULL, &hInformOnSettingRead, FALSE ) &&
740       hInformOnSettingRead 
741     )
742   {
743     BOOL bSuccess;
744     bSuccess = SetEvent( hInformOnSettingRead );
745     TRACE( "Signalling setting read event %u %s\n", 
746              hInformOnSettingRead, bSuccess ? "succeed" : "failed" );
747
748     /* Close out handle */
749     DPLAYX_GetThisLobbyHandles( NULL, NULL, &hInformOnSettingRead, TRUE );
750   }
751
752   return DP_OK;
753 }
754
755 /* Assumption: Enough contiguous space was allocated at dest */
756 void DPLAYX_CopyConnStructW( LPDPLCONNECTION dest, LPDPLCONNECTION src )
757 {
758   BYTE*              lpStartOfFreeSpace;
759
760   CopyMemory( dest, src, sizeof( DPLCONNECTION ) );
761
762   lpStartOfFreeSpace = ( (BYTE*)dest) + sizeof( DPLCONNECTION );
763
764   /* Copy the LPDPSESSIONDESC2 structure if it exists */
765   if( src->lpSessionDesc )
766   {
767     dest->lpSessionDesc = (LPDPSESSIONDESC2)lpStartOfFreeSpace;
768     lpStartOfFreeSpace += sizeof( DPSESSIONDESC2 );
769     CopyMemory( dest->lpSessionDesc, src->lpSessionDesc, sizeof( DPSESSIONDESC2 ) ); 
770
771     /* Session names may or may not exist */
772     if( src->lpSessionDesc->u1.lpszSessionName )
773     {
774       strcpyW( (LPWSTR)lpStartOfFreeSpace, dest->lpSessionDesc->u1.lpszSessionName );
775       src->lpSessionDesc->u1.lpszSessionName = (LPWSTR)lpStartOfFreeSpace;
776       lpStartOfFreeSpace +=  sizeof(WCHAR) *
777         ( strlenW( (LPWSTR)dest->lpSessionDesc->u1.lpszSessionName ) + 1 );
778     }
779
780     if( src->lpSessionDesc->u2.lpszPassword )
781     {
782       strcpyW( (LPWSTR)lpStartOfFreeSpace, src->lpSessionDesc->u2.lpszPassword );
783       dest->lpSessionDesc->u2.lpszPassword = (LPWSTR)lpStartOfFreeSpace;
784       lpStartOfFreeSpace +=  sizeof(WCHAR) *
785         ( strlenW( (LPWSTR)dest->lpSessionDesc->u2.lpszPassword ) + 1 );
786     }
787   }
788
789   /* DPNAME structure is optional */
790   if( src->lpPlayerName )
791   {
792     dest->lpPlayerName = (LPDPNAME)lpStartOfFreeSpace;
793     lpStartOfFreeSpace += sizeof( DPNAME );
794     CopyMemory( dest->lpPlayerName, src->lpPlayerName, sizeof( DPNAME ) );
795    
796     if( src->lpPlayerName->u1.lpszShortName )
797     {
798       strcpyW( (LPWSTR)lpStartOfFreeSpace, src->lpPlayerName->u1.lpszShortName );
799       dest->lpPlayerName->u1.lpszShortName = (LPWSTR)lpStartOfFreeSpace;
800       lpStartOfFreeSpace +=  sizeof(WCHAR) *
801         ( strlenW( (LPWSTR)dest->lpPlayerName->u1.lpszShortName ) + 1 );
802     }
803
804     if( src->lpPlayerName->u2.lpszLongName )
805     {
806       strcpyW( (LPWSTR)lpStartOfFreeSpace, src->lpPlayerName->u2.lpszLongName );
807       dest->lpPlayerName->u2.lpszLongName = (LPWSTR)lpStartOfFreeSpace;
808       lpStartOfFreeSpace +=  sizeof(WCHAR) *
809         ( strlenW( (LPWSTR)dest->lpPlayerName->u2.lpszLongName ) + 1 );
810     }
811
812   }
813
814   /* Copy address if it exists */
815   if( src->lpAddress )
816   {
817     dest->lpAddress = (LPVOID)lpStartOfFreeSpace;
818     CopyMemory( lpStartOfFreeSpace, src->lpAddress, src->dwAddressSize );  
819     /* No need to advance lpStartOfFreeSpace as there is no more "dynamic" data */
820   }
821
822 }
823
824 /* Store the structure into the shared data structre. Ensure that allocs for
825  * variable length strings come from the shared data structure.
826  * FIXME: We need to free information as well 
827  */
828 HRESULT DPLAYX_SetConnectionSettingsA
829 ( DWORD dwFlags,
830   DWORD dwAppID,
831   LPDPLCONNECTION lpConn )
832 {
833   LPDPLAYX_LOBBYDATA lpDplData;
834
835   /* Paramater check */
836   if( dwFlags || !lpConn )
837   {
838     ERR("invalid parameters.\n");
839     return DPERR_INVALIDPARAMS;
840   }
841
842   /* Store information */
843   if(  lpConn->dwSize != sizeof(DPLCONNECTION) )
844   {
845     ERR(": old/new DPLCONNECTION type? Size=%08lx vs. expected=%ul bytes\n",
846          lpConn->dwSize, sizeof( DPLCONNECTION ) );
847
848     return DPERR_INVALIDPARAMS;
849   }
850
851   DPLAYX_AquireSemaphore();
852
853   if ( ! DPLAYX_IsAppIdLobbied( dwAppID, &lpDplData ) )
854   {
855     DPLAYX_ReleaseSemaphore();
856
857     return DPERR_NOTLOBBIED;
858   }
859
860   if(  (!lpConn->lpSessionDesc ) ||
861        ( lpConn->lpSessionDesc->dwSize != sizeof( DPSESSIONDESC2 ) )
862     )
863   {
864     DPLAYX_ReleaseSemaphore();
865
866     ERR("DPSESSIONDESC passed in? Size=%lu vs. expected=%u bytes\n",
867          lpConn->lpSessionDesc->dwSize, sizeof( DPSESSIONDESC2 ) );
868
869     return DPERR_INVALIDPARAMS;
870   }
871
872   /* Free the existing memory */
873   DPLAYX_PrivHeapFree( lpDplData->lpConn );
874
875   lpDplData->lpConn = DPLAYX_PrivHeapAlloc( HEAP_ZERO_MEMORY, 
876                                             DPLAYX_SizeOfLobbyDataA( lpConn ) );
877
878   DPLAYX_CopyConnStructA( lpDplData->lpConn, lpConn );
879
880
881   DPLAYX_ReleaseSemaphore();
882
883   /* FIXME: Send a message - I think */
884
885   return DP_OK;
886 }
887
888 /* Store the structure into the shared data structre. Ensure that allocs for
889  * variable length strings come from the shared data structure.
890  * FIXME: We need to free information as well 
891  */
892 HRESULT DPLAYX_SetConnectionSettingsW
893 ( DWORD dwFlags,
894   DWORD dwAppID,
895   LPDPLCONNECTION lpConn )
896 {
897   LPDPLAYX_LOBBYDATA lpDplData;
898
899   /* Paramater check */
900   if( dwFlags || !lpConn )
901   {
902     ERR("invalid parameters.\n");
903     return DPERR_INVALIDPARAMS;
904   }
905
906   /* Store information */
907   if(  lpConn->dwSize != sizeof(DPLCONNECTION) )
908   {
909     ERR(": old/new DPLCONNECTION type? Size=%lu vs. expected=%u bytes\n",
910          lpConn->dwSize, sizeof( DPLCONNECTION ) );
911
912     return DPERR_INVALIDPARAMS;
913   }
914
915   DPLAYX_AquireSemaphore();
916
917   if ( ! DPLAYX_IsAppIdLobbied( dwAppID, &lpDplData ) )
918   {
919     DPLAYX_ReleaseSemaphore();
920
921     return DPERR_NOTLOBBIED;
922   }
923
924   /* Free the existing memory */
925   DPLAYX_PrivHeapFree( lpDplData->lpConn );
926
927   lpDplData->lpConn = DPLAYX_PrivHeapAlloc( HEAP_ZERO_MEMORY,
928                                             DPLAYX_SizeOfLobbyDataW( lpConn ) );
929
930   DPLAYX_CopyConnStructW( lpDplData->lpConn, lpConn );
931
932
933   DPLAYX_ReleaseSemaphore();
934
935   /* FIXME: Send a message - I think */
936
937   return DP_OK;
938 }
939
940 DWORD DPLAYX_SizeOfLobbyDataA( LPDPLCONNECTION lpConn )
941 {
942   DWORD dwTotalSize = sizeof( DPLCONNECTION );
943
944   /* Just a safety check */
945   if( lpConn == NULL )
946   {
947     ERR( "lpConn is NULL\n" );
948     return 0;
949   }
950
951   if( lpConn->lpSessionDesc != NULL )
952   {
953     dwTotalSize += sizeof( DPSESSIONDESC2 );
954
955     if( lpConn->lpSessionDesc->u1.lpszSessionNameA )
956     {
957       dwTotalSize += strlen( lpConn->lpSessionDesc->u1.lpszSessionNameA ) + 1;
958     }
959  
960     if( lpConn->lpSessionDesc->u2.lpszPasswordA )
961     {
962       dwTotalSize += strlen( lpConn->lpSessionDesc->u2.lpszPasswordA ) + 1;
963     }
964   }
965
966   if( lpConn->lpPlayerName != NULL )
967   {
968     dwTotalSize += sizeof( DPNAME );
969
970     if( lpConn->lpPlayerName->u1.lpszShortNameA )
971     {
972       dwTotalSize += strlen( lpConn->lpPlayerName->u1.lpszShortNameA ) + 1;
973     }
974
975     if( lpConn->lpPlayerName->u2.lpszLongNameA )
976     {
977       dwTotalSize += strlen( lpConn->lpPlayerName->u2.lpszLongNameA ) + 1;
978     }
979
980   }
981
982   dwTotalSize += lpConn->dwAddressSize;
983
984   return dwTotalSize;
985 }
986
987 DWORD DPLAYX_SizeOfLobbyDataW( LPDPLCONNECTION lpConn )
988 {
989   DWORD dwTotalSize = sizeof( DPLCONNECTION );
990
991   /* Just a safety check */
992   if( lpConn == NULL )
993   {
994     ERR( "lpConn is NULL\n" );
995     return 0;
996   }
997
998   if( lpConn->lpSessionDesc != NULL )
999   {
1000     dwTotalSize += sizeof( DPSESSIONDESC2 );
1001
1002     if( lpConn->lpSessionDesc->u1.lpszSessionName )
1003     {
1004       dwTotalSize += sizeof( WCHAR ) *
1005         ( strlenW( lpConn->lpSessionDesc->u1.lpszSessionName ) + 1 );
1006     }
1007
1008     if( lpConn->lpSessionDesc->u2.lpszPassword )
1009     {
1010       dwTotalSize += sizeof( WCHAR ) *
1011         ( strlenW( lpConn->lpSessionDesc->u2.lpszPassword ) + 1 );
1012     }
1013   }
1014
1015   if( lpConn->lpPlayerName != NULL )
1016   {
1017     dwTotalSize += sizeof( DPNAME );
1018
1019     if( lpConn->lpPlayerName->u1.lpszShortName )
1020     {
1021       dwTotalSize += sizeof( WCHAR ) *
1022         ( strlenW( lpConn->lpPlayerName->u1.lpszShortName ) + 1 );
1023     }
1024
1025     if( lpConn->lpPlayerName->u2.lpszLongName )
1026     {
1027       dwTotalSize += sizeof( WCHAR ) *
1028         ( strlenW( lpConn->lpPlayerName->u2.lpszLongName ) + 1 );
1029     }
1030
1031   }
1032
1033   dwTotalSize += lpConn->dwAddressSize;
1034
1035   return dwTotalSize;
1036 }
1037
1038
1039
1040 LPDPSESSIONDESC2 DPLAYX_CopyAndAllocateSessionDesc2A( LPCDPSESSIONDESC2 lpSessionSrc )
1041 {
1042    LPDPSESSIONDESC2 lpSessionDest = 
1043      (LPDPSESSIONDESC2)HeapAlloc( GetProcessHeap(), 
1044                                   HEAP_ZERO_MEMORY, sizeof( *lpSessionSrc ) );
1045    DPLAYX_CopyIntoSessionDesc2A( lpSessionDest, lpSessionSrc );
1046
1047    return lpSessionDest;
1048 }
1049
1050 /* Copy an ANSI session desc structure to the given buffer */
1051 BOOL DPLAYX_CopyIntoSessionDesc2A( LPDPSESSIONDESC2  lpSessionDest,
1052                                    LPCDPSESSIONDESC2 lpSessionSrc )
1053 {
1054   CopyMemory( lpSessionDest, lpSessionSrc, sizeof( *lpSessionSrc ) );
1055
1056   if( lpSessionSrc->u1.lpszSessionNameA )
1057   {
1058       if ((lpSessionDest->u1.lpszSessionNameA = HeapAlloc( GetProcessHeap(), 0,
1059                                                              strlen(lpSessionSrc->u1.lpszSessionNameA)+1 )))
1060           strcpy( lpSessionDest->u1.lpszSessionNameA, lpSessionSrc->u1.lpszSessionNameA );
1061   }
1062   if( lpSessionSrc->u2.lpszPasswordA )
1063   {
1064       if ((lpSessionDest->u2.lpszPasswordA = HeapAlloc( GetProcessHeap(), 0,
1065                                                           strlen(lpSessionSrc->u2.lpszPasswordA)+1 )))
1066           strcpy( lpSessionDest->u2.lpszPasswordA, lpSessionSrc->u2.lpszPasswordA );
1067   }
1068
1069   return TRUE;
1070 }
1071
1072 /* Start the index at 0. index will be updated to equal that which should
1073    be passed back into this function for the next element */
1074 LPDPSESSIONDESC2 DPLAYX_CopyAndAllocateLocalSession( UINT* index )
1075 {
1076   for( ; (*index) < numSupportedSessions; (*index)++ )
1077   {
1078     if( sessionData[(*index)].dwSize != 0 )
1079     {
1080       return DPLAYX_CopyAndAllocateSessionDesc2A( &sessionData[(*index)++] ); 
1081     }
1082   }
1083  
1084   /* No more sessions */
1085   return NULL;
1086 }
1087
1088 /* Start the index at 0. index will be updated to equal that which should
1089    be passed back into this function for the next element */
1090 BOOL DPLAYX_CopyLocalSession( UINT* index, LPDPSESSIONDESC2 lpsd )
1091 {
1092   for( ; (*index) < numSupportedSessions; (*index)++ )
1093   {
1094     if( sessionData[(*index)].dwSize != 0 )
1095     {
1096       return DPLAYX_CopyIntoSessionDesc2A( lpsd, &sessionData[(*index)++] );
1097     }
1098   }
1099
1100   /* No more sessions */
1101   return FALSE;
1102 }
1103
1104 void DPLAYX_SetLocalSession( LPCDPSESSIONDESC2 lpsd )
1105 {
1106   UINT i;
1107
1108   /* FIXME: Is this an error if it exists already? */
1109  
1110   /* Crude/wrong implementation for now. Just always add to first empty spot */
1111   for( i=0; i < numSupportedSessions; i++ )
1112   {
1113     /* Is this one empty? */
1114     if( sessionData[i].dwSize == 0 )
1115     {
1116       DPLAYX_CopyIntoSessionDesc2A( &sessionData[i], lpsd );  
1117       break;
1118     }
1119   }
1120
1121 }
1122
1123 BOOL DPLAYX_WaitForConnectionSettings( BOOL bWait )
1124 {
1125   LPDPLAYX_LOBBYDATA lpLobbyData;
1126
1127   DPLAYX_AquireSemaphore();
1128   
1129   if( !DPLAYX_IsAppIdLobbied( 0, &lpLobbyData ) )
1130   {
1131     DPLAYX_ReleaseSemaphore();
1132     return FALSE;
1133   }
1134
1135   lpLobbyData->bWaitForConnectionSettings = bWait;
1136
1137   DPLAYX_ReleaseSemaphore();
1138
1139   return TRUE;
1140 }
1141
1142 BOOL DPLAYX_AnyLobbiesWaitingForConnSettings(void)
1143 {
1144   UINT i;
1145   BOOL bFound = FALSE;
1146
1147   DPLAYX_AquireSemaphore();
1148
1149   for( i=0; i < numSupportedLobbies; i++ )
1150   {
1151     if( ( lobbyData[ i ].dwAppID != 0 ) &&            /* lobby initialized */
1152         ( lobbyData[ i ].bWaitForConnectionSettings ) /* Waiting */
1153       )
1154     { 
1155       bFound = TRUE;
1156       break; 
1157     }
1158   }
1159
1160   DPLAYX_ReleaseSemaphore();
1161
1162   return bFound;
1163 }
1164
1165 BOOL DPLAYX_SetLobbyMsgThreadId( DWORD dwAppId, DWORD dwThreadId )
1166 {
1167   LPDPLAYX_LOBBYDATA lpLobbyData;
1168
1169   DPLAYX_AquireSemaphore();
1170
1171   if( !DPLAYX_IsAppIdLobbied( dwAppId, &lpLobbyData ) )
1172   {
1173     DPLAYX_ReleaseSemaphore();
1174     return FALSE;
1175   }
1176
1177   lpLobbyData->dwLobbyMsgThreadId = dwThreadId;
1178
1179   DPLAYX_ReleaseSemaphore();
1180
1181   return TRUE;
1182 }
1183
1184 /* NOTE: This is potentially not thread safe. You are not guaranteed to end up 
1185          with the correct string printed in the case where the HRESULT is not
1186          known. You'll just get the last hr passed in printed. This can change
1187          over time if this method is used alot :) */
1188 LPCSTR DPLAYX_HresultToString(HRESULT hr)
1189 {
1190   static char szTempStr[12];
1191
1192   switch (hr)
1193   {
1194     case DP_OK:  
1195       return "DP_OK";
1196     case DPERR_ALREADYINITIALIZED: 
1197       return "DPERR_ALREADYINITIALIZED";
1198     case DPERR_ACCESSDENIED: 
1199       return "DPERR_ACCESSDENIED";
1200     case DPERR_ACTIVEPLAYERS: 
1201       return "DPERR_ACTIVEPLAYERS";
1202     case DPERR_BUFFERTOOSMALL: 
1203       return "DPERR_BUFFERTOOSMALL";
1204     case DPERR_CANTADDPLAYER: 
1205       return "DPERR_CANTADDPLAYER";
1206     case DPERR_CANTCREATEGROUP: 
1207       return "DPERR_CANTCREATEGROUP";
1208     case DPERR_CANTCREATEPLAYER: 
1209       return "DPERR_CANTCREATEPLAYER";
1210     case DPERR_CANTCREATESESSION: 
1211       return "DPERR_CANTCREATESESSION";
1212     case DPERR_CAPSNOTAVAILABLEYET: 
1213       return "DPERR_CAPSNOTAVAILABLEYET";
1214     case DPERR_EXCEPTION: 
1215       return "DPERR_EXCEPTION";
1216     case DPERR_GENERIC: 
1217       return "DPERR_GENERIC";
1218     case DPERR_INVALIDFLAGS: 
1219       return "DPERR_INVALIDFLAGS";
1220     case DPERR_INVALIDOBJECT: 
1221       return "DPERR_INVALIDOBJECT";
1222     case DPERR_INVALIDPARAMS: 
1223       return "DPERR_INVALIDPARAMS";
1224     case DPERR_INVALIDPLAYER: 
1225       return "DPERR_INVALIDPLAYER";
1226     case DPERR_INVALIDGROUP: 
1227       return "DPERR_INVALIDGROUP";
1228     case DPERR_NOCAPS: 
1229       return "DPERR_NOCAPS";
1230     case DPERR_NOCONNECTION: 
1231       return "DPERR_NOCONNECTION";
1232     case DPERR_OUTOFMEMORY: 
1233       return "DPERR_OUTOFMEMORY";
1234     case DPERR_NOMESSAGES: 
1235       return "DPERR_NOMESSAGES";
1236     case DPERR_NONAMESERVERFOUND: 
1237       return "DPERR_NONAMESERVERFOUND";
1238     case DPERR_NOPLAYERS: 
1239       return "DPERR_NOPLAYERS";
1240     case DPERR_NOSESSIONS: 
1241       return "DPERR_NOSESSIONS";
1242     case DPERR_PENDING: 
1243       return "DPERR_PENDING";
1244     case DPERR_SENDTOOBIG: 
1245       return "DPERR_SENDTOOBIG";
1246     case DPERR_TIMEOUT: 
1247       return "DPERR_TIMEOUT";
1248     case DPERR_UNAVAILABLE: 
1249       return "DPERR_UNAVAILABLE";
1250     case DPERR_UNSUPPORTED: 
1251       return "DPERR_UNSUPPORTED";
1252     case DPERR_BUSY: 
1253       return "DPERR_BUSY";
1254     case DPERR_USERCANCEL: 
1255       return "DPERR_USERCANCEL";
1256     case DPERR_NOINTERFACE: 
1257       return "DPERR_NOINTERFACE";
1258     case DPERR_CANNOTCREATESERVER: 
1259       return "DPERR_CANNOTCREATESERVER";
1260     case DPERR_PLAYERLOST: 
1261       return "DPERR_PLAYERLOST";
1262     case DPERR_SESSIONLOST: 
1263       return "DPERR_SESSIONLOST";
1264     case DPERR_UNINITIALIZED: 
1265       return "DPERR_UNINITIALIZED";
1266     case DPERR_NONEWPLAYERS: 
1267       return "DPERR_NONEWPLAYERS";
1268     case DPERR_INVALIDPASSWORD: 
1269       return "DPERR_INVALIDPASSWORD";
1270     case DPERR_CONNECTING: 
1271       return "DPERR_CONNECTING";
1272     case DPERR_CONNECTIONLOST: 
1273       return "DPERR_CONNECTIONLOST";
1274     case DPERR_UNKNOWNMESSAGE:
1275       return "DPERR_UNKNOWNMESSAGE";
1276     case DPERR_CANCELFAILED: 
1277       return "DPERR_CANCELFAILED";
1278     case DPERR_INVALIDPRIORITY: 
1279       return "DPERR_INVALIDPRIORITY";
1280     case DPERR_NOTHANDLED: 
1281       return "DPERR_NOTHANDLED";
1282     case DPERR_CANCELLED: 
1283       return "DPERR_CANCELLED";
1284     case DPERR_ABORTED: 
1285       return "DPERR_ABORTED";
1286     case DPERR_BUFFERTOOLARGE: 
1287       return "DPERR_BUFFERTOOLARGE";
1288     case DPERR_CANTCREATEPROCESS: 
1289       return "DPERR_CANTCREATEPROCESS";
1290     case DPERR_APPNOTSTARTED: 
1291       return "DPERR_APPNOTSTARTED";
1292     case DPERR_INVALIDINTERFACE: 
1293       return "DPERR_INVALIDINTERFACE";
1294     case DPERR_NOSERVICEPROVIDER: 
1295       return "DPERR_NOSERVICEPROVIDER";
1296     case DPERR_UNKNOWNAPPLICATION: 
1297       return "DPERR_UNKNOWNAPPLICATION";
1298     case DPERR_NOTLOBBIED: 
1299       return "DPERR_NOTLOBBIED";
1300     case DPERR_SERVICEPROVIDERLOADED: 
1301       return "DPERR_SERVICEPROVIDERLOADED";
1302     case DPERR_ALREADYREGISTERED: 
1303       return "DPERR_ALREADYREGISTERED";
1304     case DPERR_NOTREGISTERED: 
1305       return "DPERR_NOTREGISTERED";
1306     case DPERR_AUTHENTICATIONFAILED: 
1307       return "DPERR_AUTHENTICATIONFAILED";
1308     case DPERR_CANTLOADSSPI: 
1309       return "DPERR_CANTLOADSSPI";
1310     case DPERR_ENCRYPTIONFAILED: 
1311       return "DPERR_ENCRYPTIONFAILED";
1312     case DPERR_SIGNFAILED: 
1313       return "DPERR_SIGNFAILED";
1314     case DPERR_CANTLOADSECURITYPACKAGE: 
1315       return "DPERR_CANTLOADSECURITYPACKAGE";
1316     case DPERR_ENCRYPTIONNOTSUPPORTED: 
1317       return "DPERR_ENCRYPTIONNOTSUPPORTED";
1318     case DPERR_CANTLOADCAPI: 
1319       return "DPERR_CANTLOADCAPI";
1320     case DPERR_NOTLOGGEDIN: 
1321       return "DPERR_NOTLOGGEDIN";
1322     case DPERR_LOGONDENIED: 
1323       return "DPERR_LOGONDENIED";
1324     default:
1325       /* For errors not in the list, return HRESULT as a string
1326          This part is not thread safe */
1327       WARN( "Unknown error 0x%08lx\n", hr );
1328       wsprintfA( szTempStr, "0x%08lx", hr );
1329       return szTempStr;
1330   }
1331 }
1332