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.
610 lines
20 KiB
610 lines
20 KiB
//=================================================================
|
|
|
|
//
|
|
|
|
// Copyright (c) 2000-2001 Microsoft Corporation, All Rights Reserved
|
|
//
|
|
// binding.cpp -- Rule-based association class
|
|
//
|
|
// This class allows for the creation of a specific type of rule-based associations. Consider
|
|
// this example:
|
|
//
|
|
// CBinding MyPhysicalDiskToLogicalDisk(
|
|
// L"PhysicalDiskToLogicalDisk",
|
|
// L"Root\\default",
|
|
// L"PhysicalFixedDisk",
|
|
// L"LogicalDisk",
|
|
// L"Antecendent",
|
|
// L"Dependent",
|
|
// L"MappedDriveLetter",
|
|
// L"DriveLetter"
|
|
// );
|
|
//
|
|
// This declaration is saying that there is a class named "PhysicalDiskToLogicalDisk" which
|
|
// resides in the "root\default" namespace. It is an association between the "PhysicalFixedDisk"
|
|
// class, and the "LogicalDisk" class. The "PhysicalFixedDisk" value goes into the
|
|
// "Antecendent" property of the "PhysicalDiskToLogicalDisk" class, and the
|
|
// "LogicalDisk" value goes in the "Dependent" property of the "PhysicalDiskToLogicalDisk" class.
|
|
// Only return instances where PhysicalFixedDisk.MappedDriveLetter = LogicalDisk.DriveLetter.
|
|
//
|
|
// Some notes:
|
|
// - 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.
|
|
//
|
|
// - CBinding supports ExecQuery, GetObject, and EnumerateInstances.
|
|
//
|
|
// - CBinding is designed to be derived from. For example, if your association needs to
|
|
// support DeleteInstance, ExecMethod, or PutInstance, create a class that derives from
|
|
// CBinding, 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. CBinding will do a deep
|
|
// enumeration (actually a query, which is always deep) to retrieve the instances.
|
|
//
|
|
// - When calling the endpoint classes, CBinding 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: CAssociation (assoc.cpp) for a different type of rule-based association.
|
|
//
|
|
//=================================================================
|
|
|
|
#include "precomp.h"
|
|
#include "Binding.h"
|
|
|
|
#include <helper.h>
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function: CBinding::CBinding
|
|
//
|
|
// Constructor.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
CBinding::CBinding(
|
|
|
|
LPCWSTR pwszClassName,
|
|
LPCWSTR pwszNamespaceName,
|
|
|
|
LPCWSTR pwszLeftClassName,
|
|
LPCWSTR pwszRightClassName,
|
|
|
|
LPCWSTR pwszLeftPropertyName,
|
|
LPCWSTR pwszRightPropertyName,
|
|
|
|
LPCWSTR pwszLeftBindingPropertyName,
|
|
LPCWSTR pwszRightBindingPropertyName
|
|
|
|
) : CAssociation (
|
|
|
|
pwszClassName,
|
|
pwszNamespaceName,
|
|
pwszLeftClassName,
|
|
pwszRightClassName,
|
|
pwszLeftPropertyName,
|
|
pwszRightPropertyName
|
|
)
|
|
{
|
|
// Save off the binding property names
|
|
m_sLeftBindingPropertyName = pwszLeftBindingPropertyName;
|
|
m_sRightBindingPropertyName = pwszRightBindingPropertyName;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function: CBinding::~CBinding
|
|
//
|
|
// Destructor.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
CBinding::~CBinding()
|
|
{
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function: CBinding::AreRelated
|
|
//
|
|
// Determine whether the two instances are related. For
|
|
// CBinding, this is done by comparing their BindingProperty values.
|
|
//
|
|
// Note that NULL properties values are not considered to be related
|
|
// to anything, even another NULL.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
bool CBinding::AreRelated(
|
|
|
|
const CInstance *pLeft,
|
|
const CInstance *pRight
|
|
)
|
|
{
|
|
bool bRet = false;
|
|
|
|
variant_t LeftBindingPropertyValue,
|
|
RightBindingPropertyValue;
|
|
|
|
if (pLeft->GetVariant(m_sLeftBindingPropertyName, LeftBindingPropertyValue) &&
|
|
pRight->GetVariant(m_sRightBindingPropertyName, RightBindingPropertyValue) )
|
|
{
|
|
bRet = CompareVariantsNoCase(&LeftBindingPropertyValue, &RightBindingPropertyValue);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function: CBinding::GetRightInstances
|
|
//
|
|
// Make an async call (well, sort of async) to retrieve all of the
|
|
// instances of the righthand class. If possible use the sLeftWheres
|
|
// to create a query to minimize the number of returned instances.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CBinding::GetRightInstances(
|
|
|
|
MethodContext *pMethodContext,
|
|
TRefPointerCollection<CInstance> *lefts,
|
|
const CHStringArray &sLeftWheres
|
|
)
|
|
{
|
|
CHString sQuery;
|
|
|
|
// Did we get any where clauses?
|
|
if (sLeftWheres.GetSize() == 0)
|
|
{
|
|
// Nope, retrieve them all.
|
|
sQuery.Format(L"SELECT __RELPATH, %s FROM %s WHERE %s<>NULL",
|
|
|
|
(LPCWSTR)m_sRightBindingPropertyName,
|
|
(LPCWSTR)m_sRightClassName,
|
|
(LPCWSTR)m_sRightBindingPropertyName);
|
|
}
|
|
else
|
|
{
|
|
// Yup, build a query to only retrieve those instances.
|
|
CHString sQuery2;
|
|
|
|
sQuery.Format(L"SELECT __RELPATH, %s FROM %s WHERE (%s<>NULL) AND (%s=%s ",
|
|
|
|
(LPCWSTR)m_sRightBindingPropertyName,
|
|
(LPCWSTR)m_sRightClassName,
|
|
(LPCWSTR)m_sRightBindingPropertyName,
|
|
(LPCWSTR)m_sRightBindingPropertyName,
|
|
(LPCWSTR)sLeftWheres[0]);
|
|
|
|
// Usually, we should only have one (that what ASSOCIATORS and REFERENCES will
|
|
// generate). However, if we have more than one, tack the rest on.
|
|
for (DWORD x=1; x < sLeftWheres.GetSize(); x++)
|
|
{
|
|
sQuery2.Format(L"OR %s=%s ", (LPCWSTR)m_sRightBindingPropertyName, (LPCWSTR)sLeftWheres[x]);
|
|
sQuery += sQuery2;
|
|
}
|
|
|
|
// put the final close parenthesis.
|
|
sQuery.SetAt(sQuery.GetLength() - 1, L')');
|
|
}
|
|
|
|
// '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: CBinding::GetLeftInstances
|
|
//
|
|
// Retrieve the lefthand instances, storing them in lefts
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CBinding::GetLeftInstances(
|
|
|
|
MethodContext *pMethodContext,
|
|
TRefPointerCollection<CInstance> &lefts,
|
|
const CHStringArray &sRightWheres
|
|
)
|
|
{
|
|
CHString sQuery;
|
|
|
|
// Did we get any where clauses?
|
|
if (sRightWheres.GetSize() == 0)
|
|
{
|
|
// Nope, retrieve them all.
|
|
sQuery.Format(L"SELECT __RELPATH, %s FROM %s WHERE %s <> NULL",
|
|
(LPCWSTR)m_sLeftBindingPropertyName,
|
|
(LPCWSTR)m_sLeftClassName,
|
|
(LPCWSTR)m_sLeftBindingPropertyName
|
|
);
|
|
}
|
|
else
|
|
{
|
|
// Yup, build a query to only retrieve those instances.
|
|
CHString sQuery2;
|
|
|
|
sQuery.Format(L"SELECT __RELPATH, %s FROM %s WHERE (%s<>NULL) AND (%s=%s ",
|
|
(LPCWSTR)m_sLeftBindingPropertyName,
|
|
(LPCWSTR)m_sLeftClassName,
|
|
(LPCWSTR)m_sLeftBindingPropertyName,
|
|
(LPCWSTR)m_sLeftBindingPropertyName,
|
|
(LPCWSTR)sRightWheres[0]);
|
|
|
|
// Usually, we should only have one (that what ASSOCIATORS and REFERENCES will
|
|
// generate). However, if we have more than one, tack the rest on.
|
|
for (DWORD x=1; x < sRightWheres.GetSize(); x++)
|
|
{
|
|
sQuery2.Format(L"OR %s=%s ", (LPCWSTR)m_sLeftBindingPropertyName, (LPCWSTR)sRightWheres[x]);
|
|
sQuery += sQuery2;
|
|
}
|
|
|
|
// put the final close parenthesis.
|
|
sQuery.SetAt(sQuery.GetLength() - 1, L')');
|
|
}
|
|
|
|
return CWbemProviderGlue::GetInstancesByQuery(sQuery, &lefts, pMethodContext, GetNamespace());
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function: CBinding::RetrieveLeftInstance
|
|
//
|
|
// Retrieve a specific lefthand instance. Use per-property gets
|
|
// to only request the required properties for best performance.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CBinding::RetrieveLeftInstance(
|
|
|
|
LPCWSTR lpwszObjPath,
|
|
CInstance **ppInstance,
|
|
MethodContext *pMethodContext
|
|
)
|
|
{
|
|
CHStringArray csaProperties;
|
|
csaProperties.Add(L"__Relpath");
|
|
csaProperties.Add(m_sLeftBindingPropertyName);
|
|
|
|
return CWbemProviderGlue::GetInstancePropertiesByPath(lpwszObjPath, ppInstance, pMethodContext, csaProperties);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function: CBinding::RetrieveRightInstance
|
|
//
|
|
// Retrieve a specific lefthand instance. Use per-property gets
|
|
// to only request the required properties for best performance.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CBinding::RetrieveRightInstance(
|
|
|
|
LPCWSTR lpwszObjPath,
|
|
CInstance **ppInstance,
|
|
MethodContext *pMethodContext
|
|
)
|
|
{
|
|
CHStringArray csaProperties;
|
|
csaProperties.Add(L"__Relpath");
|
|
csaProperties.Add(m_sRightBindingPropertyName);
|
|
|
|
return CWbemProviderGlue::GetInstancePropertiesByPath(lpwszObjPath, ppInstance, pMethodContext, csaProperties);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function: CBinding::MakeWhere
|
|
//
|
|
// If the key property of the righthand class also happens to be
|
|
// the binding property AND if we have a path for specific righthand
|
|
// instances we need to return, then we can use the path for the
|
|
// righthand instances to build a where clause for the lefthand
|
|
// instances.
|
|
//
|
|
// Note that if we find invalid paths in sRightPaths, we remove
|
|
// them from sRightPaths.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
void CBinding::MakeWhere(
|
|
|
|
CHStringArray &sRightPaths,
|
|
CHStringArray &sRightWheres
|
|
)
|
|
{
|
|
// See if we have any righthand instances
|
|
if (sRightPaths.GetSize() > 0)
|
|
{
|
|
ParsedObjectPath *pParsedPath = NULL;
|
|
CObjectPathParser objpathParser;
|
|
CHString sTemp;
|
|
|
|
for (DWORD x=0; x < sRightPaths.GetSize();) // Note that x++ is done inside the loop
|
|
{
|
|
// Parse the instance
|
|
int nStatus = objpathParser.Parse( sRightPaths[x], &pParsedPath );
|
|
|
|
if ( 0 == nStatus )
|
|
{
|
|
OnDeleteObj<ParsedObjectPath *,
|
|
CObjectPathParser,
|
|
void(CObjectPathParser::*)(ParsedObjectPath *),
|
|
&CObjectPathParser::Free> ReleaseMe(&objpathParser,pParsedPath);
|
|
|
|
// See if the property name in the key is the property name we are binding on
|
|
if ( (pParsedPath->m_dwNumKeys == 1) && (pParsedPath->m_paKeys[0]->m_pName != NULL) )
|
|
{
|
|
if (_wcsicmp(pParsedPath->m_paKeys[0]->m_pName, m_sRightBindingPropertyName) == 0)
|
|
{
|
|
// Yes, it is. Make a where clause statement.
|
|
HRESULT hr = MakeString(&pParsedPath->m_paKeys[0]->m_vValue, sTemp);
|
|
|
|
// See if we already have that where clause
|
|
if ( SUCCEEDED(hr) && IsInList(sRightWheres, sTemp) == -1)
|
|
{
|
|
// A query with 1000 where clauses isn't going
|
|
// to be very efficient either. Pick a reasonable limit
|
|
if (sRightWheres.GetSize() < MAX_ORS)
|
|
{
|
|
sRightWheres.Add(sTemp);
|
|
}
|
|
else
|
|
{
|
|
// Too many. Fall back on a complete enum
|
|
sRightWheres.RemoveAll();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Fall back on a complete enum
|
|
sRightWheres.RemoveAll();
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Fall back on a complete enum
|
|
sRightWheres.RemoveAll();
|
|
break;
|
|
}
|
|
|
|
// This was a valid path
|
|
x++;
|
|
}
|
|
else
|
|
{
|
|
// This was an invalid path. Remove it
|
|
sRightPaths.RemoveAt(x);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function: CBinding::FindWhere
|
|
//
|
|
// At this point, we have loaded all the lefthand instances. We
|
|
// can use the binding property from these instances to build
|
|
// a where clause to be used when retrieve the righthand instances.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CBinding::FindWhere(
|
|
|
|
TRefPointerCollection<CInstance> &lefts,
|
|
CHStringArray &sLeftWheres
|
|
)
|
|
{
|
|
REFPTRCOLLECTION_POSITION posLeft;
|
|
CInstancePtr pLeft;
|
|
HRESULT hr = WBEM_S_NO_ERROR;
|
|
|
|
if (lefts.BeginEnum(posLeft))
|
|
{
|
|
variant_t vLeftBindingPropertyValue;
|
|
CHString sTemp;
|
|
|
|
// Walk the left instances
|
|
for (pLeft.Attach(lefts.GetNext(posLeft)) ;
|
|
(pLeft != NULL) ;
|
|
pLeft.Attach(lefts.GetNext(posLeft)) )
|
|
{
|
|
// Get the binding property from the left
|
|
if (pLeft->GetVariant(m_sLeftBindingPropertyName, vLeftBindingPropertyValue))
|
|
{
|
|
// Turn it into a where clause
|
|
hr = MakeString(&vLeftBindingPropertyValue, sTemp);
|
|
|
|
// See if we alread have this where clause
|
|
if (SUCCEEDED(hr) && IsInList(sLeftWheres, sTemp) == -1)
|
|
{
|
|
// A query with 1000 where clauses isn't going
|
|
// to be very efficient either. Pick a reasonable limit
|
|
if (sLeftWheres.GetSize() < MAX_ORS)
|
|
{
|
|
sLeftWheres.Add(sTemp);
|
|
}
|
|
else
|
|
{
|
|
// Too many. Fall back to enum
|
|
sLeftWheres.RemoveAll();
|
|
break;
|
|
}
|
|
}
|
|
|
|
vLeftBindingPropertyValue.Clear();
|
|
}
|
|
else
|
|
{
|
|
hr = WBEM_E_FAILED;
|
|
break;
|
|
}
|
|
}
|
|
|
|
lefts.EndEnum();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function: CBinding::MakeString
|
|
//
|
|
// Turn the bindingproperty value into a string suitable for using
|
|
// in a wql where clause.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CBinding::MakeString(VARIANT *pvValue, CHString &sTemp)
|
|
{
|
|
bool bIsString = V_VT(pvValue) == VT_BSTR;
|
|
HRESULT hr = VariantChangeType(
|
|
|
|
pvValue,
|
|
pvValue,
|
|
VARIANT_NOVALUEPROP,
|
|
VT_BSTR
|
|
);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// If the original type was string, we need to escape quotes
|
|
// and backslashes, and put double quotes around it.
|
|
if (bIsString)
|
|
{
|
|
CHString sTemp2;
|
|
EscapeCharacters(V_BSTR(pvValue), sTemp2);
|
|
|
|
sTemp.Format(L"\"%s\"", (LPCWSTR)sTemp2);
|
|
}
|
|
else
|
|
{
|
|
sTemp = V_BSTR(pvValue);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function: CBinding::IsInList
|
|
//
|
|
// See whether a given string already exists in a chstring array.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
DWORD CBinding::IsInList(
|
|
|
|
const CHStringArray &csaArray,
|
|
LPCWSTR pwszValue
|
|
)
|
|
{
|
|
DWORD dwSize = csaArray.GetSize();
|
|
|
|
for (DWORD x=0; x < dwSize; x++)
|
|
{
|
|
// Note this is a CASE INSENSITIVE compare
|
|
if (_wcsicmp(csaArray[x], pwszValue) == 0)
|
|
{
|
|
return x;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function: CBinding::CompareVariantsNoCase
|
|
//
|
|
// Compare two variants to see if they are the same.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
bool CBinding::CompareVariantsNoCase(const VARIANT *v1, const VARIANT *v2)
|
|
{
|
|
if (v1->vt == v2->vt)
|
|
{
|
|
switch (v1->vt)
|
|
{
|
|
case VT_NULL: return false;
|
|
case VT_BOOL: return (v1->boolVal == v2->boolVal);
|
|
case VT_UI1: return (v1->bVal == v2->bVal);
|
|
case VT_I2: return (v1->iVal == v2->iVal);
|
|
case VT_I4: return (v1->lVal == v2->lVal);
|
|
case VT_R4: return (v1->fltVal == v2->fltVal);
|
|
case VT_R8: return (v1->dblVal == v2->dblVal);
|
|
case VT_BSTR:
|
|
{
|
|
if ( (v1->bstrVal == v2->bstrVal) || // deal with both being NULL
|
|
(0 == _wcsicmp(v1->bstrVal, v2->bstrVal)) )
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
default:
|
|
{
|
|
// Should never get here
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function: CBinding::EscapeBackslashes
|
|
//
|
|
// Prefix " and \ characters with an additional \
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
VOID CBinding::EscapeCharacters(LPCWSTR wszIn,
|
|
CHString& chstrOut)
|
|
{
|
|
CHString chstrCpyNormPathname(wszIn);
|
|
LONG lNext = -1L;
|
|
chstrOut.Empty();
|
|
|
|
// Find the next character to escape
|
|
while( (lNext = chstrCpyNormPathname.FindOneOf(L"\"\\") ) != -1)
|
|
{
|
|
// Add on to the new string we are building:
|
|
chstrOut += chstrCpyNormPathname.Left(lNext + 1);
|
|
// Add on the second backslash:
|
|
chstrOut += _T('\\');
|
|
// Hack off from the input string the portion we just copied
|
|
chstrCpyNormPathname = chstrCpyNormPathname.Right(chstrCpyNormPathname.GetLength() - lNext - 1);
|
|
}
|
|
|
|
// If the last character wasn't a '\', there may still be leftovers, so
|
|
// copy them here.
|
|
if(chstrCpyNormPathname.GetLength() != 0)
|
|
{
|
|
chstrOut += chstrCpyNormPathname;
|
|
}
|
|
}
|