#include "mslocusr.h"
#include "msluglob.h"
#include "resource.h"

/* InstallLogonDialog - check if there is a primary logon provider already on
 * the system, and if not, install MSLOCUSR as a net provider and make it the
 * primary logon.  Returns TRUE if the NP was installed.
 *
 * This chunk of hideous registry code exists because NETDI.DLL (the win95
 * network setup engine) (a) has no programmatic interface, it just assumes
 * it's being driven by NETCPL.CPL;  (b) is 16-bit code, so even if it had
 * a programmatic interface, we'd have to thunk;  and (c) if everything's
 * not consistent in his database of what network components are installed
 * and which are bound to which, then the next time the user brings up the
 * network CPL, any components which don't make sense just get silently
 * deinstalled.
 *
 * The set of registry keys and values which need to be added, changed, or
 * updated was gleaned from a registry diff done after using the real network
 * CPL to install this logon provider from an INF.  A similar registry diff
 * and similar code could be created to programmatically install a transport.
 * Don't ask me to do it for you, though...
 *
 * Note that in case of registry errors, we just bail out.  It would require
 * a huge amount of extra code to keep track of everything that had been done
 * up to that point and undo it.  The worst that happens if we do strange
 * things to the net component database is that NETDI will deinstall our
 * component the next time the user brings up the network control panel.  It
 * shouldn't actually cause any crashes or anything like that.
 */

