BELLHOP.CPP

/*========================================================================== 
*
* Copyright (C) 1996-1997 Microsoft Corporation. All Rights Reserved.
*
* File: bellhop.cpp
* Content:Simple lobby program using DirectPlay.
*
***************************************************************************/

#define INITGUID
#include "bellhop.h"
#include "resource.h"

enum {
WM_USER_ADDSTRING= WM_USER+257,// window message to add string to chat string list
WM_USER_UPDATE// window message to update lobby display
};

// globals
HANDLEghReceiveThread = NULL;// handle of receive thread
DWORDgidReceiveThread = 0;// id of receive thread
HANDLEghKillReceiveEvent = NULL;// event used to kill receive thread
HWNDghMainWnd = NULL;// main window
HINSTANCEghInstance;// application instance

//EXTERNs
BOOL FAR PASCAL DirectPlayEnumConnectionsCallback(LPCGUID lpguidSP, LPVOID lpConnection,
DWORD dwSize, LPCDPNAME lpName,
DWORD dwFlags, LPVOID lpContext);
HRESULTGetConnection(HWND hWnd, int idCombo, LPVOID *lplpConnection);
HRESULTGetConnectionSPGuid(HWND hWnd, int idCombo, GUID *lpGuidSP);



///////////////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
DPLAYINFODPInfo;
intiResult = 0;
HRESULThr;

ghInstance = hInstance;

// Initialize COM library
hr = CoInitialize(NULL);
if FAILED(hr)
goto FAILURE;

// setup the connection
hr = SetupConnection(hInstance, &DPInfo);
if FAILED(hr)
goto FAILURE;

// show the chat window
iResult = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_LOBBYDIALOG), NULL, (DLGPROC) LobbyWndProc, (LPARAM) &DPInfo);

FAILURE:
// shut down the connection
hr = ShutdownConnection(&DPInfo);

// Uninitialize the COM library
CoUninitialize();

return (iResult);
}

///////////////////////////////////////////////////////////////////////////////////////
BOOL CALLBACK LobbyWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static LPDPLAYINFOlpDPInfo;
DWORDdwTextLen;

switch(uMsg)
{

case WM_INITDIALOG:
{
HDC hDC;
int xPixelsPerInch,
yPixelsPerInch;
RECT rect;

// Save the connection info pointer
lpDPInfo = (LPDPLAYINFO) lParam;

// Get the dialog spacing info
hDC = GetDC(hWnd);
xPixelsPerInch = GetDeviceCaps(hDC, LOGPIXELSX);
yPixelsPerInch = GetDeviceCaps(hDC, LOGPIXELSY);
ReleaseDC(hWnd, hDC);
lpDPInfo->xSpacing = xPixelsPerInch / 8;
lpDPInfo->ySpacing = yPixelsPerInch / 8;
lpDPInfo->xHalfSplitWidth = xPixelsPerInch / 12;

GetClientRect(hWnd, &rect);
lpDPInfo->xPaneSplit = ((rect.right - rect.left) - (2 * lpDPInfo->xSpacing )) / 3
- lpDPInfo->xHalfSplitWidth;

// Create the tree control
lpDPInfo->lpGroupTree = new CGroupTree;
lpDPInfo->lpGroupTree->Init(GetDlgItem(hWnd, IDT_MESSAGEVIEW),
lpDPInfo->lpDirectPlay3A,
lpDPInfo->dpidPlayer);
lpDPInfo->lpGroupTree->Refresh();
lpDPInfo->lpGroupTree->CreatePlayer( lpDPInfo->dpidPlayer, NULL, lpDPInfo->dwPlayerFlags );

// store global window
ghMainWnd = hWnd;

// initiallize lobby
InitializeLobby(hWnd, lpDPInfo);

OnSize( hWnd, lpDPInfo );
}
break;

case WM_DESTROY:
ghMainWnd = NULL;
break;

case WM_SIZE:
OnSize(hWnd, lpDPInfo);
break;

case WM_LBUTTONDOWN:
lpDPInfo->bSplitMove = TRUE;
SetCapture(hWnd);
break;

case WM_LBUTTONUP:
if (lpDPInfo->bSplitMove)
{
lpDPInfo->bSplitMove = FALSE;
ReleaseCapture();
}
else
{
lpDPInfo->lpGroupTree->OnLButtonUp();
}
break;

case WM_MOUSEMOVE:
if(lpDPInfo->bSplitMove)
{
// track mouse movement while adjusting the divider
RECT rect;
// change the value from unsigned to signed
int x = (int)(short)LOWORD(lParam);

GetClientRect(hWnd, &rect);
if (rect.left > x)
{
x = rect.left;
}
else if (rect.right < x)
{
x = rect.right;
}
lpDPInfo->xPaneSplit = (x - lpDPInfo->xHalfSplitWidth - (lpDPInfo->xSpacing * 2));
OnSize(hWnd, lpDPInfo);
}
else
{
lpDPInfo->lpGroupTree->OnMouseMove(LOWORD(lParam), HIWORD(lParam) );
}
break;


// this is a user-defined message used to add strings to the log window
case WM_USER_ADDSTRING:
// get length of text in log window
dwTextLen = SendDlgItemMessage(hWnd, IDC_LOGEDIT, WM_GETTEXTLENGTH,
(WPARAM) 0, (LPARAM) 0);

// put selection at end
dwTextLen = SendDlgItemMessage(hWnd, IDC_LOGEDIT, EM_SETSEL,
(WPARAM) dwTextLen, (LPARAM) dwTextLen);

// add string in lParam to log window
SendDlgItemMessage(hWnd, IDC_LOGEDIT, EM_REPLACESEL,
(WPARAM) FALSE, (LPARAM) lParam);
GlobalFreePtr((LPVOID) lParam);
break;


case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_ROOT_CREATEGROUP:
DoCreateRoom(hWnd, lpDPInfo);
break;

case ID_GROUP_CONNECTIONSETTINGS:
DoGroupConnectionSettings(hWnd, lpDPInfo);
break;

case ID_DESTROYGROUP:
DoDeleteRoom(hWnd, lpDPInfo);
break;

case ID_PLAYERINGROUP_DELETEPLAYERFROMGROUP:
DoDeletePlayerFromGroup(hWnd, lpDPInfo);
break;

case ID_CREATEGROUPINGROUP:
DoCreateTable(hWnd, lpDPInfo);
break;

case ID_SHORTCUT_DELETEGROUPFROMGROUP:
DoDeleteTable(hWnd, lpDPInfo);
break;

case ID_GROUP_STARTSESSION:
DoLaunch(hWnd, lpDPInfo);
break;

case ID_PLAYER_SETPLAYERNAME:
case ID_PLAYERINGROUP_SETPLAYERNAME:
case ID_GROUP_SETGROUPNAME:
lpDPInfo->lpGroupTree->EditLabel();
break;

case IDC_SENDBUTTON:
SendChatMessage(hWnd, lpDPInfo);
break;

case IDCANCEL:
EndDialog(hWnd, FALSE);
break;

case ID_ROOT_ENUMRECURSIVE:
lpDPInfo->lpGroupTree->Refresh( );
lpDPInfo->lpGroupTree->CreatePlayer( lpDPInfo->dpidPlayer, NULL, lpDPInfo->dwPlayerFlags );
break;

case ID_ROOT_ENUMGROUPS:
lpDPInfo->lpGroupTree->Refresh( FALSE );
lpDPInfo->lpGroupTree->CreatePlayer( lpDPInfo->dpidPlayer, NULL, lpDPInfo->dwPlayerFlags );
break;

}
break;

