## DirectPlay Tutorials

This section contains four tutorials that provide step-by-step instructions about how to connect an application with or without a lobby, how to override service provider dialog boxes, and how to create a self-refreshing session list.

The first tutorial demonstrates how to connect an application by using a DirectPlay lobby.

The second tutorial demonstrates how to connect an application by using a dialog box that queries the user for connection information. You should write your application so that it can start by using either method. The code is available in the DPCHAT sample in the LOBBY.CPP and DIALOG.CPP files.

The third tutorial demonstrates the calls you need to supply the service provider with all the information it needs so that it doesn't display dialog boxes to the user requesting information.

The fourth tutorial demonstrates how to create a self-refreshing session list.

- Tutorial 1: Connecting by Using the Lobby (DPCHAT)
- Tutorial 2: Connecting by Using a Dialog Box (DPCHAT)
- Tutorial 3: Overriding the Service Provider Dialogs
- Tutorial 4: Creating Self-Refreshing Session Lists

Note The sample files in these tutorials are written in C++. If you are using a C compiler, you must make the appropriate changes to the files for them to successfully compile. At the very least, you must add the vtables and this pointers to the interface methods. For more information, see Accessing COM in the DirectX Foundation Software Development Kit.

### Tutorial 1: Connecting by Using the Lobby

An application written to use the IDirectPlayLobby2 Interface interface can be connected to a session without requiring the user to manually enter connection information in a dialog box. To demonstrate how to create a lobbied application, the DPCHAT sample performs the following steps:

- Step 1: Creating a DirectPlayLobby Object
- Step 2: Retrieving the Connection Settings
- Step 3: Configuring the Session Description
- Step 4: Connecting to a Session
- Step 5: Creating a Player

#### Step 1: Creating a DirectPlayLobby Object

To use a DirectPlay lobby, you first create an instance of a DirectPlayLobby object by calling the DirectPlayLobbyCreate function. This function contains five parameters. The first, third, and fourth parameters are always set to NULL and are included for future expansion. The second parameter contains the address of a pointer that identifies the location of the DirectPlayLobby object if it is created. The fifth parameter is always set to 0, and is also included for future expansion.

The following example shows one way to create a DirectPlayLobby object:

    // Get an ANSI DirectPlay lobby interface.
    hr = DirectPlayLobbyCreate(NULL, &lpDirectPlayLobbyA, NULL, NULL, 0);
    if FAILED(hr)
        goto FAILURE;

#### Step 2: Retrieving the Connection Settings

After the DirectPlayLobby object has been created, use the IDirectPlayLobby2::GetConnectionSettings method to retrieve the connection settings returned from the lobby. If this method returns DPERR_NOTLOBBIED, the lobby did not start this application, and the user will have to configure the connection manually. If any other error occurs, your application should report an error that indicates that lobbying the application failed.

The following example shows how to retrieve the connection settings:

    // Retrieve the connection settings from the lobby.
    // If this routine returns DPERR_NOTLOBBIED, then a lobby did not
    // start this application and the user needs to configure the
    // connection.

    // Pass a NULL pointer to retrieve only the size of the
    // connection settings
    hr = lpDirectPlayLobbyA->GetConnectionSettings(0, NULL, &dwSize);
    if (DPERR_BUFFERTOOSMALL != hr)
        goto FAILURE;

    // Allocate memory for the connection settings.
    lpConnectionSettings = (LPDPLCONNECTION) GlobalAllocPtr(GHND, dwSize);
        if (NULL == lpConnectionSettings)
        {
            hr = DPERR_OUTOFMEMORY;
            goto FAILURE;
        }

        // Retrieve the connection settings.
        hr = lpDirectPlayLobbyA->GetConnectionSettings(0,
            lpConnectionSettings, &dwSize);
        if FAILED(hr)
            goto FAILURE;

#### Step 3: Configuring the Session Description

You should examine the DPSESSIONDESC2 structure to ensure that all the flags and properties that your application needs are set properly. If modifications are necessary, store the modified connection settings by using the IDirectPlayLobby2::SetConnectionSettings method.

The following example shows how to configure the session description and set the connection settings:

    // Before the game connects, it should configure the session
    // description with any settings it needs.

    // Set the flags and maximum players used by the game.
    lpConnectionSettings->lpSessionDesc->dwFlags = DPSESSION_MIGRATEHOST |
        DPSESSION_KEEPALIVE;
    lpConnectionSettings->lpSessionDesc->dwMaxPlayers = MAXPLAYERS;

    // Store the updated connection settings.
    hr = lpDirectPlayLobbyA->SetConnectionSettings(0, 0,
            lpConnectionSettings);
    if FAILED(hr)
        goto FAILURE;

