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.
694 lines
22 KiB
694 lines
22 KiB
//=================================================================================================
|
|
|
|
//
|
|
|
|
// Copyright (c) 2000-2001 Microsoft Corporation, All Rights Reserved
|
|
//
|
|
// assoc.cpp -- Rule-based association class
|
|
//
|
|
// This class allows for the creation of a specific type of rule-based associations. Consider
|
|
// this example:
|
|
//
|
|
// CAssociation MyThisComputerPhysicalFixedDisk(
|
|
// L"ThisComputerPhysicalFixedDisk",
|
|
// L"Root\\default",
|
|
// L"ThisComputer",
|
|
// L"PhysicalFixedDisk",
|
|
// L"GroupComponent",
|
|
// L"PartComponent"
|
|
// ) ;
|
|
//
|
|
// This declaration is saying that there is a class named "ThisComputerPhysicalFixedDisk" which
|
|
// resides in the "root\default" namespace. It is an association between the "ThisComputer"
|
|
// class, and the "PhysicalFixedDisk" class. The "ThisComputer" value goes into the
|
|
// "GroupComponent" property of the "ThisComputerPhysicalFixedDisk" class, and the
|
|
// "PhysicalFixedDisk" value goes in the "PartComponent" property of the
|
|
// "ThisComputerPhysicalFixedDisk" class.
|
|
//
|
|
// Some notes:
|
|
// - This class will take all the instances of the left class ("ThisComputer" in the example
|
|
// above) and relate them to ALL instances of the right class ("PhysicalFixedDisk" in the example
|
|
// above). So, if there are 3 instances of the left class, and 4 instances of the right class,
|
|
// this association class will return 12 instances.
|
|
//
|
|
// - When choosing which of the two classes should be the left class, choose the class that is
|
|
// likely to have fewer instances. This will result in less memory being used, and instances
|
|
// being sent back to the client sooner.
|
|
//
|
|
// - CAssociation supports ExecQuery, GetObject, and EnumerateInstances.
|
|
//
|
|
// - CAssociation is designed to be derived from. For example, if your association needs to
|
|
// support DeleteInstance, ExecMethod, or PutInstance, create a class that derives from
|
|
// CAssociation, and add the appropriate methods. Also, various methods such as
|
|
// LoadPropertyValues and AreRelated may be useful for further customization.
|
|
//
|
|
// - The two endpoint classes can be dynamic, static, or abstract. CAssociation will do a deep
|
|
// enumeration (actually a query, which is always deep) to retrieve the instances.
|
|
//
|
|
// - When calling the endpoint classes, CAssociation will use per property gets, and queries
|
|
// with Select clauses and/or Where statements. If the endpoint classes support per-property
|
|
// gets or queries, this will result in better performance for the associaton class.
|
|
//
|
|
// - The association class and both endpoints must all be in the same namespace.
|
|
//
|
|
// See also: CBinding (binding.cpp) for a different type of rule-based association.
|
|
//
|
|
//=================================================================================================
|
|
|
|
#include "precomp.h"
|
|
#include "Assoc.h"
|
|
|
|
#include <helper.h>
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function: CAssociation::CAssociation
|
|
//
|
|
// Constructor.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
CAssociation::CAssociation(
|
|
|
|
LPCWSTR pwszClassName,
|
|
LPCWSTR pwszNamespaceName,
|
|
|
|
LPCWSTR pwszLeftClassName,
|
|
LPCWSTR pwszRightClassName,
|
|
|
|
LPCWSTR pwszLeftPropertyName,
|
|
LPCWSTR pwszRightPropertyName
|
|
|
|
) : Provider(pwszClassName, pwszNamespaceName)
|
|
{
|
|
// Save off the class and property names
|
|
m_sLeftClassName = pwszLeftClassName;
|
|
m_sRightClassName = pwszRightClassName;
|
|
|
|
m_sLeftPropertyName = pwszLeftPropertyName;
|
|
m_sRightPropertyName = pwszRightPropertyName;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function: CAssociation::~CAssociation
|
|
//
|
|
// Destructor
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
CAssociation::~CAssociation()
|
|
{
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function: CAssociation::ExecQuery
|
|
//
|
|
// This routine will optimize on queries of the form:
|
|
// WHERE prop1 = value1 [ or prop1 = value2 ...]
|
|
//
|
|
// This type of query is commonly seen when doing an ASSOCIATORS or
|
|
// REFERENCES query against one of the endpoint classes.
|
|
//
|
|
// This routine will also optimize on queries of the form:
|
|
// WHERE prop1 = value1 [ or prop1 = value2 ...] AND
|
|
// prop2 = value3 [ or prop2 = value4 ...]
|
|
//
|
|
// It will NOT optmize on queries of the form:
|
|
// WHERE prop1 <> value1
|
|
// WHERE prop1 > value1
|
|
// WHERE prop1 = value1 OR prop2 = value2
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CAssociation::ExecQuery(
|
|
|
|
MethodContext* pMethodContext,
|
|
CFrameworkQuery &pQuery,
|
|
long lFlags
|
|
)
|
|
{
|
|
HRESULT hr = WBEM_S_NO_ERROR;
|
|
TRefPointerCollection<CInstance> lefts;
|
|
|
|
CHStringArray sLeftPaths, sRightPaths;
|
|
|
|
// Look for WHERE m_sLeftPropertyName=value1
|
|
pQuery.GetValuesForProp ( m_sLeftPropertyName, sLeftPaths ) ;
|
|
|
|
// Look for WHERE m_sRightPropertyName=value1
|
|
pQuery.GetValuesForProp ( m_sRightPropertyName, sRightPaths ) ;
|
|
|
|
if (sLeftPaths.GetSize() == 0)
|
|
{
|
|
// They didn't ask for a specific set of left instances. However,
|
|
// it may be that we can figure out what left instances we need
|
|
// by looking at what right instances they requested. CAssociation
|
|
// doesn't do this, but CBinding does.
|
|
CHStringArray sRightWheres;
|
|
bool bHadRights = sRightPaths.GetSize() > 0;
|
|
|
|
MakeWhere(sRightPaths, sRightWheres);
|
|
|
|
// If we used to have a list of RightWheres, and MakeWhere discarded
|
|
// them all as unusable, then there aren't going to be any
|
|
// instances that match the query.
|
|
if (!bHadRights || sRightPaths.GetSize() > 0)
|
|
{
|
|
// GetLeftInstances populates lefts using a sRightWheres
|
|
// to construct a query.
|
|
hr = GetLeftInstances(pMethodContext, lefts, sRightWheres);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// For each sLeftPaths that is valid, create an entry in lefts by
|
|
// doing a GetObject on the sLeftPaths entry.
|
|
hr = ValidateLeftObjectPaths(pMethodContext, sLeftPaths, lefts);
|
|
}
|
|
|
|
// If we failed, or if there are no instances on the left, there's
|
|
// no point in continuing.
|
|
if (SUCCEEDED(hr) && lefts.GetSize() > 0)
|
|
{
|
|
// If the where clause didn't specify any value for the right property
|
|
if (sRightPaths.GetSize() == 0)
|
|
{
|
|
// We may be able to use the information from the already retrieved
|
|
// left instances to limit which instances we retrieve from the right.
|
|
// CAssociation doesn't do this, but CBinding does.
|
|
CHStringArray sLeftWheres;
|
|
hr = FindWhere(lefts, sLeftWheres);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// GetRightInstances takes the 'lefts' and rubs all the
|
|
// rights against them creating instances where appropriate
|
|
hr = GetRightInstances(pMethodContext, &lefts, sLeftWheres);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// They gave us a list of object paths for the righthand property
|
|
TRefPointerCollection<CInstance> rights;
|
|
|
|
// For each sRightPaths that is valid, create an instance
|
|
hr = ValidateRightObjectPaths(pMethodContext, sRightPaths, lefts);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function: CAssociation::GetObject
|
|
//
|
|
// Verify the exist of the specified association class instance.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CAssociation::GetObject(
|
|
|
|
CInstance* pInstance,
|
|
long lFlags,
|
|
CFrameworkQuery &pQuery
|
|
)
|
|
{
|
|
HRESULT hr = WBEM_E_NOT_FOUND;
|
|
|
|
CHString sLeftPath, sRightPath;
|
|
|
|
// Get the two endpoints to verify
|
|
if (pInstance->GetCHString(m_sLeftPropertyName, sLeftPath ) &&
|
|
pInstance->GetCHString(m_sRightPropertyName, sRightPath ) )
|
|
{
|
|
CInstancePtr pLeft, pRight;
|
|
|
|
// Try to get the objects
|
|
if (
|
|
SUCCEEDED(hr = RetrieveLeftInstance(
|
|
|
|
sLeftPath,
|
|
&pLeft,
|
|
pInstance->GetMethodContext())
|
|
) &&
|
|
SUCCEEDED(hr = RetrieveRightInstance(
|
|
|
|
sRightPath,
|
|
&pRight,
|
|
pInstance->GetMethodContext())
|
|
)
|
|
)
|
|
{
|
|
|
|
hr = WBEM_E_NOT_FOUND;
|
|
|
|
// So, the end points exist. Are they derived from or equal
|
|
// to the classes we are working with?
|
|
CHString sLeftClass, sRightClass;
|
|
|
|
pLeft->GetCHString(L"__Class", sLeftClass);
|
|
pRight->GetCHString(L"__Class", sRightClass);
|
|
|
|
bool bDerived = IsDerivedFrom(
|
|
|
|
m_sLeftClassName,
|
|
sLeftClass,
|
|
pInstance->GetMethodContext()
|
|
);
|
|
|
|
if (bDerived)
|
|
{
|
|
bDerived = IsDerivedFrom(
|
|
|
|
m_sRightClassName,
|
|
sRightClass,
|
|
pInstance->GetMethodContext()
|
|
);
|
|
}
|
|
|
|
if (bDerived)
|
|
{
|
|
// Just because two instances are valid and derive from the right class,
|
|
// doesn't mean they are related. Do any other checks.
|
|
if (AreRelated(pLeft, pRight))
|
|
{
|
|
// CBinding and CAssoc don't populate any additional properties, but
|
|
// an overload of one of these classes might.
|
|
hr = LoadPropertyValues(pInstance, pLeft, pRight);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function: CAssociation::EnumerateInstances
|
|
//
|
|
// Return all instances of the association class
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CAssociation::EnumerateInstances(
|
|
|
|
MethodContext *pMethodContext,
|
|
long lFlags /*= 0L*/
|
|
)
|
|
{
|
|
HRESULT hr = WBEM_S_NO_ERROR;
|
|
|
|
TRefPointerCollection<CInstance> lefts;
|
|
CHStringArray sWheres;
|
|
|
|
// GetLeftInstances populates lefts
|
|
if (SUCCEEDED(hr = GetLeftInstances(pMethodContext, lefts, sWheres)))
|
|
{
|
|
// We may be able to use the information from the already retrieved
|
|
// left instances to limit which instances we retrieve from the right.
|
|
// CAssociation doesn't do this, but CBinding does.
|
|
FindWhere(lefts, sWheres);
|
|
|
|
// GetRightInstances takes the 'lefts' and rubs all the
|
|
// rights against them
|
|
hr = GetRightInstances(pMethodContext, &lefts, sWheres);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function: CAssociation::GetRightInstances
|
|
//
|
|
// For each instance of the righthand class retrieved, call
|
|
// CAssociation::StaticEnumerationCallback.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CAssociation::GetRightInstances(
|
|
|
|
MethodContext *pMethodContext,
|
|
TRefPointerCollection<CInstance> *lefts,
|
|
const CHStringArray &sLeftWheres
|
|
)
|
|
{
|
|
CHString sQuery;
|
|
sQuery.Format(L"SELECT __RELPATH FROM %s", m_sRightClassName);
|
|
|
|
// 'StaticEnumerationCallback' will get called once for each instance
|
|
// returned from the query
|
|
HRESULT hr = CWbemProviderGlue::GetInstancesByQueryAsynch(
|
|
sQuery,
|
|
this,
|
|
StaticEnumerationCallback,
|
|
GetNamespace(),
|
|
pMethodContext,
|
|
lefts);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function: CAssociation::StaticEnumerationCallback
|
|
//
|
|
// Put the 'this' pointer back, and call CAssociation::EnumerationCallback
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT WINAPI CAssociation::StaticEnumerationCallback(
|
|
|
|
Provider* pThat,
|
|
CInstance* pInstance,
|
|
MethodContext* pContext,
|
|
void* pUserData
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
|
|
CAssociation *pThis = (CAssociation *) pThat;
|
|
|
|
if (pThis)
|
|
{
|
|
hr = pThis->EnumerationCallback(pInstance, pContext, pUserData);
|
|
}
|
|
else
|
|
{
|
|
hr = WBEM_S_NO_ERROR;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function: CAssociation::EnumerationCallback
|
|
//
|
|
// Take the righthand instance that was passed in and pair it
|
|
// with each of the left hand instances.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CAssociation::EnumerationCallback(
|
|
|
|
CInstance *pRight,
|
|
MethodContext *pMethodContext,
|
|
void *pUserData
|
|
)
|
|
{
|
|
HRESULT hr = WBEM_E_FAILED;
|
|
|
|
CInstancePtr pLeft;
|
|
REFPTRCOLLECTION_POSITION posLeft;
|
|
CHString sLeftPath, sRightPath;
|
|
|
|
// Cast for userdata back to what it is
|
|
TRefPointerCollection<CInstance> *pLefts = (TRefPointerCollection<CInstance> *)pUserData;
|
|
|
|
if (pLefts->BeginEnum(posLeft))
|
|
{
|
|
hr = WBEM_S_NO_ERROR;
|
|
|
|
// Walk all the pLefts
|
|
for (pLeft.Attach(pLefts->GetNext(posLeft)) ;
|
|
(SUCCEEDED(hr)) && (pLeft != NULL) ;
|
|
pLeft.Attach(pLefts->GetNext(posLeft)) )
|
|
{
|
|
// Compare it to the current pRight
|
|
if(AreRelated(pLeft, pRight))
|
|
{
|
|
// We have a winner. Populate the properties and send it back.
|
|
if (GetLocalInstancePath(pLeft, sLeftPath) &&
|
|
GetLocalInstancePath(pRight, sRightPath))
|
|
{
|
|
CInstancePtr pNewAssoc(CreateNewInstance(pMethodContext), false);
|
|
|
|
if (pNewAssoc->SetCHString(m_sLeftPropertyName, sLeftPath) &&
|
|
pNewAssoc->SetCHString(m_sRightPropertyName, sRightPath) )
|
|
{
|
|
if (SUCCEEDED(hr = LoadPropertyValues(pNewAssoc, pLeft, pRight)))
|
|
{
|
|
hr = pNewAssoc->Commit();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pLefts->EndEnum();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function: CAssociation::ValidateLeftObjectPaths
|
|
//
|
|
// Populate the lefts array by doing GetObjects on the object paths
|
|
// passed in sPaths.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CAssociation::ValidateLeftObjectPaths(
|
|
|
|
MethodContext *pMethodContext,
|
|
const CHStringArray &sPaths,
|
|
TRefPointerCollection<CInstance> &lefts
|
|
)
|
|
{
|
|
CInstancePtr pInstance;
|
|
|
|
// Walk the object paths
|
|
for (DWORD x=0; x < sPaths.GetSize(); x++)
|
|
{
|
|
ParsedObjectPath *pParsedPath = NULL;
|
|
CObjectPathParser objpathParser;
|
|
CHString sPath(sPaths[x]);
|
|
|
|
// Parse the object path
|
|
int nStatus = objpathParser.Parse( sPath, &pParsedPath );
|
|
|
|
if ( 0 == nStatus )
|
|
{
|
|
OnDeleteObj<ParsedObjectPath *,
|
|
CObjectPathParser,
|
|
void(CObjectPathParser::*)(ParsedObjectPath *),
|
|
&CObjectPathParser::Free> ReleaseMe(&objpathParser,pParsedPath);
|
|
|
|
|
|
// Is this class derived from or equal to the lefthand class?
|
|
bool bDerived = false;
|
|
|
|
bDerived = IsDerivedFrom(
|
|
|
|
m_sLeftClassName,
|
|
pParsedPath->m_pClass,
|
|
pMethodContext
|
|
);
|
|
|
|
// Make sure this is an absolute path
|
|
if (pParsedPath->m_dwNumNamespaces == 0)
|
|
{
|
|
sPath = L"\\\\.\\" + GetNamespace() + L':' + sPath;
|
|
}
|
|
|
|
if (bDerived)
|
|
{
|
|
// See if it is valid. Note that we DON'T send back an error just because
|
|
// we can't find one of the object paths.
|
|
if (SUCCEEDED(RetrieveLeftInstance(sPath, &pInstance, pMethodContext)))
|
|
{
|
|
// Yup, add it to the list
|
|
lefts.Add(pInstance);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return WBEM_S_NO_ERROR;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function: CAssociation::ValidateRightObjectPaths
|
|
//
|
|
// Retrieve the righthand instances by doing GetObjects on the object
|
|
// paths passed in sPaths. Pass them to EnumerationCallback.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CAssociation::ValidateRightObjectPaths(
|
|
|
|
MethodContext *pMethodContext,
|
|
const CHStringArray &sPaths,
|
|
TRefPointerCollection<CInstance> &lefts
|
|
)
|
|
{
|
|
HRESULT hr = WBEM_S_NO_ERROR;;
|
|
CInstancePtr pInstance;
|
|
|
|
// Walk the object paths
|
|
for (DWORD x=0;
|
|
(x < sPaths.GetSize()) && SUCCEEDED(hr);
|
|
x++)
|
|
{
|
|
ParsedObjectPath *pParsedPath = NULL;
|
|
CObjectPathParser objpathParser;
|
|
CHString sPath(sPaths[x]);
|
|
|
|
int nStatus = objpathParser.Parse( sPath, &pParsedPath );
|
|
|
|
if ( 0 == nStatus )
|
|
{
|
|
OnDeleteObj<ParsedObjectPath *,
|
|
CObjectPathParser,
|
|
void(CObjectPathParser::*)(ParsedObjectPath *),
|
|
&CObjectPathParser::Free> ReleaseMe(&objpathParser,pParsedPath);
|
|
|
|
bool bDerived = false;
|
|
|
|
// Make sure this object path is at least related to us
|
|
bDerived = IsDerivedFrom(
|
|
|
|
m_sRightClassName,
|
|
pParsedPath->m_pClass,
|
|
pMethodContext
|
|
);
|
|
|
|
// Make sure this is an absolute path
|
|
if (pParsedPath->m_dwNumNamespaces == 0)
|
|
{
|
|
sPath = L"\\\\.\\" + GetNamespace() + L':' + sPath;
|
|
}
|
|
|
|
if (bDerived)
|
|
{
|
|
// See if it is valid. Note that we DON'T send back an error just because
|
|
// we can't find one of the object paths.
|
|
if (SUCCEEDED(RetrieveRightInstance(sPath, &pInstance, pMethodContext)))
|
|
{
|
|
hr = EnumerationCallback(pInstance, pMethodContext, &lefts);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function: CAssociation::GetLeftInstances
|
|
//
|
|
// Retrieve all the lefthand instances and store them in lefts
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CAssociation::GetLeftInstances(
|
|
|
|
MethodContext *pMethodContext,
|
|
TRefPointerCollection<CInstance> &lefts,
|
|
const CHStringArray &sRightWheres
|
|
)
|
|
{
|
|
CHString sQuery;
|
|
sQuery.Format(L"SELECT __RELPATH FROM %s", m_sLeftClassName);
|
|
|
|
return CWbemProviderGlue::GetInstancesByQuery(sQuery, &lefts, pMethodContext, GetNamespace());
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function: CAssociation::RetrieveLeftInstance
|
|
//
|
|
// Retrieve a specific lefthand instance. Use per-property gets
|
|
// to only request the keys for maximum performance.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CAssociation::RetrieveLeftInstance(
|
|
|
|
LPCWSTR lpwszObjPath,
|
|
CInstance **ppInstance,
|
|
MethodContext *pMethodContext
|
|
)
|
|
{
|
|
return CWbemProviderGlue::GetInstanceKeysByPath(lpwszObjPath, ppInstance, pMethodContext);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function: CAssociation::RetrieveRightInstance
|
|
//
|
|
// Retrieve a specific righthand instance. Use per-property gets
|
|
// to only request the keys for maximum performance.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CAssociation::RetrieveRightInstance(
|
|
|
|
LPCWSTR lpwszObjPath,
|
|
CInstance **ppInstance,
|
|
MethodContext *pMethodContext
|
|
)
|
|
{
|
|
return CWbemProviderGlue::GetInstanceKeysByPath(lpwszObjPath, ppInstance, pMethodContext);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function: CAssociation::IsInstance
|
|
//
|
|
// See whether the specified CInstance is an Instance object, or a
|
|
// Class object.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
bool CAssociation::IsInstance(const CInstance *pInstance)
|
|
{
|
|
DWORD dwGenus = 0;
|
|
|
|
pInstance->GetDWORD(L"__Genus", dwGenus);
|
|
|
|
return dwGenus == WBEM_GENUS_INSTANCE;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function: CAssociation::IsDerivedFrom
|
|
//
|
|
// See whether the specified class is derived from or equal
|
|
// to the class we are working with. Specifically, does
|
|
// pszDerivedClassName derive from pszBaseClassName?
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
bool CAssociation::IsDerivedFrom(
|
|
|
|
LPCWSTR pszBaseClassName,
|
|
LPCWSTR pszDerivedClassName,
|
|
MethodContext *pMethodContext
|
|
)
|
|
{
|
|
// First let's see if they are equal. CWbemProviderGlue::IsDerivedFrom
|
|
// doesn't check for this case
|
|
bool bDerived = _wcsicmp(pszBaseClassName, pszDerivedClassName) == 0;
|
|
if (!bDerived)
|
|
{
|
|
bDerived = CWbemProviderGlue::IsDerivedFrom(
|
|
|
|
pszBaseClassName,
|
|
pszDerivedClassName,
|
|
pMethodContext,
|
|
GetNamespace()
|
|
);
|
|
}
|
|
|
|
return bDerived;
|
|
}
|