case WM_NOTIFY :
return lpDPInfo->lpGroupTree->OnWM_NOTIFY( wParam, lParam );
break;

case WM_MENUSELECT:
return FALSE;
break;
}

// Allow for default processing
return FALSE;
}

///////////////////////////////////////////////////////////////////////////////////////
HRESULT SetupConnection(HINSTANCE hInstance, LPDPLAYINFO lpDPInfo)
{
HRESULThr;

ZeroMemory(lpDPInfo, sizeof(DPLAYINFO));

// create event used by DirectPlay to signal a message has arrived
lpDPInfo->hPlayerEvent = CreateEvent(NULL,// no security
FALSE,// auto reset
FALSE,// initial event reset
NULL);// no name
if (lpDPInfo->hPlayerEvent == NULL)
{
hr = DPERR_NOMEMORY;
goto FAILURE;
}

// create event used to signal that the receive thread should exit
ghKillReceiveEvent = CreateEvent(NULL,// no security
FALSE,// auto reset
FALSE,// initial event reset
NULL);// no name
if (ghKillReceiveEvent == NULL)
{
hr = DPERR_NOMEMORY;
goto FAILURE;
}

// create thread to receive player messages
ghReceiveThread = CreateThread(NULL,// default security
0,// default stack size
ReceiveThread,// pointer to thread routine
lpDPInfo,// argument for thread
0,// start it right away
&gidReceiveThread);
if (ghReceiveThread == NULL)
{
hr = DPERR_NOMEMORY;
goto FAILURE;
}

// if there is no lobby connection, ask the user for settings
hr = ConnectUsingDialog(hInstance, lpDPInfo);
if FAILED(hr)
goto FAILURE;

return (DP_OK);

FAILURE:
ShutdownConnection(lpDPInfo);

return (hr);
}

///////////////////////////////////////////////////////////////////////////////////////
HRESULT ShutdownConnection(LPDPLAYINFO lpDPInfo)
{
if (ghReceiveThread)
{
// wake up receive thread and wait for it to quit
SetEvent(ghKillReceiveEvent);
WaitForSingleObject(ghReceiveThread, INFINITE);

CloseHandle(ghReceiveThread);
ghReceiveThread = NULL;
}

if (ghKillReceiveEvent)
{
CloseHandle(ghKillReceiveEvent);
ghKillReceiveEvent = NULL;
}

if (lpDPInfo->lpDirectPlay3A)
{
if (lpDPInfo->dpidPlayer)
{
IDirectPlay3_DestroyPlayer(lpDPInfo->lpDirectPlay3A, lpDPInfo->dpidPlayer);
lpDPInfo->dpidPlayer = 0;
}
IDirectPlay3_Close(lpDPInfo->lpDirectPlay3A);
IDirectPlay3_Release(lpDPInfo->lpDirectPlay3A);
lpDPInfo->lpDirectPlay3A = NULL;

IDirectPlayLobby_Release(lpDPInfo->lpDirectPlayLobby2A);
lpDPInfo->lpDirectPlayLobby2A = NULL;

}

if (lpDPInfo->hPlayerEvent)
{
CloseHandle(lpDPInfo->hPlayerEvent);
lpDPInfo->hPlayerEvent = NULL;
}

if (lpDPInfo->lpGroupTree)
{
delete lpDPInfo->lpGroupTree;
lpDPInfo->lpGroupTree = NULL;
}
return (DP_OK);
}

///////////////////////////////////////////////////////////////////////////////////////
DWORD WINAPI ReceiveThread(LPVOID lpThreadParameter)
{
LPDPLAYINFOlpDPInfo = (LPDPLAYINFO) lpThreadParameter;
HANDLEeventHandles[2];

eventHandles[0] = lpDPInfo->hPlayerEvent;
eventHandles[1] = ghKillReceiveEvent;

// loop waiting for player events. If the kill event is signaled
// the thread will exit
while (WaitForMultipleObjects(2, eventHandles, FALSE, INFINITE) == WAIT_OBJECT_0)
{
// receive any messages in the queue
ReceiveMessage(lpDPInfo);
}

ExitThread(0);

return (0);
}

///////////////////////////////////////////////////////////////////////////////////////
HRESULT ReceiveMessage(LPDPLAYINFO lpDPInfo)
{
DPIDidFrom, idTo;
LPVOIDlpvMsgBuffer;
DWORDdwMsgBufferSize;
HRESULThr;

lpvMsgBuffer = NULL;
dwMsgBufferSize = 0;

// loop to read all messages in queue
do
{
// loop until a single message is successfully read
do
{
// read messages from any player, including system player
idFrom = 0;
idTo = 0;

hr = IDirectPlay3_Receive(lpDPInfo->lpDirectPlay3A, &idFrom, &idTo, DPRECEIVE_ALL,
lpvMsgBuffer, &dwMsgBufferSize);

// not enough room, so resize buffer
if (hr == DPERR_BUFFERTOOSMALL)
{
if (lpvMsgBuffer)
GlobalFreePtr(lpvMsgBuffer);

lpvMsgBuffer = GlobalAllocPtr(GHND, dwMsgBufferSize);
if (lpvMsgBuffer == NULL)
hr = DPERR_OUTOFMEMORY;
}
} while (hr == DPERR_BUFFERTOOSMALL);

if ((SUCCEEDED(hr)) &&// successfully read a message
(dwMsgBufferSize >= sizeof(DPMSG_GENERIC)))// and it is big enough
{
// update the tree view UI.
lpDPInfo->lpGroupTree->Update( lpvMsgBuffer );

// check for system message
if (idFrom == DPID_SYSMSG)
{
HandleSystemMessage(lpDPInfo, (LPDPMSG_GENERIC) lpvMsgBuffer,
dwMsgBufferSize, idFrom, idTo);
}
else
{
HandleApplicationMessage(lpDPInfo, (LPDPMSG_GENERIC) lpvMsgBuffer,
dwMsgBufferSize, idFrom, idTo);
}
}
} while (SUCCEEDED(hr));

// free any memory we created
if (lpvMsgBuffer)
GlobalFreePtr(lpvMsgBuffer);

return (DP_OK);
}