#### Step 4: Connecting to a Session

After the session description is properly configured, your application can use the IDirectPlayLobby2::Connect method to start and connect itself to a session. If this method returns DP_OK, you can create one or more players. If it returns DPERR_NOTLOBBIED, the user will have to manually select a communication medium for your application. (You can identify the service providers installed on the system by using the DirectPlayEnumerate function.) If any other error value is returned, your application should report an error that indicates that lobbying the application failed.

The following example shows how to connect to a session:

    // Connect to the session. Returns an ANSI IDirectPlay3A interface.
    hr = lpDirectPlayLobbyA->Connect(0, &lpDirectPlay2A, NULL);
    if FAILED(hr)
        goto FAILURE;
    // Obtain an IDriectPlay3A interface
    hr= lpDirectPlay2A->QueryInterface(IID_DirectPlay3A, (LPVOID*)&lpDirectPlay3A);
    if FAILED(hr)
        goto FAILURE;

#### Step 5: Creating a Player

If the application was successfully started by using the IDirectPlayLobby2::Connect method, it can now create one or more players. It can use the IDirectPlay3::CreatePlayer method to create a player with the name specified in the DPNAME structure (which was filled in by the IDirectPlayLobby2::GetConnectionSettings method).

The following example shows how to create a player:

    // create a player with the name returned in the connection settings
    hr = lpDirectPlay3A->CreatePlayer(&dpidPlayer,
        lpConnectionSettings->lpPlayerName,
        lpDPInfo->hPlayerEvent, NULL, 0, 0);
    if FAILED(hr)
        goto FAILURE;

Now your application is connected and you are ready to play.

### Tutorial 2: Connecting by Using a Dialog Box

If a lobby did not start your application, you should include code that allows the user to manually enter the connection information. To demonstrate how to manually connect to the session and create one or more players, the DPCHAT sample performs the following steps:

- Step 1: Creating the DirectPlay Object
- Step 2: Enumerating and Initializing the Service Providers
- Step 3: Joining a Session
- Step 4: Creating a Session
- Step 5: Creating a Player

#### Step 1: Creating the DirectPlay Object

Before any methods can be called, the application must create an interface to a DirectPlay object.

The following example shows how the create the IDirectPlay3A interface:

    HRESULT CreateDirectPlayInterface( LPDIRECTPLAY3A *lplpDirectPlay3A )
    {
       HRESULT         hr;
       LPDIRECTPLAY3A  lpDirectPlay3A = NULL;

       // Create an IDirectPlay3 interface
       hr = CoCreateInstance( CLSID_DirectPlay, NULL, CLSCTX_INPROC_SERVER,
                                IID_IDirectPlay3A, (LPVOID*)&lpDirectPlay3A);

       // Return interface created
       *lplpDirectPlay3A = lpDirectPlay3A;

       return (hr);
    }

#### Step 2: Enumerating and Initializing the Service Providers

The next step in creating a manual connection is to request that the user select a communication medium for the application. Your application can identify the service providers installed on a personal computer by using the EnumConnections method.

The following example shows how to enumerate the service providers:

    lpDirectPlay3A->EnumConnections(&DPCHAT_GUID, DirectPlayEnumConnectionsCallback, hWnd, 0);

The second parameter in the EnumConnections method is a callback that enumerates service providers registered with DirectPlay. The following example shows one possible way of implementing this callback function:

    BOOL FAR PASCAL DirectPlayEnumConnectionsCallback(
         LPCGUID lpguidSP, LPVOID lpConnection, DWORD dwConnectionSize,
         LPCDPNAME lpName, DWORD dwFlags, LPVOID lpContext)
    {
       HWND     hWnd = (HWND) lpContext;
       LRESULT	iIndex;
       LPVOID   lpConnectionBuffer;

       // Store service provider name in combo box
       iIndex = SendDlgItemMessage(hWnd, IDC_SPCOMBO, CB_ADDSTRING, 0,
           (LPARAM) lpName->lpszShortNameA);
       if (iIndex == CB_ERR)
         goto FAILURE;

       // make space for connection
       lpConnectionBuffer = GlobalAllocPtr(GHND, dwConnectionSize);
       if (lpConnectionBuffer == NULL)
         goto FAILURE;

       // Store pointer to connection  in combo box
       memcpy(lpConnectionBuffer, lpConnection, dwConnectionSize);
       SendDlgItemMessage(hWnd, IDC_SPCOMBO, CB_SETITEMDATA, (WPARAM) iIndex,
           (LPARAM) lpConnectionBuffer);

    FAILURE:
        return (TRUE);
    }

Once the user selects which connection to use, the DirectPlay object must be initialized with the connection buffer associated with it.

    hr = lpDirectPlay3A->InitializeConnection(lpConnection, 0);

