- Implemented loading and initialization of service providers
- Created service provider COM object
- Lots of dplay/dplobby implementation/fixes
- Clean up of ole/guid.c

diff --git a/dlls/dplayx/Makefile.in b/dlls/dplayx/Makefile.in
index 83a001d..d95b199 100644
--- a/dlls/dplayx/Makefile.in
+++ b/dlls/dplayx/Makefile.in
@@ -9,6 +9,7 @@
 C_SRCS = \
 	dpclassfactory.c \
 	dplay.c \
+	dplaysp.c \
 	dplayx_global.c \
 	dplayx_main.c \
 	dplayx_messages.c \
diff --git a/dlls/dplayx/dpclassfactory.c b/dlls/dplayx/dpclassfactory.c
index 70b7f13..660aa3a 100644
--- a/dlls/dplayx/dpclassfactory.c
+++ b/dlls/dplayx/dpclassfactory.c
@@ -39,7 +39,6 @@
         return --(This->ref);
 }
 
-/* Not the most efficient implementation, but it's simple */
 static HRESULT WINAPI DP_and_DPL_CreateInstance(
         LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj
 ) {
@@ -47,12 +46,11 @@
 
         TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
 
-        /* FIXME: reuse already created DP/DPL object if present? */
-        if ( directPlayLobby_QueryInterface( riid, ppobj ) == S_OK )
+        if ( DPL_CreateInterface( riid, ppobj ) == S_OK )
         {
            return S_OK;
         }
-        else if ( directPlay_QueryInterface( riid, ppobj ) == S_OK )
+        else if ( DP_CreateInterface( riid, ppobj ) == S_OK )
         {
            return S_OK;
         }
@@ -110,13 +108,3 @@
     ERR("(%p,%p,%p): no interface found.\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
     return CLASS_E_CLASSNOTAVAILABLE;
 }
-
-/***********************************************************************
- *		DllCanUnloadNow (DPLAYX.@)
- */
-HRESULT WINAPI DPLAYX_DllCanUnloadNow(void)
-{
-    FIXME("(void): stub\n");
-
-    return S_FALSE;
-}
diff --git a/dlls/dplayx/dpinit.h b/dlls/dplayx/dpinit.h
index 8408765..fceecc3 100644
--- a/dlls/dplayx/dpinit.h
+++ b/dlls/dplayx/dpinit.h
@@ -2,7 +2,12 @@
 #ifndef __WINE_DPINIT_H
 #define __WINE_DPINIT_H
 
-extern HRESULT directPlay_QueryInterface( REFIID riid, LPVOID* ppvObj );
-extern HRESULT directPlayLobby_QueryInterface( REFIID riid, LPVOID* ppvObj );
+#include "wtypes.h"
+#include "dplay_global.h"
+
+extern HRESULT DP_CreateInterface( REFIID riid, LPVOID* ppvObj );
+extern HRESULT DPL_CreateInterface( REFIID riid, LPVOID* ppvObj );
+extern HRESULT DPSP_CreateInterface( REFIID riid, LPVOID* ppvObj, 
+                                     IDirectPlay2Impl* dp );
 
 #endif
diff --git a/dlls/dplayx/dplay.c b/dlls/dplayx/dplay.c
index 68c2770..7f9ccf0 100644
--- a/dlls/dplayx/dplay.c
+++ b/dlls/dplayx/dplay.c
@@ -5,8 +5,7 @@
  * <presently under construction - contact hunnise@nortelnetworks.com>
  *
  */
-#include <string.h>
-
+#include "windef.h"
 #include "winerror.h"
 #include "winbase.h"
 #include "winnt.h"
@@ -19,180 +18,37 @@
 #include "dplayx_global.h"
 #include "name_server.h"
 #include "dplayx_queue.h"
+#include "dplaysp.h"
+#include "dplay_global.h"
 
 DEFAULT_DEBUG_CHANNEL(dplay)
 
+/* FIXME: Should this be externed? */
+extern HRESULT DPL_CreateCompoundAddress 
+( LPCDPCOMPOUNDADDRESSELEMENT lpElements, DWORD dwElementCount,
+  LPVOID lpAddress, LPDWORD lpdwAddressSize, BOOL bAnsiInterface );
 
-/* FIXME: This stuff shouldn't really be here. It indicates a poor architectural coupling */
-#include "dplobby.h"
-extern HRESULT DPL_CreateCompoundAddress ( LPCDPCOMPOUNDADDRESSELEMENT lpElements, DWORD dwElementCount,
-                                           LPVOID lpAddress, LPDWORD lpdwAddressSize, BOOL bAnsiInterface );
-
-
-/*****************************************************************************
- * Predeclare the interface implementation structures
- */
-typedef struct IDirectPlay2Impl IDirectPlay2AImpl;
-typedef struct IDirectPlay2Impl IDirectPlay2Impl;
-typedef struct IDirectPlay3Impl IDirectPlay3AImpl;
-typedef struct IDirectPlay3Impl IDirectPlay3Impl;
-typedef struct IDirectPlay4Impl IDirectPlay4AImpl;
-typedef struct IDirectPlay4Impl IDirectPlay4Impl;
-
-/*****************************************************************************
- * IDirectPlay implementation structure
- *
- * The philosophy behind this extra pointer dereference is that I wanted to
- * have the same structure for all types of objects without having to do
- * alot of casting. I also only wanted to implement an interface in the
- * object it was "released" with IUnknown interface being implemented in the 1 version.
- * Of course, with these new interfaces comes the data required to keep the state required
- * by these interfaces. So, basically, the pointers contain the data associated with
- * a release. If you use the data associated with release 3 in a release 2 object, you'll
- * get a run time trap, as that won't have any data.
- * 
- */
-typedef struct tagDirectPlayIUnknownData
-{
-  DWORD             ref;
-  CRITICAL_SECTION  DP_lock;
-} DirectPlayIUnknownData;
-
-typedef struct tagEnumSessionAsyncCallbackData
-{
-  LPDPENUMSESSIONSCALLBACK2 cb;
-  LPVOID lpContext;
-  DWORD dwTimeout;
-} EnumSessionAsyncCallbackData;
-
-
-struct PlayerData
-{
-  /* Individual player information */
-  DPID dpid;
-  
-  DPNAME name;
-  HANDLE hEvent;
-  LPVOID lpData;
-  DWORD  dwDataSize;
-};
-typedef struct PlayerData* lpPlayerData;
-
-struct PlayerList
-{
-  DPQ_ENTRY(PlayerList) players;
-
-  lpPlayerData lpPData;
-};
-typedef struct PlayerList* lpPlayerList;
-
-struct GroupData
-{
-  /* Internal information */
-  struct GroupData* parent; /* If parent == NULL it's a top level group */
-
-  DPQ_HEAD(GroupList)  groups;  /* A group has [0..n] groups */
-  DPQ_HEAD(PlayerList) players; /* A group has [0..n] players */
-
-  DPID idGroupOwner; /* ID of player who owns the group */
-
-  /* Individual group information exposed to outside */
-  DPID   dpid;
-  DPNAME name;
-  LPVOID lpData;
-  DWORD  dwDataSize;
-};
-typedef struct GroupData* lpGroupData;
-
-struct GroupList
-{
-  DPQ_ENTRY(GroupList) groups;
-
-  lpGroupData lpGData;
-};
-typedef struct GroupList* lpGroupList;
-
-/* Contains all dp1 and dp2 data members */
-typedef struct tagDirectPlay2Data
-{
-  BOOL   bConnectionOpen;
-
-  HANDLE hEnumSessionThread;
-
-  EnumSessionAsyncCallbackData enumSessionAsyncCallbackData;
-
-  LPVOID lpNameServerData; /* DPlay interface doesn't know contents */
-
-  BOOL bHostInterface; /* Did this interface create the session */
-
-  DPQ_HEAD(PlayerList) players; /* All players w/ interface */
-  DPQ_HEAD(GroupList)  groups;  /* All main groups w/ interface */
-} DirectPlay2Data;
-
-typedef struct tagDirectPlay3Data
-{
-  BOOL bConnectionInitialized;
-} DirectPlay3Data;
-
-typedef struct tagDirectPlay4Data
-{
-  BOOL dummy;
-} DirectPlay4Data;
-
-#define DP_IMPL_FIELDS \
-  DirectPlayIUnknownData*  unk; \
-  DirectPlay2Data*         dp2; \
-  DirectPlay3Data*         dp3; \
-  DirectPlay4Data*         dp4;
-
-struct IDirectPlay2Impl
-{
-  ICOM_VFIELD(IDirectPlay2); 
-  DP_IMPL_FIELDS
-};
-
-struct IDirectPlay3Impl
-{
-  ICOM_VFIELD(IDirectPlay3);
-  DP_IMPL_FIELDS
-};
-
-struct IDirectPlay4Impl
-{
-  ICOM_VFIELD(IDirectPlay4);
-  DP_IMPL_FIELDS
-};
-
-/* Forward declarations of virtual tables */
-static ICOM_VTABLE(IDirectPlay2) directPlay2AVT;
-static ICOM_VTABLE(IDirectPlay3) directPlay3AVT;
-static ICOM_VTABLE(IDirectPlay4) directPlay4AVT;
-
-static ICOM_VTABLE(IDirectPlay2) directPlay2WVT;
-static ICOM_VTABLE(IDirectPlay3) directPlay3WVT;
-static ICOM_VTABLE(IDirectPlay4) directPlay4WVT;
 
 /* Local function prototypes */
 static lpPlayerList DP_FindPlayer( IDirectPlay2AImpl* This, DPID dpid );
 static lpPlayerData DP_CreatePlayer( IDirectPlay2Impl* iface, LPDPID lpid, 
-                                     LPDPNAME lpName, HANDLE hEvent, 
-                                     BOOL bAnsi );
+                                     LPDPNAME lpName, DWORD dwFlags,
+                                     HANDLE hEvent, BOOL bAnsi );
 static BOOL DP_CopyDPNAMEStruct( LPDPNAME lpDst, LPDPNAME lpSrc, BOOL bAnsi );
-static void DP_SetPlayerData( lpPlayerData lpPData, LPVOID lpData, 
-                              DWORD dwDataSize );
+static void DP_SetPlayerData( lpPlayerData lpPData, DWORD dwFlags,
+                              LPVOID lpData, DWORD dwDataSize );
 
-static lpGroupList DP_FindTopGroup( IDirectPlay2AImpl* This, DPID dpid );
 static lpGroupData DP_CreateGroup( IDirectPlay2AImpl* iface, LPDPID lpid,
-                                   LPDPNAME lpName, lpGroupData lpParentData,
-                                   BOOL bAnsi );
-static void DP_SetGroupData( lpGroupData lpGData, LPVOID lpData,
-                             DWORD dwDataSize );
+                                   LPDPNAME lpName, DWORD dwFlags, 
+                                   DPID idParent, BOOL bAnsi );
+static void DP_SetGroupData( lpGroupData lpGData, DWORD dwFlags,
+                             LPVOID lpData, DWORD dwDataSize );
 static void DP_DeleteDPNameStruct( LPDPNAME lpDPName );
 static void DP_DeletePlayer( IDirectPlay2Impl* This, DPID dpid );
 static BOOL cbDeletePlayerFromAllGroups( DPID dpId, DWORD dwPlayerType, 
                                          LPCDPNAME lpName, DWORD dwFlags, 
                                          LPVOID lpContext );
-static lpGroupList DP_FindAnyGroup( IDirectPlay2AImpl* This, DPID dpid );
+static lpGroupData DP_FindAnyGroup( IDirectPlay2AImpl* This, DPID dpid );
 static BOOL cbRemoveGroupOrPlayer( DPID dpId, DWORD dwPlayerType, 
                                    LPCDPNAME lpName, DWORD dwFlags, 
                                    LPVOID lpContext );
@@ -200,15 +56,16 @@
 
 /* Helper methods for player/group interfaces */
 static HRESULT WINAPI DP_IF_DeletePlayerFromGroup
-          ( IDirectPlay2Impl* This, DPID idGroup, DPID idPlayer, BOOL bAnsi );
+          ( IDirectPlay2Impl* This, LPVOID lpMsgHdr, DPID idGroup, 
+            DPID idPlayer, BOOL bAnsi );
 static HRESULT WINAPI DP_IF_CreatePlayer
-          ( IDirectPlay2Impl* This, LPDPID lpidPlayer, LPDPNAME lpPlayerName,
-            HANDLE hEvent, LPVOID lpData, DWORD dwDataSize, 
-            DWORD dwFlags, BOOL bAnsi );
+          ( IDirectPlay2Impl* This, LPVOID lpMsgHdr, LPDPID lpidPlayer, 
+            LPDPNAME lpPlayerName, HANDLE hEvent, LPVOID lpData, 
+            DWORD dwDataSize, DWORD dwFlags, BOOL bAnsi );
 static HRESULT WINAPI DP_IF_DestroyGroup
-          ( IDirectPlay2Impl* This, DPID idGroup, BOOL bAnsi );
+          ( IDirectPlay2Impl* This, LPVOID lpMsgHdr, DPID idGroup, BOOL bAnsi );
 static HRESULT WINAPI DP_IF_DestroyPlayer
-          ( IDirectPlay2Impl* This, DPID idPlayer, BOOL bAnsi );
+          ( IDirectPlay2Impl* This, LPVOID lpMsgHdr, DPID idPlayer, BOOL bAnsi );
 static HRESULT WINAPI DP_IF_EnumGroupPlayers
           ( IDirectPlay2Impl* This, DPID idGroup, LPGUID lpguidInstance, 
             LPDPENUMPLAYERSCALLBACK2 lpEnumPlayersCallback2,
@@ -244,28 +101,88 @@
             DWORD dwFlags, BOOL bAnsi );
 static HRESULT WINAPI DP_IF_AddGroupToGroup
           ( IDirectPlay3Impl* This, DPID idParentGroup, DPID idGroup );
+static HRESULT WINAPI DP_IF_CreateGroup
+          ( IDirectPlay2AImpl* This, LPVOID lpMsgHdr, LPDPID lpidGroup, 
+            LPDPNAME lpGroupName, LPVOID lpData, DWORD dwDataSize, 
+            DWORD dwFlags, BOOL bAnsi );
 static HRESULT WINAPI DP_IF_CreateGroupInGroup
-          ( IDirectPlay3Impl* This, DPID idParentGroup, LPDPID lpidGroup,
-            LPDPNAME lpGroupName, LPVOID lpData, DWORD dwDataSize,
-            DWORD dwFlags );
+          ( IDirectPlay3Impl* This, LPVOID lpMsgHdr, DPID idParentGroup, 
+            LPDPID lpidGroup, LPDPNAME lpGroupName, LPVOID lpData, 
+            DWORD dwDataSize, DWORD dwFlags, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_AddPlayerToGroup
+          ( IDirectPlay2Impl* This, LPVOID lpMsgHdr, DPID idGroup, 
+            DPID idPlayer, BOOL bAnsi );
 static HRESULT WINAPI DP_IF_DeleteGroupFromGroup
           ( IDirectPlay3Impl* This, DPID idParentGroup, DPID idGroup );
-
-
+static HRESULT WINAPI DP_SetSessionDesc
+          ( IDirectPlay2Impl* This, LPCDPSESSIONDESC2 lpSessDesc, 
+            DWORD dwFlags, BOOL bInitial, BOOL bAnsi  );
 static HRESULT WINAPI DP_SecureOpen
           ( IDirectPlay2Impl* This, LPCDPSESSIONDESC2 lpsd, DWORD dwFlags,
-            LPCDPSECURITYDESC lpSecurity, LPCDPCREDENTIALS lpCredentials );
+            LPCDPSECURITYDESC lpSecurity, LPCDPCREDENTIALS lpCredentials,
+            BOOL bAnsi );
+static HRESULT WINAPI DP_SendEx
+          ( IDirectPlay2Impl* This, DPID idFrom, DPID idTo, DWORD dwFlags,
+            LPVOID lpData, DWORD dwDataSize, DWORD dwPriority, DWORD dwTimeout,
+            LPVOID lpContext, LPDWORD lpdwMsgID, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_Receive
+          ( IDirectPlay2Impl* This, LPDPID lpidFrom, LPDPID lpidTo,
+            DWORD dwFlags, LPVOID lpData, LPDWORD lpdwDataSize, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_GetMessageQueue
+          ( IDirectPlay4Impl* This, DPID idFrom, DPID idTo, DWORD dwFlags,
+            LPDWORD lpdwNumMsgs, LPDWORD lpdwNumBytes, BOOL bAnsi );
+static HRESULT WINAPI DP_SP_SendEx
+          ( IDirectPlay2Impl* This, DWORD dwFlags,
+            LPVOID lpData, DWORD dwDataSize, DWORD dwPriority, DWORD dwTimeout,
+            LPVOID lpContext, LPDWORD lpdwMsgID );
+static HRESULT WINAPI DP_IF_SetGroupData
+          ( IDirectPlay2Impl* This, DPID idGroup, LPVOID lpData,
+            DWORD dwDataSize, DWORD dwFlags, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_GetPlayerCaps
+          ( IDirectPlay2Impl* This, DPID idPlayer, LPDPCAPS lpDPCaps,
+            DWORD dwFlags );
+static HRESULT WINAPI DP_IF_Close( IDirectPlay2Impl* This, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_CancelMessage
+          ( IDirectPlay4Impl* This, DWORD dwMsgID, DWORD dwFlags,
+            DWORD dwMinPriority, DWORD dwMaxPriority, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_EnumGroupsInGroup
+          ( IDirectPlay3AImpl* This, DPID idGroup, LPGUID lpguidInstance,
+            LPDPENUMPLAYERSCALLBACK2 lpEnumPlayersCallback2, 
+            LPVOID lpContext, DWORD dwFlags, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_GetGroupParent
+          ( IDirectPlay3AImpl* This, DPID idGroup, LPDPID lpidGroup,
+            BOOL bAnsi );
+
+
+static inline DPID DP_NextObjectId(void);
+
+static void DP_CopySessionDesc( LPDPSESSIONDESC2 destSessionDesc, 
+                                LPCDPSESSIONDESC2 srcSessDesc, BOOL bAnsi );
+
+
+static HMODULE DP_LoadSP( LPCGUID lpcGuid, LPSPINITDATA lpSpData );
 
 
 
 
 
+#define DPID_NOPARENT_GROUP 0 /* Magic number to indicate no parent of group */
+#define DPID_SYSTEM_GROUP DPID_NOPARENT_GROUP /* If system group is supported 
+                                                 we don't have to change much */
+#define DPID_NAME_SERVER 0x19a9d65b  /* Don't ask me why */
+
+/* Strip out dwFlag values which cannot be sent in the CREATEGROUP msg */
+#define DPMSG_CREATEGROUP_DWFLAGS(x) ( (x) & DPGROUP_HIDDEN )
+
+/* Strip out all dwFlags values for CREATEPLAYER msg */
+#define DPMSG_CREATEPLAYER_DWFLAGS(x) 0
+
 static DWORD kludgePlayerGroupId = 1000;
 
 /* ------------------------------------------------------------------ */
 
 
-BOOL DP_CreateIUnknown( LPVOID lpDP )
+static BOOL DP_CreateIUnknown( LPVOID lpDP )
 {
   ICOM_THIS(IDirectPlay2AImpl,lpDP);
 
@@ -278,12 +195,10 @@
 
   InitializeCriticalSection( &This->unk->DP_lock );
 
-  IDirectPlay_AddRef( (LPDIRECTPLAY2A)lpDP );
-
   return TRUE;
 }
 
-BOOL DP_DestroyIUnknown( LPVOID lpDP )
+static BOOL DP_DestroyIUnknown( LPVOID lpDP )
 {
   ICOM_THIS(IDirectPlay2AImpl,lpDP);
 
@@ -293,7 +208,7 @@
   return TRUE;
 }
 
-BOOL DP_CreateDirectPlay2( LPVOID lpDP )
+static BOOL DP_CreateDirectPlay2( LPVOID lpDP )
 {
   ICOM_THIS(IDirectPlay2AImpl,lpDP);
 
@@ -308,46 +223,112 @@
 
   This->dp2->hEnumSessionThread = INVALID_HANDLE_VALUE;
 
-  This->dp2->enumSessionAsyncCallbackData.cb        = NULL; 
-  This->dp2->enumSessionAsyncCallbackData.lpContext = NULL;
-  This->dp2->enumSessionAsyncCallbackData.dwTimeout = INFINITE;
-
   This->dp2->bHostInterface = FALSE;
 
-  DPQ_INIT(This->dp2->players);
-  DPQ_INIT(This->dp2->groups);
+  DPQ_INIT(This->dp2->receiveMsgs);
+  DPQ_INIT(This->dp2->sendMsgs);
 
   if( !NS_InitializeSessionCache( &This->dp2->lpNameServerData ) )
   {
+    /* FIXME: Memory leak */
+    return FALSE;
+  }
+
+  /* Provide an initial session desc with nothing in it */
+  This->dp2->lpSessionDesc = (LPDPSESSIONDESC2)HeapAlloc( GetProcessHeap(), 
+                               HEAP_ZERO_MEMORY, 
+                               sizeof( *This->dp2->lpSessionDesc ) );
+  if( This->dp2->lpSessionDesc == NULL )
+  {
+    /* FIXME: Memory leak */
+    return FALSE;
+  }
+  This->dp2->lpSessionDesc->dwSize = sizeof( *This->dp2->lpSessionDesc );
+
+  /* We are a emulating a dp 6 implementation */
+  This->dp2->spData.dwSPVersion = DPSP_MAJORVERSION;
+
+  This->dp2->spData.lpCB = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                      sizeof( *This->dp2->spData.lpCB ) );
+  This->dp2->spData.lpCB->dwSize = sizeof( *This->dp2->spData.lpCB );
+  This->dp2->spData.lpCB->dwVersion = DPSP_MAJORVERSION;
+  
+  /* This is the pointer to the service provider */
+  if( FAILED( DPSP_CreateInterface( &IID_IDirectPlaySP, 
+                                    (LPVOID*)&This->dp2->spData.lpISP, This ) )
+    )
+  {
+    /* FIXME: Memory leak */
     return FALSE;
   }
 
   return TRUE;
 }
 
-BOOL DP_DestroyDirectPlay2( LPVOID lpDP )
+/* Definition of the global function in dplayx_queue.h. #
+ * FIXME: Would it be better to have a dplayx_queue.c for this function? */
+DPQ_DECL_DELETECB( cbDeleteElemFromHeap, LPVOID )
+{
+  HeapFree( GetProcessHeap(), 0, elem );
+}
+
+/* Function to delete the list of groups with this interface. Needs to
+ * delete the group and player lists associated with this group as well
+ * as the group data associated with this group. It should not delete
+ * player data as that is shared with the top player list and will be
+ * deleted with that.
+ */
+DPQ_DECL_DELETECB( cbDeleteGroupsElem, lpGroupList );
+DPQ_DECL_DELETECB( cbDeleteGroupsElem, lpGroupList )
+{
+  DPQ_DELETEQ( elem->lpGData->groups, groups, 
+               lpGroupList, cbDeleteElemFromHeap ); 
+  DPQ_DELETEQ( elem->lpGData->players, players,
+               lpPlayerList, cbDeleteElemFromHeap );
+  HeapFree( GetProcessHeap(), 0, elem->lpGData );
+  HeapFree( GetProcessHeap(), 0, elem );
+}
+
+/* Function to delete the list of players with this interface. Needs to
+ * delete the player data for all players as well.
+ */
+DPQ_DECL_DELETECB( cbDeletePlayerElem, lpPlayerList );
+DPQ_DECL_DELETECB( cbDeletePlayerElem, lpPlayerList )
+{
+  HeapFree( GetProcessHeap(), 0, elem->lpPData );
+  HeapFree( GetProcessHeap(), 0, elem );
+}
+
+static BOOL DP_DestroyDirectPlay2( LPVOID lpDP )
 {
   ICOM_THIS(IDirectPlay2AImpl,lpDP);
 
-  FIXME( ": memory leak\n" );
-
   if( This->dp2->hEnumSessionThread != INVALID_HANDLE_VALUE )
   {
     TerminateThread( This->dp2->hEnumSessionThread, 0 );
     CloseHandle( This->dp2->hEnumSessionThread );
   }
 
-  /* Delete the player and group lists */
+#if 0
+  DPQ_DELETEQ( This->dp2->players, players, lpPlayerList, cbDeletePlayerElem );
+  DPQ_DELETEQ( This->dp2->groups, groups, lpGroupList, cbDeleteGroupsElem );
+#endif
+
+  /* FIXME: Need to delete receive and send msgs queue contents */
 
   NS_DeleteSessionCache( This->dp2->lpNameServerData );
-  
+
+  HeapFree( GetProcessHeap(), 0, This->dp2->lpSessionDesc );
+
+  IDirectPlaySP_Release( This->dp2->spData.lpISP );
+
   /* Delete the contents */
   HeapFree( GetProcessHeap(), 0, This->dp2 );
 
   return TRUE;
 }
 
-BOOL DP_CreateDirectPlay3( LPVOID lpDP )
+static BOOL DP_CreateDirectPlay3( LPVOID lpDP )
 {
   ICOM_THIS(IDirectPlay3AImpl,lpDP);
 
@@ -358,12 +339,10 @@
     return FALSE;
   }
 
-  This->dp3->bConnectionInitialized = FALSE;
-
   return TRUE;
 }
 
-BOOL DP_DestroyDirectPlay3( LPVOID lpDP )
+static BOOL DP_DestroyDirectPlay3( LPVOID lpDP )
 {
   ICOM_THIS(IDirectPlay3AImpl,lpDP);
 
@@ -373,7 +352,7 @@
   return TRUE;
 }
 
-BOOL DP_CreateDirectPlay4( LPVOID lpDP )
+static BOOL DP_CreateDirectPlay4( LPVOID lpDP )
 {
   ICOM_THIS(IDirectPlay4AImpl,lpDP);
 
@@ -387,7 +366,7 @@
   return TRUE;
 }
 
-BOOL DP_DestroyDirectPlay4( LPVOID lpDP )
+static BOOL DP_DestroyDirectPlay4( LPVOID lpDP )
 {
   ICOM_THIS(IDirectPlay3AImpl,lpDP);
 
@@ -398,360 +377,217 @@
 }
 
 
-/* Get a new interface. To be used by QueryInterface. */ 
+/* Create a new interface */ 
 extern 
-HRESULT directPlay_QueryInterface 
+HRESULT DP_CreateInterface 
          ( REFIID riid, LPVOID* ppvObj )
 {
+  TRACE( " for %s\n", debugstr_guid( riid ) );
+
+  *ppvObj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                       sizeof( IDirectPlay2Impl ) );
+
+  if( *ppvObj == NULL )
+  {
+    return DPERR_OUTOFMEMORY;
+  }
 
   if( IsEqualGUID( &IID_IDirectPlay2, riid ) )
   {
-    *ppvObj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
-                         sizeof( IDirectPlay2Impl ) );
-
-    if( *ppvObj == NULL )
-    {
-       return DPERR_OUTOFMEMORY;
-    }
-
-    /* new scope for variable declaration */
-    {
-      ICOM_THIS(IDirectPlay2Impl,*ppvObj);
-
-      ICOM_VTBL(This) = &directPlay2WVT;
-
-      if ( DP_CreateIUnknown( (LPVOID)This ) &&
-           DP_CreateDirectPlay2( (LPVOID)This )
-         )
-      {
-        return S_OK;
-      }
-
-    }
-
-    goto error; 
+    ICOM_THIS(IDirectPlay2Impl,*ppvObj);
+    ICOM_VTBL(This) = &directPlay2WVT;
   } 
   else if( IsEqualGUID( &IID_IDirectPlay2A, riid ) )
   {
-    *ppvObj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
-                         sizeof( IDirectPlay2AImpl ) );
-
-    if( *ppvObj == NULL )
-    {
-       return DPERR_OUTOFMEMORY;
-    }
-
-    /* new scope for variable declaration */
-    {
-      ICOM_THIS(IDirectPlay2AImpl,*ppvObj);
-
-      ICOM_VTBL(This) = &directPlay2AVT;
-
-      if ( DP_CreateIUnknown( (LPVOID)This ) &&
-           DP_CreateDirectPlay2( (LPVOID)This )
-         )
-      {
-        return S_OK;
-      }
-
-    }
-
-    goto error;
+    ICOM_THIS(IDirectPlay2AImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlay2AVT;
   }
   else if( IsEqualGUID( &IID_IDirectPlay3, riid ) )
   {
-    *ppvObj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
-                         sizeof( IDirectPlay3Impl ) );
-
-    if( *ppvObj == NULL )
-    {
-       return DPERR_OUTOFMEMORY;
-    }
-
-    /* new scope for variable declaration */
-    {
-      ICOM_THIS(IDirectPlay3Impl,*ppvObj);
-
-      ICOM_VTBL(This) = &directPlay3WVT;
-
-      if ( DP_CreateIUnknown( (LPVOID)This ) &&
-           DP_CreateDirectPlay2( (LPVOID)This ) &&
-           DP_CreateDirectPlay3( (LPVOID)This )
-         )
-      {
-        return S_OK;
-      }
-
-    }
-
-    goto error;
+    ICOM_THIS(IDirectPlay3Impl,*ppvObj);
+    ICOM_VTBL(This) = &directPlay3WVT;
   }
   else if( IsEqualGUID( &IID_IDirectPlay3A, riid ) )
   {
-    *ppvObj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
-                         sizeof( IDirectPlay3AImpl ) );
-
-    if( *ppvObj == NULL )
-    {
-       return DPERR_OUTOFMEMORY;
-    }
-
-    /* new scope for variable declaration */
-    {
-      ICOM_THIS(IDirectPlay3AImpl,*ppvObj);
-
-      ICOM_VTBL(This) = &directPlay3AVT;
-
-      if ( DP_CreateIUnknown( (LPVOID)This ) &&
-           DP_CreateDirectPlay2( (LPVOID)This ) &&
-           DP_CreateDirectPlay3( (LPVOID)This )
-         )
-      {
-        return S_OK;
-      }
-
-    }
-
-    goto error;
+    ICOM_THIS(IDirectPlay3AImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlay3AVT;
   }
   else if( IsEqualGUID( &IID_IDirectPlay4, riid ) )
   {
-    *ppvObj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
-                         sizeof( IDirectPlay4Impl ) );
-
-    if( *ppvObj == NULL )
-    {
-       return DPERR_OUTOFMEMORY;
-    }
-
-    /* new scope for variable declaration */
-    {
-      ICOM_THIS(IDirectPlay4Impl,*ppvObj);
-
-      ICOM_VTBL(This) = &directPlay4WVT;
-
-      if ( DP_CreateIUnknown( (LPVOID)This ) &&
-           DP_CreateDirectPlay2( (LPVOID)This ) &&
-           DP_CreateDirectPlay3( (LPVOID)This ) &&
-           DP_CreateDirectPlay4( (LPVOID)This )
-         )
-      {
-        return S_OK;
-      }
-
-    }
-
-    goto error;
+    ICOM_THIS(IDirectPlay4Impl,*ppvObj);
+    ICOM_VTBL(This) = &directPlay4WVT;
   }
   else if( IsEqualGUID( &IID_IDirectPlay4A, riid ) )
   {
-    *ppvObj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
-                         sizeof( IDirectPlay4AImpl ) );
+    ICOM_THIS(IDirectPlay4AImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlay4AVT;
+  }
+  else
+  {
+    /* Unsupported interface */
+    HeapFree( GetProcessHeap(), 0, *ppvObj );
+    *ppvObj = NULL;
 
-    if( *ppvObj == NULL )
-    {
-       return DPERR_OUTOFMEMORY;
-    }
-
-    /* new scope for variable declaration */
-    {
-      ICOM_THIS(IDirectPlay4AImpl,*ppvObj);
-
-      ICOM_VTBL(This) = &directPlay4AVT;
-
-      if ( DP_CreateIUnknown( (LPVOID)This ) &&
-           DP_CreateDirectPlay2( (LPVOID)This ) &&
-           DP_CreateDirectPlay3( (LPVOID)This ) &&
-           DP_CreateDirectPlay4( (LPVOID)This )
-         )
-      {
-        return S_OK;
-      }
-
-    }
-
-    goto error;
+    return E_NOINTERFACE;
   }
 
-  /* Unsupported interface */
+  /* Initialize it */
+  if ( DP_CreateIUnknown( *ppvObj ) &&
+       DP_CreateDirectPlay2( *ppvObj ) &&
+       DP_CreateDirectPlay3( *ppvObj ) &&
+       DP_CreateDirectPlay4( *ppvObj )
+     )
+  {
+    IDirectPlayX_AddRef( (LPDIRECTPLAY2A)*ppvObj );
+
+    return S_OK;
+  }
+
+  /* Initialize failed, destroy it */
+  DP_DestroyDirectPlay4( *ppvObj );
+  DP_DestroyDirectPlay3( *ppvObj );
+  DP_DestroyDirectPlay2( *ppvObj );
+  DP_DestroyIUnknown( *ppvObj );
+
+  HeapFree( GetProcessHeap(), 0, *ppvObj );
+
   *ppvObj = NULL;
-  return E_NOINTERFACE;
-
-error:
-
-    DP_DestroyDirectPlay4( *ppvObj );
-    DP_DestroyDirectPlay3( *ppvObj );
-    DP_DestroyDirectPlay2( *ppvObj );
-    DP_DestroyIUnknown( *ppvObj );
-    HeapFree( GetProcessHeap(), 0, *ppvObj );
-
-    *ppvObj = NULL;
-    return DPERR_NOMEMORY;
-
+  return DPERR_NOMEMORY;
 }
 
 
 /* Direct Play methods */
-static HRESULT WINAPI DirectPlay2W_QueryInterface
+
+/* Shared between all dplay types */
+static HRESULT WINAPI DP_QueryInterface
          ( LPDIRECTPLAY2 iface, REFIID riid, LPVOID* ppvObj )
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
-  TRACE("(%p)->(%p,%p)\n", This, riid, ppvObj );
+  TRACE("(%p)->(%s,%p)\n", This, debugstr_guid( riid ), ppvObj );
 
-  if( IsEqualGUID( &IID_IUnknown, riid ) ||
-      IsEqualGUID( &IID_IDirectPlay2, riid )
-    )
+  *ppvObj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                       sizeof( IDirectPlay2Impl ) );
+
+  if( *ppvObj == NULL )
   {
-    IDirectPlayX_AddRef( iface );
-    *ppvObj = This;
-    return S_OK;
-  }
-  return directPlay_QueryInterface( riid, ppvObj );
-}
-
-static HRESULT WINAPI DirectPlay2A_QueryInterface
-         ( LPDIRECTPLAY2A iface, REFIID riid, LPVOID* ppvObj )
-{
-  ICOM_THIS(IDirectPlay2Impl,iface);
-  TRACE("(%p)->(%p,%p)\n", This, riid, ppvObj );
-
-  if( IsEqualGUID( &IID_IUnknown, riid ) ||
-      IsEqualGUID( &IID_IDirectPlay2A, riid )
-    )
-  {
-    IDirectPlayX_AddRef( iface );
-    *ppvObj = This;
-    return S_OK;
+    return DPERR_OUTOFMEMORY;
   }
 
-  return directPlay_QueryInterface( riid, ppvObj );
-}
+  CopyMemory( *ppvObj, iface, sizeof( IDirectPlay2Impl )  );
+  (*(IDirectPlay2Impl**)ppvObj)->ulInterfaceRef = 0;
 
-static HRESULT WINAPI DirectPlay3WImpl_QueryInterface
-         ( LPDIRECTPLAY3 iface, REFIID riid, LPVOID* ppvObj )
-{
-  ICOM_THIS(IDirectPlay3Impl,iface);
-  TRACE("(%p)->(%p,%p)\n", This, riid, ppvObj );
-
-  if( IsEqualGUID( &IID_IUnknown, riid ) ||
-      IsEqualGUID( &IID_IDirectPlay3, riid )
-    )
+  if( IsEqualGUID( &IID_IDirectPlay2, riid ) )
   {
-    IDirectPlayX_AddRef( iface );
-    *ppvObj = This;
-    return S_OK;
+    ICOM_THIS(IDirectPlay2Impl,*ppvObj);
+    ICOM_VTBL(This) = &directPlay2WVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlay2A, riid ) )
+  {
+    ICOM_THIS(IDirectPlay2AImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlay2AVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlay3, riid ) )
+  {
+    ICOM_THIS(IDirectPlay3Impl,*ppvObj);
+    ICOM_VTBL(This) = &directPlay3WVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlay3A, riid ) )
+  {
+    ICOM_THIS(IDirectPlay3AImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlay3AVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlay4, riid ) )
+  {
+    ICOM_THIS(IDirectPlay4Impl,*ppvObj);
+    ICOM_VTBL(This) = &directPlay4WVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlay4A, riid ) )
+  {
+    ICOM_THIS(IDirectPlay4AImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlay4AVT;
+  }
+  else
+  {
+    /* Unsupported interface */
+    HeapFree( GetProcessHeap(), 0, *ppvObj );
+    *ppvObj = NULL;
+
+    return E_NOINTERFACE;
   }
 
-  return directPlay_QueryInterface( riid, ppvObj );
+  IDirectPlayX_AddRef( (LPDIRECTPLAY2)*ppvObj );
+
+  return S_OK;
 }
 
-static HRESULT WINAPI DirectPlay3AImpl_QueryInterface
-         ( LPDIRECTPLAY3A iface, REFIID riid, LPVOID* ppvObj )
-{
-  ICOM_THIS(IDirectPlay3Impl,iface);
-  TRACE("(%p)->(%p,%p)\n", This, riid, ppvObj );
-
-  if( IsEqualGUID( &IID_IUnknown, riid ) ||
-      IsEqualGUID( &IID_IDirectPlay3A, riid )
-    )
-  {
-    IDirectPlayX_AddRef( iface );
-    *ppvObj = This;
-    return S_OK;
-  }
-
-  return directPlay_QueryInterface( riid, ppvObj );
-}
-
-static HRESULT WINAPI DirectPlay4WImpl_QueryInterface
-         ( LPDIRECTPLAY4 iface, REFIID riid, LPVOID* ppvObj )
-{
-  ICOM_THIS(IDirectPlay4Impl,iface);
-  TRACE("(%p)->(%p,%p)\n", This, riid, ppvObj );
-
-  if( IsEqualGUID( &IID_IUnknown, riid ) ||
-      IsEqualGUID( &IID_IDirectPlay4, riid )
-    )
-  {
-    IDirectPlayX_AddRef( iface );
-    *ppvObj = This;
-    return S_OK;
-  }
-
-  return directPlay_QueryInterface( riid, ppvObj );
-}
-
-
-static HRESULT WINAPI DirectPlay4AImpl_QueryInterface
-         ( LPDIRECTPLAY4A iface, REFIID riid, LPVOID* ppvObj )
-{
-  ICOM_THIS(IDirectPlay4Impl,iface);
-  TRACE("(%p)->(%p,%p)\n", This, riid, ppvObj );
-
-  if( IsEqualGUID( &IID_IUnknown, riid ) ||
-      IsEqualGUID( &IID_IDirectPlay4A, riid )
-    )
-  {
-    IDirectPlayX_AddRef( iface );
-    *ppvObj = This;
-    return S_OK;
-  }
-
-  return directPlay_QueryInterface( riid, ppvObj );
-}
-
-
 /* Shared between all dplay types */
-static ULONG WINAPI DirectPlay2AImpl_AddRef
+static ULONG WINAPI DP_AddRef
          ( LPDIRECTPLAY3 iface )
 {
-  ULONG refCount;
+  ULONG ulInterfaceRefCount, ulObjRefCount;
   ICOM_THIS(IDirectPlay3Impl,iface);
 
-  refCount = InterlockedIncrement( &This->unk->ref );
+  ulObjRefCount       = InterlockedIncrement( &This->unk->ulObjRef );
+  ulInterfaceRefCount = InterlockedIncrement( &This->ulInterfaceRef );
 
-  TRACE("ref count incremented to %lu for %p\n", refCount, This );
+  TRACE( "ref count incremented to %lu:%lu for %p\n", 
+         ulInterfaceRefCount, ulObjRefCount, This );
 
-  return refCount;
+  return ulObjRefCount;
 }
 
-static ULONG WINAPI DirectPlay2AImpl_Release
+static ULONG WINAPI DP_Release
 ( LPDIRECTPLAY3 iface )
 {
-  ULONG refCount;
+  ULONG ulInterfaceRefCount, ulObjRefCount;
 
   ICOM_THIS(IDirectPlay3Impl,iface);
 
-  refCount = InterlockedDecrement( &This->unk->ref );
+  ulObjRefCount       = InterlockedDecrement( &This->unk->ulObjRef );
+  ulInterfaceRefCount = InterlockedDecrement( &This->ulInterfaceRef );
 
-  TRACE("ref count decremented to %lu for %p\n", refCount, This );
+  TRACE( "ref count decremented to %lu:%lu for %p\n",
+         ulInterfaceRefCount, ulObjRefCount, This );
 
   /* Deallocate if this is the last reference to the object */
-  if( refCount == 0 )
+  if( ulObjRefCount == 0 )
   {
+     /* If we're destroying the object, this must be the last ref
+        of the last interface */
      DP_DestroyDirectPlay4( This );
      DP_DestroyDirectPlay3( This );
      DP_DestroyDirectPlay2( This );
      DP_DestroyIUnknown( This );
-     HeapFree( GetProcessHeap(), 0, This );
+  }
+  
+  /* Deallocate the interface */
+  if( ulInterfaceRefCount == 0 )
+  {
+    HeapFree( GetProcessHeap(), 0, This );
   }
 
-  return refCount;
+  return ulObjRefCount;
 }
 
-static HRESULT WINAPI DirectPlay2AImpl_AddPlayerToGroup
-          ( LPDIRECTPLAY2A iface, DPID idGroup, DPID idPlayer )
+static inline DPID DP_NextObjectId(void)
 {
-  lpGroupList  lpGList;
+  return (DPID)InterlockedIncrement( &kludgePlayerGroupId );
+}
+
+
+static HRESULT WINAPI DP_IF_AddPlayerToGroup
+          ( IDirectPlay2Impl* This, LPVOID lpMsgHdr, DPID idGroup, 
+            DPID idPlayer, BOOL bAnsi )
+{
+  lpGroupData  lpGData;
   lpPlayerList lpPList;
   lpPlayerList lpNewPList;
 
-  ICOM_THIS(IDirectPlay2AImpl,iface);
-
-  TRACE("(%p)->(0x%08lx,0x%08lx)\n", This, idGroup, idPlayer );
+  TRACE( "(%p)->(%p,0x%08lx,0x%08lx,%u)\n", 
+         This, lpMsgHdr, idGroup, idPlayer, bAnsi );
 
   /* Find the group */
-  if( ( lpGList = DP_FindAnyGroup( This, idGroup ) ) == NULL )
+  if( ( lpGData = DP_FindAnyGroup( This, idGroup ) ) == NULL )
   {
     return DPERR_INVALIDGROUP;
   }
@@ -774,86 +610,159 @@
   lpNewPList->lpPData = lpPList->lpPData;
 
   /* Add the player to the list of players for this group */
-  DPQ_INSERT(lpGList->lpGData->players,lpNewPList,players);
+  DPQ_INSERT(lpGData->players,lpNewPList,players);
 
-  /* Send a ADDPLAYERTOGROUP message */
-  FIXME( "Not sending message\n" );
+  /* Let the SP know that we've added a player to the group */
+  if( This->dp2->spData.lpCB->AddPlayerToGroup )
+  {
+    DPSP_ADDPLAYERTOGROUPDATA data;
+   
+    TRACE( "Calling SP AddPlayerToGroup\n" );
+
+    data.idPlayer = idPlayer;
+    data.idGroup  = idGroup;
+    data.lpISP    = This->dp2->spData.lpISP;
+
+    (This->dp2->spData.lpCB->AddPlayerToGroup)( &data );
+  }
+
+  /* Inform all other peers of the addition of player to the group. If there are
+   * no peers keep this event quiet.
+   * Also, if this event was the result of another machine sending it to us,
+   * don't bother rebroadcasting it.
+   */
+  if( ( lpMsgHdr == NULL ) &&
+      This->dp2->lpSessionDesc &&
+      ( This->dp2->lpSessionDesc->dwFlags & DPSESSION_MULTICASTSERVER ) )
+  {
+    DPMSG_ADDPLAYERTOGROUP msg;
+    msg.dwType = DPSYS_ADDPLAYERTOGROUP; 
+
+    msg.dpIdGroup  = idGroup;
+    msg.dpIdPlayer = idPlayer;
+
+    /* FIXME: Correct to just use send effectively? */
+    /* FIXME: Should size include data w/ message or just message "header" */
+    /* FIXME: Check return code */
+    DP_SendEx( This, DPID_SERVERPLAYER, DPID_ALLPLAYERS, 0, &msg, sizeof( msg ),               0, 0, NULL, NULL, bAnsi );
+  }
 
   return DP_OK;
 }
 
+static HRESULT WINAPI DirectPlay2AImpl_AddPlayerToGroup
+          ( LPDIRECTPLAY2A iface, DPID idGroup, DPID idPlayer )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_AddPlayerToGroup( This, NULL, idGroup, idPlayer, TRUE );
+}
+
 static HRESULT WINAPI DirectPlay2WImpl_AddPlayerToGroup
           ( LPDIRECTPLAY2 iface, DPID idGroup, DPID idPlayer )
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
-  FIXME("(%p)->(0x%08lx,0x%08lx): stub\n", This, idGroup, idPlayer );
-  return DP_OK;
+  return DP_IF_AddPlayerToGroup( This, NULL, idGroup, idPlayer, FALSE );
 }
 
+static HRESULT WINAPI DP_IF_Close( IDirectPlay2Impl* This, BOOL bAnsi )
+{
+  HRESULT hr = DP_OK;
+
+  FIXME("(%p)->(%u): stub\n", This, bAnsi );
+
+  /* FIXME: Need to find a new host I assume (how?) */
+  /* FIXME: Need to destroy all local groups */
+  /* FIXME: Need to migrate all remotely visible players to the new host */
+
+  /* Invoke the SP callback to inform of session close */
+  if( This->dp2->spData.lpCB->CloseEx )
+  {
+    DPSP_CLOSEDATA data;
+
+    TRACE( "Calling SP CloseEx\n" );
+
+    data.lpISP = This->dp2->spData.lpISP;
+ 
+    hr = (This->dp2->spData.lpCB->CloseEx)( &data );
+        
+  }
+  else if ( This->dp2->spData.lpCB->Close ) /* Try obsolete version */
+  {
+    TRACE( "Calling SP Close (obsolete interface)\n" );
+
+    hr = (This->dp2->spData.lpCB->Close)();
+  }
+
+  /* Even if the SP close failed, we should press on... */
+
+  /* Invoke the SP callback to inform the SP we don't need it any more */
+  if( This->dp2->spData.lpCB->ShutdownEx )
+  {
+    DPSP_SHUTDOWNDATA data;
+
+    TRACE( "Calling SP ShutdownEx\n" );
+
+    data.lpISP = This->dp2->spData.lpISP;
+
+    hr = (This->dp2->spData.lpCB->ShutdownEx)( &data );
+  }
+  else if (This->dp2->spData.lpCB->Shutdown ) /* obsolete interface */
+  {
+    TRACE( "Calling obsolete SP Shutdown\n" );
+    hr = (This->dp2->spData.lpCB->Shutdown)();
+  }
+
+  /* Unload the service provider now that we've stoped it */
+  FreeLibrary( This->dp2->hServiceProvider );
+
+  return hr;
+}
 
 static HRESULT WINAPI DirectPlay2AImpl_Close
           ( LPDIRECTPLAY2A iface )
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
-  FIXME("(%p)->(): stub\n", This );
-  return DP_OK;
+  return DP_IF_Close( This, TRUE );
 }
 
 static HRESULT WINAPI DirectPlay2WImpl_Close
           ( LPDIRECTPLAY2 iface )
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
-  FIXME("(%p)->(): stub\n", This );
-  return DP_OK;
+  return DP_IF_Close( This, FALSE );
 }
 
 static
 lpGroupData DP_CreateGroup( IDirectPlay2AImpl* This, LPDPID lpid,
-                            LPDPNAME lpName, lpGroupData lpParentData,
-                            BOOL bAnsi )
+                            LPDPNAME lpName, DWORD dwFlags, 
+                            DPID idParent, BOOL bAnsi )
 {
-  lpGroupList lpGroup;
-
-  TRACE( "(%p)->(%p,%p,%u)\n", This, lpid, lpName, bAnsi );
+  lpGroupData lpGData;
 
   /* Allocate the new space and add to end of high level group list */
-  lpGroup = (lpGroupList) HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
-                                     sizeof( *lpGroup ) );
+  lpGData = (lpGroupData) HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                     sizeof( *lpGData ) );
 
-  if( lpGroup == NULL )
+  if( lpGData == NULL )
   {
     return NULL;
   }
 
-  /* Allocate storage for the group and associate it with the list element */
-  lpGroup->lpGData = (lpGroupData) HeapAlloc( GetProcessHeap(),
-                                              HEAP_ZERO_MEMORY,
-                                              sizeof(*(lpGroup->lpGData)) );
+  DPQ_INIT(lpGData->groups);
+  DPQ_INIT(lpGData->players);
 
-  if( lpGroup->lpGData == NULL )
-  {
-    /* FIXME: Memory leak */
-    return NULL;
-  }
+  /* Set the desired player ID - no sanity checking to see if it exists */
+  lpGData->dpid = *lpid;
 
-  DPQ_INSERT(This->dp2->groups,lpGroup,groups);
-
-  if( *lpid == DPID_UNKNOWN )
-  {
-    /* Assign the next available player ID - FIXME crap solution */
-    lpGroup->lpGData->dpid = kludgePlayerGroupId++;
-  }
-  else
-  {
-    /* Set the desired player ID - no sanity checking to see if it exists */
-    lpGroup->lpGData->dpid = *lpid;
-  }
-
-  DP_CopyDPNAMEStruct( &lpGroup->lpGData->name, lpName, bAnsi );
+  DP_CopyDPNAMEStruct( &lpGData->name, lpName, bAnsi );
  
-  lpGroup->lpGData->parent = lpParentData;
+  /* FIXME: Should we check that the parent exists? */
+  lpGData->parent  = idParent;
 
-  return lpGroup->lpGData;
+  /* FIXME: Should we validate the dwFlags? */
+  lpGData->dwFlags = dwFlags;
+
+  return lpGData;
 }
 
 /* This method assumes that all links to it are already deleted */
@@ -864,7 +773,7 @@
 
   TRACE( "(%p)->(0x%08lx)\n", This, dpid );
 
-  DPQ_REMOVE_ENTRY( This->dp2->groups, groups, lpGData->dpid, dpid, lpGList );
+  DPQ_REMOVE_ENTRY( This->dp2->lpSysGroup->groups, groups, lpGData->dpid, ==, dpid, lpGList );
 
   if( lpGList == NULL )
   {
@@ -881,99 +790,205 @@
  
 }
 
-/* This function only finds top level groups */
-static lpGroupList DP_FindTopGroup( IDirectPlay2AImpl* This, DPID dpid )
+static lpGroupData DP_FindAnyGroup( IDirectPlay2AImpl* This, DPID dpid )
 {
   lpGroupList lpGroups;
 
   TRACE( "(%p)->(0x%08lx)\n", This, dpid );
 
-  /* Does the group exist? */
-  if( ( lpGroups = DP_FindAnyGroup( This, dpid ) ) == NULL )  
+  if( dpid == DPID_SYSTEM_GROUP )
   {
-    return NULL;
-  }
-
-  /* Is this group a top level group? */
-  if( lpGroups->lpGData->parent )
-  {
-    return lpGroups;
+    return This->dp2->lpSysGroup;
   }
   else
   {
-    return NULL;
+    DPQ_FIND_ENTRY( This->dp2->lpSysGroup->groups, groups, lpGData->dpid, ==, dpid, lpGroups );
   }
+
+  return lpGroups->lpGData;
 }
 
-static lpGroupList DP_FindAnyGroup( IDirectPlay2AImpl* This, DPID dpid )
-{
-  lpGroupList lpGroups;
-
-  TRACE( "(%p)->(0x%08lx)\n", This, dpid );
-
-  DPQ_FIND_ENTRY( This->dp2->groups, groups, lpGData->dpid, dpid, lpGroups );
-
-  return lpGroups;
-}
-
-static HRESULT WINAPI DirectPlay2AImpl_CreateGroup
-          ( LPDIRECTPLAY2A iface, LPDPID lpidGroup, LPDPNAME lpGroupName, LPVOID lpData, DWORD dwDataSize, DWORD dwFlags )
+static HRESULT WINAPI DP_IF_CreateGroup
+          ( IDirectPlay2AImpl* This, LPVOID lpMsgHdr, LPDPID lpidGroup, 
+            LPDPNAME lpGroupName, LPVOID lpData, DWORD dwDataSize, 
+            DWORD dwFlags, BOOL bAnsi )
 {
   lpGroupData lpGData;
 
-  ICOM_THIS(IDirectPlay2Impl,iface);
+  TRACE( "(%p)->(%p,%p,%p,%p,0x%08lx,0x%08lx,%u)\n", 
+         This, lpMsgHdr, lpidGroup, lpGroupName, lpData, dwDataSize, 
+         dwFlags, bAnsi );
 
-  FIXME("(%p)->(%p,%p,%p,0x%08lx,0x%08lx): stub\n", This, lpidGroup, lpGroupName, lpData, dwDataSize, dwFlags );
+  /* If the name is not specified, we must provide one */
+  if( DPID_UNKNOWN == *lpidGroup )
+  {
+    /* If we are the name server, we decide on the group ids. If not, we
+     * must ask for one before attempting a creation.
+     */
+    if( This->dp2->bHostInterface )
+    {
+      *lpidGroup = DP_NextObjectId();
+    }
+    else
+    {
+      /* Request the id from the name server */
+      FIXME( "Request id from NS for group\n" );
 
-  lpGData = DP_CreateGroup( This, lpidGroup, lpGroupName, 
-                            NULL /* Top level group */, TRUE /* Ansi */ );
+      /* Hack for now */
+      *lpidGroup = DP_NextObjectId();
+    }
+  }
+ 
+  lpGData = DP_CreateGroup( This, lpidGroup, lpGroupName, dwFlags,
+                            DPID_NOPARENT_GROUP, bAnsi );
 
   if( lpGData == NULL )
   {
     return DPERR_CANTADDPLAYER; /* yes player not group */
   }
 
-  DP_SetGroupData( lpGData, lpData, dwDataSize );
+  if( DPID_SYSTEM_GROUP == *lpidGroup )
+  {
+    This->dp2->lpSysGroup = lpGData; 
+  }
+  else
+  {
+    /* Insert into the system group */
+    lpGroupList lpGroup = (lpGroupList) HeapAlloc( GetProcessHeap(), 
+                                                   HEAP_ZERO_MEMORY,
+                                                   sizeof( *lpGroup ) );
+    lpGroup->lpGData = lpGData;
 
-  /* FIXME: Should send DPMSG_CREATEPLAYERORGROUP message to everyone,
-            local and remote, that belongs to this session. This will not
-            be done by calling SetPlayerData */
-  FIXME( "Should broadcast group creation to everything in session\n" );
+    DPQ_INSERT( This->dp2->lpSysGroup->groups, lpGroup, groups );
+  }
+
+  /* Set all the important stuff for the group */
+  DP_SetGroupData( lpGData, DPSET_REMOTE, lpData, dwDataSize );
+
+  /* FIXME: We should only create the system group if GetCaps returns
+   *        DPCAPS_GROUPOPTIMIZED.
+   */
+
+  /* Let the SP know that we've created this group */
+  if( This->dp2->spData.lpCB->CreateGroup )
+  {
+    DPSP_CREATEGROUPDATA data;
+    DWORD dwCreateFlags = 0;
+
+    TRACE( "Calling SP CreateGroup\n" );
+
+    if( *lpidGroup == DPID_NOPARENT_GROUP )
+      dwCreateFlags |= DPLAYI_GROUP_SYSGROUP;
+
+    if( lpMsgHdr == NULL )
+      dwCreateFlags |= DPLAYI_PLAYER_PLAYERLOCAL;
+
+    if( dwFlags & DPGROUP_HIDDEN )
+      dwCreateFlags |= DPLAYI_GROUP_HIDDEN;
+
+    data.idGroup           = *lpidGroup;
+    data.dwFlags           = dwCreateFlags;
+    data.lpSPMessageHeader = lpMsgHdr;
+    data.lpISP             = This->dp2->spData.lpISP;
+
+    (This->dp2->spData.lpCB->CreateGroup)( &data );
+  }
+
+  /* Inform all other peers of the creation of a new group. If there are 
+   * no peers keep this event quiet. 
+   * Also if this message was sent to us, don't rebroadcast.
+   */
+  if( ( lpMsgHdr == NULL ) &&
+      This->dp2->lpSessionDesc && 
+      ( This->dp2->lpSessionDesc->dwFlags & DPSESSION_MULTICASTSERVER ) )
+  {
+    DPMSG_CREATEPLAYERORGROUP msg;
+    msg.dwType = DPSYS_CREATEPLAYERORGROUP;
+
+    msg.dwPlayerType     = DPPLAYERTYPE_GROUP;
+    msg.dpId             = *lpidGroup;
+    msg.dwCurrentPlayers = 0; /* FIXME: Incorrect? */
+    msg.lpData           = lpData;
+    msg.dwDataSize       = dwDataSize;
+    msg.dpnName          = *lpGroupName;
+    msg.dpIdParent       = DPID_NOPARENT_GROUP;
+    msg.dwFlags          = DPMSG_CREATEGROUP_DWFLAGS( dwFlags );
+
+    /* FIXME: Correct to just use send effectively? */
+    /* FIXME: Should size include data w/ message or just message "header" */
+    /* FIXME: Check return code */
+    DP_SendEx( This, DPID_SERVERPLAYER, DPID_ALLPLAYERS, 0, &msg, sizeof( msg ),
+               0, 0, NULL, NULL, bAnsi );
+  }
 
   return DP_OK;
 }
 
+static HRESULT WINAPI DirectPlay2AImpl_CreateGroup
+          ( LPDIRECTPLAY2A iface, LPDPID lpidGroup, LPDPNAME lpGroupName,
+            LPVOID lpData, DWORD dwDataSize, DWORD dwFlags )
+{
+  *lpidGroup = DPID_UNKNOWN;
+
+  return DP_IF_CreateGroup( (IDirectPlay2AImpl*)iface, NULL, lpidGroup, 
+                            lpGroupName, lpData, dwDataSize, dwFlags, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_CreateGroup
+          ( LPDIRECTPLAY2 iface, LPDPID lpidGroup, LPDPNAME lpGroupName, 
+            LPVOID lpData, DWORD dwDataSize, DWORD dwFlags )
+{
+  *lpidGroup = DPID_UNKNOWN;
+
+  return DP_IF_CreateGroup( (IDirectPlay2AImpl*)iface, NULL, lpidGroup, 
+                            lpGroupName, lpData, dwDataSize, dwFlags, FALSE );
+}
+
+
 static void
-DP_SetGroupData( lpGroupData lpGData, LPVOID lpData, DWORD dwDataSize )
+DP_SetGroupData( lpGroupData lpGData, DWORD dwFlags, 
+                 LPVOID lpData, DWORD dwDataSize )
 {
   /* Clear out the data with this player */
-  if( lpGData->dwDataSize != 0 )
+  if( ( dwFlags & DPSET_LOCAL ) &&
+      ( lpGData->dwLocalDataSize != 0 )
+    )
   {
-        HeapFree( GetProcessHeap(), 0, lpGData->lpData );
-        lpGData->lpData = NULL;
-        lpGData->dwDataSize = 0;
+    HeapFree( GetProcessHeap(), 0, lpGData->lpLocalData );
+    lpGData->lpLocalData = NULL;
+    lpGData->dwLocalDataSize = 0;
+  }
+  if( ( dwFlags & DPSET_REMOTE ) &&
+      ( lpGData->dwRemoteDataSize != 0 )
+    )
+  {
+    HeapFree( GetProcessHeap(), 0, lpGData->lpRemoteData );
+    lpGData->lpRemoteData = NULL;
+    lpGData->dwRemoteDataSize = 0;
   }
 
   /* Reallocate for new data */
-  if( lpData )
+  if( lpData != NULL )
   {
-    lpGData->lpData = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
-                                 sizeof( dwDataSize ) );
-    memcpy( lpGData->lpData, lpData, dwDataSize );
-    lpGData->dwDataSize = dwDataSize;
+    LPVOID lpNewData = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                  sizeof( dwDataSize ) );
+    CopyMemory( lpNewData, lpData, dwDataSize );
+
+    if( dwFlags & DPSET_REMOTE )
+    {
+      lpGData->lpRemoteData     = lpNewData;
+      lpGData->dwRemoteDataSize = dwDataSize;
+    }
+
+    if( dwFlags & DPSET_LOCAL )
+    {
+      lpGData->lpLocalData     = lpData;
+      lpGData->dwLocalDataSize = dwDataSize;
+    }
   }
 
 }
 
-
-static HRESULT WINAPI DirectPlay2WImpl_CreateGroup
-          ( LPDIRECTPLAY2 iface, LPDPID lpidGroup, LPDPNAME lpGroupName, LPVOID lpData, DWORD dwDataSize, DWORD dwFlags )
-{
-  ICOM_THIS(IDirectPlay2Impl,iface);
-  FIXME("(%p)->(%p,%p,%p,0x%08lx,0x%08lx): stub\n", This, lpidGroup, lpGroupName, lpData, dwDataSize, dwFlags );
-  return DP_OK;
-}
-
 /* This function will just create the storage for the new player.
  * In the future it may want to intialize, but for the time being
  * that will be done seperately. 
@@ -982,50 +997,43 @@
  */
 static 
 lpPlayerData DP_CreatePlayer( IDirectPlay2Impl* This, LPDPID lpid, 
-                              LPDPNAME lpName, HANDLE hEvent, BOOL bAnsi )
+                              LPDPNAME lpName, DWORD dwFlags, 
+                              HANDLE hEvent, BOOL bAnsi )
 {
-  lpPlayerList lpPlayer;
+  lpPlayerData lpPData;
 
   TRACE( "(%p)->(%p,%p,%u)\n", This, lpid, lpName, bAnsi );
 
-  /* Allocate the new space and add to end of interface player list */
-  lpPlayer = (lpPlayerList) HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
-                                       sizeof( *lpPlayer ) );
-
-  if( lpPlayer == NULL )
-  {
-    return NULL;
-  }
-
   /* Allocate the storage for the player and associate it with list element */
-  lpPlayer->lpPData = (lpPlayerData) HeapAlloc( GetProcessHeap(), 
-                                                HEAP_ZERO_MEMORY,
-                                                sizeof(*(lpPlayer->lpPData)) );
-  if( lpPlayer->lpPData == NULL )
+  lpPData = (lpPlayerData) HeapAlloc( GetProcessHeap(), 
+                                      HEAP_ZERO_MEMORY,
+                                      sizeof( *lpPData ) );
+  if( lpPData == NULL )
   {
-    /* FIXME: Memory leak */
     return NULL;
   }
 
-  /* Insert the player list into the master list of players */
-  DPQ_INSERT( This->dp2->players, lpPlayer, players );
+  /* Set the desired player ID */
+  lpPData->dpid = *lpid;
 
-  if( *lpid == DPID_UNKNOWN )
+  DP_CopyDPNAMEStruct( &lpPData->name, lpName, bAnsi );
+
+  lpPData->dwFlags = dwFlags;
+
+  /* If we were given an event handle, duplicate it */
+  if( hEvent != 0 )
   {
-    /* Assign the next available player ID - FIXME crap solution */
-    lpPlayer->lpPData->dpid = kludgePlayerGroupId++; 
-  }
-  else 
-  {
-    /* Set the desired player ID - no sanity checking to see if it exists */
-    lpPlayer->lpPData->dpid = *lpid;
+    if( !DuplicateHandle( GetCurrentProcess(), hEvent,
+                          GetCurrentProcess(), &lpPData->hEvent,
+                          0, FALSE, DUPLICATE_SAME_ACCESS )
+      )
+    {
+      /* FIXME: Memory leak */
+      ERR( "Can't duplicate player msg handle %x\n", hEvent );
+    }
   }
 
-  DP_CopyDPNAMEStruct( &lpPlayer->lpPData->name, lpName, bAnsi );
-
-  lpPlayer->lpPData->hEvent = hEvent;
-
-  return lpPlayer->lpPData;
+  return lpPData;
 }
 
 /* Delete the contents of the DPNAME struct */
@@ -1044,7 +1052,7 @@
 
   TRACE( "(%p)->(0x%08lx)\n", This, dpid );
 
-  DPQ_REMOVE_ENTRY( This->dp2->players, players, lpPData->dpid, dpid, lpPlayers );
+  DPQ_REMOVE_ENTRY( This->dp2->lpSysGroup->players, players, lpPData->dpid, ==, dpid, lpPlayers );
 
   if( lpPlayers == NULL )
   {
@@ -1054,11 +1062,12 @@
  
   /* Delete player */
   DP_DeleteDPNameStruct( &lpPlayers->lpPData->name );
+
+  CloseHandle( lpPlayers->lpPData->hEvent );
   HeapFree( GetProcessHeap(), 0, lpPlayers->lpPData );
 
   /* Delete Player List object */
   HeapFree( GetProcessHeap(), 0, lpPlayers );
-
 }
 
 static lpPlayerList DP_FindPlayer( IDirectPlay2AImpl* This, DPID dpid )
@@ -1067,7 +1076,7 @@
 
   TRACE( "(%p)->(0x%08lx)\n", This, dpid );
 
-  DPQ_FIND_ENTRY( This->dp2->players, players, lpPData->dpid, dpid, lpPlayers );
+  DPQ_FIND_ENTRY( This->dp2->lpSysGroup->players, players, lpPData->dpid, ==, dpid, lpPlayers );
 
   return lpPlayers;
 }
@@ -1099,7 +1108,7 @@
   }
 
   /* Copy as required */
-  memcpy( lpDst, lpSrc, lpSrc->dwSize ); 
+  CopyMemory( lpDst, lpSrc, lpSrc->dwSize ); 
 
   if( bAnsi )
   {
@@ -1136,29 +1145,52 @@
 }
 
 static void 
-DP_SetPlayerData( lpPlayerData lpPData, LPVOID lpData, DWORD dwDataSize )
+DP_SetPlayerData( lpPlayerData lpPData, DWORD dwFlags, 
+                  LPVOID lpData, DWORD dwDataSize )
 {
   /* Clear out the data with this player */
-  if( lpPData->dwDataSize != 0 )
+  if( ( dwFlags & DPSET_LOCAL ) &&
+      ( lpPData->dwLocalDataSize != 0 )
+    )
   {
-        HeapFree( GetProcessHeap(), 0, lpPData->lpData );
-        lpPData->lpData = NULL;
-        lpPData->dwDataSize = 0;
+    HeapFree( GetProcessHeap(), 0, lpPData->lpLocalData );
+    lpPData->lpLocalData = NULL;
+    lpPData->dwLocalDataSize = 0;
+  }
+  if( ( dwFlags & DPSET_REMOTE ) &&
+      ( lpPData->dwRemoteDataSize != 0 )
+    )
+  {
+    HeapFree( GetProcessHeap(), 0, lpPData->lpRemoteData );
+    lpPData->lpRemoteData = NULL;
+    lpPData->dwRemoteDataSize = 0;
   }
 
   /* Reallocate for new data */
-  if( lpData )
+  if( lpData != NULL )
   {
-    lpPData->lpData = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
-                                   sizeof( dwDataSize ) );
-    memcpy( lpPData->lpData, lpData, dwDataSize );
-    lpPData->dwDataSize = dwDataSize;
+    LPVOID lpNewData = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                  sizeof( dwDataSize ) );
+    CopyMemory( lpNewData, lpData, dwDataSize );
+
+    if( dwFlags & DPSET_REMOTE )
+    {
+      lpPData->lpRemoteData     = lpNewData;
+      lpPData->dwRemoteDataSize = dwDataSize;
+    }
+
+    if( dwFlags & DPSET_LOCAL )
+    {
+      lpPData->lpLocalData     = lpData;
+      lpPData->dwLocalDataSize = dwDataSize;
+    }
   }
-  
+
 }
 
 static HRESULT WINAPI DP_IF_CreatePlayer 
 ( IDirectPlay2Impl* This, 
+  LPVOID lpMsgHdr, /* NULL for local creation, non NULL for remote creation */
   LPDPID lpidPlayer, 
   LPDPNAME lpPlayerName, 
   HANDLE hEvent, 
@@ -1168,6 +1200,7 @@
   BOOL bAnsi )
 {
   lpPlayerData lpPData;
+  lpPlayerList lpPList;
 
   TRACE( "(%p)->(%p,%p,%d,%p,0x%08lx,0x%08lx,%u)\n", 
          This, lpidPlayer, lpPlayerName, hEvent, lpData, 
@@ -1178,6 +1211,11 @@
     dwFlags = DPPLAYER_SPECTATOR;
   }
 
+  if( lpidPlayer == NULL )
+  {
+    return DPERR_INVALIDPARAMS;
+  }
+
   /* Verify we know how to handle all the flags */
   if( !( ( dwFlags & DPPLAYER_SERVERPLAYER ) ||
          ( dwFlags & DPPLAYER_SPECTATOR )
@@ -1188,29 +1226,32 @@
     ERR( "unknown dwFlags = 0x%08lx\n", dwFlags );
   }
 
-  if ( dwFlags & DPPLAYER_SERVERPLAYER )
+  /* If the name is not specified, we must provide one */
+  if( *lpidPlayer == DPID_UNKNOWN )
   {
-    /* We have a request to create the "master" of the session.
-     * This computer needs to be the session host and the server
-     * player can't have been created yet. 
-     */
-    if( ( !This->dp2->bHostInterface ) ||
-        ( DP_FindPlayer( This, DPID_SERVERPLAYER ) )
-      )
+    /* If we are the session master, we dish out the group/player ids */
+    if( This->dp2->bHostInterface )
     {
-      TRACE( "Denying SERVERPLAYER creation\n" );
-      return DPERR_CANTCREATEPLAYER;
+      *lpidPlayer = DP_NextObjectId();
     }
+    else
+    {
+      /* Request the id from the name server */
+      FIXME( "Request id from NS for new player\n" );
 
-    *lpidPlayer = DPID_SERVERPLAYER;
+      /* Hack for now */
+      *lpidPlayer = DP_NextObjectId();
+    }
   }
   else
   {
-    *lpidPlayer = DPID_UNKNOWN;
+    /* FIXME: Would be nice to perhaps verify that we don't already have
+     *        this player.
+     */
   }
 
-  lpPData = DP_CreatePlayer( This, lpidPlayer, 
-                             lpPlayerName, hEvent, bAnsi );
+  lpPData = DP_CreatePlayer( This, lpidPlayer, lpPlayerName, dwFlags,
+                             hEvent, bAnsi );
 
   if( lpPData == NULL )
   {
@@ -1218,13 +1259,87 @@
   }
 
   /* Update the information and send it to all players in the session */
-  DP_SetPlayerData( lpPData, lpData, dwDataSize );  
+  DP_SetPlayerData( lpPData, DPSET_REMOTE, lpData, dwDataSize );  
+
+  /* Create the list object and link it in */
+  lpPList = (lpPlayerList)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                     sizeof( *lpPList ) );
+  if( lpPList == NULL )
+  {
+    FIXME( "Memory leak\n" );
+    return DPERR_CANTADDPLAYER;
+  }
+
+  lpPList->lpPData = lpPData;
+
+  DPQ_INSERT( This->dp2->lpSysGroup->players, lpPList, players );
 
 
-  /* FIXME: Should send DPMSG_CREATEPLAYERORGROUP message to everyone, 
-            local and remote, that belongs to this session. This will not 
-            be done by calling SetPlayerData */
-  FIXME( "Should broadcast player creation to everything in session\n" );
+  /* Let the SP know that we've created this player */
+  if( This->dp2->spData.lpCB->CreatePlayer )
+  {
+    DPSP_CREATEPLAYERDATA data;
+    DWORD dwCreateFlags = 0;
+
+    TRACE( "Calling SP CreatePlayer\n" );
+
+    if( ( dwFlags & DPPLAYER_SERVERPLAYER ) &&
+        ( *lpidPlayer == DPID_SERVERPLAYER )
+      )
+      dwCreateFlags |= DPLAYI_PLAYER_APPSERVER;
+
+    if( ( dwFlags & DPPLAYER_SERVERPLAYER ) &&
+        ( *lpidPlayer == DPID_NAME_SERVER ) 
+      )
+      dwCreateFlags |= (DPLAYI_PLAYER_NAMESRVR|DPLAYI_PLAYER_SYSPLAYER);
+
+    if( lpMsgHdr == NULL )
+      dwCreateFlags |= DPLAYI_PLAYER_PLAYERLOCAL;
+
+    data.idPlayer          = *lpidPlayer;
+    data.dwFlags           = dwCreateFlags;
+    data.lpSPMessageHeader = lpMsgHdr;
+    data.lpISP             = This->dp2->spData.lpISP;
+
+    (This->dp2->spData.lpCB->CreatePlayer)( &data );
+  }
+
+  /* FIXME: The native version adds everything to group 0 (similar to
+   *        the player queue that we have. I think that the native
+   *        service providers rely on this fact.
+   * NOTE: The lpMsgHdr is always non NULL because there is no requirement to
+   *       broadcast this information since every node will automatically 
+   *       do it.
+   */
+  DP_IF_AddPlayerToGroup( This, NULL, DPID_NOPARENT_GROUP, 
+                          *lpidPlayer, bAnsi );
+
+  /* Inform all other peers of the creation of a new player. If there are
+   * no peers keep this quiet. 
+   * Also, if this was a remote event, no need to rebroadcast it.
+   */
+  if( ( lpMsgHdr == NULL ) &&
+      This->dp2->lpSessionDesc &&
+      ( This->dp2->lpSessionDesc->dwFlags & DPSESSION_MULTICASTSERVER ) )
+  {
+    DPMSG_CREATEPLAYERORGROUP msg;
+    msg.dwType = DPSYS_CREATEPLAYERORGROUP;
+
+    msg.dwPlayerType     = DPPLAYERTYPE_PLAYER;
+    msg.dpId             = *lpidPlayer;
+    msg.dwCurrentPlayers = 0; /* FIXME: Incorrect */
+    msg.lpData           = lpData;
+    msg.dwDataSize       = dwDataSize;
+    msg.dpnName          = *lpPlayerName;
+    msg.dpIdParent       = DPID_NOPARENT_GROUP;
+    msg.dwFlags          = DPMSG_CREATEPLAYER_DWFLAGS( dwFlags );
+
+    /* FIXME: Correct to just use send effectively? */
+    /* FIXME: Should size include data w/ message or just message "header" */
+    /* FIXME: Check return code */
+    DP_SendEx( This, DPID_SERVERPLAYER, DPID_ALLPLAYERS, 0, &msg, sizeof( msg ),
+               0, 0, NULL, NULL, bAnsi );
+  }
 
   return DP_OK;
 }
@@ -1233,7 +1348,17 @@
           ( LPDIRECTPLAY2A iface, LPDPID lpidPlayer, LPDPNAME lpPlayerName, HANDLE hEvent, LPVOID lpData, DWORD dwDataSize, DWORD dwFlags )
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
-  return DP_IF_CreatePlayer( This, lpidPlayer, lpPlayerName, hEvent, 
+
+  if( dwFlags & DPPLAYER_SERVERPLAYER )
+  {
+    *lpidPlayer = DPID_SERVERPLAYER;
+  }
+  else
+  {
+    *lpidPlayer = DPID_UNKNOWN;
+  }
+
+  return DP_IF_CreatePlayer( This, NULL, lpidPlayer, lpPlayerName, hEvent, 
                            lpData, dwDataSize, dwFlags, TRUE );
 }
 
@@ -1241,20 +1366,34 @@
           ( LPDIRECTPLAY2 iface, LPDPID lpidPlayer, LPDPNAME lpPlayerName, HANDLE hEvent, LPVOID lpData, DWORD dwDataSize, DWORD dwFlags )
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
-  return DP_IF_CreatePlayer( This, lpidPlayer, lpPlayerName, hEvent, 
+
+  if( dwFlags & DPPLAYER_SERVERPLAYER )
+  {
+    *lpidPlayer = DPID_SERVERPLAYER;
+  }
+  else
+  {
+    *lpidPlayer = DPID_UNKNOWN;
+  }
+
+  return DP_IF_CreatePlayer( This, NULL, lpidPlayer, lpPlayerName, hEvent, 
                            lpData, dwDataSize, dwFlags, FALSE );
 }
 
 static HRESULT WINAPI DP_IF_DeletePlayerFromGroup
-          ( IDirectPlay2Impl* This, DPID idGroup, DPID idPlayer, BOOL bAnsi )
+          ( IDirectPlay2Impl* This, LPVOID lpMsgHdr, DPID idGroup, 
+            DPID idPlayer, BOOL bAnsi )
 {
-  lpGroupList  lpGList;
+  HRESULT hr = DP_OK;
+
+  lpGroupData  lpGData;
   lpPlayerList lpPList;
   
-  TRACE("(%p)->(0x%08lx,0x%08lx,%u)\n", This, idGroup, idPlayer, bAnsi );
+  TRACE( "(%p)->(%p,0x%08lx,0x%08lx,%u)\n", 
+         This, lpMsgHdr, idGroup, idPlayer, bAnsi );
 
   /* Find the group */
-  if( ( lpGList = DP_FindAnyGroup( This, idGroup ) ) == NULL )
+  if( ( lpGData = DP_FindAnyGroup( This, idGroup ) ) == NULL )
   {
     return DPERR_INVALIDGROUP;
   }
@@ -1266,34 +1405,48 @@
   }
 
   /* Remove the player shortcut from the group */
-  DPQ_REMOVE_ENTRY( lpGList->lpGData->players, players, lpPData->dpid, idPlayer, lpPList );
+  DPQ_REMOVE_ENTRY( lpGData->players, players, lpPData->dpid, ==, idPlayer, lpPList );
 
   if( lpPList == NULL )
   {
-    return FALSE;
+    return DPERR_INVALIDPLAYER;
   }
 
   /* Delete the Player List element */
   HeapFree( GetProcessHeap(), 0, lpPList );
 
+  /* Inform the SP if they care */
+  if( This->dp2->spData.lpCB->RemovePlayerFromGroup )
+  {
+    DPSP_REMOVEPLAYERFROMGROUPDATA data;
+
+    TRACE( "Calling SP RemovePlayerFromGroup\n" );
+
+    data.idPlayer = idPlayer;
+    data.idGroup  = idGroup;
+    data.lpISP    = This->dp2->spData.lpISP;
+
+    hr = (This->dp2->spData.lpCB->RemovePlayerFromGroup)( &data );
+  }
+
   /* Need to send a DELETEPLAYERFROMGROUP message */
   FIXME( "Need to send a message\n" );
 
-  return DP_OK;
+  return hr;
 }
 
 static HRESULT WINAPI DirectPlay2AImpl_DeletePlayerFromGroup
           ( LPDIRECTPLAY2A iface, DPID idGroup, DPID idPlayer )
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
-  return DP_IF_DeletePlayerFromGroup( This, idGroup, idPlayer, TRUE );
+  return DP_IF_DeletePlayerFromGroup( This, NULL, idGroup, idPlayer, TRUE );
 }
 
 static HRESULT WINAPI DirectPlay2WImpl_DeletePlayerFromGroup
           ( LPDIRECTPLAY2 iface, DPID idGroup, DPID idPlayer )
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
-  return DP_IF_DeletePlayerFromGroup( This, idGroup, idPlayer, FALSE );
+  return DP_IF_DeletePlayerFromGroup( This, NULL, idGroup, idPlayer, FALSE );
 }
 
 typedef struct _DPRGOPContext
@@ -1312,6 +1465,7 @@
     LPVOID          lpContext )
 {
   lpDPRGOPContext lpCtxt = (lpDPRGOPContext)lpContext;
+
   TRACE( "Removing element:0x%08lx (type:0x%08lx) from element:0x%08lx\n", 
            dpId, dwPlayerType, lpCtxt->idGroup );
 
@@ -1342,42 +1496,35 @@
 }
 
 static HRESULT WINAPI DP_IF_DestroyGroup
-          ( IDirectPlay2Impl* This, DPID idGroup, BOOL bAnsi )
+          ( IDirectPlay2Impl* This, LPVOID lpMsgHdr, DPID idGroup, BOOL bAnsi )
 {
-  lpGroupList lpGList;
+  lpGroupData lpGData;
   DPRGOPContext context;
 
-  FIXME("(%p)->(0x%08lx,%u): semi stub\n", This, idGroup, bAnsi );
+  FIXME( "(%p)->(%p,0x%08lx,%u): semi stub\n", 
+         This, lpMsgHdr, idGroup, bAnsi );
 
   /* Find the group */
-  if( ( lpGList = DP_FindAnyGroup( This, idGroup ) ) == NULL )
+  if( ( lpGData = DP_FindAnyGroup( This, idGroup ) ) == NULL )
   {
     return DPERR_INVALIDPLAYER; /* yes player */
   }
 
-  /* Yes we're performing a dangerous cast, but it will not be used
-     unless it's actually a dp3 interface because we will have no
-     nested groups to delete and we're performing a check below */
   context.iface   = (LPDIRECTPLAY3)This;
   context.bAnsi   = bAnsi;
   context.idGroup = idGroup;
 
-  /* We should only concern ourselves with a group having groups if this is
-     DirectPlay 3 or greater */
-  if( This->dp3 )
-  {
-    /* Remove all links to groups that this group has since this is dp3 */
-    IDirectPlayX_EnumGroupsInGroup( (LPDIRECTPLAY3A)This, idGroup, NULL, 
-                                    cbRemoveGroupOrPlayer, (LPVOID)&context, 0 );
-    /* FIXME: Is it allowed to delete a sub group with a parent? Must be */
-    if( lpGList->lpGData->parent )
-    {
-      IDirectPlayX_DeleteGroupFromGroup( (LPDIRECTPLAY3A)This, 
-                                         lpGList->lpGData->parent->dpid, 
-                                         idGroup ); 
-    } 
+  /* Remove all links to groups that this group has since this is dp3 */
+  IDirectPlayX_EnumGroupsInGroup( (LPDIRECTPLAY3A)This, idGroup, NULL, 
+                                  cbRemoveGroupOrPlayer, (LPVOID)&context, 0 );
 
-  }
+  /* FIXME: Is it allowed to delete a sub group with a parent? Must be */
+  if( lpGData->parent != DPID_SYSTEM_GROUP )
+  {
+      IDirectPlayX_DeleteGroupFromGroup( (LPDIRECTPLAY3A)This, 
+                                         lpGData->parent, 
+                                         idGroup ); 
+  } 
   
   /* Remove all players that this group has */
   IDirectPlayX_EnumGroupPlayers( (LPDIRECTPLAY3A)This, idGroup, NULL, 
@@ -1386,7 +1533,21 @@
   /* Now delete this group data and list */
   DP_DeleteGroup( This, idGroup ); 
 
-  /* Send out a DESTORYPLAYERORGROUP message */
+  /* Let the SP know that we've destroyed this group */
+  if( This->dp2->spData.lpCB->DeleteGroup )
+  {
+    DPSP_DELETEGROUPDATA data;
+
+    FIXME( "data.dwFlags is incorrect\n" );
+
+    data.idGroup = idGroup;
+    data.dwFlags = 0; 
+    data.lpISP   = This->dp2->spData.lpISP;
+
+    (This->dp2->spData.lpCB->DeleteGroup)( &data );
+  }
+
+  FIXME( "Send out a DESTORYPLAYERORGROUP message\n" );
 
   return DP_OK;
 }
@@ -1395,14 +1556,14 @@
           ( LPDIRECTPLAY2A iface, DPID idGroup )
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
-  return DP_IF_DestroyGroup( This, idGroup, TRUE );
+  return DP_IF_DestroyGroup( This, NULL, idGroup, TRUE );
 }
 
 static HRESULT WINAPI DirectPlay2WImpl_DestroyGroup
           ( LPDIRECTPLAY2 iface, DPID idGroup )
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
-  return DP_IF_DestroyGroup( This, idGroup, FALSE );
+  return DP_IF_DestroyGroup( This, NULL, idGroup, FALSE );
 }
 
 typedef struct _DPFAGContext
@@ -1412,13 +1573,14 @@
 } DPFAGContext, *lpDPFAGContext;
 
 static HRESULT WINAPI DP_IF_DestroyPlayer
-          ( IDirectPlay2Impl* This, DPID idPlayer, BOOL bAnsi )
+          ( IDirectPlay2Impl* This, LPVOID lpMsgHdr, DPID idPlayer, BOOL bAnsi )
 {
   DPFAGContext cbContext;
 
-  FIXME("(%p)->(0x%08lx,%u): semi stub\n", This, idPlayer, bAnsi );
+  FIXME( "(%p)->(%p,0x%08lx,%u): semi stub\n", 
+         This, lpMsgHdr, idPlayer, bAnsi );
 
-  if( DP_FindPlayer( This, idPlayer ) )
+  if( DP_FindPlayer( This, idPlayer ) == NULL )
   {
     return DPERR_INVALIDPLAYER;
   }
@@ -1436,7 +1598,21 @@
   /* Now delete player and player list */
   DP_DeletePlayer( This, idPlayer );
 
-  /* FIXME: Send a DELETEPLAYERORGROUP msg */
+  /* Let the SP know that we've destroyed this group */
+  if( This->dp2->spData.lpCB->DeletePlayer )
+  {
+    DPSP_DELETEPLAYERDATA data;
+
+    FIXME( "data.dwFlags is incorrect\n" );
+
+    data.idPlayer = idPlayer;
+    data.dwFlags = 0; 
+    data.lpISP   = This->dp2->spData.lpISP;
+
+    (This->dp2->spData.lpCB->DeletePlayer)( &data );
+  }
+
+  FIXME( "Send a DELETEPLAYERORGROUP msg\n" );
 
   return DP_OK;
 }
@@ -1473,14 +1649,14 @@
           ( LPDIRECTPLAY2A iface, DPID idPlayer )
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
-  return DP_IF_DestroyPlayer( This, idPlayer, TRUE );
+  return DP_IF_DestroyPlayer( This, NULL, idPlayer, TRUE );
 }
 
 static HRESULT WINAPI DirectPlay2WImpl_DestroyPlayer
           ( LPDIRECTPLAY2 iface, DPID idPlayer )
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
-  return DP_IF_DestroyPlayer( This, idPlayer, FALSE );
+  return DP_IF_DestroyPlayer( This, NULL, idPlayer, FALSE );
 }
 
 static HRESULT WINAPI DP_IF_EnumGroupPlayers
@@ -1488,9 +1664,58 @@
             LPDPENUMPLAYERSCALLBACK2 lpEnumPlayersCallback2,
             LPVOID lpContext, DWORD dwFlags, BOOL bAnsi )
 {
-  FIXME("(%p)->(0x%08lx,%p,%p,%p,0x%08lx,%u): stub\n", 
+  lpGroupData  lpGData;
+  lpPlayerList lpPList;
+
+  FIXME("(%p)->(0x%08lx,%p,%p,%p,0x%08lx,%u): semi stub\n", 
           This, idGroup, lpguidInstance, lpEnumPlayersCallback2, 
           lpContext, dwFlags, bAnsi );
+
+  /* Find the group */
+  if( ( lpGData = DP_FindAnyGroup( This, idGroup ) ) == NULL )
+  {
+    return DPERR_INVALIDGROUP;
+  }
+
+  if( DPQ_IS_EMPTY( lpGData->players ) )
+  {
+    return DP_OK;
+  }
+ 
+  lpPList = DPQ_FIRST( lpGData->players );
+
+  /* Walk the players in this group */
+  for( ;; )
+  {
+    /* We do not enum the name server or app server as they are of no
+     * concequence to the end user.
+     */
+    if( ( lpPList->lpPData->dpid != DPID_NAME_SERVER ) &&
+        ( lpPList->lpPData->dpid != DPID_SERVERPLAYER ) 
+      )
+    {
+
+      /* FIXME: Need to add stuff for dwFlags checking */
+
+      if( !lpEnumPlayersCallback2( lpPList->lpPData->dpid, DPPLAYERTYPE_PLAYER,
+                                   &lpPList->lpPData->name,
+                                   lpPList->lpPData->dwFlags,
+                                   lpContext )
+        )
+      {
+        /* User requested break */
+        return DP_OK;
+      }
+    }
+
+    if( DPQ_IS_ENDOFLIST( lpPList->players ) )
+    {
+      break; 
+    }
+
+    lpPList = DPQ_NEXT( lpPList->players );
+  }
+
   return DP_OK;
 }
 
@@ -1522,38 +1747,10 @@
             LPDPENUMPLAYERSCALLBACK2 lpEnumPlayersCallback2, 
             LPVOID lpContext, DWORD dwFlags, BOOL bAnsi )
 {
-  lpGroupList lpGList;
-
-  FIXME("(%p)->(%p,%p,%p,0x%08lx,%u): semi stub\n", 
-         This, lpguidInstance, lpEnumPlayersCallback2, 
-         lpContext, dwFlags, bAnsi );
-
-  lpGList = This->dp2->groups.lpQHFirst; 
-
-  while( lpGList )
-  {
-    /* Is this a top level group? */
-    if( lpGList->lpGData->parent )
-    {
-      continue;
-    }
-
-    /* FIXME: Should check dwFlags for match here */
-
-    if( !(*lpEnumPlayersCallback2)( lpGList->lpGData->dpid, DPPLAYERTYPE_GROUP,
-                                    &lpGList->lpGData->name, dwFlags, 
-                                    lpContext ) )
-    {
-      break; /* User requested break */
-    }
-
-    if( ( lpGList = lpGList->groups.lpQNext ) == This->dp2->groups.lpQHFirst )
-    {
-      break;
-    }
-  } 
-
-  return DP_OK;
+  return DP_IF_EnumGroupsInGroup( (IDirectPlay3Impl*)This, 
+                                  DPID_SYSTEM_GROUP, lpguidInstance,
+                                  lpEnumPlayersCallback2, lpContext,
+                                  dwFlags, bAnsi );
 }
 
 static HRESULT WINAPI DirectPlay2AImpl_EnumGroups
@@ -1581,12 +1778,9 @@
             LPDPENUMPLAYERSCALLBACK2 lpEnumPlayersCallback2, 
             LPVOID lpContext, DWORD dwFlags, BOOL bAnsi )
 {
-
-  FIXME("(%p)->(%p,%p,%p,0x%08lx,%u): stub\n", 
-          This, lpguidInstance, lpEnumPlayersCallback2, 
-          lpContext, dwFlags, bAnsi );
-
-  return DP_OK;
+  return DP_IF_EnumGroupPlayers( This, DPID_SYSTEM_GROUP, lpguidInstance,
+                                 lpEnumPlayersCallback2, lpContext,
+                                 dwFlags, bAnsi );
 }
 
 static HRESULT WINAPI DirectPlay2AImpl_EnumPlayers
@@ -1624,6 +1818,7 @@
   NS_ResetSessionEnumeration( lpNSInfo );
 
   /* Enumerate all sessions */
+  /* FIXME: Need to indicate ANSI */
   while( (lpSessionDesc = NS_WalkSessions( lpNSInfo ) ) != NULL )
   {
     TRACE( "EnumSessionsCallback2 invoked\n" );
@@ -1633,106 +1828,165 @@
     }
   }
 
+  /* Invoke one last time to indicate that there is no more to come */
+  lpEnumSessionsCallback2( NULL, &dwTimeout, DPESC_TIMEDOUT, lpContext );
 }
 
-static DWORD CALLBACK DP_EnumSessionsSpwanThreadA( LPVOID lpContext )
+static void DP_InvokeEnumSessionCallbacksW( LPDPENUMSESSIONSCALLBACK2 lpEnumSessionsCallback2,
+                                     LPVOID lpNSInfo,
+                                     DWORD dwTimeout,
+                                     LPVOID lpContext )
 {
-  ICOM_THIS(IDirectPlay2Impl,lpContext);
-  DWORD dwTimeout = This->dp2->enumSessionAsyncCallbackData.dwTimeout;
+  LPDPSESSIONDESC2 lpSessionDesc;
 
-  TRACE( "(%p)->(0x%08lx)\n", This, dwTimeout );
+  FIXME( ": not checking for conditions\n" );
 
-  /* FIXME: Don't think this is exactly right. It'll do for now */
+  NS_ResetSessionEnumeration( lpNSInfo );
+
+  /* Enumerate all sessions */
+  /* FIXME: Need to indicate UNICODE */
+  while( (lpSessionDesc = NS_WalkSessions( lpNSInfo ) ) != NULL )
+  {
+    TRACE( "EnumSessionsCallback2 invoked\n" );
+    if( !lpEnumSessionsCallback2( lpSessionDesc, &dwTimeout, 0, lpContext ) )
+    {
+      return;
+    }
+  }
+
+  /* Invoke one last time to indicate that there is no more to come */
+  lpEnumSessionsCallback2( NULL, &dwTimeout, DPESC_TIMEDOUT, lpContext );
+  
+}
+
+static DWORD CALLBACK DP_EnumSessionsSendAsyncRequestThread( LPVOID lpContext )
+{
+  EnumSessionAsyncCallbackData* data = (EnumSessionAsyncCallbackData*)lpContext;
+  HANDLE hSuicideRequest = data->hSuicideRequest;
+  DWORD dwTimeout = data->dwTimeout;
+
+  TRACE( "Thread started with timeout = 0x%08lx\n", dwTimeout );
+
   for( ;; )
   {
-    /* 2: Send the broadcast for session enumeration */
-    NS_SendSessionRequestBroadcast( This->dp2->lpNameServerData );
+    NS_SendSessionRequestBroadcast( &data->requestGuid,
+                                    data->dwEnumSessionFlags,
+                                    data->lpSpData );
 
-    SleepEx( dwTimeout, FALSE ); 
-
-    DP_InvokeEnumSessionCallbacksA( This->dp2->enumSessionAsyncCallbackData.cb,
-                                    This->dp2->lpNameServerData, 
-                                    dwTimeout,
-                                    This->dp2->enumSessionAsyncCallbackData.lpContext );
-
-    /* All sessions have been enumerated. Invoke the callback function
-       once more indicating a timeout has occured. This is the way
-       that the application can indicate that it wishes to continue with the
-       enumeration */
-    if( !(This->dp2->enumSessionAsyncCallbackData.cb)( NULL, &dwTimeout, DPESC_TIMEDOUT, lpContext ) )
+    /* Sleep up to dwTimeout waiting for request to terminate thread */
+    if( WaitForSingleObject( hSuicideRequest, dwTimeout ) == WAIT_OBJECT_0 )
     {
-      /* The application doesn't want us to continue - end this thread */
-      return 0;
+      TRACE( "Thread terminating on terminate request\n" );
+      break;
     }
-
   }
 
+  TRACE( "Thread terminating\n" );
+
+  /* Clean up the thread data */
+  CloseHandle( hSuicideRequest );
+  HeapFree( GetProcessHeap(), 0, lpContext );
+
   return 1;
 }
 
+static void DP_KillEnumSessionThread( IDirectPlay2Impl* This )
+{
+  /* Does a thread exist? If so we were doing an async enum session */
+  if( This->dp2->hEnumSessionThread != INVALID_HANDLE_VALUE )
+  {
+    TRACE( "Killing EnumSession thread\n" );
+
+    /* Request that the thread kill itself nicely */
+    SetEvent( This->dp2->hKillEnumSessionThreadEvent );
+    CloseHandle( This->dp2->hKillEnumSessionThreadEvent );
+
+    /* We no longer need to know about the thread */
+    CloseHandle( This->dp2->hEnumSessionThread );
+
+    This->dp2->hEnumSessionThread = INVALID_HANDLE_VALUE;
+  }
+}
+
 static HRESULT WINAPI DirectPlay2AImpl_EnumSessions
-          ( LPDIRECTPLAY2A iface, LPDPSESSIONDESC2 lpsd, DWORD dwTimeout, LPDPENUMSESSIONSCALLBACK2 lpEnumSessionsCallback2,
+          ( LPDIRECTPLAY2A iface, LPDPSESSIONDESC2 lpsd, DWORD dwTimeout, 
+            LPDPENUMSESSIONSCALLBACK2 lpEnumSessionsCallback2,
             LPVOID lpContext, DWORD dwFlags )
 {
+  HRESULT hr = DP_OK;
   ICOM_THIS(IDirectPlay2Impl,iface);
 
-  TRACE("(%p)->(%p,0x%08lx,%p,%p,0x%08lx)\n", This, lpsd, dwTimeout, lpEnumSessionsCallback2, lpContext, dwFlags );
+  /* FIXME: Combine ANSI and UNICODE versions */
 
+  TRACE( "(%p)->(%p,0x%08lx,%p,%p,0x%08lx)\n", 
+         This, lpsd, dwTimeout, lpEnumSessionsCallback2, lpContext, dwFlags );
+
+  /* Can't enumerate if the interface is already open */
+  if( This->dp2->bConnectionOpen )
+  {
+    return DPERR_GENERIC;
+  }
+
+  /* Use the service provider default? */
   if( dwTimeout == 0 )
   {
-    /* Should actually be getting the dwTimeout value through 
-       IDirectPlay_GetCaps( This, ...)  */
-    FIXME( ": should provide a dependent dwTimeout\n" );
-    dwTimeout = 5 * 1000; /* 5 seconds */
+    DPCAPS spCaps;
+    spCaps.dwSize = sizeof( spCaps );
+
+    IDirectPlayX_GetCaps( iface, &spCaps, 0 );
+    dwTimeout = spCaps.dwTimeout;
+
+    /* FIXME: If it's still 0, we need to provide the IP default */
   }
 
   if( dwFlags & DPENUMSESSIONS_STOPASYNC )
   {
-    /* Does a thread exist? If so we were doing an async enum session */
-    if( This->dp2->hEnumSessionThread != INVALID_HANDLE_VALUE )
-    {
-      /* FIXME: This needs to be send an event to the thread to clean itself up nicely */
-      TerminateThread( This->dp2->hEnumSessionThread, 0 );
-      CloseHandle( This->dp2->hEnumSessionThread );
-
-      This->dp2->hEnumSessionThread = INVALID_HANDLE_VALUE;
-
-      This->dp2->enumSessionAsyncCallbackData.cb        = NULL;
-      This->dp2->enumSessionAsyncCallbackData.lpContext = NULL;
-      This->dp2->enumSessionAsyncCallbackData.dwTimeout = INFINITE;
-
-      return DP_OK;
-    }
-  
-    /* Indicate some sort of error... */
-    WARN( "STOPASYNC attempted when no async running\n" );  
-    return DP_OK;
+    DP_KillEnumSessionThread( This );
+    return hr;
   }
 
   /* FIXME: Interface locking sucks in this method */
-
   if( ( dwFlags & DPENUMSESSIONS_ASYNC ) )
   {
-    DWORD dwThreadId;
- 
     /* Enumerate everything presently in the local session cache */
-    DP_InvokeEnumSessionCallbacksA( lpEnumSessionsCallback2, This->dp2->lpNameServerData, dwTimeout, lpContext );
+    DP_InvokeEnumSessionCallbacksA( lpEnumSessionsCallback2, 
+                                    This->dp2->lpNameServerData, dwTimeout, 
+                                    lpContext );
+
 
     /* See if we've already created a thread to service this interface */
     if( This->dp2->hEnumSessionThread == INVALID_HANDLE_VALUE )
     {
-      /* FIXME: Should be adding a reference here - another thread now knows
-                how to call this interface */
-      This->dp2->enumSessionAsyncCallbackData.cb        = lpEnumSessionsCallback2;
-      This->dp2->enumSessionAsyncCallbackData.lpContext = lpContext;
-      This->dp2->enumSessionAsyncCallbackData.dwTimeout = dwTimeout;
+      DWORD dwThreadId;
+      EnumSessionAsyncCallbackData* lpData 
+        = (EnumSessionAsyncCallbackData*)HeapAlloc( GetProcessHeap(),
+                                                    HEAP_ZERO_MEMORY,
+                                                    sizeof( *lpData ) );
+      /* FIXME: need to kill the thread on object deletion */
+      lpData->lpSpData  = &This->dp2->spData;
+      CopyMemory( &lpData->requestGuid, &lpsd->guidApplication, sizeof(GUID) );
+      lpData->dwEnumSessionFlags = dwFlags;
+      lpData->dwTimeout = dwTimeout;
 
-      TRACE( ": creating EnumSessions thread\n" );
+      This->dp2->hKillEnumSessionThreadEvent = 
+        CreateEventA( NULL, TRUE, FALSE, NULL );
+
+      if( !DuplicateHandle( GetCurrentProcess(), 
+                            This->dp2->hKillEnumSessionThreadEvent,
+                            GetCurrentProcess(),
+                            &lpData->hSuicideRequest,
+                            0, FALSE, DUPLICATE_SAME_ACCESS )
+        )
+      {
+        ERR( "Can't duplicate thread killing handle\n" );
+      }
     
+      TRACE( ": creating EnumSessionsRequest thread\n" );
+
       This->dp2->hEnumSessionThread = CreateThread( NULL,
                                                     0,
-                                                    DP_EnumSessionsSpwanThreadA,
-                                                    This,
+                                                    DP_EnumSessionsSendAsyncRequestThread,
+                                                    lpData,
                                                     0,
                                                     &dwThreadId );
     }
@@ -1740,66 +1994,205 @@
   }
   else
   {
-    /* Send the broadcast for session enumeration */
-    NS_SendSessionRequestBroadcast( This->dp2->lpNameServerData );
+    /* Invalidate the session cache for the interface */
+    NS_InvalidateSessionCache( This->dp2->lpNameServerData );
 
-    SleepEx( dwTimeout, FALSE ); 
- 
-    DP_InvokeEnumSessionCallbacksA( lpEnumSessionsCallback2, This->dp2->lpNameServerData, dwTimeout, lpContext );
+    /* Send the broadcast for session enumeration */
+    hr = NS_SendSessionRequestBroadcast( &lpsd->guidApplication,
+                                         dwFlags,
+                                         &This->dp2->spData );
+
+
+    SleepEx( dwTimeout, FALSE );
+
+    DP_InvokeEnumSessionCallbacksA( lpEnumSessionsCallback2, 
+                                    This->dp2->lpNameServerData, dwTimeout, lpContext );
   }
 
-  return DP_OK;
+  return hr;
 }
 
 static HRESULT WINAPI DirectPlay2WImpl_EnumSessions
-          ( LPDIRECTPLAY2 iface, LPDPSESSIONDESC2 lpsd, DWORD dwTimeout, LPDPENUMSESSIONSCALLBACK2 lpEnumSessionsCallback2,
+          ( LPDIRECTPLAY2 iface, LPDPSESSIONDESC2 lpsd, DWORD dwTimeout, 
+            LPDPENUMSESSIONSCALLBACK2 lpEnumSessionsCallback2,
             LPVOID lpContext, DWORD dwFlags )
 {
+  HRESULT hr = DP_OK;
   ICOM_THIS(IDirectPlay2Impl,iface);
-  FIXME("(%p)->(%p,0x%08lx,%p,%p,0x%08lx): stub\n", This, lpsd, dwTimeout, lpEnumSessionsCallback2, lpContext, dwFlags );
-  return DP_OK;
+
+  TRACE( "(%p)->(%p,0x%08lx,%p,%p,0x%08lx)\n", 
+         This, lpsd, dwTimeout, lpEnumSessionsCallback2, lpContext, dwFlags );
+
+  /* Can't enumerate if the interface is already open */
+  if( This->dp2->bConnectionOpen )
+  {
+    return DPERR_GENERIC;
+  }
+
+  /* Use the service provider default? */
+  if( dwTimeout == 0 )
+  {
+    DPCAPS spCaps;
+    spCaps.dwSize = sizeof( spCaps );
+
+    IDirectPlayX_GetCaps( iface, &spCaps, 0 );
+    dwTimeout = spCaps.dwTimeout;
+  }
+
+  /* FIXME: Interface locking ... */
+
+  if( dwFlags & DPENUMSESSIONS_STOPASYNC )
+  {
+    DP_KillEnumSessionThread( This );
+
+    return hr;
+  }
+
+  /* FIXME: Interface locking sucks in this method */
+
+  if( ( dwFlags & DPENUMSESSIONS_ASYNC ) )
+  {
+    /* Enumerate everything presently in the local session cache */
+    DP_InvokeEnumSessionCallbacksW( lpEnumSessionsCallback2, 
+                                    This->dp2->lpNameServerData, 
+                                    dwTimeout, lpContext );
+
+    /* See if we've already created a thread to service this interface */
+    if( This->dp2->hEnumSessionThread == INVALID_HANDLE_VALUE )
+    {
+      DWORD dwThreadId;
+
+      EnumSessionAsyncCallbackData* lpData
+        = (EnumSessionAsyncCallbackData*)HeapAlloc( GetProcessHeap(),
+                                                    HEAP_ZERO_MEMORY,
+                                                    sizeof( *lpData ) );
+      /* FIXME: need to kill the thread on object deletion */
+      lpData->lpSpData  = &This->dp2->spData;
+      CopyMemory( &lpData->requestGuid, &lpsd->guidApplication, sizeof(GUID) );
+      lpData->dwEnumSessionFlags = dwFlags;
+      lpData->dwTimeout = dwTimeout;
+
+      This->dp2->hKillEnumSessionThreadEvent =
+        CreateEventA( NULL, TRUE, FALSE, NULL );
+
+      if( !DuplicateHandle( GetCurrentProcess(),
+                            This->dp2->hKillEnumSessionThreadEvent,
+                            GetCurrentProcess(),
+                            &lpData->hSuicideRequest,
+                            0, FALSE, DUPLICATE_SAME_ACCESS )
+        )
+      {
+        ERR( "Can't duplicate thread killing handle\n" );
+      }
+
+
+      TRACE( ": creating EnumSessionsRequest thread\n" );
+
+      This->dp2->hEnumSessionThread = CreateThread( NULL,
+                                                    0,
+                                                    DP_EnumSessionsSendAsyncRequestThread,
+                                                    lpData,
+                                                    0,
+                                                    &dwThreadId );
+    }
+
+  }
+  else
+  {
+    /* Invalidate the session cache for the interface */
+    NS_InvalidateSessionCache( This->dp2->lpNameServerData );
+
+    /* Send the broadcast for session enumeration */
+    hr = NS_SendSessionRequestBroadcast( &lpsd->guidApplication,
+                                         dwFlags,
+                                         &This->dp2->spData );
+
+    SleepEx( dwTimeout, FALSE );
+
+    DP_InvokeEnumSessionCallbacksW( lpEnumSessionsCallback2, 
+                                    This->dp2->lpNameServerData, 
+                                    dwTimeout, lpContext );
+  }
+
+  return hr;
 }
 
+static HRESULT WINAPI DP_IF_GetPlayerCaps
+          ( IDirectPlay2Impl* This, DPID idPlayer, LPDPCAPS lpDPCaps, 
+            DWORD dwFlags )
+{
+  DPSP_GETCAPSDATA data;
+
+  TRACE("(%p)->(0x%08lx,%p,0x%08lx)\n", This, idPlayer, lpDPCaps, dwFlags);
+
+  /* Query the service provider */
+  data.idPlayer = idPlayer;
+  data.dwFlags  = dwFlags;
+  data.lpCaps   = lpDPCaps;
+  data.lpISP    = This->dp2->spData.lpISP;
+
+  return (This->dp2->spData.lpCB->GetCaps)( &data );
+}
+
+
 static HRESULT WINAPI DirectPlay2AImpl_GetCaps
           ( LPDIRECTPLAY2A iface, LPDPCAPS lpDPCaps, DWORD dwFlags )
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
-  FIXME("(%p)->(%p,0x%08lx): stub\n", This, lpDPCaps, dwFlags );
-  return DP_OK;
+  return DP_IF_GetPlayerCaps( This, DPID_ALLPLAYERS, lpDPCaps, dwFlags );
 }
 
 static HRESULT WINAPI DirectPlay2WImpl_GetCaps
           ( LPDIRECTPLAY2 iface, LPDPCAPS lpDPCaps, DWORD dwFlags )
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
-  FIXME("(%p)->(%p,0x%08lx): stub\n", This, lpDPCaps, dwFlags );
-  return DP_OK;
+  return DP_IF_GetPlayerCaps( This, DPID_ALLPLAYERS, lpDPCaps, dwFlags );
 }
 
 static HRESULT WINAPI DP_IF_GetGroupData
           ( IDirectPlay2Impl* This, DPID idGroup, LPVOID lpData, 
             LPDWORD lpdwDataSize, DWORD dwFlags, BOOL bAnsi )
 {
-  lpGroupList lpGList;
+  lpGroupData lpGData;
+  DWORD dwRequiredBufferSize;
+  LPVOID lpCopyDataFrom;
 
-  FIXME("(%p)->(0x%08lx,%p,%p,0x%08lx,%u): dwFlags ignored\n", 
-          This, idGroup, lpData, lpdwDataSize, dwFlags, bAnsi );
+  TRACE( "(%p)->(0x%08lx,%p,%p,0x%08lx,%u)\n", 
+         This, idGroup, lpData, lpdwDataSize, dwFlags, bAnsi );
 
-  if( ( lpGList = DP_FindAnyGroup( This, idGroup ) ) == NULL )
+  if( ( lpGData = DP_FindAnyGroup( This, idGroup ) ) == NULL )
   {
     return DPERR_INVALIDGROUP;
   }
 
+  /* How much buffer is required? */
+  if( dwFlags & DPSET_REMOTE )
+  {
+    dwRequiredBufferSize = lpGData->dwRemoteDataSize;
+    lpCopyDataFrom       = lpGData->lpRemoteData;
+  }
+  else if( dwFlags & DPSET_LOCAL )
+  {
+    dwRequiredBufferSize = lpGData->dwLocalDataSize;
+    lpCopyDataFrom       = lpGData->lpLocalData;
+  }
+  else
+  {
+    ERR( "Neither local or remote data requested!?!\n" );
+    dwRequiredBufferSize = 0;
+    lpCopyDataFrom = NULL;
+  }
+
   /* Is the user requesting to know how big a buffer is required? */
   if( ( lpData == NULL ) ||
-      ( *lpdwDataSize < lpGList->lpGData->dwDataSize ) 
+      ( *lpdwDataSize < dwRequiredBufferSize ) 
     )
   {
-    *lpdwDataSize = lpGList->lpGData->dwDataSize;
+    *lpdwDataSize = dwRequiredBufferSize;
     return DPERR_BUFFERTOOSMALL; 
   }
 
-  memcpy( lpData, lpGList->lpGData->lpData, lpGList->lpGData->dwDataSize );
+  CopyMemory( lpData, lpCopyDataFrom, dwRequiredBufferSize );
 
   return DP_OK;
 }
@@ -1826,28 +2219,28 @@
           ( IDirectPlay2Impl* This, DPID idGroup, LPVOID lpData, 
             LPDWORD lpdwDataSize, BOOL bAnsi )
 {
-  lpGroupList lpGList;
+  lpGroupData lpGData;
   LPDPNAME    lpName = (LPDPNAME)lpData;
   DWORD       dwRequiredDataSize;
 
   FIXME("(%p)->(0x%08lx,%p,%p,%u) ANSI ignored\n", 
           This, idGroup, lpData, lpdwDataSize, bAnsi );
 
-  if( ( lpGList = DP_FindAnyGroup( This, idGroup ) ) == NULL )
+  if( ( lpGData = DP_FindAnyGroup( This, idGroup ) ) == NULL )
   {
     return DPERR_INVALIDGROUP;
   }
 
-  dwRequiredDataSize = lpGList->lpGData->name.dwSize;
+  dwRequiredDataSize = lpGData->name.dwSize;
 
-  if( lpGList->lpGData->name.psn.lpszShortNameA )
+  if( lpGData->name.psn.lpszShortNameA )
   {
-    dwRequiredDataSize += strlen( lpGList->lpGData->name.psn.lpszShortNameA ) + 1;
+    dwRequiredDataSize += strlen( lpGData->name.psn.lpszShortNameA ) + 1;
   }
 
-  if( lpGList->lpGData->name.pln.lpszLongNameA )
+  if( lpGData->name.pln.lpszLongNameA )
   {
-    dwRequiredDataSize += strlen( lpGList->lpGData->name.pln.lpszLongNameA ) + 1;
+    dwRequiredDataSize += strlen( lpGData->name.pln.lpszLongNameA ) + 1;
   }
  
   if( ( lpData == NULL ) ||
@@ -1859,22 +2252,22 @@
   }
 
   /* Copy the structure */
-  memcpy( lpName, &lpGList->lpGData->name, lpGList->lpGData->name.dwSize );
+  CopyMemory( lpName, &lpGData->name, lpGData->name.dwSize );
 
-  if( lpGList->lpGData->name.psn.lpszShortNameA )
+  if( lpGData->name.psn.lpszShortNameA )
   {
-    strcpy( ((BYTE*)lpName)+lpGList->lpGData->name.dwSize, 
-            lpGList->lpGData->name.psn.lpszShortNameA );
+    strcpy( ((BYTE*)lpName)+lpGData->name.dwSize, 
+            lpGData->name.psn.lpszShortNameA );
   }
   else
   {
     lpName->psn.lpszShortNameA = NULL;
   }
 
-  if( lpGList->lpGData->name.psn.lpszShortNameA )
+  if( lpGData->name.psn.lpszShortNameA )
   {
-    strcpy( ((BYTE*)lpName)+lpGList->lpGData->name.dwSize, 
-            lpGList->lpGData->name.pln.lpszLongNameA );
+    strcpy( ((BYTE*)lpName)+lpGData->name.dwSize, 
+            lpGData->name.pln.lpszLongNameA );
   }
   else
   {
@@ -1900,20 +2293,28 @@
   return DP_IF_GetGroupName( This, idGroup, lpData, lpdwDataSize, FALSE );
 }
 
+static HRESULT WINAPI DP_IF_GetMessageCount
+          ( IDirectPlay2Impl* This, DPID idPlayer, 
+            LPDWORD lpdwCount, BOOL bAnsi )
+{
+  FIXME("(%p)->(0x%08lx,%p,%u): stub\n", This, idPlayer, lpdwCount, bAnsi );
+  return DP_IF_GetMessageQueue( (IDirectPlay4Impl*)This, 0, idPlayer, 
+                                DPMESSAGEQUEUE_RECEIVE, lpdwCount, NULL, 
+                                bAnsi );
+}
+
 static HRESULT WINAPI DirectPlay2AImpl_GetMessageCount
           ( LPDIRECTPLAY2A iface, DPID idPlayer, LPDWORD lpdwCount )
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
-  FIXME("(%p)->(0x%08lx,%p): stub\n", This, idPlayer, lpdwCount );
-  return DP_OK;
+  return DP_IF_GetMessageCount( This, idPlayer, lpdwCount, TRUE );
 }
 
 static HRESULT WINAPI DirectPlay2WImpl_GetMessageCount
           ( LPDIRECTPLAY2 iface, DPID idPlayer, LPDWORD lpdwCount )
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
-  FIXME("(%p)->(0x%08lx,%p): stub\n", This, idPlayer, lpdwCount );
-  return DP_OK;
+  return DP_IF_GetMessageCount( This, idPlayer, lpdwCount, FALSE );
 }
 
 static HRESULT WINAPI DirectPlay2AImpl_GetPlayerAddress
@@ -1933,19 +2334,19 @@
 }
 
 static HRESULT WINAPI DirectPlay2AImpl_GetPlayerCaps
-          ( LPDIRECTPLAY2A iface, DPID idPlayer, LPDPCAPS lpPlayerCaps, DWORD dwFlags )
+          ( LPDIRECTPLAY2A iface, DPID idPlayer, LPDPCAPS lpPlayerCaps, 
+            DWORD dwFlags )
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
-  FIXME("(%p)->(0x%08lx,%p,0x%08lx): stub\n", This, idPlayer, lpPlayerCaps, dwFlags );
-  return DP_OK;
+  return DP_IF_GetPlayerCaps( This, idPlayer, lpPlayerCaps, dwFlags );
 }
 
 static HRESULT WINAPI DirectPlay2WImpl_GetPlayerCaps
-          ( LPDIRECTPLAY2 iface, DPID idPlayer, LPDPCAPS lpPlayerCaps, DWORD dwFlags )
+          ( LPDIRECTPLAY2 iface, DPID idPlayer, LPDPCAPS lpPlayerCaps, 
+            DWORD dwFlags )
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
-  FIXME("(%p)->(0x%08lx,%p,0x%08lx): stub\n", This, idPlayer, lpPlayerCaps, dwFlags );
-  return DP_OK;
+  return DP_IF_GetPlayerCaps( This, idPlayer, lpPlayerCaps, dwFlags );
 }
 
 static HRESULT WINAPI DP_IF_GetPlayerData
@@ -1953,8 +2354,10 @@
             LPDWORD lpdwDataSize, DWORD dwFlags, BOOL bAnsi )
 {
   lpPlayerList lpPList;
+  DWORD dwRequiredBufferSize;
+  LPVOID lpCopyDataFrom;
 
-  FIXME( "(%p)->(0x%08lx,%p,%p,0x%08lx,%u): stub\n", 
+  TRACE( "(%p)->(0x%08lx,%p,%p,0x%08lx,%u)\n", 
          This, idPlayer, lpData, lpdwDataSize, dwFlags, bAnsi );
 
   if( ( lpPList = DP_FindPlayer( This, idPlayer ) ) == NULL )
@@ -1962,16 +2365,34 @@
     return DPERR_INVALIDPLAYER;
   }
 
+  /* How much buffer is required? */
+  if( dwFlags & DPSET_REMOTE )
+  {
+    dwRequiredBufferSize = lpPList->lpPData->dwRemoteDataSize;
+    lpCopyDataFrom       = lpPList->lpPData->lpRemoteData;
+  }
+  else if( dwFlags & DPSET_LOCAL )
+  {
+    dwRequiredBufferSize = lpPList->lpPData->dwLocalDataSize;
+    lpCopyDataFrom       = lpPList->lpPData->lpLocalData;
+  }
+  else
+  {
+    ERR( "Neither local or remote data requested!?!\n" );
+    dwRequiredBufferSize = 0;
+    lpCopyDataFrom = NULL;
+  }
+
   /* Is the user requesting to know how big a buffer is required? */
   if( ( lpData == NULL ) ||
-      ( *lpdwDataSize < lpPList->lpPData->dwDataSize )
+      ( *lpdwDataSize < dwRequiredBufferSize )
     )
   {
-    *lpdwDataSize = lpPList->lpPData->dwDataSize;
+    *lpdwDataSize = dwRequiredBufferSize;
     return DPERR_BUFFERTOOSMALL;
   }
 
-  memcpy( lpData, lpPList->lpPData->lpData, lpPList->lpPData->dwDataSize );
+  CopyMemory( lpData, lpCopyDataFrom, dwRequiredBufferSize );
 
   return DP_OK;
 }
@@ -2031,7 +2452,7 @@
   }
 
   /* Copy the structure */
-  memcpy( lpName, &lpPList->lpPData->name, lpPList->lpPData->name.dwSize );
+  CopyMemory( lpName, &lpPList->lpPData->name, lpPList->lpPData->name.dwSize );
 
   if( lpPList->lpPData->name.psn.lpszShortNameA )
   {
@@ -2072,20 +2493,47 @@
   return DP_IF_GetPlayerName( This, idPlayer, lpData, lpdwDataSize, FALSE );
 }
 
+static HRESULT WINAPI DP_GetSessionDesc
+          ( IDirectPlay2Impl* This, LPVOID lpData, LPDWORD lpdwDataSize, 
+            BOOL bAnsi )
+{
+  DWORD dwRequiredSize;
+
+  TRACE( "(%p)->(%p,%p,%u)\n", This, lpData, lpdwDataSize, bAnsi );
+
+  if( ( lpData == NULL ) && ( lpdwDataSize == NULL ) ) 
+  {
+    return DPERR_INVALIDPARAMS;
+  }
+
+  /* FIXME: Get from This->dp2->lpSessionDesc */
+  dwRequiredSize = DP_CalcSessionDescSize( This->dp2->lpSessionDesc, bAnsi );
+
+  if ( ( lpData == NULL ) ||
+       ( *lpdwDataSize < dwRequiredSize )
+     )
+  {
+    *lpdwDataSize = dwRequiredSize;
+    return DPERR_BUFFERTOOSMALL;
+  }
+  
+  DP_CopySessionDesc( lpData, This->dp2->lpSessionDesc, bAnsi );
+
+  return DP_OK;  
+}
+
 static HRESULT WINAPI DirectPlay2AImpl_GetSessionDesc
           ( LPDIRECTPLAY2A iface, LPVOID lpData, LPDWORD lpdwDataSize )
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
-  FIXME("(%p)->(%p,%p): stub\n", This, lpData, lpdwDataSize );
-  return DP_OK;
+  return DP_GetSessionDesc( This, lpData, lpdwDataSize, TRUE );
 }
 
 static HRESULT WINAPI DirectPlay2WImpl_GetSessionDesc
           ( LPDIRECTPLAY2 iface, LPVOID lpData, LPDWORD lpdwDataSize )
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
-  FIXME("(%p)->(%p,%p): stub\n", This, lpData, lpdwDataSize );
-  return DP_OK;
+  return DP_GetSessionDesc( This, lpData, lpdwDataSize, TRUE );
 }
 
 /* Intended only for COM compatibility. Always returns an error. */
@@ -2109,9 +2557,12 @@
 
 static HRESULT WINAPI DP_SecureOpen
           ( IDirectPlay2Impl* This, LPCDPSESSIONDESC2 lpsd, DWORD dwFlags,
-            LPCDPSECURITYDESC lpSecurity, LPCDPCREDENTIALS lpCredentials )
+            LPCDPSECURITYDESC lpSecurity, LPCDPCREDENTIALS lpCredentials,
+            BOOL bAnsi )
 {
-  FIXME( "(%p)->(%p,0x%08lx,%p,%p): semi stub\n", 
+  HRESULT hr = DP_OK;
+
+  FIXME( "(%p)->(%p,0x%08lx,%p,%p): partial stub\n", 
          This, lpsd, dwFlags, lpSecurity, lpCredentials );
 
   if( This->dp2->bConnectionOpen )
@@ -2120,28 +2571,76 @@
     return DPERR_ALREADYINITIALIZED;
   }
 
-  /* When we open we need to stop any EnumSession activity */
-  /* FIXME: Perhaps some sort of internal interface would be better */
-  IDirectPlayX_EnumSessions( (LPDIRECTPLAY2A)This, NULL, 0, NULL, NULL, 
-                             DPENUMSESSIONS_STOPASYNC ); 
+  /* If we're enumerating, kill the thread */
+  DP_KillEnumSessionThread( This );
 
   if( dwFlags & DPOPEN_CREATE )
   {
-    dwFlags &= ~DPOPEN_CREATE;
-
     /* Rightoo - this computer is the host and the local computer needs to be
        the name server so that others can join this session */
     NS_SetLocalComputerAsNameServer( lpsd );
 
     This->dp2->bHostInterface = TRUE;
+
+    hr = DP_SetSessionDesc( This, lpsd, 0, TRUE, bAnsi );
+    if( FAILED( hr ) )
+    {
+      ERR( "Unable to set session desc: %s\n", DPLAYX_HresultToString( hr ) );  
+      return hr;
+    }
   }
 
-  if( dwFlags )
+  /* Invoke the conditional callback for the service provider */
+  if( This->dp2->spData.lpCB->Open )
   {
-    ERR( ": ignored dwFlags 0x%08lx\n", dwFlags );
+    DPSP_OPENDATA data;
+
+    FIXME( "Not all data fields are correct. Need new parameter\n" );
+
+    data.bCreate           = (dwFlags & DPOPEN_CREATE ) ? TRUE : FALSE;
+    data.lpSPMessageHeader = (dwFlags & DPOPEN_CREATE ) ? NULL 
+                                                        : NS_GetNSAddr( This->dp2->lpNameServerData );
+    data.lpISP             = This->dp2->spData.lpISP;
+    data.bReturnStatus     = (dwFlags & DPOPEN_RETURNSTATUS) ? TRUE : FALSE;
+    data.dwOpenFlags       = dwFlags;
+    data.dwSessionFlags    = This->dp2->lpSessionDesc->dwFlags;
+
+    hr = (This->dp2->spData.lpCB->Open)(&data);
+  }
+ 
+  {
+    /* FIXME: DPLAY creates this after the name server... */
+    DPID systemGroup = DPID_SYSTEM_GROUP;
+
+    hr = DP_IF_CreateGroup( This, NULL, &systemGroup, NULL,
+                            NULL, 0, 0, TRUE );
+                       
   }
 
-  return DP_OK;
+  if( dwFlags & DPOPEN_JOIN )
+  {
+    DPID dpidServerId = DPID_SERVERPLAYER;
+ 
+    /* Create the server player for this interface. This way we can receive
+     * messages for this session.
+     */
+    /* FIXME: I suppose that we should be setting an event for a receive 
+     *        type of thing. That way the messaging thread could know to wake
+     *        up. DPlay would then trigger the hEvent for the player the
+     *        message is directed to.
+     */
+    hr = DP_IF_CreatePlayer( This, NULL, &dpidServerId, NULL, 0, NULL, 
+                             0, DPPLAYER_SERVERPLAYER, bAnsi );
+  }
+  else if( dwFlags & DPOPEN_CREATE )
+  {
+    DPID dpidNameServerId = DPID_NAME_SERVER;
+
+    hr = DP_IF_CreatePlayer( This, NULL, &dpidNameServerId, NULL, 0, NULL,
+                             0, DPPLAYER_SERVERPLAYER, bAnsi );
+  }
+
+  return hr;
 }
 
 static HRESULT WINAPI DirectPlay2AImpl_Open
@@ -2149,7 +2648,7 @@
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
   TRACE("(%p)->(%p,0x%08lx)\n", This, lpsd, dwFlags );
-  return DP_SecureOpen( This, lpsd, dwFlags, NULL, NULL );
+  return DP_SecureOpen( This, lpsd, dwFlags, NULL, NULL, TRUE );
 }
 
 static HRESULT WINAPI DirectPlay2WImpl_Open
@@ -2157,48 +2656,103 @@
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
   TRACE("(%p)->(%p,0x%08lx)\n", This, lpsd, dwFlags );
-  return DP_SecureOpen( This, lpsd, dwFlags, NULL, NULL );
+  return DP_SecureOpen( This, lpsd, dwFlags, NULL, NULL, FALSE );
+}
+
+static HRESULT WINAPI DP_IF_Receive
+          ( IDirectPlay2Impl* This, LPDPID lpidFrom, LPDPID lpidTo, 
+            DWORD dwFlags, LPVOID lpData, LPDWORD lpdwDataSize, BOOL bAnsi )
+{
+  LPDPMSG lpMsg = NULL;
+
+  FIXME( "(%p)->(%p,%p,0x%08lx,%p,%p,%u): stub\n", 
+         This, lpidFrom, lpidTo, dwFlags, lpData, lpdwDataSize, bAnsi );
+
+  if( dwFlags == 0 )
+  {
+    dwFlags = DPRECEIVE_ALL;
+  }
+
+  /* If the lpData is NULL, we must be peeking the message */
+  if(  ( lpData == NULL ) &&
+      !( dwFlags & DPRECEIVE_PEEK )
+    )
+  {
+    return DPERR_INVALIDPARAMS;
+  }
+
+  if( dwFlags & DPRECEIVE_ALL )
+  {
+    lpMsg = This->dp2->receiveMsgs.lpQHFirst;
+
+    if( !( dwFlags & DPRECEIVE_PEEK ) )
+    {
+      FIXME( "Remove from queue\n" );
+    } 
+  }
+  else if( ( dwFlags & DPRECEIVE_TOPLAYER ) ||
+           ( dwFlags & DPRECEIVE_FROMPLAYER )
+         )
+  {
+    FIXME( "Find matching message 0x%08lx\n", dwFlags );
+  }
+  else
+  {
+    ERR( "Hmmm..dwFlags 0x%08lx\n", dwFlags );
+  }
+
+  if( lpMsg == NULL )
+  {
+    return DPERR_NOMESSAGES;
+  }
+
+  /* Copy into the provided buffer */
+  CopyMemory( lpData, lpMsg->msg, *lpdwDataSize );
+
+  return DP_OK;
 }
 
 static HRESULT WINAPI DirectPlay2AImpl_Receive
-          ( LPDIRECTPLAY2A iface, LPDPID lpidFrom, LPDPID lpidTo, DWORD dwFlags, LPVOID lpData, LPDWORD lpdwDataSize )
+          ( LPDIRECTPLAY2A iface, LPDPID lpidFrom, LPDPID lpidTo, 
+            DWORD dwFlags, LPVOID lpData, LPDWORD lpdwDataSize )
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
-  FIXME("(%p)->(%p,%p,0x%08lx,%p,%p): stub\n", This, lpidFrom, lpidTo, dwFlags, lpData, lpdwDataSize );
-  return DP_OK;
+  return DP_IF_Receive( This, lpidFrom, lpidTo, dwFlags, 
+                        lpData, lpdwDataSize, TRUE );
 }
 
 static HRESULT WINAPI DirectPlay2WImpl_Receive
-          ( LPDIRECTPLAY2 iface, LPDPID lpidFrom, LPDPID lpidTo, DWORD dwFlags, LPVOID lpData, LPDWORD lpdwDataSize )
+          ( LPDIRECTPLAY2 iface, LPDPID lpidFrom, LPDPID lpidTo, 
+            DWORD dwFlags, LPVOID lpData, LPDWORD lpdwDataSize )
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
-  FIXME("(%p)->(%p,%p,0x%08lx,%p,%p): stub\n", This, lpidFrom, lpidTo, dwFlags, lpData, lpdwDataSize );
-  return DP_OK;
+  return DP_IF_Receive( This, lpidFrom, lpidTo, dwFlags, 
+                        lpData, lpdwDataSize, FALSE );
 }
 
 static HRESULT WINAPI DirectPlay2AImpl_Send
           ( LPDIRECTPLAY2A iface, DPID idFrom, DPID idTo, DWORD dwFlags, LPVOID lpData, DWORD dwDataSize )
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
-  FIXME("(%p)->(0x%08lx,0x%08lx,0x%08lx,%p,0x%08lx): stub\n", This, idFrom, idTo, dwFlags, lpData, dwDataSize );
-  return DP_OK;
+  return DP_SendEx( This, idFrom, idTo, dwFlags, lpData, dwDataSize, 
+                    0, 0, NULL, NULL, TRUE );
 }
 
 static HRESULT WINAPI DirectPlay2WImpl_Send
           ( LPDIRECTPLAY2 iface, DPID idFrom, DPID idTo, DWORD dwFlags, LPVOID lpData, DWORD dwDataSize )
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
-  FIXME("(%p)->(0x%08lx,0x%08lx,0x%08lx,%p,0x%08lx): stub\n", This, idFrom, idTo, dwFlags, lpData, dwDataSize );
-  return DP_OK;
+  return DP_SendEx( This, idFrom, idTo, dwFlags, lpData, dwDataSize, 
+                    0, 0, NULL, NULL, FALSE );
 }
 
 static HRESULT WINAPI DP_IF_SetGroupData
           ( IDirectPlay2Impl* This, DPID idGroup, LPVOID lpData, 
             DWORD dwDataSize, DWORD dwFlags, BOOL bAnsi )
 {
-  lpGroupList lpGList;
+  lpGroupData lpGData;
 
-  FIXME( "(%p)->(0x%08lx,%p,0x%08lx,0x%08lx,%u): dwFlags ignored\n", 
+  TRACE( "(%p)->(0x%08lx,%p,0x%08lx,0x%08lx,%u)\n", 
          This, idGroup, lpData, dwDataSize, dwFlags, bAnsi );
 
   /* Parameter check */
@@ -2210,12 +2764,28 @@
   }
 
   /* Find the pointer to the data for this player */
-  if( ( lpGList = DP_FindAnyGroup( This, idGroup ) ) == NULL )
+  if( ( lpGData = DP_FindAnyGroup( This, idGroup ) ) == NULL )
   {
     return DPERR_INVALIDOBJECT;
   }
 
-  DP_SetGroupData( lpGList->lpGData, lpData, dwDataSize );
+  if( dwFlags & DPSET_REMOTE )
+  {
+    FIXME( "Was this group created by this interface?\n" );
+    /* FIXME: If this is a remote update need to allow it but not 
+     *        send a message.
+     */
+  }
+
+  DP_SetGroupData( lpGData, dwFlags, lpData, dwDataSize );
+
+  /* FIXME: Only send a message if this group is local to the session otherwise
+   * it will have been rejected above
+   */
+  if( dwFlags & DPSET_REMOTE )
+  {
+    FIXME( "Send msg?\n" );
+  }
 
   return DP_OK;
 }
@@ -2240,17 +2810,17 @@
           ( IDirectPlay2Impl* This, DPID idGroup, LPDPNAME lpGroupName, 
             DWORD dwFlags, BOOL bAnsi )
 {
-  lpGroupList lpGList;
+  lpGroupData lpGData;
 
   TRACE( "(%p)->(0x%08lx,%p,0x%08lx,%u)\n", This, idGroup, 
          lpGroupName, dwFlags, bAnsi );
 
-  if( ( lpGList = DP_FindAnyGroup( This, idGroup ) ) == NULL )
+  if( ( lpGData = DP_FindAnyGroup( This, idGroup ) ) == NULL )
   {
     return DPERR_INVALIDGROUP;
   }
 
-  DP_CopyDPNAMEStruct( &lpGList->lpGData->name, lpGroupName, bAnsi );
+  DP_CopyDPNAMEStruct( &lpGData->name, lpGroupName, bAnsi );
 
   /* Should send a DPMSG_SETPLAYERORGROUPNAME message */
   FIXME( "Message not sent and dwFlags ignored\n" );
@@ -2297,11 +2867,19 @@
     return DPERR_INVALIDPLAYER;
   }
 
-  DP_SetPlayerData( lpPList->lpPData, lpData, dwDataSize );
-
-  if( !(dwFlags & DPSET_LOCAL ) ) /* Is DPSET_REMOTE? */
+  if( dwFlags & DPSET_REMOTE )
   {
-    FIXME( "Change not propagated to all players in the session\n" );
+    FIXME( "Was this group created by this interface?\n" );
+    /* FIXME: If this is a remote update need to allow it but not 
+     *        send a message.
+     */
+  }
+
+  DP_SetPlayerData( lpPList->lpPData, dwFlags, lpData, dwDataSize );
+
+  if( dwFlags & DPSET_REMOTE )
+  {
+    FIXME( "Send msg?\n" );
   }
 
   return DP_OK;
@@ -2363,37 +2941,190 @@
   return DP_IF_SetPlayerName( This, idPlayer, lpPlayerName, dwFlags, FALSE );
 }
 
+static HRESULT WINAPI DP_SetSessionDesc
+          ( IDirectPlay2Impl* This, LPCDPSESSIONDESC2 lpSessDesc, 
+            DWORD dwFlags, BOOL bInitial, BOOL bAnsi  )
+{
+  DWORD            dwRequiredSize;
+  LPDPSESSIONDESC2 lpTempSessDesc;
+
+  TRACE( "(%p)->(%p,0x%08lx,%u,%u)\n", 
+         This, lpSessDesc, dwFlags, bInitial, bAnsi );
+
+  if( dwFlags )
+  {
+    return DPERR_INVALIDPARAMS;
+  }
+
+  /* Only the host is allowed to update the session desc */
+  if( !This->dp2->bHostInterface )
+  {
+    return DPERR_ACCESSDENIED;
+  }
+
+  /* FIXME: Copy into This->dp2->lpSessionDesc */
+  dwRequiredSize = DP_CalcSessionDescSize( lpSessDesc, bAnsi );
+  lpTempSessDesc = (LPDPSESSIONDESC2)HeapAlloc( GetProcessHeap(), 
+                                                HEAP_ZERO_MEMORY, 
+                                                dwRequiredSize );
+
+  if( lpTempSessDesc == NULL )
+  {
+    return DPERR_OUTOFMEMORY;
+  }
+
+  /* Free the old */
+  HeapFree( GetProcessHeap(), 0, This->dp2->lpSessionDesc );
+
+  This->dp2->lpSessionDesc = lpTempSessDesc;
+
+  /* Set the new */
+  DP_CopySessionDesc( This->dp2->lpSessionDesc, lpSessDesc, bAnsi );
+
+  /* If this is an external invocation of the interface, we should be 
+   * letting everyone know that things have changed. Otherwise this is
+   * just an initialization and it doesn't need to be propagated.
+   */
+  if( !bInitial )
+  {
+    FIXME( "Need to send a DPMSG_SETSESSIONDESC msg to everyone\n" ); 
+  }
+
+  return DP_OK;
+}
+
 static HRESULT WINAPI DirectPlay2AImpl_SetSessionDesc
           ( LPDIRECTPLAY2A iface, LPDPSESSIONDESC2 lpSessDesc, DWORD dwFlags )
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
-  FIXME("(%p)->(%p,0x%08lx): stub\n", This, lpSessDesc, dwFlags );
-  return DP_OK;
+  return DP_SetSessionDesc( This, lpSessDesc, dwFlags, FALSE, TRUE );
 }
 
 static HRESULT WINAPI DirectPlay2WImpl_SetSessionDesc
           ( LPDIRECTPLAY2 iface, LPDPSESSIONDESC2 lpSessDesc, DWORD dwFlags )
 {
   ICOM_THIS(IDirectPlay2Impl,iface);
-  FIXME("(%p)->(%p,0x%08lx): stub\n", This, lpSessDesc, dwFlags );
-  return DP_OK;
+  return DP_SetSessionDesc( This, lpSessDesc, dwFlags, FALSE, TRUE );
 }
 
+/* FIXME: See about merging some of this stuff with dplayx_global.c stuff */
+DWORD DP_CalcSessionDescSize( LPCDPSESSIONDESC2 lpSessDesc, BOOL bAnsi )
+{
+  DWORD dwSize = 0;
+
+  if( lpSessDesc == NULL )
+  {
+    /* Hmmm..don't need any size? */
+    ERR( "NULL lpSessDesc\n" );
+    return dwSize;
+  }
+
+  dwSize += sizeof( *lpSessDesc );
+
+  if( bAnsi )
+  {
+    if( lpSessDesc->sess.lpszSessionNameA )
+    {
+      dwSize += lstrlenA( lpSessDesc->sess.lpszSessionNameA ) + 1;
+    }
+
+    if( lpSessDesc->pass.lpszPasswordA )
+    {
+      dwSize += lstrlenA( lpSessDesc->pass.lpszPasswordA ) + 1;
+    }
+  }
+  else /* UNICODE */
+  {
+    if( lpSessDesc->sess.lpszSessionName )
+    {
+      dwSize += sizeof( WCHAR ) *
+        ( lstrlenW( lpSessDesc->sess.lpszSessionName ) + 1 );
+    }
+
+    if( lpSessDesc->pass.lpszPassword )
+    {
+      dwSize += sizeof( WCHAR ) *
+        ( lstrlenW( lpSessDesc->pass.lpszPassword ) + 1 );
+    }
+  }
+
+  return dwSize;
+}
+
+/* Assumes that contugous buffers are already allocated. */
+static void DP_CopySessionDesc( LPDPSESSIONDESC2 lpSessionDest, 
+                                LPCDPSESSIONDESC2 lpSessionSrc, BOOL bAnsi )
+{
+  BYTE* lpStartOfFreeSpace;
+
+  if( lpSessionDest == NULL )
+  {
+    ERR( "NULL lpSessionDest\n" );
+    return;
+  }
+
+  CopyMemory( lpSessionDest, lpSessionSrc, sizeof( *lpSessionSrc ) );
+
+  lpStartOfFreeSpace = ((BYTE*)lpSessionDest) + sizeof( *lpSessionSrc );
+
+  if( bAnsi )
+  {
+    if( lpSessionSrc->sess.lpszSessionNameA )
+    {
+      lstrcpyA( (LPSTR)lpStartOfFreeSpace, 
+                lpSessionDest->sess.lpszSessionNameA );
+      lpSessionDest->sess.lpszSessionNameA = (LPSTR)lpStartOfFreeSpace;
+      lpStartOfFreeSpace += 
+        lstrlenA( (LPSTR)lpSessionDest->sess.lpszSessionNameA ) + 1;
+    } 
+
+    if( lpSessionSrc->pass.lpszPasswordA )
+    {
+      lstrcpyA( (LPSTR)lpStartOfFreeSpace, 
+                lpSessionDest->pass.lpszPasswordA );
+      lpSessionDest->pass.lpszPasswordA = (LPSTR)lpStartOfFreeSpace;
+      lpStartOfFreeSpace += 
+        lstrlenA( (LPSTR)lpSessionDest->pass.lpszPasswordA ) + 1;
+    }  
+  }
+  else /* UNICODE */
+  {
+    if( lpSessionSrc->sess.lpszSessionName )
+    {
+      lstrcpyW( (LPWSTR)lpStartOfFreeSpace,
+                lpSessionDest->sess.lpszSessionName );
+      lpSessionDest->sess.lpszSessionName = (LPWSTR)lpStartOfFreeSpace;
+      lpStartOfFreeSpace += sizeof(WCHAR) *
+        ( lstrlenW( (LPWSTR)lpSessionDest->sess.lpszSessionName ) + 1 );
+    }
+
+    if( lpSessionSrc->pass.lpszPassword )
+    { 
+      lstrcpyW( (LPWSTR)lpStartOfFreeSpace, 
+                lpSessionDest->pass.lpszPassword );
+      lpSessionDest->pass.lpszPassword = (LPWSTR)lpStartOfFreeSpace;
+      lpStartOfFreeSpace += sizeof(WCHAR) *
+        ( lstrlenW( (LPWSTR)lpSessionDest->pass.lpszPassword ) + 1 );
+    }
+  }  
+}
+
+
 static HRESULT WINAPI DP_IF_AddGroupToGroup
           ( IDirectPlay3Impl* This, DPID idParentGroup, DPID idGroup )
 {
-  lpGroupList lpGParentList;
-  lpGroupList lpGList;
+  lpGroupData lpGParentData;
+  lpGroupData lpGData;
   lpGroupList lpNewGList;
 
   TRACE( "(%p)->(0x%08lx,0x%08lx)\n", This, idParentGroup, idGroup );
 
-  if( ( lpGParentList = DP_FindAnyGroup( (IDirectPlay2AImpl*)This, idParentGroup ) ) == NULL )
+  if( ( lpGParentData = DP_FindAnyGroup( (IDirectPlay2AImpl*)This, idParentGroup ) ) == NULL )
   {
     return DPERR_INVALIDGROUP;
   }
 
-  if( ( lpGList = DP_FindAnyGroup( (IDirectPlay2AImpl*)This, idGroup ) ) == NULL )
+  if( ( lpGData = DP_FindAnyGroup( (IDirectPlay2AImpl*)This, idGroup ) ) == NULL )
   {
     return DPERR_INVALIDGROUP;
   }
@@ -2407,10 +3138,10 @@
   }  
 
   /* Add the shortcut */
-  lpNewGList->lpGData = lpGList->lpGData;
+  lpNewGList->lpGData = lpGData;
 
   /* Add the player to the list of players for this group */
-  DPQ_INSERT( lpGList->lpGData->groups, lpNewGList, groups );
+  DPQ_INSERT( lpGData->groups, lpNewGList, groups );
 
   /* Send a ADDGROUPTOGROUP message */
   FIXME( "Not sending message\n" );
@@ -2433,20 +3164,20 @@
 }
 
 static HRESULT WINAPI DP_IF_CreateGroupInGroup
-          ( IDirectPlay3Impl* This, DPID idParentGroup, LPDPID lpidGroup, 
-            LPDPNAME lpGroupName, LPVOID lpData, DWORD dwDataSize, 
-            DWORD dwFlags )
+          ( IDirectPlay3Impl* This, LPVOID lpMsgHdr, DPID idParentGroup, 
+            LPDPID lpidGroup, LPDPNAME lpGroupName, LPVOID lpData, 
+            DWORD dwDataSize, DWORD dwFlags, BOOL bAnsi )
 {
-  lpGroupList lpGParentList;
+  lpGroupData lpGParentData;
   lpGroupList lpGList;
   lpGroupData lpGData;
 
-  TRACE( "(%p)->(0x%08lx,%p,%p,%p,0x%08lx,0x%08lx)\n", 
+  TRACE( "(%p)->(0x%08lx,%p,%p,%p,0x%08lx,0x%08lx,%u)\n", 
          This, idParentGroup, lpidGroup, lpGroupName, lpData, 
-         dwDataSize, dwFlags );
+         dwDataSize, dwFlags, bAnsi );
 
   /* Verify that the specified parent is valid */
-  if( ( lpGParentList = DP_FindAnyGroup( (IDirectPlay2AImpl*)This, 
+  if( ( lpGParentData = DP_FindAnyGroup( (IDirectPlay2AImpl*)This, 
                                          idParentGroup ) ) == NULL 
     )
   {
@@ -2454,14 +3185,14 @@
   } 
 
   lpGData = DP_CreateGroup( (IDirectPlay2AImpl*)This, lpidGroup, lpGroupName, 
-                            lpGParentList->lpGData, TRUE /* Ansi */ );
+                            dwFlags, idParentGroup, bAnsi );
 
   if( lpGData == NULL )
   {
     return DPERR_CANTADDPLAYER; /* yes player not group */
   }
   
-  DP_SetGroupData( lpGData, lpData, dwDataSize );
+  DP_SetGroupData( lpGData, DPSET_REMOTE, lpData, dwDataSize );
 
   /* The list has now been inserted into the interface group list. We now
      need to put a "shortcut" to this group in the parent group */
@@ -2475,13 +3206,46 @@
 
   lpGList->lpGData = lpGData; 
 
-  DPQ_INSERT( lpGParentList->lpGData->groups, lpGList, groups );
- 
+  DPQ_INSERT( lpGParentData->groups, lpGList, groups );
 
-  /* FIXME: Should send DPMSG_CREATEPLAYERORGROUP message to everyone,
-            local and remote, that belongs to this session. This will not
-            be done by calling SetPlayerData */
-  FIXME( "Should broadcast group creation to everything in session\n" );
+  /* Let the SP know that we've created this group */
+  if( This->dp2->spData.lpCB->CreateGroup )
+  {
+    DPSP_CREATEGROUPDATA data;
+
+    TRACE( "Calling SP CreateGroup\n" );
+
+    data.idGroup           = *lpidGroup;
+    data.dwFlags           = dwFlags;
+    data.lpSPMessageHeader = lpMsgHdr;
+    data.lpISP             = This->dp2->spData.lpISP;
+
+    (This->dp2->spData.lpCB->CreateGroup)( &data );
+  }
+
+  /* Inform all other peers of the creation of a new group. If there are 
+   * no peers keep this quiet. 
+   */
+  if( This->dp2->lpSessionDesc && 
+      ( This->dp2->lpSessionDesc->dwFlags & DPSESSION_MULTICASTSERVER ) )
+  {
+    DPMSG_CREATEPLAYERORGROUP msg;
+
+    msg.dwType = DPSYS_CREATEPLAYERORGROUP;
+    msg.dwPlayerType = DPPLAYERTYPE_GROUP;
+    msg.dpId = *lpidGroup;
+    msg.dwCurrentPlayers = idParentGroup; /* FIXME: Incorrect? */
+    msg.lpData = lpData;
+    msg.dwDataSize = dwDataSize;
+    msg.dpnName = *lpGroupName;
+
+    /* FIXME: Correct to just use send effectively? */
+    /* FIXME: Should size include data w/ message or just message "header" */
+    /* FIXME: Check return code */
+    DP_SendEx( (IDirectPlay2Impl*)This, 
+               DPID_SERVERPLAYER, DPID_ALLPLAYERS, 0, &msg, sizeof( msg ),
+               0, 0, NULL, NULL, bAnsi );
+  }
 
   return DP_OK;
 }
@@ -2492,8 +3256,12 @@
             DWORD dwFlags )
 {
   ICOM_THIS(IDirectPlay3Impl,iface);
-  return DP_IF_CreateGroupInGroup( This, idParentGroup, lpidGroup, lpGroupName,
-                                   lpData, dwDataSize, dwFlags );
+
+  *lpidGroup = DPID_UNKNOWN;
+
+  return DP_IF_CreateGroupInGroup( This, NULL, idParentGroup, lpidGroup, 
+                                   lpGroupName, lpData, dwDataSize, dwFlags, 
+                                   TRUE );
 }
 
 static HRESULT WINAPI DirectPlay3WImpl_CreateGroupInGroup
@@ -2502,26 +3270,30 @@
             DWORD dwFlags )
 {
   ICOM_THIS(IDirectPlay3Impl,iface);
-  return DP_IF_CreateGroupInGroup( This, idParentGroup, lpidGroup, lpGroupName,
-                                   lpData, dwDataSize, dwFlags );
+
+  *lpidGroup = DPID_UNKNOWN;
+
+  return DP_IF_CreateGroupInGroup( This, NULL, idParentGroup, lpidGroup, 
+                                   lpGroupName, lpData, dwDataSize, 
+                                   dwFlags, FALSE );
 }
 
 static HRESULT WINAPI DP_IF_DeleteGroupFromGroup
           ( IDirectPlay3Impl* This, DPID idParentGroup, DPID idGroup )
 {
   lpGroupList lpGList;
-  lpGroupList lpGParentList;
+  lpGroupData lpGParentData;
 
   TRACE("(%p)->(0x%08lx,0x%08lx)\n", This, idParentGroup, idGroup );
 
   /* Is the parent group valid? */
-  if( ( lpGParentList = DP_FindAnyGroup( (IDirectPlay2AImpl*)This, idParentGroup ) ) == NULL )
+  if( ( lpGParentData = DP_FindAnyGroup( (IDirectPlay2AImpl*)This, idParentGroup ) ) == NULL )
   {
     return DPERR_INVALIDGROUP;
   } 
 
   /* Remove the group from the parent group queue */
-  DPQ_REMOVE_ENTRY( lpGParentList->lpGData->groups, groups, lpGData->dpid, idGroup, lpGList );
+  DPQ_REMOVE_ENTRY( lpGParentData->groups, groups, lpGData->dpid, ==, idGroup, lpGList );
 
   if( lpGList == NULL )
   {
@@ -2650,8 +3422,9 @@
                allocating an 80 byte buffer which isn't even a filled with a valid compound 
                address. Oh well. Creating a proper compound address is the way to go anyways 
                despite this method taking slightly more heap space and realtime :) */
-      dpCompoundAddress.guidDataType = DPAID_ServiceProvider;
       dpCompoundAddress.dwDataSize   = sizeof( GUID );
+      memcpy( &dpCompoundAddress.guidDataType, &DPAID_ServiceProvider, 
+              sizeof( GUID ) ) ;
       dpCompoundAddress.lpData       = &serviceProviderGUID; 
 
       if( ( hr = DPL_CreateCompoundAddress( &dpCompoundAddress, 1, lpAddressBuffer, 
@@ -2675,8 +3448,6 @@
       if( !lpEnumCallback( &serviceProviderGUID, lpAddressBuffer, dwAddressBufferSize, 
                            &dpName, DPCONNECTION_DIRECTPLAY, lpContext ) )
       {
-         WARN("lpEnumCallback returning FALSE\n" );
-
          return DP_OK;
       }
     }
@@ -2702,7 +3473,7 @@
     }
 
 
-    /* Traverse all the service providers we have available */
+    /* Traverse all the lobby providers we have available */
     for( dwIndex=0;
          RegEnumKeyExA( hkResult, dwIndex, subKeyName, &sizeOfSubKeyName, 
                         NULL, NULL, NULL, &filetime ) != ERROR_NO_MORE_ITEMS;
@@ -2757,7 +3528,7 @@
                allocating an 80 byte buffer which isn't even a filled with a valid compound 
                address. Oh well. Creating a proper compound address is the way to go anyways 
                despite this method taking slightly more heap space and realtime :) */
-      dpCompoundAddress.guidDataType = DPAID_ServiceProvider;
+      dpCompoundAddress.guidDataType = DPAID_LobbyProvider;
       dpCompoundAddress.dwDataSize   = sizeof( GUID );
       dpCompoundAddress.lpData       = &serviceProviderGUID; 
 
@@ -2780,10 +3551,8 @@
 
       /* The enumeration will return FALSE if we are not to continue */
       if( !lpEnumCallback( &serviceProviderGUID, lpAddressBuffer, dwAddressBufferSize,
-                           &dpName, DPCONNECTION_DIRECTPLAY, lpContext ) )
+                           &dpName, DPCONNECTION_DIRECTPLAYLOBBY, lpContext ) )
       {
-         WARN("lpEnumCallback returning FALSE\n" );
-
          return DP_OK;
       }
     }
@@ -2800,48 +3569,73 @@
   return DP_OK;
 }
 
-static HRESULT WINAPI DirectPlay3AImpl_EnumGroupsInGroup
-          ( LPDIRECTPLAY3A iface, DPID idGroup, LPGUID lpguidInstance, LPDPENUMPLAYERSCALLBACK2 lpEnumPlayersCallback2, LPVOID lpContext, DWORD dwFlags )
+static HRESULT WINAPI DP_IF_EnumGroupsInGroup
+          ( IDirectPlay3AImpl* This, DPID idGroup, LPGUID lpguidInstance,
+            LPDPENUMPLAYERSCALLBACK2 lpEnumPlayersCallback2, 
+            LPVOID lpContext, DWORD dwFlags, BOOL bAnsi )
 {
   lpGroupList lpGList;
-  lpGroupList lpGiGList;
-  ICOM_THIS(IDirectPlay3AImpl,iface);
+  lpGroupData lpGData;
 
-  FIXME("(%p)->(0x%08lx,%p,%p,%p,0x%08lx): semi stub\n", This, idGroup, lpguidInstance, lpEnumPlayersCallback2, lpContext, dwFlags );
+  FIXME( "(%p)->(0x%08lx,%p,%p,%p,0x%08lx,%u): semi stub\n", 
+         This, idGroup, lpguidInstance, lpEnumPlayersCallback2,  
+         lpContext, dwFlags, bAnsi );
 
-  if( ( lpGList = DP_FindAnyGroup( (IDirectPlay2AImpl*)This, idGroup ) ) == NULL ) 
+  if( ( lpGData = DP_FindAnyGroup( (IDirectPlay2AImpl*)This, idGroup ) ) == NULL ) 
   {
     return DPERR_INVALIDGROUP;
   }
 
-  lpGiGList = lpGList->lpGData->groups.lpQHFirst;
-
-  while( lpGiGList )
+  if( DPQ_IS_EMPTY( lpGData->groups ) )
   {
-     /* FIXME: Should check dwFlags for match here */
+    return DP_OK;
+  }
 
-     if( !(*lpEnumPlayersCallback2)( lpGList->lpGData->dpid, DPPLAYERTYPE_GROUP,
-                                     &lpGList->lpGData->name, dwFlags,
-                                     lpContext ) )
-     {
-       return DP_OK; /* User requested break */
-     }
+  lpGList = DPQ_FIRST( lpGData->groups );
 
-     if( ( lpGiGList = lpGiGList->groups.lpQNext ) == lpGList->lpGData->groups.lpQHFirst )
-     {
-        return DP_OK; /* End of groups */
-     }
+  for( ;; )
+  {
+    /* FIXME: Should check dwFlags for match here */
+
+    if( !(*lpEnumPlayersCallback2)( lpGList->lpGData->dpid, DPPLAYERTYPE_GROUP,
+                                    &lpGList->lpGData->name, dwFlags,
+                                    lpContext ) )
+    {
+      return DP_OK; /* User requested break */
+    }
+
+    if( DPQ_IS_ENDOFLIST( lpGList->groups ) )
+    {
+      break;
+    }
+
+    lpGList = DPQ_NEXT( lpGList->groups );
+
   }
 
   return DP_OK;
 }
 
-static HRESULT WINAPI DirectPlay3WImpl_EnumGroupsInGroup
-          ( LPDIRECTPLAY3 iface, DPID idGroup, LPGUID lpguidInstance, LPDPENUMPLAYERSCALLBACK2 lpEnumPlayersCallback2, LPVOID lpContext, DWORD dwFlags )
+static HRESULT WINAPI DirectPlay3AImpl_EnumGroupsInGroup
+          ( LPDIRECTPLAY3A iface, DPID idGroup, LPGUID lpguidInstance, 
+            LPDPENUMPLAYERSCALLBACK2 lpEnumPlayersCallback2, LPVOID lpContext, 
+            DWORD dwFlags )
 {
   ICOM_THIS(IDirectPlay3Impl,iface);
-  FIXME("(%p)->(0x%08lx,%p,%p,%p,0x%08lx): stub\n", This, idGroup, lpguidInstance, lpEnumPlayersCallback2, lpContext, dwFlags );
-  return DP_OK;
+  return DP_IF_EnumGroupsInGroup( This, idGroup, lpguidInstance, 
+                                  lpEnumPlayersCallback2, lpContext, dwFlags,
+                                  TRUE );
+}
+
+static HRESULT WINAPI DirectPlay3WImpl_EnumGroupsInGroup
+          ( LPDIRECTPLAY3A iface, DPID idGroup, LPGUID lpguidInstance,
+            LPDPENUMPLAYERSCALLBACK2 lpEnumPlayersCallback2, LPVOID lpContext,
+            DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+  return DP_IF_EnumGroupsInGroup( This, idGroup, lpguidInstance,
+                                  lpEnumPlayersCallback2, lpContext, dwFlags,
+                                  FALSE );
 }
 
 static HRESULT WINAPI DirectPlay3AImpl_GetGroupConnectionSettings
@@ -2860,33 +3654,204 @@
   return DP_OK;
 }
 
+BOOL CALLBACK DP_GetSpLpGuidFromCompoundAddress(
+    REFGUID         guidDataType,
+    DWORD           dwDataSize,
+    LPCVOID         lpData,
+    LPVOID          lpContext )
+{
+  /* Looking for the GUID of the provider to load */
+  if( ( IsEqualGUID( guidDataType, &DPAID_ServiceProvider ) ) ||
+      ( IsEqualGUID( guidDataType, &DPAID_LobbyProvider ) )
+    )
+  {
+    TRACE( "Found SP/LP (%s) %s (data size = 0x%08lx)\n", 
+           debugstr_guid( guidDataType ), debugstr_guid( lpData ), dwDataSize );
+
+    if( dwDataSize != sizeof( GUID ) )
+    {
+      ERR( "Invalid sp/lp guid size 0x%08lx\n", dwDataSize );
+    }
+
+    memcpy( lpContext, lpData, dwDataSize );
+
+    /* There shouldn't be more than 1 GUID/compound address */
+    return FALSE;
+  }
+  
+  /* Still waiting for what we want */
+  return TRUE;
+}
+
+
+/* Find and perform a LoadLibrary on the requested SP or LP GUID */
+static HMODULE DP_LoadSP( LPCGUID lpcGuid, LPSPINITDATA lpSpData )
+{
+  UINT i; 
+  LPCSTR spSubKey         = "SOFTWARE\\Microsoft\\DirectPlay\\Service Providers";
+  LPCSTR lpSubKey         = "SOFTWARE\\Microsoft\\DirectPlay\\Lobby Providers"; 
+  LPCSTR guidDataSubKey   = "Guid";
+  LPCSTR majVerDataSubKey = "dwReserved1";
+  LPCSTR minVerDataSubKey = "dwReserved2";
+  LPCSTR pathSubKey       = "Path";
+
+  TRACE( " request to load %s\n", debugstr_guid( lpcGuid ) );
+
+  /* FIXME: Cloned code with a quick hack. */
+  for( i=0; i<2; i++ )
+  {
+    HKEY hkResult;
+    LPCSTR searchSubKey;
+    char subKeyName[51];
+    DWORD dwIndex, sizeOfSubKeyName=50;
+    FILETIME filetime;
+
+    (i == 0) ? (searchSubKey = spSubKey ) : (searchSubKey = lpSubKey );
+    
+
+    /* Need to loop over the service providers in the registry */
+    if( RegOpenKeyExA( HKEY_LOCAL_MACHINE, searchSubKey,
+                         0, KEY_READ, &hkResult ) != ERROR_SUCCESS )
+    {
+      /* Hmmm. Does this mean that there are no service providers? */
+      ERR(": no service providers?\n");
+      return 0;
+    }
+
+    /* Traverse all the service providers we have available */
+    for( dwIndex=0;
+         RegEnumKeyExA( hkResult, dwIndex, subKeyName, &sizeOfSubKeyName,
+                        NULL, NULL, NULL, &filetime ) != ERROR_NO_MORE_ITEMS;
+         ++dwIndex, sizeOfSubKeyName=51 )
+    {
+
+      HKEY     hkServiceProvider;
+      GUID     serviceProviderGUID;
+      DWORD    returnType, sizeOfReturnBuffer = 255;
+      char     returnBuffer[256];
+      LPWSTR   lpWGUIDString;
+      DWORD    dwTemp;
+
+      TRACE(" this time through: %s\n", subKeyName );
+
+      /* Get a handle for this particular service provider */
+      if( RegOpenKeyExA( hkResult, subKeyName, 0, KEY_READ,
+                         &hkServiceProvider ) != ERROR_SUCCESS )
+      {
+         ERR(": what the heck is going on?\n" );
+         continue;
+      }
+
+      if( RegQueryValueExA( hkServiceProvider, guidDataSubKey,
+                            NULL, &returnType, returnBuffer,
+                            &sizeOfReturnBuffer ) != ERROR_SUCCESS )
+      {
+        ERR(": missing GUID registry data members\n" );
+        continue;
+      }
+
+      /* FIXME: Check return types to ensure we're interpreting data right */
+      lpWGUIDString = HEAP_strdupAtoW( GetProcessHeap(), 0, returnBuffer );
+      CLSIDFromString( (LPCOLESTR)lpWGUIDString, &serviceProviderGUID );
+      HeapFree( GetProcessHeap(), 0, lpWGUIDString );
+      /* FIXME: Have I got a memory leak on the serviceProviderGUID? */
+
+      /* Determine if this is the Service Provider that the user asked for */
+      if( !IsEqualGUID( &serviceProviderGUID, lpcGuid ) )
+      {
+        continue;
+      }
+
+      /* Save the name of the SP or LP */
+      lpSpData->lpszName = HEAP_strdupAtoW( GetProcessHeap(), 0, subKeyName );
+
+      sizeOfReturnBuffer = 255;
+
+      /* Get dwReserved1 */
+      if( RegQueryValueExA( hkServiceProvider, majVerDataSubKey,
+                            NULL, &returnType, returnBuffer,
+                            &sizeOfReturnBuffer ) != ERROR_SUCCESS )
+      {
+         ERR(": missing dwReserved1 registry data members\n") ;
+         continue;
+      }
+
+      lpSpData->dwReserved1 = GET_DWORD( returnBuffer );
+ 
+      sizeOfReturnBuffer = 255;
+
+      /* Get dwReserved2 */
+      if( RegQueryValueExA( hkServiceProvider, minVerDataSubKey,
+                            NULL, &returnType, returnBuffer,
+                            &sizeOfReturnBuffer ) != ERROR_SUCCESS )
+      {
+         ERR(": missing dwReserved1 registry data members\n") ;
+         continue;
+      }
+
+      lpSpData->dwReserved2 = GET_DWORD( returnBuffer );
+
+
+      sizeOfReturnBuffer = 255;
+   
+      /* Get the path for this service provider */
+      if( ( dwTemp = RegQueryValueExA( hkServiceProvider, pathSubKey,
+                            NULL, NULL, returnBuffer,
+                            &sizeOfReturnBuffer ) ) != ERROR_SUCCESS )
+      {
+        ERR(": missing PATH registry data members: 0x%08lx\n", dwTemp );
+        continue;
+      }
+
+      return LoadLibraryA( returnBuffer );
+    }
+  }
+
+  return 0;
+}
+
 static HRESULT WINAPI DirectPlay3AImpl_InitializeConnection
           ( LPDIRECTPLAY3A iface, LPVOID lpConnection, DWORD dwFlags )
 {
   HMODULE hServiceProvider;
-  /*DWORD   dwReturnValue; */
-  typedef DWORD (WINAPI *SP_SPInit)(LPVOID, LPVOID, LPVOID ); /* FIXME: How many arguments? */
-  SP_SPInit SPInit;
+  HRESULT hr;
+  LPDPSP_SPINIT SPInit;
+  GUID guidSP;
+  DWORD dwAddrSize = 80; /* FIXME: Need to calculate it correctly */
 
   ICOM_THIS(IDirectPlay3Impl,iface);
 
-  FIXME("(%p)->(%p,0x%08lx): stub\n", This, lpConnection, dwFlags );
+  TRACE("(%p)->(%p,0x%08lx)\n", This, lpConnection, dwFlags );
 
   if( dwFlags != 0 )
   {
     return DPERR_INVALIDFLAGS;
   }
 
-  if( This->dp3->bConnectionInitialized == TRUE )
+  if( This->dp2->bConnectionInitialized == TRUE )
   {
     return DPERR_ALREADYINITIALIZED;
   }
 
-  /* Parse lpConnection as a compound address for the service provider */
-  /* Take service provider GUID and find the path to it */
+  /* Find out what the requested SP is and how large this buffer is */
+  hr = DPL_EnumAddress( DP_GetSpLpGuidFromCompoundAddress, lpConnection, 
+                        dwAddrSize, &guidSP );
 
-  /* FIXME: Hard coded to only load the tcp/ip service provider for now... */
-  hServiceProvider = LoadLibraryA( "dpwsockx.dll" );
+  if( FAILED(hr) )
+  {
+    ERR( "Invalid compound address?\n" );
+    return DPERR_UNAVAILABLE;
+  }
+
+  /* Initialize what we can of the Service Provider required information.
+   * The rest will be done in DP_LoadSP
+   */
+  This->dp2->spData.lpAddress = lpConnection;
+  This->dp2->spData.dwAddressSize = dwAddrSize;
+  This->dp2->spData.lpGuid = &guidSP;
+
+  /* Load the service provider */
+  hServiceProvider = DP_LoadSP( &guidSP, &This->dp2->spData );
 
   if( hServiceProvider == 0 )
   {
@@ -2895,23 +3860,33 @@
   }
   
   /* Initialize the service provider by calling SPInit */
-  SPInit = (SP_SPInit)GetProcAddress( hServiceProvider, "SPInit" );
+  SPInit = (LPDPSP_SPINIT)GetProcAddress( hServiceProvider, "SPInit" );
 
   if( SPInit == NULL )
   {
     ERR( "Service provider doesn't provide SPInit interface?\n" );
+    FreeLibrary( hServiceProvider );
+    return DPERR_UNAVAILABLE;
   }  
 
-#if 0
   /* NOTE: This will crash until I know what parameters/interface this has */
-  /* FIXME: Take a guess that we just pass the compound address to the SP */
-  /* Hmmm...how to say which parameters need to be gotten from the SP. They must
-     come from the compound address, but how do we communicate what's required? */
-  dwReturnValue = (*SPInit)( lpConnection, NULL, NULL );
-#endif
+  /* The first parameter is a pointer to memory which is written to... */
+
+  TRACE( "Calling SPInit\n" );
+  hr = (*SPInit)( &This->dp2->spData );
+
+  if( FAILED(hr) )
+  {
+    ERR( "SP Initialization failed: %s\n", DPLAYX_HresultToString(hr) );
+    FreeLibrary( hServiceProvider );
+    return hr;
+  }
 
   /* This interface is now initialized */
-  This->dp3->bConnectionInitialized = TRUE;
+  This->dp2->bConnectionInitialized = TRUE;
+
+  /* Store the handle of the module so that we can unload it later */
+  This->dp2->hServiceProvider = hServiceProvider;
 
   return DP_OK;
 }
@@ -2929,7 +3904,7 @@
             LPCDPSECURITYDESC lpSecurity, LPCDPCREDENTIALS lpCredentials )
 {
   ICOM_THIS(IDirectPlay2Impl,iface); /* Yes a dp 2 interface */
-  return DP_SecureOpen( This, lpsd, dwFlags, lpSecurity, lpCredentials );
+  return DP_SecureOpen( This, lpsd, dwFlags, lpSecurity, lpCredentials, TRUE );
 }
 
 static HRESULT WINAPI DirectPlay3WImpl_SecureOpen
@@ -2937,7 +3912,7 @@
             LPCDPSECURITYDESC lpSecurity, LPCDPCREDENTIALS lpCredentials )
 {   
   ICOM_THIS(IDirectPlay2Impl,iface); /* Yes a dp 2 interface */
-  return DP_SecureOpen( This, lpsd, dwFlags, lpSecurity, lpCredentials );
+  return DP_SecureOpen( This, lpsd, dwFlags, lpSecurity, lpCredentials, FALSE );
 }
 
 static HRESULT WINAPI DirectPlay3AImpl_SendChatMessage
@@ -3004,31 +3979,35 @@
   return DP_OK;
 }
 
-static HRESULT WINAPI DirectPlay3AImpl_GetGroupParent
-          ( LPDIRECTPLAY3A iface, DPID idGroup, LPDPID lpidGroup )
+static HRESULT WINAPI DP_IF_GetGroupParent
+          ( IDirectPlay3AImpl* This, DPID idGroup, LPDPID lpidGroup, 
+            BOOL bAnsi )
 {
-  lpGroupList lpGList;
+  lpGroupData lpGData;
 
-  ICOM_THIS(IDirectPlay3AImpl,iface);
+  TRACE("(%p)->(0x%08lx,%p,%u)\n", This, idGroup, lpidGroup, bAnsi );
 
-  TRACE("(%p)->(0x%08lx,%p)\n", This, idGroup, lpidGroup );
-
-  if( ( lpGList = DP_FindAnyGroup( (IDirectPlay2AImpl*)This, idGroup ) ) == NULL )
+  if( ( lpGData = DP_FindAnyGroup( (IDirectPlay2AImpl*)This, idGroup ) ) == NULL )
   {
     return DPERR_INVALIDGROUP;
   }
 
-  *lpidGroup = lpGList->lpGData->dpid;
+  *lpidGroup = lpGData->dpid;
 
   return DP_OK;
 }
 
+static HRESULT WINAPI DirectPlay3AImpl_GetGroupParent
+          ( LPDIRECTPLAY3A iface, DPID idGroup, LPDPID lpidGroup )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+  return DP_IF_GetGroupParent( This, idGroup, lpidGroup, TRUE ); 
+}
 static HRESULT WINAPI DirectPlay3WImpl_GetGroupParent
           ( LPDIRECTPLAY3 iface, DPID idGroup, LPDPID lpidGroup )
 {
   ICOM_THIS(IDirectPlay3Impl,iface);
-  FIXME("(%p)->(0x%08lx,%p): stub\n", This, idGroup, lpidGroup );
-  return DP_OK;
+  return DP_IF_GetGroupParent( This, idGroup, lpidGroup, FALSE ); 
 }
 
 static HRESULT WINAPI DirectPlay3AImpl_GetPlayerAccount
@@ -3095,68 +4074,324 @@
   return DP_OK;
 }
 
-static HRESULT WINAPI DirectPlay4AImpl_SendEx
-          ( LPDIRECTPLAY4A iface, DPID idFrom, DPID idTo, DWORD dwFlags, LPVOID lpData, DWORD dwDataSize, DWORD dwPriority, DWORD dwTimeout, LPVOID lpContext, LPDWORD lpdwMsgID )
+static HRESULT WINAPI DP_SendEx
+          ( IDirectPlay2Impl* This, DPID idFrom, DPID idTo, DWORD dwFlags, 
+            LPVOID lpData, DWORD dwDataSize, DWORD dwPriority, DWORD dwTimeout,
+            LPVOID lpContext, LPDWORD lpdwMsgID, BOOL bAnsi )
 {
-  ICOM_THIS(IDirectPlay4Impl,iface);
-  FIXME("(%p)->(0x%08lx,0x%08lx,0x%08lx,%p,0x%08lx,0x%08lx,0x%08lx,%p,%p): stub\n", This, idFrom, idTo, dwFlags, lpData, dwDataSize, dwPriority, dwTimeout, lpContext, lpdwMsgID );
-  return DP_OK;
+  lpPlayerList lpPList;
+  lpGroupData  lpGData;
+  BOOL         bValidDestination = FALSE;
+
+  FIXME( "(%p)->(0x%08lx,0x%08lx,0x%08lx,%p,0x%08lx,0x%08lx,0x%08lx,%p,%p,%u)"
+         ": stub\n",
+         This, idFrom, idTo, dwFlags, lpData, dwDataSize, dwPriority,
+         dwTimeout, lpContext, lpdwMsgID, bAnsi );
+
+  /* FIXME: Add parameter checking */
+  /* FIXME: First call to this needs to aquire a message id which will be
+   *        used for multiple sends
+   */
+
+  /* NOTE: Can't send messages to yourself - this will be trapped in receive */
+
+  /* Verify that the message is being sent from a valid local player. The
+   * from player may be anonymous DPID_UNKNOWN
+   */
+  if( idFrom != DPID_UNKNOWN )
+  {
+    if( ( lpPList = DP_FindPlayer( This, idFrom ) ) == NULL )
+    {
+      WARN( "INFO: Invalid from player 0x%08lx\n", idFrom );
+      return DPERR_INVALIDPLAYER;
+    }
+  }
+
+  /* Verify that the message is being sent to a valid player, group or to
+   * everyone. If it's valid, send it to those players.
+   */
+  if( idTo == DPID_ALLPLAYERS )
+  {
+    bValidDestination = TRUE;  
+
+    /* See if SP has the ability to multicast. If so, use it */
+    if( This->dp2->spData.lpCB->SendToGroupEx )
+    {
+      FIXME( "Use group sendex to group 0\n" );
+    }
+    else if( This->dp2->spData.lpCB->SendToGroup ) /* obsolete interface */
+    {
+      FIXME( "Use obsolete group send to group 0\n" );
+    }
+    else /* No multicast, multiplicate */
+    {
+      /* Send to all players we know about */
+      FIXME( "Send to all players using EnumPlayersInGroup\n" );
+    }
+  }
+
+  if( ( !bValidDestination ) && 
+      ( DP_FindPlayer( This, idTo ) != NULL )
+    )
+  {
+    bValidDestination = TRUE;
+
+    /* Have the servie provider send this message */
+    /* FIXME: Could optimize for local interface sends */
+    return DP_SP_SendEx( This, dwFlags, lpData, dwDataSize, dwPriority,
+                         dwTimeout, lpContext, lpdwMsgID );
+  }
+
+  if( ( !bValidDestination ) &&
+      ( ( lpGData = DP_FindAnyGroup( This, idTo ) ) != NULL )
+    )
+  {
+    bValidDestination = TRUE;
+
+    /* See if SP has the ability to multicast. If so, use it */
+    if( This->dp2->spData.lpCB->SendToGroupEx )
+    {
+      FIXME( "Use group sendex\n" );
+    }
+    else if( This->dp2->spData.lpCB->SendToGroup ) /* obsolete interface */
+    {
+      FIXME( "Use obsolete group send to group\n" );
+    }
+    else /* No multicast, multiplicate */
+    {
+      FIXME( "Send to all players using EnumPlayersInGroup\n" );
+    }
+  }
+
+  if( !bValidDestination )
+  {
+    return DPERR_INVALIDPLAYER;
+  }
+  else
+  {
+    /* FIXME: Should return what the send returned */
+    return DP_OK;
+  }
+}
+
+
+static HRESULT WINAPI DirectPlay4AImpl_SendEx
+          ( LPDIRECTPLAY4A iface, DPID idFrom, DPID idTo, DWORD dwFlags, 
+            LPVOID lpData, DWORD dwDataSize, DWORD dwPriority, DWORD dwTimeout,
+            LPVOID lpContext, LPDWORD lpdwMsgID )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface); /* yes downcast to 2 */
+  return DP_SendEx( This, idFrom, idTo, dwFlags, lpData, dwDataSize, 
+                    dwPriority, dwTimeout, lpContext, lpdwMsgID, TRUE );
 }
 
 static HRESULT WINAPI DirectPlay4WImpl_SendEx
-          ( LPDIRECTPLAY4 iface, DPID idFrom, DPID idTo, DWORD dwFlags, LPVOID lpData, DWORD dwDataSize, DWORD dwPriority, DWORD dwTimeout, LPVOID lpContext, LPDWORD lpdwMsgID )
+          ( LPDIRECTPLAY4 iface, DPID idFrom, DPID idTo, DWORD dwFlags, 
+            LPVOID lpData, DWORD dwDataSize, DWORD dwPriority, DWORD dwTimeout,
+            LPVOID lpContext, LPDWORD lpdwMsgID )
 {
-  ICOM_THIS(IDirectPlay4Impl,iface);
-  FIXME("(%p)->(0x%08lx,0x%08lx,0x%08lx,%p,0x%08lx,0x%08lx,0x%08lx,%p,%p): stub\n", This, idFrom, idTo, dwFlags, lpData, dwDataSize, dwPriority, dwTimeout, lpContext, lpdwMsgID );
+  ICOM_THIS(IDirectPlay2Impl,iface); /* yes downcast to 2 */
+  return DP_SendEx( This, idFrom, idTo, dwFlags, lpData, dwDataSize, 
+                    dwPriority, dwTimeout, lpContext, lpdwMsgID, FALSE );
+}
+
+static HRESULT WINAPI DP_SP_SendEx
+          ( IDirectPlay2Impl* This, DWORD dwFlags,
+            LPVOID lpData, DWORD dwDataSize, DWORD dwPriority, DWORD dwTimeout,
+            LPVOID lpContext, LPDWORD lpdwMsgID )
+{
+  LPDPMSG lpMElem;
+
+  FIXME( ": stub - wont work for multi process\n" );
+
+  /* FIXME: Need to send the passed message inside another message type
+   *        I think. System message - but what is the format? 
+   */
+
+  /* OK. Let's run a nasty hack to bypass the fact that we don't yet know
+     how to initialize the service provider. The hack will place the message
+     to be sent automatically into the send queue. There will
+     be nothing put into the send queue. In reality we should be calling
+     the service provider and only putting things into the send queue if
+     the service provider can't immediately accept/send it. 
+     NOTE: Two hacks are provided. The first is only for a single process,
+           the second is a start towards a allowing sharing across the whole
+           computer (it requires some way of signalling that there's a message
+           available).
+   */
+
+#ifdef SP_MULTIPROCESS_SEND_HACK
+  lpMElem = (LPDPMSG)DPLAYX_PrivHeapAlloc( HEAP_ZERO_MEMORY,
+                                           sizeof( *lpMElem ) );
+  lpMElem->msg = (DPMSG_GENERIC*)DPLAYX_PrivHeapAlloc( HEAP_ZERO_MEMORY,
+                                                       dwDataSize );
+#else
+  lpMElem = (LPDPMSG)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                               sizeof( *lpMElem ) );
+  lpMElem->msg = (DPMSG_GENERIC*)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 
+                                            dwDataSize );
+#endif
+
+  CopyMemory( lpMElem->msg, lpData, dwDataSize );
+
+  DPQ_INSERT( This->dp2->sendMsgs, lpMElem, msgs ); 
+   
   return DP_OK;
 }
 
+static HRESULT WINAPI DP_IF_GetMessageQueue
+          ( IDirectPlay4Impl* This, DPID idFrom, DPID idTo, DWORD dwFlags, 
+            LPDWORD lpdwNumMsgs, LPDWORD lpdwNumBytes, BOOL bAnsi )
+{
+  HRESULT hr = DP_OK;
+
+  FIXME( "(%p)->(0x%08lx,0x%08lx,0x%08lx,%p,%p,%u): semi stub\n", 
+         This, idFrom, idTo, dwFlags, lpdwNumMsgs, lpdwNumBytes, bAnsi );
+
+  /* FIXME: Do we need to do idFrom and idTo sanity checking here? */
+  /* FIXME: What about sends which are not immediate? */
+
+  if( This->dp2->spData.lpCB->GetMessageQueue )
+  {
+    DPSP_GETMESSAGEQUEUEDATA data;
+
+    FIXME( "Calling SP GetMessageQueue - is it right?\n" );
+
+    /* FIXME: None of this is documented :( */
+
+    data.lpISP        = This->dp2->spData.lpISP;
+    data.dwFlags      = dwFlags; 
+    data.idFrom       = idFrom;
+    data.idTo         = idTo;
+    data.lpdwNumMsgs  = lpdwNumMsgs;
+    data.lpdwNumBytes = lpdwNumBytes;
+  
+    hr = (This->dp2->spData.lpCB->GetMessageQueue)( &data );
+  }
+  else
+  {
+    FIXME( "No SP for GetMessageQueue - fake some data\n" );
+  }
+
+  return hr;
+}
+
 static HRESULT WINAPI DirectPlay4AImpl_GetMessageQueue
-          ( LPDIRECTPLAY4A iface, DPID idFrom, DPID idTo, DWORD dwFlags, LPDWORD lpdwNumMsgs, LPDWORD lpdwNumBytes )
+          ( LPDIRECTPLAY4A iface, DPID idFrom, DPID idTo, DWORD dwFlags, 
+            LPDWORD lpdwNumMsgs, LPDWORD lpdwNumBytes )
 {
   ICOM_THIS(IDirectPlay4Impl,iface);
-  FIXME("(%p)->(0x%08lx,0x%08lx,0x%08lx,%p,%p): stub\n", This, idFrom, idTo, dwFlags, lpdwNumMsgs, lpdwNumBytes );
-  return DP_OK;
+  return DP_IF_GetMessageQueue( This, idFrom, idTo, dwFlags, lpdwNumMsgs,
+                                lpdwNumBytes, TRUE );
 }
 
 static HRESULT WINAPI DirectPlay4WImpl_GetMessageQueue
-          ( LPDIRECTPLAY4 iface, DPID idFrom, DPID idTo, DWORD dwFlags, LPDWORD lpdwNumMsgs, LPDWORD lpdwNumBytes )
+          ( LPDIRECTPLAY4 iface, DPID idFrom, DPID idTo, DWORD dwFlags, 
+            LPDWORD lpdwNumMsgs, LPDWORD lpdwNumBytes )
 {
   ICOM_THIS(IDirectPlay4Impl,iface);
-  FIXME("(%p)->(0x%08lx,0x%08lx,0x%08lx,%p,%p): stub\n", This, idFrom, idTo, dwFlags, lpdwNumMsgs, lpdwNumBytes );
-  return DP_OK;
+  return DP_IF_GetMessageQueue( This, idFrom, idTo, dwFlags, lpdwNumMsgs,
+                                lpdwNumBytes, FALSE );
+}
+
+static HRESULT WINAPI DP_IF_CancelMessage
+          ( IDirectPlay4Impl* This, DWORD dwMsgID, DWORD dwFlags, 
+            DWORD dwMinPriority, DWORD dwMaxPriority, BOOL bAnsi )
+{
+  HRESULT hr = DP_OK;
+
+  FIXME( "(%p)->(0x%08lx,0x%08lx,%u): semi stub\n", 
+         This, dwMsgID, dwFlags, bAnsi );
+
+  if( This->dp2->spData.lpCB->Cancel )
+  {
+    DPSP_CANCELDATA data;
+
+    TRACE( "Calling SP Cancel\n" );
+
+    /* FIXME: Undocumented callback */
+
+    data.lpISP          = This->dp2->spData.lpISP;
+    data.dwFlags        = dwFlags;
+    data.lprglpvSPMsgID = NULL;
+    data.cSPMsgID       = dwMsgID;
+    data.dwMinPriority  = dwMinPriority;
+    data.dwMaxPriority  = dwMaxPriority;
+
+    hr = (This->dp2->spData.lpCB->Cancel)( &data );
+  }
+  else
+  {
+    FIXME( "SP doesn't implement Cancel\n" );
+  }
+
+  return hr;
 }
 
 static HRESULT WINAPI DirectPlay4AImpl_CancelMessage
           ( LPDIRECTPLAY4A iface, DWORD dwMsgID, DWORD dwFlags )
 {
   ICOM_THIS(IDirectPlay4Impl,iface);
-  FIXME("(%p)->(0x%08lx,0x%08lx): stub\n", This, dwMsgID, dwFlags );
-  return DP_OK;
+
+  if( dwFlags != 0 )
+  {
+    return DPERR_INVALIDFLAGS;
+  }
+
+  if( dwMsgID == 0 )
+  {
+    dwFlags |= DPCANCELSEND_ALL;
+  }
+ 
+  return DP_IF_CancelMessage( This, dwMsgID, dwFlags, 0, 0, TRUE );
 }
 
 static HRESULT WINAPI DirectPlay4WImpl_CancelMessage
           ( LPDIRECTPLAY4 iface, DWORD dwMsgID, DWORD dwFlags )
 {
   ICOM_THIS(IDirectPlay4Impl,iface);
-  FIXME("(%p)->(0x%08lx,0x%08lx): stub\n", This, dwMsgID, dwFlags );
-  return DP_OK;
+
+  if( dwFlags != 0 )
+  {
+    return DPERR_INVALIDFLAGS;
+  }
+
+  if( dwMsgID == 0 )
+  {
+    dwFlags |= DPCANCELSEND_ALL;
+  }
+
+  return DP_IF_CancelMessage( This, dwMsgID, dwFlags, 0, 0, FALSE );
 }
 
 static HRESULT WINAPI DirectPlay4AImpl_CancelPriority
-          ( LPDIRECTPLAY4A iface, DWORD dwMinPriority, DWORD dwMaxPriority, DWORD dwFlags )
+          ( LPDIRECTPLAY4A iface, DWORD dwMinPriority, DWORD dwMaxPriority, 
+            DWORD dwFlags )
 {
   ICOM_THIS(IDirectPlay4Impl,iface);
-  FIXME("(%p)->(0x%08lx,0x%08lx,0x%08lx): stub\n", This, dwMinPriority, dwMaxPriority, dwFlags );
-  return DP_OK;
+
+  if( dwFlags != 0 )
+  {
+    return DPERR_INVALIDFLAGS;
+  }
+
+  return DP_IF_CancelMessage( This, 0, DPCANCELSEND_PRIORITY, dwMinPriority, 
+                              dwMaxPriority, TRUE );
 }
 
 static HRESULT WINAPI DirectPlay4WImpl_CancelPriority
-          ( LPDIRECTPLAY4 iface, DWORD dwMinPriority, DWORD dwMaxPriority, DWORD dwFlags )
+          ( LPDIRECTPLAY4 iface, DWORD dwMinPriority, DWORD dwMaxPriority, 
+            DWORD dwFlags )
 {
   ICOM_THIS(IDirectPlay4Impl,iface);
-  FIXME("(%p)->(0x%08lx,0x%08lx,0x%08lx): stub\n", This, dwMinPriority, dwMaxPriority, dwFlags );
-  return DP_OK;
+
+  if( dwFlags != 0 )
+  {
+    return DPERR_INVALIDFLAGS;
+  }
+
+  return DP_IF_CancelMessage( This, 0, DPCANCELSEND_PRIORITY, dwMinPriority, 
+                              dwMaxPriority, FALSE );
 }
 
 /* Note: Hack so we can reuse the old functions without compiler warnings */
@@ -3169,9 +4404,9 @@
 static ICOM_VTABLE(IDirectPlay2) directPlay2WVT = 
 {
   ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
-  DirectPlay2W_QueryInterface,
-  XCAST(AddRef)DirectPlay2AImpl_AddRef,
-  XCAST(Release)DirectPlay2AImpl_Release,
+  XCAST(QueryInterface)DP_QueryInterface,
+  XCAST(AddRef)DP_AddRef,
+  XCAST(Release)DP_Release,
 
   DirectPlay2WImpl_AddPlayerToGroup,
   DirectPlay2WImpl_Close,
@@ -3215,9 +4450,9 @@
 static ICOM_VTABLE(IDirectPlay2) directPlay2AVT = 
 {
   ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
-  DirectPlay2A_QueryInterface,
-  XCAST(AddRef)DirectPlay2AImpl_AddRef,
-  XCAST(Release)DirectPlay2AImpl_Release,
+  XCAST(QueryInterface)DP_QueryInterface,
+  XCAST(AddRef)DP_AddRef,
+  XCAST(Release)DP_Release,
 
   DirectPlay2AImpl_AddPlayerToGroup,
   DirectPlay2AImpl_Close,
@@ -3262,9 +4497,9 @@
 static ICOM_VTABLE(IDirectPlay3) directPlay3AVT = 
 {
   ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
-  DirectPlay3AImpl_QueryInterface,
-  XCAST(AddRef)DirectPlay2AImpl_AddRef,
-  XCAST(Release)DirectPlay2AImpl_Release,
+  XCAST(QueryInterface)DP_QueryInterface,
+  XCAST(AddRef)DP_AddRef,
+  XCAST(Release)DP_Release,
 
   XCAST(AddPlayerToGroup)DirectPlay2AImpl_AddPlayerToGroup,
   XCAST(Close)DirectPlay2AImpl_Close,
@@ -3323,9 +4558,9 @@
 static ICOM_VTABLE(IDirectPlay3) directPlay3WVT = 
 {
   ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
-  DirectPlay3WImpl_QueryInterface,
-  XCAST(AddRef)DirectPlay2AImpl_AddRef,
-  XCAST(Release)DirectPlay2AImpl_Release,
+  XCAST(QueryInterface)DP_QueryInterface,
+  XCAST(AddRef)DP_AddRef,
+  XCAST(Release)DP_Release,
 
   XCAST(AddPlayerToGroup)DirectPlay2WImpl_AddPlayerToGroup,
   XCAST(Close)DirectPlay2WImpl_Close,
@@ -3384,9 +4619,9 @@
 static ICOM_VTABLE(IDirectPlay4) directPlay4WVT =
 {
   ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
-  DirectPlay4WImpl_QueryInterface,
-  XCAST(AddRef)DirectPlay2AImpl_AddRef,
-  XCAST(Release)DirectPlay2AImpl_Release,
+  XCAST(QueryInterface)DP_QueryInterface,
+  XCAST(AddRef)DP_AddRef,
+  XCAST(Release)DP_Release,
 
   XCAST(AddPlayerToGroup)DirectPlay2WImpl_AddPlayerToGroup,
   XCAST(Close)DirectPlay2WImpl_Close,
@@ -3453,9 +4688,9 @@
 static ICOM_VTABLE(IDirectPlay4) directPlay4AVT =
 {
   ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
-  DirectPlay4AImpl_QueryInterface,
-  XCAST(AddRef)DirectPlay2AImpl_AddRef,
-  XCAST(Release)DirectPlay2AImpl_Release,
+  XCAST(QueryInterface)DP_QueryInterface,
+  XCAST(AddRef)DP_AddRef,
+  XCAST(Release)DP_Release,
 
   XCAST(AddPlayerToGroup)DirectPlay2AImpl_AddPlayerToGroup,
   XCAST(Close)DirectPlay2AImpl_Close,
@@ -3658,6 +4893,40 @@
 
 }
 
+typedef struct tagCreateEnum
+{
+  LPVOID  lpConn;
+  LPCGUID lpGuid; 
+} CreateEnumData, *lpCreateEnumData;
+
+/* Find and copy the matching connection for the SP guid */
+BOOL CALLBACK cbDPCreateEnumConnections( 
+    LPCGUID     lpguidSP,
+    LPVOID      lpConnection,
+    DWORD       dwConnectionSize,
+    LPCDPNAME   lpName,
+    DWORD       dwFlags,
+    LPVOID      lpContext)
+{
+  lpCreateEnumData lpData = (lpCreateEnumData)lpContext; 
+
+  if( IsEqualGUID( lpguidSP, lpData->lpGuid ) )
+  {
+    TRACE( "Found SP entry with guid %s\n", debugstr_guid(lpData->lpGuid) );
+
+    lpData->lpConn = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                dwConnectionSize );
+    CopyMemory( lpData->lpConn, lpConnection, dwConnectionSize );
+
+    /* Found the record that we were looking for */
+    return FALSE;
+  }
+
+  /* Haven't found what were looking for yet */
+  return TRUE;
+}
+
+
 /***************************************************************************
  *  DirectPlayCreate (DPLAYX.1) (DPLAY.1)
  *
@@ -3665,6 +4934,10 @@
 HRESULT WINAPI DirectPlayCreate
 ( LPGUID lpGUID, LPDIRECTPLAY2 *lplpDP, IUnknown *pUnk)
 {
+  HRESULT hr;
+  LPDIRECTPLAY3A lpDP3A;
+  CreateEnumData cbData;
+  
   TRACE( "lpGUID=%s lplpDP=%p pUnk=%p\n", debugstr_guid(lpGUID), lplpDP, pUnk );
 
   if( pUnk != NULL )
@@ -3675,7 +4948,7 @@
 
   /* Create an IDirectPlay object. We don't support that so we'll cheat and
      give them an IDirectPlay2A object and hope that doesn't cause problems */
-  if( directPlay_QueryInterface( &IID_IDirectPlay2A, (LPVOID*)lplpDP ) != DP_OK )
+  if( DP_CreateInterface( &IID_IDirectPlay2A, (LPVOID*)lplpDP ) != DP_OK )
   {
     return DPERR_UNAVAILABLE;
   } 
@@ -3683,28 +4956,50 @@
   if( IsEqualGUID( &GUID_NULL, lpGUID ) )
   {
     /* The GUID_NULL means don't bind a service provider. Just return the
-       interface */ 
+       interface as is */ 
     return DP_OK;
   }
 
+  /* Bind the desired service provider since lpGUID is non NULL */
+  TRACE( "Service Provider binding for %s\n", debugstr_guid(lpGUID) );
 
-  /* Bind the desired service provider */
-  if( ( IsEqualGUID( lpGUID, &DPSPGUID_MODEM ) ) ||
-      ( IsEqualGUID( lpGUID, &DPSPGUID_SERIAL ) ) ||
-      ( IsEqualGUID( lpGUID, &DPSPGUID_TCPIP ) ) ||
-      ( IsEqualGUID( lpGUID, &DPSPGUID_IPX ) ) 
-    )
+  /* We're going to use a DP3 interface */
+  hr = IDirectPlayX_QueryInterface( *lplpDP, &IID_IDirectPlay3A, 
+                                    (LPVOID*)&lpDP3A );
+  if( FAILED(hr) )
   {
-     FIXME( "Service provider binding not supported yet\n" );
-     IDirectPlayX_Release( *lplpDP );
-     *lplpDP = NULL;
-     return DPERR_INVALIDPARAMS; 
+    ERR( "Failed to get DP3 interface: %s\n", DPLAYX_HresultToString(hr) ); 
+    return hr;
   }
 
-  ERR( "unknown Service Provider %s\n", debugstr_guid(lpGUID) );
+  cbData.lpConn = NULL;
+  cbData.lpGuid = lpGUID;
 
-  IDirectPlayX_Release( *lplpDP );
-  *lplpDP = NULL;
+  /* We were given a service provider, find info about it... */
+  hr = IDirectPlayX_EnumConnections( lpDP3A, NULL, cbDPCreateEnumConnections, 
+                                     &cbData, DPCONNECTION_DIRECTPLAY );
+  if( ( FAILED(hr) ) ||
+      ( cbData.lpConn == NULL )    
+    )
+  {
+    ERR( "Failed to get Enum for SP: %s\n", DPLAYX_HresultToString(hr) );  
+    IDirectPlayX_Release( lpDP3A );
+    return DPERR_UNAVAILABLE;
+  }
 
-  return DPERR_INVALIDPARAMS;
+  /* Initialize the service provider */
+  hr = IDirectPlayX_InitializeConnection( lpDP3A, cbData.lpConn, 0 );
+  if( FAILED(hr) ) 
+  {
+    ERR( "Failed to Initialize SP: %s\n", DPLAYX_HresultToString(hr) );
+    HeapFree( GetProcessHeap(), 0, cbData.lpConn );
+    IDirectPlayX_Release( lpDP3A );
+    return hr;
+  }
+
+  /* Release our version of the interface now that we're done with it */
+  IDirectPlayX_Release( lpDP3A );
+  HeapFree( GetProcessHeap(), 0, cbData.lpConn );
+
+  return DP_OK;
 }
diff --git a/dlls/dplayx/dplay_global.h b/dlls/dplayx/dplay_global.h
new file mode 100644
index 0000000..8547568
--- /dev/null
+++ b/dlls/dplayx/dplay_global.h
@@ -0,0 +1,185 @@
+#ifndef __WINE_DPLAY_GLOBAL_INCLUDED
+#define __WINE_DPLAY_GLOBAL_INCLUDED
+
+#include "dplaysp.h"
+#include "dplayx_queue.h"
+
+extern HRESULT DPL_EnumAddress( LPDPENUMADDRESSCALLBACK lpEnumAddressCallback, 
+                                LPCVOID lpAddress, DWORD dwAddressSize, 
+                                LPVOID lpContext );
+
+extern DWORD DP_CalcSessionDescSize( LPCDPSESSIONDESC2 lpSessDesc, BOOL bAnsi );
+
+/*****************************************************************************
+ * Predeclare the interface implementation structures
+ */
+typedef struct IDirectPlay2Impl IDirectPlay2AImpl;
+typedef struct IDirectPlay2Impl IDirectPlay2Impl;
+typedef struct IDirectPlay3Impl IDirectPlay3AImpl;
+typedef struct IDirectPlay3Impl IDirectPlay3Impl;
+typedef struct IDirectPlay4Impl IDirectPlay4AImpl;
+typedef struct IDirectPlay4Impl IDirectPlay4Impl;
+
+typedef struct tagDirectPlayIUnknownData
+{
+  ULONG             ulObjRef;
+  CRITICAL_SECTION  DP_lock;
+} DirectPlayIUnknownData;
+
+typedef struct tagEnumSessionAsyncCallbackData
+{
+  LPSPINITDATA lpSpData;
+  GUID         requestGuid;
+  DWORD        dwEnumSessionFlags;
+  DWORD        dwTimeout;
+  HANDLE       hSuicideRequest;
+} EnumSessionAsyncCallbackData;
+
+struct PlayerData
+{
+  /* Individual player information */
+  DPID dpid;
+
+  DPNAME name;
+  HANDLE hEvent;
+
+  /* View of local data */
+  LPVOID lpLocalData;
+  DWORD  dwLocalDataSize;
+
+  /* View of remote data */
+  LPVOID lpRemoteData;
+  DWORD  dwRemoteDataSize;
+
+  DWORD  dwFlags; /* Special remarks about the type of player */
+};
+typedef struct PlayerData* lpPlayerData;
+
+struct PlayerList
+{
+  DPQ_ENTRY(PlayerList) players;
+
+  lpPlayerData lpPData;
+};
+typedef struct PlayerList* lpPlayerList;
+
+struct GroupData
+{
+  /* Internal information */
+  DPID parent; /* If parent == 0 it's a top level group */
+
+  DPQ_HEAD(GroupList)  groups;  /* A group has [0..n] groups */
+  DPQ_HEAD(PlayerList) players; /* A group has [0..n] players */
+
+  DPID idGroupOwner; /* ID of player who owns the group */
+
+  DWORD dwFlags; /* Flags describing anything special about the group */
+
+  DPID   dpid;
+  DPNAME name;
+
+  /* View of local data */
+  LPVOID lpLocalData;
+  DWORD  dwLocalDataSize;
+
+  /* View of remote data */
+  LPVOID lpRemoteData;
+  DWORD  dwRemoteDataSize;
+};
+typedef struct GroupData  GroupData;
+typedef struct GroupData* lpGroupData;
+
+struct GroupList
+{
+  DPQ_ENTRY(GroupList) groups;
+
+  lpGroupData lpGData;
+};
+typedef struct GroupList* lpGroupList;
+
+struct DPMSG
+{
+  DPQ_ENTRY( DPMSG ) msgs;
+  DPMSG_GENERIC* msg;
+};
+typedef struct DPMSG* LPDPMSG;
+
+/* Contains all dp1 and dp2 data members */
+typedef struct tagDirectPlay2Data
+{
+  BOOL   bConnectionOpen;
+
+  /* For async EnumSessions requests */
+  HANDLE hEnumSessionThread;
+  HANDLE hKillEnumSessionThreadEvent;
+
+  LPVOID lpNameServerData; /* DPlay interface doesn't know contents */
+
+  BOOL bHostInterface; /* Did this interface create the session */
+
+#if 0
+  DPQ_HEAD(PlayerList) players; /* All players w/ interface */
+  DPQ_HEAD(GroupList)  groups;  /* All main groups w/ interface */
+#else 
+  lpGroupData lpSysGroup; /* System group with _everything_ in it */
+#endif
+
+  LPDPSESSIONDESC2 lpSessionDesc;
+
+  /* I/O Msg queues */
+  DPQ_HEAD( DPMSG ) receiveMsgs; /* Msg receive queue */
+  DPQ_HEAD( DPMSG ) sendMsgs;    /* Msg send pending queue */
+
+  /* Information about the service provider active on this connection */
+  SPINITDATA spData;
+
+  /* Our service provider */
+  HMODULE hServiceProvider;
+
+  BOOL bConnectionInitialized;
+} DirectPlay2Data;
+
+typedef struct tagDirectPlay3Data
+{
+  BOOL dummy;
+} DirectPlay3Data;
+typedef struct tagDirectPlay4Data
+{
+  BOOL dummy;
+} DirectPlay4Data;
+
+#define DP_IMPL_FIELDS \
+  ULONG ulInterfaceRef; \
+  DirectPlayIUnknownData*  unk; \
+  DirectPlay2Data*         dp2; \
+  DirectPlay3Data*         dp3; \
+  DirectPlay4Data*         dp4;
+
+struct IDirectPlay2Impl
+{
+  ICOM_VFIELD(IDirectPlay2);
+  DP_IMPL_FIELDS
+};
+
+struct IDirectPlay3Impl
+{
+  ICOM_VFIELD(IDirectPlay3);
+  DP_IMPL_FIELDS
+};
+
+struct IDirectPlay4Impl
+{
+  ICOM_VFIELD(IDirectPlay4);
+  DP_IMPL_FIELDS
+};
+
+/* Forward declarations of virtual tables */
+extern ICOM_VTABLE(IDirectPlay2) directPlay2AVT;
+extern ICOM_VTABLE(IDirectPlay3) directPlay3AVT;
+extern ICOM_VTABLE(IDirectPlay4) directPlay4AVT;
+
+extern ICOM_VTABLE(IDirectPlay2) directPlay2WVT;
+extern ICOM_VTABLE(IDirectPlay3) directPlay3WVT;
+extern ICOM_VTABLE(IDirectPlay4) directPlay4WVT;
+
+#endif /* __WINE_DPLAY_GLOBAL_INCLUDED */
diff --git a/dlls/dplayx/dplaysp.c b/dlls/dplayx/dplaysp.c
new file mode 100644
index 0000000..98962de
--- /dev/null
+++ b/dlls/dplayx/dplaysp.c
@@ -0,0 +1,880 @@
+/* This contains the implementation of the interface Service
+ * Providers require to communicate with Direct Play 
+ *
+ * Copyright 2000 Peter Hunnisett <hunnise@nortelnetworks.com>
+ */
+
+#include "heap.h"
+#include "winerror.h"
+#include "debugtools.h"
+
+#include "dpinit.h"
+#include "dplaysp.h"
+#include "dplay_global.h"
+#include "name_server.h"
+#include "dplayx_messages.h"
+
+#include "dplayx_global.h" /* FIXME: For global hack */
+
+/* FIXME: Need to add interface locking inside procedures */
+
+DEFAULT_DEBUG_CHANNEL(dplay);
+
+/* Prototypes */
+static BOOL DPSP_CreateIUnknown( LPVOID lpSP );
+static BOOL DPSP_DestroyIUnknown( LPVOID lpSP );
+static BOOL DPSP_CreateDirectPlaySP( LPVOID lpSP, IDirectPlay2Impl* dp );
+static BOOL DPSP_DestroyDirectPlaySP( LPVOID lpSP );
+
+
+/* Predefine the interface */
+typedef struct IDirectPlaySPImpl IDirectPlaySPImpl;
+
+typedef struct tagDirectPlaySPIUnknownData
+{
+  ULONG             ulObjRef;
+  CRITICAL_SECTION  DPSP_lock;
+} DirectPlaySPIUnknownData;
+
+typedef struct tagDirectPlaySPData
+{
+  LPVOID lpSpRemoteData;
+  DWORD  dwSpRemoteDataSize; /* Size of data pointed to by lpSpRemoteData */
+
+  LPVOID lpSpLocalData;
+  DWORD  dwSpLocalDataSize; /* Size of data pointed to by lpSpLocalData */
+
+  IDirectPlay2Impl* dplay; /* FIXME: This should perhaps be iface not impl */
+
+  LPVOID lpPlayerData; /* FIXME: Need to figure out how this actually behaves */
+  DWORD dwPlayerDataSize;
+} DirectPlaySPData;
+
+#define DPSP_IMPL_FIELDS \
+   ULONG ulInterfaceRef; \
+   DirectPlaySPIUnknownData* unk; \
+   DirectPlaySPData* sp;
+
+struct IDirectPlaySPImpl
+{
+  ICOM_VFIELD(IDirectPlaySP);
+  DPSP_IMPL_FIELDS
+};
+
+/* Forward declaration of virtual tables */
+static ICOM_VTABLE(IDirectPlaySP) directPlaySPVT;
+
+
+
+/* Create the SP interface */
+extern
+HRESULT DPSP_CreateInterface( REFIID riid, LPVOID* ppvObj, IDirectPlay2Impl* dp )
+{
+  TRACE( " for %s\n", debugstr_guid( riid ) );
+
+  *ppvObj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                       sizeof( IDirectPlaySPImpl ) );
+
+  if( *ppvObj == NULL )
+  {
+    return DPERR_OUTOFMEMORY;
+  }
+ 
+  if( IsEqualGUID( &IID_IDirectPlaySP, riid ) )
+  {
+    ICOM_THIS(IDirectPlaySPImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlaySPVT;
+  }
+  else
+  {
+    /* Unsupported interface */
+    HeapFree( GetProcessHeap(), 0, *ppvObj );
+    *ppvObj = NULL;
+
+    return E_NOINTERFACE;
+  }
+
+  /* Initialize it */
+  if( DPSP_CreateIUnknown( *ppvObj ) &&
+      DPSP_CreateDirectPlaySP( *ppvObj, dp )
+    )
+  {
+    IDirectPlaySP_AddRef( (LPDIRECTPLAYSP)*ppvObj );
+    return S_OK;
+  }
+
+  /* Initialize failed, destroy it */
+  DPSP_DestroyDirectPlaySP( *ppvObj );
+  DPSP_DestroyIUnknown( *ppvObj );
+
+  HeapFree( GetProcessHeap(), 0, *ppvObj );
+  *ppvObj = NULL;
+
+  return DPERR_NOMEMORY;
+}
+
+static BOOL DPSP_CreateIUnknown( LPVOID lpSP )
+{
+  ICOM_THIS(IDirectPlaySPImpl,lpSP);
+
+  This->unk = (DirectPlaySPIUnknownData*)HeapAlloc( GetProcessHeap(), 
+                                                    HEAP_ZERO_MEMORY,
+                                                    sizeof( *(This->unk) ) );
+
+  if ( This->unk == NULL )
+  {
+    return FALSE;
+  }
+
+  InitializeCriticalSection( &This->unk->DPSP_lock );
+
+  return TRUE;
+}
+
+static BOOL DPSP_DestroyIUnknown( LPVOID lpSP )
+{
+  ICOM_THIS(IDirectPlaySPImpl,lpSP);
+ 
+  DeleteCriticalSection( &This->unk->DPSP_lock );
+  HeapFree( GetProcessHeap(), 0, This->unk );
+
+  return TRUE;
+}
+
+
+static BOOL DPSP_CreateDirectPlaySP( LPVOID lpSP, IDirectPlay2Impl* dp )
+{
+  ICOM_THIS(IDirectPlaySPImpl,lpSP);
+
+  This->sp = (DirectPlaySPData*)HeapAlloc( GetProcessHeap(),
+                                           HEAP_ZERO_MEMORY,
+                                           sizeof( *(This->sp) ) );
+
+  if ( This->sp == NULL )
+  {
+    return FALSE;
+  }
+
+  This->sp->dplay = dp;
+
+  /* Normally we should be keeping a reference, but since only the dplay
+   * interface that created us can destroy us, we do not keep a reference
+   * to it (ie we'd be stuck with always having one reference to the dplay
+   * object, and hence us, around).
+   * NOTE: The dp object does reference count us.
+   */
+  /* IDirectPlayX_AddRef( (LPDIRECTPLAY2)dp ); */
+
+  return TRUE;
+}
+
+static BOOL DPSP_DestroyDirectPlaySP( LPVOID lpSP )
+{
+  ICOM_THIS(IDirectPlaySPImpl,lpSP);
+
+  /* Normally we should be keeping a reference, but since only the dplay
+   * interface that created us can destroy us, we do not keep a reference
+   * to it (ie we'd be stuck with always having one reference to the dplay
+   * object, and hence us, around).
+   * NOTE: The dp object does reference count us.
+   */
+  /*IDirectPlayX_Release( (LPDIRECTPLAY2)This->sp->dplay ); */
+
+  HeapFree( GetProcessHeap(), 0, This->sp->lpSpRemoteData );
+  HeapFree( GetProcessHeap(), 0, This->sp->lpSpLocalData );
+
+  /* FIXME: Need to delete player queue */
+
+  HeapFree( GetProcessHeap(), 0, This->sp );
+  return TRUE;
+}
+
+/* Interface implementation */
+
+static HRESULT WINAPI DPSP_QueryInterface
+( LPDIRECTPLAYSP iface,
+  REFIID riid,
+  LPVOID* ppvObj )
+{
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+  TRACE("(%p)->(%s,%p)\n", This, debugstr_guid( riid ), ppvObj );
+
+  *ppvObj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                       sizeof( IDirectPlaySPImpl ) );
+
+  if( *ppvObj == NULL )
+  {
+    return DPERR_OUTOFMEMORY;
+  }
+
+  CopyMemory( *ppvObj, iface, sizeof( IDirectPlaySPImpl )  );
+  (*(IDirectPlaySPImpl**)ppvObj)->ulInterfaceRef = 0;
+
+  if( IsEqualGUID( &IID_IDirectPlaySP, riid ) )
+  {
+    ICOM_THIS(IDirectPlaySPImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlaySPVT;
+  }
+  else
+  {
+    /* Unsupported interface */
+    HeapFree( GetProcessHeap(), 0, *ppvObj );
+    *ppvObj = NULL;
+
+    return E_NOINTERFACE;
+  }
+ 
+  IDirectPlaySP_AddRef( (LPDIRECTPLAYSP)*ppvObj );
+
+  return S_OK;
+} 
+
+static ULONG WINAPI DPSP_AddRef
+( LPDIRECTPLAYSP iface )
+{
+  ULONG ulInterfaceRefCount, ulObjRefCount;
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  ulObjRefCount       = InterlockedIncrement( &This->unk->ulObjRef );
+  ulInterfaceRefCount = InterlockedIncrement( &This->ulInterfaceRef );
+
+  TRACE( "ref count incremented to %lu:%lu for %p\n",
+         ulInterfaceRefCount, ulObjRefCount, This );
+
+  return ulObjRefCount;
+}
+
+static ULONG WINAPI DPSP_Release
+( LPDIRECTPLAYSP iface )
+{
+  ULONG ulInterfaceRefCount, ulObjRefCount;
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  ulObjRefCount       = InterlockedDecrement( &This->unk->ulObjRef );
+  ulInterfaceRefCount = InterlockedDecrement( &This->ulInterfaceRef );
+
+  TRACE( "ref count decremented to %lu:%lu for %p\n",
+         ulInterfaceRefCount, ulObjRefCount, This );
+
+  /* Deallocate if this is the last reference to the object */
+  if( ulObjRefCount == 0 )
+  {
+     DPSP_DestroyDirectPlaySP( This );
+     DPSP_DestroyIUnknown( This );
+  }
+
+  if( ulInterfaceRefCount == 0 )
+  {
+    HeapFree( GetProcessHeap(), 0, This );
+  }
+
+  return ulInterfaceRefCount;
+}
+
+static HRESULT WINAPI IDirectPlaySPImpl_AddMRUEntry
+( LPDIRECTPLAYSP iface,
+  LPCWSTR lpSection,
+  LPCWSTR lpKey,
+  LPCVOID lpData, 
+  DWORD   dwDataSize, 
+  DWORD   dwMaxEntries
+)
+{
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  FIXME( "(%p)->(%p,%p%p,0x%08lx,0x%08lx): stub\n", 
+         This, lpSection, lpKey, lpData, dwDataSize, dwMaxEntries );
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI IDirectPlaySPImpl_CreateAddress
+( LPDIRECTPLAYSP iface,
+  REFGUID guidSP, 
+  REFGUID guidDataType, 
+  LPCVOID lpData, 
+  DWORD   dwDataSize, 
+  LPVOID  lpAddress,
+  LPDWORD lpdwAddressSize
+)
+{
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  FIXME( "(%p)->(%s,%s,%p,0x%08lx,%p,%p): stub\n", 
+         This, debugstr_guid(guidSP), debugstr_guid(guidDataType),
+         lpData, dwDataSize, lpAddress, lpdwAddressSize );
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI IDirectPlaySPImpl_EnumAddress
+( LPDIRECTPLAYSP iface,
+  LPDPENUMADDRESSCALLBACK lpEnumAddressCallback, 
+  LPCVOID lpAddress, 
+  DWORD dwAddressSize, 
+  LPVOID lpContext
+)
+{
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  TRACE( "(%p)->(%p,%p,0x%08lx,%p)\n", 
+         This, lpEnumAddressCallback, lpAddress, dwAddressSize, lpContext );
+
+  DPL_EnumAddress( lpEnumAddressCallback, lpAddress, dwAddressSize, lpContext );
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI IDirectPlaySPImpl_EnumMRUEntries
+( LPDIRECTPLAYSP iface,
+  LPCWSTR lpSection, 
+  LPCWSTR lpKey, 
+  LPENUMMRUCALLBACK lpEnumMRUCallback, 
+  LPVOID lpContext
+)
+{
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  FIXME( "(%p)->(%p,%p,%p,%p,): stub\n", 
+         This, lpSection, lpKey, lpEnumMRUCallback, lpContext );
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI IDirectPlaySPImpl_GetPlayerFlags
+( LPDIRECTPLAYSP iface,
+  DPID idPlayer, 
+  LPDWORD lpdwPlayerFlags
+)
+{
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  FIXME( "(%p)->(0x%08lx,%p): stub\n", 
+         This, idPlayer, lpdwPlayerFlags );
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI IDirectPlaySPImpl_GetSPPlayerData
+( LPDIRECTPLAYSP iface,
+  DPID idPlayer, 
+  LPVOID* lplpData, 
+  LPDWORD lpdwDataSize, 
+  DWORD dwFlags
+)
+{
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  TRACE( "Called on process 0x%08lx\n", GetCurrentProcessId() );
+  FIXME( "(%p)->(0x%08lx,%p,%p,0x%08lx): stub\n", 
+         This, idPlayer, lplpData, lpdwDataSize, dwFlags );
+
+  *lplpData     = This->sp->lpPlayerData;
+  *lpdwDataSize = This->sp->dwPlayerDataSize;
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI IDirectPlaySPImpl_HandleMessage
+( LPDIRECTPLAYSP iface,
+  LPVOID lpMessageBody, 
+  DWORD  dwMessageBodySize, 
+  LPVOID lpMessageHeader
+)
+{
+  LPDPMSG_SENDENVELOPE lpMsg = (LPDPMSG_SENDENVELOPE)lpMessageBody;
+  HRESULT hr = DPERR_GENERIC;
+
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  TRACE( "Called on process 0x%08lx\n", GetCurrentProcessId() );
+  FIXME( "(%p)->(%p,0x%08lx,%p): mostly stub\n", 
+         This, lpMessageBody, dwMessageBodySize, lpMessageHeader );
+
+  TRACE( "Incomming message has envelope of 0x%08lx, %u, %u\n", 
+         lpMsg->dwMagic, lpMsg->wCommandId, lpMsg->wVersion );
+
+  if( lpMsg->dwMagic != DPMSGMAGIC_DPLAYMSG )
+  {
+    FIXME( "Unknown magic 0x%08lx!\n", lpMsg->dwMagic );
+  }
+
+  switch( lpMsg->wCommandId )
+  {
+    case DPMSGCMD_ENUMSESSIONSREQUEST:
+    {
+      DPSP_REPLYDATA data;
+
+      data.lpSPMessageHeader = lpMessageHeader;
+      data.idNameServer      = 0;
+      data.lpISP             = iface;
+ 
+      NS_ReplyToEnumSessionsRequest( lpMessageBody, &data, This->sp->dplay );
+
+      hr = (This->sp->dplay->dp2->spData.lpCB->Reply)( &data );
+
+      if( FAILED(hr) )
+      {
+        ERR( "Reply failed 0x%08lx\n", hr );
+      }
+
+      break;
+    }
+
+    case DPMSGCMD_ENUMSESSIONSREPLY:
+    {
+      NS_SetRemoteComputerAsNameServer( lpMessageHeader, 
+                                        This->sp->dplay->dp2->spData.dwSPHeaderSize,
+                                        (LPDPMSG_ENUMSESSIONSREPLY)lpMessageBody,
+                                        This->sp->dplay->dp2->lpNameServerData );
+
+      /* No reply expected */
+
+      break;
+    }
+    default:
+      FIXME( "Unknown Command of %u\n", lpMsg->wCommandId );
+  }
+
+#if 0
+  HRESULT hr = DP_OK;
+  HANDLE  hReceiveEvent = 0;
+  /* FIXME: Aquire some sort of interface lock */
+  /* FIXME: Need some sort of context for this callback. Need to determine
+   *        how this is actually done with the SP
+   */
+  /* FIXME: Add in size checks for all messages to determine corrupt messages */  /* FIXME: Who needs to delete the message when done? */
+  /* FIXME: Does this get invoked as soon as the message arrives, or as soon
+   *        as it's removed from the queue (or peeked in queue?)
+   */
+  switch( lpMsg->dwType )
+  {
+    case DPSYS_CREATEPLAYERORGROUP:
+    {
+      LPDPMSG_CREATEPLAYERORGROUP msg = (LPDPMSG_CREATEPLAYERORGROUP)lpMsg;
+
+      if( msg->dwPlayerType == DPPLAYERTYPE_PLAYER )
+      {
+        hr = DP_IF_CreatePlayer( This, lpMessageHeader, msg->dpId, 
+                                 &msg->dpnName, 0, msg->lpData, 
+                                 msg->dwDataSize, msg->dwFlags, ... );
+      }
+      else if( msg->dwPlayerType == DPPLAYERTYPE_GROUP )
+      {
+        /* Group in group situation? */
+        if( msg->dpIdParent == DPID_NOPARENT_GROUP )
+        {
+          hr = DP_IF_CreateGroup( This, lpMessageHeader, msg->dpId, 
+                                  &msg->dpnName, 0, msg->lpData, 
+                                  msg->dwDataSize, msg->dwFlags, ... );
+        }
+        else /* Group in Group */
+        {
+          hr = DP_IF_CreateGroupInGroup( This, lpMessageHeader, msg->dpIdParent,
+                                         &msg->dpnName, 0, msg->lpData, 
+                                         msg->dwDataSize, msg->dwFlags, ... );
+        }
+      }
+      else /* Hmmm? */
+      {
+        ERR( "Corrupt msg->dwPlayerType for DPSYS_CREATEPLAYERORGROUP\n" );
+        return;
+      }
+
+      break;
+    }
+
+    case DPSYS_DESTROYPLAYERORGROUP:
+    {
+      LPDPMSG_DESTROYPLAYERORGROUP msg = (LPDPMSG_DESTROYPLAYERORGROUP)lpMsg;
+
+      if( msg->dwPlayerType == DPPLAYERTYPE_PLAYER )
+      {
+        hr = DP_IF_DestroyPlayer( This, msg->dpId, ... );
+      }
+      else if( msg->dwPlayerType == DPPLAYERTYPE_GROUP )
+      {
+        hr = DP_IF_DestroyGroup( This, msg->dpId, ... );
+      }
+      else /* Hmmm? */
+      {
+        ERR( "Corrupt msg->dwPlayerType for DPSYS_DESTROYPLAYERORGROUP\n" );
+        return;
+      }
+
+      break;
+    }
+
+    case DPSYS_ADDPLAYERTOGROUP:
+    {
+      LPDPMSG_ADDPLAYERTOGROUP msg = (LPDPMSG_ADDPLAYERTOGROUP)lpMsg;
+
+      hr = DP_IF_AddPlayerToGroup( This, msg->dpIdGroup, msg->dpIdPlayer, ... );
+      break;
+    }
+
+    case DPSYS_DELETEPLAYERFROMGROUP:
+    {
+      LPDPMSG_DELETEPLAYERFROMGROUP msg = (LPDPMSG_DELETEPLAYERFROMGROUP)lpMsg;
+
+      hr = DP_IF_DeletePlayerFromGroup( This, msg->dpIdGroup, msg->dpIdPlayer,
+                                        ... );
+
+      break;
+    }
+
+    case DPSYS_SESSIONLOST:
+    {
+      LPDPMSG_SESSIONLOST msg = (LPDPMSG_SESSIONLOST)lpMsg;
+
+      FIXME( "DPSYS_SESSIONLOST not handled\n" );
+
+      break;
+    }
+
+    case DPSYS_HOST:
+    {
+      LPDPMSG_HOST msg = (LPDPMSG_HOST)lpMsg;
+
+      FIXME( "DPSYS_HOST not handled\n" );
+
+      break;
+    }
+
+    case DPSYS_SETPLAYERORGROUPDATA:
+    {
+      LPDPMSG_SETPLAYERORGROUPDATA msg = (LPDPMSG_SETPLAYERORGROUPDATA)lpMsg;
+
+      if( msg->dwPlayerType == DPPLAYERTYPE_PLAYER )
+      {
+        hr = DP_IF_SetPlayerData( This, msg->dpId, msg->lpData, msg->dwDataSize,                                  DPSET_REMOTE, ... );
+      }
+      else if( msg->dwPlayerType == DPPLAYERTYPE_GROUP )
+      {
+        hr = DP_IF_SetGroupData( This, msg->dpId, msg->lpData, msg->dwDataSize,
+                                 DPSET_REMOTE, ... );
+      }
+      else /* Hmmm? */
+      {
+        ERR( "Corrupt msg->dwPlayerType for LPDPMSG_SETPLAYERORGROUPDATA\n" );
+        return;
+      }
+
+      break;
+    }
+
+    case DPSYS_SETPLAYERORGROUPNAME:
+    {
+      LPDPMSG_SETPLAYERORGROUPNAME msg = (LPDPMSG_SETPLAYERORGROUPNAME)lpMsg;
+
+      if( msg->dwPlayerType == DPPLAYERTYPE_PLAYER )
+      {
+        hr = DP_IF_SetPlayerName( This, msg->dpId, msg->dpnName, ... );
+      }
+      else if( msg->dwPlayerType == DPPLAYERTYPE_GROUP )
+      {
+        hr = DP_IF_SetGroupName( This, msg->dpId, msg->dpnName, ... );
+      }
+      else /* Hmmm? */
+      {
+        ERR( "Corrupt msg->dwPlayerType for LPDPMSG_SETPLAYERORGROUPDATA\n" );
+        return;
+      }
+
+      break;
+    }
+
+    case DPSYS_SETSESSIONDESC;
+    {
+      LPDPMSG_SETSESSIONDESC msg = (LPDPMSG_SETSESSIONDESC)lpMsg;
+
+      hr = DP_IF_SetSessionDesc( This, &msg->dpDesc );
+
+      break;
+    }
+
+    case DPSYS_ADDGROUPTOGROUP:
+    {
+      LPDPMSG_ADDGROUPTOGROUP msg = (LPDPMSG_ADDGROUPTOGROUP)lpMsg;
+
+      hr = DP_IF_AddGroupToGroup( This, msg->dpIdParentGroup, msg->dpIdGroup,
+                                  ... );
+
+      break;
+    }
+
+    case DPSYS_DELETEGROUPFROMGROUP:
+    {
+      LPDPMSG_DELETEGROUPFROMGROUP msg = (LPDPMSG_DELETEGROUPFROMGROUP)lpMsg;
+
+      hr = DP_IF_DeleteGroupFromGroup( This, msg->dpIdParentGroup,
+                                       msg->dpIdGroup, ... );
+
+      break;
+    }
+
+    case DPSYS_SECUREMESSAGE:
+    {
+      LPDPMSG_SECUREMESSAGE msg = (LPDPMSG_SECUREMESSAGE)lpMsg;
+
+      FIXME( "DPSYS_SECUREMESSAGE not implemented\n" );
+
+      break;
+    }
+
+    case DPSYS_STARTSESSION:
+    {
+      LPDPMSG_STARTSESSION msg = (LPDPMSG_STARTSESSION)lpMsg;
+
+      FIXME( "DPSYS_STARTSESSION not implemented\n" );
+
+      break;
+    }
+
+    case DPSYS_CHAT:
+    {
+      LPDPMSG_CHAT msg = (LPDPMSG_CHAT)lpMsg;
+
+      FIXME( "DPSYS_CHAT not implemeneted\n" );
+
+      break;
+    }   
+
+    case DPSYS_SETGROUPOWNER:
+    {
+      LPDPMSG_SETGROUPOWNER msg = (LPDPMSG_SETGROUPOWNER)lpMsg;
+
+      FIXME( "DPSYS_SETGROUPOWNER not implemented\n" );
+
+      break;
+    }
+
+    case DPSYS_SENDCOMPLETE:
+    {
+      LPDPMSG_SENDCOMPLETE msg = (LPDPMSG_SENDCOMPLETE)lpMsg;
+
+      FIXME( "DPSYS_SENDCOMPLETE not implemented\n" );
+
+      break;
+    }
+
+    default:
+    {
+      /* NOTE: This should be a user defined type. There is nothing that we
+       *       need to do with it except queue it.
+       */
+      TRACE( "Received user message type(?) 0x%08lx through SP.\n",
+              lpMsg->dwType );
+      break;
+    }
+  }
+
+  FIXME( "Queue message in the receive queue. Need some context data!\n" );
+
+  if( FAILED(hr) )
+  {
+    ERR( "Unable to perform action for msg type 0x%08lx\n", lpMsg->dwType );
+  }
+  /* If a receieve event was registered for this player, invoke it */
+  if( hReceiveEvent )
+  {
+    SetEvent( hReceiveEvent );
+  }
+#endif
+
+  return hr;
+}
+
+static HRESULT WINAPI IDirectPlaySPImpl_SetSPPlayerData
+( LPDIRECTPLAYSP iface,
+  DPID idPlayer, 
+  LPVOID lpData, 
+  DWORD dwDataSize, 
+  DWORD dwFlags
+)
+{
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  /* FIXME: I'm not sure if this stuff should be associated with the DPlay
+   *        player lists. How else would this stuff get deleted? 
+   */
+
+  TRACE( "Called on process 0x%08lx\n", GetCurrentProcessId() );
+  FIXME( "(%p)->(0x%08lx,%p,0x%08lx,0x%08lx): stub\n", 
+         This, idPlayer, lpData, dwDataSize, dwFlags );
+
+  This->sp->lpPlayerData = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwDataSize ); 
+
+  This->sp->dwPlayerDataSize = dwDataSize;
+  CopyMemory( This->sp->lpPlayerData, lpData, dwDataSize );
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI IDirectPlaySPImpl_CreateCompoundAddress
+( LPDIRECTPLAYSP iface,
+  LPCDPCOMPOUNDADDRESSELEMENT lpElements, 
+  DWORD dwElementCount, 
+  LPVOID lpAddress, 
+  LPDWORD lpdwAddressSize
+)
+{
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  FIXME( "(%p)->(%p,0x%08lx,%p,%p): stub\n", 
+         This, lpElements, dwElementCount, lpAddress, lpdwAddressSize );
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI IDirectPlaySPImpl_GetSPData
+( LPDIRECTPLAYSP iface,
+  LPVOID* lplpData, 
+  LPDWORD lpdwDataSize, 
+  DWORD dwFlags
+)
+{
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  TRACE( "Called on process 0x%08lx\n", GetCurrentProcessId() );
+  TRACE( "(%p)->(%p,%p,0x%08lx)\n", 
+         This, lplpData, lpdwDataSize, dwFlags );
+
+#if 0
+  /* This is what the documentation says... */
+  if( dwFlags != 0 )
+  {
+    return DPERR_INVALIDPARAMS;
+  }
+#else
+  /* ... but most service providers call this with 1 */
+  /* Guess that this is using a DPSET_LOCAL or DPSET_REMOTE type of
+   * thing?
+   */
+  if( dwFlags != 0 )
+  {
+    FIXME( "Undocumented dwFlags 0x%08lx used\n", dwFlags );
+  }
+#endif
+
+  /* Yes, we're supposed to return a pointer to the memory we have stored! */
+  if( dwFlags == DPSET_REMOTE )
+  {
+    *lpdwDataSize = This->sp->dwSpRemoteDataSize;
+    *lplpData     = This->sp->lpSpRemoteData;
+  }
+  else if( dwFlags == DPSET_LOCAL )
+  {
+    *lpdwDataSize = This->sp->dwSpLocalDataSize;
+    *lplpData     = This->sp->lpSpLocalData;
+  }
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI IDirectPlaySPImpl_SetSPData
+( LPDIRECTPLAYSP iface,
+  LPVOID lpData, 
+  DWORD dwDataSize, 
+  DWORD dwFlags
+)
+{
+  LPVOID lpSpData;
+
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  TRACE( "Called on process 0x%08lx\n", GetCurrentProcessId() );
+  TRACE( "(%p)->(%p,0x%08lx,0x%08lx)\n", 
+         This, lpData, dwDataSize, dwFlags );
+
+#if 0
+  /* This is what the documentation says... */
+  if( dwFlags != 0 )
+  {
+    return DPERR_INVALIDPARAMS;
+  }
+#else
+  /* ... but most service providers call this with 1 */
+  /* Guess that this is using a DPSET_LOCAL or DPSET_REMOTE type of
+   * thing?
+   */
+  if( dwFlags != 0 )
+  {
+    FIXME( "Undocumented dwFlags 0x%08lx used\n", dwFlags );
+  }
+#endif
+
+  if( dwFlags == DPSET_REMOTE )
+  {
+    lpSpData = DPLAYX_PrivHeapAlloc( HEAP_ZERO_MEMORY, dwDataSize );
+  }  
+  else
+  {
+    lpSpData = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwDataSize ); 
+  }
+
+  CopyMemory( lpSpData, lpData, dwDataSize );
+
+  /* If we have data already allocated, free it and replace it */
+  if( dwFlags == DPSET_REMOTE )
+  {
+    /* FIXME: This doesn't strictly make sense as there is no means to share
+     *        this shared data. Must be misinterpreting something...
+     */
+    if( This->sp->lpSpRemoteData )
+    {
+      DPLAYX_PrivHeapFree( This->sp->lpSpRemoteData );
+    }
+
+    /* NOTE: dwDataSize is also stored in the heap structure */
+    This->sp->dwSpRemoteDataSize = dwDataSize;
+    This->sp->lpSpRemoteData = lpSpData;
+  }
+  else if ( dwFlags == DPSET_LOCAL )
+  {
+    if( This->sp->lpSpLocalData )
+    {
+      HeapFree( GetProcessHeap(), 0, This->sp->lpSpLocalData );
+    }
+
+    This->sp->lpSpLocalData     = lpSpData;
+    This->sp->dwSpLocalDataSize = dwDataSize;
+  }
+
+  return DP_OK;
+}
+
+static VOID WINAPI IDirectPlaySPImpl_SendComplete
+( LPDIRECTPLAYSP iface,
+  LPVOID unknownA, 
+  DWORD unknownB
+)
+{
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  FIXME( "(%p)->(%p,0x%08lx): stub\n", 
+         This, unknownA, unknownB );
+}
+
+
+static struct ICOM_VTABLE(IDirectPlaySP) directPlaySPVT =
+{
+  ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+
+  DPSP_QueryInterface,
+  DPSP_AddRef,
+  DPSP_Release,
+
+  IDirectPlaySPImpl_AddMRUEntry,
+  IDirectPlaySPImpl_CreateAddress,
+  IDirectPlaySPImpl_EnumAddress,
+  IDirectPlaySPImpl_EnumMRUEntries,
+  IDirectPlaySPImpl_GetPlayerFlags,
+  IDirectPlaySPImpl_GetSPPlayerData,
+  IDirectPlaySPImpl_HandleMessage,
+  IDirectPlaySPImpl_SetSPPlayerData,
+  IDirectPlaySPImpl_CreateCompoundAddress,
+  IDirectPlaySPImpl_GetSPData,
+  IDirectPlaySPImpl_SetSPData,
+  IDirectPlaySPImpl_SendComplete
+};
diff --git a/dlls/dplayx/dplaysp.h b/dlls/dplayx/dplaysp.h
new file mode 100644
index 0000000..c2028f0
--- /dev/null
+++ b/dlls/dplayx/dplaysp.h
@@ -0,0 +1,337 @@
+#ifndef __WINE_DIRECT_PLAY_SP_H
+#define __WINE_DIRECT_PLAY_SP_H
+
+#include "dplay.h"
+#include "dplobby.h"
+
+/* GUID for IDirectPlaySP  {0C9F6360-CC61-11cf-ACEC-00AA006886E3} */
+DEFINE_GUID(IID_IDirectPlaySP, 0xc9f6360, 0xcc61, 0x11cf, 0xac, 0xec, 0x0, 0xaa, 0x0, 0x68, 0x86, 0xe3);
+typedef struct IDirectPlaySP IDirectPlaySP, *LPDIRECTPLAYSP;
+
+
+typedef BOOL (CALLBACK* LPENUMMRUCALLBACK)( LPCVOID lpData,
+                                            DWORD  dwDataSize,
+                                            LPVOID lpContext );
+
+/* For SP. Top 16 bits is dplay, bottom 16 is SP */
+#define DPSP_MAJORVERSION 0x00060000
+#define DPSP_DX5VERSION   0x00050000
+#define DPSP_DX3VERSION   0x00040000
+
+#define DPSP_MAJORVERSIONMASK 0xFFFF0000
+#define DPSP_MINORVERSIONMASK 0x0000FFFF
+
+
+/* Some flags */
+#define DPLAYI_PLAYER_SYSPLAYER      0x00000001
+#define DPLAYI_PLAYER_NAMESRVR       0x00000002
+#define DPLAYI_PLAYER_PLAYERINGROUP  0x00000004
+#define DPLAYI_PLAYER_PLAYERLOCAL    0x00000008
+#define DPLAYI_GROUP_SYSGROUP        0x00000020
+#define DPLAYI_GROUP_DPLAYOWNS       0x00000040
+#define DPLAYI_PLAYER_APPSERVER      0x00000080
+#define DPLAYI_GROUP_HIDDEN          0x00000400
+
+/* Define the COM interface */
+#define ICOM_INTERFACE IDirectPlaySP
+#define IDirectPlaySP_METHODS \
+   ICOM_METHOD5(HRESULT,AddMRUEntry, LPCWSTR,lpSection, LPCWSTR,lpKey, LPCVOID,lpData, DWORD,dwDataSize, DWORD,dwMaxEntries ) \
+   ICOM_METHOD6(HRESULT,CreateAddress, REFGUID,guidSP, REFGUID,guidDataType, LPCVOID,lpData, DWORD,dwDataSize, LPVOID,lpAddress,LPDWORD,lpdwAddressSize) \
+   ICOM_METHOD4(HRESULT,EnumAddress, LPDPENUMADDRESSCALLBACK,lpEnumAddressCallback, LPCVOID,lpAddress, DWORD,dwAddressSize, LPVOID,lpContext ) \
+   ICOM_METHOD4(HRESULT,EnumMRUEntries, LPCWSTR,lpSection, LPCWSTR,lpKey, LPENUMMRUCALLBACK,lpEnumMRUCallback, LPVOID,lpContext ) \
+   ICOM_METHOD2(HRESULT,GetPlayerFlags,       DPID,idPlayer, LPDWORD,lpdwPlayerFlags ) \
+   ICOM_METHOD4(HRESULT,GetSPPlayerData,      DPID,idPlayer, LPVOID*,lplpData, LPDWORD,lpdwDataSize, DWORD,dwFlags ) \
+   ICOM_METHOD3(HRESULT,HandleMessage,        LPVOID,lpMessageBody, DWORD,dwMessageBodySize, LPVOID,lpMessageHeader ) \
+   ICOM_METHOD4(HRESULT,SetSPPlayerData,      DPID,idPlayer, LPVOID,lpData, DWORD,dwDataSize, DWORD,dwFlags ) \
+   ICOM_METHOD4(HRESULT,CreateCompoundAddress, LPCDPCOMPOUNDADDRESSELEMENT,lpElements, DWORD,dwElementCount, LPVOID,lpAddress, LPDWORD,lpdwAddressSize ) \
+   ICOM_METHOD3(HRESULT,GetSPData,                LPVOID*,lplpData, LPDWORD,dwDataSize, DWORD,dwFlags ) \
+   ICOM_METHOD3(HRESULT,SetSPData,                LPVOID,lpData, DWORD,dwDataSize, DWORD,dwFlags ) \
+   ICOM_METHOD2(VOID,SendComplete,              LPVOID,, DWORD, )
+
+#define IDirectPlaySP_IMETHODS \
+   IUnknown_IMETHODS \
+   IDirectPlaySP_METHODS
+
+ICOM_DEFINE(IDirectPlaySP,IUnknown)
+#undef ICOM_INTERFACE
+
+/*** IUnknown methods ***/
+#define IDirectPlaySP_QueryInterface(p,a,b) ICOM_CALL2(QueryInterface,p,a,b)
+#define IDirectPlaySP_AddRef(p)             ICOM_CALL (AddRef,p)
+#define IDirectPlaySP_Release(p)            ICOM_CALL (Release,p)
+/*** IDirectPlaySP methods ***/
+#define IDirectPlaySP_AddMRUEntry           ICOM_CALL5(AddMRUEntry,p,a,b,c,d,e)
+#define IDirectPlaySP_CreateAddress         ICOM_CALL6(CreateAddress,p,a,b,c,d,e,f)
+#define IDirectPlaySP_EnumAddress           ICOM_CALL4(EnumAddress,p,a,b,c,d)
+#define IDirectPlaySP_EnumMRUEntries        ICOM_CALL4(EnumMRUEntries,p,a,b,c,d)
+#define IDirectPlaySP_GetPlayerFlags        ICOM_CALL2(GetPlayerFlags,p,a,b)
+#define IDirectPlaySP_GetSPPlayerData       ICOM_CALL4(GetSPPlayerData,p,a,b,c,d)
+#define IDirectPlaySP_HandleMessage         ICOM_CALL3(HandleMessage,p,a,b,c)
+#define IDirectPlaySP_SetSPPlayerData       ICOM_CALL4(SetSPPlayerData,p,a,b,c,d)
+#define IDirectPlaySP_CreateCompoundAddress ICOM_CALL4(CreateCompoundAddress,p,a,b,c,d)
+#define IDirectPlaySP_GetSPData             ICOM_CALL3(GetSPData,p,a,b,c)
+#define IDirectPlaySP_SetSPData             ICOM_CALL3(SetSPData,p,a,b,c)
+#define IDirectPlaySP_SendComplete          ICOM_CALL2(SendComplete,p,a,b)
+
+/* SP Callback stuff */
+
+typedef struct tagDPSP_ADDPLAYERTOGROUPDATA
+{
+  DPID           idPlayer;
+  DPID           idGroup;
+  IDirectPlaySP* lpISP;
+} DPSP_ADDPLAYERTOGROUPDATA, *LPDPSP_ADDPLAYERTOGROUPDATA;
+
+typedef struct tagDPSP_CLOSEDATA
+{
+  IDirectPlaySP* lpISP;
+} DPSP_CLOSEDATA, *LPDPSP_CLOSEDATA;
+
+typedef struct tagDPSP_CREATEGROUPDATA
+{
+  DPID           idGroup;
+  DWORD          dwFlags;
+  LPVOID         lpSPMessageHeader;
+  IDirectPlaySP* lpISP;
+} DPSP_CREATEGROUPDATA, *LPDPSP_CREATEGROUPDATA;
+
+typedef struct tagDPSP_CREATEPLAYERDATA
+{
+  DPID           idPlayer;
+  DWORD          dwFlags;
+  LPVOID         lpSPMessageHeader;
+  IDirectPlaySP* lpISP;
+} DPSP_CREATEPLAYERDATA, *LPDPSP_CREATEPLAYERDATA;
+
+typedef struct tagDPSP_DELETEGROUPDATA
+{
+  DPID           idGroup;
+  DWORD          dwFlags;
+  IDirectPlaySP* lpISP;
+} DPSP_DELETEGROUPDATA, *LPDPSP_DELETEGROUPDATA;
+
+typedef struct tagDPSP_DELETEPLAYERDATA
+{
+  DPID           idPlayer;
+  DWORD          dwFlags;
+  IDirectPlaySP* lpISP;
+} DPSP_DELETEPLAYERDATA, *LPDPSP_DELETEPLAYERDATA;
+
+typedef struct tagDPSP_ENUMSESSIONSDATA
+{
+  LPVOID         lpMessage;
+  DWORD          dwMessageSize;
+  IDirectPlaySP* lpISP;
+  BOOL           bReturnStatus;
+} DPSP_ENUMSESSIONSDATA, *LPDPSP_ENUMSESSIONSDATA;
+
+typedef struct _DPSP_GETADDRESSDATA
+{
+  DPID           idPlayer;
+  DWORD          dwFlags;
+  LPDPADDRESS    lpAddress;
+  LPDWORD        lpdwAddressSize;
+  IDirectPlaySP* lpISP;
+} DPSP_GETADDRESSDATA, *LPDPSP_GETADDRESSDATA;
+
+typedef struct tagDPSP_GETADDRESSCHOICESDATA
+{
+  LPDPADDRESS    lpAddress;
+  LPDWORD        lpdwAddressSize;
+  IDirectPlaySP* lpISP;
+} DPSP_GETADDRESSCHOICESDATA, *LPDPSP_GETADDRESSCHOICESDATA;
+
+typedef struct tagDPSP_GETCAPSDATA
+{
+  DPID           idPlayer;
+  LPDPCAPS       lpCaps;
+  DWORD          dwFlags;
+  IDirectPlaySP* lpISP;
+} DPSP_GETCAPSDATA, *LPDPSP_GETCAPSDATA;
+
+typedef struct tagDPSP_OPENDATA
+{
+  BOOL           bCreate;
+  LPVOID         lpSPMessageHeader;
+  IDirectPlaySP* lpISP;
+  BOOL           bReturnStatus;
+  DWORD          dwOpenFlags;
+  DWORD          dwSessionFlags;
+} DPSP_OPENDATA, *LPDPSP_OPENDATA;
+
+typedef struct tagDPSP_REMOVEPLAYERFROMGROUPDATA
+{
+  DPID           idPlayer;
+  DPID           idGroup;
+  IDirectPlaySP* lpISP;
+} DPSP_REMOVEPLAYERFROMGROUPDATA, *LPDPSP_REMOVEPLAYERFROMGROUPDATA;
+
+typedef struct tagDPSP_REPLYDATA
+{      
+  LPVOID         lpSPMessageHeader;
+  LPVOID         lpMessage;
+  DWORD          dwMessageSize;
+  DPID           idNameServer;
+  IDirectPlaySP* lpISP;
+} DPSP_REPLYDATA, *LPDPSP_REPLYDATA;
+
+typedef struct tagDPSP_SENDDATA
+{
+  DWORD          dwFlags;
+  DPID           idPlayerTo;
+  DPID           idPlayerFrom;
+  LPVOID         lpMessage;
+  DWORD          dwMessageSize;
+  BOOL           bSystemMessage;
+  IDirectPlaySP* lpISP;
+} DPSP_SENDDATA, *LPDPSP_SENDDATA;
+
+typedef struct tagDPSP_SENDTOGROUPDATA
+{
+  DWORD          dwFlags;
+  DPID           idGroupTo;
+  DPID           idPlayerFrom;
+  LPVOID         lpMessage;
+  DWORD          dwMessageSize;
+  IDirectPlaySP* lpISP;
+} DPSP_SENDTOGROUPDATA, *LPDPSP_SENDTOGROUPDATA;
+
+typedef struct tagDPSP_SENDEXDATA
+{
+  IDirectPlaySP* lpISP;
+  DWORD          dwFlags;
+  DPID           idPlayerTo;
+  DPID           idPlayerFrom;
+  LPSGBUFFER     lpSendBuffers;
+  DWORD          cBuffers;
+  DWORD          dwMessageSize;
+  DWORD          dwPriority;
+  DWORD          dwTimeout;
+  LPVOID         lpDPContext;
+  LPDWORD        lpdwSPMsgID;
+  BOOL           bSystemMessage;
+} DPSP_SENDEXDATA, *LPDPSP_SENDEXDATA;
+
+typedef struct tagDPSP_SENDTOGROUPEXDATA
+{
+  IDirectPlaySP* lpISP;
+  DWORD          dwFlags;
+  DPID           idGroupTo;
+  DPID           idPlayerFrom;
+  LPSGBUFFER     lpSendBuffers;
+  DWORD          cBuffers;
+  DWORD          dwMessageSize;
+  DWORD          dwPriority;
+  DWORD          dwTimeout;
+  LPVOID         lpDPContext;
+  LPDWORD        lpdwSPMsgID;
+} DPSP_SENDTOGROUPEXDATA, *LPDPSP_SENDTOGROUPEXDATA;
+
+typedef struct tagDPSP_GETMESSAGEQUEUEDATA
+{
+  IDirectPlaySP* lpISP;
+  DWORD          dwFlags;
+  DPID           idFrom;
+  DPID           idTo;
+  LPDWORD        lpdwNumMsgs;
+  LPDWORD        lpdwNumBytes;
+} DPSP_GETMESSAGEQUEUEDATA, *LPDPSP_GETMESSAGEQUEUEDATA;
+
+#define DPCANCELSEND_PRIORITY 0x00000001
+#define DPCANCELSEND_ALL      0x00000002
+
+typedef struct tagDPSP_CANCELDATA
+{
+  IDirectPlaySP* lpISP;
+  DWORD          dwFlags;
+  LPRGLPVOID     lprglpvSPMsgID;
+  DWORD          cSPMsgID;
+  DWORD          dwMinPriority;
+  DWORD          dwMaxPriority;
+} DPSP_CANCELDATA, *LPDPSP_CANCELDATA;
+
+typedef struct tagDPSP_SHUTDOWNDATA
+{
+  IDirectPlaySP* lpISP;
+} DPSP_SHUTDOWNDATA, *LPDPSP_SHUTDOWNDATA;
+
+
+/* Prototypes returned by SPInit */
+typedef HRESULT (WINAPI *LPDPSP_CREATEPLAYER)(LPDPSP_CREATEPLAYERDATA);
+typedef HRESULT (WINAPI *LPDPSP_DELETEPLAYER)(LPDPSP_DELETEPLAYERDATA);
+typedef HRESULT (WINAPI *LPDPSP_SEND)(LPDPSP_SENDDATA);
+typedef HRESULT (WINAPI *LPDPSP_ENUMSESSIONS)(LPDPSP_ENUMSESSIONSDATA);
+typedef HRESULT (WINAPI *LPDPSP_REPLY)(LPDPSP_REPLYDATA);
+typedef HRESULT (WINAPI *LPDPSP_SHUTDOWN)(void);
+typedef HRESULT (WINAPI *LPDPSP_CREATEGROUP)(LPDPSP_CREATEGROUPDATA);
+typedef HRESULT (WINAPI *LPDPSP_DELETEGROUP)(LPDPSP_DELETEGROUPDATA);
+typedef HRESULT (WINAPI *LPDPSP_ADDPLAYERTOGROUP)(LPDPSP_ADDPLAYERTOGROUPDATA);
+typedef HRESULT (WINAPI *LPDPSP_REMOVEPLAYERFROMGROUP)(LPDPSP_REMOVEPLAYERFROMGROUPDATA);
+typedef HRESULT (WINAPI *LPDPSP_GETCAPS)(LPDPSP_GETCAPSDATA);
+typedef HRESULT (WINAPI *LPDPSP_GETADDRESS)(LPDPSP_GETADDRESSDATA);
+typedef HRESULT (WINAPI *LPDPSP_GETADDRESSCHOICES)(LPDPSP_GETADDRESSCHOICESDATA);
+typedef HRESULT (WINAPI *LPDPSP_OPEN)(LPDPSP_OPENDATA);
+typedef HRESULT (WINAPI *LPDPSP_CLOSE)(void);
+typedef HRESULT (WINAPI *LPDPSP_SENDTOGROUP)(LPDPSP_SENDTOGROUPDATA);
+typedef HRESULT (WINAPI *LPDPSP_SHUTDOWNEX)(LPDPSP_SHUTDOWNDATA);
+typedef HRESULT (WINAPI *LPDPSP_CLOSEEX)(LPDPSP_CLOSEDATA);
+typedef HRESULT (WINAPI *LPDPSP_SENDEX)(LPDPSP_SENDEXDATA);
+typedef HRESULT (WINAPI *LPDPSP_SENDTOGROUPEX)(LPDPSP_SENDTOGROUPEXDATA);
+typedef HRESULT (WINAPI *LPDPSP_CANCEL)(LPDPSP_CANCELDATA);
+typedef HRESULT (WINAPI *LPDPSP_GETMESSAGEQUEUE)(LPDPSP_GETMESSAGEQUEUEDATA);
+
+
+typedef struct tagDPSP_SPCALLBACKS
+{
+    DWORD                        dwSize;
+    DWORD                        dwVersion;
+
+    LPDPSP_ENUMSESSIONS          EnumSessions;          /* Must be provided */
+    LPDPSP_REPLY                 Reply;                 /* Must be provided */
+    LPDPSP_SEND                  Send;                  /* Must be provided */
+    LPDPSP_ADDPLAYERTOGROUP      AddPlayerToGroup;      /* Optional */
+    LPDPSP_CLOSE                 Close;                 /* Optional */
+    LPDPSP_CREATEGROUP           CreateGroup;           /* Optional */
+    LPDPSP_CREATEPLAYER          CreatePlayer;          /* Optional */
+    LPDPSP_DELETEGROUP           DeleteGroup;           /* Optional */
+    LPDPSP_DELETEPLAYER          DeletePlayer;          /* Optional */
+    LPDPSP_GETADDRESS            GetAddress;            /* Optional */
+    LPDPSP_GETCAPS               GetCaps;               /* Optional */
+    LPDPSP_OPEN                  Open;                  /* Optional */
+    LPDPSP_REMOVEPLAYERFROMGROUP RemovePlayerFromGroup; /* Optional */
+    LPDPSP_SENDTOGROUP           SendToGroup;           /* Optional */
+    LPDPSP_SHUTDOWN              Shutdown;              /* Optional */
+
+    LPDPSP_CLOSEEX               CloseEx;               /* Optional */
+    LPDPSP_SHUTDOWNEX            ShutdownEx;            /* Optional */
+    LPDPSP_GETADDRESSCHOICES     GetAddressChoices;     /* Optional */
+
+    LPDPSP_SENDEX                SendEx;                /* Optional */
+    LPDPSP_SENDTOGROUPEX         SendToGroupEx;         /* Optional */
+    LPDPSP_CANCEL                Cancel;                /* Optional */
+    LPDPSP_GETMESSAGEQUEUE       GetMessageQueue;       /* Optional */ 
+} DPSP_SPCALLBACKS, *LPDPSP_SPCALLBACKS;
+
+typedef struct tagSPINITDATA
+{
+    LPDPSP_SPCALLBACKS  lpCB; 
+    IDirectPlaySP*      lpISP;
+    LPWSTR              lpszName;
+    LPGUID              lpGuid;
+    DWORD               dwReserved1; 
+    DWORD               dwReserved2;
+    DWORD               dwSPHeaderSize;     
+    LPDPADDRESS         lpAddress;
+    DWORD               dwAddressSize;      
+    DWORD               dwSPVersion;        
+} SPINITDATA, *LPSPINITDATA;
+
+typedef HRESULT (WINAPI *LPDPSP_SPINIT)(LPSPINITDATA);
+
+/* This variable is exported from the DLL at ordinal 6 to be accessed by the 
+ * SP directly 
+ */
+extern DWORD gdwDPlaySPRefCount;
+
+#endif
+
diff --git a/dlls/dplayx/dplayx_global.c b/dlls/dplayx/dplayx_global.c
index 1e171a6..59d4225 100644
--- a/dlls/dplayx/dplayx_global.c
+++ b/dlls/dplayx/dplayx_global.c
@@ -10,11 +10,14 @@
  *       dplayx.dll data which is accessible from all processes.
  */ 
 
-#include <stdio.h>
 #include "debugtools.h"
 #include "winbase.h"
 #include "winerror.h"
 #include "wine/unicode.h"
+#include "heap.h"
+
+#include "wingdi.h"
+#include "winuser.h"
 
 #include "dplayx_global.h"
 #include "dplayx_messages.h" /* For CreateMessageReceptionThread only */
@@ -42,9 +45,9 @@
 
 
 /* HACK for simple global data right now */ 
-#define dwStaticSharedSize (128 * 1024) /* FIXME: Way too much */
-#define dwDynamicSharedSize (128 * 1024) /* FIXME: Enough? */
-#define dwTotalSharedSize  (dwStaticSharedSize + dwDynamicSharedSize)
+#define dwStaticSharedSize (128 * 1024) /* 128 KBytes */
+#define dwDynamicSharedSize (512 * 1024) /* 512 KBytes */
+#define dwTotalSharedSize  ( dwStaticSharedSize + dwDynamicSharedSize )
 
 
 /* FIXME: Is there no easier way? */
@@ -53,7 +56,6 @@
  * Each block has 4 bytes which are 0 unless used */
 #define dwBlockSize 512
 #define dwMaxBlock  (dwDynamicSharedSize/dwBlockSize)
-DWORD dwBlockOn  = 0;
 
 typedef struct 
 {
@@ -61,7 +63,7 @@
   DWORD data[dwBlockSize-sizeof(DWORD)];
 } DPLAYX_MEM_SLICE;
 
-DPLAYX_MEM_SLICE* lpMemArea;
+static DPLAYX_MEM_SLICE* lpMemArea;
 
 void DPLAYX_PrivHeapFree( LPVOID addr );
 void DPLAYX_PrivHeapFree( LPVOID addr )
@@ -81,6 +83,7 @@
   lpMemArea[ dwBlockUsed ].used = 0;
 }
 
+/* FIXME: This should be static, but is being used for a hack right now */
 LPVOID DPLAYX_PrivHeapAlloc( DWORD flags, DWORD size );
 LPVOID DPLAYX_PrivHeapAlloc( DWORD flags, DWORD size )
 {
@@ -149,17 +152,19 @@
 
   /* Information for dplobby interfaces */
   DWORD           dwAppID;
-  HANDLE          hReceiveEvent;
   DWORD           dwAppLaunchedFromID;
 
   /* Should this lobby app send messages to creator at important life
    * stages
    */
-  BOOL bInformOnConnect;     /* FIXME: Not used yet */
-  BOOL bInformOnSettingRead;
-  BOOL bInformOnAppDeath;    /* FIXME: Not used yet */
+  HANDLE hInformOnAppStart;
+  HANDLE hInformOnAppDeath;
+  HANDLE hInformOnSettingRead;
 
+  /* Sundries */
   BOOL bWaitForConnectionSettings;
+  DWORD dwLobbyMsgThreadId;
+  
 
 } DPLAYX_LOBBYDATA, *LPDPLAYX_LOBBYDATA;
 
@@ -190,6 +195,7 @@
   SECURITY_ATTRIBUTES s_attrib;
   BOOL                bInitializeSharedMemory = FALSE;
   LPVOID              lpDesiredMemoryMapStart = (LPVOID)0x50000000;
+  HANDLE              hInformOnStart;
 
   TRACE( "DPLAYX dll loaded - construct called\n" );
 
@@ -309,6 +315,22 @@
   
   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 %u %s\n", 
+             hInformOnStart, bSuccess ? "succeed" : "failed" );
+
+    /* Close out handle */
+    DPLAYX_GetThisLobbyHandles( &hInformOnStart, NULL, NULL, TRUE );
+  }
+
   return TRUE;
 }
 
@@ -318,8 +340,26 @@
  ***************************************************************************/ 
 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 %u %s\n", 
+             hInformOnDeath, bSuccess ? "succeed" : "failed" );
+
+    /* Close out handle */
+    DPLAYX_GetThisLobbyHandles( NULL, &hInformOnDeath, NULL, TRUE );
+  }
+
+  /* DO CLEAN UP (LAST) */
+
   /* Delete the semaphore */
   CloseHandle( hDplayxSema );
  
@@ -334,9 +374,6 @@
 void DPLAYX_InitializeLobbyDataEntry( LPDPLAYX_LOBBYDATA lpData )
 {
   ZeroMemory( lpData, sizeof( *lpData ) );
-
-  /* Set the handle to a better invalid value */
-  lpData->hReceiveEvent = INVALID_HANDLE_VALUE;
 }
 
 /* NOTE: This must be called with the semaphore aquired. 
@@ -370,7 +407,7 @@
 }
 
 /* Reserve a spot for the new appliction. TRUE means success and FALSE failure.  */
-BOOL DPLAYX_CreateLobbyApplication( DWORD dwAppID, HANDLE hReceiveEvent )
+BOOL DPLAYX_CreateLobbyApplication( DWORD dwAppID )
 {
   UINT i;
 
@@ -388,16 +425,16 @@
     if( lobbyData[ i ].dwAppID == 0 )
     {
       /* This process is now lobbied */
-      TRACE( "Setting lobbyData[%u] for (0x%08lx,%u,0x%08lx)\n",
-              i, dwAppID, hReceiveEvent, GetCurrentProcessId() );
+      TRACE( "Setting lobbyData[%u] for (0x%08lx,0x%08lx)\n",
+              i, dwAppID, GetCurrentProcessId() );
 
       lobbyData[ i ].dwAppID              = dwAppID;
-      lobbyData[ i ].hReceiveEvent        = hReceiveEvent;
       lobbyData[ i ].dwAppLaunchedFromID  = GetCurrentProcessId();
 
-      lobbyData[ i ].bInformOnConnect     = TRUE;
-      lobbyData[ i ].bInformOnSettingRead = TRUE;
-      lobbyData[ i ].bInformOnAppDeath    = TRUE;
+      /* 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;
@@ -437,14 +474,114 @@
   return FALSE;
 }
 
+BOOL DPLAYX_SetLobbyHandles( DWORD dwAppID,
+                             HANDLE hStart, HANDLE hDeath, HANDLE hConnRead )
+{
+  LPDPLAYX_LOBBYDATA lpLData;
+
+  /* Need to explictly give lobby application. Can't set for yourself */
+  if( dwAppID == 0 )
+  {
+    return FALSE;
+  }
+
+  DPLAYX_AquireSemaphore();
+
+  if( !DPLAYX_IsAppIdLobbied( dwAppID, &lpLData ) )
+  {
+    DPLAYX_ReleaseSemaphore();
+    return FALSE;
+  }
+
+  lpLData->hInformOnAppStart    = hStart;
+  lpLData->hInformOnAppDeath    = hDeath;
+  lpLData->hInformOnSettingRead = hConnRead;
+
+  DPLAYX_ReleaseSemaphore();
+
+  return TRUE;
+}
+
+BOOL DPLAYX_GetThisLobbyHandles( LPHANDLE lphStart, 
+                                 LPHANDLE lphDeath,
+                                 LPHANDLE lphConnRead,
+                                 BOOL     bClearSetHandles )
+{
+  LPDPLAYX_LOBBYDATA lpLData;
+
+  DPLAYX_AquireSemaphore();
+
+  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;
+}
+
+
 HRESULT DPLAYX_GetConnectionSettingsA
 ( DWORD dwAppID,
   LPVOID lpData,
-  LPDWORD lpdwDataSize,
-  LPBOOL  lpbSendHaveReadMessage )
+  LPDWORD lpdwDataSize )
 {
   LPDPLAYX_LOBBYDATA lpDplData;
   DWORD              dwRequiredDataSize = 0;
+  HANDLE             hInformOnSettingRead;
 
   DPLAYX_AquireSemaphore();
 
@@ -472,23 +609,33 @@
     return DPERR_BUFFERTOOSMALL;
   }
 
-  /* They have gotten the information */
-  *lpbSendHaveReadMessage = lpDplData->bInformOnSettingRead;
-  lpDplData->bInformOnSettingRead = FALSE;
-
   DPLAYX_CopyConnStructA( (LPDPLCONNECTION)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 %u %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 */
 void DPLAYX_CopyConnStructA( LPDPLCONNECTION dest, LPDPLCONNECTION src )
 {
-  BYTE*              lpStartOfFreeSpace;
+  BYTE* lpStartOfFreeSpace;
 
-  memcpy( dest, src, sizeof( DPLCONNECTION ) );
+  CopyMemory( dest, src, sizeof( DPLCONNECTION ) );
 
   lpStartOfFreeSpace = ((BYTE*)dest) + sizeof( DPLCONNECTION );
 
@@ -497,7 +644,7 @@
   {
     dest->lpSessionDesc = (LPDPSESSIONDESC2)lpStartOfFreeSpace;
     lpStartOfFreeSpace += sizeof( DPSESSIONDESC2 );
-    memcpy( dest->lpSessionDesc, src->lpSessionDesc, sizeof( DPSESSIONDESC2 ) );
+    CopyMemory( dest->lpSessionDesc, src->lpSessionDesc, sizeof( DPSESSIONDESC2 ) );
 
     /* Session names may or may not exist */
     if( src->lpSessionDesc->sess.lpszSessionNameA )
@@ -505,7 +652,7 @@
       strcpy( (LPSTR)lpStartOfFreeSpace, src->lpSessionDesc->sess.lpszSessionNameA );
       dest->lpSessionDesc->sess.lpszSessionNameA = (LPSTR)lpStartOfFreeSpace;
       lpStartOfFreeSpace +=  
-        strlen( (LPSTR)dest->lpSessionDesc->sess.lpszSessionName ) + 1;
+        strlen( (LPSTR)dest->lpSessionDesc->sess.lpszSessionNameA ) + 1;
     }
 
     if( src->lpSessionDesc->pass.lpszPasswordA )
@@ -522,7 +669,7 @@
   {
     dest->lpPlayerName = (LPDPNAME)lpStartOfFreeSpace;
     lpStartOfFreeSpace += sizeof( DPNAME );
-    memcpy( dest->lpPlayerName, src->lpPlayerName, sizeof( DPNAME ) );
+    CopyMemory( dest->lpPlayerName, src->lpPlayerName, sizeof( DPNAME ) );
 
     if( src->lpPlayerName->psn.lpszShortNameA )
     {
@@ -546,7 +693,7 @@
   if( src->lpAddress )
   {
     dest->lpAddress = (LPVOID)lpStartOfFreeSpace;
-    memcpy( lpStartOfFreeSpace, src->lpAddress, src->dwAddressSize );
+    CopyMemory( lpStartOfFreeSpace, src->lpAddress, src->dwAddressSize );
     /* No need to advance lpStartOfFreeSpace as there is no more "dynamic" data */
   }
 }
@@ -554,11 +701,11 @@
 HRESULT DPLAYX_GetConnectionSettingsW
 ( DWORD dwAppID,
   LPVOID lpData,
-  LPDWORD lpdwDataSize,
-  LPBOOL  lpbSendHaveReadMessage )
+  LPDWORD lpdwDataSize )
 {
   LPDPLAYX_LOBBYDATA lpDplData;
   DWORD              dwRequiredDataSize = 0;
+  HANDLE             hInformOnSettingRead;
 
   DPLAYX_AquireSemaphore();
 
@@ -584,14 +731,24 @@
     return DPERR_BUFFERTOOSMALL;
   }
 
-  /* They have gotten the information */
-  *lpbSendHaveReadMessage = lpDplData->bInformOnSettingRead;
-  lpDplData->bInformOnSettingRead = FALSE;
-
   DPLAYX_CopyConnStructW( (LPDPLCONNECTION)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 %u %s\n", 
+             hInformOnSettingRead, bSuccess ? "succeed" : "failed" );
+
+    /* Close out handle */
+    DPLAYX_GetThisLobbyHandles( NULL, NULL, &hInformOnSettingRead, TRUE );
+  }
+
   return DP_OK;
 }
 
@@ -600,7 +757,7 @@
 {
   BYTE*              lpStartOfFreeSpace;
 
-  memcpy( dest, src, sizeof( DPLCONNECTION ) );
+  CopyMemory( dest, src, sizeof( DPLCONNECTION ) );
 
   lpStartOfFreeSpace = ( (BYTE*)dest) + sizeof( DPLCONNECTION );
 
@@ -609,7 +766,7 @@
   {
     dest->lpSessionDesc = (LPDPSESSIONDESC2)lpStartOfFreeSpace;
     lpStartOfFreeSpace += sizeof( DPSESSIONDESC2 );
-    memcpy( dest->lpSessionDesc, src->lpSessionDesc, sizeof( DPSESSIONDESC2 ) ); 
+    CopyMemory( dest->lpSessionDesc, src->lpSessionDesc, sizeof( DPSESSIONDESC2 ) ); 
 
     /* Session names may or may not exist */
     if( src->lpSessionDesc->sess.lpszSessionName )
@@ -634,7 +791,7 @@
   {
     dest->lpPlayerName = (LPDPNAME)lpStartOfFreeSpace;
     lpStartOfFreeSpace += sizeof( DPNAME );
-    memcpy( dest->lpPlayerName, src->lpPlayerName, sizeof( DPNAME ) );
+    CopyMemory( dest->lpPlayerName, src->lpPlayerName, sizeof( DPNAME ) );
    
     if( src->lpPlayerName->psn.lpszShortName )
     {
@@ -658,7 +815,7 @@
   if( src->lpAddress )
   {
     dest->lpAddress = (LPVOID)lpStartOfFreeSpace;
-    memcpy( lpStartOfFreeSpace, src->lpAddress, src->dwAddressSize );  
+    CopyMemory( lpStartOfFreeSpace, src->lpAddress, src->dwAddressSize );  
     /* No need to advance lpStartOfFreeSpace as there is no more "dynamic" data */
   }
 
@@ -883,7 +1040,8 @@
 LPDPSESSIONDESC2 DPLAYX_CopyAndAllocateSessionDesc2A( LPCDPSESSIONDESC2 lpSessionSrc )
 {
    LPDPSESSIONDESC2 lpSessionDest = 
-     (LPDPSESSIONDESC2)DPLAYX_PrivHeapAlloc( HEAP_ZERO_MEMORY, sizeof( *lpSessionSrc ) );
+     (LPDPSESSIONDESC2)HeapAlloc( GetProcessHeap(), 
+                                  HEAP_ZERO_MEMORY, sizeof( *lpSessionSrc ) );
    DPLAYX_CopyIntoSessionDesc2A( lpSessionDest, lpSessionSrc );
 
    return lpSessionDest;
@@ -893,17 +1051,19 @@
 BOOL DPLAYX_CopyIntoSessionDesc2A( LPDPSESSIONDESC2  lpSessionDest,
                                    LPCDPSESSIONDESC2 lpSessionSrc )
 {
-  memcpy( lpSessionDest, lpSessionSrc, sizeof( *lpSessionSrc ) );
+  CopyMemory( lpSessionDest, lpSessionSrc, sizeof( *lpSessionSrc ) );
 
   if( lpSessionSrc->sess.lpszSessionNameA )
   {
     lpSessionDest->sess.lpszSessionNameA =
-      DPLAYX_strdupA( HEAP_ZERO_MEMORY, lpSessionSrc->sess.lpszSessionNameA );
+      HEAP_strdupA( GetProcessHeap(),
+                    HEAP_ZERO_MEMORY, lpSessionSrc->sess.lpszSessionNameA );
   }
   if( lpSessionSrc->pass.lpszPasswordA )
   {
     lpSessionDest->pass.lpszPasswordA =
-      DPLAYX_strdupA( HEAP_ZERO_MEMORY, lpSessionSrc->pass.lpszPasswordA );
+      HEAP_strdupA( GetProcessHeap(),
+                    HEAP_ZERO_MEMORY, lpSessionSrc->pass.lpszPasswordA );
   }
 
   return TRUE;
@@ -1002,6 +1162,24 @@
   return bFound;
 }
 
+BOOL DPLAYX_SetLobbyMsgThreadId( DWORD dwAppId, DWORD dwThreadId )
+{
+  LPDPLAYX_LOBBYDATA lpLobbyData;
+
+  DPLAYX_AquireSemaphore();
+
+  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
@@ -1149,7 +1327,7 @@
       /* For errors not in the list, return HRESULT as a string
          This part is not thread safe */
       WARN( "Unknown error 0x%08lx\n", hr );
-      sprintf( szTempStr, "0x%08lx", hr );
+      wsprintfA( szTempStr, "0x%08lx", hr );
       return szTempStr;
   }
 }
diff --git a/dlls/dplayx/dplayx_global.h b/dlls/dplayx/dplayx_global.h
index 569edb1..bc51533 100644
--- a/dlls/dplayx/dplayx_global.h
+++ b/dlls/dplayx/dplayx_global.h
@@ -9,12 +9,10 @@
 
 HRESULT DPLAYX_GetConnectionSettingsA ( DWORD dwAppID, 
                                         LPVOID lpData, 
-                                        LPDWORD lpdwDataSize,
-                                        LPBOOL  lpbSendHaveReadMessage );
+                                        LPDWORD lpdwDataSize );
 HRESULT DPLAYX_GetConnectionSettingsW ( DWORD dwAppID, 
                                         LPVOID lpData,
-                                        LPDWORD lpdwDataSize, 
-                                        LPBOOL  lpbSendHaveReadMessage );
+                                        LPDWORD lpdwDataSize );
 
 HRESULT DPLAYX_SetConnectionSettingsA ( DWORD dwFlags, 
                                         DWORD dwAppID, 
@@ -23,16 +21,33 @@
                                         DWORD dwAppID, 
                                         LPDPLCONNECTION lpConn );
 
-BOOL DPLAYX_CreateLobbyApplication( DWORD dwAppID, HANDLE hReceiveEvent );
+BOOL DPLAYX_CreateLobbyApplication( DWORD dwAppID );
 BOOL DPLAYX_DestroyLobbyApplication( DWORD dwAppID );
 
 BOOL DPLAYX_WaitForConnectionSettings( BOOL bWait );
 BOOL DPLAYX_AnyLobbiesWaitingForConnSettings(void);
 
+BOOL DPLAYX_SetLobbyHandles( DWORD dwAppID, 
+                             HANDLE hStart, HANDLE hDeath, HANDLE hConnRead );
+BOOL DPLAYX_GetThisLobbyHandles( LPHANDLE lphStart, 
+                                 LPHANDLE lphDeath,
+                                 LPHANDLE lphConnRead, BOOL bClearSetHandles );
+
 LPDPSESSIONDESC2 DPLAYX_CopyAndAllocateLocalSession( UINT* index );
 BOOL DPLAYX_CopyLocalSession( UINT* index, LPDPSESSIONDESC2 lpsd );
 void DPLAYX_SetLocalSession( LPCDPSESSIONDESC2 lpsd );
 
+BOOL DPLAYX_SetLobbyMsgThreadId( DWORD dwAppId, DWORD dwThreadId );
+
+/* FIXME: This should not be here */
+LPVOID DPLAYX_PrivHeapAlloc( DWORD flags, DWORD size );
+void DPLAYX_PrivHeapFree( LPVOID addr );
+
+LPSTR DPLAYX_strdupA( DWORD flags, LPCSTR str );
+LPWSTR DPLAYX_strdupW( DWORD flags, LPCWSTR str );
+/* FIXME: End shared data alloc which should be local */
+
+
 /* Convert a DP or DPL HRESULT code into a string for human consumption */
 LPCSTR DPLAYX_HresultToString( HRESULT hr );
 
diff --git a/dlls/dplayx/dplayx_main.c b/dlls/dplayx/dplayx_main.c
index 56702f9..77c0da7 100644
--- a/dlls/dplayx/dplayx_main.c
+++ b/dlls/dplayx/dplayx_main.c
@@ -5,19 +5,24 @@
  *
  * contact <hunnise@nortelnetworks.com>
  */
-
+#include "winerror.h"
 #include "winbase.h"
 #include "debugtools.h"
 
-#include "initguid.h"
-#include "dplay.h"
-#include "dplobby.h"
+#include "initguid.h"  /* To define the GUIDs */
+#include "dplaysp.h"
 #include "dplayx_global.h"
 
 DEFAULT_DEBUG_CHANNEL(dplay);
 
+DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
+
 static DWORD DPLAYX_dwProcessesAttached = 0;
 
+/* This is a globally exported variable at ordinal 6 of DPLAYX.DLL */
+DWORD gdwDPlaySPRefCount = 0; /* FIXME: Should it be initialized here? */
+
+
 BOOL WINAPI DPLAYX_LibMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
 {
 
@@ -59,3 +64,20 @@
 
   return TRUE;
 }
+
+/***********************************************************************
+ *              DllCanUnloadNow (DPLAYX.10)
+ */
+HRESULT WINAPI DPLAYX_DllCanUnloadNow(void)
+{
+  HRESULT hr = ( gdwDPlaySPRefCount > 0 ) ? S_FALSE : S_OK;
+
+  /* FIXME: Should I be putting a check in for class factory objects 
+   *        as well
+   */
+
+  TRACE( ": returning 0x%08lx\n", hr );
+ 
+  return hr;
+}
+
diff --git a/dlls/dplayx/dplayx_messages.c b/dlls/dplayx/dplayx_messages.c
index 5fb41e6..bc23ef2 100644
--- a/dlls/dplayx/dplayx_messages.c
+++ b/dlls/dplayx/dplayx_messages.c
@@ -9,48 +9,130 @@
 #include "winbase.h"
 #include "debugtools.h"
 
+#include "wingdi.h"
+#include "winuser.h"
+
 #include "dplayx_messages.h"
 
 DEFAULT_DEBUG_CHANNEL(dplay)
 
+typedef struct tagMSGTHREADINFO
+{
+  HANDLE hStart;
+  HANDLE hDeath; 
+  HANDLE hSettingRead;
+  HANDLE hNotifyEvent; 
+} MSGTHREADINFO, *LPMSGTHREADINFO;
 
-static DWORD CALLBACK DPLAYX_MSG_ThreadMain( LPVOID lpContext );
+
+static DWORD CALLBACK DPL_MSG_ThreadMain( LPVOID lpContext );
 
 /* Create the message reception thread to allow the application to receive
  * asynchronous message reception
  */
-DWORD CreateMessageReceptionThread( HANDLE hNotifyEvent )
+DWORD CreateLobbyMessageReceptionThread( HANDLE hNotifyEvent, HANDLE hStart, 
+                                         HANDLE hDeath, HANDLE hConnRead )
 {
-  DWORD dwMsgThreadId;
+  DWORD           dwMsgThreadId;
+  LPMSGTHREADINFO lpThreadInfo;
 
-  if( !DuplicateHandle( 0, hNotifyEvent, 0, NULL, 0, FALSE, 0 ) )
+  lpThreadInfo = HeapAlloc( GetProcessHeap(), 0, sizeof( *lpThreadInfo ) );
+  if( lpThreadInfo == NULL )
   {
-    ERR( "Unable to duplicate event handle\n" );
     return 0;
   }
 
-  /* FIXME: Should most likely store that thread handle */
-  CreateThread( NULL,                  /* Security attribs */
-                0,                     /* Stack */ 
-                DPLAYX_MSG_ThreadMain, /* Msg reception function */
-                (LPVOID)hNotifyEvent,  /* Msg reception function parameter */
-                0,                     /* Flags */
-                &dwMsgThreadId         /* Updated with thread id */
-              );
+  /* The notify event may or may not exist. Depends if async comm or not */
+  if( hNotifyEvent &&
+      !DuplicateHandle( GetCurrentProcess(), hNotifyEvent, 
+                        GetCurrentProcess(), &lpThreadInfo->hNotifyEvent, 
+                        0, FALSE, DUPLICATE_SAME_ACCESS ) )
+  {
+    ERR( "Unable to duplicate event handle\n" );
+    goto error;
+  }
+
+  /* These 3 handles don't need to be duplicated because we don't keep a
+   * reference to them where they're created. They're created specifically
+   * for the message thread 
+   */
+  lpThreadInfo->hStart       = hStart;
+  lpThreadInfo->hDeath       = hDeath;
+  lpThreadInfo->hSettingRead = hConnRead;
+
+  if( !CreateThread( NULL,                  /* Security attribs */
+                     0,                     /* Stack */ 
+                     DPL_MSG_ThreadMain,    /* Msg reception function */
+                     lpThreadInfo,          /* Msg reception func parameter */
+                     0,                     /* Flags */
+                     &dwMsgThreadId         /* Updated with thread id */
+                   )
+    )
+  {
+    ERR( "Unable to create msg thread\n" );
+    goto error;
+  }
+
+  /* FIXME: Should I be closing the handle to the thread or does that
+            terminate the thread? */
  
   return dwMsgThreadId;
+
+error:
+
+  HeapFree( GetProcessHeap(), 0, lpThreadInfo );
+
+  return 0;
 }
-static DWORD CALLBACK DPLAYX_MSG_ThreadMain( LPVOID lpContext )
+
+static DWORD CALLBACK DPL_MSG_ThreadMain( LPVOID lpContext )
 {
-  HANDLE hMsgEvent = (HANDLE)lpContext;
+  LPMSGTHREADINFO lpThreadInfo = (LPMSGTHREADINFO)lpContext;
+  DWORD dwWaitResult;
+ 
+  TRACE( "Msg thread created. Waiting on app startup\n" );
+
+  /* Wait to ensure that the lobby application is started w/ 1 min timeout */
+  dwWaitResult = WaitForSingleObject( lpThreadInfo->hStart, 10000 /* 10 sec */ );
+  if( dwWaitResult == WAIT_TIMEOUT )
+  {
+    FIXME( "Should signal app/wait creation failure (0x%08lx)\n", dwWaitResult );
+    goto end_of_thread;
+  }
+
+  /* Close this handle as it's not needed anymore */
+  CloseHandle( lpThreadInfo->hStart );
+  lpThreadInfo->hStart = 0;
+
+  /* Wait until the lobby knows what it is */
+  dwWaitResult = WaitForSingleObject( lpThreadInfo->hSettingRead, INFINITE );
+  if( dwWaitResult == WAIT_TIMEOUT )
+  {
+    ERR( "App Read connection setting timeout fail (0x%08lx)\n", dwWaitResult );
+  }
+
+  /* Close this handle as it's not needed anymore */
+  CloseHandle( lpThreadInfo->hSettingRead );
+  lpThreadInfo->hSettingRead = 0;
+
+  TRACE( "App created && intialized starting main message reception loop\n" );
 
   for ( ;; )
   {
-    FIXME( "Ho Hum. Msg thread with nothing to do on handle %u\n", hMsgEvent );
+    MSG lobbyMsg;
+#ifdef STRICT
+    HANDLE hNullHandle = NULL;
+#else
+    HANDLE hNullHandle = 0;
+#endif
 
-    SleepEx( 10000, FALSE );  /* 10 secs */
+    GetMessageW( &lobbyMsg, hNullHandle, 0, 0 );
   }
 
-  CloseHandle( hMsgEvent );
+end_of_thread:
+  TRACE( "Msg thread exiting!\n" );
+  HeapFree( GetProcessHeap(), 0, lpThreadInfo );
+
+  return 0;
 }
 
diff --git a/dlls/dplayx/dplayx_messages.h b/dlls/dplayx/dplayx_messages.h
index 117678f..7c8cd07 100644
--- a/dlls/dplayx/dplayx_messages.h
+++ b/dlls/dplayx/dplayx_messages.h
@@ -1,10 +1,136 @@
 
-#ifndef __WINE_DPLAYX_MESSAGES
-#define __WINE_DPLAYX_MESSAGES
+#ifndef __WINE_DPLAYX_MESSAGES__
+#define __WINE_DPLAYX_MESSAGES__
 
 #include "windef.h"
+#include "dplay.h"
+#include "rpc.h" /* For GUID */
 
-DWORD CreateMessageReceptionThread( HANDLE hNotifyEvent );
+DWORD CreateLobbyMessageReceptionThread( HANDLE hNotifyEvent, HANDLE hStart, 
+                                         HANDLE hDeath, HANDLE hConnRead );
 
 
+/* Message types etc. */
+#include "pshpack1.h"
+
+/* Non provided messages for DPLAY - guess work which may be wrong :( */
+#define DPMSGCMD_ENUMSESSIONSREPLY    1
+#define DPMSGCMD_ENUMSESSIONSREQUEST  2
+
+#define DPMSGCMD_GETSETNAMETABLE      3 /* Request info from NS about 
+                                           existing players/groups etc. Is
+                                           also used for reply */
+
+#define DPMSGCMD_REQUESTNEWPLAYERID   5
+
+#define DPMSGCMD_NEWPLAYERIDREPLY     7
+#define DPMSGCMD_CREATESESSION        8  
+#define DPMSGCMD_CREATENEWPLAYER      9
+#define DPMSGCMD_SYSTEMMESSAGE        10 
+
+#define DPMSGCMD_DELETEGROUP          12
+
+#define DPMSGCMD_ENUMGROUPS           17
+
+/* This is what DP 6 defines it as. Don't know what it means. All messages
+ * defined below are DPMSGVER_DP6.
+ */
+#define DPMSGVER_DP6 11
+
+/* MAGIC number at the start of all dplay packets ("play" in ASCII) */
+#define DPMSGMAGIC_DPLAYMSG  0x79616c70
+
+/* All messages sent from the system are sent with this at the beginning of
+ * the message.
+ */
+
+/* Size is 4 bytes */
+typedef struct tagDPMSG_SENDENVELOPE
+{
+  DWORD dwMagic;
+  WORD  wCommandId;
+  WORD  wVersion;
+} DPMSG_SENDENVELOPE, *LPDPMSG_SENDENVELOPE;
+typedef const DPMSG_SENDENVELOPE* LPCDPMSG_SENDENVELOPE;
+
+typedef struct tagDPMSG_SYSMSGENVELOPE
+{
+  DWORD dwPlayerFrom;
+  DWORD dwPlayerTo;
+} DPMSG_SYSMSGENVELOPE, *LPDPMSG_SYSMSGENVELOPE;
+typedef const DPMSG_SYSMSGENVELOPE* LPCDPMSG_SYSMSGENVELOPE;
+
+
+typedef struct tagDPMSG_ENUMSESSIONSREPLY
+{
+  DPMSG_SENDENVELOPE envelope;
+
+#if 0
+  DWORD dwSize;  /* Size of DPSESSIONDESC2 struct */
+  DWORD dwFlags; /* Sessions flags */
+
+  GUID guidInstance; /* Not 100% sure this is what it is... */
+
+  GUID guidApplication;
+
+  DWORD dwMaxPlayers;
+  DWORD dwCurrentPlayers;
+
+  BYTE unknown[36];
+#else
+  DPSESSIONDESC2 sd;
+#endif
+
+  DWORD dwUnknown;  /* Seems to be equal to 0x5c which is a "\\" */
+                    /* Encryption package string? */
+
+  /* At the end we have ... */
+  /* WCHAR wszSessionName[1];  Var length with NULL terminal */
+
+} DPMSG_ENUMSESSIONSREPLY, *LPDPMSG_ENUMSESSIONSREPLY;
+typedef const DPMSG_ENUMSESSIONSREPLY* LPCDPMSG_ENUMSESSIONSREPLY;
+
+typedef struct tagDPMSG_ENUMSESSIONSREQUEST
+{
+  DPMSG_SENDENVELOPE envelope;
+
+  GUID  guidApplication;
+
+  DWORD dwPasswordSize; /* A Guess. This is normally 0x00000000. */
+                        /* This might be the name server DPID which
+                           is needed for the reply */
+
+  DWORD dwFlags; /* dwFlags from EnumSessions */
+
+} DPMSG_ENUMSESSIONSREQUEST, *LPDPMSG_ENUMSESSIONSREQUEST;
+typedef const DPMSG_ENUMSESSIONSREQUEST* LPCDPMSG_ENUMSESSIONSREQUEST;
+
+/* Size is 146 received - with 18 or 20 bytes header = ~128 bytes */
+typedef struct tagDPMSG_CREATESESSION
+{
+  DPMSG_SENDENVELOPE envelope;
+} DPMSG_CREATESESSION, *LPDPMSG_CREATESESSION;
+typedef const DPMSG_CREATESESSION* LPCDPMSG_CREATESESSION;
+
+/* 28 bytes - ~18 header ~= 10 bytes msg */
+typedef struct tagDPMSG_REQUESTNEWPLAYERID
+{
+  DPMSG_SENDENVELOPE envelope;
+
+  
+
+} DPMSG_REQUESTNEWPLAYERID, *LPDPMSG_REQUESTNEWPLAYERID;
+typedef const DPMSG_REQUESTNEWPLAYERID* LPCDPMSG_REQUESTNEWPLAYERID;
+
+/* 64 byte - ~18 header ~= 46 bytes msg */
+typedef struct tagDPMSG_NEWPLAYERIDREPLY
+{
+  DPMSG_SENDENVELOPE envelope;
+
+} DPMSG_NEWPLAYERIDREPLY, *LPDPMSG_NEWPLAYERIDREPLY;
+typedef const DPMSG_NEWPLAYERIDREPLY* LPCDPMSG_NEWPLAYERIDREPLY;
+
+
+#include "poppack.h"
+
 #endif
diff --git a/dlls/dplayx/dplayx_queue.h b/dlls/dplayx/dplayx_queue.h
index 471f25c..142713a 100644
--- a/dlls/dplayx/dplayx_queue.h
+++ b/dlls/dplayx/dplayx_queue.h
@@ -7,6 +7,8 @@
 #ifndef __WINE_DPLAYX_QUEUE_H
 #define __WINE_DPLAYX_QUEUE_H
 
+#include "winbase.h"
+
 #define DPQ_INSERT(a,b,c) DPQ_INSERT_IN_TAIL(a,b,c)
 
 /*
@@ -33,6 +35,19 @@
         (head).lpQHLast = &(head).lpQHFirst; \
 } while(0)
 
+/* Front of the queue */
+#define DPQ_FIRST( head ) ( (head).lpQHFirst )
+
+/* Check if the queue has any elements */
+#define DPQ_IS_EMPTY( head ) ( DPQ_FIRST(head) == NULL ) 
+
+/* Next entry -- FIXME: Convert everything over to this macro ... */
+#define DPQ_NEXT( elem ) (elem).lpQNext
+
+#define DPQ_IS_ENDOFLIST( elem ) \
+    ( DPQ_NEXT(elem) == NULL )
+
+/* Insert element at end of queue */
 #define DPQ_INSERT_IN_TAIL(head, elm, field)     \
 do {                                             \
         (elm)->field.lpQNext = NULL;             \
@@ -41,6 +56,7 @@
         (head).lpQHLast = &(elm)->field.lpQNext; \
 } while(0)
 
+/* Remove element from the queue */
 #define DPQ_REMOVE(head, elm, field)                    \
 do {                                                    \
         if (((elm)->field.lpQNext) != NULL)             \
@@ -53,18 +69,20 @@
 
 /* head - pointer to DPQ_HEAD struct
  * elm  - how to find the next element
- * field - to be concatenated to rc to compare with fieldToEqual
- * fieldToEqual - The value that we're looking for
+ * field - to be concatenated to rc to compare with fieldToCompare
+ * fieldToCompare - The value that we're comparing against
+ * fieldCompareOperator - The logical operator to compare field and
+ *                        fieldToCompare. 
  * rc - Variable to put the return code. Same type as (head).lpQHFirst
  */
-#define DPQ_FIND_ENTRY( head, elm, field, fieldToEqual, rc )   \
+#define DPQ_FIND_ENTRY( head, elm, field, fieldCompareOperator, fieldToCompare, rc )\
 do {                                                           \
   (rc) = (head).lpQHFirst; /* NULL head? */                    \
                                                                \
   while( rc )                                                  \
   {                                                            \
       /* What we're searching for? */                          \
-      if( (rc)->field == (fieldToEqual) )                      \
+      if( (rc)->field fieldCompareOperator (fieldToCompare) )  \
       {                                                        \
         break; /* rc == correct element */                     \
       }                                                        \
@@ -81,12 +99,14 @@
 /* head - pointer to DPQ_HEAD struct
  * elm  - how to find the next element
  * field - to be concatenated to rc to compare with fieldToEqual
- * fieldToEqual - The value that we're looking for
+ * fieldToCompare - The value that we're comparing against
+ * fieldCompareOperator - The logical operator to compare field and
+ *                        fieldToCompare.
  * rc - Variable to put the return code. Same type as (head).lpQHFirst
  */
-#define DPQ_REMOVE_ENTRY( head, elm, field, fieldToEqual, rc ) \
+#define DPQ_REMOVE_ENTRY( head, elm, field, fieldCompareOperator, fieldToCompare, rc )\
 do {                                                           \
-  DPQ_FIND_ENTRY( head, elm, field, fieldToEqual, rc );        \
+  DPQ_FIND_ENTRY( head, elm, field, fieldCompareOperator, fieldToCompare, rc );\
                                                                \
   /* Was the element found? */                                 \
   if( rc )                                                     \
@@ -95,4 +115,24 @@
   }                                                            \
 } while(0)
 
+/* Delete the entire queue 
+ * head - pointer to the head of the queue
+ * field - field to access the next elements of the queue
+ * type - type of the pointer to the element element
+ * df - a delete function to be called. Declared with DPQ_DECL_DELETECB.
+ */
+#define DPQ_DELETEQ( head, field, type, df )            \
+while( !DPQ_IS_EMPTY(head) )                               \
+{                                                       \
+  type holder = (head).lpQHFirst;                      \
+  DPQ_REMOVE( head, holder, field );                    \
+  df( holder );                                \
+}
+
+/* How to define the method to be passed to DPQ_DELETEQ */
+#define DPQ_DECL_DELETECB( name, type ) void name( type elem )
+
+/* Prototype of a method which just performs a HeapFree on the elem */
+DPQ_DECL_DELETECB( cbDeleteElemFromHeap, LPVOID );
+
 #endif /* __WINE_DPLAYX_QUEUE_H */
diff --git a/dlls/dplayx/dplobby.c b/dlls/dplayx/dplobby.c
index 8507cce..3a451fe 100644
--- a/dlls/dplayx/dplobby.c
+++ b/dlls/dplayx/dplobby.c
@@ -5,8 +5,6 @@
  * <presently under construction - contact hunnise@nortelnetworks.com>
  *
  */
-#include <string.h>
-
 #include "winerror.h"
 #include "winnt.h"
 #include "winreg.h"
@@ -17,6 +15,7 @@
 #include "dpinit.h"
 #include "dplayx_global.h"
 #include "dplayx_messages.h"
+#include "dplayx_queue.h"
 
 DEFAULT_DEBUG_CHANNEL(dplay)
 
@@ -39,13 +38,16 @@
 
 
 
-static HRESULT DPL_EnumAddress( LPDPENUMADDRESSCALLBACK lpEnumAddressCallback, LPCVOID lpAddress,
+extern HRESULT DPL_EnumAddress( LPDPENUMADDRESSCALLBACK lpEnumAddressCallback, LPCVOID lpAddress,
                                 DWORD dwAddressSize, LPVOID lpContext );
 
 static HRESULT WINAPI DPL_ConnectEx( IDirectPlayLobbyAImpl* This, 
                                      DWORD dwFlags, REFIID riid, 
                                      LPVOID* lplpDP, IUnknown* pUnk );
 
+BOOL DPL_CreateAndSetLobbyHandles( DWORD dwDestProcessId, HANDLE hDestProcess,
+                                   LPHANDLE lphStart, LPHANDLE lphDeath,
+                                   LPHANDLE lphRead );
 
 
 /*****************************************************************************
@@ -61,16 +63,23 @@
  * get a run time trap, as that won't have any data.
  *
  */
+struct DPLMSG
+{
+  DPQ_ENTRY( DPLMSG ) msgs;  /* Link to next queued message */
+};
+typedef struct DPLMSG* LPDPLMSG;
 
 typedef struct tagDirectPlayLobbyIUnknownData
 {
-  DWORD             ref;
+  ULONG             ulObjRef;
   CRITICAL_SECTION  DPL_lock;
 } DirectPlayLobbyIUnknownData;
 
 typedef struct tagDirectPlayLobbyData
 {
   HKEY  hkCallbackKeyHack;
+  DWORD dwMsgThread;
+  DPQ_HEAD( DPLMSG ) msgs;  /* List of messages received */
 } DirectPlayLobbyData;
 
 typedef struct tagDirectPlayLobby2Data
@@ -84,6 +93,7 @@
 } DirectPlayLobby3Data;
 
 #define DPL_IMPL_FIELDS \
+ ULONG ulInterfaceRef; \
  DirectPlayLobbyIUnknownData*  unk; \
  DirectPlayLobbyData*          dpl; \
  DirectPlayLobby2Data*         dpl2; \
@@ -120,6 +130,112 @@
 
 
 
+static BOOL DPL_CreateIUnknown( LPVOID lpDPL ) 
+{
+  ICOM_THIS(IDirectPlayLobbyAImpl,lpDPL);
+
+  This->unk = (DirectPlayLobbyIUnknownData*)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 
+                                                       sizeof( *(This->unk) ) ); 
+  if ( This->unk == NULL )
+  {
+    return FALSE; 
+  }
+
+  InitializeCriticalSection( &This->unk->DPL_lock );
+
+  return TRUE;
+}
+
+static BOOL DPL_DestroyIUnknown( LPVOID lpDPL )
+{
+  ICOM_THIS(IDirectPlayLobbyAImpl,lpDPL);
+
+  DeleteCriticalSection( &This->unk->DPL_lock );
+  HeapFree( GetProcessHeap(), 0, This->unk );
+
+  return TRUE;
+}
+
+static BOOL DPL_CreateLobby1( LPVOID lpDPL )
+{
+  ICOM_THIS(IDirectPlayLobbyAImpl,lpDPL);
+
+  This->dpl = (DirectPlayLobbyData*)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                               sizeof( *(This->dpl) ) );
+  if ( This->dpl == NULL )
+  {
+    return FALSE;
+  }
+
+  DPQ_INIT( This->dpl->msgs ); 
+
+  return TRUE;  
+}
+
+static BOOL DPL_DestroyLobby1( LPVOID lpDPL )
+{
+  ICOM_THIS(IDirectPlayLobbyAImpl,lpDPL);
+
+  if( This->dpl->dwMsgThread )
+  {
+    FIXME( "Should kill the msg thread\n" );
+  }
+
+  DPQ_DELETEQ( This->dpl->msgs, msgs, LPDPLMSG, cbDeleteElemFromHeap );
+
+  /* Delete the contents */
+  HeapFree( GetProcessHeap(), 0, This->dpl );
+
+  return TRUE;
+}
+
+static BOOL DPL_CreateLobby2( LPVOID lpDPL )
+{
+  ICOM_THIS(IDirectPlayLobby2AImpl,lpDPL);
+
+  This->dpl2 = (DirectPlayLobby2Data*)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                                 sizeof( *(This->dpl2) ) );
+  if ( This->dpl2 == NULL )
+  {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+static BOOL DPL_DestroyLobby2( LPVOID lpDPL )
+{
+  ICOM_THIS(IDirectPlayLobby2AImpl,lpDPL);
+
+  HeapFree( GetProcessHeap(), 0, This->dpl2 );
+
+  return TRUE;
+}
+
+static BOOL DPL_CreateLobby3( LPVOID lpDPL )
+{
+  ICOM_THIS(IDirectPlayLobby3AImpl,lpDPL);
+
+  This->dpl3 = (DirectPlayLobby3Data*)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                                 sizeof( *(This->dpl3) ) );
+  if ( This->dpl3 == NULL )
+  {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+static BOOL DPL_DestroyLobby3( LPVOID lpDPL )
+{
+  ICOM_THIS(IDirectPlayLobby3AImpl,lpDPL);
+
+  HeapFree( GetProcessHeap(), 0, This->dpl3 );
+
+  return TRUE;
+}
+
+
 /* The COM interface for upversioning an interface
  * We've been given a GUID (riid) and we need to replace the present
  * interface with that of the requested interface.
@@ -142,439 +258,162 @@
  *    queries successfully for a second, and through that pointer queries
  *    successfully for a third interface, a query for the first interface
  *    through the pointer for the third interface must succeed.
- *
- *  As you can see, this interface doesn't qualify but will most likely
- *  be good enough for the time being.
  */
-
-
-BOOL DPL_CreateIUnknown( LPVOID lpDPL ) 
-{
-  ICOM_THIS(IDirectPlayLobbyAImpl,lpDPL);
-
-  This->unk = (DirectPlayLobbyIUnknownData*)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 
-                                                       sizeof( *(This->unk) ) ); 
-  if ( This->unk == NULL )
-  {
-    return FALSE; 
-  }
-
-  InitializeCriticalSection( &This->unk->DPL_lock );
-
-  IDirectPlayLobby_AddRef( (LPDIRECTPLAYLOBBYA)lpDPL );
-
-  return TRUE;
-}
-
-BOOL DPL_DestroyIUnknown( LPVOID lpDPL )
-{
-  ICOM_THIS(IDirectPlayLobbyAImpl,lpDPL);
-
-  DeleteCriticalSection( &This->unk->DPL_lock );
-  HeapFree( GetProcessHeap(), 0, This->unk );
-
-  return TRUE;
-}
-
-BOOL DPL_CreateLobby1( LPVOID lpDPL )
-{
-  ICOM_THIS(IDirectPlayLobbyAImpl,lpDPL);
-
-  This->dpl = (DirectPlayLobbyData*)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
-                                               sizeof( *(This->dpl) ) );
-  if ( This->dpl == NULL )
-  {
-    return FALSE;
-  }
-
-  return TRUE;  
-}
-
-BOOL DPL_DestroyLobby1( LPVOID lpDPL )
-{
-  ICOM_THIS(IDirectPlayLobbyAImpl,lpDPL);
-
-  /* Delete the contents */
-  HeapFree( GetProcessHeap(), 0, This->dpl );
-
-  return TRUE;
-}
-
-BOOL DPL_CreateLobby2( LPVOID lpDPL )
-{
-  ICOM_THIS(IDirectPlayLobby2AImpl,lpDPL);
-
-  This->dpl2 = (DirectPlayLobby2Data*)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
-                                                 sizeof( *(This->dpl2) ) );
-  if ( This->dpl2 == NULL )
-  {
-    return FALSE;
-  }
-
-  return TRUE;
-}
-
-BOOL DPL_DestroyLobby2( LPVOID lpDPL )
-{
-  ICOM_THIS(IDirectPlayLobby2AImpl,lpDPL);
-
-  HeapFree( GetProcessHeap(), 0, This->dpl2 );
-
-  return TRUE;
-}
-
-BOOL DPL_CreateLobby3( LPVOID lpDPL )
-{
-  ICOM_THIS(IDirectPlayLobby3AImpl,lpDPL);
-
-  This->dpl3 = (DirectPlayLobby3Data*)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
-                                                 sizeof( *(This->dpl3) ) );
-  if ( This->dpl3 == NULL )
-  {
-    return FALSE;
-  }
-
-  return TRUE;
-}
-
-BOOL DPL_DestroyLobby3( LPVOID lpDPL )
-{
-  ICOM_THIS(IDirectPlayLobby3AImpl,lpDPL);
-
-  HeapFree( GetProcessHeap(), 0, This->dpl3 );
-
-  return TRUE;
-}
-
-
-/* Helper function for DirectPlayLobby  QueryInterface */ 
 extern 
-HRESULT directPlayLobby_QueryInterface
+HRESULT DPL_CreateInterface
          ( REFIID riid, LPVOID* ppvObj )
 {
+  TRACE( " for %s\n", debugstr_guid( riid ) );
+
+  *ppvObj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                       sizeof( IDirectPlayLobbyWImpl ) );
+
+  if( *ppvObj == NULL )
+  {
+    return DPERR_OUTOFMEMORY;
+  }
+
   if( IsEqualGUID( &IID_IDirectPlayLobby, riid ) )
   {
-    *ppvObj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
-                         sizeof( IDirectPlayLobbyWImpl ) );
-
-    if( *ppvObj == NULL )
-    {
-      return E_OUTOFMEMORY;
-    }
-    
-    /* new scope for variable declaration */
-    {
-      ICOM_THIS(IDirectPlayLobbyWImpl,*ppvObj);
-
-      ICOM_VTBL(This) = &directPlayLobbyWVT;
-
-      if ( DPL_CreateIUnknown( (LPVOID)This ) &&
-           DPL_CreateLobby1( (LPVOID)This ) 
-         )
-      { 
-        return S_OK;
-      }
-
-    }
-
-    goto error;
+    ICOM_THIS(IDirectPlayLobbyWImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlayLobbyWVT;
   }
   else if( IsEqualGUID( &IID_IDirectPlayLobbyA, riid ) )
   {
-    *ppvObj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
-                         sizeof( IDirectPlayLobbyAImpl ) );
-
-    if( *ppvObj == NULL )
-    {
-      return E_OUTOFMEMORY;
-    }
-
-    {
-      ICOM_THIS(IDirectPlayLobbyAImpl,*ppvObj);
-
-      ICOM_VTBL(This) = &directPlayLobbyAVT;
-
-      if ( DPL_CreateIUnknown( (LPVOID)This ) &&
-           DPL_CreateLobby1( (LPVOID)This ) 
-         )
-      { 
-        return S_OK;
-      }
-    }
-
-    goto error;
+    ICOM_THIS(IDirectPlayLobbyAImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlayLobbyAVT;
   }
   else if( IsEqualGUID( &IID_IDirectPlayLobby2, riid ) )
   {
-    *ppvObj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
-                         sizeof( IDirectPlayLobby2WImpl ) );
-
-    if( *ppvObj == NULL )
-    {
-      return E_OUTOFMEMORY;
-    }
-
-    {
-      ICOM_THIS(IDirectPlayLobby2WImpl,*ppvObj);
-
-      ICOM_VTBL(This) = &directPlayLobby2WVT;
-
-      if ( DPL_CreateIUnknown( (LPVOID)This ) &&
-           DPL_CreateLobby1( (LPVOID)This ) &&
-           DPL_CreateLobby2( (LPVOID)This )
-         )
-      {
-        return S_OK;
-      }
-    }
-
-    goto error;
+    ICOM_THIS(IDirectPlayLobby2WImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlayLobby2WVT;
   }
   else if( IsEqualGUID( &IID_IDirectPlayLobby2A, riid ) )
   {
-    *ppvObj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
-                         sizeof( IDirectPlayLobby2AImpl ) );
-
-    if( *ppvObj == NULL )
-    {
-      return E_OUTOFMEMORY;
-    }
-
-    {
-      ICOM_THIS(IDirectPlayLobby2AImpl,*ppvObj);
-
-      ICOM_VTBL(This) = &directPlayLobby2AVT;
-
-      if ( DPL_CreateIUnknown( (LPVOID)This ) &&
-           DPL_CreateLobby1( (LPVOID)This )  &&
-           DPL_CreateLobby2( (LPVOID)This )
-         )
-      {
-        return S_OK;
-      }
-    }
-
-    goto error;
+    ICOM_THIS(IDirectPlayLobby2AImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlayLobby2AVT;
   }
   else if( IsEqualGUID( &IID_IDirectPlayLobby3, riid ) )
   {
-    *ppvObj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
-                         sizeof( IDirectPlayLobby3WImpl ) );
-
-    if( *ppvObj == NULL )
-    {
-      return E_OUTOFMEMORY;
-    }
-
-    {
-      ICOM_THIS(IDirectPlayLobby3WImpl,*ppvObj);
-
-      ICOM_VTBL(This) = &directPlayLobby3WVT;
-
-      if ( DPL_CreateIUnknown( *ppvObj ) &&
-           DPL_CreateLobby1( *ppvObj ) &&
-           DPL_CreateLobby2( *ppvObj ) &&
-           DPL_CreateLobby3( *ppvObj )
-         )
-      {
-        return S_OK;
-      }
-    }    
- 
-    goto error;
+    ICOM_THIS(IDirectPlayLobby3WImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlayLobby3WVT;
   }
   else if( IsEqualGUID( &IID_IDirectPlayLobby3A, riid ) )
   {
-     *ppvObj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
-                          sizeof( IDirectPlayLobby3AImpl ) );
+    ICOM_THIS(IDirectPlayLobby3AImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlayLobby3AVT;
+  }
+  else
+  {
+    /* Unsupported interface */
+    HeapFree( GetProcessHeap(), 0, *ppvObj );
+    *ppvObj = NULL;
 
-    if( *ppvObj == NULL )
-    {
-      return E_OUTOFMEMORY;
-    }
-
-    {
-      ICOM_THIS(IDirectPlayLobby3AImpl,*ppvObj);
-
-      ICOM_VTBL(This) = &directPlayLobby3AVT;
-
-      if ( DPL_CreateIUnknown( *ppvObj ) &&
-           DPL_CreateLobby1( *ppvObj ) &&
-           DPL_CreateLobby2( *ppvObj ) &&
-           DPL_CreateLobby3( *ppvObj )
-         )
-      {
-        return S_OK;
-      }
-    }
-
-    goto error;
+    return E_NOINTERFACE;
+  }
+  
+  /* Initialize it */
+  if ( DPL_CreateIUnknown( *ppvObj ) &&
+       DPL_CreateLobby1( *ppvObj ) &&
+       DPL_CreateLobby2( *ppvObj ) &&
+       DPL_CreateLobby3( *ppvObj )
+     )
+  { 
+    IDirectPlayLobby_AddRef( (LPDIRECTPLAYLOBBY)*ppvObj );
+    return S_OK;
   }
 
-  /* Unsupported interface */
+  /* Initialize failed, destroy it */
+  DPL_DestroyLobby3( *ppvObj );
+  DPL_DestroyLobby2( *ppvObj );
+  DPL_DestroyLobby1( *ppvObj );
+  DPL_DestroyIUnknown( *ppvObj );
+  HeapFree( GetProcessHeap(), 0, *ppvObj );
+
   *ppvObj = NULL;
-  return E_NOINTERFACE;
-
-error:
-
-    DPL_DestroyLobby3( *ppvObj );
-    DPL_DestroyLobby2( *ppvObj );
-    DPL_DestroyLobby1( *ppvObj );
-    DPL_DestroyIUnknown( *ppvObj );
-    HeapFree( GetProcessHeap(), 0, *ppvObj );
-
-    *ppvObj = NULL;
-    return DPERR_NOMEMORY;
+  return DPERR_NOMEMORY;
 }
 
-static HRESULT WINAPI IDirectPlayLobbyAImpl_QueryInterface
+static HRESULT WINAPI DPL_QueryInterface
 ( LPDIRECTPLAYLOBBYA iface,
   REFIID riid,
   LPVOID* ppvObj )
 {
   ICOM_THIS(IDirectPlayLobbyAImpl,iface);
-  TRACE("(%p)->(%p,%p)\n", This, riid, ppvObj );
+  TRACE("(%p)->(%s,%p)\n", This, debugstr_guid( riid ), ppvObj );
 
-  if( IsEqualGUID( &IID_IUnknown, riid )  ||
-      IsEqualGUID( &IID_IDirectPlayLobbyA, riid )
-    )
+  *ppvObj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                       sizeof( IDirectPlayLobbyWImpl ) );
+
+  if( *ppvObj == NULL )
   {
-    IDirectPlayLobby_AddRef( iface );
-    *ppvObj = This;
-    return S_OK;
+    return DPERR_OUTOFMEMORY;
   }
 
-  return directPlayLobby_QueryInterface( riid, ppvObj );
+  CopyMemory( *ppvObj, iface, sizeof( IDirectPlayLobbyWImpl )  );
+  (*(IDirectPlayLobbyWImpl**)ppvObj)->ulInterfaceRef = 0;
 
-}
-
-static HRESULT WINAPI IDirectPlayLobbyW_QueryInterface
-( LPDIRECTPLAYLOBBY iface,
-  REFIID riid,
-  LPVOID* ppvObj )
-{
-  ICOM_THIS(IDirectPlayLobbyWImpl,iface);
-  TRACE("(%p)->(%p,%p)\n", This, riid, ppvObj );
-
-  if( IsEqualGUID( &IID_IUnknown, riid )  ||
-      IsEqualGUID( &IID_IDirectPlayLobby, riid )
-    )
+  if( IsEqualGUID( &IID_IDirectPlayLobby, riid ) )
   {
-    IDirectPlayLobby_AddRef( iface );
-    *ppvObj = This;
-    return S_OK;
+    ICOM_THIS(IDirectPlayLobbyWImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlayLobbyWVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlayLobbyA, riid ) )
+  {
+    ICOM_THIS(IDirectPlayLobbyAImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlayLobbyAVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlayLobby2, riid ) )
+  {
+    ICOM_THIS(IDirectPlayLobby2WImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlayLobby2WVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlayLobby2A, riid ) )
+  {
+    ICOM_THIS(IDirectPlayLobby2AImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlayLobby2AVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlayLobby3, riid ) )
+  {
+    ICOM_THIS(IDirectPlayLobby3WImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlayLobby3WVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlayLobby3A, riid ) )
+  {
+    ICOM_THIS(IDirectPlayLobby3AImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlayLobby3AVT;
+  }
+  else
+  {
+    /* Unsupported interface */
+    HeapFree( GetProcessHeap(), 0, *ppvObj );
+    *ppvObj = NULL;
+
+    return E_NOINTERFACE;
   }
 
-  return directPlayLobby_QueryInterface( riid, ppvObj );
-}
+  IDirectPlayLobby_AddRef( (LPDIRECTPLAYLOBBY)*ppvObj );
 
-
-static HRESULT WINAPI IDirectPlayLobby2AImpl_QueryInterface
-( LPDIRECTPLAYLOBBY2A iface,
-  REFIID riid,
-  LPVOID* ppvObj )
-{
-  ICOM_THIS(IDirectPlayLobby2AImpl,iface);
-  TRACE("(%p)->(%p,%p)\n", This, riid, ppvObj );
-
-  /* Compare riids. We know this object is a direct play lobby 2A object.
-     If we are asking about the same type of interface we're fine.
-   */
-  if( IsEqualGUID( &IID_IUnknown, riid )  ||
-      IsEqualGUID( &IID_IDirectPlayLobby2A, riid )
-    )
-  {
-    IDirectPlayLobby_AddRef( iface );
-    *ppvObj = This;
-    return S_OK;
-  }
-  return directPlayLobby_QueryInterface( riid, ppvObj ); 
-}
-
-static HRESULT WINAPI IDirectPlayLobby2WImpl_QueryInterface
-( LPDIRECTPLAYLOBBY2 iface,
-  REFIID riid,
-  LPVOID* ppvObj )
-{
-  ICOM_THIS(IDirectPlayLobby2WImpl,iface);
-
-  /* Compare riids. We know this object is a direct play lobby 2 object.
-     If we are asking about the same type of interface we're fine.
-   */
-  if( IsEqualGUID( &IID_IUnknown, riid ) ||
-      IsEqualGUID( &IID_IDirectPlayLobby2, riid ) 
-    )
-  {
-    IDirectPlayLobby_AddRef( iface );
-    *ppvObj = This;
-    return S_OK;
-  }
-
-  return directPlayLobby_QueryInterface( riid, ppvObj ); 
-
-}
-
-static HRESULT WINAPI IDirectPlayLobby3AImpl_QueryInterface
-( LPDIRECTPLAYLOBBY3A iface,
-  REFIID riid,
-  LPVOID* ppvObj )
-{
-  ICOM_THIS(IDirectPlayLobby3AImpl,iface);
-
-  /* Compare riids. We know this object is a direct play lobby 3 object.
-     If we are asking about the same type of interface we're fine.
-   */
-  if( IsEqualGUID( &IID_IUnknown, riid ) ||
-      IsEqualGUID( &IID_IDirectPlayLobby3A, riid )
-    )
-  {
-    IDirectPlayLobby_AddRef( iface );
-    *ppvObj = This;
-    return S_OK;
-  }
-
-  return directPlayLobby_QueryInterface( riid, ppvObj );
-
-}
-
-static HRESULT WINAPI IDirectPlayLobby3WImpl_QueryInterface
-( LPDIRECTPLAYLOBBY3 iface,
-  REFIID riid,
-  LPVOID* ppvObj )
-{
-  ICOM_THIS(IDirectPlayLobby3WImpl,iface);
-
-  /* Compare riids. We know this object is a direct play lobby 3 object.
-     If we are asking about the same type of interface we're fine.
-   */
-  if( IsEqualGUID( &IID_IUnknown, riid ) ||
-      IsEqualGUID( &IID_IDirectPlayLobby3, riid )
-    )
-  {
-    IDirectPlayLobby_AddRef( iface );
-    *ppvObj = This;
-    return S_OK;
-  }
-
-  return directPlayLobby_QueryInterface( riid, ppvObj );
-
+  return S_OK;
 }
 
 /* 
  * Simple procedure. Just increment the reference count to this
  * structure and return the new reference count.
  */
-static ULONG WINAPI IDirectPlayLobbyImpl_AddRef
+static ULONG WINAPI DPL_AddRef
 ( LPDIRECTPLAYLOBBY iface )
 {
-  ULONG refCount;
+  ULONG ulInterfaceRefCount, ulObjRefCount;
   ICOM_THIS(IDirectPlayLobbyWImpl,iface);
 
-  refCount = InterlockedIncrement( &This->unk->ref );
+  ulObjRefCount       = InterlockedIncrement( &This->unk->ulObjRef );
+  ulInterfaceRefCount = InterlockedIncrement( &This->ulInterfaceRef );
 
-  TRACE("ref count incremented to %lu for %p\n", refCount, This );
+  TRACE( "ref count incremented to %lu:%lu for %p\n",
+         ulInterfaceRefCount, ulObjRefCount, This );
 
-  return refCount;
+  return ulObjRefCount;
 }
 
 /*
@@ -582,27 +421,33 @@
  * If the object no longer has any reference counts, free up the associated
  * memory.
  */
-static ULONG WINAPI IDirectPlayLobbyAImpl_Release
+static ULONG WINAPI DPL_Release
 ( LPDIRECTPLAYLOBBYA iface )
 {
-  ULONG refCount;
+  ULONG ulInterfaceRefCount, ulObjRefCount;
   ICOM_THIS(IDirectPlayLobbyAImpl,iface);
 
-  refCount = InterlockedDecrement( &This->unk->ref );
+  ulObjRefCount       = InterlockedDecrement( &This->unk->ulObjRef );
+  ulInterfaceRefCount = InterlockedDecrement( &This->ulInterfaceRef );
 
-  TRACE("ref count decremeneted to %lu for %p\n", refCount, This );
+  TRACE( "ref count decremented to %lu:%lu for %p\n",
+         ulInterfaceRefCount, ulObjRefCount, This );
 
   /* Deallocate if this is the last reference to the object */
-  if( refCount )
+  if( ulObjRefCount == 0 )
   {
      DPL_DestroyLobby3( This );
      DPL_DestroyLobby2( This );
      DPL_DestroyLobby1( This );
      DPL_DestroyIUnknown( This );
-     HeapFree( GetProcessHeap(), 0, This );
   }
 
-  return refCount;
+  if( ulInterfaceRefCount == 0 )
+  {
+    HeapFree( GetProcessHeap(), 0, This );
+  }
+
+  return ulInterfaceRefCount;
 }
 
 
@@ -628,25 +473,25 @@
 
   FIXME("(%p)->(0x%08lx,%p,%p): semi stub\n", This, dwFlags, lplpDP, pUnk );
 
-  if( dwFlags || pUnk )
+  if( pUnk )
   {
      return DPERR_INVALIDPARAMS;
   }
 
+  /* Backwards compatibility */
+  if( dwFlags == 0 )
+  {
+    dwFlags = DPCONNECT_RETURNSTATUS;
+  }
+
   /* Create the DirectPlay interface */
-  if( ( hr = directPlay_QueryInterface( riid, lplpDP ) ) != DP_OK )
+  if( ( hr = DP_CreateInterface( riid, lplpDP ) ) != DP_OK )
   {
      ERR( "error creating interface for %s:%s.\n", 
           debugstr_guid( riid ), DPLAYX_HresultToString( hr ) );
      return hr;
   }
 
-  /* - Need to call IDirectPlay::EnumConnections with the service provider to get that good information
-   * - Need to call CreateAddress to create the lpConnection param for IDirectPlay::InitializeConnection
-   * - Call IDirectPlay::InitializeConnection
-   * - Call IDirectPlay::Open 
-   */
-
   /* FIXME: Is it safe/correct to use appID of 0? */
   hr = IDirectPlayLobby_GetConnectionSettings( (LPDIRECTPLAYLOBBY)This, 
                                                0, NULL, &dwConnSize ); 
@@ -670,6 +515,17 @@
     return hr;
   }
 
+#if 0
+  /* - Need to call IDirectPlay::EnumConnections with the service provider to get that good information
+   * - Need to call CreateAddress to create the lpConnection param for IDirectPlay::InitializeConnection
+   * - Call IDirectPlay::InitializeConnection
+   */
+
+  /* Now initialize the Service Provider */
+  hr = IDirectPlayX_InitializeConnection( (*(LPDIRECTPLAY2*)lplpDP),
+#endif
+                                          
+
   /* Setup flags to pass into DirectPlay::Open */
   if( dwFlags & DPCONNECT_RETURNSTATUS )
   {
@@ -811,7 +667,7 @@
   return DPL_EnumAddress( lpEnumAddressCallback, lpAddress, dwAddressSize, lpContext );
 }
 
-static HRESULT DPL_EnumAddress( LPDPENUMADDRESSCALLBACK lpEnumAddressCallback, LPCVOID lpAddress,
+extern HRESULT DPL_EnumAddress( LPDPENUMADDRESSCALLBACK lpEnumAddressCallback, LPCVOID lpAddress,
                                 DWORD dwAddressSize, LPVOID lpContext )
 { 
   DWORD dwTotalSizeEnumerated = 0;
@@ -824,14 +680,16 @@
     DWORD dwSizeThisEnumeration; 
 
     /* Invoke the enum method. If false is returned, stop enumeration */
-    if ( !lpEnumAddressCallback( &lpElements->guidDataType, lpElements->dwDataSize, 
-                                 lpElements + sizeof( DPADDRESS ), lpContext ) )
+    if ( !lpEnumAddressCallback( &lpElements->guidDataType, 
+                                 lpElements->dwDataSize, 
+                                 (BYTE*)lpElements + sizeof( DPADDRESS ), 
+                                 lpContext ) )
     {
       break;
     }
 
     dwSizeThisEnumeration  = sizeof( DPADDRESS ) + lpElements->dwDataSize;
-    lpAddress = (char *) lpAddress + dwSizeThisEnumeration;
+    lpAddress = (BYTE*) lpAddress + dwSizeThisEnumeration;
     dwTotalSizeEnumerated += dwSizeThisEnumeration;
   }
 
@@ -1113,7 +971,6 @@
 {
   ICOM_THIS(IDirectPlayLobbyAImpl,iface);
   HRESULT hr; 
-  BOOL    bSendHaveReadSettingsMessage = FALSE;
 
   TRACE("(%p)->(0x%08lx,%p,%p)\n", This, dwAppID, lpData, lpdwDataSize );
 
@@ -1121,17 +978,11 @@
 
   hr = DPLAYX_GetConnectionSettingsA( dwAppID, 
                                       lpData, 
-                                      lpdwDataSize,
-                                      &bSendHaveReadSettingsMessage
+                                      lpdwDataSize
                                     );
 
   LeaveCriticalSection( &This->unk->DPL_lock );
 
-  if( bSendHaveReadSettingsMessage )
-  {
-    FIXME( "Send a DPSYS_CONNECTIONSETTINGSREAD message\n" );
-  }
-
   return hr;
 }
 
@@ -1143,7 +994,6 @@
 {
   ICOM_THIS(IDirectPlayLobbyWImpl,iface);
   HRESULT hr;
-  BOOL    bSendHaveReadSettingsMessage = FALSE;
 
   TRACE("(%p)->(0x%08lx,%p,%p)\n", This, dwAppID, lpData, lpdwDataSize );
  
@@ -1151,17 +1001,11 @@
 
   hr = DPLAYX_GetConnectionSettingsW( dwAppID, 
                                       lpData, 
-                                      lpdwDataSize,
-                                      &bSendHaveReadSettingsMessage
+                                      lpdwDataSize
                                     );
 
   LeaveCriticalSection( &This->unk->DPL_lock );
 
-  if( bSendHaveReadSettingsMessage )
-  {
-    FIXME( "Send a DPSYS_CONNECTIONSETTINGSREAD message\n" );
-  }
-
   return hr;
 }
 
@@ -1288,6 +1132,54 @@
   return TRUE; /* Keep enumerating, haven't found the application yet */ 
 }
 
+BOOL DPL_CreateAndSetLobbyHandles( DWORD dwDestProcessId, HANDLE hDestProcess,
+                                   LPHANDLE lphStart, LPHANDLE lphDeath, 
+                                   LPHANDLE lphRead )
+{
+  /* These are the handles for the created process */
+  HANDLE hAppStart, hAppDeath, hAppRead, hTemp;
+  SECURITY_ATTRIBUTES s_attrib;
+
+  s_attrib.nLength              = sizeof( s_attrib );
+  s_attrib.lpSecurityDescriptor = NULL;
+  s_attrib.bInheritHandle       = TRUE;
+
+  /* FIXME: Is there a handle leak here? */
+  hTemp = CreateEventA( &s_attrib, TRUE, FALSE, NULL );
+  *lphStart = ConvertToGlobalHandle( hTemp );
+
+  hTemp = CreateEventA( &s_attrib, TRUE, FALSE, NULL );
+  *lphDeath = ConvertToGlobalHandle( hTemp );
+
+  hTemp = CreateEventA( &s_attrib, TRUE, FALSE, NULL );
+  *lphRead  = ConvertToGlobalHandle( hTemp );
+
+  if( ( !DuplicateHandle( GetCurrentProcess(), *lphStart, 
+                          hDestProcess, &hAppStart, 
+                          0, FALSE, DUPLICATE_SAME_ACCESS ) ) ||
+      ( !DuplicateHandle( GetCurrentProcess(), *lphDeath, 
+                          hDestProcess, &hAppDeath, 
+                          0, FALSE, DUPLICATE_SAME_ACCESS ) ) ||
+      ( !DuplicateHandle( GetCurrentProcess(), *lphRead, 
+                          hDestProcess, &hAppRead, 
+                          0, FALSE, DUPLICATE_SAME_ACCESS ) )
+    )
+  {
+    /* FIXME: Handle leak... */
+    ERR( "Unable to dup handles\n" );
+    return FALSE;
+  }
+
+  if( !DPLAYX_SetLobbyHandles( dwDestProcessId, 
+                               hAppStart, hAppDeath, hAppRead ) )
+  {
+    return FALSE; 
+  }
+
+  return TRUE;
+}
+
+
 /********************************************************************
  *
  * Starts an application and passes to it all the information to
@@ -1309,8 +1201,10 @@
   PROCESS_INFORMATION newProcessInfo;
   LPSTR appName;
   DWORD dwSuspendCount;
+  HANDLE hStart, hDeath, hSettingRead;
 
-  TRACE( "(%p)->(0x%08lx,%p,%p,%x)\n", This, dwFlags, lpdwAppID, lpConn, hReceiveEvent );
+  TRACE( "(%p)->(0x%08lx,%p,%p,%x)\n", 
+         This, dwFlags, lpdwAppID, lpConn, hReceiveEvent );
 
   if( dwFlags != 0 )
   {
@@ -1380,7 +1274,7 @@
   HeapFree( GetProcessHeap(), 0, enumData.lpszCurrentDirectory );
 
   /* Reserve this global application id! */
-  if( !DPLAYX_CreateLobbyApplication( newProcessInfo.dwProcessId, hReceiveEvent ) )
+  if( !DPLAYX_CreateLobbyApplication( newProcessInfo.dwProcessId ) )
   {
     ERR( "Unable to create global application data for 0x%08lx\n",
            newProcessInfo.dwProcessId );
@@ -1394,17 +1288,22 @@
     return hr;
   }
 
-  /* Everything seems to have been set correctly, update the dwAppID */
-  *lpdwAppID = newProcessInfo.dwProcessId;
+  /* Setup the handles for application notification */
+  DPL_CreateAndSetLobbyHandles( newProcessInfo.dwProcessId,
+                                newProcessInfo.hProcess,
+                                &hStart, &hDeath, &hSettingRead );
 
-  if( hReceiveEvent )
-  {
-    FIXME( "Need to store msg thread id\n" );
-    CreateMessageReceptionThread( hReceiveEvent );
-  } 
+  /* Setup the message thread ID */
+  This->dpl->dwMsgThread = 
+    CreateLobbyMessageReceptionThread( hReceiveEvent, hStart, hDeath, hSettingRead );
+
+  DPLAYX_SetLobbyMsgThreadId( newProcessInfo.dwProcessId, This->dpl->dwMsgThread );
 
   LeaveCriticalSection( &This->unk->DPL_lock );
 
+  /* Everything seems to have been set correctly, update the dwAppID */
+  *lpdwAppID = newProcessInfo.dwProcessId;
+
   /* Unsuspend the process - should return the prev suspension count */ 
   if( ( dwSuspendCount = ResumeThread( newProcessInfo.hThread ) ) != 1 )
   {
@@ -1481,8 +1380,11 @@
   if( hr == DPERR_NOTLOBBIED )
   {
     FIXME( "Unlobbied app setting connections. Is this correct behavior?\n" );
-    dwAppID = GetCurrentProcessId();
-    DPLAYX_CreateLobbyApplication( dwAppID, 0 );
+    if( dwAppID == 0 )
+    {
+       dwAppID = GetCurrentProcessId();
+    }
+    DPLAYX_CreateLobbyApplication( dwAppID );
     hr = DPLAYX_SetConnectionSettingsW( dwFlags, dwAppID, lpConn );
   }
 
@@ -1513,7 +1415,7 @@
   {
     FIXME( "Unlobbied app setting connections. Is this correct behavior?\n" );
     dwAppID = GetCurrentProcessId();
-    DPLAYX_CreateLobbyApplication( dwAppID, 0 );
+    DPLAYX_CreateLobbyApplication( dwAppID );
     hr = DPLAYX_SetConnectionSettingsA( dwFlags, dwAppID, lpConn );
   }
 
@@ -1658,7 +1560,7 @@
   {
     LPDPADDRESS lpdpAddress = (LPDPADDRESS)lpAddress;
 
-    lpdpAddress->guidDataType = DPAID_TotalSize;
+    CopyMemory( &lpdpAddress->guidDataType, &DPAID_TotalSize, sizeof( GUID ) );
     lpdpAddress->dwDataSize = sizeof( DWORD );
     lpAddress = (char *) lpAddress + sizeof( DPADDRESS );
 
@@ -1677,11 +1579,12 @@
     {
       LPDPADDRESS lpdpAddress = (LPDPADDRESS)lpAddress;
 
-      lpdpAddress->guidDataType = lpElements->guidDataType;
+      CopyMemory( &lpdpAddress->guidDataType, &lpElements->guidDataType, 
+                  sizeof( GUID ) );
       lpdpAddress->dwDataSize = sizeof( GUID );
       lpAddress = (char *) lpAddress + sizeof( DPADDRESS );
 
-      *((LPGUID)lpAddress) = *((LPGUID)lpElements->lpData);
+      CopyMemory( lpAddress, lpElements->lpData, sizeof( GUID ) );
       lpAddress = (char *) lpAddress + sizeof( GUID );
     }
     else if ( ( IsEqualGUID( &lpElements->guidDataType, &DPAID_Phone ) ) ||
@@ -1691,7 +1594,8 @@
     {
       LPDPADDRESS lpdpAddress = (LPDPADDRESS)lpAddress;
 
-      lpdpAddress->guidDataType = lpElements->guidDataType;
+      CopyMemory( &lpdpAddress->guidDataType, &lpElements->guidDataType, 
+                  sizeof( GUID ) );
       lpdpAddress->dwDataSize = lpElements->dwDataSize;
       lpAddress = (char *) lpAddress + sizeof( DPADDRESS );
 
@@ -1707,7 +1611,8 @@
     {
       LPDPADDRESS lpdpAddress = (LPDPADDRESS)lpAddress;
 
-      lpdpAddress->guidDataType = lpElements->guidDataType;
+      CopyMemory( &lpdpAddress->guidDataType, &lpElements->guidDataType, 
+                  sizeof( GUID ) );
       lpdpAddress->dwDataSize = lpElements->dwDataSize;
       lpAddress = (char *) lpAddress + sizeof( DPADDRESS );
 
@@ -1720,7 +1625,8 @@
     {
       LPDPADDRESS lpdpAddress = (LPDPADDRESS)lpAddress;
 
-      lpdpAddress->guidDataType = lpElements->guidDataType;
+      CopyMemory( &lpdpAddress->guidDataType, &lpElements->guidDataType, 
+                  sizeof( GUID ) );
       lpdpAddress->dwDataSize = lpElements->dwDataSize;
       lpAddress = (char *) lpAddress + sizeof( DPADDRESS );
 
@@ -1731,11 +1637,12 @@
     {
       LPDPADDRESS lpdpAddress = (LPDPADDRESS)lpAddress;
 
-      lpdpAddress->guidDataType = lpElements->guidDataType;
+      CopyMemory( &lpdpAddress->guidDataType, &lpElements->guidDataType, 
+                  sizeof( GUID ) );
       lpdpAddress->dwDataSize = lpElements->dwDataSize;
       lpAddress = (char *) lpAddress + sizeof( DPADDRESS );
 
-      memcpy( lpAddress, lpElements->lpData, sizeof( DPADDRESS ) ); 
+      CopyMemory( lpAddress, lpElements->lpData, sizeof( DPADDRESS ) ); 
       lpAddress = (char *) lpAddress + sizeof( DPADDRESS );
     }
   }
@@ -1839,9 +1746,9 @@
 {
   ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
 
-  IDirectPlayLobbyAImpl_QueryInterface,
-  XCAST(AddRef)IDirectPlayLobbyImpl_AddRef,
-  XCAST(Release)IDirectPlayLobbyAImpl_Release,
+  XCAST(QueryInterface)DPL_QueryInterface,
+  XCAST(AddRef)DPL_AddRef,
+  XCAST(Release)DPL_Release,
 
   IDirectPlayLobbyAImpl_Connect,
   IDirectPlayLobbyAImpl_CreateAddress,
@@ -1870,9 +1777,9 @@
 {
   ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
 
-  IDirectPlayLobbyW_QueryInterface,
-  XCAST(AddRef)IDirectPlayLobbyImpl_AddRef,
-  XCAST(Release)IDirectPlayLobbyAImpl_Release,
+  XCAST(QueryInterface)DPL_QueryInterface,
+  XCAST(AddRef)DPL_AddRef,
+  XCAST(Release)DPL_Release,
 
   IDirectPlayLobbyWImpl_Connect,
   IDirectPlayLobbyWImpl_CreateAddress, 
@@ -1900,9 +1807,9 @@
 {
   ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
 
-  IDirectPlayLobby2AImpl_QueryInterface,
-  XCAST(AddRef)IDirectPlayLobbyImpl_AddRef,
-  XCAST(Release)IDirectPlayLobbyAImpl_Release,
+  XCAST(QueryInterface)DPL_QueryInterface,
+  XCAST(AddRef)DPL_AddRef,
+  XCAST(Release)DPL_Release,
 
   XCAST(Connect)IDirectPlayLobbyAImpl_Connect,
   XCAST(CreateAddress)IDirectPlayLobbyAImpl_CreateAddress,
@@ -1932,9 +1839,9 @@
 {
   ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
 
-  IDirectPlayLobby2WImpl_QueryInterface,
-  XCAST(AddRef)IDirectPlayLobbyImpl_AddRef, 
-  XCAST(Release)IDirectPlayLobbyAImpl_Release,
+  XCAST(QueryInterface)DPL_QueryInterface,
+  XCAST(AddRef)DPL_AddRef, 
+  XCAST(Release)DPL_Release,
 
   XCAST(Connect)IDirectPlayLobbyWImpl_Connect,
   XCAST(CreateAddress)IDirectPlayLobbyWImpl_CreateAddress,
@@ -1964,9 +1871,9 @@
 static ICOM_VTABLE(IDirectPlayLobby3) directPlayLobby3AVT =
 {
   ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
-  IDirectPlayLobby3AImpl_QueryInterface,
-  XCAST(AddRef)IDirectPlayLobbyImpl_AddRef,
-  XCAST(Release)IDirectPlayLobbyAImpl_Release,
+  XCAST(QueryInterface)DPL_QueryInterface,
+  XCAST(AddRef)DPL_AddRef,
+  XCAST(Release)DPL_Release,
 
   XCAST(Connect)IDirectPlayLobbyAImpl_Connect,
   XCAST(CreateAddress)IDirectPlayLobbyAImpl_CreateAddress,
@@ -2001,9 +1908,9 @@
 static ICOM_VTABLE(IDirectPlayLobby3) directPlayLobby3WVT =
 {
   ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
-  IDirectPlayLobby3WImpl_QueryInterface,
-  XCAST(AddRef)IDirectPlayLobbyImpl_AddRef,
-  XCAST(Release)IDirectPlayLobbyAImpl_Release,
+  XCAST(QueryInterface)DPL_QueryInterface,
+  XCAST(AddRef)DPL_AddRef,
+  XCAST(Release)DPL_Release,
 
   XCAST(Connect)IDirectPlayLobbyWImpl_Connect,
   XCAST(CreateAddress)IDirectPlayLobbyWImpl_CreateAddress,
@@ -2029,7 +1936,7 @@
 
 /*********************************************************
  *
- * Direct Play and Direct Play Lobby Interface Implementation 
+ * Direct Play Lobby Interface Implementation 
  * 
  *********************************************************/ 
 
@@ -2049,13 +1956,20 @@
   /* Parameter Check: lpGUIDSP, lpUnk & lpData must be NULL. dwDataSize must
    * equal 0. These fields are mostly for future expansion.
    */
-  if ( lpGUIDDSP || lpUnk || lpData || dwDataSize )
+  if ( lpGUIDDSP || lpData || dwDataSize )
   {
      *lplpDPL = NULL;
      return DPERR_INVALIDPARAMS;
   }
 
-  return directPlayLobby_QueryInterface( &IID_IDirectPlayLobbyA, (void**)lplpDPL ); 
+  if( lpUnk )
+  {
+     *lplpDPL = NULL;
+     ERR("Bad parameters!\n" );
+     return CLASS_E_NOAGGREGATION;
+  }
+
+  return DPL_CreateInterface( &IID_IDirectPlayLobbyA, (void**)lplpDPL ); 
 }
 
 /***************************************************************************
@@ -2088,6 +2002,5 @@
      return CLASS_E_NOAGGREGATION;
   }
 
-  return directPlayLobby_QueryInterface( &IID_IDirectPlayLobby, (void**)lplpDPL );  
-
+  return DPL_CreateInterface( &IID_IDirectPlayLobby, (void**)lplpDPL );  
 }
diff --git a/dlls/dplayx/name_server.c b/dlls/dplayx/name_server.c
index d81ad57..07a66dc 100644
--- a/dlls/dplayx/name_server.c
+++ b/dlls/dplayx/name_server.c
@@ -10,101 +10,190 @@
 
 #include "winbase.h"
 #include "debugtools.h"
+#include "heap.h"
 
 #include "dplayx_global.h"
 #include "name_server.h"
+#include "dplaysp.h"
+#include "dplayx_messages.h"
+#include "dplayx_queue.h"
 
-/* FIXME: Need to aquire the interface semaphore before didlling data */
+/* Can't seem to solve unresolved reference even with import */
+#define HACK_TIMEGETTIME
+
+#if defined( HACK_TIMEGETTIME )
+static DWORD timeGetTime(void);
+#else
+#include "mmsystem.h"
+#endif
+
+/* FIXME: Need to create a crit section, store and use it */
 
 DEFAULT_DEBUG_CHANNEL(dplay);
 
 /* NS specific structures */
-typedef struct tagNSCacheData
+struct NSCacheData
 {
-  struct tagNSCacheData* next;
+  DPQ_ENTRY(NSCacheData) next;
 
+  DWORD dwTime; /* Time at which data was last known valid */
   LPDPSESSIONDESC2 data;
 
-} NSCacheData, *lpNSCacheData;
+  LPVOID lpNSAddrHdr;
 
-typedef struct tagNSCache
+};
+typedef struct NSCacheData NSCacheData, *lpNSCacheData;
+
+struct NSCache
 {
-  lpNSCacheData present; /* keep track of what is to be looked at */
-  lpNSCacheData first; 
-} NSCache, *lpNSCache;
+  lpNSCacheData present; /* keep track of what is to be looked at when walking */
 
-/* Local Prototypes */
-static void NS_InvalidateSessionCache( lpNSCache lpCache );
-
+  DPQ_HEAD(NSCacheData) first;
+}; 
+typedef struct NSCache NSCache, *lpNSCache;
 
 /* Name Server functions 
  * --------------------- 
  */
 void NS_SetLocalComputerAsNameServer( LPCDPSESSIONDESC2 lpsd )
 {
+#if 0
   DPLAYX_SetLocalSession( lpsd );
+#endif
+}
+
+/* Store the given NS remote address for future reference */
+void NS_SetRemoteComputerAsNameServer( LPVOID                    lpNSAddrHdr,
+                                       DWORD                     dwHdrSize,
+                                       LPDPMSG_ENUMSESSIONSREPLY lpMsg,
+                                       LPVOID                    lpNSInfo )
+{
+  lpNSCache     lpCache = (lpNSCache)lpNSInfo;
+  lpNSCacheData lpCacheNode;
+
+  TRACE( "%p, %p, %p\n", lpNSAddrHdr, lpMsg, lpNSInfo );
+
+  /* FIXME: Should check to see if the reply is for an existing session. If
+   *        so we just update the contents and update the timestamp.
+   */
+  lpCacheNode = (lpNSCacheData)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 
+                                          sizeof( *lpCacheNode ) );
+
+  if( lpCacheNode == NULL )
+  {
+    ERR( "no memory for NS node\n" );
+    return;
+  }
+
+  lpCacheNode->lpNSAddrHdr = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                        dwHdrSize );
+  CopyMemory( lpCacheNode->lpNSAddrHdr, lpNSAddrHdr, dwHdrSize );
+              
+
+  lpCacheNode->data = (LPDPSESSIONDESC2)HeapAlloc( GetProcessHeap(),
+                                                   HEAP_ZERO_MEMORY, 
+                                                   sizeof( *lpCacheNode->data ) );
+
+  if( lpCacheNode->data == NULL )
+  {
+    ERR( "no memory for SESSIONDESC2\n" );
+    return;
+  }
+
+  CopyMemory( lpCacheNode->data, &lpMsg->sd, sizeof( *lpCacheNode->data ) );
+  lpCacheNode->data->sess.lpszSessionNameA = HEAP_strdupWtoA( GetProcessHeap(),
+                                                              HEAP_ZERO_MEMORY, 
+                                                              (LPWSTR)(lpMsg+1) );
+
+  lpCacheNode->dwTime = timeGetTime();
+
+  DPQ_INSERT(lpCache->first, lpCacheNode, next );
+
+  lpCache->present = lpCacheNode;
+
+  /* Use this message as an oportunity to weed out any old sessions so 
+   * that we don't enum them again 
+   */
+  NS_PruneSessionCache( lpNSInfo );
+}
+
+LPVOID NS_GetNSAddr( LPVOID lpNSInfo )
+{
+  lpNSCache lpCache = (lpNSCache)lpNSInfo;
+
+  FIXME( ":quick stub\n" );
+
+  /* Ok. Cheat and don't search for the correct stuff just take the first.
+   * FIXME: In the future how are we to know what is _THE_ enum we used?
+   */
+
+  return lpCache->first.lpQHFirst->lpNSAddrHdr;
 }
 
 /* This function is responsible for sending a request for all other known
    nameservers to send us what sessions they have registered locally
  */
-void NS_SendSessionRequestBroadcast( LPVOID lpNSInfo )
+HRESULT NS_SendSessionRequestBroadcast( LPCGUID lpcGuid,
+                                        DWORD dwFlags,
+                                        LPSPINITDATA lpSpData )
+                                     
 {
-  UINT index = 0;
-  lpNSCache lpCache = (lpNSCache)lpNSInfo;
-  LPDPSESSIONDESC2 lpTmp = NULL; 
+  DPSP_ENUMSESSIONSDATA data;
+  LPDPMSG_ENUMSESSIONSREQUEST lpMsg;
 
-  /* Invalidate the session cache for the interface */
-  NS_InvalidateSessionCache( lpCache );
- 
-  /* Add the local known sessions to the cache */
-  if( ( lpTmp = DPLAYX_CopyAndAllocateLocalSession( &index ) ) != NULL )
-  {
-    lpCache->first = (lpNSCacheData)HeapAlloc( GetProcessHeap(), 
-                                               HEAP_ZERO_MEMORY,
-                                               sizeof( *(lpCache->first) ) );
-    lpCache->first->data = lpTmp;
-    lpCache->first->next = NULL;
-    lpCache->present = lpCache->first;
+  TRACE( "enumerating for guid %s\n", debugstr_guid( lpcGuid ) );
 
-    while( ( lpTmp = DPLAYX_CopyAndAllocateLocalSession( &index ) ) != NULL )
-    {
-      lpCache->present->next = (lpNSCacheData)HeapAlloc( GetProcessHeap(), 
-                                                         HEAP_ZERO_MEMORY,
-                                                         sizeof( *(lpCache->present) ) ); 
-      lpCache->present = lpCache->present->next;
-      lpCache->present->data = lpTmp;
-      lpCache->present->next = NULL;
-    }
+  /* Get the SP to deal with sending the EnumSessions request */
+  FIXME( ": not all data fields are correct\n" );
 
-    lpCache->present = lpCache->first;
-  }
+  data.dwMessageSize = lpSpData->dwSPHeaderSize + sizeof( *lpMsg ); /*FIXME!*/
+  data.lpMessage = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 
+                              data.dwMessageSize );
+  data.lpISP = lpSpData->lpISP;
+  data.bReturnStatus = (dwFlags & DPENUMSESSIONS_RETURNSTATUS) ? TRUE : FALSE;
 
-  /* Send out requests for matching sessions to all other known computers */
-  FIXME( ": no remote requests sent\n" );
-  /* FIXME - how to handle responses to messages anyways? */
+
+  lpMsg = (LPDPMSG_ENUMSESSIONSREQUEST)(((BYTE*)data.lpMessage)+lpSpData->dwSPHeaderSize); 
+
+  /* Setup EnumSession reqest message */
+  lpMsg->envelope.dwMagic    = DPMSGMAGIC_DPLAYMSG;
+  lpMsg->envelope.wCommandId = DPMSGCMD_ENUMSESSIONSREQUEST;
+  lpMsg->envelope.wVersion   = DPMSGVER_DP6;
+
+  lpMsg->dwPasswordSize = 0; /* FIXME: If enumerating passwords..? */
+  lpMsg->dwFlags        = dwFlags;
+
+  CopyMemory( &lpMsg->guidApplication, lpcGuid, sizeof( *lpcGuid ) );
+
+  return (lpSpData->lpCB->EnumSessions)( &data ); 
 }
 
-/* Render all data in a session cache invalid */
-static void NS_InvalidateSessionCache( lpNSCache lpCache )
+DPQ_DECL_DELETECB( cbDeleteNSNodeFromHeap, lpNSCacheData );
+DPQ_DECL_DELETECB( cbDeleteNSNodeFromHeap, lpNSCacheData )
 {
+  /* FIXME: Memory leak on data (contained ptrs) */
+  HeapFree( GetProcessHeap(), 0, elem->data );
+  HeapFree( GetProcessHeap(), 0, elem->lpNSAddrHdr );
+  HeapFree( GetProcessHeap(), 0, elem );
+}
+
+
+
+/* Render all data in a session cache invalid */
+void NS_InvalidateSessionCache( LPVOID lpNSInfo )
+{
+  lpNSCache lpCache = (lpNSCache)lpNSInfo;
+
   if( lpCache == NULL )
   {
     ERR( ": invalidate non existant cache\n" );
     return;
   }
 
-  /* Remove everything from the cache */
-  while( lpCache->first )
-  {
-    lpCache->present = lpCache->first;
-    lpCache->first = lpCache->first->next; 
-    HeapFree( GetProcessHeap(), 0, lpCache->present );
-  }
+  DPQ_DELETEQ( lpCache->first, next, lpNSCacheData, cbDeleteNSNodeFromHeap );
 
-  /* NULL out the cache pointers */
+  /* NULL out the walking pointer */
   lpCache->present = NULL;
-  lpCache->first   = NULL;
 }
 
 /* Create and initialize a session cache */
@@ -121,7 +210,8 @@
     return FALSE;
   }
 
-  lpCache->first = lpCache->present = NULL;
+  DPQ_INIT(lpCache->first);
+  lpCache->present = NULL;
 
   return TRUE;
 }
@@ -136,7 +226,7 @@
 void NS_ResetSessionEnumeration( LPVOID lpNSInfo )
 {
  
-  ((lpNSCache)lpNSInfo)->present = ((lpNSCache)lpNSInfo)->first;
+  ((lpNSCache)lpNSInfo)->present = ((lpNSCache)lpNSInfo)->first.lpQHFirst;
 }
 
 LPDPSESSIONDESC2 NS_WalkSessions( LPVOID lpNSInfo )
@@ -144,6 +234,8 @@
   LPDPSESSIONDESC2 lpSessionDesc;
   lpNSCache lpCache = (lpNSCache)lpNSInfo;
 
+  /* FIXME: The pointers could disappear when walking if a prune happens */
+
   /* Test for end of the list */ 
   if( lpCache->present == NULL )
   {
@@ -153,7 +245,93 @@
   lpSessionDesc = lpCache->present->data;
 
   /* Advance tracking pointer */
-  lpCache->present = lpCache->present->next;
+  lpCache->present = lpCache->present->next.lpQNext;
 
   return lpSessionDesc;
 }
+
+/* This method should check to see if there are any sessions which are
+ * older than the criteria. If so, just delete that information.
+ */
+void NS_PruneSessionCache( LPVOID lpNSInfo )
+{
+  lpNSCache     lpCache = lpNSInfo;
+  lpNSCacheData lpCacheEntry;
+
+  DWORD dwPresentTime = timeGetTime();
+#if defined( HACK_TIMEGETTIME )
+  DWORD dwPruneTime   = dwPresentTime - 2; /* One iteration with safety */
+#else
+  DWORD dwPruneTime   = dwPresentTime - 10000 /* 10 secs? */;
+#endif
+
+  FIXME( ": semi stub\n" );
+
+  /* FIXME: This doesn't handle time roll over correctly */
+  /* FIXME: Session memory leak on delete */
+  do
+  {
+    DPQ_FIND_ENTRY( lpCache->first, next, dwTime, <=, dwPruneTime, lpCacheEntry );
+  }
+  while( lpCacheEntry != NULL );
+
+}
+
+
+
+/* Message stuff */
+void NS_ReplyToEnumSessionsRequest( LPVOID lpMsg, 
+                                    LPDPSP_REPLYDATA lpReplyData,
+                                    IDirectPlay2Impl* lpDP )
+{
+  LPDPMSG_ENUMSESSIONSREPLY rmsg;
+  DWORD dwVariableSize;
+  DWORD dwVariableLen;
+  LPWSTR string;
+  /* LPDPMSG_ENUMSESSIONSREQUEST msg = (LPDPMSG_ENUMSESSIONSREQUEST)lpMsg; */
+  BOOL bAnsi = TRUE; /* FIXME: This needs to be in the DPLAY interface */
+
+  FIXME( ": few fixed + need to check request for response\n" );
+
+  dwVariableLen = bAnsi ? lstrlenA( lpDP->dp2->lpSessionDesc->sess.lpszSessionNameA ) + 1 
+                         : lstrlenW( lpDP->dp2->lpSessionDesc->sess.lpszSessionName ) + 1;
+
+  dwVariableSize = dwVariableLen * sizeof( WCHAR );
+
+  lpReplyData->dwMessageSize = lpDP->dp2->spData.dwSPHeaderSize +
+                                 sizeof( *rmsg ) + dwVariableSize;
+  lpReplyData->lpMessage     = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                          lpReplyData->dwMessageSize );
+
+  rmsg = (LPDPMSG_ENUMSESSIONSREPLY)lpReplyData->lpMessage;  
+
+  rmsg->envelope.dwMagic    = DPMSGMAGIC_DPLAYMSG; 
+  rmsg->envelope.wCommandId = DPMSGCMD_ENUMSESSIONSREPLY;
+  rmsg->envelope.wVersion   = DPMSGVER_DP6;
+
+  CopyMemory( &rmsg->sd, lpDP->dp2->lpSessionDesc, 
+              sizeof( lpDP->dp2->lpSessionDesc->dwSize ) ); 
+  rmsg->dwUnknown = 0x0000005c;
+  if( bAnsi )
+  {
+    string = HEAP_strdupAtoW( GetProcessHeap(), 0, 
+                              lpDP->dp2->lpSessionDesc->sess.lpszSessionNameA );
+    /* FIXME: Memory leak */
+  }
+  else
+  {
+    string = lpDP->dp2->lpSessionDesc->sess.lpszSessionName;
+  }
+
+  lstrcpyW( (LPWSTR)rmsg+1, string );
+ 
+}
+
+#if defined( HACK_TIMEGETTIME )
+DWORD timeGetTime(void)
+{
+  static DWORD time = 0;
+  
+  return time++;
+}
+#endif
diff --git a/dlls/dplayx/name_server.h b/dlls/dplayx/name_server.h
index 33c27ac..9c500cd 100644
--- a/dlls/dplayx/name_server.h
+++ b/dlls/dplayx/name_server.h
@@ -3,14 +3,33 @@
 #define __WINE_DPLAYX_NAMESERVER
 
 #include "dplay.h"
+#include "dplaysp.h"
+#include "dplayx_messages.h"
+#include "dplay_global.h"
 
 void NS_SetLocalComputerAsNameServer( LPCDPSESSIONDESC2 lpsd );
-void NS_SendSessionRequestBroadcast( LPVOID lpNSInfo );
+void NS_SetRemoteComputerAsNameServer( LPVOID lpNSAddrHdr,
+                                       DWORD dwHdrSize,
+                                       LPDPMSG_ENUMSESSIONSREPLY lpMsg,
+                                       LPVOID lpNSInfo );
+LPVOID NS_GetNSAddr( LPVOID lpNSInfo );
+
+void NS_ReplyToEnumSessionsRequest( LPVOID lpMsg, 
+                                    LPDPSP_REPLYDATA lpReplyData,
+                                    IDirectPlay2Impl* lpDP );
+
+HRESULT NS_SendSessionRequestBroadcast( LPCGUID lpcGuid,
+                                        DWORD dwFlags,
+                                        LPSPINITDATA lpSpData );
+                                        
 
 BOOL NS_InitializeSessionCache( LPVOID* lplpNSInfo );
 void NS_DeleteSessionCache( LPVOID lpNSInfo );
+void NS_InvalidateSessionCache( LPVOID lpNSInfo );
+
 
 void NS_ResetSessionEnumeration( LPVOID lpNSInfo );
 LPDPSESSIONDESC2 NS_WalkSessions( LPVOID lpNSInfo );
+void NS_PruneSessionCache( LPVOID lpNSInfo );
 
 #endif /* __WINE_DPLAYX_NAMESERVER */
diff --git a/include/dplay.h b/include/dplay.h
index 891a9f3..966c0e1 100644
--- a/include/dplay.h
+++ b/include/dplay.h
@@ -13,6 +13,13 @@
 
 #include "pshpack1.h"
 
+typedef LPVOID (*LPRGLPVOID)[];
+typedef LPRGLPVOID PRGPVOID, LPRGPVOID, PRGLPVOID, PAPVOID, LPAPVOID, PALPVOID, LPALPVOID;
+
+#define VOL volatile
+typedef VOID *VOL LPVOIDV;
+
+
 /*****************************************************************************
  * Predeclare the interfaces
  */
@@ -283,6 +290,13 @@
     }msgstr;
 } DPCHAT, *LPDPCHAT;
 
+typedef struct
+{
+  UINT   len;
+  PUCHAR pData;
+} SGBUFFER, *PSGBUFFER, *LPSGBUFFER;
+
+
 typedef struct tagDPSECURITYDESC
 {
     DWORD dwSize;                   /* Size of structure */
@@ -342,14 +356,7 @@
     DWORD       dwMinorVersion, /* Minor # of driver spec in lpguidSP */ 
     LPVOID      lpContext);     /* User given */
 
-/* NOTE: This isn't in the dplay.h header file, but this shouldn't be 
- *       a problem. We require this because we include all these header files
- *       which declare GUIDs in guid.c
- */
-#ifndef __LPCGUID_DEFINED__
-#define __LPCGUID_DEFINED__
 typedef const GUID   *LPCGUID;
-#endif
 
 typedef const DPNAME *LPCDPNAME;
 
diff --git a/ole/Makefile.in b/ole/Makefile.in
index 7746030..17c4838 100644
--- a/ole/Makefile.in
+++ b/ole/Makefile.in
@@ -6,7 +6,6 @@
 MODULE    = ole
 
 C_SRCS = \
-	guid.c \
 	ole2nls.c 
 
 EXTRASUBDIRS = nls
diff --git a/ole/guid.c b/ole/guid.c
deleted file mode 100644
index 2a210e6..0000000
--- a/ole/guid.c
+++ /dev/null
@@ -1,32 +0,0 @@
-#define INITGUID
-
-/* FIXME: we include all the header files containing GUIDs
- * so that the corresponding variables get defined. But they
- * don't even all belong to the same DLL !!!
- *
- * Therefore, as the builtin DLL's get teased apart (e.g. for elf-dlls)
- * then this file will have to be partitioned into per dll files.
- */
-#include "initguid.h"
-
-#if 0
-#include "shlguid.h"
-#include "docobj.h"
-#include "olectl.h"
-#include "oleidl.h"
-#include "oaidl.h"
-#include "ocidl.h"
-#include "objbase.h"
-#include "servprov.h"
-#include "ddraw.h"
-#include "d3d.h"
-#include "dinput.h"
-#include "dsound.h"
-#include "dplay.h"
-#include "dplobby.h"
-#include "vfw.h"
-#include "shlobj.h"
-#endif
-
-/* and now for the one assumed GUID... */
-DEFINE_GUID(GUID_NULL,   0,0,0,0,0,0,0,0,0,0,0);