///////////////////////////////////////////////////////////////////////////////////////
void HandleApplicationMessage(LPDPLAYINFO lpDPInfo, LPDPMSG_GENERIC lpMsg, DWORD dwMsgSize,
DPID idFrom, DPID idTo)
{
// Any messages received from the lobby server would be handled here.
// In this case, we don't have any
}

///////////////////////////////////////////////////////////////////////////////////////
void HandleSystemMessage(LPDPLAYINFO lpDPInfo, LPDPMSG_GENERIC lpMsg, DWORD dwMsgSize,
DPID idFrom, DPID idTo)
{
LPSTRlpszStr = NULL;

// The body of each case is there so you can set a breakpoint and examine
// the contents of the message received.
switch (lpMsg->dwType)
{
case DPSYS_STARTSESSION:
{
LPDPMSG_STARTSESSIONlp = (LPDPMSG_STARTSESSION) lpMsg;

HandleStartSession(lp->lpConn, lpDPInfo);
}
break;

case DPSYS_CREATEPLAYERORGROUP:
{
LPDPMSG_CREATEPLAYERORGROUPlp = (LPDPMSG_CREATEPLAYERORGROUP) lpMsg;
LPSTRlpszName, lpszDisplayFormat;

if (lp->dwPlayerType == DPPLAYERTYPE_PLAYER)
lpszDisplayFormat = "Player \"%s\" created\r\n";
else if (lp->dwPlayerType == DPPLAYERTYPE_GROUP)
lpszDisplayFormat = "Group \"%s\" created\r\n";
else
lpszDisplayFormat = "Unknown object \"%s\" created\r\n";

// get pointer to player name
if (lp->dpnName.lpszShortNameA)
lpszName = lp->dpnName.lpszShortNameA;
else
lpszName = "unknown";

// allocate space for string
lpszStr = (LPSTR) GlobalAllocPtr(GHND, lstrlen(lpszDisplayFormat) +
lstrlen(lpszName) + 1);
if (lpszStr == NULL)
break;

// build string
wsprintf(lpszStr, lpszDisplayFormat, lpszName);
}
break;

case DPSYS_DESTROYPLAYERORGROUP:
{
LPDPMSG_DESTROYPLAYERORGROUPlp = (LPDPMSG_DESTROYPLAYERORGROUP)lpMsg;
LPSTRlpszName, lpszDisplayFormat;

if (lp->dwPlayerType == DPPLAYERTYPE_PLAYER)
lpszDisplayFormat = "Player \"%s\" destroyed\r\n";
else if (lp->dwPlayerType == DPPLAYERTYPE_GROUP)
lpszDisplayFormat = "Group \"%s\" destroyed\r\n";
else
lpszDisplayFormat = "Unknown object \"%s\" destroyed\r\n";

// get pointer to player name
if (lp->dpnName.lpszShortNameA)
lpszName = lp->dpnName.lpszShortNameA;
else
lpszName = "unknown";

// allocate space for string
lpszStr = (LPSTR) GlobalAllocPtr(GHND, lstrlen(lpszDisplayFormat) +
lstrlen(lpszName) + 1);
if (lpszStr == NULL)
break;

// build string
wsprintf(lpszStr, lpszDisplayFormat, lpszName);
}
break;

case DPSYS_ADDPLAYERTOGROUP:
case DPSYS_DELETEPLAYERFROMGROUP:
{
LPDPMSG_ADDPLAYERTOGROUPlpAddMsg = (LPDPMSG_ADDPLAYERTOGROUP)lpMsg;
LPDPMSG_DELETEPLAYERFROMGROUPlpDeleteMsg = (LPDPMSG_DELETEPLAYERFROMGROUP)lpMsg;
DPIDdpidPlayer, dpidGroup;
LPSTRlpszPlayerName, lpszGroupName;
LPDPNAMElpPlayerName, lpGroupName;
LPSTRlpszDisplayFormat;
HRESULThr;

if (lpMsg->dwType == DPSYS_ADDPLAYERTOGROUP)
{
dpidPlayer = lpAddMsg->dpIdPlayer;
dpidGroup = lpAddMsg->dpIdGroup;
lpszDisplayFormat = "Player \"%s\" added to group \"%s\"\r\n";
}
else
{
dpidPlayer = lpDeleteMsg->dpIdPlayer;
dpidGroup = lpDeleteMsg->dpIdGroup;
lpszDisplayFormat = "Player \"%s\" removed from group \"%s\"\r\n";
}

// get pointer to player name
hr = GetPlayerName(lpDPInfo->lpDirectPlay3A, dpidPlayer, &lpPlayerName);
if FAILED(hr)
{
// A failure may mean that the player has been deleted
// since we began processing this message
lpPlayerName = NULL;
}

if ((lpPlayerName) && (lpPlayerName->lpszShortNameA))
lpszPlayerName = lpPlayerName->lpszShortNameA;
else
lpszPlayerName = "unknown";

// get pointer to group name
hr = GetGroupName(lpDPInfo->lpDirectPlay3A, dpidGroup, &lpGroupName);
if FAILED(hr)
{
// A failure may mean that the group has been deleted
// since we began processing this message
lpGroupName = NULL;
}

if ((lpGroupName) && (lpGroupName->lpszShortNameA))
lpszGroupName = lpGroupName->lpszShortNameA;
else
lpszGroupName = "unknown";


// allocate space for string
lpszStr = (LPSTR) GlobalAllocPtr(GHND, lstrlen(lpszDisplayFormat) +
lstrlen(lpszPlayerName) +
lstrlen(lpszGroupName) + 1);
// build string
if (lpszStr)
wsprintf(lpszStr, lpszDisplayFormat, lpszPlayerName, lpszGroupName);

// free data we allocated
if (lpPlayerName)
GlobalFreePtr(lpPlayerName);
if (lpGroupName)
GlobalFreePtr(lpGroupName);
}
break;

case DPSYS_ADDGROUPTOGROUP:
case DPSYS_DELETEGROUPFROMGROUP:
{
LPDPMSG_ADDGROUPTOGROUPlpAddMsg = (LPDPMSG_ADDGROUPTOGROUP)lpMsg;
LPDPMSG_DELETEGROUPFROMGROUPlpDeleteMsg = (LPDPMSG_DELETEGROUPFROMGROUP)lpMsg;
DPIDdpidParentGroup, dpidGroup;
LPSTRlpszParentGroupName, lpszGroupName;
LPDPNAMElpParentGroupName, lpGroupName;
LPSTRlpszDisplayFormat;
HRESULThr;

if (lpMsg->dwType == DPSYS_ADDGROUPTOGROUP)
{
dpidGroup = lpAddMsg->dpIdGroup;
dpidParentGroup = lpAddMsg->dpIdParentGroup;
lpszDisplayFormat = "Group \"%s\" added to group \"%s\"\r\n";
}
else
{
dpidParentGroup = lpDeleteMsg->dpIdParentGroup;
dpidGroup = lpDeleteMsg->dpIdGroup;
lpszDisplayFormat = "Group \"%s\" removed from group \"%s\"\r\n";
}

// get pointer to player name
hr = GetGroupName(lpDPInfo->lpDirectPlay3A, dpidParentGroup, &lpParentGroupName);
if FAILED(hr)
lpParentGroupName = NULL;

if ((lpParentGroupName) && (lpParentGroupName->lpszShortNameA))
lpszParentGroupName = lpParentGroupName->lpszShortNameA;
else
lpszParentGroupName = "unknown";

// get pointer to group name
hr = GetGroupName(lpDPInfo->lpDirectPlay3A, dpidGroup, &lpGroupName);
if FAILED(hr)
lpGroupName = NULL;

if ((lpGroupName) && (lpGroupName->lpszShortNameA))
lpszGroupName = lpGroupName->lpszShortNameA;
else
lpszGroupName = "unknown";

// allocate space for string
lpszStr = (LPSTR) GlobalAllocPtr(GHND, lstrlen(lpszDisplayFormat) +
lstrlen(lpszParentGroupName) +
lstrlen(lpszGroupName) + 1);
// build string
if (lpszStr)
wsprintf(lpszStr, lpszDisplayFormat, lpszGroupName, lpszParentGroupName );

// free data we allocated
if (lpParentGroupName)
GlobalFreePtr(lpParentGroupName);
if (lpGroupName)
GlobalFreePtr(lpGroupName);
}
break;

case DPSYS_SESSIONLOST:
{
LPDPMSG_SESSIONLOST lp = (LPDPMSG_SESSIONLOST)lpMsg;
LPSTRszDisplayFormat = "Session lost.\r\n";

// allocate space for string
lpszStr = (LPSTR) GlobalAllocPtr(GHND, lstrlen(szDisplayFormat) + 1);
if (lpszStr == NULL)
break;

// build string
lstrcpy(lpszStr, szDisplayFormat);
}
break;

case DPSYS_HOST:
{
LPDPMSG_HOSTlp = (LPDPMSG_HOST)lpMsg;
LPSTRszDisplayFormat = "You have become the host\r\n";

// allocate space for string
lpszStr = (LPSTR) GlobalAllocPtr(GHND, lstrlen(szDisplayFormat) + 1);
if (lpszStr == NULL)
break;

// build string
lstrcpy(lpszStr, szDisplayFormat);

// we are now the host
lpDPInfo->bIsHost = TRUE;
}
break;

case DPSYS_SETPLAYERORGROUPDATA:
{
LPDPMSG_SETPLAYERORGROUPDATA lp = (LPDPMSG_SETPLAYERORGROUPDATA)lpMsg;
}
break;

case DPSYS_SETPLAYERORGROUPNAME:
{
LPDPMSG_SETPLAYERORGROUPNAME lp = (LPDPMSG_SETPLAYERORGROUPNAME)lpMsg;
}
break;

case DPSYS_CHAT:
{
LPDPMSG_CHATlp = (LPDPMSG_CHAT)lpMsg;
DWORDdwSize = lstrlen( lp->lpChat->lpszMessageA ) + 12;
//Allow extra room for "{whisper} ", in case this message
//was sent just to me.

// allocate space for string
lpszStr = (LPSTR) GlobalAllocPtr( GHND, dwSize );

if (lpszStr == NULL)
break;

if (NULL == lp->idToGroup)
{
//This message was sent just to me, not to a whole group
lstrcpy( lpszStr, "{whisper} " );
lstrcat( lpszStr, lp->lpChat->lpszMessageA );
}
else
{
// build string
lstrcpy(lpszStr, lp->lpChat->lpszMessageA);
}
}
break;

}

// post string to chat window
if (lpszStr)
{
// make sure window is still valid
if (ghMainWnd)
PostMessage(ghMainWnd, WM_USER_ADDSTRING, (WPARAM) 0, (LPARAM) lpszStr);
else
GlobalFreePtr(lpszStr);
}

}