BOOL InstallLogonDialog(void)
{
    HKEY hkey;          /* used for various work */
    LONG err;
    TCHAR szBuf[MAX_PATH];
    DWORD dwType;
    DWORD cbData;
    DWORD dwTemp;
    DWORD dwDisp;

    NLS_STR nlsNPName(MAX_RES_STR_LEN);
    if (nlsNPName.LoadString(IDS_NP_NAME) != ERROR_SUCCESS)
        return FALSE;

    err = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Network\\Logon", 0,
                       KEY_QUERY_VALUE | KEY_SET_VALUE, &hkey);
    if (err != ERROR_SUCCESS)
        return FALSE;           /* big problems if we can't get this guy */

    /* Get the PrimaryProvider value, which is the name of the net provider
     * that's handling the main logon dialog.  If it's there and not blank,
     * then presumably the user's on a LAN or something, so we don't want
     * to replace the logon dialog.
     */
    cbData = sizeof(szBuf);
    err = RegQueryValueEx(hkey, "PrimaryProvider", NULL, &dwType,
                          (LPBYTE)szBuf, &cbData);
    if (err == ERROR_SUCCESS && szBuf[0] != '\0') {
        RegCloseKey(hkey);
        return FALSE;
    }

    /* Make us the primary logon provider, as far as MPR and the logon code
     * are concerned.
     */
    err = RegSetValueEx(hkey, "PrimaryProvider", 0, REG_SZ,
                        (LPBYTE)nlsNPName.QueryPch(), nlsNPName.strlen()+1);
    RegCloseKey(hkey);
    if (err != ERROR_SUCCESS)
        return FALSE;

    /* Under HKLM\SW\MS\W\CV\Network\Real Mode Net, preferredredir=null string,
     * since we will now be the primary network in all respects.  NETDI needs
     * this to avoid getting confused.
     */
    err = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\Network\\Real Mode Net",
                       0, KEY_QUERY_VALUE, &hkey);
    if (err == ERROR_SUCCESS) {
        err = RegSetValueEx(hkey, "preferredredir", 0, REG_SZ, (LPBYTE)TEXT(""), sizeof(TCHAR));
        RegCloseKey(hkey);
    }

    /* Add new keys under HKLM\System\CurrentControlSet which will actually
     * make MPR load our DLL as a net provider.
     */
    HKEY hkeyFamilyClient;
    err = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\NPSTUB\\NetworkProvider", 
                         0, "", REG_OPTION_NON_VOLATILE,
                         KEY_SET_VALUE,
                         NULL, &hkeyFamilyClient, &dwDisp);
    if (err == ERROR_SUCCESS) {
        RegSetValueEx(hkeyFamilyClient, "Name", 0, REG_SZ,
                      (LPBYTE)nlsNPName.QueryPch(), nlsNPName.strlen()+1);
        RegSetValueEx(hkeyFamilyClient, "ProviderPath", 0, REG_SZ,
                      (LPBYTE)TEXT("ienpstub.dll"), 11 * sizeof(TCHAR));
        RegSetValueEx(hkeyFamilyClient, "RealDLL", 0, REG_SZ,
                      (LPBYTE)TEXT("mslocusr.dll"), 13 * sizeof(TCHAR));
        RegSetValueEx(hkeyFamilyClient, "Description", 0, REG_SZ,
                      (LPBYTE)nlsNPName.QueryPch(), nlsNPName.strlen()+1);

        dwTemp = WNNC_NET_MSNET;
        RegSetValueEx(hkeyFamilyClient, "NetID", 0, REG_DWORD,
                      (LPBYTE)&dwTemp, sizeof(dwTemp));
        dwTemp = 0x40000000;
        RegSetValueEx(hkeyFamilyClient, "CallOrder", 0, REG_DWORD,
                      (LPBYTE)&dwTemp, sizeof(dwTemp));

        RegCloseKey(hkeyFamilyClient);
    }

    err = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Control\\NetworkProvider\\Order", 
                         0, "", REG_OPTION_NON_VOLATILE,
                         KEY_SET_VALUE,
                         NULL, &hkeyFamilyClient, &dwDisp);
    if (err == ERROR_SUCCESS) {
        cbData = sizeof(szBuf);
        if (RegQueryValueEx(hkeyFamilyClient, "NPSTUB", NULL, &dwType, 
                            (LPBYTE)szBuf, &cbData) == ERROR_SUCCESS) {
            /* Our provider is already installed!  Better not do anything
             * more than just making it default, which we've already done.
             */
            RegCloseKey(hkeyFamilyClient);
            return FALSE;
        }
        RegSetValueEx(hkeyFamilyClient, "NPSTUB", 0, REG_SZ,
                      (LPBYTE)TEXT(""), sizeof(TCHAR));
        RegCloseKey(hkeyFamilyClient);
    }

    /* We've now installed our NP in the registry, and to see it appear we
     * need a reboot.  So from here on, if we bail out, we return TRUE.
     */

    /* First big chunk of network component database management.  Under
     * HKLM\System\CurrentControlSet\Services\Class\NetClient there is a
     * four-digit numeric subkey (e.g., "0000") for each network client.
     * One of them will be the default network client as far as NETDI's
     * database is concerned;  this is indicated by the existence of the
     * "Ndi\Default" subkey under the number key.  If we find one of those
     * guys, we save away the DeviceID value from the Ndi subkey so we can
     * tweak some configuration flags later in another part of the database.
     *
     * While enumerating the keys, we keep track of the highest number we've
     * seen so far.  When we're done, we add 1 to that and use that as the
     * subkey name for our client.  The number is kept separate from the
     * RegEnumKey index because the numbers are not necessarily packed (nor
     * will RegEnumKey necessarily return them in numeric order!).
     */

    HKEY hkeyNetClient;
    err = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\Class\\NetClient",
                         0, "", REG_OPTION_NON_VOLATILE,
                         KEY_CREATE_SUB_KEY | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE | KEY_SET_VALUE,
                         NULL, &hkeyNetClient, &dwDisp);
    if (err != ERROR_SUCCESS)
        return TRUE;

    UINT nFamilyNum;
    TCHAR szFamilyNumString[5]; /* four digits plus null */
    TCHAR szDefaultDeviceID[MAX_PATH] = "";

    if (dwDisp == REG_OPENED_EXISTING_KEY) {
        NLS_STR nlsSubKey(20);       /* enough for four digits, plus some just in case */
        DWORD iSubKey = 0;
        UINT maxSubKey = 0;

        for (;;) {
            err = RegEnumKey(hkeyNetClient, iSubKey, nlsSubKey.Party(), nlsSubKey.QueryAllocSize());
            nlsSubKey.DonePartying();
            if (err != ERROR_SUCCESS)
                break;

            NLS_STR nls2(nlsSubKey.strlen() + 12);
            if (nls2.QueryError() == ERROR_SUCCESS) {
                nls2 = nlsSubKey;
                nls2.strcat("\\Ndi\\Default");
                cbData = sizeof(szBuf);
                err = RegQueryValue(hkeyNetClient, nls2.QueryPch(), szBuf, (PLONG)&cbData);
                if (err == ERROR_SUCCESS) {
                    if (!lstrcmpi(szBuf, "True")) {
                        HKEY hkeyNdi;

                        NLS_STR nls3(nlsSubKey.strlen() + 5);
                        if (nls3.QueryError() == ERROR_SUCCESS) {
                            nls3 = nlsSubKey;
                            nls3.strcat("\\Ndi");

                            err = RegOpenKeyEx(hkeyNetClient, nls3.QueryPch(), 0, KEY_QUERY_VALUE, &hkeyNdi);
                            if (err == ERROR_SUCCESS) {
                                cbData = sizeof(szDefaultDeviceID);
                                RegQueryValueEx(hkeyNdi, "DeviceID", NULL, &dwType,
                                                (LPBYTE)szDefaultDeviceID, &cbData);
                                RegCloseKey(hkeyNdi);
                            }
                        }
                    }
                    RegDeleteKey(hkeyNetClient, nls2.QueryPch());
                }
            }

            UINT nSubKey = nlsSubKey.atoi();
            if (nSubKey > maxSubKey)
                maxSubKey = nSubKey;

            iSubKey++;
        }
        nFamilyNum = maxSubKey+1;
    }
    else
        nFamilyNum = 0;

    wsprintf(szFamilyNumString, "%04d", nFamilyNum);
    err = RegCreateKeyEx(hkeyNetClient, szFamilyNumString, 
                         0, "", REG_OPTION_NON_VOLATILE,
                         KEY_CREATE_SUB_KEY | KEY_SET_VALUE,
                         NULL, &hkeyFamilyClient, &dwDisp);
    if (err == ERROR_SUCCESS) {
        RegSetValueEx(hkeyFamilyClient, "DriverDesc", 0, REG_SZ,
                      (LPBYTE)nlsNPName.QueryPch(), nlsNPName.strlen()+1);
        RegSetValueEx(hkeyFamilyClient, "InfPath", 0, REG_SZ,
                      (LPBYTE)TEXT("NETFAM.INF"), 11 * sizeof(TCHAR));
        RegSetValueEx(hkeyFamilyClient, "DriverDate", 0, REG_SZ,
                      (LPBYTE)TEXT(" 5-21-1997"), 11 * sizeof(TCHAR));
        err = RegCreateKeyEx(hkeyFamilyClient, "Ndi", 
                             0, "", REG_OPTION_NON_VOLATILE,
                             KEY_CREATE_SUB_KEY | KEY_SET_VALUE,
                             NULL, &hkey, &dwDisp);
        if (err == ERROR_SUCCESS) {
            RegSetValueEx(hkey, "DeviceID", 0, REG_SZ,
                          (LPBYTE)TEXT("FAMILY"), 7 * sizeof(TCHAR));
            RegSetValueEx(hkey, "NetworkProvider", 0, REG_SZ,
                          (LPBYTE)nlsNPName.QueryPch(), nlsNPName.strlen()+1);
            RegSetValueEx(hkey, "InstallInf", 0, REG_SZ,
                          (LPBYTE)TEXT(""), sizeof(TCHAR));
            RegSetValueEx(hkey, "InfSection", 0, REG_SZ,
                          (LPBYTE)TEXT("FAMILY.ndi"), 11 * sizeof(TCHAR));

            {
                NLS_STR nlsHelpText(MAX_RES_STR_LEN);
                if (nlsHelpText.LoadString(IDS_NETFAM_HELP_TEXT) == ERROR_SUCCESS) {
                    RegSetValueEx(hkey, "HelpText", 0, REG_SZ,
                                  (LPBYTE)nlsHelpText.QueryPch(), nlsHelpText.strlen() + 1);
                }
            }

            HKEY hkeyInterfaces;
            err = RegCreateKeyEx(hkey, "Interfaces", 
                                 0, "", REG_OPTION_NON_VOLATILE,
                                 KEY_CREATE_SUB_KEY | KEY_SET_VALUE,
                                 NULL, &hkeyInterfaces, &dwDisp);
            if (err == ERROR_SUCCESS) {
                RegSetValueEx(hkeyInterfaces, "DefLower", 0, REG_SZ,
                              (LPBYTE)TEXT("netbios,ipxDHost"), 13 * sizeof(TCHAR));
                RegSetValueEx(hkeyInterfaces, "LowerRange", 0, REG_SZ,
                              (LPBYTE)TEXT("netbios,ipxDHost"), 13 * sizeof(TCHAR));
                RegSetValueEx(hkeyInterfaces, "Lower", 0, REG_SZ,
                              (LPBYTE)TEXT("netbios,ipxDHost"), 13 * sizeof(TCHAR));
                RegSetValueEx(hkeyInterfaces, "Upper", 0, REG_SZ,
                              (LPBYTE)TEXT(""), sizeof(TCHAR));
                RegCloseKey(hkeyInterfaces);
            }
            if (err == ERROR_SUCCESS)
                err = RegSetValue(hkey, "Install", REG_SZ, "FAMILY.Install", 14);
            if (err == ERROR_SUCCESS)
                err = RegSetValue(hkey, "Remove", REG_SZ, "FAMILY.Remove", 13);
            if (err == ERROR_SUCCESS)
                err = RegSetValue(hkey, "Default", REG_SZ, "True", 5);

            RegCloseKey(hkey);
        }
        RegCloseKey(hkeyFamilyClient);
    }
    RegCloseKey(hkeyNetClient);

    if (err != ERROR_SUCCESS)
        return TRUE;

    /* Now the other half of the database, under HKLM\Enum\Network.  This has
     * a subkey (named by DeviceID, as seen above) for each network component.
     * Under each such subkey, there's a numbered subkey for each instance.
     * We have three tasks here:  First of all, for each instance of the client
     * that used to be the default, we have to mask out bit 0x00000010 from
     * the ConfigFlags value, to make it no longer the default client.  Then
     * we have to create a new branch for our own client, which mainly points
     * back to the other section of the database which we just finished with.
     * Finally, we must find MSTCP and add a binding between it and our client,
     * because NETDI assumes that a client that's not bound to any transports
     * must be messed up, so it deletes it.
     */

    HKEY hkeyEnum;
    err = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Enum\\Network", 
                         0, "", REG_OPTION_NON_VOLATILE,
                         KEY_CREATE_SUB_KEY | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE | KEY_SET_VALUE,
                         NULL, &hkeyEnum, &dwDisp);
    if (err != ERROR_SUCCESS)
        return TRUE;

    /* Un-default the default client. */
    if (szDefaultDeviceID[0] != '\0') {
        HKEY hkeyDefaultDevice;
        err = RegOpenKeyEx(hkeyEnum, szDefaultDeviceID, 0,
                           KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE | KEY_SET_VALUE,
                           &hkeyDefaultDevice);
        if (err == ERROR_SUCCESS) {
            NLS_STR nlsSubKey(20);       /* enough for four digits, plus some just in case */
            DWORD iSubKey = 0;

            for (;;) {
                err = RegEnumKey(hkeyDefaultDevice, iSubKey, nlsSubKey.Party(), nlsSubKey.QueryAllocSize());
                nlsSubKey.DonePartying();
                if (err != ERROR_SUCCESS)
                    break;

                HKEY hkeyInstance;
                err = RegOpenKeyEx(hkeyDefaultDevice, nlsSubKey.QueryPch(),
                                   0, KEY_QUERY_VALUE | KEY_SET_VALUE, &hkeyInstance);
                if (err == ERROR_SUCCESS) {
                    DWORD dwConfigFlags;
                    cbData = sizeof(dwConfigFlags);
                    err = RegQueryValueEx(hkeyInstance, "ConfigFlags", NULL,
                                          &dwType, (LPBYTE)&dwConfigFlags,
                                          &cbData);
                    if (err == ERROR_SUCCESS &&
                        (dwType == REG_DWORD || dwType == REG_BINARY) &&
                        (dwConfigFlags & 0x10)) {
                        dwConfigFlags &= ~0x10;
                        RegSetValueEx(hkeyInstance, "ConfigFlags", 0, dwType,
                                      (LPBYTE)&dwConfigFlags, cbData);
                    }
                    RegCloseKey(hkeyInstance);
                }

                iSubKey++;
            }
            RegCloseKey(hkeyDefaultDevice);
        }
    }

    /* Now create a new branch for our client. */

    err = RegCreateKeyEx(hkeyEnum, "FAMILY\\0000", 
                         0, "", REG_OPTION_NON_VOLATILE,
                         KEY_SET_VALUE,
                         NULL, &hkeyFamilyClient, &dwDisp);
    if (err == ERROR_SUCCESS) {
        RegSetValueEx(hkeyFamilyClient, "Class", 0, REG_SZ,
                      (LPBYTE)TEXT("NetClient"), 10 * sizeof(TCHAR));
        lstrcpy(szBuf, "NetClient\\");
        lstrcat(szBuf, szFamilyNumString);
        RegSetValueEx(hkeyFamilyClient, "Driver", 0, REG_SZ, (LPBYTE)szBuf, lstrlen(szBuf)+1);
        RegSetValueEx(hkeyFamilyClient, "MasterCopy", 0, REG_SZ,
                      (LPBYTE)TEXT("Enum\\Network\\FAMILY\\0000"), 25 * sizeof(TCHAR));
        RegSetValueEx(hkeyFamilyClient, "DeviceDesc", 0, REG_SZ,
                      (LPBYTE)nlsNPName.QueryPch(), nlsNPName.strlen()+1);
        RegSetValueEx(hkeyFamilyClient, "CompatibleIDs", 0, REG_SZ,
                      (LPBYTE)TEXT("FAMILY"), 7 * sizeof(TCHAR));
        RegSetValueEx(hkeyFamilyClient, "Mfg", 0, REG_SZ,
                      (LPBYTE)TEXT("Microsoft"), 10 * sizeof(TCHAR));
        dwTemp = 0x00000010;
        RegSetValueEx(hkeyFamilyClient, "ConfigFlags", 0, REG_BINARY,
                      (LPBYTE)&dwTemp, sizeof(dwTemp));

        /* A "Bindings" subkey needs to exist here, with no values in it
         * (since our "client" isn't bound to any higher level components
         * like servers).
         */
        err = RegCreateKeyEx(hkeyFamilyClient, "Bindings", 
                             0, "", REG_OPTION_NON_VOLATILE,
                             KEY_SET_VALUE,
                             NULL, &hkey, &dwDisp);
        if (err == ERROR_SUCCESS)
            RegCloseKey(hkey);

        RegCloseKey(hkeyFamilyClient);
    }

    /* Get MSTCP's enum key, get the first instance, and from it we can find
     * the "master" instance.  We can then add a binding to ourselves there.
     * Can't just assume "0000" as the first one, unfortunately.
     */
    err = RegOpenKeyEx(hkeyEnum, "MSTCP", 0,
                       KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE | KEY_SET_VALUE,
                       &hkey);
    if (err == ERROR_SUCCESS) {
        NLS_STR nlsSubKey(20);       /* enough for four digits, plus some just in case */
        DWORD iSubKey = 0;

        for (;;) {
            err = RegEnumKey(hkey, iSubKey, nlsSubKey.Party(), nlsSubKey.QueryAllocSize());
            nlsSubKey.DonePartying();
            if (err != ERROR_SUCCESS)
                break;

            HKEY hkeyInstance;
            err = RegOpenKeyEx(hkey, nlsSubKey.QueryPch(),
                               0, KEY_QUERY_VALUE | KEY_SET_VALUE, &hkeyInstance);
            if (err == ERROR_SUCCESS) {
                cbData = sizeof(szBuf);
                err = RegQueryValueEx(hkeyInstance, "MasterCopy", NULL,
                                      &dwType, (LPBYTE)szBuf,
                                      &cbData);
                RegCloseKey(hkeyInstance);

                /* The MasterCopy value is actually a path to a registry key
                 * from HKEY_LOCAL_MACHINE.  We want to deal with its Bindings
                 * subkey.
                 */
                if (err == ERROR_SUCCESS) {
                    HKEY hkeyBindings;
                    lstrcat(szBuf, "\\Bindings");
                    err = RegCreateKeyEx(HKEY_LOCAL_MACHINE, szBuf,
                                         0, "", REG_OPTION_NON_VOLATILE,
                                         KEY_SET_VALUE,
                                         NULL, &hkeyBindings, &dwDisp);
                    if (err == ERROR_SUCCESS) {
                        RegSetValueEx(hkeyBindings, "FAMILY\\0000", 0, REG_SZ,
                                      (LPBYTE)TEXT(""), sizeof(TCHAR));
                        RegCloseKey(hkeyBindings);
                    }
                    break;      /* abandon enum loop */
                }

                iSubKey++;
            }
        }
        RegCloseKey(hkey);
    }

    RegCloseKey(hkeyEnum);

    return TRUE;
}


