/****************************************************************************
 *
 *    File: netinfo.cpp
 * Project: DxDiag (DirectX Diagnostic Tool)
 *  Author: Mike Anderson (manders@microsoft.com)
 * Purpose: Gather information about DirectPlay
 *
 * (C) Copyright 1998 Microsoft Corp.  All rights reserved.
 *
 ****************************************************************************/

#include <tchar.h>
#include <Windows.h>
#include <multimon.h>
#include <stdio.h>
#include <dplobby.h>
#include "resource.h"
#include "reginfo.h"
#include "sysinfo.h"
#include "dispinfo.h" // for TestResult
#include "fileinfo.h" // for GetFileVersion
#include "netinfo.h"

static HRESULT NewNetSP(NetInfo* pNetInfo, NetSP** ppNetSPNew);
static VOID DeleteNetSP(NetInfo* pNetInfo, NetSP* pNetSP);
static HRESULT NewNetApp(NetInfo* pNetInfo, NetApp** ppNetAppNew);
static HRESULT GetDX7ServiceProviders(NetInfo* pNetInfo);
static HRESULT GetDX8ServiceProviders(NetInfo* pNetInfo);
static HRESULT GetDX7LobbyableApps(NetInfo* pNetInfo);
static HRESULT GetDX8LobbyableApps(NetInfo* pNetInfo);
static BOOL ConvertStringToGUID(const WCHAR* strBuffer, GUID* lpguid);

/****************************************************************************
 *
 *  GetNetInfo
 *
 ****************************************************************************/
HRESULT GetNetInfo(SysInfo* pSysInfo, NetInfo** ppNetInfo)
{
    HRESULT hr = S_OK;
    NetInfo* pNetInfo;

    pNetInfo = new NetInfo;
    if (pNetInfo == NULL)
        return E_OUTOFMEMORY;
    *ppNetInfo = pNetInfo;
    ZeroMemory(pNetInfo, sizeof(NetInfo));

    if( FALSE == BIsIA64() )
    {
        if (FAILED(hr = GetDX7ServiceProviders(pNetInfo)))
            return hr;
        if (FAILED(hr = GetDX7LobbyableApps(pNetInfo)))
            return hr;
    }

    if( pSysInfo->m_dwDirectXVersionMajor >= 8 )
    {
        if (FAILED(hr = GetDX8ServiceProviders(pNetInfo)))
            return hr;
        if (FAILED(hr = GetDX8LobbyableApps(pNetInfo)))
            return hr;
    }

    return hr;
}


/****************************************************************************
 *
 *  NewNetSP
 *
 ****************************************************************************/
HRESULT NewNetSP(NetInfo* pNetInfo, NetSP** ppNetSPNew)
{
    NetSP* pNetSPNew;

    pNetSPNew = new NetSP;
    if (pNetSPNew == NULL)
        return E_OUTOFMEMORY;
    ZeroMemory(pNetSPNew, sizeof(NetSP));
    if (pNetInfo->m_pNetSPFirst == NULL)
    {
        pNetInfo->m_pNetSPFirst = pNetSPNew;
    }
    else
    {
        NetSP* pNetSP;
        for (pNetSP = pNetInfo->m_pNetSPFirst; 
            pNetSP->m_pNetSPNext != NULL; 
            pNetSP = pNetSP->m_pNetSPNext)
            {
            }
        pNetSP->m_pNetSPNext = pNetSPNew;
    }
    pNetSPNew->m_bRegistryOK = TRUE; // so far
    *ppNetSPNew = pNetSPNew;
    return S_OK;
}


/****************************************************************************
 *
 *  DeleteNetSP
 *
 ****************************************************************************/
VOID DeleteNetSP(NetInfo* pNetInfo, NetSP* pNetSP)
{
    NetSP* pNetSPPrev = NULL;
    NetSP* pNetSPCur;
    for (pNetSPCur = pNetInfo->m_pNetSPFirst; 
        pNetSPCur != NULL; 
        pNetSPCur = pNetSPCur->m_pNetSPNext)
    {
        if (pNetSPCur == pNetSP)
        {
            if (pNetSPPrev == NULL)
                pNetInfo->m_pNetSPFirst = pNetSPCur->m_pNetSPNext;
            else
                pNetSPPrev->m_pNetSPNext = pNetSPCur->m_pNetSPNext;
            delete pNetSPCur;
            return;
        }
        pNetSPPrev = pNetSPCur;
    }
}


/****************************************************************************
 *
 *  NewNetApp
 *
 ****************************************************************************/
HRESULT NewNetApp(NetInfo* pNetInfo, NetApp** ppNetAppNew)
{
    NetApp* pNetAppNew;

    pNetAppNew = new NetApp;
    if (pNetAppNew == NULL)
        return E_OUTOFMEMORY;
    ZeroMemory(pNetAppNew, sizeof(NetApp));
    if (pNetInfo->m_pNetAppFirst == NULL)
    {
        pNetInfo->m_pNetAppFirst = pNetAppNew;
    }
    else
    {
        NetApp* pNetApp;
        for (pNetApp = pNetInfo->m_pNetAppFirst; 
            pNetApp->m_pNetAppNext != NULL; 
            pNetApp = pNetApp->m_pNetAppNext)
            {
            }
        pNetApp->m_pNetAppNext = pNetAppNew;
    }
    pNetAppNew->m_bRegistryOK = TRUE; // so far
    *ppNetAppNew = pNetAppNew;
    return S_OK;
}


