home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / graphics / directx / dpchat / dpchat.cpp < prev    next >
C/C++ Source or Header  |  1997-07-14  |  16KB  |  624 lines

  1. /*==========================================================================
  2.  *
  3.  *  Copyright (C) 1996-1997 Microsoft Corporation.  All Rights Reserved.
  4.  *
  5.  *  File:       dpchat.cpp
  6.  *  Content:    Simple chat program using DirectPlay.
  7.  *
  8.  ***************************************************************************/
  9.  
  10. #include <windows.h>
  11. #include <windowsx.h>
  12.  
  13. #include "dpchat.h"
  14. #include "resource.h"
  15.  
  16. // constants
  17. const DWORD APPMSG_CHATSTRING    = 0;            // message type for chat string
  18. const UINT    WM_USER_ADDSTRING    = WM_USER+257;    // window message to add string to chat string list
  19. const DWORD    MAXSTRLEN            = 200;            // max size of a temporary string
  20.  
  21. // structures
  22.  
  23. // message structure used to send a chat string to another player
  24. typedef struct {
  25.     DWORD    dwType;                                // message type (APPMSG_CHATSTRING)
  26.     char    szMsg[1];                            // message string (variable length)
  27. } MSG_CHATSTRING, *LPMSG_CHATSTRING;
  28.  
  29. // globals
  30. HANDLE            ghReceiveThread = NULL;            // handle of receive thread
  31. DWORD            gidReceiveThread = 0;            // id of receive thread
  32. HANDLE            ghKillReceiveEvent = NULL;        // event used to kill receive thread
  33. HWND            ghChatWnd = NULL;                // main chat window
  34.  
  35. // prototypes
  36. BOOL CALLBACK    ChatWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  37. HRESULT            SetupConnection(HINSTANCE hInstance, LPDPLAYINFO lpDPInfo);
  38. HRESULT            ShutdownConnection(LPDPLAYINFO lpDPInfo);
  39. DWORD WINAPI    ReceiveThread(LPVOID lpThreadParameter);
  40. HRESULT            ReceiveMessage(LPDPLAYINFO lpDPInfo);
  41. void            HandleApplicationMessage(LPDPLAYINFO lpDPInfo, LPDPMSG_GENERIC lpMsg, DWORD dwMsgSize,
  42.                                          DPID idFrom, DPID idTo);
  43. void            HandleSystemMessage(LPDPLAYINFO lpDPInfo, LPDPMSG_GENERIC lpMsg, DWORD dwMsgSize,
  44.                                     DPID idFrom, DPID idTo);
  45. HRESULT            GetChatPlayerName(LPDIRECTPLAY3A lpDirectPlay3A, DPID dpidPlayer,
  46.                                   LPDPNAME *lplpName);
  47. HRESULT            SendChatMessage(HWND hWnd, LPDPLAYINFO lpDPInfo);
  48. HRESULT            NewChatString(LPDIRECTPLAY3A lpDirectPlay3A, DPID dpidPlayer,
  49.                               LPSTR lpszMsg, LPSTR *lplpszStr);
  50.  
  51. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  52.                    LPSTR lpCmdLine, int nCmdShow )
  53. {
  54.     DPLAYINFO    DPInfo;
  55.     int            iResult = 0;
  56.     HRESULT        hr;
  57.  
  58.     // Initialize COM library
  59.     hr = CoInitialize(NULL);
  60.     if ( FAILED(hr) )
  61.         goto FAILURE;
  62.  
  63.  
  64.     // setup the connection
  65.     hr = SetupConnection(hInstance, &DPInfo);
  66.     if FAILED(hr)
  67.         goto FAILURE;
  68.  
  69.     // show the chat window
  70.     iResult = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_CHATDIALOG), NULL, (DLGPROC) ChatWndProc, (LPARAM) &DPInfo);
  71.  
  72. FAILURE:
  73.     // shut down the connection
  74.     hr = ShutdownConnection(&DPInfo);
  75.  
  76.     // Uninitialize the COM library
  77.     CoUninitialize();
  78.  
  79.     return (iResult);
  80. }
  81.  
  82. BOOL CALLBACK ChatWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  83. {
  84.     static LPDPLAYINFO    lpDPInfo;
  85.     DWORD                dwTextLen;
  86.  
  87.     switch(uMsg)
  88.     {
  89.     case WM_INITDIALOG:
  90.         // Save the connection info pointer
  91.         lpDPInfo = (LPDPLAYINFO) lParam;
  92.  
  93.         // store global window
  94.         ghChatWnd = hWnd;
  95.         break;
  96.  
  97.     case WM_DESTROY:
  98.         ghChatWnd = NULL;
  99.         break;
  100.  
  101.     // this is a user-defined message used to add strings to the log window
  102.     case WM_USER_ADDSTRING:
  103.         // get length of text in log window
  104.         dwTextLen = SendDlgItemMessage(hWnd, IDC_LOGEDIT, WM_GETTEXTLENGTH,
  105.                                        (WPARAM) 0, (LPARAM) 0);
  106.  
  107.         // put selection at end
  108.         dwTextLen = SendDlgItemMessage(hWnd, IDC_LOGEDIT, EM_SETSEL,
  109.                                        (WPARAM) dwTextLen, (LPARAM) dwTextLen);
  110.  
  111.         // add string in lParam to log window
  112.         SendDlgItemMessage(hWnd, IDC_LOGEDIT, EM_REPLACESEL,
  113.                             (WPARAM) FALSE, (LPARAM) lParam);
  114.         GlobalFreePtr((LPVOID) lParam);
  115.         break;
  116.  
  117.     case WM_COMMAND:
  118.         switch(LOWORD(wParam))
  119.         {
  120.         case IDC_SENDBUTTON:
  121.             SendChatMessage(hWnd, lpDPInfo);
  122.             break;            
  123.  
  124.         case IDCANCEL:
  125.             EndDialog(hWnd, FALSE);
  126.             break;
  127.         }
  128.         break;
  129.     }
  130.  
  131.     // Allow for default processing
  132.     return FALSE;
  133. }
  134.  
  135. HRESULT SetupConnection(HINSTANCE hInstance, LPDPLAYINFO lpDPInfo)
  136. {
  137.     HRESULT        hr;
  138.  
  139.     ZeroMemory(lpDPInfo, sizeof(DPLAYINFO));
  140.  
  141.     // create event used by DirectPlay to signal a message has arrived
  142.     lpDPInfo->hPlayerEvent = CreateEvent(NULL,        // no security
  143.                                          FALSE,        // auto reset
  144.                                          FALSE,        // initial event reset
  145.                                          NULL);        // no name
  146.     if (lpDPInfo->hPlayerEvent == NULL)
  147.     {
  148.         hr = DPERR_NOMEMORY;
  149.         goto FAILURE;
  150.     }
  151.  
  152.     // create event used to signal that the receive thread should exit
  153.     ghKillReceiveEvent = CreateEvent(NULL,        // no security
  154.                                      FALSE,        // auto reset
  155.                                      FALSE,        // initial event reset
  156.                                      NULL);        // no name
  157.     if (ghKillReceiveEvent == NULL)
  158.     {
  159.         hr = DPERR_NOMEMORY;
  160.         goto FAILURE;
  161.     }
  162.  
  163.     // create thread to receive player messages
  164.     ghReceiveThread = CreateThread(NULL,            // default security
  165.                                    0,                // default stack size
  166.                                    ReceiveThread,    // pointer to thread routine
  167.                                    lpDPInfo,        // argument for thread
  168.                                    0,                // start it right away
  169.                                    &gidReceiveThread);
  170.     if (ghReceiveThread == NULL)
  171.     {
  172.         hr = DPERR_NOMEMORY;
  173.         goto FAILURE;
  174.     }
  175.  
  176.     // try to connect using the lobby
  177.     hr = ConnectUsingLobby(lpDPInfo);
  178.     if FAILED(hr)
  179.     {
  180.         // if the error returned is DPERR_NOTLOBBIED, that means we
  181.         // were not launched by a lobby and we should ask the user for
  182.         // connection settings. If any other error is returned it means
  183.         // we were launched by a lobby but there was an error making the
  184.         // connection.
  185.  
  186.         if (hr != DPERR_NOTLOBBIED)
  187.             ErrorBox("Could not connect using lobby because of error 0x%08X", hr);
  188.  
  189.         // if there is no lobby connection, ask the user for settings
  190.         hr = ConnectUsingDialog(hInstance, lpDPInfo);
  191.         if FAILED(hr)
  192.             goto FAILURE;
  193.     }
  194.  
  195.     return (DP_OK);    
  196.  
  197. FAILURE:
  198.     ShutdownConnection(lpDPInfo);
  199.  
  200.     return (hr);
  201. }
  202.  
  203. HRESULT ShutdownConnection(LPDPLAYINFO lpDPInfo)
  204. {
  205.     if (ghReceiveThread)
  206.     {
  207.         // wake up receive thread and wait for it to quit
  208.         SetEvent(ghKillReceiveEvent);
  209.         WaitForSingleObject(ghReceiveThread, INFINITE);
  210.  
  211.         CloseHandle(ghReceiveThread);
  212.         ghReceiveThread = NULL;
  213.     }
  214.  
  215.     if (ghKillReceiveEvent)
  216.     {
  217.         CloseHandle(ghKillReceiveEvent);
  218.         ghKillReceiveEvent = NULL;
  219.     }
  220.  
  221.     if (lpDPInfo->lpDirectPlay3A)
  222.     {
  223.         if (lpDPInfo->dpidPlayer)
  224.         {
  225.             lpDPInfo->lpDirectPlay3A->DestroyPlayer(lpDPInfo->dpidPlayer);
  226.             lpDPInfo->dpidPlayer = 0;
  227.         }
  228.         lpDPInfo->lpDirectPlay3A->Close();
  229.         lpDPInfo->lpDirectPlay3A->Release();
  230.         lpDPInfo->lpDirectPlay3A = NULL;
  231.     }
  232.  
  233.     if (lpDPInfo->hPlayerEvent)
  234.     {
  235.         CloseHandle(lpDPInfo->hPlayerEvent);
  236.         lpDPInfo->hPlayerEvent = NULL;
  237.     }
  238.  
  239.     return (DP_OK);
  240. }
  241.  
  242. DWORD WINAPI ReceiveThread(LPVOID lpThreadParameter)
  243. {
  244.     LPDPLAYINFO    lpDPInfo = (LPDPLAYINFO) lpThreadParameter;
  245.     HANDLE        eventHandles[2];
  246.  
  247.     eventHandles[0] = lpDPInfo->hPlayerEvent;
  248.     eventHandles[1] = ghKillReceiveEvent;
  249.  
  250.     // loop waiting for player events. If the kill event is signaled
  251.     // the thread will exit
  252.     while (WaitForMultipleObjects(2, eventHandles, FALSE, INFINITE) == WAIT_OBJECT_0)
  253.     {
  254.         // receive any messages in the queue
  255.         ReceiveMessage(lpDPInfo);
  256.     }
  257.  
  258.     ExitThread(0);
  259.  
  260.     return (0);
  261. }
  262.  
  263. HRESULT ReceiveMessage(LPDPLAYINFO lpDPInfo)
  264. {
  265.     DPID                idFrom, idTo;
  266.     LPVOID                lpvMsgBuffer;
  267.     DWORD                dwMsgBufferSize;
  268.     HRESULT                hr;
  269.  
  270.     lpvMsgBuffer = NULL;
  271.     dwMsgBufferSize = 0;
  272.  
  273.     // loop to read all messages in queue
  274.     do
  275.     {
  276.         // loop until a single message is successfully read
  277.         do
  278.         {
  279.             // read messages from any player, including system player
  280.             idFrom = 0;
  281.             idTo = 0;
  282.  
  283.             hr = lpDPInfo->lpDirectPlay3A->Receive(&idFrom, &idTo, DPRECEIVE_ALL,
  284.                                                    lpvMsgBuffer, &dwMsgBufferSize);
  285.  
  286.             // not enough room, so resize buffer
  287.             if (hr == DPERR_BUFFERTOOSMALL)
  288.             {
  289.                 if (lpvMsgBuffer)
  290.                     GlobalFreePtr(lpvMsgBuffer);
  291.                 lpvMsgBuffer = GlobalAllocPtr(GHND, dwMsgBufferSize);
  292.                 if (lpvMsgBuffer == NULL)
  293.                     hr = DPERR_OUTOFMEMORY;
  294.             }
  295.         } while (hr == DPERR_BUFFERTOOSMALL);
  296.  
  297.         if ((SUCCEEDED(hr)) &&                            // successfully read a message
  298.             (dwMsgBufferSize >= sizeof(DPMSG_GENERIC)))    // and it is big enough
  299.         {
  300.             // check for system message
  301.             if (idFrom == DPID_SYSMSG)
  302.             {
  303.                 HandleSystemMessage(lpDPInfo, (LPDPMSG_GENERIC) lpvMsgBuffer,
  304.                                     dwMsgBufferSize, idFrom, idTo);
  305.             }
  306.             else
  307.             {
  308.                 HandleApplicationMessage(lpDPInfo, (LPDPMSG_GENERIC) lpvMsgBuffer,
  309.                                          dwMsgBufferSize, idFrom, idTo);
  310.             }
  311.         }
  312.     } while (SUCCEEDED(hr));
  313.  
  314.     // free any memory we created
  315.     if (lpvMsgBuffer)
  316.         GlobalFreePtr(lpvMsgBuffer);
  317.  
  318.     return (DP_OK);
  319. }
  320.  
  321. void HandleApplicationMessage(LPDPLAYINFO lpDPInfo, LPDPMSG_GENERIC lpMsg, DWORD dwMsgSize,
  322.                               DPID idFrom, DPID idTo)
  323. {
  324.     LPSTR    lpszStr = NULL;
  325.     HRESULT    hr;
  326.  
  327.     switch (lpMsg->dwType)
  328.     {
  329.     case APPMSG_CHATSTRING:
  330.         {
  331.             LPMSG_CHATSTRING   lp = (LPMSG_CHATSTRING) lpMsg;
  332.  
  333.             // create string to display
  334.             hr = NewChatString(lpDPInfo->lpDirectPlay3A, idFrom, lp->szMsg, &lpszStr);
  335.             if FAILED(hr)
  336.                 break;
  337.         }
  338.         break;
  339.     }
  340.  
  341.     // post string to chat window
  342.     if (lpszStr)
  343.     {
  344.         // make sure window is still valid
  345.         if (ghChatWnd)
  346.             PostMessage(ghChatWnd, WM_USER_ADDSTRING, (WPARAM) 0, (LPARAM) lpszStr);
  347.         else
  348.             GlobalFreePtr(lpszStr);
  349.     }
  350. }
  351.  
  352. void HandleSystemMessage(LPDPLAYINFO lpDPInfo, LPDPMSG_GENERIC lpMsg, DWORD dwMsgSize,
  353.                          DPID idFrom, DPID idTo)
  354. {
  355.     LPSTR        lpszStr = NULL;
  356.  
  357.     // The body of each case is there so you can set a breakpoint and examine
  358.     // the contents of the message received.
  359.     switch (lpMsg->dwType)
  360.     {
  361.     case DPSYS_CREATEPLAYERORGROUP:
  362.         {
  363.             LPDPMSG_CREATEPLAYERORGROUP        lp = (LPDPMSG_CREATEPLAYERORGROUP) lpMsg;
  364.             LPSTR                            lpszPlayerName;
  365.             LPSTR                            szDisplayFormat = "\"%s\" has joined\r\n";
  366.             
  367.             // get pointer to player name
  368.             if (lp->dpnName.lpszShortNameA)
  369.                 lpszPlayerName = lp->dpnName.lpszShortNameA;
  370.             else
  371.                 lpszPlayerName = "unknown";
  372.  
  373.             // allocate space for string
  374.             lpszStr = (LPSTR) GlobalAllocPtr(GHND, lstrlen(szDisplayFormat) +
  375.                                                    lstrlen(lpszPlayerName) + 1);
  376.             if (lpszStr == NULL)
  377.                 break;
  378.  
  379.             // build string
  380.             wsprintf(lpszStr, szDisplayFormat, lpszPlayerName);
  381.         }
  382.         break;
  383.  
  384.     case DPSYS_DESTROYPLAYERORGROUP:
  385.         {
  386.             LPDPMSG_DESTROYPLAYERORGROUP    lp = (LPDPMSG_DESTROYPLAYERORGROUP)lpMsg;
  387.             LPSTR                            lpszPlayerName;
  388.             LPSTR                            szDisplayFormat = "\"%s\" has left\r\n";
  389.             
  390.             // get pointer to player name
  391.             if (lp->dpnName.lpszShortNameA)
  392.                 lpszPlayerName = lp->dpnName.lpszShortNameA;
  393.             else
  394.                 lpszPlayerName = "unknown";
  395.  
  396.             // allocate space for string
  397.             lpszStr = (LPSTR) GlobalAllocPtr(GHND, lstrlen(szDisplayFormat) +
  398.                                                    lstrlen(lpszPlayerName) + 1);
  399.             if (lpszStr == NULL)
  400.                 break;
  401.  
  402.             // build string
  403.             wsprintf(lpszStr, szDisplayFormat, lpszPlayerName);
  404.         }
  405.         break;
  406.  
  407.     case DPSYS_ADDPLAYERTOGROUP:
  408.         {
  409.             LPDPMSG_ADDPLAYERTOGROUP lp = (LPDPMSG_ADDPLAYERTOGROUP)lpMsg;
  410.         }
  411.         break;
  412.  
  413.     case DPSYS_DELETEPLAYERFROMGROUP:
  414.         {
  415.             LPDPMSG_DELETEPLAYERFROMGROUP lp = (LPDPMSG_DELETEPLAYERFROMGROUP)lpMsg;
  416.         }
  417.         break;
  418.  
  419.     case DPSYS_SESSIONLOST:
  420.         {
  421.             LPDPMSG_SESSIONLOST lp = (LPDPMSG_SESSIONLOST)lpMsg;
  422.         }
  423.         break;
  424.  
  425.     case DPSYS_HOST:
  426.         {
  427.             LPDPMSG_HOST    lp = (LPDPMSG_HOST)lpMsg;
  428.             LPSTR            szDisplayFormat = "You have become the host\r\n";
  429.  
  430.             // allocate space for string
  431.             lpszStr = (LPSTR) GlobalAllocPtr(GHND, lstrlen(szDisplayFormat) + 1);
  432.             if (lpszStr == NULL)
  433.                 break;
  434.  
  435.             // build string
  436.             lstrcpy(lpszStr, szDisplayFormat);
  437.  
  438.             // we are now the host
  439.             lpDPInfo->bIsHost = TRUE;
  440.         }
  441.         break;
  442.  
  443.     case DPSYS_SETPLAYERORGROUPDATA:
  444.         {
  445.             LPDPMSG_SETPLAYERORGROUPDATA lp = (LPDPMSG_SETPLAYERORGROUPDATA)lpMsg;
  446.         }
  447.         break;
  448.  
  449.     case DPSYS_SETPLAYERORGROUPNAME:
  450.         {
  451.             LPDPMSG_SETPLAYERORGROUPNAME lp = (LPDPMSG_SETPLAYERORGROUPNAME)lpMsg;
  452.         }
  453.         break;
  454.     }
  455.  
  456.     // post string to chat window
  457.     if (lpszStr)
  458.     {
  459.         // make sure window is still valid
  460.         if (ghChatWnd)
  461.             PostMessage(ghChatWnd, WM_USER_ADDSTRING, (WPARAM) 0, (LPARAM) lpszStr);
  462.         else
  463.             GlobalFreePtr(lpszStr);
  464.     }
  465. }
  466.  
  467. HRESULT GetChatPlayerName(LPDIRECTPLAY3A lpDirectPlay3A, DPID dpidPlayer,
  468.                           LPDPNAME *lplpName)
  469. {
  470.     LPDPNAME    lpName = NULL;
  471.     DWORD        dwNameSize;
  472.     HRESULT        hr;
  473.  
  474.     // get size of player name data
  475.     hr = lpDirectPlay3A->GetPlayerName(dpidPlayer, NULL, &dwNameSize);
  476.     if (hr != DPERR_BUFFERTOOSMALL)
  477.         goto FAILURE;
  478.  
  479.     // make room for it
  480.     lpName = (LPDPNAME) GlobalAllocPtr(GHND, dwNameSize);
  481.     if (lpName == NULL)
  482.     {
  483.         hr = DPERR_OUTOFMEMORY;
  484.         goto FAILURE;
  485.     }
  486.  
  487.     // get player name data
  488.     hr = lpDirectPlay3A->GetPlayerName(dpidPlayer, lpName, &dwNameSize);
  489.     if FAILED(hr)
  490.         goto FAILURE;
  491.  
  492.     // return pointer to name structure
  493.     *lplpName = lpName;
  494.  
  495.     return (DP_OK);
  496.  
  497. FAILURE:
  498.     if (lpName)
  499.         GlobalFreePtr(lpName);
  500.  
  501.     return (hr);
  502. }
  503.  
  504. HRESULT SendChatMessage(HWND hWnd, LPDPLAYINFO lpDPInfo)
  505. {
  506.     LPSTR                lpszChatStr = NULL;
  507.     LPSTR                lpszStr = NULL;
  508.     LPMSG_CHATSTRING    lpChatMessage = NULL;
  509.     DWORD                dwChatMessageSize;
  510.     LONG                lStrLen;
  511.     HRESULT                hr;
  512.  
  513.     // get length of item text
  514.     lStrLen = SendDlgItemMessage(hWnd, IDC_SENDEDIT, WM_GETTEXTLENGTH,
  515.                                 (WPARAM) 0, (LPARAM) 0);
  516.  
  517.     // make room for it
  518.     lpszChatStr = (LPSTR) GlobalAllocPtr(GHND, lStrLen + 1);
  519.     if (lpszChatStr == NULL)
  520.     {
  521.         hr = DPERR_OUTOFMEMORY;
  522.         goto FAILURE;
  523.     }
  524.  
  525.     // get item text
  526.     lStrLen = GetDlgItemText(hWnd, IDC_SENDEDIT, lpszChatStr, lStrLen + 1);
  527.  
  528.     // create string to display this text
  529.     hr = NewChatString(lpDPInfo->lpDirectPlay3A, lpDPInfo->dpidPlayer, lpszChatStr, &lpszStr);
  530.     if FAILED(hr)
  531.         goto FAILURE;
  532.  
  533.     // display this string
  534.     PostMessage(hWnd, WM_USER_ADDSTRING, (WPARAM) 0, (LPARAM) lpszStr);
  535.     lpszStr = NULL;                        // set to NULL so we don't delete it below
  536.  
  537.     // create space for message plus string (string length included in message header)
  538.     dwChatMessageSize = sizeof(MSG_CHATSTRING) + lstrlen(lpszChatStr);
  539.     lpChatMessage = (LPMSG_CHATSTRING) GlobalAllocPtr(GHND, dwChatMessageSize);
  540.     if (lpChatMessage == NULL)
  541.     {
  542.         hr = DPERR_OUTOFMEMORY;
  543.         goto FAILURE;
  544.     }
  545.  
  546.     // build message    
  547.     lpChatMessage->dwType = APPMSG_CHATSTRING;
  548.     lstrcpy(lpChatMessage->szMsg, lpszChatStr);
  549.     
  550.     // send this string to all other players
  551.     hr = lpDPInfo->lpDirectPlay3A->Send(lpDPInfo->dpidPlayer, DPID_ALLPLAYERS,
  552.                                         DPSEND_GUARANTEED, lpChatMessage, dwChatMessageSize);
  553.     if FAILED(hr)
  554.         goto FAILURE;
  555.  
  556. FAILURE:
  557.     if (lpszChatStr)
  558.         GlobalFreePtr(lpszChatStr);
  559.  
  560.     if (lpszStr)
  561.         GlobalFreePtr(lpszStr);
  562.  
  563.     if (lpChatMessage)
  564.         GlobalFreePtr(lpChatMessage);
  565.  
  566.     SetDlgItemText(hWnd, IDC_SENDEDIT, "");
  567.  
  568.     return (hr);
  569. }
  570.  
  571. HRESULT NewChatString(LPDIRECTPLAY3A lpDirectPlay3A, DPID dpidPlayer,
  572.                       LPSTR lpszMsg, LPSTR *lplpszStr)
  573. {
  574.     LPDPNAME    lpName = NULL;
  575.     LPSTR        lpszStr = NULL;
  576.     LPSTR        lpszPlayerName;
  577.     LPSTR        szDisplayFormat = "%s>\t%s\r\n";
  578.     HRESULT        hr;
  579.     
  580.     // get name of player
  581.     hr = GetChatPlayerName(lpDirectPlay3A, dpidPlayer, &lpName);
  582.     if FAILED(hr)
  583.         goto FAILURE;
  584.  
  585.     if (lpName->lpszShortNameA)
  586.         lpszPlayerName = lpName->lpszShortNameA;
  587.     else
  588.         lpszPlayerName = "unknown";
  589.  
  590.     // allocate space for display string
  591.     lpszStr = (LPSTR) GlobalAllocPtr(GHND, lstrlen(szDisplayFormat) +
  592.                                            lstrlen(lpszPlayerName) +
  593.                                            lstrlen(lpszMsg) + 1);
  594.     if (lpszStr == NULL)
  595.     {
  596.         hr = DPERR_OUTOFMEMORY;
  597.         goto FAILURE;
  598.     }
  599.  
  600.     // build string
  601.     wsprintf(lpszStr, szDisplayFormat, lpszPlayerName, lpszMsg);
  602.  
  603.     *lplpszStr = lpszStr;
  604.     lpszStr = NULL;
  605.  
  606. FAILURE:
  607.     if (lpszStr)
  608.         GlobalFreePtr(lpszStr);
  609.     
  610.     if (lpName)
  611.         GlobalFreePtr(lpName);
  612.  
  613.     return (hr);
  614. }
  615.  
  616. void ErrorBox(LPSTR lpszErrorStr, HRESULT hr)
  617. {
  618.     char    szStr[MAXSTRLEN];
  619.  
  620.     wsprintf(szStr, lpszErrorStr, hr);
  621.  
  622.     MessageBox(NULL, szStr, "Error", MB_OK);
  623. }
  624.