/*==========================================================================
*
* Copyright (C) 1996-1997 Microsoft Corporation. All Rights Reserved.
*
* File: dpchat.cpp
* Content:Simple chat program using DirectPlay.
*
***************************************************************************/
#include <windows.h>
#include <windowsx.h>
#include "dpchat.h"
#include "resource.h"
// constants
const DWORD APPMSG_CHATSTRING= 0;// message type for chat string
const UINTWM_USER_ADDSTRING= WM_USER+257;// window message to add string to chat string list
const DWORDMAXSTRLEN= 200;// max size of a temporary string
// structures
// message structure used to send a chat string to another player
typedef struct {
DWORDdwType;// message type (APPMSG_CHATSTRING)
charszMsg[1];// message string (variable length)
} MSG_CHATSTRING, *LPMSG_CHATSTRING;
// globals
HANDLEghReceiveThread = NULL;// handle of receive thread
DWORDgidReceiveThread = 0;// id of receive thread
HANDLEghKillReceiveEvent = NULL;// event used to kill receive thread
HWNDghChatWnd = NULL;// main chat window
// prototypes
BOOL CALLBACKChatWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
HRESULTSetupConnection(HINSTANCE hInstance, LPDPLAYINFO lpDPInfo);
HRESULTShutdownConnection(LPDPLAYINFO lpDPInfo);
DWORD WINAPIReceiveThread(LPVOID lpThreadParameter);
HRESULTReceiveMessage(LPDPLAYINFO lpDPInfo);
voidHandleApplicationMessage(LPDPLAYINFO lpDPInfo, LPDPMSG_GENERIC lpMsg, DWORD dwMsgSize,
DPID idFrom, DPID idTo);
voidHandleSystemMessage(LPDPLAYINFO lpDPInfo, LPDPMSG_GENERIC lpMsg, DWORD dwMsgSize,
DPID idFrom, DPID idTo);
HRESULTGetChatPlayerName(LPDIRECTPLAY3A lpDirectPlay3A, DPID dpidPlayer,
LPDPNAME *lplpName);
HRESULTSendChatMessage(HWND hWnd, LPDPLAYINFO lpDPInfo);
HRESULTNewChatString(LPDIRECTPLAY3A lpDirectPlay3A, DPID dpidPlayer,
LPSTR lpszMsg, LPSTR *lplpszStr);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
DPLAYINFODPInfo;
intiResult = 0;
HRESULThr;
// 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_CHATDIALOG), NULL, (DLGPROC) ChatWndProc, (LPARAM) &DPInfo);
FAILURE:
// shut down the connection
hr = ShutdownConnection(&DPInfo);
// Uninitialize the COM library
CoUninitialize();
return (iResult);
}
BOOL CALLBACK ChatWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static LPDPLAYINFOlpDPInfo;
DWORDdwTextLen;
switch(uMsg)
{
case WM_INITDIALOG:
// Save the connection info pointer
lpDPInfo = (LPDPLAYINFO) lParam;
// store global window
ghChatWnd = hWnd;
break;
case WM_DESTROY:
ghChatWnd = NULL;
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 IDC_SENDBUTTON:
SendChatMessage(hWnd, lpDPInfo);
break;
case IDCANCEL:
EndDialog(hWnd, FALSE);
break;
}
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;
}
// try to connect using the lobby
hr = ConnectUsingLobby(lpDPInfo);
if FAILED(hr)
{
// if the error returned is DPERR_NOTLOBBIED, that means we
// were not launched by a lobby and we should ask the user for
// connection settings. If any other error is returned it means
// we were launched by a lobby but there was an error making the
// connection.
if (hr != DPERR_NOTLOBBIED)
ErrorBox("Could not connect using lobby because of error 0x%08X", hr);
// 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)
{
lpDPInfo->lpDirectPlay3A->DestroyPlayer(lpDPInfo->dpidPlayer);
lpDPInfo->dpidPlayer = 0;
}
lpDPInfo->lpDirectPlay3A->Close();
lpDPInfo->lpDirectPlay3A->Release();
lpDPInfo->lpDirectPlay3A = NULL;
}
if (lpDPInfo->hPlayerEvent)
{
CloseHandle(lpDPInfo->hPlayerEvent);
lpDPInfo->hPlayerEvent = 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 = lpDPInfo->lpDirectPlay3A->Receive(&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
{
// 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)
{
LPSTRlpszStr = NULL;
HRESULThr;
switch (lpMsg->dwType)
{
case APPMSG_CHATSTRING:
{
LPMSG_CHATSTRING lp = (LPMSG_CHATSTRING) lpMsg;
// create string to display
hr = NewChatString(lpDPInfo->lpDirectPlay3A, idFrom, lp->szMsg, &lpszStr);
if FAILED(hr)
break;
}
break;
}
// post string to chat window
if (lpszStr)
{
// make sure window is still valid
if (ghChatWnd)
PostMessage(ghChatWnd, WM_USER_ADDSTRING, (WPARAM) 0, (LPARAM) lpszStr);
else
GlobalFreePtr(lpszStr);
}
}
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_CREATEPLAYERORGROUP:
{
LPDPMSG_CREATEPLAYERORGROUPlp = (LPDPMSG_CREATEPLAYERORGROUP) lpMsg;
LPSTRlpszPlayerName;
LPSTRszDisplayFormat = "\"%s\" has joined\r\n";
// get pointer to player name
if (lp->dpnName.lpszShortNameA)
lpszPlayerName = lp->dpnName.lpszShortNameA;
else
lpszPlayerName = "unknown";
// allocate space for string
lpszStr = (LPSTR) GlobalAllocPtr(GHND, lstrlen(szDisplayFormat) +
lstrlen(lpszPlayerName) + 1);
if (lpszStr == NULL)
break;
// build string
wsprintf(lpszStr, szDisplayFormat, lpszPlayerName);
}
break;
case DPSYS_DESTROYPLAYERORGROUP:
{
LPDPMSG_DESTROYPLAYERORGROUPlp = (LPDPMSG_DESTROYPLAYERORGROUP)lpMsg;
LPSTRlpszPlayerName;
LPSTRszDisplayFormat = "\"%s\" has left\r\n";
// get pointer to player name
if (lp->dpnName.lpszShortNameA)
lpszPlayerName = lp->dpnName.lpszShortNameA;
else
lpszPlayerName = "unknown";
// allocate space for string
lpszStr = (LPSTR) GlobalAllocPtr(GHND, lstrlen(szDisplayFormat) +
lstrlen(lpszPlayerName) + 1);
if (lpszStr == NULL)
break;
// build string
wsprintf(lpszStr, szDisplayFormat, lpszPlayerName);
}
break;
case DPSYS_ADDPLAYERTOGROUP:
{
LPDPMSG_ADDPLAYERTOGROUP lp = (LPDPMSG_ADDPLAYERTOGROUP)lpMsg;
}
break;
case DPSYS_DELETEPLAYERFROMGROUP:
{
LPDPMSG_DELETEPLAYERFROMGROUP lp = (LPDPMSG_DELETEPLAYERFROMGROUP)lpMsg;
}
break;
case DPSYS_SESSIONLOST:
{
LPDPMSG_SESSIONLOST lp = (LPDPMSG_SESSIONLOST)lpMsg;
}
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;
}
// post string to chat window
if (lpszStr)
{
// make sure window is still valid
if (ghChatWnd)
PostMessage(ghChatWnd, WM_USER_ADDSTRING, (WPARAM) 0, (LPARAM) lpszStr);
else
GlobalFreePtr(lpszStr);
}
}
HRESULT GetChatPlayerName(LPDIRECTPLAY3A lpDirectPlay3A, DPID dpidPlayer,
LPDPNAME *lplpName)
{
LPDPNAMElpName = NULL;
DWORDdwNameSize;
HRESULThr;
// get size of player name data
hr = lpDirectPlay3A->GetPlayerName(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 = lpDirectPlay3A->GetPlayerName(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 SendChatMessage(HWND hWnd, LPDPLAYINFO lpDPInfo)
{
LPSTRlpszChatStr = NULL;
LPSTRlpszStr = NULL;
LPMSG_CHATSTRINGlpChatMessage = NULL;
DWORDdwChatMessageSize;
LONGlStrLen;
HRESULThr;
// 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;
// display this string
PostMessage(hWnd, WM_USER_ADDSTRING, (WPARAM) 0, (LPARAM) lpszStr);
lpszStr = NULL;// set to NULL so we don't delete it below
// create space for message plus string (string length included in message header)
dwChatMessageSize = sizeof(MSG_CHATSTRING) + lstrlen(lpszChatStr);
lpChatMessage = (LPMSG_CHATSTRING) GlobalAllocPtr(GHND, dwChatMessageSize);
if (lpChatMessage == NULL)
{
hr = DPERR_OUTOFMEMORY;
goto FAILURE;
}
// build message
lpChatMessage->dwType = APPMSG_CHATSTRING;
lstrcpy(lpChatMessage->szMsg, lpszChatStr);
// send this string to all other players
hr = lpDPInfo->lpDirectPlay3A->Send(lpDPInfo->dpidPlayer, DPID_ALLPLAYERS,
DPSEND_GUARANTEED, lpChatMessage, dwChatMessageSize);
if FAILED(hr)
goto FAILURE;
FAILURE:
if (lpszChatStr)
GlobalFreePtr(lpszChatStr);
if (lpszStr)
GlobalFreePtr(lpszStr);
if (lpChatMessage)
GlobalFreePtr(lpChatMessage);
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 = GetChatPlayerName(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);
}
void ErrorBox(LPSTR lpszErrorStr, HRESULT hr)
{
charszStr[MAXSTRLEN];
wsprintf(szStr, lpszErrorStr, hr);
MessageBox(NULL, szStr, "Error", MB_OK);
}