///////////////////////////////////////////////////////////////////////////////////////
HRESULT GetPlayerName(LPDIRECTPLAY3A lpDirectPlay3A, DPID dpidPlayer,
LPDPNAME *lplpName)
{
LPDPNAMElpName = NULL;
DWORDdwNameSize;
HRESULThr;

// get size of player name data
hr = IDirectPlay3_GetPlayerName(lpDirectPlay3A, dpidPlayer, NULL, &dwNameSize);
if (hr != DPERR_BUFFERTOOSMALL)
goto FAILURE;

// make room for it
lpName = (LPDPNAME) GlobalAllocPtr(GHND, dwNameSize);
if (lpName == NULL)
{
hr = DPERR_OUTOFMEMORY;
goto FAILURE;
}

// get player name data
hr = IDirectPlay3_GetPlayerName(lpDirectPlay3A, dpidPlayer, lpName, &dwNameSize);
if FAILED(hr)
goto FAILURE;

// return pointer to name structure
*lplpName = lpName;

return (DP_OK);

FAILURE:
if (lpName)
GlobalFreePtr(lpName);

return (hr);
}

///////////////////////////////////////////////////////////////////////////////////////
HRESULT GetGroupName(LPDIRECTPLAY3A lpDirectPlay3A, DPID dpidGroup,
LPDPNAME *lplpName)
{
LPDPNAMElpName = NULL;
DWORDdwNameSize;
HRESULThr;

// get size of player name data
hr = IDirectPlay3_GetGroupName(lpDirectPlay3A, dpidGroup, NULL, &dwNameSize);
if (hr != DPERR_BUFFERTOOSMALL)
goto FAILURE;

// make room for it
lpName = (LPDPNAME) GlobalAllocPtr(GHND, dwNameSize);
if (lpName == NULL)
{
hr = DPERR_OUTOFMEMORY;
goto FAILURE;
}

// get player name data
hr = IDirectPlay3_GetGroupName(lpDirectPlay3A, dpidGroup, lpName, &dwNameSize);
if FAILED(hr)
goto FAILURE;

// return pointer to name structure
*lplpName = lpName;

return (DP_OK);

FAILURE:
if (lpName)
GlobalFreePtr(lpName);

return (hr);
}