/*
Purpose: Recursively delete the key, including all child values
         and keys.  Mimics what RegDeleteKey does in Win95.

         Snarfed from shlwapi so we don't end up loading him at
         boot time.

Returns: 
Cond:    --
*/
DWORD
DeleteKeyRecursively(
    IN HKEY   hkey, 
    IN LPCSTR pszSubKey)
{
    DWORD dwRet;
    HKEY hkSubKey;

    // Open the subkey so we can enumerate any children
    dwRet = RegOpenKeyEx(hkey, pszSubKey, 0, KEY_ALL_ACCESS, &hkSubKey);
    if (ERROR_SUCCESS == dwRet)
    {
        DWORD   dwIndex;
        CHAR    szSubKeyName[MAX_PATH + 1];
        DWORD   cchSubKeyName = ARRAYSIZE(szSubKeyName);
        CHAR    szClass[MAX_PATH];
        DWORD   cbClass = ARRAYSIZE(szClass);

        // I can't just call RegEnumKey with an ever-increasing index, because
        // I'm deleting the subkeys as I go, which alters the indices of the
        // remaining subkeys in an implementation-dependent way.  In order to
        // be safe, I have to count backwards while deleting the subkeys.

        // Find out how many subkeys there are
        dwRet = RegQueryInfoKey(hkSubKey,
                                szClass,
                                &cbClass,
                                NULL,
                                &dwIndex, // The # of subkeys -- all we need
                                NULL,
                                NULL,
                                NULL,
                                NULL,
                                NULL,
                                NULL,
                                NULL);

        if (NO_ERROR == dwRet)
        {
            // dwIndex is now the count of subkeys, but it needs to be
            // zero-based for RegEnumKey, so I'll pre-decrement, rather
            // than post-decrement.
            while (ERROR_SUCCESS == RegEnumKey(hkSubKey, --dwIndex, szSubKeyName, cchSubKeyName))
            {
                DeleteKeyRecursively(hkSubKey, szSubKeyName);
            }
        }

        RegCloseKey(hkSubKey);

        dwRet = RegDeleteKey(hkey, pszSubKey);
    }

    return dwRet;
}


