Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1052 lines
25 KiB

/**********************************************************************/
/** Microsoft Windows NT **/
/** Copyright(c) Microsoft Corp., 1992 **/
/**********************************************************************/
/*
BindInit.cxx
OLDNAME: NCPAPBNDI.CXX:
Windows/NT Network Control Panel Applet
BINDERY Initialization.
FILE HISTORY:
DavidHov 3/22/92 Created
*/
#include "pch.hxx" // Precompiled header
#pragma hdrstop
#define BNDI_RULE_MAX_SIZE 100000
#define CR '\r'
#define LF '\n'
#define EF '\0x1a'
const TCHAR * pszDefaultSrcPath = SZ("A:\\") ;
/*******************************************************************
NAME: BINDERY::GetTextResource
SYNOPSIS: Read the a TEXT resource from the resource fork
ENTRY:
EXIT:
RETURNS:
NOTES:
HISTORY:
********************************************************************/
CHAR * BINDERY :: GetTextResource ( const TCHAR * pchResName )
{
HRSRC hResource ;
HGLOBAL hRuleResource ;
CHAR * pszResult ;
INT cbSize ;
hResource = ::FindResource( g_hinst,
pchResName,
RGAS_RES_RULES_TYPE ) ;
if ( hResource == NULL )
return NULL ;
cbSize = ::SizeofResource( g_hinst, hResource ) ;
hRuleResource = ::LoadResource( g_hinst, hResource ) ;
if ( hRuleResource == NULL )
return NULL ;
// Note that text files are stored as CHAR by the Resource Compiler
// Do a C++-compatible "strdup". Allow for addition of
// another NUL character at the end of the buffer.
pszResult = new CHAR [ cbSize + (sizeof (CHAR) * 2) ] ;
if ( pszResult )
{
// Move the resource data to the new block
::memcpy( pszResult,
::LockResource( hRuleResource ),
cbSize ) ;
// Guarantee that there's no old-style EOF in the buffer
CHAR * pch = pszResult ;
INT cb ;
for ( cb = 0, pch = pszResult ; cb < cbSize ; cb++, pch++ )
{
if ( *pch == 0 || *pch == EF )
break ;
}
*pch = 0 ;
}
UnlockResource( hRuleResource ) ;
::FreeResource( hRuleResource ) ;
return pszResult ;
}
/*******************************************************************
NAME: BINDERY::AddNcpaDefaultRulesValue
SYNOPSIS: Read the default rules TEXT resource from the resource
fork, convert it Unicode, demarcate its lines and
write the result to a REG_MULTI_SZ value called RawRules.
ENTRY:
EXIT:
RETURNS:
NOTES:
HISTORY:
********************************************************************/
APIERR BINDERY :: AddNcpaDefaultRulesValue ()
{
APIERR err = 0 ;
STRLIST slRules ;
NLS_STR * pnlsNext ;
INT cStrs, cOk ;
CHAR * psz1,
* psz2 ;
CHAR * pszDefaultRules = GetTextResource( RGAS_RES_DEFAULT_RULES_NAME ) ;
if ( pszDefaultRules == NULL )
{
return ERROR_RESOURCE_NAME_NOT_FOUND ;
}
for ( cStrs = 0, psz1 = psz2 = pszDefaultRules ;
*psz1 ;
psz2 = psz1 )
{
for ( cOk = 0 ; *psz1 != 0 ; psz1++ )
{
if ( *psz1 == CR )
break ;
if ( *psz1 >= ' ' )
{
if ( cOk++ == 0 )
psz2 = psz1 ;
}
}
if ( cOk > 0 )
{
pnlsNext = new NLS_STR ;
if ( pnlsNext == NULL )
{
err = ERROR_NOT_ENOUGH_MEMORY ;
break ;
}
CHAR ch = *psz1 ;
*psz1 = 0 ;
err = pnlsNext->MapCopyFrom( psz2 ) ;
*psz1 = ch ;
if ( err )
break ;
if ( err = slRules.Append( pnlsNext ) )
break ;
cStrs++ ;
}
while ( *psz1 == CR || *psz1 == LF )
{
psz1++ ;
}
}
if ( err == 0 && cStrs > 0 )
{
err = _prnNcpa->SetValue( RGAS_RAW_RULES_NAME, & slRules ) ;
}
delete pszDefaultRules ;
return err ;
}
/*******************************************************************
NAME: BINDERY::BINDERY
SYNOPSIS: Constructor of BINDERY. SProlog engine and parent
Registry Manager construct without arguments.
Contains pointer placeholders for results of Registry scans.
ENTRY: Nothing
EXIT: Nothing
RETURNS: Nothing; QueryError() for construction failure.
NOTES:
HISTORY:
********************************************************************/
BINDERY :: BINDERY ( BOOL fMainInstall,
const TCHAR * pszInstallParms )
: _paCompAssoc( NULL ),
_pcdlAdapters( NULL ),
_pcdlTransports( NULL ),
_pcdlServices( NULL ),
_pcdlDrivers( NULL ),
_prnNcpa( NULL ),
_fAdmin( FALSE ),
_pszRuleFileName( NULL ),
_pszRuleData( NULL ),
_bindState( BND_NOT_LOADED )
{
APIERR err = 0,
err2 = 0 ;
NLS_STR nlsNcpa( RGAS_NCPA_HOME ) ;
if ( QueryError() )
{
return ;
}
if ( err = nlsNcpa.QueryError() )
{
ReportError( err ) ;
return ;
}
// Obtain access to the NCPA's home location in the Registry.
// First, try to get WRITE access.
_prnNcpa = new REG_KEY( *_prnLocalMachine,
nlsNcpa,
GENERIC_READ | GENERIC_WRITE ) ;
if ( _prnNcpa == NULL )
{
ReportError( ERROR_NOT_ENOUGH_MEMORY ) ;
return ;
}
// If error, attempt to create the key and its value data.
if ( _prnNcpa->QueryError()
&& _prnNcpa->QueryError() != ERROR_ACCESS_DENIED)
{
delete _prnNcpa ;
_prnNcpa = NULL ;
if ( (err = CreateNcpaRegKey( pszInstallParms )) == 0 )
{
_fAdmin = TRUE ;
}
}
else if ( _prnNcpa->QueryError() == ERROR_ACCESS_DENIED )
{
// Try with read only access
delete _prnNcpa ;
_prnNcpa = NULL ;
_prnNcpa = new REG_KEY( *_prnLocalMachine,
nlsNcpa,
GENERIC_READ ) ;
if ( _prnNcpa == NULL )
{
ReportError( ERROR_NOT_ENOUGH_MEMORY ) ;
return ;
}
}
if ( err )
{
ReportError( IDS_NCPA_REG_FMT_NO_HOME ) ;
return ;
}
// Get the name of the rule file or the rule data value string
err = QueryValueString( _prnNcpa,
RGAS_NCPA_RULE_FILE,
& _pszRuleFileName,
NULL,
MAX_PATH ) ;
if ( (_pszRuleData = GetRulesResource()) == NULL )
err2 = IDS_NCPA_REG_FMT_NO_RULEFILE ;
if ( err != 0 && err2 != 0 )
{
// One or the other MUST be present, or we can't compute bindings
ReportError( IDS_NCPA_REG_FMT_NO_RULEFILE );
return ;
}
}
/*******************************************************************
NAME: BINDERY:: ~ BINDERY
SYNOPSIS: Destructor of BINDERY.
ENTRY:
EXIT:
RETURNS:
NOTES:
HISTORY:
********************************************************************/
BINDERY :: ~ BINDERY ()
{
Reset() ;
delete _pszRuleFileName ;
delete _pszRuleData ;
delete _prnNcpa ;
}
/*******************************************************************
NAME: BINDERY::CreateNcpaRegKey
SYNOPSIS: Create the NCPA's home product location in the
Registry if necessary. Establish all pertinent
values. Extract the SProlog rule set from the
resource fork and write it to the Regsitry.
ENTRY:
EXIT:
RETURNS:
NOTES:
HISTORY:
********************************************************************/
APIERR BINDERY :: CreateNcpaRegKey ( const TCHAR * pszInstallParms )
{
APIERR err ;
REG_KEY * prkProd = NULL ;
REG_KEY_CREATE_STRUCT rkcStruct ;
NLS_STR nlsNcpa( RGAS_NCPA_HOME ) ;
NLS_STR nlsTemp,
nlsSrcPath( pszDefaultSrcPath ),
nlsMt ;
TCHAR * pchTempData = NULL ;
LSPL_PROD_TYPE lspt ;
delete _prnNcpa ;
_prnNcpa = NULL ;
// Get the package product type
if ( LSA_POLICY::QueryProductType( & lspt ) != 0 )
{
// If unanticipated error, assume "Windows NT"
lspt = LSPL_PROD_WIN_NT ;
}
do
{ // pseudo-loop to avoid excessive internal returns.
if ( err = nlsNcpa.QueryError() )
break ;
_prnNcpa = new REG_KEY( *_prnLocalMachine, nlsNcpa ) ;
if ( _prnNcpa == NULL )
{
err = ERROR_NOT_ENOUGH_MEMORY ;
break ;
}
if ( _prnNcpa->QueryError() )
{
// At this point, we know the key does not exist
// or is broken in some way. Attempt to recreate it.
delete _prnNcpa ;
_prnNcpa = NULL ;
rkcStruct.dwTitleIndex = 0 ;
rkcStruct.ulOptions = 0 ;
rkcStruct.ulDisposition = 0 ;
rkcStruct.regSam = 0 ;
rkcStruct.pSecAttr = NULL ;
rkcStruct.nlsClass = RGAS_GENERAL_CLASS ;
#if defined(DEBUG)
if ( lspt == LSPL_PROD_WIN_NT )
{
TRACEEOL( "NCPA/BNDI: Create NCPA Product key for Windows NT" ) ;
}
else
{
TRACEEOL( "NCPA/BNDI: Create NCPA Product key for Lanman NT" ) ;
}
#endif
if ( err = rkcStruct.nlsClass.QueryError() )
break ;
_prnNcpa = new REG_KEY( *_prnLocalMachine, nlsNcpa, & rkcStruct ) ;
if ( _prnNcpa == NULL )
{
err = ERROR_NOT_ENOUGH_MEMORY ;
break ;
}
if ( err = _prnNcpa->QueryError() )
break ;
}
// The key exists.
// Add the INF file path name values as REG_EXPAND_SZ
// NtlanmanInfName = NTLANMAN.INF
err = nlsTemp.CopyFrom( RGAS_VALUE_NTLANMAN_NAME ) ;
if ( err )
break ;
err = _prnNcpa->SetValue( RGAS_VALUE_NTLANMAN_INF,
nlsTemp,
0, NULL, TRUE ) ;
if ( err )
break ;
// NcpaShelInfName = NCPASHEL.INF
err = nlsTemp.CopyFrom( RGAS_VALUE_NCPASHEL_NAME ) ;
if ( err )
break ;
err = _prnNcpa->SetValue( RGAS_VALUE_NCPASHEL_INF,
nlsTemp,
0, NULL, TRUE ) ;
if ( err )
break ;
// Write the default SProlog rules to the RawRules value
if ( err = AddNcpaDefaultRulesValue() )
break ;
// Create an empty prior configuration history. Ignore any error.
_prnNcpa->SetValue( RGAS_NCPA_BIND_FILE_EX, nlsMt ) ;
// If this is main setup, record the setup source path into
// the Registry.
/* BUGBUG - the following routines will need to be added to Setup.cxx
if ( pszInstallParms )
{
// Alter default setup medium source path if present
NCPA_DIALOG::FindSetupParameter( pszInstallParms,
STF_SRCDIR,
& nlsSrcPath );
// If this is an IDW installation, record that fact.
if ( NCPA_DIALOG::FindSetupParameter( pszInstallParms,
STF_IDW,
& nlsTemp )
&& ::stricmpf( STF_TRUE, nlsTemp.QueryPch() ) == 0 )
{
TRACEEOL( SZ("NCPA/BNDI: IDW installation ") );
_prnNcpa->SetValue( RGAS_NCPA_IDW, (DWORD) 1 );
}
}
// See if a review INF was specified. Record it
// or the default value as a REG_MULTI_SZ.
if ( pszInstallParms == NULL
|| ! NCPA_DIALOG::FindSetupParameter( pszInstallParms,
STF_REVIEW,
& nlsTemp ) )
{
nlsTemp = RGAS_VALUE_REVIEW_NAME ;
}
*/
{
STRLIST strLst ;
NLS_STR * pnlsTemp = new NLS_STR( RGAS_VALUE_REVIEW_NAME ) ;
TRACEEOL( SZ("NCPA/BNDI: ReviewPrograms INF is ")
<< nlsTemp.QueryPch() );
if ( pnlsTemp != NULL
&& pnlsTemp->QueryError() == 0
&& strLst.Append( pnlsTemp ) == 0 )
{
_prnNcpa->SetValue( RGAS_REVIEW_INFS,
& strLst );
}
}
}
while ( FALSE ) ;
if ( err )
{
delete _prnNcpa ;
_prnNcpa = NULL ;
}
return err ;
}
/*******************************************************************
NAME: BINDERY::GetRulesResource
SYNOPSIS: Read the SProlog rule file in from the resource fork
and promote it to UNICODE.
ENTRY:
EXIT:
RETURNS:
NOTES: If resource name pointer is NULL, defaults to
standard NCPARULE.SPR rules resource.
HISTORY:
********************************************************************/
TCHAR * BINDERY :: GetRulesResource ( const TCHAR * pszResourceName )
{
CHAR * pchRuleData ;
TCHAR * pszResult ;
UINT cbSize ;
if ( pszResourceName == NULL )
{
pszResourceName = RGAS_RES_RULES_NAME ;
}
if ( (pchRuleData = GetTextResource( pszResourceName )) == NULL )
{
return NULL ;
}
cbSize = strlen( pchRuleData ) ;
pszResult = new TCHAR [ cbSize + (sizeof (TCHAR) * 2) ] ;
if ( pszResult )
{
TCHAR * pcha ;
CHAR * pchb ;
UINT i ;
// Make a copy of the data, guaranteeing that it's zero-terminated.
// NOTE: This also performs a trivial promotion to UNICODE.
for ( i = cbSize,
pcha = pszResult,
pchb = pchRuleData ;
i-- ; *pcha++ = (TCHAR) (*pchb++) ) ;
*pcha++ = 0 ;
}
delete pchRuleData ;
return pszResult ;
}
/*******************************************************************
NAME: BINDERY::ResetInterpreter
SYNOPSIS: Reset the SPROLOG interpreter.
ENTRY:
EXIT:
RETURNS:
NOTES:
HISTORY:
********************************************************************/
APIERR BINDERY :: ResetInterpreter ()
{
_queryEngine.Reset() ;
return 0 ;
}
/*******************************************************************
NAME: BINDERY::IsInteriorBinding
SYNOPSIS: Given a component index and a binding index,
return TRUE if the binding is entirely contained
(is a pure subset of and lacks a head atom) of
at least one other binding.
ENTRY:
EXIT:
RETURNS:
NOTES:
HISTORY:
********************************************************************/
BOOL BINDERY :: IsInteriorBinding ( INT iComp, INT iBind )
{
ASSERT( _paCompAssoc != NULL ) ;
ASSERT( _paCompAssoc->QueryCount() > iComp ) ;
COMP_ASSOC * pComp = & (*_paCompAssoc)[iComp],
* pCompNext ;
INT iCompNext,
iBindNext ;
COMP_BINDING * pBind = NULL,
* pBindNext,
* pBindExterior = NULL ;
{
// Locate the binding in question
ITER_DL_OF( COMP_BINDING ) itBind( pComp->_dlcbBinds ) ;
INT i ;
for ( i = 0 ; (pBind = itBind.Next()) && i < iBind; i++ ) ;
if ( pBind == NULL )
{
TRACEEOL( SZ("NCPA/BIND: Binding ")
<< iComp
<< SZ(", ")
<< iBind
<< SZ(" does not exist") ) ;
return FALSE ; // Bind index is invalid!
}
}
// For each component...
for ( iCompNext = 0 ;
pBindExterior == NULL && iCompNext < _paCompAssoc->QueryCount() ;
iCompNext++ )
{
pCompNext = & (*_paCompAssoc)[iCompNext] ;
ITER_DL_OF( COMP_BINDING ) itBind( pCompNext->_dlcbBinds ) ;
// For each binding...
for ( iBindNext = 0 ;
pBindExterior == NULL && (pBindNext = itBind.Next()) ;
iBindNext++ )
{
// No need to check for the trivial (self <==> self) case;
// it MUST fail, since it's not a proper subset of itself.
if ( pBind->IsInteriorTo( pBindNext,
pComp->_huaDevName,
pCompNext->_huaDevName ) )
pBindExterior = pBindNext ;
}
}
#if defined(DEBUG)
if ( pBindExterior )
{
TRACEEOL( SZ("NCPA/BIND: Binding ")
<< pBind->QueryBindString()
<< SZ(" is interior to ")
<< pBindExterior->QueryBindString() );
}
#endif
return pBindExterior != NULL ;
}
/*******************************************************************
NAME: BINDERY::DetermineInteriorBindings
SYNOPSIS: Iterate over all components and bindings, setting
the "interior" flag on any binding which is
a subset of another binding.
ENTRY:
EXIT:
RETURNS:
NOTES:
HISTORY:
********************************************************************/
VOID BINDERY :: DetermineInteriorBindings ()
{
ASSERT( _paCompAssoc != NULL ) ;
INT cComp = _paCompAssoc->QueryCount(),
iComp,
iBind ;
TRACEEOL( SZ("NCPA/BIND: Determine interior bindings start") ) ;
for ( iComp = 0 ; iComp < cComp ; iComp++ )
{
COMP_ASSOC * pComp = & (*_paCompAssoc)[iComp] ;
ITER_DL_OF( COMP_BINDING ) itBind( pComp->_dlcbBinds ) ;
COMP_BINDING * pBind ;
for ( iBind = 0 ; pBind = itBind.Next() ; iBind++ )
{
pBind->SetInterior( IsInteriorBinding( iComp, iBind ) ) ;
}
}
TRACEEOL( SZ("NCPA/BIND: Determine interior bindings end") ) ;
}
/*******************************************************************
NAME: BINDERY::HandleInteriorBindings
SYNOPSIS: After the user has fiddled with the bindings, determine
if any interior bindings have been implicitly
deactivated.
An interior binding is left "active" iff there exists
at least one active binding to which it is interior.
ENTRY:
EXIT:
RETURNS:
NOTES: This routine depends upon DetermineInteriorBindings()
having been called to set the "interior" flag.
HISTORY:
********************************************************************/
VOID BINDERY :: HandleInteriorBindings ()
{
ASSERT( _paCompAssoc != NULL ) ;
INT cComp = _paCompAssoc->QueryCount(),
iComp,
iBind ;
TRACEEOL( SZ("NCPA/BIND: Handle interior bindings start") ) ;
for ( iComp = 0 ; iComp < cComp ; iComp++ )
{
COMP_ASSOC * pComp = & (*_paCompAssoc)[iComp] ;
ITER_DL_OF( COMP_BINDING ) itBind( pComp->_dlcbBinds ) ;
COMP_BINDING * pBind ;
for ( iBind = 0 ; pBind = itBind.Next() ; iBind++ )
{
// If this binding is interior, do the N**2 search.
if ( pBind->QueryInterior() )
{
pBind->SetState( RequiredInteriorBinding( pComp, pBind ) ) ;
}
}
}
TRACEEOL( SZ("NCPA/BIND: Handle interior bindings end") ) ;
}
/*******************************************************************
NAME: BINDERY::RequiredInteriorBinding
SYNOPSIS: Helper routine to HandleInteriorBindings(). Determine
if a single interior binding is still required; i.e.,
do any of its exterior bindings remain active?
ENTRY:
EXIT:
RETURNS:
NOTES: This routine depends upon DetermineInteriorBindings()
having been called to set the "interior" flag.
HISTORY:
********************************************************************/
BOOL BINDERY :: RequiredInteriorBinding (
COMP_ASSOC * pCompCheck,
COMP_BINDING * pBindCheck )
{
ASSERT( _paCompAssoc != NULL && pBindCheck->QueryInterior() ) ;
INT cComp = _paCompAssoc->QueryCount(),
iComp ;
BOOL fResult = FALSE ;
COMP_ASSOC * pComp ;
for ( iComp = 0 ; (! fResult) && iComp < cComp ; iComp++ )
{
pComp = & (*_paCompAssoc)[iComp] ;
if ( pComp == pCompCheck )
continue ;
ITER_DL_OF( COMP_BINDING ) itBind( pComp->_dlcbBinds ) ;
COMP_BINDING * pBind ;
while ( (! fResult) && (pBind = itBind.Next()) )
{
if ( pBind->QueryState()
&& pBindCheck->IsInteriorTo( pBind,
pCompCheck->_huaDevName,
pComp->_huaDevName ) )
fResult = TRUE ;
}
}
return fResult ;
}
/*******************************************************************
NAME: BINDERY::GetNcpaValueString
SYNOPSIS:
ENTRY:
EXIT:
RETURNS:
NOTES: Get one of the NCPA's configured
REG_EXPAND_SZ strings.
HISTORY:
********************************************************************/
APIERR BINDERY :: GetNcpaValueString (
const TCHAR * pszValue,
NLS_STR * pnlsResult )
{
APIERR err = _prnNcpa->QueryValue( pszValue,
pnlsResult,
0, NULL, TRUE ) ;
#if defined(DEBUG)
if ( err )
{
TRACEEOL( SZ("NCPA/BINDERY: failed to read NCPA key value ")
<< pszValue );
SetLastErrorName( pszValue ) ;
}
else
{
TRACEEOL( SZ("NCPA/BINDERY: NCPA key value ")
<< pszValue
<< SZ(" retrieved was ")
<< pnlsResult->QueryPch() ) ;
}
#endif
return err ;
}
/*******************************************************************
NAME: BINDERY::GetNcpaValueNumber
SYNOPSIS:
ENTRY:
EXIT:
RETURNS:
NOTES:
HISTORY:
********************************************************************/
APIERR BINDERY :: GetNcpaValueNumber (
const TCHAR * pszValue,
DWORD * pdwValue )
{
APIERR err = _prnNcpa->QueryValue( pszValue,
pdwValue ) ;
#if defined(DEBUG)
if ( err )
{
TRACEEOL( SZ("NCPA/BINDERY: failed to read NCPA key value ")
<< pszValue );
SetLastErrorName( pszValue ) ;
}
else
{
TRACEEOL( SZ("NCPA/BINDERY: NCPA key value ")
<< pszValue
<< SZ(" retrieved was ")
<< *pdwValue ) ;
}
#endif
return err ;
}
/*******************************************************************
NAME: BINDERY::QueryCfgDirty
SYNOPSIS: See SetCfgDirty().
ENTRY: Nothing
EXIT: Nothing
RETURNS: TRUE if configuration is in need of a reboot.
NOTES:
HISTORY:
********************************************************************/
BOOL BINDERY :: QueryCfgDirty ()
{
NLS_STR nlsDirtyKeyName( RGAS_NCPA_CFG_DIRTY_KEY_NAME );
ASSERT( _prnNcpa != NULL ) ;
REG_KEY rkDirty( *_prnNcpa, nlsDirtyKeyName ) ;
return rkDirty.QueryError() == 0 ;
}
/*******************************************************************
NAME: BINDERY::SetCfgDirty
SYNOPSIS: A volatile Registry key is created to indicate
that the current configuration needs to be rebooted.
This routine either creates or destroys that key.
ENTRY: BOOL fDirty state configuration should be in
EXIT: Nothing
RETURNS: TRUE if configuration was dirty.
NOTES:
HISTORY:
********************************************************************/
BOOL BINDERY :: SetCfgDirty ( BOOL fDirty )
{
APIERR err = 0 ;
NLS_STR nlsDirtyKeyName( RGAS_NCPA_CFG_DIRTY_KEY_NAME );
ASSERT( _prnNcpa != NULL ) ;
REG_KEY rkDirty( *_prnNcpa, nlsDirtyKeyName ) ;
BOOL fWasDirty = rkDirty.QueryError() == 0 ;
if ( fWasDirty ^ fDirty )
{
// State doesn't match user's desire.
// Do we create or destroy?
if ( fDirty )
{
// Create
REG_KEY_CREATE_STRUCT rkcStruct ;
rkcStruct.dwTitleIndex = 0 ;
rkcStruct.ulOptions = REG_OPTION_VOLATILE ;
rkcStruct.ulDisposition = 0 ;
rkcStruct.regSam = 0 ;
rkcStruct.pSecAttr = NULL ;
rkcStruct.nlsClass = RGAS_GENERAL_CLASS ;
REG_KEY rkNewDirty( *_prnNcpa,
nlsDirtyKeyName,
& rkcStruct ) ;
err = rkNewDirty.QueryError() ;
#if defined(TRACE)
if ( err )
{
TRACEEOL( "NCPA/BNDI: Creation of cfg dirty key returned "
<< err ) ;
}
#endif
}
else
{
// Destroy
err = rkDirty.Delete() ;
#if defined(TRACE)
if ( err )
{
TRACEEOL( "NCPA/BNDI: Deletion of cfg dirty key returned "
<< err ) ;
}
#endif
}
}
return fWasDirty ;
}
// End of NCPABNDI.CXX