///////////////////////////////////////////////////////////////////////////////////////
void InitializeLobbyGroupWindow(HWND hWnd, LPLOBBYGROUPCONTEXT lpContext)
{
HRESULThr = DPERR_GENERIC;
ENUMCONNSTRUCTenStruct;

if (lpContext->dpidRoom == 0)
SetWindowText(hWnd, "Create Room");
else
SetWindowText(hWnd, "Create Table");

// initialize max players
SetDlgItemInt(hWnd, IDC_MAXPLAYERSEDIT, 0, FALSE);

// put all the DirectPlay applications in a combo box
lpContext->lpDPInfo->lpDirectPlayLobby2A->EnumLocalApplications(EnumApp, hWnd, 0);
SendDlgItemMessage(hWnd, IDC_APPCOMBO, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);

// put all the available connections in a combo box
enStruct.hWnd = hWnd;
enStruct.idCombo = IDC_GROUPCONNECTIONSPCOMBO;

hr = IDirectPlay3_EnumConnections(lpContext->lpDPInfo->lpDirectPlay3A,
&BELLHOP_GUID,
DirectPlayEnumConnectionsCallback,
&enStruct, DPCONNECTION_DIRECTPLAY );

SendDlgItemMessage(hWnd, IDC_GROUPCONNECTIONSPCOMBO, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
return;

}

///////////////////////////////////////////////////////////////////////////////////////
HRESULT CreateLobbyGroup(HWND hWnd, LPLOBBYGROUPCONTEXT lpContext)
{
CHARszShortName[MAXSTRLEN],
szLongName[MAXSTRLEN],
szPassword[MAXSTRLEN];
DPNAMEdpName;
DWORDdwMaxPlayers;
DPIDdpID;
DPLCONNECTIONdplconn;
DPSESSIONDESC2dpsess;
HRESULThr;
DWORDdwFlags = 0;

// get strings from dialog
GetDlgItemText(hWnd, IDC_SHORTNAMEEDIT, szShortName, MAXSTRLEN);
GetDlgItemText(hWnd, IDC_LONGNAMEEDIT, szLongName, MAXSTRLEN);

if (BST_CHECKED == SendDlgItemMessage( hWnd, IDC_STAGINGAREA, BM_GETCHECK, 0, 0 ))
{
GetDlgItemText(hWnd, IDC_PASSWORDEDIT, szPassword, MAXSTRLEN);
dwMaxPlayers = GetDlgItemInt(hWnd, IDC_MAXPLAYERSEDIT, NULL, FALSE);
dwFlags = DPGROUP_STAGINGAREA;
}

// build a dpname structure
ZeroMemory(&dpName, sizeof(DPNAME));
dpName.dwSize = sizeof(DPNAME);
dpName.lpszShortNameA = szShortName;
dpName.lpszLongNameA = szLongName;

// create a root group
if (lpContext->dpidRoom == 0)
{
hr = IDirectPlay3_CreateGroup(lpContext->lpDPInfo->lpDirectPlay3A,
&dpID, &dpName, NULL, 0, dwFlags);
}

// create the table group
else
{
hr = IDirectPlay3_CreateGroupInGroup(lpContext->lpDPInfo->lpDirectPlay3A,
lpContext->dpidRoom,
&dpID, &dpName,
NULL, 0, dwFlags);
}

if FAILED(hr)
goto FAILURE;

if ( DPGROUP_STAGINGAREA & dwFlags )
{

// Fill out the DPSESSIONDESC2 structure
ZeroMemory( &dpsess, sizeof(DPSESSIONDESC2) );
dpsess.dwSize = sizeof( DPSESSIONDESC2 );
dpsess.dwFlags = DPSESSION_KEEPALIVE | DPSESSION_MIGRATEHOST;

CoCreateGuid(&(dpsess.guidInstance));

GetComboBoxGuid(hWnd, IDC_APPCOMBO, &dpsess.guidApplication);

dpsess.dwMaxPlayers = dwMaxPlayers;
dpsess.dwCurrentPlayers = 0;
dpsess.lpszSessionNameA = dpName.lpszShortNameA;
if (lstrlen(szPassword))
dpsess.lpszPasswordA = szPassword;

// Fill out the DPLCONNECTION structure
ZeroMemory( &dplconn, sizeof(DPLCONNECTION) );
dplconn.dwSize = sizeof( DPLCONNECTION );
dplconn.lpSessionDesc = &dpsess;

GetConnectionSPGuid(hWnd, IDC_GROUPCONNECTIONSPCOMBO, &dplconn.guidSP);

// The rest of the DPLCONNECTION structure gets
// filled in by the lobby

hr = IDirectPlay3_SetGroupConnectionSettings(lpContext->lpDPInfo->lpDirectPlay3A,
0, dpID, &dplconn );

if FAILED(hr)
goto FAILURE;
}

FAILURE:
return (hr);
}

///////////////////////////////////////////////////////////////////////////////////////
BOOL CALLBACK LobbyGroupWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LPLOBBYGROUPCONTEXTlpContext = (LPLOBBYGROUPCONTEXT) GetWindowLong(hWnd, DWL_USER);

switch(uMsg)
{
case WM_INITDIALOG:

// context passed in lParam
lpContext = (LPLOBBYGROUPCONTEXT) lParam;

// save the globals with the window
SetWindowLong(hWnd, DWL_USER, (LONG) lpContext);

// Initialize dialog with appropriate information
InitializeLobbyGroupWindow(hWnd, lpContext);
break;

case WM_DESTROY:
{
WPARAMindex;
LRESULTlpData;

// destroy the GUID's stored with each app name
index = 0;
while (TRUE)
{
lpData = SendDlgItemMessage(hWnd, IDC_APPCOMBO, CB_GETITEMDATA, (WPARAM) index, 0);
if ((lpData == CB_ERR) || (lpData == 0))
break;

GlobalFreePtr((LPVOID) lpData);
index += 1;
}

// destroy the connection info in the combo box.
index = 0;
while (TRUE)
{
lpData = SendDlgItemMessage(hWnd, IDC_GROUPCONNECTIONSPCOMBO, CB_GETITEMDATA, (WPARAM) index, 0);
if ((lpData == CB_ERR) || (lpData == 0))
break;

GlobalFreePtr((LPVOID) lpData);
index += 1;
}
}
break;

case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDOK:
// save changes they made
CreateLobbyGroup(hWnd, lpContext);
// Return success
EndDialog(hWnd, TRUE);

break;

case IDCANCEL:
// Return failure
EndDialog(hWnd, FALSE);

break;

case IDC_STAGINGAREA:
{
int i = SendDlgItemMessage( hWnd, IDC_STAGINGAREA, BM_GETCHECK, 0, 0 );
EnableWindow( GetDlgItem( hWnd, IDC_PASSWORDEDIT ), (BST_CHECKED==i));
EnableWindow( GetDlgItem( hWnd, IDC_APPCOMBO ), (BST_CHECKED==i));
EnableWindow( GetDlgItem( hWnd, IDC_MAXPLAYERSEDIT ), (BST_CHECKED==i));
EnableWindow( GetDlgItem( hWnd, IDC_GROUPCONNECTIONSPCOMBO ), (BST_CHECKED==i));
}
break;

}

break;
}

// Allow for default processing
return FALSE;
}

///////////////////////////////////////////////////////////////////////////////////////
HRESULT DoGroupConnectionSettings(HWND hWnd, LPDPLAYINFO lpDPInfo)
{
LOBBYGROUPCONTEXTcontext;

context.lpDPInfo = lpDPInfo;
context.dpidRoom = lpDPInfo->lpGroupTree->GetDPIDOfCurrentSelection();


if (DialogBoxParam(ghInstance, MAKEINTRESOURCE(IDD_CONNECTIONSETTINGSDIALOG), hWnd,
(DLGPROC) ConnectionSettingsDialogProc, (LPARAM) &context))
{
// something changed
// We could update a status bar here.
}

return (DP_OK);
}