void DeinstallLogonDialog(void)
{
    RegDeleteKey(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\NPSTUB\\NetworkProvider");
    RegDeleteKey(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\NPSTUB");

    HKEY hkey;

    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Control\\NetworkProvider\\Order",
                     0, KEY_WRITE, &hkey) == ERROR_SUCCESS) {
        RegDeleteValue(hkey, "NPSTUB");
        RegCloseKey(hkey);
    }

    char szBuf[MAX_PATH];

    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Network\\Logon",
                     0, KEY_WRITE, &hkey) == ERROR_SUCCESS) {
        DWORD cbData = sizeof(szBuf);
        DWORD dwType;
        LONG err = RegQueryValueEx(hkey, "PrimaryProvider", NULL, &dwType,
                                   (LPBYTE)szBuf, &cbData);
        if (err == ERROR_SUCCESS && szBuf[0] != '\0') {
            NLS_STR nlsNPName(MAX_RES_STR_LEN);
            if (nlsNPName.LoadString(IDS_NP_NAME) == ERROR_SUCCESS) {
                if (!::strcmpf(nlsNPName.QueryPch(), szBuf)) {
                    RegDeleteValue(hkey, "PrimaryProvider");
                }
            }
        }

        RegCloseKey(hkey);
    }

    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Enum\\Network\\FAMILY", 0,
                     KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE | KEY_WRITE,
                     &hkey) == ERROR_SUCCESS) {
        UINT i=0;

        /* For each instance of us under the Enum branch, fetch the
         * corresponding key name under the other half of the database
         * and delete it.
         */
        for (;;) {
            DWORD err = RegEnumKey(hkey, i, szBuf, sizeof(szBuf));
            if (err != ERROR_SUCCESS)
                break;

            HKEY hkeyInstance;
            err = RegOpenKeyEx(hkey, szBuf,
                               0, KEY_QUERY_VALUE | KEY_SET_VALUE, &hkeyInstance);
            if (err == ERROR_SUCCESS) {
                strcpyf(szBuf, "System\\CurrentControlSet\\Services\\Class\\");

                DWORD dwType;
                DWORD cbData = sizeof(szBuf) - 40;  /* - length of above string */
                if (RegQueryValueEx(hkeyInstance, "Driver", NULL, &dwType,
                                    (LPBYTE)szBuf + 40, &cbData) == ERROR_SUCCESS) {
                    /* szBuf now equals the other branch we need to kill */
                    DeleteKeyRecursively(HKEY_LOCAL_MACHINE, szBuf);
                }
                RegCloseKey(hkeyInstance);
            }
            i++;
        }

        RegCloseKey(hkey);

        DeleteKeyRecursively(HKEY_LOCAL_MACHINE, "Enum\\Network\\FAMILY");
    }

    /* Now clean up bindings to our client, otherwise PNP will try to install
     * us as a new (unknown) device.  This involves enumerating components
     * under HKLM\Enum\Network;  for each one, enumerate the instances;  for
     * each instance's Bindings key, enumerate the values, and delete all
     * values that begin with FAMILY\.
     */

    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Enum\\Network", 0,
                     KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE | KEY_WRITE,
                     &hkey) == ERROR_SUCCESS) {
        UINT iComponent = 0;

        for (;;) {
            DWORD err = RegEnumKey(hkey, iComponent, szBuf, sizeof(szBuf));
            if (err != ERROR_SUCCESS)
                break;

            HKEY hkeyComponent;
            err = RegOpenKeyEx(hkey, szBuf,
                               0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hkeyComponent);
            if (err == ERROR_SUCCESS) {

                /* Opened a component's key.  Enumerate its instances, opening
                 * each one's Bindings subkey.
                 */
                TCHAR szInstance[16];       /* actually only needs to be "nnnn\Bindings" plus null char */

                UINT iInstance = 0;

                for (;;) {
                    err = RegEnumKey(hkeyComponent, iInstance, szInstance, sizeof(szInstance));
                    if (err != ERROR_SUCCESS)
                        break;

                    if (strlenf(szInstance)*sizeof(TCHAR) <= sizeof(szInstance) - sizeof("\\Bindings"))
                        strcatf(szInstance, "\\Bindings");
                    HKEY hkeyInstance;
                    err = RegOpenKeyEx(hkeyComponent, szInstance,
                               0, KEY_QUERY_VALUE | KEY_SET_VALUE, &hkeyInstance);
                    if (err == ERROR_SUCCESS) {

                        /* Opened a Bindings subkey.  For each value under this
                         * key, the value name indicates the instance being
                         * bound to, and the value data is empty.  So we can
                         * enum values, ignoring the value data and type and
                         * just concentrating on the name.
                         */

                        TCHAR szValueName[64];      /* usually "COMPONENT\nnnn" */
                        UINT iValue = 0;
                        for (;;) {
                            DWORD cchValue = ARRAYSIZE(szValueName);
                            err = RegEnumValue(hkeyInstance, iValue, szValueName, 
                                               &cchValue, NULL, NULL, NULL, NULL);
                            if (err != ERROR_SUCCESS)
                                break;

                            /* If this is a binding to our client, delete the
                             * binding and reset (deleting values while enuming
                             * can be unpredictable).
                             */
                            if (!strnicmpf(szValueName, "FAMILY\\", 7)) {
                                RegDeleteValue(hkeyInstance, szValueName);
                                iValue = 0;
                                continue;
                            }

                            iValue++;
                        }

                        RegCloseKey(hkeyInstance);
                    }

                    iInstance++;
                }


                RegCloseKey(hkeyComponent);
            }
            iComponent++;
        }

        RegCloseKey(hkey);
    }
}