/****************************************************************************
 *
 *  GetDX7ServiceProviders
 *
 ****************************************************************************/
HRESULT GetDX7ServiceProviders(NetInfo* pNetInfo)
{
    HRESULT hr;
    HKEY hkey = NULL;
    HKEY hkey2 = NULL;
    DWORD dwIndex;
    DWORD dwBufferLen;
    BOOL bTCPIPFound = FALSE;
    BOOL bIPXFound = FALSE;
    BOOL bModemFound = FALSE;
    BOOL bSerialFound = FALSE;
    TCHAR szName[MAX_PATH+1];
    NetSP* pNetSPNew;

    if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\DirectPlay\\Service Providers"), 0, KEY_READ, &hkey))
    {
        dwIndex = 0;
        while (ERROR_SUCCESS == RegEnumKey(hkey, dwIndex, szName, MAX_PATH+1))
        {
            // Note: I'm not putting the following keyname strings into resources because 
            // they're supposed to be in English regardless of user's system (at least on DX6
            // and higher--see another note below).  If I put them into resources, they're 
            // more likely to get inadvertently localized.
            if (lstrcmpi(szName, TEXT("Internet TCP/IP Connection For DirectPlay")) == 0)
                bTCPIPFound = TRUE;
            else if (lstrcmpi(szName, TEXT("IPX Connection For DirectPlay")) == 0)
                bIPXFound = TRUE;
            else if (lstrcmpi(szName, TEXT("Modem Connection For DirectPlay")) == 0)
                bModemFound = TRUE;
            else if (lstrcmpi(szName, TEXT("Serial Connection For DirectPlay")) == 0)
                bSerialFound = TRUE;

            if (FAILED(hr = NewNetSP(pNetInfo, &pNetSPNew)))
            {
                RegCloseKey(hkey);
                return hr;
            }
            
            pNetSPNew->m_dwDXVer = 7;

            // The following line is the right thing to do on DX5, but in DX6 the
            // name will get overwritten by the "DescriptionW" / "DescriptionA" string.
            lstrcpy(pNetSPNew->m_szName, szName);
            lstrcpy(pNetSPNew->m_szNameEnglish, szName);

            if (ERROR_SUCCESS == RegOpenKeyEx(hkey, szName, 0, KEY_READ, &hkey2))
            {
                // Get (localized) connection name, if it exists
                TCHAR szDescription[200];
                lstrcpy(szDescription, TEXT(""));
                dwBufferLen = 200;
                RegQueryValueEx(hkey2, 
                    // 25080: Always use DescriptionW because it's more localized
                    // than DescriptionA is.
                    TEXT("DescriptionW"), 
                    0, NULL, (LPBYTE)szDescription, &dwBufferLen);

                if (lstrlen(szDescription) > 0)
                    lstrcpy(pNetSPNew->m_szName, szDescription);

                dwBufferLen = 100;
                if (ERROR_SUCCESS == RegQueryValueEx(hkey2, TEXT("Guid"), 0, NULL, (LPBYTE)pNetSPNew->m_szGuid, &dwBufferLen))
                {
                    // On DX5, the names of the registry keys for "Internet TCP/IP Connection 
                    // For DirectPlay", etc. were localized, so we need to check GUIDs to avoid
                    // incorrectly thinking that some standard service providers are missing:
                    if (!bTCPIPFound &&
                        lstrcmpi(pNetSPNew->m_szGuid, TEXT("{36E95EE0-8577-11cf-960C-0080C7534E82}")) == 0)
                    {
                        bTCPIPFound = TRUE;
                    }
                    if (!bIPXFound &&
                        lstrcmpi(pNetSPNew->m_szGuid, TEXT("{685BC400-9D2C-11cf-A9CD-00AA006886E3}")) == 0)
                    {
                        bIPXFound = TRUE;
                    }
                    if (!bModemFound &&
                        lstrcmpi(pNetSPNew->m_szGuid, TEXT("{44EAA760-CB68-11cf-9C4E-00A0C905425E}")) == 0)
                    {
                        bModemFound = TRUE;
                    }
                    if (!bSerialFound &&
                        lstrcmpi(pNetSPNew->m_szGuid, TEXT("{0F1D6860-88D9-11cf-9C4E-00A0C905425E}")) == 0)
                    {
                        bSerialFound = TRUE;
                    }

                    // If a non-English DX5 system was upgraded to DX6, it will have BOTH localized
                    // and non-localized keynames for each service provider.  The DX6 ones have
                    // "DescriptionA" and "DescriptionW" strings
                    // If a SP with this GUID has already been enumerated, replace it with the current
                    // one if the current one has a description.  Otherwise forget about the current
                    // one.
                    NetSP* pNetSPSearch;
                    BOOL bFound = FALSE;
                    for (pNetSPSearch = pNetInfo->m_pNetSPFirst; pNetSPSearch != NULL; pNetSPSearch = pNetSPSearch->m_pNetSPNext)
                    {
                        if (pNetSPSearch == pNetSPNew)
                            continue;
                        if (lstrcmp(pNetSPSearch->m_szGuid, pNetSPNew->m_szGuid) == 0)
                        {
                            bFound = TRUE;
                            break;
                        }
                    }
                    if (bFound)
                    {
                        if (lstrlen(szDescription) > 0)
                        {
                            // Current SP is better, nuke old one
                            DeleteNetSP(pNetInfo, pNetSPSearch);
                        }
                        else
                        {
                            // Old SP is (probably) better, nuke current one
                            DeleteNetSP(pNetInfo, pNetSPNew);
                            goto LDoneWithSubKey;
                        }
                    }
                }
                else
                {
                    pNetSPNew->m_bRegistryOK = FALSE;
                }
                
                TCHAR szPath[MAX_PATH];
                TCHAR* pszFile;
                dwBufferLen = MAX_PATH;
                if (ERROR_SUCCESS == RegQueryValueEx(hkey2, TEXT("Path"), 0, NULL, (LPBYTE)szPath, &dwBufferLen))
                {
                    // On Win9x, szPath is full path.  On NT, it's leaf only.
                    pszFile = _tcsrchr(szPath, TEXT('\\'));
                    if (pszFile == NULL)
                    {
                        lstrcpy(pNetSPNew->m_szFile, szPath);
                        GetSystemDirectory(pNetSPNew->m_szPath, MAX_PATH);
                        lstrcat(pNetSPNew->m_szPath, TEXT("\\"));
                        lstrcat(pNetSPNew->m_szPath, szPath);
                    }
                    else
                    {
                        lstrcpy(pNetSPNew->m_szPath, szPath);
                        lstrcpy(pNetSPNew->m_szFile, pszFile + 1); // skip backslash
                    }

                    WIN32_FIND_DATA findFileData;
                    HANDLE hFind = FindFirstFile(pNetSPNew->m_szPath, &findFileData);
                    if (hFind == INVALID_HANDLE_VALUE)
                    {
                        pNetSPNew->m_bFileMissing = TRUE;
                        LoadString(NULL, IDS_FILEMISSING, pNetSPNew->m_szVersion, 50);
                        LoadString(NULL, IDS_FILEMISSING_ENGLISH, pNetSPNew->m_szVersionEnglish, 50);
                    }
                    else
                    {
                        FindClose(hFind);
                        GetFileVersion(pNetSPNew->m_szPath, pNetSPNew->m_szVersion, 
                            NULL, NULL, NULL);
                        GetFileVersion(pNetSPNew->m_szPath, pNetSPNew->m_szVersionEnglish, 
                            NULL, NULL, NULL);
                    }
                }
                else
                {
                    pNetSPNew->m_bRegistryOK = FALSE;
                }
LDoneWithSubKey:                
                RegCloseKey(hkey2);
            }
            else
            {
                pNetSPNew->m_bRegistryOK = FALSE;
            }
            dwIndex++;
        }
        
        RegCloseKey(hkey);
    }

    if (!bTCPIPFound)
    {
        if (FAILED(hr = NewNetSP(pNetInfo, &pNetSPNew)))
            return hr;
        lstrcpy(pNetSPNew->m_szName, TEXT("Internet TCP/IP Connection For DirectPlay"));
        lstrcpy(pNetSPNew->m_szNameEnglish, TEXT("Internet TCP/IP Connection For DirectPlay"));
        pNetSPNew->m_bRegistryOK = FALSE;
    }
    if (!bIPXFound)
    {
        if (FAILED(hr = NewNetSP(pNetInfo, &pNetSPNew)))
            return hr;
        lstrcpy(pNetSPNew->m_szName, TEXT("IPX Connection For DirectPlay"));
        lstrcpy(pNetSPNew->m_szNameEnglish, TEXT("IPX Connection For DirectPlay"));
        pNetSPNew->m_bRegistryOK = FALSE;
    }
    if (!bModemFound)
    {
        if (FAILED(hr = NewNetSP(pNetInfo, &pNetSPNew)))
            return hr;
        lstrcpy(pNetSPNew->m_szName, TEXT("Modem Connection For DirectPlay"));
        lstrcpy(pNetSPNew->m_szNameEnglish, TEXT("Modem Connection For DirectPlay"));
        pNetSPNew->m_bRegistryOK = FALSE;
    }
    if (!bSerialFound)
    {
        if (FAILED(hr = NewNetSP(pNetInfo, &pNetSPNew)))
            return hr;
        lstrcpy(pNetSPNew->m_szName, TEXT("Serial Connection For DirectPlay"));
        lstrcpy(pNetSPNew->m_szNameEnglish, TEXT("Serial Connection For DirectPlay"));
        pNetSPNew->m_bRegistryOK = FALSE;
    }

    return S_OK;
}