///////////////////////////////////////////////////////////////////////////////////////
HRESULT DoCreateRoom(HWND hWnd, LPDPLAYINFO lpDPInfo)
{
LOBBYGROUPCONTEXTcontext;

context.lpDPInfo = lpDPInfo;
context.dpidRoom = (DPID) 0;

if (DialogBoxParam(ghInstance, MAKEINTRESOURCE(IDD_LOBBYGROUPDIALOG), hWnd,
(DLGPROC) LobbyGroupWndProc, (LPARAM) &context))
{
// something changed
// We could update a status bar here.
}

return (DP_OK);
}

///////////////////////////////////////////////////////////////////////////////////////
HRESULT DoDeleteRoom(HWND hWnd, LPDPLAYINFO lpDPInfo)
{
DPIDdpidRoom;
HRESULThr = DP_OK;

dpidRoom = lpDPInfo->lpGroupTree->GetDPIDOfCurrentSelection();

// delete the group
hr = IDirectPlay3_DestroyGroup(lpDPInfo->lpDirectPlay3A, dpidRoom);

return (hr);
}

///////////////////////////////////////////////////////////////////////////////////////
HRESULT DoDeletePlayerFromGroup(HWND hWnd, LPDPLAYINFO lpDPInfo)
{
DPIDdpidRoom,
dpidPlayer;
HRESULThr = DP_OK;

dpidRoom = lpDPInfo->lpGroupTree->GetDPIDOfCurrentSelectionParent();
dpidPlayer = lpDPInfo->lpGroupTree->GetDPIDOfCurrentSelection();

// delete the player from the group
hr = IDirectPlay3_DeletePlayerFromGroup(lpDPInfo->lpDirectPlay3A, dpidRoom, dpidPlayer );

return (hr);
}

///////////////////////////////////////////////////////////////////////////////////////
HRESULT DoCreateTable(HWND hWnd, LPDPLAYINFO lpDPInfo)
{
LOBBYGROUPCONTEXTcontext;


context.lpDPInfo = lpDPInfo;
context.dpidRoom = lpDPInfo->lpGroupTree->GetDPIDOfCurrentSelection();

DialogBoxParam(ghInstance, MAKEINTRESOURCE(IDD_LOBBYGROUPDIALOG), hWnd,
(DLGPROC) LobbyGroupWndProc, (LPARAM) &context);
return (DP_OK);
}

///////////////////////////////////////////////////////////////////////////////////////
HRESULT DoDeleteTable(HWND hWnd, LPDPLAYINFO lpDPInfo)
{
DPIDdpidRoom,
dpidShortcut;
HRESULThr = DP_OK;

dpidRoom = lpDPInfo->lpGroupTree->GetDPIDOfCurrentSelectionParent();
dpidShortcut = lpDPInfo->lpGroupTree->GetDPIDOfCurrentSelection();

// delete the player from the group
hr = IDirectPlay3_DeleteGroupFromGroup(lpDPInfo->lpDirectPlay3A, dpidRoom, dpidShortcut );
return (hr);
}

///////////////////////////////////////////////////////////////////////////////////////
HRESULT DoLaunch(HWND hWnd, LPDPLAYINFO lpDPInfo)
{
DPIDdpidRoom;
HRESULThr = DPERR_GENERIC;

dpidRoom = lpDPInfo->lpGroupTree->GetDPIDOfCurrentSelection();

hr = IDirectPlay3_StartSession(lpDPInfo->lpDirectPlay3A, 0, dpidRoom);

return (hr);
}

///////////////////////////////////////////////////////////////////////////////////////
HRESULT InitializeLobby(HWND hWnd, LPDPLAYINFO lpDPInfo)
{
DPNAMEname;
DPIDdpID;
HRESULThr = DP_OK;

if (lpDPInfo->bIsHost)
{
// add some groups to start with
ZeroMemory(&name, sizeof(DPNAME));
name.dwSize = sizeof(DPNAME);

name.lpszShortNameA = "Golf Shack";
hr = IDirectPlay3_CreateGroup(lpDPInfo->lpDirectPlay3A, &dpID, &name, NULL, 0, 0);
if FAILED(hr)
goto FAILURE;

name.lpszShortNameA = "Monster Truck Rally";
hr = IDirectPlay3_CreateGroup(lpDPInfo->lpDirectPlay3A, &dpID, &name, NULL, 0, 0);
if FAILED(hr)
goto FAILURE;

name.lpszShortNameA = "Club Hellbender";
hr = IDirectPlay3_CreateGroup(lpDPInfo->lpDirectPlay3A, &dpID, &name, NULL, 0, 0);
if FAILED(hr)
goto FAILURE;
}

FAILURE:
return (hr);
}

///////////////////////////////////////////////////////////////////////////////////////
void LogString(LPSTR lpszDisplayFormat, LPSTR lpszDataStr)
{
LPSTRlpszStr;

// allocate space for string
lpszStr = (LPSTR) GlobalAllocPtr(GHND, lstrlen(lpszDisplayFormat) +
lstrlen(lpszDataStr) + 1);
if (lpszStr == NULL)
return;

// build string
wsprintf(lpszStr, lpszDisplayFormat, lpszDataStr);

// post string to chat window
// make sure window is still valid
if (ghMainWnd)
PostMessage(ghMainWnd, WM_USER_ADDSTRING, (WPARAM) 0, (LPARAM) lpszStr);
else
GlobalFreePtr(lpszStr);
}

///////////////////////////////////////////////////////////////////////////////////////
HRESULT SendChatMessage(HWND hWnd, LPDPLAYINFO lpDPInfo)
{
LPSTRlpszChatStr = NULL;
LPSTRlpszStr = NULL;
LONGlStrLen;
HRESULThr;
DPCHATdpc;
DPIDdpidTarget;

// get length of item text
lStrLen = SendDlgItemMessage(hWnd, IDC_SENDEDIT, WM_GETTEXTLENGTH,
(WPARAM) 0, (LPARAM) 0);

// make room for it
lpszChatStr = (LPSTR) GlobalAllocPtr(GHND, lStrLen + 1);
if (lpszChatStr == NULL)
{
hr = DPERR_OUTOFMEMORY;
goto FAILURE;
}

// get item text
lStrLen = GetDlgItemText(hWnd, IDC_SENDEDIT, lpszChatStr, lStrLen + 1);

// create string to display this text
hr = NewChatString(lpDPInfo->lpDirectPlay3A, lpDPInfo->dpidPlayer, lpszChatStr, &lpszStr);
if FAILED(hr)
goto FAILURE;

// setup the DPCHAT struct
memset(&dpc, 0, sizeof(DPCHAT));
dpc.dwSize = sizeof(DPCHAT);
dpc.lpszMessageA = lpszStr;
BRANCHSTRUCTbs;

dpidTarget = lpDPInfo->lpGroupTree->GetDPIDOfCurrentSelection( &bs );

hr = IDirectPlay3_SendChatMessage(lpDPInfo->lpDirectPlay3A, lpDPInfo->dpidPlayer,
dpidTarget, DPSEND_GUARANTEED, &dpc);


if FAILED(hr)
goto FAILURE;

// display this string
PostMessage(hWnd, WM_USER_ADDSTRING, (WPARAM) 0, (LPARAM) lpszStr);
lpszStr = NULL;// set to NULL so we don't delete it below

FAILURE:
if (lpszChatStr)
GlobalFreePtr(lpszChatStr);

if (lpszStr)
GlobalFreePtr(lpszStr);

SetDlgItemText(hWnd, IDC_SENDEDIT, "");

return (hr);
}

