1383 lines
29 KiB
C++
1383 lines
29 KiB
C++
// File : HMObject.inl
|
|
//
|
|
// Copyright (c) 2000 Microsoft Corporation
|
|
//
|
|
// 03/25/00 v-marfin 60298 Delete source object after a drag and drop MOVE
|
|
// operation.
|
|
// 04/05/00 v-marfin 59492b Also check for object type of CActionPolicy
|
|
// in Refresh function so this type is not deleted as
|
|
// part of the refresh.
|
|
//
|
|
//
|
|
//
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#endif
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Create/Destroy
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
inline void CHMObject::Destroy(bool bDeleteClassObject /*= false*/)
|
|
{
|
|
TRACEX(_T("CHMObject::Destroy\n"));
|
|
|
|
if( bDeleteClassObject )
|
|
{
|
|
DeleteClassObject();
|
|
}
|
|
|
|
for( int i = 0; i < GetChildCount(); i++ )
|
|
{
|
|
CHMObject* pObject = GetChild(i);
|
|
if( pObject )
|
|
{
|
|
pObject->Destroy(bDeleteClassObject);
|
|
delete pObject;
|
|
m_Children.SetAt(i,NULL);
|
|
}
|
|
}
|
|
m_Children.RemoveAll();
|
|
|
|
RemoveAllScopeItems();
|
|
|
|
// TODO : Destroy Events
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// WMI Operations
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
inline HRESULT CHMObject::EnumerateChildren()
|
|
{
|
|
TRACEX(_T("CHMObject::EnumerateChildren\n"));
|
|
|
|
// ASSERT(FALSE);
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
inline CString CHMObject::GetObjectPath()
|
|
{
|
|
TRACEX(_T("CHMObject::GetObjectPath\n"));
|
|
|
|
return _T("");
|
|
}
|
|
|
|
inline CString CHMObject::GetStatusObjectPath()
|
|
{
|
|
TRACEX(_T("CHMObject::GetStatusObjectPath\n"));
|
|
|
|
return _T("");
|
|
}
|
|
|
|
inline CWbemClassObject* CHMObject::GetClassObject()
|
|
{
|
|
TRACEX(_T("CHMObject::GetClassObject\n"));
|
|
|
|
CWbemClassObject* pClassObject = new CWbemClassObject;
|
|
|
|
if( ! CHECKHRESULT(pClassObject->Create(GetSystemName())) )
|
|
{
|
|
delete pClassObject;
|
|
return NULL;
|
|
}
|
|
|
|
if( ! CHECKHRESULT(pClassObject->GetObject(GetObjectPath())) )
|
|
{
|
|
delete pClassObject;
|
|
return NULL;
|
|
}
|
|
|
|
return pClassObject;
|
|
}
|
|
|
|
inline CWbemClassObject* CHMObject::GetParentClassObject()
|
|
{
|
|
TRACEX(_T("CHMObject::GetParentClassObject\n"));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
inline CHMEvent* CHMObject::GetStatusClassObject()
|
|
{
|
|
TRACEX(_T("CHMObject::GetStatusClassObject\n"));
|
|
|
|
CHMEvent* pClassObject = new CHMEvent;
|
|
|
|
pClassObject->SetMachineName(GetSystemName());
|
|
|
|
if( ! CHECKHRESULT(pClassObject->GetObject(GetStatusObjectPath())) )
|
|
{
|
|
delete pClassObject;
|
|
return NULL;
|
|
}
|
|
|
|
return pClassObject;
|
|
}
|
|
|
|
inline void CHMObject::DeleteClassObject()
|
|
{
|
|
TRACEX(_T("CHMObject::DeleteClassObject\n"));
|
|
|
|
if( GetObjectPath().IsEmpty() ) // check if there is a WMI class associated to this object
|
|
{
|
|
return;
|
|
}
|
|
|
|
CWbemClassObject HMSystemConfig;
|
|
HMSystemConfig.Create(GetSystemName());
|
|
|
|
if( ! CHECKHRESULT(HMSystemConfig.GetObject(IDS_STRING_MOF_SYSTEM_CONFIG)) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
int iResult = 0;
|
|
CString sArgValue;
|
|
sArgValue.Format(_T("{%s}"),GetGuid());
|
|
if( ! CHECKHRESULT(HMSystemConfig.ExecuteMethod(_T("Delete"),_T("TargetGUID"),sArgValue,iResult)) )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
inline bool CHMObject::ImportMofFile(CArchive& ar)
|
|
{
|
|
TRACEX(_T("CHMObject::ImportMofFile\n"));
|
|
|
|
ASSERT(ar.IsLoading());
|
|
|
|
return true;
|
|
}
|
|
|
|
inline bool CHMObject::ExportMofFile(CArchive& ar)
|
|
{
|
|
TRACEX(_T("CHMObject::ExportMofFile\n"));
|
|
|
|
ASSERT(ar.IsStoring());
|
|
|
|
CString sText;
|
|
|
|
CWbemClassObject* pClassObject = GetClassObject();
|
|
|
|
pClassObject->GetObjectText(sText);
|
|
|
|
USES_CONVERSION;
|
|
char* pszAnsiText = T2A(sText);
|
|
|
|
ar.Write(pszAnsiText,strlen(pszAnsiText)+sizeof(char));
|
|
|
|
/*
|
|
for( int i = 0; i < GetChildCount(); i++ )
|
|
{
|
|
CHMObject* pObject = GetChild(i);
|
|
if( pObject )
|
|
{
|
|
pObject->ExportMofFile(ar);
|
|
}
|
|
}
|
|
*/
|
|
return true;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Clipboard Operations
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
inline bool CHMObject::Copy(CString& sParentGuid, COleSafeArray& Instances)
|
|
{
|
|
TRACEX(_T("CHMObject::Copy\n"));
|
|
|
|
CWbemClassObject SystemConfigObject;
|
|
|
|
SystemConfigObject.Create(GetSystemName());
|
|
|
|
HRESULT hr = SystemConfigObject.GetObject(IDS_STRING_MOF_SYSTEM_CONFIG);
|
|
if( ! CHECKHRESULT(hr) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
CWbemClassObject CopyMethodIn;
|
|
CWbemClassObject CopyMethodOut;
|
|
hr = SystemConfigObject.GetMethod(_T("Copy"),CopyMethodIn);
|
|
if( ! CHECKHRESULT(hr) )
|
|
{
|
|
CnxDisplayErrorMsgBox(hr,GetSystemName());
|
|
return false;
|
|
}
|
|
|
|
CString sGuid = _T("{") + GetGuid() + _T("}");
|
|
CopyMethodIn.SetProperty(_T("TargetGUID"),sGuid);
|
|
|
|
hr = SystemConfigObject.ExecuteMethod(_T("Copy"),CopyMethodIn,CopyMethodOut);
|
|
if( ! CHECKHRESULT(hr) )
|
|
{
|
|
CnxDisplayErrorMsgBox(hr,GetSystemName());
|
|
return false;
|
|
}
|
|
|
|
CopyMethodOut.GetProperty(_T("OriginalParentGuid"),sParentGuid);
|
|
CopyMethodOut.GetProperty(_T("Instances"),Instances);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Move operations result in bIsOperationCopy = 0
|
|
// Copy operations result in bIsOperationCopy = 1
|
|
inline bool CHMObject::Paste(CHMObject* pObject, bool bIsOperationCopy)
|
|
{
|
|
TRACEX(_T("CHMObject::Paste\n"));
|
|
TRACEARGn(pObject);
|
|
TRACEARGn(bIsOperationCopy);
|
|
|
|
if( ! GfxCheckObjPtr(pObject,CHMObject) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
COleSafeArray Instances;
|
|
CString sParentGuid;
|
|
|
|
if( ! pObject->Copy(sParentGuid,Instances) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
CWbemClassObject SystemConfigObject;
|
|
|
|
SystemConfigObject.Create(GetSystemName());
|
|
|
|
HRESULT hr = SystemConfigObject.GetObject(IDS_STRING_MOF_SYSTEM_CONFIG);
|
|
if( ! CHECKHRESULT(hr) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
CWbemClassObject PasteMethodIn;
|
|
CWbemClassObject PasteMethodOut;
|
|
hr = SystemConfigObject.GetMethod(_T("Paste"),PasteMethodIn);
|
|
if( ! CHECKHRESULT(hr) )
|
|
{
|
|
CnxDisplayErrorMsgBox(hr,GetSystemName());
|
|
return false;
|
|
}
|
|
|
|
CString sGuid;
|
|
if( GetGuid() == _T("@") )
|
|
sGuid = GetGuid();
|
|
else
|
|
sGuid = _T("{") + GetGuid() + _T("}");
|
|
PasteMethodIn.SetProperty(_T("TargetGUID"),sGuid);
|
|
PasteMethodIn.SetProperty(_T("ForceReplace"),false);
|
|
PasteMethodIn.SetProperty(_T("OriginalSystem"),pObject->GetSystemName());
|
|
PasteMethodIn.SetProperty(_T("OriginalParentGuid"),sParentGuid);
|
|
PasteMethodIn.SetProperty(_T("Instances"),Instances);
|
|
|
|
hr = SystemConfigObject.ExecuteMethod(_T("Paste"),PasteMethodIn,PasteMethodOut);
|
|
if( ! CHECKHRESULT(hr) )
|
|
{
|
|
CnxDisplayErrorMsgBox(hr,GetSystemName());
|
|
return false;
|
|
}
|
|
|
|
// v-marfin : 60298 -----------------------------------------
|
|
// If this is a move operation, delete the source object.
|
|
// RemoveScopeItem instead?
|
|
if (!bIsOperationCopy)
|
|
{
|
|
// Call the scope item's OnDelete() function which will do the
|
|
// necessary cleanup etc.
|
|
CHMScopeItem* pItem = (CHMScopeItem*)pObject->GetScopeItem(0);
|
|
pItem->OnDelete(FALSE); // FALSE = skip the confirmation prompt
|
|
}
|
|
//-----------------------------------------------------------
|
|
|
|
Refresh();
|
|
|
|
return true;
|
|
}
|
|
|
|
inline bool CHMObject::QueryPaste(CHMObject* pObject)
|
|
{
|
|
TRACEX(_T("CHMObject::GetGuid\n"));
|
|
TRACEARGn(pObject);
|
|
|
|
if( ! GfxCheckObjPtr(pObject,CHMObject) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
CString sTargetClass = GetRuntimeClass()->m_lpszClassName;
|
|
CString sSourceClass = pObject->GetRuntimeClass()->m_lpszClassName;
|
|
if( sTargetClass == _T("CSystemGroup") )
|
|
{
|
|
if( sSourceClass != _T("CSystem") )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if( sTargetClass == _T("CSystem") )
|
|
{
|
|
if( sSourceClass == _T("CSystem") || sSourceClass == _T("CSystemGroup") )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if( sTargetClass == _T("CDataGroup") )
|
|
{
|
|
if( sSourceClass == _T("CSystem") || sSourceClass == _T("CSystemGroup") )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if( sTargetClass == _T("CDataElement") )
|
|
{
|
|
if( sSourceClass != _T("CRule") )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if( sTargetClass == _T("CRule") )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Operations
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
inline CString CHMObject::GetGuid()
|
|
{
|
|
TRACEX(_T("CHMObject::GetGuid\n"));
|
|
|
|
return m_sGUID;
|
|
}
|
|
|
|
inline void CHMObject::SetGuid(const CString& sGuid)
|
|
{
|
|
TRACEX(_T("CHMObject::SetGuid\n"));
|
|
TRACEARGs(m_sGUID);
|
|
|
|
m_sGUID = sGuid;
|
|
|
|
if( m_sGUID[0] == _T('{') )
|
|
{
|
|
m_sGUID.TrimLeft(_T('{'));
|
|
m_sGUID.TrimRight(_T('}'));
|
|
}
|
|
}
|
|
|
|
inline CString CHMObject::GetName()
|
|
{
|
|
TRACEX(_T("CHMObject::GetName\n"));
|
|
|
|
return m_sName;
|
|
}
|
|
|
|
inline void CHMObject::SetName(const CString& sNewName)
|
|
{
|
|
TRACEX(_T("CHMObject::SetName\n"));
|
|
TRACEARGs(sNewName);
|
|
|
|
m_sName = sNewName;
|
|
}
|
|
|
|
inline bool CHMObject::Rename(const CString& sNewName)
|
|
{
|
|
TRACEX(_T("CHMObject::Rename\n"));
|
|
TRACEARGs(sNewName);
|
|
|
|
if( ! GetScopeItemCount() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// rename the WMI instance...if this object has one
|
|
if( GetObjectPath().IsEmpty() ) // no object path, so just rename the scope items and the object
|
|
{
|
|
CHMScopeItem* pParentItem = (CHMScopeItem*)GetScopeItem(0)->GetParent();
|
|
|
|
if( pParentItem )
|
|
{
|
|
CHMObject* pParentObject = pParentItem->GetObjectPtr();
|
|
if( pParentObject )
|
|
{
|
|
if( pParentObject->GetChild(sNewName) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_sName = sNewName;
|
|
|
|
for(int i = 0; i < m_ScopeItems.GetSize(); i++)
|
|
{
|
|
m_ScopeItems[i]->SetDisplayName(0,m_sName);
|
|
m_ScopeItems[i]->SetItem();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
CWbemClassObject WmiInstance;
|
|
|
|
if( ! CHECKHRESULT(WmiInstance.Create(GetSystemName())) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if( ! CHECKHRESULT(WmiInstance.GetObject(GetObjectPath())) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
CHMScopeItem* pParentItem = (CHMScopeItem*)GetScopeItem(0)->GetParent();
|
|
|
|
if( pParentItem )
|
|
{
|
|
CHMObject* pParentObject = pParentItem->GetObjectPtr();
|
|
if( pParentObject )
|
|
{
|
|
if( pParentObject->GetChild(sNewName) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
WmiInstance.SetProperty(IDS_STRING_MOF_NAME,sNewName);
|
|
|
|
if( ! CHECKHRESULT(WmiInstance.SaveAllProperties()) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
m_sName = sNewName;
|
|
|
|
for(int i = 0; i < m_ScopeItems.GetSize(); i++)
|
|
{
|
|
m_ScopeItems[i]->SetDisplayName(0,m_sName);
|
|
m_ScopeItems[i]->SetItem();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
inline CString CHMObject::GetTypeName()
|
|
{
|
|
TRACEX(_T("CHMObject::GetTypeName\n"));
|
|
|
|
return m_sTypeName;
|
|
}
|
|
|
|
inline CString CHMObject::GetUITypeName()
|
|
{
|
|
TRACEX(_T("CHMObject::GetUITypeName\n"));
|
|
|
|
return _T("");
|
|
}
|
|
|
|
inline CString CHMObject::GetSystemName()
|
|
{
|
|
TRACEX(_T("CHMObject::GetSystemName\n"));
|
|
|
|
return m_sSystemName;
|
|
}
|
|
|
|
inline void CHMObject::SetSystemName(const CString& sNewSystemName)
|
|
{
|
|
TRACEX(_T("CHMObject::SetSystemName\n"));
|
|
TRACEARGs(sNewSystemName);
|
|
|
|
m_sSystemName = sNewSystemName;
|
|
}
|
|
|
|
inline void CHMObject::ToggleStatusIcon(bool bOn /*= true*/)
|
|
{
|
|
TRACEX(_T("CHMObject::ToggleStatusIcon\n"));
|
|
TRACEARGn(m_bStatusIconsOn);
|
|
|
|
m_bStatusIconsOn = bOn;
|
|
|
|
for(int i = 0; i < m_ScopeItems.GetSize(); i++)
|
|
{
|
|
// turn status icons on/off
|
|
}
|
|
}
|
|
|
|
inline bool CHMObject::Refresh()
|
|
{
|
|
TRACEX(_T("CHMObject::Refresh\n"));
|
|
|
|
for( int i = GetChildCount()-1; i >= 0; i-- )
|
|
{
|
|
CHMObject* pChildObject = GetChild(i);
|
|
|
|
// v-marfin 59492b : Also check to see if object is a CActionPolicy ("Actions")
|
|
// item since we added an object path to it before so that
|
|
// handling of individual action states would work.
|
|
if( pChildObject &&
|
|
!pChildObject->GetObjectPath().IsEmpty() &&
|
|
!pChildObject->IsActionsItem()) // 59492b
|
|
{
|
|
pChildObject->DestroyAllChildren();
|
|
DestroyChild(i);
|
|
}
|
|
else
|
|
{
|
|
pChildObject->Refresh();
|
|
}
|
|
}
|
|
|
|
EnumerateChildren();
|
|
UpdateStatus();
|
|
|
|
return true;
|
|
}
|
|
|
|
inline bool CHMObject::ResetStatus()
|
|
{
|
|
TRACEX(_T("CHMObject::ResetStatus\n"));
|
|
|
|
if( GetObjectPath().IsEmpty() ) // check if there is a WMI class associated to this object
|
|
{
|
|
return false;
|
|
}
|
|
|
|
CWbemClassObject HMSystemConfig;
|
|
HMSystemConfig.Create(GetSystemName());
|
|
|
|
if( ! CHECKHRESULT(HMSystemConfig.GetObject(IDS_STRING_MOF_SYSTEM_CONFIG)) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int iResult = 0;
|
|
CString sArgValue;
|
|
if( GetGuid() == _T("@") )
|
|
sArgValue.Format(_T("%s"),GetGuid());
|
|
else
|
|
sArgValue.Format(_T("{%s}"),GetGuid());
|
|
if( ! CHECKHRESULT(HMSystemConfig.ExecuteMethod(_T("ResetDataCollectorState"),_T("TargetGUID"),sArgValue,iResult)) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
inline bool CHMObject::ResetStatistics()
|
|
{
|
|
TRACEX(_T("CHMObject::ResetStatistics\n"));
|
|
|
|
if( GetObjectPath().IsEmpty() ) // check if there is a WMI class associated to this object
|
|
{
|
|
return false;
|
|
}
|
|
|
|
CWbemClassObject HMSystemConfig;
|
|
HMSystemConfig.Create(GetSystemName());
|
|
|
|
if( ! CHECKHRESULT(HMSystemConfig.GetObject(IDS_STRING_MOF_SYSTEM_CONFIG)) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int iResult = 0;
|
|
CString sArgValue;
|
|
if( GetGuid() == _T("@") )
|
|
sArgValue.Format(_T("%s"),GetGuid());
|
|
else
|
|
sArgValue.Format(_T("{%s}"),GetGuid());
|
|
if( ! CHECKHRESULT(HMSystemConfig.ExecuteMethod(_T("ResetDataCollectorStatistics"),_T("TargetGUID"),sArgValue,iResult)) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
inline bool CHMObject::CheckNow()
|
|
{
|
|
TRACEX(_T("CHMObject::ResetStatistics\n"));
|
|
|
|
if( GetObjectPath().IsEmpty() ) // check if there is a WMI class associated to this object
|
|
{
|
|
return false;
|
|
}
|
|
|
|
CWbemClassObject HMSystemConfig;
|
|
HMSystemConfig.Create(GetSystemName());
|
|
|
|
if( ! CHECKHRESULT(HMSystemConfig.GetObject(IDS_STRING_MOF_SYSTEM_CONFIG)) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int iResult = 0;
|
|
CString sArgValue;
|
|
if( GetGuid() == _T("@") )
|
|
sArgValue.Format(_T("%s"),GetGuid());
|
|
else
|
|
sArgValue.Format(_T("{%s}"),GetGuid());
|
|
if( ! CHECKHRESULT(HMSystemConfig.ExecuteMethod(_T("EvaluateDataCollectorNow"),_T("TargetGUID"),sArgValue,iResult)) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
inline bool CHMObject::DeleteActionAssoc(const CString& sActionConfigGuid)
|
|
{
|
|
CWbemClassObject SystemConfigObject;
|
|
|
|
SystemConfigObject.Create(GetSystemName());
|
|
|
|
HRESULT hr = SystemConfigObject.GetObject(IDS_STRING_MOF_SYSTEM_CONFIG);
|
|
if( ! CHECKHRESULT(hr) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
CWbemClassObject MethodIn;
|
|
CWbemClassObject MethodOut;
|
|
hr = SystemConfigObject.GetMethod(_T("DeleteConfigurationActionAssociation"),MethodIn);
|
|
if( ! CHECKHRESULT(hr) )
|
|
{
|
|
CnxDisplayErrorMsgBox(hr,GetSystemName());
|
|
return false;
|
|
}
|
|
|
|
CString sGuid = _T("{") + GetGuid() + _T("}");
|
|
MethodIn.SetProperty(_T("TargetGUID"),sGuid);
|
|
MethodIn.SetProperty(_T("ActionGUID"),sActionConfigGuid);
|
|
|
|
|
|
hr = SystemConfigObject.ExecuteMethod(_T("DeleteConfigurationActionAssociation"),MethodIn,MethodOut);
|
|
if( ! CHECKHRESULT(hr) )
|
|
{
|
|
CnxDisplayErrorMsgBox(hr,GetSystemName());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
inline int CHMObject::IsEnabled()
|
|
{
|
|
TRACEX(_T("CHMObject::IsEnabled\n"));
|
|
|
|
if( GetObjectPath().IsEmpty() ) // check if there is a WMI class associated to this object
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return( GetState() != HMS_DISABLED );
|
|
}
|
|
|
|
inline void CHMObject::Enable()
|
|
{
|
|
TRACEX(_T("CHMObject::Enable\n"));
|
|
|
|
if( GetObjectPath().IsEmpty() ) // check if there is a WMI class associated to this object
|
|
{
|
|
return;
|
|
}
|
|
|
|
CWbemClassObject* pClassObject = GetClassObject();
|
|
if( ! pClassObject )
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool bEnabled = true;
|
|
HRESULT hr = pClassObject->SetProperty(IDS_STRING_MOF_ENABLE,bEnabled);
|
|
pClassObject->SaveAllProperties();
|
|
delete pClassObject;
|
|
|
|
if( ! CHECKHRESULT(hr) )
|
|
{
|
|
TRACE(_T("FAILED : CWbemClassObject::SetProperty failed.\n"));
|
|
}
|
|
}
|
|
|
|
inline void CHMObject::Disable()
|
|
{
|
|
TRACEX(_T("CHMObject::Disable\n"));
|
|
|
|
if( GetObjectPath().IsEmpty() ) // check if there is a WMI class associated to this object
|
|
{
|
|
return;
|
|
}
|
|
|
|
CWbemClassObject* pClassObject = GetClassObject();
|
|
if( ! pClassObject )
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool bEnabled = false;
|
|
HRESULT hr = pClassObject->SetProperty(IDS_STRING_MOF_ENABLE,bEnabled);
|
|
pClassObject->SaveAllProperties();
|
|
delete pClassObject;
|
|
|
|
if( ! CHECKHRESULT(hr) )
|
|
{
|
|
TRACE(_T("FAILED : CWbemClassObject::SetProperty failed.\n"));
|
|
}
|
|
}
|
|
|
|
inline CTime CHMObject::GetCreateDateTime()
|
|
{
|
|
TRACEX(_T("CHMObject::GetCreateDateTime\n"));
|
|
|
|
return m_CreateDateTime;
|
|
}
|
|
|
|
inline void CHMObject::GetCreateDateTime(CString& sDateTime)
|
|
{
|
|
TRACEX(_T("CHMObject::GetCreateDateTime\n"));
|
|
TRACEARGs(sDateTime);
|
|
|
|
SYSTEMTIME st;
|
|
|
|
m_CreateDateTime.GetAsSystemTime(st);
|
|
|
|
CString sTime;
|
|
CString sDate;
|
|
|
|
int iLen = GetTimeFormat(LOCALE_USER_DEFAULT,0L,&st,NULL,NULL,0);
|
|
iLen = GetTimeFormat(LOCALE_USER_DEFAULT,0L,&st,NULL,sTime.GetBuffer(iLen+(sizeof(TCHAR)*1)),iLen);
|
|
sTime.ReleaseBuffer();
|
|
|
|
iLen = GetDateFormat(LOCALE_USER_DEFAULT,0L,&st,NULL,NULL,0);
|
|
iLen = GetDateFormat(LOCALE_USER_DEFAULT,0L,&st,NULL,sDate.GetBuffer(iLen+(sizeof(TCHAR)*1)),iLen);
|
|
sDate.ReleaseBuffer();
|
|
|
|
sDateTime = sDate + _T(" ") + sTime;
|
|
}
|
|
|
|
inline void CHMObject::SetCreateDateTime(const CTime& dtime)
|
|
{
|
|
TRACEX(_T("CHMObject::SetCreateDateTime\n"));
|
|
|
|
m_CreateDateTime = dtime;
|
|
}
|
|
|
|
inline CTime CHMObject::GetModifiedDateTime()
|
|
{
|
|
TRACEX(_T("CHMObject::GetModifiedDateTime\n"));
|
|
|
|
return m_ModifiedDateTime;
|
|
}
|
|
|
|
inline void CHMObject::GetModifiedDateTime(CString& sDateTime)
|
|
{
|
|
TRACEX(_T("CHMObject::GetModifiedDateTime\n"));
|
|
TRACEARGs(sDateTime);
|
|
|
|
SYSTEMTIME st;
|
|
|
|
m_ModifiedDateTime.GetAsSystemTime(st);
|
|
|
|
CString sTime;
|
|
CString sDate;
|
|
|
|
int iLen = GetTimeFormat(LOCALE_USER_DEFAULT,0L,&st,NULL,NULL,0);
|
|
iLen = GetTimeFormat(LOCALE_USER_DEFAULT,0L,&st,NULL,sTime.GetBuffer(iLen+(sizeof(TCHAR)*1)),iLen);
|
|
sTime.ReleaseBuffer();
|
|
|
|
iLen = GetDateFormat(LOCALE_USER_DEFAULT,0L,&st,NULL,NULL,0);
|
|
iLen = GetDateFormat(LOCALE_USER_DEFAULT,0L,&st,NULL,sDate.GetBuffer(iLen+(sizeof(TCHAR)*1)),iLen);
|
|
sDate.ReleaseBuffer();
|
|
|
|
sDateTime = sDate + _T(" ") + sTime;
|
|
}
|
|
|
|
inline void CHMObject::SetModifiedDateTime(const CTime& dtime)
|
|
{
|
|
TRACEX(_T("CHMObject::SetModifiedDateTime\n"));
|
|
|
|
m_ModifiedDateTime = dtime;
|
|
}
|
|
|
|
inline CString CHMObject::GetComment()
|
|
{
|
|
TRACEX(_T("CHMObject::GetComment\n"));
|
|
|
|
return m_sComment;
|
|
}
|
|
|
|
inline void CHMObject::SetComment(const CString& sComment)
|
|
{
|
|
TRACEX(_T("CHMObject::SetComment\n"));
|
|
|
|
m_sComment = sComment;
|
|
}
|
|
|
|
inline void CHMObject::UpdateComment(const CString& sComment)
|
|
{
|
|
TRACEX(_T("CHMObject::UpdateComment\n"));
|
|
|
|
SetComment(sComment);
|
|
|
|
for( int i = 0; i < GetScopeItemCount(); i++ )
|
|
{
|
|
CStringArray& saNames = GetScopeItem(i)->GetDisplayNames();
|
|
if( saNames.GetSize() > 1 )
|
|
{
|
|
GetScopeItem(i)->SetDisplayName((int)saNames.GetUpperBound(),sComment);
|
|
GetScopeItem(i)->SetItem();
|
|
}
|
|
}
|
|
|
|
// update the comment for the WMI instance...if this object has one
|
|
if( GetObjectPath().IsEmpty() ) // no object path, so just return
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
CWbemClassObject* pClassObject = GetClassObject();
|
|
if( pClassObject )
|
|
{
|
|
pClassObject->SetProperty(IDS_STRING_MOF_DESCRIPTION,m_sComment);
|
|
pClassObject->SaveAllProperties();
|
|
delete pClassObject;
|
|
}
|
|
}
|
|
|
|
inline void CHMObject::Serialize(CArchive& ar)
|
|
{
|
|
TRACEX(_T("CHMObject::Serialize\n"));
|
|
|
|
CCmdTarget::Serialize(ar);
|
|
|
|
if( ar.IsStoring() )
|
|
{
|
|
ar << GetName();
|
|
}
|
|
else
|
|
{
|
|
CString sName;
|
|
ar >> sName;
|
|
SetName(sName);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Child Members
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
inline int CHMObject::GetChildCount(CRuntimeClass* pClass /*=NULL*/)
|
|
{
|
|
TRACEX(_T("CHMObject::GetChildCount\n"));
|
|
TRACEARGn(pClass);
|
|
|
|
if( ! pClass )
|
|
{
|
|
return (int)m_Children.GetSize();
|
|
}
|
|
|
|
int iCount = 0;
|
|
for( int i = 0; i < m_Children.GetSize(); i++ )
|
|
{
|
|
if( m_Children[i]->IsKindOf(pClass) )
|
|
{
|
|
iCount++;
|
|
}
|
|
}
|
|
|
|
return iCount;
|
|
}
|
|
|
|
inline int CHMObject::AddChild(CHMObject* pObject)
|
|
{
|
|
TRACEX(_T("CHMObject::AddChild\n"));
|
|
TRACEARGn(pObject);
|
|
|
|
if( ! GfxCheckObjPtr(pObject,CHMObject) )
|
|
{
|
|
TRACE(_T("FAILED : pObject is not a valid pointer.\n"));
|
|
return -1;
|
|
}
|
|
|
|
int iIndex = (int)m_Children.Add(pObject);
|
|
|
|
pObject->SetScopePane(GetScopePane());
|
|
|
|
for( int i = 0; i < GetScopeItemCount(); i++ )
|
|
{
|
|
CScopePaneItem* pItem = pObject->CreateScopeItem();
|
|
|
|
if( ! pItem->Create(m_pPane,GetScopeItem(i)) )
|
|
{
|
|
ASSERT(FALSE);
|
|
delete pItem;
|
|
return -1;
|
|
}
|
|
|
|
pItem->SetIconIndex(pObject->GetState());
|
|
pItem->SetOpenIconIndex(pObject->GetState());
|
|
|
|
pItem->SetDisplayName(0,pObject->GetName());
|
|
|
|
if( GfxCheckObjPtr(pItem,CHMScopeItem) )
|
|
{
|
|
((CHMScopeItem*)pItem)->SetObjectPtr(pObject);
|
|
}
|
|
|
|
int iIndex = GetScopeItem(i)->AddChild(pItem);
|
|
pItem->InsertItem(iIndex);
|
|
pObject->AddScopeItem(pItem);
|
|
|
|
}
|
|
|
|
AddContainer(GetGuid(),pObject->GetGuid(),pObject);
|
|
pObject->UpdateStatus();
|
|
|
|
return iIndex;
|
|
}
|
|
|
|
inline bool CHMObject::CreateChild(CHMObject* pObject, const CString& sWMIClassName, const CString& sWMIAssociatorClassName)
|
|
{
|
|
TRACEX(_T("CHMObject::CreateChild\n"));
|
|
TRACEARGn(pObject);
|
|
TRACEARGs(sWMIClassName);
|
|
TRACEARGs(sWMIAssociatorClassName);
|
|
|
|
if( ! GfxCheckObjPtr(pObject,CHMObject) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
pObject->SetSystemName(GetSystemName());
|
|
|
|
// create the GUID
|
|
GUID ChildGuid;
|
|
CoCreateGuid(&ChildGuid);
|
|
|
|
OLECHAR szGuid[GUID_CCH];
|
|
::StringFromGUID2(ChildGuid, szGuid, GUID_CCH);
|
|
CString sGuid = OLE2CT(szGuid);
|
|
pObject->SetGuid(sGuid);
|
|
|
|
// Add the child
|
|
AddChild(pObject);
|
|
|
|
// create child instance
|
|
CWbemClassObject ChildClassObject;
|
|
if( ! CHECKHRESULT(ChildClassObject.Create(GetSystemName())) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
BSTR bsTemp = sWMIClassName.AllocSysString();
|
|
if( ! CHECKHRESULT(ChildClassObject.CreateInstance(bsTemp)) )
|
|
{
|
|
::SysFreeString(bsTemp);
|
|
return false;
|
|
}
|
|
::SysFreeString(bsTemp);
|
|
|
|
// Save the child instance properties for name and guid
|
|
ChildClassObject.SetProperty(IDS_STRING_MOF_NAME,pObject->GetName());
|
|
|
|
ChildClassObject.SetProperty(IDS_STRING_MOF_GUID,sGuid);
|
|
|
|
// create the association class instance
|
|
CWbemClassObject Associator;
|
|
if( ! CHECKHRESULT(Associator.Create(GetSystemName())) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bsTemp = sWMIAssociatorClassName.AllocSysString();
|
|
if( ! CHECKHRESULT(Associator.CreateInstance(bsTemp)) )
|
|
{
|
|
::SysFreeString(bsTemp);
|
|
return false;
|
|
}
|
|
::SysFreeString(bsTemp);
|
|
|
|
// set path properties for the association class instance
|
|
Associator.SetProperty(IDS_STRING_MOF_PARENT_ASSOC,GetObjectPath());
|
|
|
|
Associator.SetProperty(IDS_STRING_MOF_CHILD_ASSOC,pObject->GetObjectPath());
|
|
|
|
// commit the changes to WMI
|
|
ChildClassObject.SaveAllProperties();
|
|
Associator.SaveAllProperties();
|
|
|
|
// Add the child
|
|
return true;
|
|
}
|
|
|
|
inline CString CHMObject::GetUniqueChildName(UINT uiFmtID /*= IDS_STRING_UNITITLED */)
|
|
{
|
|
TRACEX(_T("CHMObject::GetUniqueChildName\n"));
|
|
|
|
CString sName;
|
|
sName.LoadString(uiFmtID);
|
|
sName = sName.Left(sName.GetLength()-3);
|
|
|
|
while( GetChild(sName) != NULL )
|
|
{
|
|
sName.Format(uiFmtID,m_lNameSuffix++);
|
|
}
|
|
|
|
return sName;
|
|
}
|
|
|
|
inline CHMObject* CHMObject::GetChild(const CString& sName)
|
|
{
|
|
TRACEX(_T("CHMObject::GetChild\n"));
|
|
|
|
for( int i = 0; i < m_Children.GetSize(); i++ )
|
|
{
|
|
if( m_Children[i]->GetName().CompareNoCase(sName) == 0 )
|
|
{
|
|
return GetChild(i);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
inline CHMObject* CHMObject::GetChildByGuid(const CString& sGuid)
|
|
{
|
|
TRACEX(_T("CHMObject::GetChildByGUID\n"));
|
|
TRACEARGs(sGuid);
|
|
|
|
for( int i = 0; i < m_Children.GetSize(); i++ )
|
|
{
|
|
if( m_Children[i]->GetGuid() == sGuid )
|
|
{
|
|
return GetChild(i);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
inline CHMObject* CHMObject::GetDescendantByGuid(const CString& sGuid)
|
|
{
|
|
TRACEX(_T("CHMObject::GetDescendantByGuid\n"));
|
|
TRACEARGs(sGuid);
|
|
|
|
for( int i = 0; i < m_Children.GetSize(); i++ )
|
|
{
|
|
if( m_Children[i]->GetGuid() == sGuid )
|
|
{
|
|
return GetChild(i);
|
|
}
|
|
|
|
CHMObject* pDescendantObject = m_Children[i]->GetDescendantByGuid(sGuid);
|
|
if( pDescendantObject )
|
|
{
|
|
return pDescendantObject;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
inline CHMObject* CHMObject::GetChild(int iIndex)
|
|
{
|
|
TRACEX(_T("CHMObject::GetChild\n"));
|
|
TRACEARGn(iIndex);
|
|
|
|
if( iIndex >= m_Children.GetSize() || iIndex < 0 )
|
|
{
|
|
TRACE(_T("FAILED : iIndex is out of array bounds.\n"));
|
|
return NULL;
|
|
}
|
|
|
|
return m_Children[iIndex];
|
|
}
|
|
|
|
inline void CHMObject::RemoveChild(CHMObject* pObject)
|
|
{
|
|
TRACEX(_T("CHMObject::RemoveChild\n"));
|
|
TRACEARGn(pObject);
|
|
|
|
for( int i = 0; i < m_Children.GetSize(); i++ )
|
|
{
|
|
if( m_Children[i] == pObject )
|
|
{
|
|
m_Children.RemoveAt(i);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
inline void CHMObject::DestroyChild(CHMObject* pChildObject, bool bDeleteClassObject /*= false*/)
|
|
{
|
|
TRACEX(_T("CHMObject::DestroyChild\n"));
|
|
TRACEARGn(pChildObject);
|
|
TRACEARGn(bDeleteClassObject);
|
|
|
|
for( int i = 0; i < m_Children.GetSize(); i++ )
|
|
{
|
|
if( GetChild(i) == pChildObject )
|
|
{
|
|
DestroyChild(i,bDeleteClassObject);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void CHMObject::DestroyAllChildren()
|
|
{
|
|
TRACEX(_T("CHMObject::DestroyAllChildren\n"));
|
|
|
|
for( int i = (int)m_Children.GetSize() - 1; i >= 0; i-- )
|
|
{
|
|
m_Children[i]->DestroyAllChildren();
|
|
DestroyChild(i);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// State Members
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
inline void CHMObject::SetState(int iState, bool bUpdateScopeItems /*= false*/, bool bApplyToChildren /*= false*/)
|
|
{
|
|
TRACEX(_T("CHMObject::SetState\n"));
|
|
TRACEARGn(iState);
|
|
|
|
m_nState = iState;
|
|
|
|
if( bUpdateScopeItems )
|
|
{
|
|
for( int i = 0; i < GetScopeItemCount(); i++ )
|
|
{
|
|
CScopePaneItem* pItem = GetScopeItem(i);
|
|
if( pItem )
|
|
{
|
|
pItem->SetIconIndex(GetState());
|
|
pItem->SetOpenIconIndex(GetState());
|
|
pItem->SetItem();
|
|
}
|
|
}
|
|
}
|
|
|
|
if( bApplyToChildren )
|
|
{
|
|
for( int i = 0; i < GetChildCount(); i++ )
|
|
{
|
|
CHMObject* pObject = GetChild(i);
|
|
if( pObject )
|
|
{
|
|
pObject->SetState(iState,bUpdateScopeItems,bApplyToChildren);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void CHMObject::TallyChildStates()
|
|
{
|
|
TRACEX(_T("CHMObject::TallyChildStates\n"));
|
|
|
|
m_lNormalCount = 0L;
|
|
m_lWarningCount = 0L;
|
|
m_lCriticalCount = 0L;
|
|
m_lUnknownCount = 0L;
|
|
|
|
|
|
for( int i = 0; i < m_Children.GetSize(); i++ )
|
|
{
|
|
switch( m_Children[i]->GetState() )
|
|
{
|
|
case HMS_NORMAL:
|
|
{
|
|
m_lNormalCount++;
|
|
}
|
|
break;
|
|
|
|
case HMS_WARNING:
|
|
{
|
|
m_lWarningCount++;
|
|
}
|
|
break;
|
|
|
|
case HMS_CRITICAL:
|
|
{
|
|
m_lCriticalCount++;
|
|
}
|
|
break;
|
|
|
|
case HMS_UNKNOWN:
|
|
{
|
|
m_lUnknownCount++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Associated Scope Pane Member
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
inline CScopePane* CHMObject::GetScopePane()
|
|
{
|
|
TRACEX(_T("CHMObject::GetScopePane\n"));
|
|
|
|
if( ! GfxCheckObjPtr(m_pPane,CScopePane) )
|
|
{
|
|
TRACE(_T("FAILED : m_pPane is not a valid pointer.\n"));
|
|
return NULL;
|
|
}
|
|
|
|
return m_pPane;
|
|
}
|
|
|
|
inline void CHMObject::SetScopePane(CScopePane* pPane)
|
|
{
|
|
TRACEX(_T("CHMObject::SetScopePane\n"));
|
|
TRACEARGn(pPane);
|
|
|
|
if( ! GfxCheckObjPtr(pPane,CScopePane) )
|
|
{
|
|
TRACE(_T("FAILED : pPane is not a valid pointer.\n"));
|
|
return;
|
|
}
|
|
|
|
m_pPane = pPane;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Scope Item Members
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
inline int CHMObject::GetScopeItemCount()
|
|
{
|
|
TRACEX(_T("CHMObject::GetScopeItemCount\n"));
|
|
|
|
return (int)m_ScopeItems.GetSize();
|
|
}
|
|
|
|
inline CScopePaneItem* CHMObject::GetScopeItem(int iIndex)
|
|
{
|
|
TRACEX(_T("CHMObject::GetScopeItem\n"));
|
|
TRACEARGn(iIndex);
|
|
|
|
if( iIndex >= m_ScopeItems.GetSize() || iIndex < 0 )
|
|
{
|
|
TRACE(_T("FAILED : iIndex is out of array bounds.\n"));
|
|
return NULL;
|
|
}
|
|
|
|
if( ! GfxCheckObjPtr(m_ScopeItems[iIndex],CScopePaneItem) )
|
|
{
|
|
TRACE(_T("FAILED : The Scope Pane Item pointer stored at iIndex is invalid.\n"));
|
|
return NULL;
|
|
}
|
|
|
|
return m_ScopeItems[iIndex];
|
|
}
|
|
|
|
inline int CHMObject::AddScopeItem(CScopePaneItem* pItem)
|
|
{
|
|
TRACEX(_T("CHMObject::AddScopeItem\n"));
|
|
TRACEARGn(pItem);
|
|
|
|
if( ! GfxCheckObjPtr(pItem,CScopePaneItem) )
|
|
{
|
|
TRACE(_T("FAILED : pItem is not a valid pointer.\n"));
|
|
return -1;
|
|
}
|
|
|
|
if( pItem->GetChildCount() == 0 && GetChildCount() != 0 )
|
|
{
|
|
for( int i = 0; i < m_Children.GetSize(); i++ )
|
|
{
|
|
CScopePaneItem* pNewItem = m_Children[i]->CreateScopeItem();
|
|
if( ! pNewItem->Create(m_pPane,pItem) )
|
|
{
|
|
ASSERT(FALSE);
|
|
delete pNewItem;
|
|
return -1;
|
|
}
|
|
|
|
if( m_Children[i]->GetScopeItemCount() )
|
|
{
|
|
pNewItem->SetDisplayNames(m_Children[i]->GetScopeItem(0)->GetDisplayNames());
|
|
}
|
|
else
|
|
{
|
|
pNewItem->SetDisplayName(0,m_Children[i]->GetName());
|
|
}
|
|
pNewItem->SetIconIndex(m_Children[i]->GetState());
|
|
pNewItem->SetOpenIconIndex(m_Children[i]->GetState());
|
|
int iIndex = pItem->AddChild(pNewItem);
|
|
pNewItem->InsertItem(iIndex);
|
|
m_Children[i]->AddScopeItem(pNewItem);
|
|
}
|
|
|
|
}
|
|
|
|
// make certain the new scope item has the correct icon
|
|
pItem->SetIconIndex(GetState());
|
|
pItem->SetOpenIconIndex(GetState());
|
|
|
|
return (int)m_ScopeItems.Add(pItem);
|
|
}
|
|
|
|
inline void CHMObject::RemoveScopeItem(int iIndex)
|
|
{
|
|
TRACEX(_T("CHMObject::RemoveScopeItem\n"));
|
|
TRACEARGn(iIndex);
|
|
|
|
if( iIndex >= m_ScopeItems.GetSize() || iIndex < 0 )
|
|
{
|
|
TRACE(_T("FAILED : iIndex is out of array bounds.\n"));
|
|
return;
|
|
}
|
|
|
|
if( ! GfxCheckObjPtr(m_ScopeItems[iIndex],CScopePaneItem) )
|
|
{
|
|
TRACE(_T("FAILED : The Scope Pane Item pointer stored at iIndex is invalid.\n"));
|
|
return;
|
|
}
|
|
|
|
m_ScopeItems.RemoveAt(iIndex);
|
|
}
|
|
|
|
inline void CHMObject::RemoveAllScopeItems()
|
|
{
|
|
TRACEX(_T("CHMObject::RemoveAllScopeItems\n"));
|
|
|
|
m_ScopeItems.RemoveAll();
|
|
}
|
|
|
|
inline CScopePaneItem* CHMObject::IsSelected()
|
|
{
|
|
TRACEX(_T("CHMObject::IsSelected\n"));
|
|
|
|
if( ! GetScopePane() )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
for( int i = 0; i < GetScopeItemCount(); i++ )
|
|
{
|
|
if( m_pPane->GetSelectedScopeItem() == GetScopeItem(i) )
|
|
{
|
|
return GetScopeItem(i);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|