/****************************************************************************
 *
 *  GetDX8ServiceProviders
 *
 ****************************************************************************/
HRESULT GetDX8ServiceProviders(NetInfo* pNetInfo)
{
    HRESULT hr;
    HKEY hkey = NULL;
    HKEY hkey2 = NULL;
    HKEY hkeyDLL = NULL;
    DWORD dwIndex;
    DWORD dwBufferLen;
    BOOL bTCPIPFound = FALSE;
    BOOL bIPXFound = FALSE;
    BOOL bModemFound = FALSE;
    BOOL bSerialFound = FALSE;
    TCHAR szName[MAX_PATH+1];
    NetSP* pNetSPNew;

    if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\DirectPlay8\\Service Providers"), 0, KEY_READ, &hkey))
    {
        dwIndex = 0;
        while (ERROR_SUCCESS == RegEnumKey(hkey, dwIndex, szName, MAX_PATH+1))
        {
            if (FAILED(hr = NewNetSP(pNetInfo, &pNetSPNew)))
            {
                RegCloseKey(hkey);
                return hr;
            }
            
            pNetSPNew->m_dwDXVer = 8;

            if (ERROR_SUCCESS == RegOpenKeyEx(hkey, szName, 0, KEY_READ, &hkey2))
            {
                TCHAR szDescription[200];
                lstrcpy(szDescription, TEXT(""));
                TCHAR szEnglishDescription[200];

                dwBufferLen = 200;
                if (ERROR_SUCCESS == RegQueryValueEx( hkey2, TEXT("Friendly Name"), 0, NULL, (LPBYTE)szDescription, &dwBufferLen) )
                {
                }
                else
                {
                    pNetSPNew->m_bRegistryOK = FALSE;
                }

                lstrcpy(szEnglishDescription, szDescription);

                dwBufferLen = 100;
                if (ERROR_SUCCESS == RegQueryValueEx(hkey2, TEXT("GUID"), 0, NULL, (LPBYTE)pNetSPNew->m_szGuid, &dwBufferLen))
                {
                    WCHAR strBuffer[MAX_PATH];
#ifdef _UNICODE
                    wcscpy( strBuffer, pNetSPNew->m_szGuid );
#else
                    MultiByteToWideChar( CP_ACP, 0, pNetSPNew->m_szGuid, -1, 
                                         strBuffer, _tcslen(pNetSPNew->m_szGuid) );
#endif
                    ConvertStringToGUID( strBuffer, &pNetSPNew->m_guid );
                }
                else
                {
                    pNetSPNew->m_bRegistryOK = FALSE;
                }

                TCHAR szRegKey[200];
                wsprintf( szRegKey, TEXT("CLSID\\%s\\InprocServer32"), pNetSPNew->m_szGuid );
                if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, szRegKey, 0, KEY_READ, &hkeyDLL))
                {
                    if (ERROR_SUCCESS == RegQueryValueEx(hkeyDLL, NULL, 0, NULL, (LPBYTE)pNetSPNew->m_szFile, &dwBufferLen))
                    {
                        GetSystemDirectory(pNetSPNew->m_szPath, MAX_PATH);
                        lstrcat(pNetSPNew->m_szPath, TEXT("\\"));
                        lstrcat(pNetSPNew->m_szPath, pNetSPNew->m_szFile);

                        WIN32_FIND_DATA findFileData;
                        HANDLE hFind = FindFirstFile(pNetSPNew->m_szPath, &findFileData);
                        if (hFind == INVALID_HANDLE_VALUE)
                        {
                            pNetSPNew->m_bFileMissing = TRUE;
                            LoadString(NULL, IDS_FILEMISSING, pNetSPNew->m_szVersion, 50);
                            LoadString(NULL, IDS_FILEMISSING_ENGLISH, pNetSPNew->m_szVersionEnglish, 50);
                        }
                        else
                        {
                            FindClose(hFind);
                            GetFileVersion(pNetSPNew->m_szPath, pNetSPNew->m_szVersion, 
                                NULL, NULL, NULL);
                            GetFileVersion(pNetSPNew->m_szPath, pNetSPNew->m_szVersionEnglish, 
                                NULL, NULL, NULL);
                        }
                    }
                    else
                    {
                        pNetSPNew->m_bRegistryOK = FALSE;
                    }

                    RegCloseKey(hkeyDLL);
                }
                else
                {
                    pNetSPNew->m_bRegistryOK = FALSE;
                }

                
                if (!bTCPIPFound &&
                    lstrcmpi(pNetSPNew->m_szGuid, TEXT("{EBFE7BA0-628D-11D2-AE0F-006097B01411}")) == 0)
                {
                    if( lstrcmpi(pNetSPNew->m_szFile, TEXT("dpnwsock.dll") ) != 0)
                        pNetSPNew->m_bRegistryOK = FALSE;

                    lstrcpy( szEnglishDescription, TEXT("DirectPlay8 TCP/IP Service Provider") );
                    bTCPIPFound = TRUE;
                }

                if (!bIPXFound &&
                    lstrcmpi(pNetSPNew->m_szGuid, TEXT("{53934290-628D-11D2-AE0F-006097B01411}")) == 0)
                {
                    if( lstrcmpi(pNetSPNew->m_szFile, TEXT("dpnwsock.dll") ) != 0)
                        pNetSPNew->m_bRegistryOK = FALSE;

                    lstrcpy( szEnglishDescription, TEXT("DirectPlay8 IPX Service Provider") );
                    bIPXFound = TRUE;
                }
                if (!bModemFound &&
                    lstrcmpi(pNetSPNew->m_szGuid, TEXT("{6D4A3650-628D-11D2-AE0F-006097B01411}")) == 0)
                {
                    if( lstrcmpi(pNetSPNew->m_szFile, TEXT("dpnmodem.dll") ) != 0)
                        pNetSPNew->m_bRegistryOK = FALSE;

                    lstrcpy( szEnglishDescription, TEXT("DirectPlay8 Modem Service Provider") );
                    bModemFound = TRUE;
                }
                if (!bSerialFound &&
                    lstrcmpi(pNetSPNew->m_szGuid, TEXT("{743B5D60-628D-11D2-AE0F-006097B01411}")) == 0)
                {
                    if( lstrcmpi(pNetSPNew->m_szFile, TEXT("dpnmodem.dll") ) != 0)
                        pNetSPNew->m_bRegistryOK = FALSE;

                    lstrcpy( szEnglishDescription, TEXT("DirectPlay8 Serial Service Provider") );
                    bSerialFound = TRUE;
                }

                lstrcpy(pNetSPNew->m_szName, szDescription);
                lstrcpy(pNetSPNew->m_szNameEnglish, szEnglishDescription);

                RegCloseKey(hkey2);
            }
            else
            {
                pNetSPNew->m_bRegistryOK = FALSE;
            }
            dwIndex++;
        }
        
        RegCloseKey(hkey);
    }

    if (!bTCPIPFound)
    {
        if (FAILED(hr = NewNetSP(pNetInfo, &pNetSPNew)))
            return hr;
        lstrcpy(pNetSPNew->m_szName, TEXT("DirectPlay8 TCP/IP Service Provider"));
        lstrcpy(pNetSPNew->m_szNameEnglish, TEXT("DirectPlay8 TCP/IP Service Provider"));
        pNetSPNew->m_bRegistryOK = FALSE;
    }
    if (!bIPXFound)
    {
        if (FAILED(hr = NewNetSP(pNetInfo, &pNetSPNew)))
            return hr;
        lstrcpy(pNetSPNew->m_szName, TEXT("DirectPlay8 IPX Service Provider"));
        lstrcpy(pNetSPNew->m_szNameEnglish, TEXT("DirectPlay8 IPX Service Provider"));
        pNetSPNew->m_bRegistryOK = FALSE;
    }
    if (!bModemFound)
    {
        if (FAILED(hr = NewNetSP(pNetInfo, &pNetSPNew)))
            return hr;
        lstrcpy(pNetSPNew->m_szName, TEXT("DirectPlay8 Modem Service Provider"));
        lstrcpy(pNetSPNew->m_szNameEnglish, TEXT("DirectPlay8 Modem Service Provider"));
        pNetSPNew->m_bRegistryOK = FALSE;
    }
    if (!bSerialFound)
    {
        if (FAILED(hr = NewNetSP(pNetInfo, &pNetSPNew)))
            return hr;
        lstrcpy(pNetSPNew->m_szName, TEXT("DirectPlay8 Serial Service Provider"));
        lstrcpy(pNetSPNew->m_szNameEnglish, TEXT("DirectPlay8 Serial Service Provider"));
        pNetSPNew->m_bRegistryOK = FALSE;
    }

    return S_OK;
}


