windows-nt/Source/XPSP1/NT/net/config/common/ncbase/ncmisc.cpp
2020-09-26 16:20:57 +08:00

869 lines
24 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1997-1999.
//
// File: N C M I S C . C P P
//
// Contents: Miscellaneous common code.
//
// Notes: Pollute this under penalty of death.
//
// Author: shaunco 10 Oct 1997
//
//----------------------------------------------------------------------------
#include <pch.h>
#pragma hdrstop
#include "ncdebug.h"
#include "ncmisc.h"
#include "ncreg.h"
#include "ncsvc.h"
#include "ncexcept.h"
#include <eh.h>
//+---------------------------------------------------------------------------
//
// Function: FInSystemSetup
//
// Purpose: Determines whether the machine is in GUI mode setup or not.
//
// Arguments:
// (none)
//
// Returns: TRUE if in GUI mode (system) setup, FALSE if not.
//
// Author: danielwe 13 Jun 1997
//
// Notes: The state is cached (since it can't change without a reboot)
// so call as often as you like. No need to keep you're own
// cached copy.
//
BOOL
FInSystemSetup ()
{
enum SETUP_STATE
{
SS_UNKNOWN = 0, // state unknown
SS_NOTINSETUP, // not in setup mode
SS_SYSTEMSETUP // in GUI mode setup
};
static SETUP_STATE s_CachedSetupState = SS_UNKNOWN;
if (SS_UNKNOWN == s_CachedSetupState)
{
s_CachedSetupState = SS_NOTINSETUP;
// Open the setup key
//
HRESULT hr;
HKEY hkeySetup;
hr = HrRegOpenKeyEx(HKEY_LOCAL_MACHINE, L"System\\Setup",
KEY_READ, &hkeySetup);
if (S_OK == hr)
{
// get the value of the setup in progress
//
DWORD dwSysSetup;
hr = HrRegQueryDword(hkeySetup, L"SystemSetupInProgress",
&dwSysSetup);
if ((S_OK == hr) && dwSysSetup)
{
s_CachedSetupState = SS_SYSTEMSETUP;
}
RegCloseKey(hkeySetup);
}
}
Assert (SS_UNKNOWN != s_CachedSetupState);
return (SS_SYSTEMSETUP == s_CachedSetupState);
}
//+---------------------------------------------------------------------------
//
// Function: GetProductFlavor
//
// Purpose: Returns the flavor of NT currenty running on the machine.
//
// Arguments:
// pvReserved [in] Reserved. Must be NULL.
// ppf [out] Returned flavor.
//
// Returns: nothing
//
// Author: shaunco 24 Mar 1997
//
// Notes:
//
NOTHROW
VOID
GetProductFlavor (
const void* pvReserved,
PRODUCT_FLAVOR* ppf)
{
NT_PRODUCT_TYPE Type;
Assert(!pvReserved);
Assert(ppf);
// Assume workstation product
//
*ppf = PF_WORKSTATION;
// Even if RtlGetProductType fails, its documented to return
// NtProductWinNt.
//
RtlGetNtProductType (&Type);
if (NtProductWinNt != Type)
{
*ppf = PF_SERVER;
}
}
//+---------------------------------------------------------------------------
//
// Function: HrIsNetworkingInstalled
//
// Purpose: Returns whether networking is installed.
//
// Arguments:
// (none)
//
// Returns: S_OK if networking is installed, S_FALSE if not, Win32 error
// otherwise.
//
// Author: danielwe 25 Jun 1997
//
// Notes: To determine if networking is installed, the ProviderOrder
// value of System\CurrentControlSet\Control\NetworkProvider\Order
// registry key is queried. If any data is present, networking
// is installed.
//
HRESULT
HrIsNetworkingInstalled ()
{
HRESULT hr = S_OK;
HKEY hkeyProvider;
DWORD cbSize = 0;
DWORD dwType;
extern const WCHAR c_szRegKeyCtlNPOrder[];
extern const WCHAR c_szProviderOrder[];
// open the provider key
hr = HrRegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szRegKeyCtlNPOrder,
KEY_READ, &hkeyProvider);
if (S_OK == hr)
{
// get the count in bytes of the provider order value
hr = HrRegQueryValueEx(hkeyProvider, c_szProviderOrder,
&dwType, (LPBYTE)NULL, &cbSize);
if (S_OK == hr)
{
if (cbSize > 2)
{
// if the value was present and it contained information
// then we have networking of some sorts
//
hr = S_OK;
}
else
{
hr = S_FALSE;
}
}
else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
{
hr = S_FALSE;
}
RegCloseKey(hkeyProvider);
}
TraceError("HrIsNetworkingInstalled", (hr == S_FALSE) ? S_OK : hr);
return hr;
}
#ifdef REMOTEBOOT
//+---------------------------------------------------------------------------
//
// Function: HrIsRemoteBootMachine
//
// Purpose: Returns whether this is a remote boot client.
//
// Arguments:
// (none)
//
// Returns: S_OK if it is remote boot, S_FALSE if not.
//
// Author: adamba 27 Mar 1998
//
// Notes: Calls GetSystemInfoEx to determine whether this is a
// remote boot client.
//
HRESULT HrIsRemoteBootMachine()
{
BOOL fIsRemoteBoot;
BOOL ok;
DWORD size = sizeof(fIsRemoteBoot);
ok = GetSystemInfoEx(SystemInfoRemoteBoot, &fIsRemoteBoot, &size);
Assert(ok);
if (fIsRemoteBoot) {
return S_OK;
} else {
return S_FALSE;
}
}
#endif // defined(REMOTEBOOT)
//+---------------------------------------------------------------------------
//
// Function: HrRegisterOrUnregisterComObject
//
// Purpose: Handles registration or unregistration of one or more COM
// objects contained in a DLL that supports the
// DllRegisterServer or DllUnregisterServer entry points.
//
// Arguments:
// pszDllPath [in] Path to DLL that contains COM object(s) to (un)register.
// rf [in] Function to perform
//
// Returns: S_OK if successful, Win32 or OLE HRESULT if failure.
//
// Author: danielwe 6 May 1997
//
// Notes:
//
HRESULT
HrRegisterOrUnregisterComObject (
PCWSTR pszDllPath,
REGISTER_FUNCTION rf)
{
BOOL fCoUninitialize = TRUE;
HRESULT hr = CoInitializeEx( NULL,
COINIT_DISABLE_OLE1DDE | COINIT_MULTITHREADED );
if (RPC_E_CHANGED_MODE == hr)
{
hr = S_OK;
fCoUninitialize = FALSE;
}
if (SUCCEEDED(hr))
{
// ANSI only
const CHAR c_szaRegisterFunction[] = "DllRegisterServer";
const CHAR c_szaUnregisterFunction[] = "DllUnregisterServer";
typedef HRESULT (CALLBACK *HCRET)(void);
HCRET pfnRegister;
HMODULE hModule;
// Get a pointer the the registration function in the Dll
hr = HrLoadLibAndGetProc (pszDllPath,
((RF_REGISTER == rf) ?
c_szaRegisterFunction : c_szaUnregisterFunction),
&hModule,
reinterpret_cast<FARPROC*>(&pfnRegister));
if (S_OK == hr)
{
// Call the registration function
hr = (*pfnRegister)();
// RAID #160109 (danielwe) 21 Apr 1998: Handle this error and
// ignore it.
if (RPC_E_CHANGED_MODE == hr)
{
hr = S_OK;
}
TraceError ("HrRegisterOrUnregisterComObject - "
"Dll(Un)RegisterServer failed!", hr);
FreeLibrary (hModule);
}
// Balances call to CoInitialize() above. Not harmful if CoInitialize()
// was called more than once before this.
if (fCoUninitialize)
{
CoUninitialize();
}
}
TraceError ("HrRegisterOrUnregisterComObject", hr);
return hr;
}
//
// Special case handling for Netbios stopping
//
#include <nb30p.h> // Netbios IOCTLs and netbios name #define
//+---------------------------------------------------------------------------
//
// Func: ScStopNetbios
//
// Desc: This function checks if the driver being unloaded is NETBIOS.SYS.
// If so it performs some special case processing for Netbios.
//
// Args: none
//
// Return: STATUS_SUCCESS if successful, or an error status
//
// History: 28-Apr-98 SumitC got from VRaman
//
//----------------------------------------------------------------------------
DWORD
ScStopNetbios()
{
OBJECT_ATTRIBUTES ObjAttr;
UNICODE_STRING NbDeviceName;
IO_STATUS_BLOCK IoStatus, StopStatus;
NTSTATUS ntStatus = STATUS_SUCCESS;
HANDLE NbHandle = NULL;
do
{
//
// Driver being stopped is netbios
//
//
// 1. Open a handle to the \\Device\Netbios
//
RtlInitUnicodeString(&NbDeviceName, NB_DEVICE_NAME);
InitializeObjectAttributes(
&ObjAttr, // obj attr to initialize
&NbDeviceName, // string to use
OBJ_CASE_INSENSITIVE, // Attributes
NULL, // Root directory
NULL); // Security Descriptor
ntStatus = NtCreateFile(
&NbHandle, // ptr to handle
GENERIC_READ|GENERIC_WRITE, // desired access
&ObjAttr, // name & attributes
&IoStatus, // I/O status block.
NULL, // alloc size.
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_DELETE // share...
| FILE_SHARE_READ
| FILE_SHARE_WRITE, // ...access
FILE_OPEN_IF, // create disposition
0, // ...options
NULL, // EA buffer
0L // Ea buffer len
);
if (!NT_SUCCESS(ntStatus))
{
TraceTag(ttidError, "Failed to open file handle to Netbios device (%08lx)",
ntStatus);
break;
}
//
// 2. Send a stop IOCTL to it.
//
ntStatus = NtDeviceIoControlFile(
NbHandle, // Handle to device
NULL, // Event to be signalled
NULL, // No post routine
NULL, // no context for post
&StopStatus, // return status block
IOCTL_NB_STOP, // IOCTL
NULL, // No input parameters
0,
NULL, // No output paramters
0
);
if (!NT_SUCCESS(ntStatus))
{
TraceTag(ttidSvcCtl, "Failed to send STOP IOCTL to netbios (%08lx).",
"probably means Netbios isn't running... anyway, we can't stop it",
ntStatus);
break;
}
} while (FALSE);
//
// 4. Close the handle just opened to the driver
//
if (NULL != NbHandle)
{
NtClose( NbHandle );
}
TraceError("ScStopNetbios", HRESULT_FROM_WIN32(ntStatus));
return ntStatus;
}
// ----------------------------------------------------------------------
//
// Function: HrEnableAndStartSpooler
//
// Purpose: Start spooler, enable if necessary
//
// Arguments: None
//
// Returns: S_OK on success, otherwise an error code
//
// Author: kumarp 19-May-98
//
// Notes:
//
HRESULT HrEnableAndStartSpooler ()
{
static const WCHAR c_szSpooler[] = L"Spooler";
TraceTag(ttidNetcfgBase, "entering ---> HrEnableAndStartSpooler" );
// Try to start the spooler. Need to explicitly open the service
// control manager with all access first, so that in case we need to
// change the start type, we have the proper permission.
//
CServiceManager scm;
HRESULT hr = scm.HrOpen (NO_LOCK, SC_MANAGER_ALL_ACCESS);
if (S_OK == hr)
{
hr = scm.HrStartServiceAndWait (c_szSpooler);
if (HRESULT_FROM_WIN32(ERROR_SERVICE_DISABLED) == hr)
{
TraceTag(ttidNetcfgBase, "HrEnableAndStartSpooler: spooler is "
"disabled trying to enable it..." );
// Have to lock the service controller before changing the
// configuration of a service. Do so and unlock before trying to
// start the service.
//
hr = scm.HrLock ();
if (S_OK == hr)
{
CService svc;
hr = scm.HrOpenService (&svc, c_szSpooler,
NO_LOCK,
SC_MANAGER_ALL_ACCESS,
STANDARD_RIGHTS_REQUIRED
| SERVICE_CHANGE_CONFIG);
if (S_OK == hr)
{
hr = svc.HrSetStartType (SERVICE_DEMAND_START);
}
scm.Unlock ();
}
if (S_OK == hr)
{
TraceTag(ttidNetcfgBase, "HrEnableAndStartSpooler: succeeded "
"in enabling spooer. Now starting..." );
hr = scm.HrStartServiceAndWait(c_szSpooler);
}
}
}
TraceError("HrEnableAndStartSpooler", hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrCreateDirectoryTree
//
// Purpose: Creates (or ensures existence of) all directories on the path
// specified in szPath.
//
// Arguments:
// pszPath [in] Full path of one or more directories to create
// (i.e. c:\this\is\a\directory\path)
// psa [in] Security attributes
//
// Returns: S_OK if success, Win32 error code otherwise
//
// Author: shaunco (copied from RASUI by danielwe) 26 Jun 1998
//
// Notes:
//
HRESULT HrCreateDirectoryTree(PWSTR pszPath, LPSECURITY_ATTRIBUTES psa)
{
HRESULT hr = S_OK;
if (pszPath)
{
DWORD dwErr = ERROR_SUCCESS;
// Loop through the path.
//
PWSTR pch;
for (pch = pszPath; *pch; pch++)
{
// Stop at each backslash and make sure the path
// is created to that point. Do this by changing the
// backslash to a null-terminator, calling CreateDirecotry,
// and changing it back.
//
if (L'\\' == *pch)
{
BOOL fOk;
*pch = 0;
fOk = CreateDirectory(pszPath, psa);
*pch = L'\\';
// Any errors other than path alredy exists and we should
// bail out. We also get access denied when trying to
// create a root drive (i.e. c:) so check for this too.
//
if (!fOk)
{
dwErr = GetLastError();
if (ERROR_ALREADY_EXISTS == dwErr)
{
dwErr = ERROR_SUCCESS;
}
else if ((ERROR_ACCESS_DENIED == dwErr) &&
(pch - 1 > pszPath) && (L':' == *(pch - 1)))
{
dwErr = ERROR_SUCCESS;
}
else
{
break;
}
}
}
}
if (ERROR_ALREADY_EXISTS == dwErr)
{
dwErr = ERROR_SUCCESS;
}
if (dwErr != ERROR_SUCCESS)
{
hr = HRESULT_FROM_WIN32(dwErr);
}
}
TraceError("HrCreateDirectoryTree", hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrDeleteFileSpecification
//
// Purpose: Delete the files specified with pszFileSpec from the
// directory given by pszDirectoryPath.
//
// Arguments:
// pszFileSpec [in] File specificaion to delete. e.g. *.mdb
// pszDirectoryPath [in] Directory path to delete from
//
// Returns: S_OK or an error code.
//
// Author: shaunco 4 Jun 1998
//
// Notes:
//
HRESULT
HrDeleteFileSpecification (
PCWSTR pszFileSpec,
PCWSTR pszDirectoryPath)
{
Assert (pszFileSpec && *pszFileSpec);
Assert (pszDirectoryPath && *pszDirectoryPath);
HRESULT hr = S_OK;
INT cchSpec = lstrlenW (pszFileSpec);
INT cchDir = lstrlenW (pszDirectoryPath);
// Make sure the length of the directory and filespec combined is less
// than MAX_PATH before continuing. The '+1' is for the backslash
// that we may add.
//
if (cchDir + 1 + cchSpec > MAX_PATH)
{
hr = HRESULT_FROM_WIN32 (ERROR_BAD_PATHNAME);
}
else
{
WCHAR szPath[MAX_PATH];
// Form the path by copying the directory and making sure it
// is terminated with a backslash if needed.
//
lstrcpyW (szPath, pszDirectoryPath);
if (cchDir &&
(L':' != pszDirectoryPath[cchDir - 1]) &&
(L'\\' != pszDirectoryPath[cchDir - 1]))
{
lstrcatW (szPath, L"\\");
cchDir++;
}
// Append the filespec to the directory and look for the first
// file.
lstrcatW (szPath, pszFileSpec);
TraceTag (ttidNetcfgBase, "Looking to delete %S (cchDir=%u)",
szPath, cchDir);
WIN32_FIND_DATA FindData;
HANDLE hFind = FindFirstFile (szPath, &FindData);
if (INVALID_HANDLE_VALUE != hFind)
{
PCWSTR pszFileName;
INT cchFileName;
do
{
// Skip files with these attributes.
//
if (FindData.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY |
FILE_ATTRIBUTE_HIDDEN |
FILE_ATTRIBUTE_READONLY |
FILE_ATTRIBUTE_SYSTEM))
{
continue;
}
// Use the shortname where possible to give us a chance
// of using a path within MAX_PATH first.
//
pszFileName = FindData.cAlternateFileName;
cchFileName = lstrlenW (pszFileName);
if (!cchFileName)
{
pszFileName = FindData.cFileName;
cchFileName = lstrlenW (pszFileName);
}
// If the length of the directory and filename don't exceed
// MAX_PATH, form the full pathname and delete it.
//
if (cchDir + cchFileName < MAX_PATH)
{
lstrcpyW (&szPath[cchDir], pszFileName);
TraceTag (ttidNetcfgBase, "Deleting %S", szPath);
if (!DeleteFile (szPath))
{
hr = HrFromLastWin32Error ();
TraceError ("DeleteFile failed. Ignoring.", hr);
}
}
}
while (FindNextFile (hFind, &FindData));
// FindNextFile should set last error to ERROR_NO_MORE_FILES
// on a succesful termination.
//
hr = HrFromLastWin32Error ();
if (HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES) == hr)
{
hr = S_OK;
}
FindClose (hFind);
}
else
{
// If FindFirstFile didn't find anything, that's okay.
//
hr = HrFromLastWin32Error ();
if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
{
hr = S_OK;
}
}
}
TraceError ("HrDeleteFileSpecification", hr);
return hr;
}
// ----------------------------------------------------------------------
//
// Function: HrDeleteDirectory
//
// Purpose: Recursively delete a directory and its all sub-dirs.
//
// Arguments:
// pszDir [in] full path to a dir
// fContinueOnError [in] whether to continue deleting others when we
// error when deleting one
//
// Returns: S_OK on success, otherwise an error code
//
// Author: kumarp 19-December-97
// danielwe 15-December-98 (moved to common and revised)
//
// Notes:
//
HRESULT HrDeleteDirectory(IN PCWSTR pszDir,
IN BOOL fContinueOnError)
{
HRESULT hr = S_OK;
WCHAR szPrefix[MAX_PATH];
WCHAR szFileSpec[MAX_PATH];
WCHAR szAllFiles[MAX_PATH];
HANDLE hFileContext;
WIN32_FIND_DATA fd;
TraceTag(ttidNetcfgBase, "Deleting directory %S", pszDir);
lstrcpyW(szPrefix, pszDir);
lstrcatW(szPrefix, L"\\");
lstrcpyW(szAllFiles, pszDir);
lstrcatW(szAllFiles, L"\\");
lstrcatW(szAllFiles, L"*");
hFileContext = FindFirstFile(szAllFiles, &fd);
if (hFileContext != INVALID_HANDLE_VALUE)
{
do
{
lstrcpyW(szFileSpec, szPrefix);
lstrcatW(szFileSpec, fd.cFileName);
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if (!(!lstrcmpiW(fd.cFileName, L".") ||
!lstrcmpiW(fd.cFileName, L"..")))
{
hr = HrDeleteDirectory(szFileSpec, fContinueOnError);
if (FAILED(hr) && fContinueOnError)
{
hr = S_OK;
}
}
}
else
{
TraceTag(ttidNetcfgBase, "Deleting file %S", szFileSpec);
if (DeleteFile(szFileSpec))
{
hr = S_OK;
}
else
{
TraceTag(ttidNetcfgBase, "Error deleting file %S",
szFileSpec);
TraceError("HrDeleteDirectory", hr);
hr = fContinueOnError ? S_OK : HrFromLastWin32Error();
}
}
if ((S_OK == hr) && FindNextFile(hFileContext, &fd))
{
hr = S_OK;
}
else
{
hr = HrFromLastWin32Error();
}
}
while (S_OK == hr);
if (hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES))
{
hr = S_OK;
}
FindClose(hFileContext);
if (S_OK == hr)
{
if (RemoveDirectory(pszDir))
{
hr = S_OK;
}
else
{
TraceTag(ttidNetcfgBase, "Error deleting directory %S", pszDir);
TraceLastWin32Error("HrDeleteDirectory");
hr = fContinueOnError ? S_OK : HrFromLastWin32Error();
}
}
}
else
{
hr = HrFromLastWin32Error();
}
TraceError("HrDeleteDirectory", hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: LowerCaseComputerName
//
// Purpose: Utility function to lowercase a name obtained either from
// the user via an UPPERCASE edit control, or via GetComputerName.
//
// Arguments:
// szName [in,out] Computername, which is modified in-place
//
// Returns: VOID
//
// Author: SumitC 29 Sep 1999
//
// Notes: The conversion only fails if CharLowerBuffW fails. Per the user
// guys, CharLowerBuff never actually returns any indication of
// failure, so we can't tell anyway. I've been assured that the
// conversion is VERY unlikely to fail.
//
VOID
LowerCaseComputerName(
IN OUT PWSTR szName)
{
// try the conversion
Assert(szName);
DWORD dwLen = wcslen(szName);
DWORD dwConverted = CharLowerBuff(szName, dwLen);
Assert(dwConverted == dwLen);
}
void __cdecl nc_trans_func( unsigned int uSECode, EXCEPTION_POINTERS* pExp )
{
throw NC_SEH_Exception( uSECode );
}
void EnableCPPExceptionHandling()
{
_set_se_translator(nc_trans_func);
}
void DisableCPPExceptionHandling()
{
_set_se_translator(NULL);
}