#### Step 3: Joining a Session

If the user wants to join an existing session, enumerate the available sessions by using the IDirectPlay3::EnumSessions method, present the choices to the user, and then connect to that session by using the IDirectPlay3::Open method, specifying the DPOPEN_JOIN flag. The service provider might display a dialog box requesting some information from the user before it can enumerate the sessions.

See Tutorial 4 for details on the asynchronous EnumSessions functionality.

The following example shows how to enumerate the available sessions:

    // Search for this kind of session.
    ZeroMemory(&sessionDesc, sizeof(DPSESSIONDESC2));
    sessionDesc.dwSize = sizeof(DPSESSIONDESC2);
    sessionDesc.guidApplication = DPCHAT_GUID;

    hr = lpDirectPlay3A->EnumSessions(&sessionDesc, 0, EnumSessionsCallback,
        hWnd, DPENUMSESSIONS_AVAILABLE);
    if FAILED(hr)
        goto FAILURE;

In the previous example, the third parameter in the IDirectPlay3A::EnumSessions method is a callback that enumerates the available sessions. The following example shows one way to implement this callback function:

    BOOL FAR PASCAL EnumSessionsCallback(
        LPCDPSESSIONDESC2 lpSessionDesc, LPDWORD lpdwTimeOut,
        DWORD dwFlags, LPVOID lpContext)
    {
    HWND   hWnd = lpContext;
    LPGUID lpGuid;
    LONG   iIndex;

    // Determine if the enumeration has timed out.
    if (dwFlags & DPESC_TIMEDOUT)
        return (FALSE);            // Do not try again

    // Store the session name in the list.
    iIndex = SendDlgItemMessage(hWnd, IDC_SESSIONLIST, LB_ADDSTRING,
        (WPARAM) 0, (LPARAM) lpSessionDesc->lpszSessionNameA);
    if (iIndex == CB_ERR)
        goto FAILURE;

    // Make space for the session instance GUID.
    lpGuid = (LPGUID) GlobalAllocPtr(GHND, sizeof(GUID));
    if (lpGuid == NULL)
        goto FAILURE;

    // Store the pointer to the GUID in the list.
    *lpGuid = lpSessionDesc->guidInstance;
    SendDlgItemMessage(hWnd, IDC_SESSIONLIST, LB_SETITEMDATA,
        (WPARAM) iIndex, (LPARAM) lpGuid);

    FAILURE:
        return (TRUE);
    }

After the user has selected a session, your application can allow the user to join an existing session. The following example shows how to join an existing session:

    // Join an existing session.
    ZeroMemory(&sessionDesc, sizeof(DPSESSIONDESC2));
    sessionDesc.dwSize = sizeof(DPSESSIONDESC2);
    sessionDesc.guidInstance = *lpguidSessionInstance;

    hr = lpDirectPlay3A->Open(&sessionDesc, DPOPEN_JOIN);
    if FAILED(hr)
        goto OPEN_FAILURE;

#### Step 4: Creating a Session

If the user wants to create a new session, your application can create it by using the IDirectPlay3::Open method and specifying the DPOPEN_CREATE flag. Again, the service provider might display a dialog box requesting information from the user before it can create the session.

The following example shows how to create a new session:

    // Host a new session.
    ZeroMemory(&sessionDesc, sizeof(DPSESSIONDESC2));
    sessionDesc.dwSize = sizeof(DPSESSIONDESC2);
    sessionDesc.dwFlags = DPSESSION_MIGRATEHOST | DPSESSION_KEEPALIVE;
    sessionDesc.guidApplication = DPCHAT_GUID;
    sessionDesc.dwMaxPlayers = MAXPLAYERS;
    sessionDesc.lpszSessionNameA = lpszSessionName;

    hr = lpDirectPlay3A->Open(&sessionDesc, DPOPEN_CREATE);
    if FAILED(hr)
        goto OPEN_FAILURE;

#### Step 5: Creating a Player

After a session has been created or joined, your application can create one or more players by using the IDirectPlay3::CreatePlayer method. The following example shows one way to create a player:

    // Fill out the name structure.
    ZeroMemory(&dpName, sizeof(DPNAME));
    dpName.dwSize = sizeof(DPNAME);
    dpName.lpszShortNameA = lpszPlayerName;
    dpName.lpszLongNameA = NULL;

    // Create a player with this name.
    hr = lpDirectPlay3A->CreatePlayer(&dpidPlayer, &dpName,
        lpDPInfo->hPlayerEvent, NULL, 0, 0);
    if FAILED(hr)
        goto CREATEPLAYER_FAILURE;