/****************************************************************************
 *
 *  GetDX7LobbyableApps
 *
 ****************************************************************************/
HRESULT GetDX7LobbyableApps(NetInfo* pNetInfo)
{
    HRESULT hr;
    HKEY hkey = NULL;
    HKEY hkey2 = NULL;
    DWORD dwIndex;
    DWORD dwBufferLen;
    TCHAR szName[MAX_PATH+1];
    NetApp* pNetAppNew;

    if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\DirectPlay\\Applications"), 0, KEY_READ, &hkey))
    {
        dwIndex = 0;
        while (ERROR_SUCCESS == RegEnumKey(hkey, dwIndex, szName, MAX_PATH+1))
        {
            BOOL bSkip = FALSE;

            // Bug 37989: skip any dplay app that has the DPLAPP_NOENUM flag set.
            if (ERROR_SUCCESS == RegOpenKeyEx(hkey, szName, 0, KEY_READ, &hkey2))
            {
                dwBufferLen = MAX_PATH;
                DWORD dwFlags = 0;
                dwBufferLen = sizeof(DWORD);
                DWORD dwType = 0;
                RegQueryValueEx(hkey2, TEXT("dwFlags"), 0, &dwType, (LPBYTE)&dwFlags, &dwBufferLen);
                if( (dwFlags & DPLAPP_NOENUM) != 0 )
                    bSkip = TRUE;
                RegCloseKey(hkey2);
            }

            if( bSkip )
            {
                dwIndex++;
                continue;
            }

            if (FAILED(hr = NewNetApp(pNetInfo, &pNetAppNew)))
            {
                RegCloseKey(hkey);
                return hr;
            }
            lstrcpy(pNetAppNew->m_szName, szName);
            pNetAppNew->m_dwDXVer = 7;
            
            if (ERROR_SUCCESS == RegOpenKeyEx(hkey, szName, 0, KEY_READ, &hkey2))
            {
                dwBufferLen = MAX_PATH;
                if (ERROR_SUCCESS == RegQueryValueEx(hkey2, TEXT("Path"), 0, NULL, (LPBYTE)pNetAppNew->m_szExePath, &dwBufferLen))
                {
                }
                else
                {
                    pNetAppNew->m_bRegistryOK = FALSE;
                }
                dwBufferLen = 100;
                if (ERROR_SUCCESS == RegQueryValueEx(hkey2, TEXT("File"), 0, NULL, (LPBYTE)pNetAppNew->m_szExeFile, &dwBufferLen))
                {
                    lstrcat(pNetAppNew->m_szExePath, TEXT("\\"));
                    lstrcat(pNetAppNew->m_szExePath, pNetAppNew->m_szExeFile);

                    WIN32_FIND_DATA findFileData;
                    HANDLE hFind = FindFirstFile(pNetAppNew->m_szExePath, &findFileData);
                    if (hFind == INVALID_HANDLE_VALUE)
                    {
                        pNetAppNew->m_bFileMissing = TRUE;
                        LoadString(NULL, IDS_FILEMISSING, pNetAppNew->m_szExeVersion, 50);
                        LoadString(NULL, IDS_FILEMISSING, pNetAppNew->m_szExeVersionEnglish, 50);
                    }
                    else
                    {
                        FindClose(hFind);
                        GetFileVersion(pNetAppNew->m_szExePath, pNetAppNew->m_szExeVersion, 
                            NULL, NULL, NULL);
                        GetFileVersion(pNetAppNew->m_szExePath, pNetAppNew->m_szExeVersionEnglish, 
                            NULL, NULL, NULL);
                    }
                }
                else
                {
                    pNetAppNew->m_bRegistryOK = FALSE;
                }
                dwBufferLen = 100;
                if (ERROR_SUCCESS == RegQueryValueEx(hkey2, TEXT("Guid"), 0, NULL, (LPBYTE)pNetAppNew->m_szGuid, &dwBufferLen))
                {
                }
                else
                {
                    pNetAppNew->m_bRegistryOK = FALSE;
                }

                RegCloseKey(hkey2);
            }
            else
            {
                pNetAppNew->m_bRegistryOK = FALSE;
            }
            dwIndex++;
        }
        
        RegCloseKey(hkey);
    }

    return S_OK;
}


