//================================================================================ // Copyright (c) 1997 Microsoft Corporation // Author: RameshV // Description: this file deals with the part that keeps the registry bitmask in // sync. the way, this is done is by keeping a count in each RANGE object to // count the # of operations that have been performed on it.. if it crosses // a threshold, it is saved to registry.. //================================================================================ //#include #include #include #include extern CRITICAL_SECTION DhcpGlobalInProgressCritSect; extern CRITICAL_SECTION DhcpGlobalMemoryCritSect; #define LOCK_MEMORY() EnterCriticalSection(&DhcpGlobalMemoryCritSect) #define UNLOCK_MEMORY() LeaveCriticalSection(&DhcpGlobalMemoryCritSect) #define LOCK_INPROGRESS_LIST() EnterCriticalSection(&DhcpGlobalInProgressCritSect) #define UNLOCK_INPROGRESS_LIST() LeaveCriticalSection(&DhcpGlobalInProgressCritSect) #define DIRT_THRESHOLD 10 // flush every 10 addresses //BeginExport(defines) #define FLUSH_MODIFIED_DIRTY 0 #define FLUSH_MODIFIED 1 #define FLUSH_ANYWAY 2 //EndExport(defines) DWORD FlushCheckLoop( IN PARRAY ArrayToLoopThru, IN DWORD (*Iterator)(LPVOID Element, DWORD FlushNow, LPVOID Context), IN DWORD FlushNow, IN LPVOID Context ) { DWORD LastFailure; DWORD Error; ARRAY_LOCATION Loc; LPVOID Element; LastFailure = ERROR_SUCCESS; Error = MemArrayInitLoc(ArrayToLoopThru, &Loc); while(ERROR_FILE_NOT_FOUND != Error ) { Require(ERROR_SUCCESS == Error); Error = MemArrayGetElement(ArrayToLoopThru, &Loc, &Element); Require(ERROR_SUCCESS == Error && NULL != Element); Error = Iterator(Element, FlushNow, Context); Require(ERROR_SUCCESS == Error); if( ERROR_SUCCESS != Error ) LastFailure = Error; Error = MemArrayNextLoc(ArrayToLoopThru, &Loc); } return LastFailure; } BOOL _inline SpecialValues( IN LPWSTR ValueName ) { return (0 == wcscmp(ValueName, REG_RANGE_START_ADDRESS) || 0 == wcscmp(ValueName, REG_RANGE_END_ADDRESS) || 0 == wcscmp(ValueName, REG_FLAGS) || 0 == wcscmp(ValueName, REG_SUBNET_EXCL) || 0 == wcscmp(ValueName, REG_SUBNET_SWITCHED_FLAG) || 0 == wcscmp(ValueName, REG_MSCOPE_NAME) || 0 == wcscmp(ValueName, REG_MSCOPE_COMMENT) || 0 == wcscmp(ValueName, REG_MSCOPE_SCOPEID) || 0 == wcscmp(ValueName, REG_MSCOPE_STATE) || 0 == wcscmp(ValueName, REG_MSCOPE_ADDR_POLICY) || 0 == wcscmp(ValueName, REG_MSCOPE_TTL) || 0 == wcscmp(ValueName, REG_MSCOPE_LANG_TAG) || 0 == wcscmp(ValueName, REG_MSCOPE_EXPIRY_TIME) ); } typedef struct _FLSH_BITS_CTXT { REG_HANDLE *Hdl; PM_SUBNET Subnet; PM_RANGE Range; LONG Index; } FLSH_BITS_CTXT, *LPFLSH_BITS_CTXT; static BYTE TempBuffer[MAX_BIT1SIZE + sizeof(DWORD)*4]; DWORD FlushBitmask( IN PM_BITMASK1 Bits1, IN DWORD FlushNow, IN LPVOID FlushCtxt ) { LPFLSH_BITS_CTXT Ctxt = FlushCtxt; HKEY Key = Ctxt->Hdl->Key; WCHAR BitsValueName[REG_RANGE_BITS_PREFIX_WCHAR_COUNT+57]; LONG Count, Index = (Ctxt->Index++); ULONG WriteSize = 4*sizeof(DWORD), Tmp; DWORD Error; LPWSTR TmpStr; if( FLUSH_ANYWAY != FlushNow && 0 == Bits1->nDirtyOps ) { return ERROR_SUCCESS; } Bits1->nDirtyOps = 0; ZeroMemory( BitsValueName, sizeof(BitsValueName)); ConvertAddressToLPWSTR(Ctxt->Range->Start, BitsValueName); wcscat(BitsValueName, L" "); wcscat(BitsValueName, REG_RANGE_BITS_PREFIX); TmpStr = &BitsValueName[wcslen(BitsValueName)]; for( Count = 5; Count >= 0; Count -- ) { TmpStr[Count] = (WCHAR)(L'0' + (Index%10)); Index/= 10; } TmpStr[6] = L'\0'; if( 0 == Bits1->nSet ) { // // If no bit is set, we don't have to write this to registry -- just need to REMOVE value.. // Error = RegDeleteValue(Key, (LPWSTR)BitsValueName); if( ERROR_FILE_NOT_FOUND == Error || ERROR_PATH_NOT_FOUND == Error ) { return ERROR_SUCCESS; } return Error; } // // compose TempBuffer -- note that this whole func is serialized, so we can use // TempBuffer safely.. // Tmp = htonl(Bits1->Size); memcpy(&TempBuffer[0*sizeof(DWORD)], &Tmp, sizeof(DWORD)); if( Bits1->Size == Bits1->nSet ) { Require(Bits1->Mask == NULL); Tmp = 0; } else Tmp = htonl(Bits1->AllocSize); memcpy(&TempBuffer[1*sizeof(DWORD)], &Tmp, sizeof(DWORD)); Tmp = htonl(Bits1->nSet); memcpy(&TempBuffer[2*sizeof(DWORD)], &Tmp, sizeof(DWORD)); Tmp = htonl(Bits1->Offset); memcpy(&TempBuffer[3*sizeof(DWORD)], &Tmp, sizeof(DWORD)); if( Bits1->Mask ) { memcpy(&TempBuffer[4*sizeof(DWORD)], Bits1->Mask, Bits1->AllocSize); WriteSize += Bits1->AllocSize; } return RegSetValueEx( Key, (LPWSTR)BitsValueName, 0, REG_BINARY, TempBuffer, WriteSize ); } DWORD DhcpRegClearupRangeValues( IN PM_SUBNET Subnet, IN PM_RANGE Range ) /*++ Routine Description: This routine clears up all values for a given Range (this can be specified via a single Key for the range, or via the Range/Subnet object pair) excepting "StartAddress", "EndAddress" and "Flags". Arguments: Key INVALID_HANDLE_VALUE if range is specified via Range, Subnet pair. Else Range key in registry. Subnet Subnet object if Key is not speificed. Range Range object if key is not specified. Returns: Win32 errors (registry) or ERROR_SUCCESS on success. --*/ { ULONG Error, nValues, Index; REG_HANDLE Hdl; HKEY UseKey; WCHAR ValueNameBuf[100], RangeStr[50]; DWORD ValueNameSize, ValueType; Error = DhcpRegGetThisServer( &Hdl ); if( NO_ERROR != Error ) return Error; ZeroMemory( RangeStr, sizeof(RangeStr)); ConvertAddressToLPWSTR( Range->Start, RangeStr ); UseKey = Hdl.Key; do { Error = RegQueryInfoKey( UseKey, NULL, NULL, NULL, NULL, NULL, NULL, &nValues, NULL, NULL, NULL, NULL ); if( ERROR_SUCCESS != Error ) break; Index = nValues -1; while( nValues ) { ValueNameSize = sizeof(ValueNameBuf)/sizeof(WCHAR); Error = RegEnumValue( UseKey, Index, (LPWSTR)ValueNameBuf, &ValueNameSize, NULL, &ValueType, NULL, NULL ); if( ERROR_SUCCESS != Error ) break; if( 0 == wcsncmp( ValueNameBuf, RangeStr, wcslen(RangeStr) ) ) { RegDeleteValue(UseKey, (LPWSTR)ValueNameBuf); } Index --; nValues --; } } while ( 0 ); DhcpRegCloseHdl(&Hdl); return Error; } // This function is ALSO CALLED FROM REGREAD.C while reading in a subnet info.. //BeginExport(function) DWORD FlushRanges( IN PM_RANGE Range, IN DWORD FlushNow, IN PM_SUBNET Subnet ) //EndExport(function) { DWORD Error; REG_HANDLE Hdl; PM_BITMASK BitMask; FLSH_BITS_CTXT Ctxt = { &Hdl, Subnet, Range, 0}; if( (FLUSH_ANYWAY != FlushNow && 0 == Range->DirtyOps) || (FLUSH_MODIFIED_DIRTY == FlushNow && Range->DirtyOps < DIRT_THRESHOLD ) ) { return ERROR_SUCCESS; } Error = DhcpRegGetThisServer( &Hdl ); if( NO_ERROR != Error ) return Error; // // Lock is needed to serialize access to memory -- shouldn't be allocating addresses // while we're planning to save it to registry.. // LOCK_INPROGRESS_LIST(); LOCK_MEMORY(); Range->DirtyOps = 0; BitMask = Range->BitMask; if( FLUSH_ANYWAY == FlushNow ) { DhcpRegClearupRangeValues(Subnet, Range); } Error = FlushCheckLoop(&BitMask->Array, FlushBitmask, FlushNow, &Ctxt ); DhcpRegCloseHdl( &Hdl ); UNLOCK_MEMORY(); UNLOCK_INPROGRESS_LIST(); return Error; } DWORD FlushSubnets( IN PM_SUBNET Subnet, IN DWORD FlushNow, IN LPVOID Context_UNUSED ) { ULONG Error; Error = FlushCheckLoop(&Subnet->Ranges, FlushRanges, FlushNow, Subnet); Require( ERROR_SUCCESS == Error ); return Error; } //BeginExport(function) DWORD DhcpRegServerFlush( IN PM_SERVER Server, IN DWORD FlushNow ) //EndExport(function) { DWORD Error; Error = FlushCheckLoop(&Server->Subnets, FlushSubnets, FlushNow, NULL); Require(ERROR_SUCCESS == Error); Error = FlushCheckLoop(&Server->MScopes, FlushSubnets, FlushNow, NULL); Require(ERROR_SUCCESS == Error); return Error; } //BeginExport(function) DWORD DhcpRegFlushServer( IN DWORD FlushNow ) //EndExport(function) { PM_SERVER Server; DWORD Error; PM_SERVER DhcpGetCurrentServer(VOID); Server = DhcpGetCurrentServer(); Error = FlushCheckLoop(&Server->Subnets, FlushSubnets, FlushNow, NULL); Require(ERROR_SUCCESS == Error); Error = FlushCheckLoop(&Server->MScopes, FlushSubnets, FlushNow, NULL); Require(ERROR_SUCCESS == Error); return Error; } //================================================================================ // ds support routines -- flush a full server to disk //================================================================================ DWORD SaveArray( IN PARRAY Array, IN DWORD (*Func)(LPVOID, LPVOID, LPVOID), IN LPVOID Arg2, IN LPVOID Arg3 ) { ARRAY_LOCATION Loc; DWORD Result; LPVOID ThisPtr; Result = MemArrayInitLoc(Array, &Loc); while(ERROR_FILE_NOT_FOUND != Result ) { Result = MemArrayGetElement(Array, &Loc, &ThisPtr); Require(ERROR_SUCCESS == Result && NULL != ThisPtr ); Result = Func(ThisPtr, Arg2, Arg3); if( ERROR_SUCCESS != Result ) return Result; Result = MemArrayNextLoc(Array, &Loc); } return ERROR_SUCCESS; } DWORD DhcpRegSaveOptList( IN PM_ONECLASS_OPTLIST OptClassOptList, IN LPVOID Arg1, IN LPVOID Arg2 ) { DWORD Result; DWORD ClassId; DWORD VendorId; LPWSTR ClassName; LPWSTR VendorName; PM_OPTLIST OptList; PM_SERVER Server; PM_SUBNET Subnet; PM_RESERVATION Reservation; PM_SSCOPE SScope; PM_OPTION Option; PM_CLASSDEF ClassDef; ARRAY_LOCATION Loc; ClassId = OptClassOptList->ClassId; VendorId = OptClassOptList->VendorId; OptList = &OptClassOptList->OptList; if( NULL != Arg1 && NULL != Arg2 ) { // reservations Reservation = Arg1; Subnet = Arg2; Server = Subnet->ServerPtr; } else if( NULL == Arg2 ) { // subnet options Reservation = NULL; Subnet = Arg1; Server = Subnet->ServerPtr; } else if( NULL == Arg1 ) { // global options Reservation = NULL; Subnet = NULL; Server = Arg2; } else { // enterprise options? return ERROR_NOT_SUPPORTED; } Result = MemServerGetClassDef( // get the vendor name first Server, VendorId, NULL, 0, NULL, &ClassDef ); if( ERROR_SUCCESS != Result ) { VendorName = NULL; } else { VendorName = ClassDef->Name; Require(ClassDef->IsVendor == TRUE); } Result = MemServerGetClassDef( // get the class name for this class Server, ClassId, NULL, 0, NULL, &ClassDef ); if( ERROR_SUCCESS != Result ) { ClassName = NULL; } else { ClassName = ClassDef->Name; Require(ClassDef->IsVendor == FALSE); } Result = MemArrayInitLoc(OptList, &Loc); while( ERROR_FILE_NOT_FOUND != Result ) { //- ERROR_SUCCESS == Result Result = MemArrayGetElement(OptList, &Loc, &Option); //- ERROR_SUCCESS == Result && NULL != Option if( NULL != Reservation ) { // save reservation options Result = DhcpRegSaveReservedOption( Subnet->Address, Reservation->Address, Option->OptId, ClassName, VendorName, Option->Val, Option->Len ); } else if( NULL != Subnet ) { // save subnet optinos Result = DhcpRegSaveSubnetOption( Subnet, Option->OptId, ClassName, VendorName, Option->Val, Option->Len ); } else if( NULL != Server ) { // save global options Result = DhcpRegSaveGlobalOption( Option->OptId, ClassName, VendorName, Option->Val, Option->Len ); } else { // save enterprise wide optinos return ERROR_CALL_NOT_IMPLEMENTED; } if( ERROR_SUCCESS != Result ) return Result; Result = MemArrayNextLoc(OptList, &Loc); } return ERROR_SUCCESS; } DWORD DhcpRegSaveReservationOptions( IN PM_OPTCLASS OptClass, IN PM_RESERVATION Reservation, IN PM_SUBNET Subnet ) { DWORD Result; ARRAY_LOCATION Loc; return SaveArray(&OptClass->Array, DhcpRegSaveOptList, Reservation, Subnet); } DWORD DhcpRegSaveSubnetOptions( IN PM_OPTCLASS OptClass, IN PM_SUBNET Subnet, IN LPVOID Unused ) { return SaveArray(&OptClass->Array, DhcpRegSaveOptList, Subnet, NULL); } DWORD DhcpRegSaveGlobalOptions( IN PM_OPTCLASS OptClass, IN PM_SERVER Server, IN LPVOID Unused ) { return SaveArray(&OptClass->Array, DhcpRegSaveOptList, NULL, Server); } DWORD DhcpRegSaveOptDefList( IN PM_OPTCLASSDEFL_ONE OClassDefL, IN PM_SERVER Server, IN LPVOID Unused ) { DWORD Result; DWORD ClassId; DWORD VendorId; LPWSTR ClassName; LPWSTR VendorName; PM_OPTDEFLIST OptDefList; PM_OPTDEF OptDef; PM_CLASSDEF ClassDef; ARRAY_LOCATION Loc; ClassId = OClassDefL->ClassId; VendorId = OClassDefL->VendorId; OptDefList = &OClassDefL->OptDefList; Result = MemServerGetClassDef( // first find the vendor name Server, VendorId, NULL, 0, NULL, &ClassDef ); if( ERROR_SUCCESS != Result ) { VendorName = NULL; } else { VendorName = ClassDef->Name; Require(ClassDef->IsVendor == TRUE); } Result = MemServerGetClassDef( // now find the class name Server, ClassId, NULL, 0, NULL, &ClassDef ); if( ERROR_SUCCESS != Result ) { ClassName = NULL; } else { ClassName = ClassDef->Name; Require(ClassDef->IsVendor == FALSE); } Result = MemArrayInitLoc(&OptDefList->OptDefArray, &Loc); while( ERROR_FILE_NOT_FOUND != Result ) { //- ERROR_SUCCESS == Result Result = MemArrayGetElement(&OptDefList->OptDefArray, &Loc, &OptDef); //- ERROR_SUCCESS == Result && NULL != OptDef Result = DhcpRegSaveOptDef( OptDef->OptId, ClassName, VendorName, OptDef->OptName, OptDef->OptComment, OptDef->Type, OptDef->OptVal, OptDef->OptValLen ); if( ERROR_SUCCESS != Result ) return Result; Result = MemArrayNextLoc(&OptDefList->OptDefArray, &Loc); } return ERROR_SUCCESS; } DWORD DhcpRegSaveOptDefs( IN PM_OPTCLASSDEFLIST OptDefs, IN PM_SERVER Server, IN LPVOID Unused ) { return SaveArray(&OptDefs->Array, DhcpRegSaveOptDefList, Server, NULL); } DWORD DhcpRegSaveClass( IN PM_CLASSDEF Class, IN PM_SERVER Server, IN LPVOID Unused ) { return DhcpRegSaveClassDef( Class->Name, Class->Comment, (DWORD)Class->IsVendor, Class->ActualBytes, Class->nBytes ); } DWORD DhcpRegSaveClassDefs( IN PM_CLASSDEFLIST ClassDefs, IN PM_SERVER Server, IN LPVOID Unused ) { return SaveArray(&ClassDefs->ClassDefArray, DhcpRegSaveClass, Server, NULL); } DWORD DhcpRegSaveRanges( IN PM_RANGE Range, IN PM_SUBNET Subnet, IN LPVOID Unused ) { DWORD Result; DWORD Zero = 0; return DhcpRegAddRangeEx( Subnet, Range->Start, Range->End, Range->BootpAllocated, Range->MaxBootpAllowed, Range->State, (LPBYTE)&Zero, sizeof(Zero), (LPBYTE)&Zero, sizeof(Zero) ); } DWORD DhcpRegSaveExcls( IN PM_SUBNET Subnet, IN PARRAY Excl ) { DWORD Result; DWORD nElems; DWORD *ExclArray; DWORD i; ARRAY_LOCATION Loc; PM_RANGE ThisRange; nElems = MemArraySize(Excl); ExclArray = MemAlloc(( nElems*2+1) *sizeof(DWORD)); if( NULL == ExclArray ) return ERROR_NOT_ENOUGH_MEMORY; ExclArray[0] = nElems; MemArrayInitLoc(Excl, &Loc); for( i = 0 ; i < nElems ; i ++ ) { MemArrayGetElement(Excl, &Loc, &ThisRange); Require(ThisRange != NULL); ExclArray[2*i+1] = ThisRange->Start; ExclArray[2*i+2] = ThisRange->End; MemArrayNextLoc(Excl, &Loc); } Result = DhcpRegSaveExcl(Subnet, (LPBYTE)ExclArray, sizeof(DWORD)*(nElems*2+1)); MemFree(ExclArray); return Result; } DWORD DhcpRegSaveReservation1( IN PM_RESERVATION Reservation, IN PM_SUBNET Subnet, IN LPVOID Unused ) { DWORD Result; Result = DhcpRegSaveReservation( Subnet->Address, Reservation->Address, Reservation->Flags, Reservation->ClientUID, Reservation->nBytes ); if( ERROR_SUCCESS != Result ) return Result; return DhcpRegSaveReservationOptions( &Reservation->Options, Reservation, Subnet ); } DWORD DhcpRegSaveReservations( IN PM_RESERVATIONS Reservations, IN PM_SUBNET Subnet, IN LPVOID Unused ) { return SaveArray(Reservations, DhcpRegSaveReservation1, Subnet, NULL); } DWORD DhcpRegSaveSubnets( IN PM_SUBNET Subnet, IN PM_SERVER Server, IN LPVOID Unused2 ) { DWORD Result; PM_SSCOPE SScope; if( Subnet->fSubnet ) { Result = DhcpRegSaveSubnet( Subnet->Address, Subnet->Mask, Subnet->State, Subnet->Name, Subnet->Description ); } else { Result = DhcpRegSaveMScope( Subnet->MScopeId, Subnet->State, Subnet->Policy, Subnet->TTL, Subnet->Name, Subnet->Description, Subnet->LangTag, &Subnet->ExpiryTime ); } if( ERROR_SUCCESS != Result ) return Result; Result = SaveArray(&Subnet->Ranges, DhcpRegSaveRanges, Subnet, NULL); if( ERROR_SUCCESS != Result ) return Result; Result = DhcpRegSaveExcls( Subnet, &Subnet->Exclusions ); if( ERROR_SUCCESS != Result ) return Result; Result = DhcpRegSaveSubnetOptions(&Subnet->Options, Subnet, NULL); if( ERROR_SUCCESS != Result ) return Result; Result = DhcpRegSaveReservations(&Subnet->Reservations, Subnet, NULL); if( ERROR_SUCCESS != Result ) return Result; if( 0 == Subnet->SuperScopeId ) return ERROR_SUCCESS; Result = MemServerFindSScope( Server, Subnet->SuperScopeId, NULL, &SScope ); if( ERROR_FILE_NOT_FOUND == Result ) return ERROR_SUCCESS; if( ERROR_SUCCESS != Result ) return Result; Result = DhcpRegSScopeSaveSubnet(SScope->Name, Subnet->Address); if( ERROR_SUCCESS != Result ) return Result; return ERROR_SUCCESS; } DWORD DhcpRegSaveMScopes( IN PM_MSCOPE MScope, IN PM_SERVER Server, IN LPVOID Unused ) { return DhcpRegSaveSubnets(MScope, Server, NULL); } //BeginExport(function) DWORD DhcpRegServerSave( IN PM_SERVER Server ) //EndExport(function) { DWORD Result; #if 0 Result = DhcpRegServerSetAttributes( Hdl, &Server->Name, &Server->Comment, &Server->State ); if( ERROR_SUCCESS != Result ) return Result; #endif Result = SaveArray(&Server->Subnets, DhcpRegSaveSubnets, Server, NULL); if( ERROR_SUCCESS != Result ) return Result; Result = SaveArray(&Server->MScopes, DhcpRegSaveMScopes, Server, NULL); if( ERROR_SUCCESS != Result ) return Result; #if 0 Result = SaveArray(&Server->SuperScopes, DhcpRegSaveSuperScopes, Server, NULL); if( ERROR_SUCCESS != Result ) return Result; #endif Result = DhcpRegSaveGlobalOptions(&Server->Options, Server, NULL); if( ERROR_SUCCESS != Result ) return Result; Result = DhcpRegSaveOptDefs(&Server->OptDefs, Server, NULL); if( ERROR_SUCCESS != Result ) return Result; Result = DhcpRegSaveClassDefs(&Server->ClassDefs, Server, NULL); if( ERROR_SUCCESS != Result ) return Result; return ERROR_SUCCESS; } #if 0 // ---BeginExport(function) DWORD DhcpRegSaveThisServer( IN LPWSTR Location, IN PM_SERVER Server ) // ---EndExport(function) { DWORD Result, Result2; REG_HANDLE Hdl; REG_HANDLE SaveHdl; Result = DhcpRegGetThisServer(&Hdl); if( ERROR_SUCCESS != Result ) return Result; Result = DhcpRegGetNextHdl(&Hdl, Location, &SaveHdl); Result2 = DhcpRegCloseHdl(&Hdl); Require( ERROR_SUCCESS == Result2 ); if( ERROR_SUCCESS != Result ) return Result; DhcpRegSetCurrentServer(&SaveHdl); Result = DhcpRegServerSave(Server); DhcpRegSetCurrentServer(NULL); Result2 = DhcpRegCloseHdl(&SaveHdl); Require( ERROR_SUCCESS == Result2 ); return Result; } #endif //BeginExport(function) DWORD DhcpMigrateMScopes( IN LPCWSTR OldMscopeName, IN LPCWSTR NewMscopeName, IN DWORD (*SaveOrRestoreRoutine)( IN HKEY Key, IN LPWSTR ConfigName, IN BOOL fRestore ) ) //EndExport(function) /*++ Routine Description: This routine attempts to migrate the key stored under OldMscopeName to NewMscopeName name. N.B. It does not delete the old key. Return Values: Win32 error codes --*/ { REG_HANDLE Hdl1, Hdl2, Hdl3; ULONG Error, Error2; Error = DhcpRegGetThisServer(&Hdl1); if( NO_ERROR != Error ) return Error; Error = DhcpRegServerGetMScopeHdl( &Hdl1, (LPWSTR)OldMscopeName, &Hdl2 ); if( NO_ERROR == Error ) { Error2 = DhcpRegServerGetMScopeHdl( &Hdl1, (LPWSTR)NewMscopeName, &Hdl3 ); } DhcpRegCloseHdl(&Hdl1); if( NO_ERROR != Error ) return Error; if( NO_ERROR != Error2 ) { DhcpRegCloseHdl(&Hdl2); return Error2; } Error = SaveOrRestoreRoutine( Hdl2.Key, L"DHCPMSCOPE.CFG", FALSE ); if( NO_ERROR == Error ) { Error = SaveOrRestoreRoutine( Hdl3.Key, L"DHCPMSCOPE.CFG", TRUE ); } DhcpRegCloseHdl(&Hdl2); DhcpRegCloseHdl(&Hdl3); return Error; } //================================================================================ // end of file //================================================================================