Your application can determine a player's communication capabilities by using the IDirectPlay3::GetCaps and IDirectPlay3::GetPlayerCaps methods. Your application can find other players by using the IDirectPlay3::EnumPlayers method.

Now your application is connected and you are ready to play.

### Tutorial 3: Overriding the Service Provider Dialogs

DirectPlay now gives applications the ability to suppress the standard service provider dialogs. Below is a brief outline of how this is done. A code example can be found in the OVERRIDE sample application.

It is generally not possible to suppress all service provider dialogs. The standard TCP/IP, modem, and serial service provider dialogs can be suppressed (IPX has no dialog box). However, there is the possibility that third party service providers might require fairly complex information from the user which cannot be overridden in any general way. The solution is to simply allow these dialog boxes to appear over your application user interface. If the application is a DirectDraw® full-screen application, be sure to turn off page flipping before calling IDirectPlay3::EnumSessions or IDirectPlay3::Open to create a session.

Another way to suppress service provider dialog boxes is to make your application lobby-aware. Most third party service providers will also have a lobby from which to launch games, and games launched from a lobby do not display a connection dialog box.

An application first calls IDirectPlay3::EnumConnections to see which connections are available, presents the list to the user, and allows the user to select one. Once the user has selected one, the application can attempt to override the dialog box before calling IDirectPlay3::EnumSessions or IDirectPlay3::Open.

These are the steps you should follow to suppress service provider dialog boxes:

1. Examine the service provider GUID of the selected service provider to see if it matches one of the known service providers that the application knows how to override. If the service provider GUID is unknown, then skip the remaining steps and be prepared to allow a dialog box to appear.
2. Display the appropriate user interface to collect the information needed from the user for that specific service provider. IPX requires no information. TCP/IP requires an IP address for EnumSessions (DPAID_Inet or DPAID_InetW), but requires nothing to create a session using Open. Modem-to-modem requires the user to select a modem (DPAID_Modem or DPAID_ModemW) and also needs a phone number (DPAID_Phone or DPAID_PhoneW) when calling EnumSessions. Serial link needs the DPCOMPORTADDRESS structure (DPAID_ComPort) filled in to configure the COM port for both EnumSessions and Open.
3. Build a DirectPlay Address using the IDirectPlayLobby2::CreateCompoundAddress method. The address elements that must be passed in are the service provider GUID (DPAID_ServiceProvider) and the individual address components for the selected service provider.
4. Initialize the DirectPlay object by calling InitializeConnection with the DirectPlay Address.
5. Call EnumSessions with the DPENUMSESSIONS_RETURNSTATUS flag. This will prevent any status dialog boxes from appearing and, if the connection cannot be made immediately, EnumSessions will return with a DPERR_CONNECTING error. Your application must periodically call EnumSessions until DP_OK is returned (meaning the enumeration was successful) or some other error is returned (meaning it failed).
6. If a session is to be created using the Open method with DPOPEN_CREATE, specify the DPOPEN_RETURNSTATUS flag as well. Like DPENUMSESSIONS_RETURNSTATUS, this will suppress status dialog boxes and return DPERR_CONNECTING until the function is complete.

Note: In some cases, the application will need to query the service provider at run time to obtain a list of valid choices for a particular DirectPlay Address element. For example, to obtain a list of the modems installed in the system. The application must create a separate DirectPlay object, initialize the modem service provider, and then call IDirectPlay3::GetPlayerAddress with a DPID of zero to obtain a DirectPlay Address that will contain the list of modems. After releasing the DirectPlay object, the application must parse the address using IDirectPlayLobby2::EnumAddress and extract the modem list to present to the user.

### Tutorial 4: Creating Self-Refreshing Session Lists

IDirectPlay3::EnumSessions can now be called asynchronously. This gives an application the ability to maintain a self-refreshing session list. A code example can be found in the DUEL and DXVIEW sample applications.

The steps you need to follow to create a self-refreshing session list are:

1. Call IDirectPlay3::EnumSessions with the DPENUMSESSIONS_ASYNC flag and a time-out of zero (which will use the service provider default). The method will not enumerate any sessions and will return immediately. However, DirectPlay is enumerating sessions in the background.
2. Display the user interface in which all the sessions will appear. Set a timer to go off at whatever interval you want to refresh your session list. The application can find out what the default time-out interval of the enumeration is by calling IDirectPlay3::GetCaps.
3. Each time the timer goes off, call EnumSessions to obtain the current session list. This is a complete active session list with stale sessions deleted, new sessions added, and existing sessions updated. Delete all the items from the list before calling EnumSessions and add the sessions back to the list in the EnumSessionsCallback2 function.

© 1997 Microsoft Corporation. All rights reserved. Terms of Use.