/****************************************************************************
 *
 *  GetDX8LobbyableApps
 *
 ****************************************************************************/
HRESULT GetDX8LobbyableApps(NetInfo* pNetInfo)
{
    HRESULT hr;
    HKEY hkey = NULL;
    HKEY hkey2 = NULL;
    DWORD dwIndex;
    DWORD dwBufferLen;
    TCHAR szGuid[MAX_PATH+1];
    NetApp* pNetAppNew;

    if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\DirectPlay8\\Applications"), 0, KEY_READ, &hkey))
    {
        dwIndex = 0;
        while (ERROR_SUCCESS == RegEnumKey(hkey, dwIndex, szGuid, MAX_PATH+1))
        {
            if (FAILED(hr = NewNetApp(pNetInfo, &pNetAppNew)))
            {
                RegCloseKey(hkey);
                return hr;
            }
            lstrcpy(pNetAppNew->m_szGuid, szGuid);
            pNetAppNew->m_dwDXVer = 8;
            
            if (ERROR_SUCCESS == RegOpenKeyEx(hkey, szGuid, 0, KEY_READ, &hkey2))
            {
                dwBufferLen = MAX_PATH;
                if (ERROR_SUCCESS == RegQueryValueEx(hkey2, TEXT("ExecutablePath"), 0, NULL, (LPBYTE)pNetAppNew->m_szExePath, &dwBufferLen))
                {
                }
                else
                {
                    pNetAppNew->m_bRegistryOK = FALSE;
                }
                dwBufferLen = 100;
                if (ERROR_SUCCESS == RegQueryValueEx(hkey2, TEXT("ExecutableFilename"), 0, NULL, (LPBYTE)pNetAppNew->m_szExeFile, &dwBufferLen))
                {
                    lstrcat(pNetAppNew->m_szExePath, TEXT("\\"));
                    lstrcat(pNetAppNew->m_szExePath, pNetAppNew->m_szExeFile);

                    WIN32_FIND_DATA findFileData;
                    HANDLE hFind = FindFirstFile(pNetAppNew->m_szExePath, &findFileData);
                    if (hFind == INVALID_HANDLE_VALUE)
                    {
                        pNetAppNew->m_bFileMissing = TRUE;
                        LoadString(NULL, IDS_FILEMISSING, pNetAppNew->m_szExeVersion, 50);
                        LoadString(NULL, IDS_FILEMISSING, pNetAppNew->m_szExeVersionEnglish, 50);
                    }
                    else
                    {
                        FindClose(hFind);
                        GetFileVersion(pNetAppNew->m_szExePath, pNetAppNew->m_szExeVersion, 
                            NULL, NULL, NULL);
                        GetFileVersion(pNetAppNew->m_szExePath, pNetAppNew->m_szExeVersionEnglish, 
                            NULL, NULL, NULL);
                    }
                }
                else
                {
                    pNetAppNew->m_bRegistryOK = FALSE;
                }


                dwBufferLen = MAX_PATH;
                if (ERROR_SUCCESS == RegQueryValueEx(hkey2, TEXT("LauncherPath"), 0, NULL, (LPBYTE)pNetAppNew->m_szLauncherPath, &dwBufferLen))
                {
                }
                else
                {
                    pNetAppNew->m_bRegistryOK = FALSE;
                }
                dwBufferLen = 100;
                if (ERROR_SUCCESS == RegQueryValueEx(hkey2, TEXT("LauncherFilename"), 0, NULL, (LPBYTE)pNetAppNew->m_szLauncherFile, &dwBufferLen))
                {
                    lstrcat(pNetAppNew->m_szLauncherPath, TEXT("\\"));
                    lstrcat(pNetAppNew->m_szLauncherPath, pNetAppNew->m_szLauncherFile);

                    WIN32_FIND_DATA findFileData;
                    HANDLE hFind = FindFirstFile(pNetAppNew->m_szLauncherPath, &findFileData);
                    if (hFind == INVALID_HANDLE_VALUE)
                    {
                        pNetAppNew->m_bFileMissing = TRUE;
                        LoadString(NULL, IDS_FILEMISSING, pNetAppNew->m_szLauncherVersion, 50);
                        LoadString(NULL, IDS_FILEMISSING, pNetAppNew->m_szLauncherVersionEnglish, 50);
                    }
                    else
                    {
                        FindClose(hFind);
                        GetFileVersion(pNetAppNew->m_szExePath, pNetAppNew->m_szLauncherVersion, 
                            NULL, NULL, NULL);
                        GetFileVersion(pNetAppNew->m_szExePath, pNetAppNew->m_szLauncherVersionEnglish, 
                            NULL, NULL, NULL);
                    }
                }
                else
                {
                    pNetAppNew->m_bRegistryOK = FALSE;
                }


                dwBufferLen = 100;
                if (ERROR_SUCCESS == RegQueryValueEx(hkey2, TEXT("ApplicationName"), 0, NULL, (LPBYTE)pNetAppNew->m_szName, &dwBufferLen))
                {
                }
                else
                {
                    pNetAppNew->m_bRegistryOK = FALSE;
                }

                RegCloseKey(hkey2);
            }
            else
            {
                pNetAppNew->m_bRegistryOK = FALSE;
            }
            dwIndex++;
        }
        
        RegCloseKey(hkey);
    }

    return S_OK;
}


