windows-nt/Source/XPSP1/NT/shell/ext/hnw/wizard/icsinst.cpp
2020-09-26 16:20:57 +08:00

587 lines
16 KiB
C++

//
// ICSInst.cpp
//
// ICS (Internet Connection Sharing) installation functions and thunk
// layer.
//
// History:
//
// 9/27/1999 RayRicha Created
// 11/01/1999 KenSh Store function ptrs in array rather than globals
// 12/09/1999 KenSh Check for 3rd party NATs
//
#include "stdafx.h"
#include "ICSInst.h"
#include "TheApp.h"
#include "Config.h"
#include "DefConn.h"
#include "NetConn.h"
#include "Util.h"
#include "netapi.h"
extern "C" {
#include "icsapi.h"
}
// these are the Command Line parameters to CreateProcess for running a first time install and a reconfig
static const TCHAR c_szUpdateDriverBindings[] = _T("rundll.exe ISSETUP.DLL,UpdateDriverBindings");
static const TCHAR c_szInstallICS[] = _T("rundll.exe ISSETUP.DLL,InstallOptionalComponent ICS");
static const TCHAR c_szUninstall[] = _T("rundll.exe ISSETUP.DLL,ExtUninstall");
static const TCHAR c_szICSSettingsKey[] = _T("System\\CurrentControlSet\\Services\\ICSharing\\Settings\\General");
static const TCHAR c_szICSInt[] = _T("System\\CurrentControlSet\\Services\\ICSharing\\Settings\\General\\InternalAdapters");
static const TCHAR c_szInternalAdapters[] = _T("InternalAdapters");
static const TCHAR c_szRunServices[] = _T("Software\\Microsoft\\Windows\\CurrentVersion\\RunServices");
#define c_szIcsRegVal_ShowTrayIcon _T("ShowTrayIcon")
#define SZ_UNINSTALL_KEY _T("Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall")
static void (PASCAL FAR * g_pfInstallOptionalComponent)(HWND, HINSTANCE, LPSTR, int);
HHOOK g_hSupressRebootHook = NULL;
//////////////////////////////////////////////////////////////////////////////
// Helper functions
BOOL RunNetworkInstall(BOOL* pfRebootRequired)
{
PROCESS_INFORMATION ProcessInfo;
STARTUPINFO si;
DWORD dwExitCode = 0xffffffffL;
BOOL fSuccess;
memset((char *)&si, 0, sizeof(si));
si.cb = sizeof(si);
si.wShowWindow = SW_SHOW;
fSuccess = CreateProcess(NULL, (LPTSTR)c_szUpdateDriverBindings, NULL, NULL, FALSE,
0, NULL, NULL, &si, &ProcessInfo);
if (fSuccess)
{
HANDLE hProcess = ProcessInfo.hProcess;
CloseHandle(ProcessInfo.hThread);
//
// wait for update driver bindings to complete
//
WaitForSingleObject(hProcess, INFINITE);
GetExitCodeProcess(hProcess, &dwExitCode);
CloseHandle(hProcess);
*pfRebootRequired = TRUE;
return TRUE;
}
return FALSE;
}
// check for 3rd party NATs - returns TRUE if any are installed
BOOL IsOtherNATAlreadyInstalled(LPTSTR pszOtherNatDescription, int cchOtherNatDescription)
{
BOOL bRet = FALSE;
CRegistry reg;
LPCTSTR pszUninstallKey = NULL;
if (reg.OpenKey(HKEY_LOCAL_MACHINE, c_szRunServices, KEY_READ))
{
if (0 != reg.GetValueSize(_T("SyGateService")))
{
bRet = TRUE;
pszUninstallKey = _T("SyGate");
}
else if (0 != reg.GetValueSize(_T("WinGate Service")))
{
bRet = TRUE;
pszUninstallKey = _T("WinGate");
}
else if (0 != reg.GetValueSize(_T("ENSApServer"))) // Intel AnyPoint
{
bRet = TRUE;
pszUninstallKey = _T("Intel AnyPoint Network Software");
}
else if (0 != reg.GetValueSize(_T("WinNATService"))) // Diamond HomeFree
{
bRet = TRUE;
pszUninstallKey = _T("WinNAT");
}
}
// WinProxy has to be launched manually, and requires a static IP. Just check
// to see if it's installed - the user might not even be running it.
//
if (reg.OpenKey(HKEY_LOCAL_MACHINE, SZ_UNINSTALL_KEY _T("\\WinProxy"), KEY_READ))
{
bRet = TRUE;
pszUninstallKey = _T("WinProxy");
}
if (pszOtherNatDescription != NULL)
{
*pszOtherNatDescription = _T('\0');
if (bRet) // Get the friendly name of the conflicting service from uninstall key
{
if (reg.OpenKey(HKEY_LOCAL_MACHINE, SZ_UNINSTALL_KEY, KEY_READ))
{
if (reg.OpenSubKey(pszUninstallKey, KEY_READ))
{
reg.QueryStringValue(_T("DisplayName"), pszOtherNatDescription, cchOtherNatDescription);
}
}
}
}
return bRet;
}
//////////////////////////////////////////////////////////////////////////////
// CICSInst
CICSInst::CICSInst()
{
m_option = ICS_NOACTION;
m_pszHostName = theApp.LoadStringAlloc(IDS_ICS_HOST);
m_bInstalledElsewhere = FALSE;
m_bShowTrayIcon = TRUE;
CRegistry reg;
if (reg.OpenKey(HKEY_LOCAL_MACHINE, c_szICSSettingsKey, KEY_READ))
{
TCHAR szTrayIcon[10];
if (reg.QueryStringValue(c_szIcsRegVal_ShowTrayIcon, szTrayIcon, _countof(szTrayIcon)))
{
if (!StrCmp(szTrayIcon, _T("0")))
{
m_bShowTrayIcon = FALSE;
}
}
}
}
CICSInst::~CICSInst()
{
free(m_pszHostName);
}
BOOL
CICSInst::InitICSAPI()
{
return TRUE;
}
// UpdateIcsTrayIcon
//
// Updates the registry values that affect the ICS tray icon, and
// immediately updates the icon to reflect the new values.
//
// 2/04/2000 KenSh Created
//
void CICSInst::UpdateIcsTrayIcon()
{
CRegistry reg;
if (reg.CreateKey(HKEY_LOCAL_MACHINE, c_szICSSettingsKey))
{
// Update the tray icon setting in the registry
TCHAR szVal[2];
szVal[0] = m_bShowTrayIcon ? _T('1') : _T('0');
szVal[1] = _T('\0');
reg.SetStringValue(c_szIcsRegVal_ShowTrayIcon, szVal);
}
// Show or hide the icon immediately
HWND hwndTray = ::FindWindow(_T("ICSTrayWnd"), NULL);
if (hwndTray != NULL)
{
// Post a custom message to the ICS manager window (icshare\util\icsmgr\trayicon.c)
//
// This message shows or hides the tray icon according to the value in
// the registry.
//
// wParam: enable/disable accordint to value in registry
// lParam: unused
//
UINT uUpdateMsg = RegisterWindowMessage(_T("ICSTaskbarUpdate"));
PostMessage(hwndTray, uUpdateMsg, FALSE, 0L);
}
}
void CICSInst::DoInstallOption(BOOL* pfRebootRequired, UINT ipaInternal)
{
BOOL bIcsInstalled = ::IsIcsInstalled();
// Force uninstall if internal or external NIC is not valid
if ((m_option == ICS_UNINSTALL && TRUE == bIcsInstalled)||
(bIcsInstalled && m_option == ICS_NOACTION && !this->IsInstalled()))
{
Uninstall(pfRebootRequired);
bIcsInstalled = FALSE;
}
// Force tray icon to show up, if ICS is currently installed
m_bShowTrayIcon = TRUE;
UpdateIcsTrayIcon();
switch (m_option)
{
case ICS_INSTALL:
if(FALSE == IsInstalled())
{
Install(pfRebootRequired, ipaInternal);
}
break;
case ICS_UPDATEBINDINGS:
UpdateBindings(pfRebootRequired, ipaInternal);
break;
case ICS_UNINSTALL:
// Already handled above
break;
case ICS_ENABLE:
Enable();
break;
case ICS_DISABLE:
Disable();
break;
case ICS_CLIENTSETUP:
SetupClient();
break;
case ICS_NOACTION:
break;
}
}
// Similar steps from here as Win98SE ConfigureICS (without UI)
void CICSInst::UpdateBindings(BOOL* pfRebootRequired, UINT ipaInternal)
{
CConfig rghConfig;
// TODO: remove hardcoded values!
StrCpy(rghConfig.m_HangupTimer, _T("300"));
SetInternetConnection();
SetHomeConnection(ipaInternal);
// REVIEW: is there a case where these values should be set differently?
rghConfig.m_EnableICS = TRUE;
rghConfig.m_EnableDialOnDemand = TRUE;
rghConfig.m_EnableDHCP = TRUE;
rghConfig.m_ShowTrayIcon = m_bShowTrayIcon;
rghConfig.InitWizardResult();
// Set to TRUE until we see a need to differentiate between new install and update
rghConfig.WriteWizardCode(TRUE);
int iSaveStatus = rghConfig.SaveConfig();
// TODO: determine if we need to check for binding changes
//if ( iSaveStatus == BINDINGS_NEEDED )
//{
RunNetworkInstall(pfRebootRequired);
//}
}
void CICSInst::Install(BOOL* pfRebootRequired, UINT ipaInternal)
{
PROCESS_INFORMATION ProcessInfo;
STARTUPINFO si;
BOOL fSuccess;
// Check for conflicting 3rd party NAT
{
TCHAR szConflictingNAT[260];
if (IsOtherNATAlreadyInstalled(szConflictingNAT, _countof(szConflictingNAT)))
{
if (szConflictingNAT[0] == _T('\0'))
{
LPTSTR pszDefault1 = theApp.LoadStringAlloc(IDS_OTHERNAT_GENERIC);
LPTSTR pszDefault2 = theApp.LoadStringAlloc(IDS_OTHERNAT_GENERIC_THE);
if (pszDefault1 && pszDefault2)
theApp.MessageBoxFormat(MB_ICONEXCLAMATION | MB_OK, IDS_ERR_OTHERNAT, pszDefault1, pszDefault2);
free(pszDefault2);
free(pszDefault1);
}
else
{
theApp.MessageBoxFormat(MB_ICONEXCLAMATION | MB_OK, IDS_ERR_OTHERNAT, szConflictingNAT, szConflictingNAT);
}
return; // block ICS installation
}
}
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.wShowWindow = SW_SHOW;
fSuccess = CreateProcess(NULL, (LPTSTR)c_szInstallICS, NULL, NULL, FALSE,
0, NULL, NULL, &si, &ProcessInfo);
if (fSuccess)
{
HANDLE hProcess = ProcessInfo.hProcess;
CloseHandle(ProcessInfo.hThread);
//
// wait for update driver bindings to complete
//
WaitForSingleObject(hProcess, INFINITE);
CloseHandle(hProcess);
UpdateBindings(pfRebootRequired, ipaInternal);
// Need to reboot
*pfRebootRequired = TRUE;
}
}
LRESULT CALLBACK SupressRebootDialog(int nCode, WPARAM wParam, LPARAM lParam)
{
LRESULT lResult = 0;
if (nCode == HCBT_CREATEWND)
{
HWND hwnd = (HWND)wParam;
CBT_CREATEWND* pCW = (CBT_CREATEWND*)lParam;
LPCREATESTRUCT pCreateStruct = pCW->lpcs;
lResult = 1; // prevent window creation
}
else
{
lResult = CallNextHookEx(g_hSupressRebootHook, nCode, wParam, lParam);
}
return lResult;
}
void CICSInst::Uninstall(BOOL* pfRebootRequired)
{
g_hSupressRebootHook = SetWindowsHookEx(WH_CBT, SupressRebootDialog, NULL, GetCurrentThreadId()); // not thread safe, should be OK
IcsUninstall();
if(NULL != g_hSupressRebootHook)
{
UnhookWindowsHookEx(g_hSupressRebootHook );
}
*pfRebootRequired = TRUE;
return;
}
BOOL CICSInst::IsInstalled()
{
// Make sure ICS is installed correctly by checking Internet and Home connection.
return (IsIcsInstalled() && GetICSConnections(NULL, NULL) && IsHomeConnectionValid());
}
BOOL CICSInst::IsEnabled()
{
return IsIcsEnabled();
}
BOOL CICSInst::IsInstalledElsewhere()
{
if (m_bInstalledElsewhere || IsIcsAvailable())
{
//MessageBox(theApp.m_hWndMain, "IsIcsAvailable returned TRUE", "Test", MB_OK);
// Note: if we knew the name of the ICS host, here's where we'd set m_pszHostName.
return TRUE;
}
else
{
//MessageBox(theApp.m_hWndMain, "IsIcsAvailable returned FALSE", "Test", MB_OK);
return FALSE;
}
}
void CICSInst::SetInternetConnection()
{
/*
if (-1 != theApp.m_uExternalAdapter)
{
TCHAR szClassKey[MAX_KEY_SIZE];
StrCpy(szClassKey, FindFileTitle(theApp.m_pCachedNetAdapters[theApp.m_uExternalAdapter].szClassKey));
CRegistry reg;
reg.OpenKey(HKEY_LOCAL_MACHINE, c_szICSSettingsKey);
reg.SetStringValue(_T("ExternalAdapter"), szClassKey);
reg.SetStringValue(_T("ExternalAdapterReg"), szClassKey);
}
*/
}
BOOL CICSInst::GetICSConnections(LPTSTR szExternalConnection, LPTSTR szInternalConnection)
{
CRegistry reg;
TCHAR szEntry[10];
if (reg.OpenKey(HKEY_LOCAL_MACHINE, c_szICSSettingsKey, KEY_READ))
{
if (reg.QueryStringValue(_T("ExternalAdapterReg"), szEntry, _countof(szEntry)) &&
lstrlen(szEntry))
{
if (szExternalConnection)
{
StrCpy(szExternalConnection, szEntry);
}
if (reg.QueryStringValue(_T("InternalAdapterReg"), szEntry, _countof(szEntry)) &&
lstrlen(szEntry))
{
if (szInternalConnection)
{
StrCpy(szInternalConnection, szEntry);
}
return TRUE;
}
}
}
return FALSE;
}
void CICSInst::SetHomeConnection(UINT ipaInternal)
{
int cInternalAdapter = 0; // hack for one adapter
TCHAR szNumber[5];
wnsprintf(szNumber, ARRAYSIZE(szNumber), TEXT("%04d"), cInternalAdapter);
const NETADAPTER* pAdapterArray;
EnumCachedNetAdapters(&pAdapterArray);
const NETADAPTER* pAdapter = &pAdapterArray[ipaInternal];
TCHAR szClassKey[MAX_KEY_SIZE];
StrCpy(szClassKey, FindFileTitle((LPCTSTR)pAdapter->szClassKey));
LPTSTR* prgBindings;
int cBindings = EnumMatchingNetBindings(pAdapter->szEnumKey, SZ_PROTOCOL_TCPIP, (LPWSTR**)&prgBindings);
CRegistry reg2(HKEY_LOCAL_MACHINE, c_szICSInt);
reg2.CreateSubKey(szNumber);
reg2.SetStringValue(_T("InternalAdapterReg"), szClassKey);
reg2.SetStringValue(_T("InternalAdapter"), szClassKey);
// Assume that adapter is only bound to one TCP/IP instance
reg2.SetStringValue(_T("InternalBinding"), prgBindings[0]);
TCHAR szIPAddress[30];
wnsprintf(szIPAddress, ARRAYSIZE(szIPAddress), TEXT("192.168.%d.1,255.255.255.0"), cInternalAdapter);
reg2.SetStringValue(_T("IntranetInfo"), szIPAddress);
// TODO: remove
// Put the first adapter in the "old location" to support legacy config
CRegistry reg;
reg.OpenKey(HKEY_LOCAL_MACHINE, c_szICSSettingsKey);
reg.DeleteSubKey(c_szInternalAdapters);
reg.CreateSubKey(c_szInternalAdapters);
reg.OpenKey(HKEY_LOCAL_MACHINE, c_szICSSettingsKey);
reg.SetStringValue(_T("InternalAdapterReg"), szClassKey);
reg.SetStringValue(_T("InternalAdapter"), szClassKey);
// Assume that adapter is only bound to one TCP/IP instance
reg.SetStringValue(_T("InternalBinding"), prgBindings[0]);
reg.SetStringValue(_T("IntranetInfo"), szIPAddress);
}
// TODO: expand with support for multiple adapters
BOOL CICSInst::IsHomeConnectionValid()
{
CRegistry reg;
TCHAR szEntry[10];
if (reg.OpenKey(HKEY_LOCAL_MACHINE, c_szICSSettingsKey, KEY_READ))
{
if (reg.QueryStringValue(_T("InternalAdapterReg"), szEntry, _countof(szEntry)) &&
lstrlen(szEntry))
{
return TRUE;
}
else
{
// Check for valid multiple-adapter scenario
if (reg.OpenKey(HKEY_LOCAL_MACHINE, c_szICSInt) &&
reg.OpenSubKey(_T("0000")) &&
reg.QueryStringValue(_T("InternalAdapterReg"), szEntry, _countof(szEntry)) &&
lstrlen(szEntry))
{
return TRUE;
}
}
}
return FALSE;
}
BOOL CICSInst::Enable()
{
if (InitICSAPI())
{
IcsEnable(0);
return TRUE;
}
else
{
return FALSE;
}
// return (!IcsEnable(0));
}
BOOL CICSInst::Disable()
{
if (InitICSAPI())
{
IcsDisable(0);
return TRUE;
}
else
{
return FALSE;
}
// return (!IcsDisable(0));
}
void CICSInst::SetupClient()
{
// Move this functionality to WizPages.cpp and Install.cpp for now
//::SetDefaultDialupConnection(NULL);
}
BOOLEAN APIENTRY IsIcsInstalled(VOID) // API not available on Win98 so implement it here
{
BOOLEAN fIcsInstalled = FALSE;
HKEY hKey;
DWORD dwRet = RegOpenKeyEx (HKEY_LOCAL_MACHINE, REGSTR_PATH_RUN, (DWORD)0, KEY_READ, &hKey);
if (ERROR_SUCCESS == dwRet)
{
DWORD dwType;
char szValue[128];
DWORD dwSize = sizeof(szValue) / sizeof(char);
dwRet = RegQueryValueExA(hKey, "ICSMGR", NULL, &dwType, reinterpret_cast<LPBYTE>(szValue), &dwSize);
if ((ERROR_SUCCESS == dwRet) && (dwType == REG_SZ))
{
fIcsInstalled = 0 == lstrcmpA(szValue, "ICSMGR.EXE");
}
RegCloseKey ( hKey );
}
return (fIcsInstalled);
}