659 lines
28 KiB
C++
659 lines
28 KiB
C++
#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);
|
|
}
|
|
}
|