/****************************************************************************
 *
 *  DestroyNetInfo
 *
 ****************************************************************************/
VOID DestroyNetInfo(NetInfo* pNetInfo)
{
    if( pNetInfo )
    {
        NetSP* pNetSP;
        NetSP* pNetSPNext;

        for (pNetSP = pNetInfo->m_pNetSPFirst; pNetSP != NULL; 
            pNetSP = pNetSPNext)
        {
            pNetSPNext = pNetSP->m_pNetSPNext;
            delete pNetSP;
        }

        NetApp* pNetApp;
        NetApp* pNetAppNext;

        for (pNetApp = pNetInfo->m_pNetAppFirst; pNetApp != NULL; 
            pNetApp = pNetAppNext)
        {
            pNetAppNext = pNetApp->m_pNetAppNext;
            delete pNetApp;
        }

        delete pNetInfo;
    }
}


/****************************************************************************
 *
 *  DiagnoseNetInfo
 *
 ****************************************************************************/
VOID DiagnoseNetInfo(SysInfo* pSysInfo, NetInfo* pNetInfo)
{
    NetSP* pNetSP;
    NetApp* pNetApp;
    TCHAR szMessage[500];
    TCHAR szFmt[500];
    BOOL bProblem = FALSE;
    BOOL bShouldReinstall = FALSE;

    _tcscpy( pSysInfo->m_szNetworkNotes, TEXT("") );
    _tcscpy( pSysInfo->m_szNetworkNotesEnglish, TEXT("") );

    // Report any problems
    if( pNetInfo != NULL )
    {
        for (pNetSP = pNetInfo->m_pNetSPFirst; pNetSP != NULL; pNetSP = pNetSP->m_pNetSPNext)
        {
            if (!pNetSP->m_bRegistryOK)
            {
                LoadString(NULL, IDS_SPREGISTRYERRORFMT, szFmt, 500);
                wsprintf(szMessage, szFmt, pNetSP->m_szName);
                _tcscat(pSysInfo->m_szNetworkNotes, szMessage);

                LoadString(NULL, IDS_SPREGISTRYERRORFMT_ENGLISH, szFmt, 500);
                wsprintf(szMessage, szFmt, pNetSP->m_szName);
                _tcscat(pSysInfo->m_szNetworkNotesEnglish, szMessage);

                pNetSP->m_bProblem = TRUE;
                bProblem = TRUE;
                bShouldReinstall = TRUE;
            }
            else if (pNetSP->m_bFileMissing)
            {
                LoadString(NULL, IDS_FILEMISSING, pNetSP->m_szVersion, 50);
                LoadString(NULL, IDS_SPFILEMISSINGFMT, szFmt, 500);
                wsprintf(szMessage, szFmt, pNetSP->m_szFile, pNetSP->m_szName);
                _tcscat(pSysInfo->m_szNetworkNotes, szMessage);

                LoadString(NULL, IDS_FILEMISSING_ENGLISH, pNetSP->m_szVersion, 50);
                LoadString(NULL, IDS_SPFILEMISSINGFMT_ENGLISH, szFmt, 500);
                wsprintf(szMessage, szFmt, pNetSP->m_szFile, pNetSP->m_szName);
                _tcscat(pSysInfo->m_szNetworkNotesEnglish, szMessage);

                pNetSP->m_bProblem = TRUE;
                bShouldReinstall = TRUE;
                bProblem = TRUE;
            }
        }
        for (pNetApp = pNetInfo->m_pNetAppFirst; pNetApp != NULL; pNetApp = pNetApp->m_pNetAppNext)
        {
            if (!pNetApp->m_bRegistryOK)
            {
                LoadString(NULL, IDS_APPREGISTRYERRORFMT, szFmt, 500);
                wsprintf(szMessage, szFmt, pNetApp->m_szName);
                _tcscat(pSysInfo->m_szNetworkNotes, szMessage);

                LoadString(NULL, IDS_APPREGISTRYERRORFMT_ENGLISH, szFmt, 500);
                wsprintf(szMessage, szFmt, pNetApp->m_szName);
                _tcscat(pSysInfo->m_szNetworkNotesEnglish, szMessage);

                pNetApp->m_bProblem = TRUE;
                bProblem = TRUE;
            }
    /* 26298: Don't scare users with this warning...it's usually harmless:
            else if (pNetApp->m_bFileMissing)
            {
                LoadString(NULL, IDS_FILEMISSING, pNetApp->m_szVersion, 50);
                LoadString(NULL, IDS_APPFILEMISSINGFMT, szFmt, 500);
                wsprintf(szMessage, szFmt, pNetApp->m_szFile, pNetApp->m_szName);
                _tcscat(pSysInfo->m_szNetworkNotes, szMessage);
                pNetApp->m_bProblem = TRUE;
                bProblem = TRUE;
            }
    */
        }
    }
    else
    {
        bProblem = TRUE;
        bShouldReinstall = TRUE;
    }

    if( bShouldReinstall )
    {
        BOOL bTellUser = FALSE;

        // Figure out if the user can install DirectX
        if( BIsPlatform9x() )
            bTellUser = TRUE;
        else if( BIsWin2k() && pSysInfo->m_dwDirectXVersionMajor >= 8 )
            bTellUser = TRUE;

        if( bTellUser )
        {
            LoadString(NULL, IDS_REINSTALL_DX, szMessage, 500);
            _tcscat( pSysInfo->m_szNetworkNotes, szMessage);

            LoadString(NULL, IDS_REINSTALL_DX_ENGLISH, szMessage, 500);
            _tcscat( pSysInfo->m_szNetworkNotesEnglish, szMessage);
        }
    }

    if (!bProblem)
    {
        LoadString(NULL, IDS_NOPROBLEM, szMessage, 500);
        _tcscat(pSysInfo->m_szNetworkNotes, szMessage);

        LoadString(NULL, IDS_NOPROBLEM_ENGLISH, szMessage, 500);
        _tcscat(pSysInfo->m_szNetworkNotesEnglish, szMessage);
    }

    // Show test results or instructions to run test:
    if (pNetInfo && pNetInfo->m_testResult.m_bStarted)
    {
        LoadString(NULL, IDS_DPLAYRESULTS, szMessage, 500);
        _tcscat( pSysInfo->m_szNetworkNotes, szMessage );
        _tcscat( pSysInfo->m_szNetworkNotes, pNetInfo->m_testResult.m_szDescription );
        _tcscat( pSysInfo->m_szNetworkNotes, TEXT("\r\n") );

        LoadString(NULL, IDS_DPLAYRESULTS_ENGLISH, szMessage, 500);
        _tcscat( pSysInfo->m_szNetworkNotesEnglish, szMessage );
        _tcscat( pSysInfo->m_szNetworkNotesEnglish, pNetInfo->m_testResult.m_szDescriptionEnglish );
        _tcscat( pSysInfo->m_szNetworkNotesEnglish, TEXT("\r\n") );
    }
    else
    {
        LoadString(NULL, IDS_DPLAYINSTRUCTIONS, szMessage, 500);
        _tcscat(pSysInfo->m_szNetworkNotes, szMessage);

        LoadString(NULL, IDS_DPLAYINSTRUCTIONS_ENGLISH, szMessage, 500);
        _tcscat(pSysInfo->m_szNetworkNotesEnglish, szMessage);
    }
}