///////////////////////////////////////////////////////////////////////////////////////
HRESULT NewChatString(LPDIRECTPLAY3A lpDirectPlay3A, DPID dpidPlayer,
LPSTR lpszMsg, LPSTR *lplpszStr)
{
LPDPNAMElpName = NULL;
LPSTRlpszStr = NULL;
LPSTRlpszPlayerName;
LPSTRszDisplayFormat = "%s>\t%s\r\n";
HRESULThr;

// get name of player
hr = GetPlayerName(lpDirectPlay3A, dpidPlayer, &lpName);
if FAILED(hr)
goto FAILURE;

if (lpName->lpszShortNameA)
lpszPlayerName = lpName->lpszShortNameA;
else
lpszPlayerName = "unknown";

// allocate space for display string
lpszStr = (LPSTR) GlobalAllocPtr(GHND, lstrlen(szDisplayFormat) +
lstrlen(lpszPlayerName) +
lstrlen(lpszMsg) + 1);
if (lpszStr == NULL)
{
hr = DPERR_OUTOFMEMORY;
goto FAILURE;
}

// build string
wsprintf(lpszStr, szDisplayFormat, lpszPlayerName, lpszMsg);

*lplpszStr = lpszStr;
lpszStr = NULL;

FAILURE:
if (lpszStr)
GlobalFreePtr(lpszStr);

if (lpName)
GlobalFreePtr(lpName);

return (hr);
}


///////////////////////////////////////////////////////////////////////////////////////
HRESULT WaitForRunMsg( DWORD dwAppId, HANDLE hReceiveEvent, LPDWORD lpdwStatus, LPDPLAYINFO lpDPInfo )
{
LPVOID lpvMsg = NULL;
DWORD dwMsgFlags;
DWORDdwMsgSize= 0;
BOOLbContinue= TRUE;
BOOLbNotFinished= TRUE;
HRESULT hr= E_FAIL;

// For this function, we could have spun a seperate thread or integrated
// the app/lobby client messaging into the main receive thread.
// For simplicity sake, the app/lobby client messaging loop is contained below.

// There are seven possible states that get us out of this function:
//1. receive DPLSYS_DPLAYCONNECTSUCCEEDED
// 2. receive DPLSYS_DPLAYCONNECTFAILED
// 3. (Option) timeout waiting for hReceiveEvent
// 4. DPERR_OUTOFMEMORY
// 5. We receive an unexpected player-to-player msg.
// 6. The app terminated.
// 7. We get a system message we don't know about.
// 8. We get an error calling receive

*lpdwStatus = 0;

while (bNotFinished)
{
if (hReceiveEvent)
{
if (WAIT_TIMEOUT == WaitForSingleObject( hReceiveEvent, 20000 ))
{
hr = DPERR_TIMEOUT;
bNotFinished = FALSE;
break;
}
}

do
{
hr =IDirectPlayLobby_ReceiveLobbyMessage(lpDPInfo->lpDirectPlayLobby2A,
0, dwAppId, &dwMsgFlags,
lpvMsg, &dwMsgSize);

switch( hr )
{
case DPERR_BUFFERTOOSMALL:
{
if (lpvMsg)
{
GlobalFreePtr( lpvMsg );
lpvMsg = NULL;
}

lpvMsg = GlobalAllocPtr(GHND, dwMsgSize);
if(!lpvMsg)
{hr = DPERR_OUTOFMEMORY;
goto end;
}
}
break;

case DP_OK:
{
// This better be a system message
if(!(dwMsgFlags & DPLAD_SYSTEM))
{
LPDPMSG_GENERIC lp = (LPDPMSG_GENERIC) lpvMsg;
hr = DPERR_GENERIC;
goto end;
}

if ( lpvMsg )
{
// Switch on the system message type
*lpdwStatus = *(LPDWORD)lpvMsg;

switch(*lpdwStatus)
{
case DPLSYS_APPTERMINATED:
// App shut down
bNotFinished = FALSE;
bContinue = FALSE;
break;

case DPLSYS_CONNECTIONSETTINGSREAD:
break;

case DPLSYS_DPLAYCONNECTSUCCEEDED:
case DPLSYS_DPLAYCONNECTFAILED:
bNotFinished = FALSE;
break;

default:
// RUNDPMSGLOG: Unexpected system message type
hr = DPERR_GENERIC;
bContinue = FALSE;
bNotFinished = FALSE;
break;
}
}
}
break;

case DPERR_NOMESSAGES:
// There are no messages left.
// We need to stop and wait for another
bContinue = FALSE;
break;

default:
goto end;
break;
}

} while( bContinue );
}

end:

// Free resources
GlobalFreePtr( lpvMsg );

// Return
return (DPERR_NOMESSAGES == hr ? DP_OK : hr );
}

///////////////////////////////////////////////////////////////////////////////////////
void HandleStartSession(LPDPLCONNECTION lpConn, LPDPLAYINFO lpDPInfo )
{
DWORDdwAppID;
CHARszStr[MAXSTRLEN];
HRESULThr;
HANDLEhEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
DWORDdwStatus = 0;

if (hEvent)
{
// display name of application to launch
hr = GetLocalAppName(lpDPInfo->lpDirectPlayLobby2A,
&lpConn->lpSessionDesc->guidApplication, szStr);

if FAILED(hr)
lstrcpy(szStr, "unknown");

LogString("Launching \"%s\"...\r\n", szStr);

// Call RunApplication
hr = IDirectPlayLobby_RunApplication(lpDPInfo->lpDirectPlayLobby2A, 0, &dwAppID, lpConn, hEvent );

if FAILED(hr)
{
ErrorBox("Could not launch application because of error 0x%08X", hr);
}
else
{
// Wait for the app to launch
hr = WaitForRunMsg( dwAppID, hEvent, &dwStatus, lpDPInfo );
}

CloseHandle( hEvent );
}

} // HandleStartSession