/****************************************************************************
 *
 *  ConvertStringToGUID
 *
 ****************************************************************************/
BOOL ConvertStringToGUID(const WCHAR* strBuffer, GUID* lpguid)
{
    UINT aiTmp[10];

    if( swscanf( strBuffer, L"{%8X-%4X-%4X-%2X%2X-%2X%2X%2X%2X%2X%2X}",
                    &lpguid->Data1, 
                    &aiTmp[0], &aiTmp[1], 
                    &aiTmp[2], &aiTmp[3],
                    &aiTmp[4], &aiTmp[5],
                    &aiTmp[6], &aiTmp[7],
                    &aiTmp[8], &aiTmp[9] ) != 11 )
    {
    	ZeroMemory(lpguid, sizeof(GUID));
        return FALSE;
    }
    else
    {
        lpguid->Data2       = (USHORT) aiTmp[0];
        lpguid->Data3       = (USHORT) aiTmp[1];
        lpguid->Data4[0]    = (BYTE) aiTmp[2];
        lpguid->Data4[1]    = (BYTE) aiTmp[3];
        lpguid->Data4[2]    = (BYTE) aiTmp[4];
        lpguid->Data4[3]    = (BYTE) aiTmp[5];
        lpguid->Data4[4]    = (BYTE) aiTmp[6];
        lpguid->Data4[5]    = (BYTE) aiTmp[7];
        lpguid->Data4[6]    = (BYTE) aiTmp[8];
        lpguid->Data4[7]    = (BYTE) aiTmp[9];
        return TRUE;
    }
}