///////////////////////////////////////////////////////////////////////////////////////
BOOL FAR PASCAL EnumApp(LPCDPLAPPINFO lpAppInfo, LPVOID lpContext, DWORD dwFlags)
{
HWNDhWnd = (HWND)lpContext;
LRESULTiIndex;
LPGUIDlpGuid;

// store application name in combo box
iIndex = SendDlgItemMessage(hWnd, IDC_APPCOMBO, CB_ADDSTRING, 0, (LPARAM) lpAppInfo->lpszAppNameA);
if (iIndex == LB_ERR)
goto Failure;

// make space for application GUID
lpGuid = (LPGUID) GlobalAllocPtr(GHND, sizeof(GUID));
if (lpGuid == NULL)
goto Failure;

// store pointer to GUID in combo box
*lpGuid = lpAppInfo->guidApplication;
SendDlgItemMessage(hWnd, IDC_APPCOMBO, CB_SETITEMDATA, (WPARAM) iIndex, (LPARAM) lpGuid);

Failure:
return (TRUE);
}

///////////////////////////////////////////////////////////////////////////////////////
HRESULT GetComboBoxGuid(HWND hWnd, LONG iDialogItem, LPGUID lpguidReturn)
{
LONGiIndex;

// get index of selected item
iIndex = SendDlgItemMessage(hWnd, iDialogItem, CB_GETCURSEL,
(WPARAM) 0, (LPARAM) 0);
if (iIndex == CB_ERR)
return (DPERR_GENERIC);

// get data associated with this item
iIndex = SendDlgItemMessage(hWnd, iDialogItem, CB_GETITEMDATA,
(WPARAM) iIndex, (LPARAM) 0);
if ((iIndex == CB_ERR) || (iIndex == 0))
return (DPERR_GENERIC);

// data is a pointer to a guid
*lpguidReturn = *((LPGUID) iIndex);

return (DP_OK);
}

///////////////////////////////////////////////////////////////////////////////////////
BOOL FAR PASCAL GetLocalAppNameCallback(LPCDPLAPPINFO lpAppInfo, LPVOID lpContext, DWORD dwFlags)
{
LPAPPNAMECONTEXTlpAppNameContext = (LPAPPNAMECONTEXT) lpContext;

if (IsEqualGUID(lpAppInfo->guidApplication, lpAppNameContext->guidApplication))
{
lstrcpy(lpAppNameContext->szAppName, lpAppInfo->lpszAppNameA);
return (FALSE);
}

return (TRUE);
}

///////////////////////////////////////////////////////////////////////////////////////
HRESULT GetLocalAppName(LPDIRECTPLAYLOBBY lpIDPL,
LPGUID lpguidApplication, LPSTR lpszAppName)
{
HRESULThr;
APPNAMECONTEXTAppNameContext;

ZeroMemory(&AppNameContext, sizeof(APPNAMECONTEXT));
AppNameContext.guidApplication = *lpguidApplication;

// search local apps for matching guid
hr = IDirectPlayLobby_EnumLocalApplications(lpIDPL, GetLocalAppNameCallback, &AppNameContext, 0);
if FAILED(hr)
goto FAILURE;

// no local app found matching this guid
if (lstrlen(AppNameContext.szAppName) == 0)
{
hr = DPERR_GENERIC;
goto FAILURE;
}

// return name
lstrcpy(lpszAppName, AppNameContext.szAppName);

FAILURE:
return (hr);
}

///////////////////////////////////////////////////////////////////////////////////////
void ErrorBox(LPSTR lpszErrorStr, HRESULT hr)
{
charszStr[MAXSTRLEN];

wsprintf(szStr, lpszErrorStr, hr);

MessageBox(NULL, szStr, "Error", MB_OK);
}

///////////////////////////////////////////////////////////////////////////////////////
void OnSize(HWND hWnd, LPDPLAYINFO lpDPInfo)
{
HDWP hDWP;
RECT ClientRect;
int Height;
int xSpacing = lpDPInfo->xSpacing;
int ySpacing = lpDPInfo->ySpacing;
HWND hKeyTreeWnd;
HWND hValueListWnd;
HWND hMsgEditWnd;
HWND hSendButtonWnd;
int x;
int dx;
RECT SendButtonRect;
int sendbuttonwidth;
int sendbuttonheight;

if (IsIconic(hWnd))
return;

if ((hDWP = BeginDeferWindowPos(4)) != NULL)
{
// Data structure used when calling GetEffectiveClientRect (which takes into
// account space taken up by the toolbars/status bars). First half of the
// pair is zero when at the end of the list, second half is the control id.
int s_EffectiveClientRectData[] = {
1, 0, // For the menu bar, but is unused
0, 0 // First zero marks end of data
};

GetEffectiveClientRect(hWnd, &ClientRect, s_EffectiveClientRectData);
Height = ClientRect.bottom - ClientRect.top - (ySpacing * 5);

// Resize the tree control
hKeyTreeWnd = GetDlgItem( hWnd, IDT_MESSAGEVIEW );

DeferWindowPos(hDWP, hKeyTreeWnd, NULL,
xSpacing, ClientRect.top + ySpacing,
lpDPInfo->xPaneSplit, Height,
SWP_NOZORDER | SWP_NOACTIVATE);

x = lpDPInfo->xPaneSplit + lpDPInfo->xHalfSplitWidth * 2;
dx = ClientRect.right - ClientRect.left - x - xSpacing;

// Resize the logging window
hValueListWnd = GetDlgItem( hWnd, IDC_LOGEDIT );

DeferWindowPos(hDWP, hValueListWnd, NULL,
x, ClientRect.top+ySpacing,
dx, Height,
SWP_NOZORDER | SWP_NOACTIVATE);

// move the Send button, its size is constant
hSendButtonWnd = GetDlgItem( hWnd, IDC_SENDBUTTON);

GetWindowRect( hSendButtonWnd, &SendButtonRect );
sendbuttonwidth = SendButtonRect.right - SendButtonRect.left;
sendbuttonheight = SendButtonRect.bottom - SendButtonRect.top;

DeferWindowPos(hDWP, hSendButtonWnd, NULL,
ClientRect.right - ( sendbuttonwidth + xSpacing ),
ClientRect.bottom - ( sendbuttonheight + ySpacing ),
sendbuttonwidth,
sendbuttonheight,
SWP_NOZORDER | SWP_NOACTIVATE);

// Resize and move the message edit control
hMsgEditWnd = GetDlgItem( hWnd, IDC_SENDEDIT );

DeferWindowPos(hDWP, hMsgEditWnd, NULL,
ClientRect.left + xSpacing,
ClientRect.bottom - ( sendbuttonheight + ySpacing ),
ClientRect.right - ClientRect.left - (sendbuttonwidth + xSpacing * 3),
sendbuttonheight,
SWP_NOZORDER | SWP_NOACTIVATE);

EndDeferWindowPos(hDWP);
}
}