WindowsXP-SP1/admin/hmonitor/hmagent/copypaste.cpp
2020-09-30 16:53:49 +02:00

1420 lines
47 KiB
C++

//***************************************************************************
//
// COPYPASTE.CPP
//
// Module: HEALTHMON SERVER AGENT
//
// Purpose: Copying and pasting code below.
//
// Copyright (c)2000 Microsoft Corporation, All Rights Reserved
//
//***************************************************************************
#pragma warning (disable: 4786) // exceeds 255 chars in browser info
#define _WIN32_DCOM
#include "global.h"
#include "system.h"
#define ASSERT MY_ASSERT
#define TRACE(x) MY_OUTPUT(x, 4)
#include <comdef.h>
#include <wbemcli.h>
#include "SafeArray.h"
#include "StringMap.h"
// smart pointers for common WMI types
_COM_SMARTPTR_TYPEDEF(IWbemLocator, __uuidof(IWbemLocator));
_COM_SMARTPTR_TYPEDEF(IWbemServices, __uuidof(IWbemServices));
_COM_SMARTPTR_TYPEDEF(IWbemClassObject, __uuidof(IWbemClassObject));
_COM_SMARTPTR_TYPEDEF(IWbemQualifierSet, __uuidof(IWbemQualifierSet));
_COM_SMARTPTR_TYPEDEF(IWbemObjectSink, __uuidof(IWbemObjectSink));
_COM_SMARTPTR_TYPEDEF(IEnumWbemClassObject, __uuidof(IEnumWbemClassObject));
// a bunch of constant strings. Enables us to change class names without a lot of cut
// and paste
static const _bstr_t bstrLocalHealthMonNamespace(L"\\\\.\\ROOT\\CIMV2\\MicrosoftHealthMonitor");
static const _bstr_t bstrHealthMonNamespace(L"ROOT\\CIMV2\\MicrosoftHealthMonitor");
static const _bstr_t bstrBaseConfigurationPath(L"MicrosoftHM_Configuration");
static const _bstr_t bstrFilterToConsumerBindingClassPath(L"__FilterToConsumerBinding");
static const _bstr_t bstrEventConsumerClassPath(L"__EventConsumer");
static const _bstr_t bstrEventFilterClassPath(L"__EventFilter");
static const _bstr_t bstrActionAssocClassPath(L"MicrosoftHM_ConfigurationActionAssociation");
static const _bstr_t bstrActionConfigurationClassPath(L"MicrosoftHM_ActionConfiguration");
static const _bstr_t bstrTopPath(L"MicrosoftHM_SystemConfiguration.GUID=\"{@}\"");
static const _bstr_t bstrTopGUID(L"{@}");
static const _bstr_t bstrConfigurationAssocClassPath(L"MicrosoftHM_ConfigurationAssociation");
static const _bstr_t bstrThresholdConfigurationClassPath(L"MicrosoftHM_ThresholdConfiguration");
static const _bstr_t bstrDataCollectorConfigurationClassPath(L"MicrosoftHM_DataCollectorConfiguration");
static const _bstr_t bstrDataGroupConfigurationClassPath(L"MicrosoftHM_DataGroupConfiguration");
static const _bstr_t bstrSystemConfigurationClassPath(L"MicrosoftHM_SystemConfiguration");
static const _bstr_t strLanguage(L"WQL");
static const _bstr_t MOFFileHeader1(
L"////////////////////////////////////////////////////////\n"
L"// Automatically generated Health Monitor MOF dump\n"
L"// Parent Root = "
);
static const _bstr_t MOFFileHeader2(
L"\n"
L"////////////////////////////////////////////////////////\n"
L"\n"
L"#pragma autorecover\n"
L"#pragma namespace(\"\\\\\\\\.\\\\ROOT\\\\CIMV2\\\\MicrosoftHealthMonitor\")\n"
L"\n"
L"\n");
// get a BSTR-valued property
static HRESULT GetStringProperty (IWbemClassObject* pObj,
LPCWSTR lpszPropName,
_bstr_t& bstr)
{
_variant_t var;
CHECK_ERROR (pObj->Get(lpszPropName, 0, &var, NULL, NULL));
if (V_VT(&var) != VT_BSTR)
{
CHECK_ERROR (E_INVALIDARG); // bad data type. should never happen
}
CHECK_ERROR (SafeAssign (bstr, var));
return S_OK;
}
static HRESULT GetUint32Property (IWbemClassObject* pObj,
LPCWSTR lpszPropName,
DWORD& val)
{
_variant_t var;
CHECK_ERROR (pObj->Get(lpszPropName, 0, &var, NULL, NULL));
if (V_VT(&var) != VT_I4)
{
CHECK_ERROR (E_INVALIDARG); // bad data type. should never happen
}
val = V_I4(&var);
return S_OK;
}
// get a BSTR-valued property
static HRESULT PutStringProperty (IWbemClassObject* pObj,
LPCWSTR lpszPropName,
const _bstr_t& bstr)
{
_variant_t var;
CHECK_ERROR (SafeAssign (var, bstr));
CHECK_ERROR (pObj->Put(lpszPropName, 0, &var, NULL));
return S_OK;
}
static HRESULT CompareInstances (IWbemClassObject* pObj1, IWbemClassObject* pObj2, bool& bMatch)
{
// compare all the properties of these two instances
if (pObj1 == NULL || pObj2 == NULL)
return WBEM_E_INVALID_PARAMETER;
HRESULT hr = S_OK;
CHECK_ERROR (pObj1->BeginEnumeration (WBEM_FLAG_NONSYSTEM_ONLY));
while (TRUE)
{
// get property.
BSTR bstrVal;
hr = pObj1->Next(0, &bstrVal, NULL, NULL, NULL );
if (hr == WBEM_S_NO_MORE_DATA)
break; // BREAK OUT!!!! We're done.
else if (FAILED (hr))
break;
_bstr_t bstrName(bstrVal, false); // false for attach and auto-free
_variant_t var1, var2;
CIMTYPE eType1, eType2;
if (pObj1->Get(bstrName, 0, &var1, &eType1, NULL))
break;
if (pObj2->Get(bstrName, 0, &var2, &eType2, NULL))
break;
if (eType1 != eType2)
{
bMatch = false; // differnet CIM types
break;
}
else if (var1 != var2)
{
bMatch = false; // differnet values
break;
}
}
if (FAILED (hr))
{
pObj1->EndEnumeration (); // clean up just in case
CHECK_ERROR (hr);
}
CHECK_ERROR (pObj1->EndEnumeration ());
bMatch = true;
return WBEM_S_NO_ERROR;
}
// Same as Clone(), but works across machine boundaries. Clone()
// only works on local instances.
static HRESULT CopyInstance (IWbemServicesPtr& WMI,
IWbemClassObjectPtr& pObj1,
IWbemClassObjectPtr& pObj2)
{
// first, get the class so that we can spawn a new instance
_bstr_t bstrClass;
IWbemClassObjectPtr smartpClass;
CHECK_ERROR (GetStringProperty(pObj1, L"__CLASS", bstrClass));
CHECK_ERROR (WMI->GetObject (bstrClass,
WBEM_FLAG_RETURN_WBEM_COMPLETE,
NULL,
&smartpClass,
NULL));
CHECK_ERROR (smartpClass->SpawnInstance (0, &pObj2));
HRESULT hr = S_OK;
CHECK_ERROR (pObj1->BeginEnumeration (WBEM_FLAG_NONSYSTEM_ONLY));
while (TRUE)
{
// get property name
BSTR bstrVal;
hr = pObj1->Next(0, &bstrVal, NULL, NULL, NULL );
if (hr == WBEM_S_NO_MORE_DATA)
break; // BREAK OUT!!!! We're done.
else if (FAILED (hr))
break;
_bstr_t bstrName(bstrVal, false); // false for attach and auto-free
// get the property on one and copy to the other
_variant_t var;
CIMTYPE eType1;
if (FAILED(hr = pObj1->Get(bstrName, 0, &var, &eType1, NULL)))
break;
if (FAILED(hr = pObj2->Put(bstrName, 0, &var, 0)))
break;
}
if (FAILED (hr))
{
pObj1->EndEnumeration (); // clean up just in case
CHECK_ERROR (hr);
}
CHECK_ERROR (pObj1->EndEnumeration ());
return WBEM_S_NO_ERROR;
}
//
// escape a string so that it's OK to put in an object path
//
static HRESULT WmiPathEscape (LPCWSTR pszPath, _bstr_t& ResultPath)
{
// first, allocate a string twice as big. (escaping never exceeds 2x size)
BSTR bstr = ::SysAllocStringLen (NULL, wcslen(pszPath)*2);
if (bstr == NULL)
CHECK_ERROR (E_OUTOFMEMORY);
WCHAR *pDest = bstr;
for (LPCWSTR pSrc = pszPath; *pSrc; pSrc++)
{
if (*pSrc == L'\"' || *pSrc == L'\\')
{
*pDest++ = L'\\';
}
*pDest++ = *pSrc; // unescaped char
}
*pDest++ = 0; // null-terminate
CHECK_ERROR (SafeAssign(ResultPath, bstr));
::SysFreeString(bstr); // free this now that copy is made
return S_OK;
}
static HRESULT GetAssociationPath (_bstr_t& strAssocPath,
LPCWSTR szAssocClass,
LPCWSTR szInstance1Role,
LPCWSTR szInstance1Path,
LPCWSTR szInstance2Role,
LPCWSTR szInstance2Path)
{
try
{
// first, escape the object paths so that they can be encased in
// other object paths
_bstr_t bstrInstance1Path, bstrInstance2Path;
CHECK_ERROR (WmiPathEscape(szInstance1Path, bstrInstance1Path))
CHECK_ERROR (WmiPathEscape(szInstance2Path, bstrInstance2Path))
// now construct the association path
strAssocPath = szAssocClass;
strAssocPath += L".";
strAssocPath += szInstance1Role;
strAssocPath += L"=\"";
strAssocPath += bstrInstance1Path;
strAssocPath += L"\",";
strAssocPath += szInstance2Role;
strAssocPath += L"=\"";
strAssocPath += bstrInstance2Path;
strAssocPath += L"\"";
}
catch (_com_error e)
{
CHECK_ERROR (e.Error());
}
return S_OK;
}
// OK, it's an action. There are four instances:
// This method saves them all.
// a) The ConfigurationActionAssication, between the HM object and the action
// b) the action configuration itself
// c) The event filter
// d) the event consumer
// e) the filter-to-consumer binding
static HRESULT AddActionOtherInstances (
SafeArrayOneDimWbemClassObject& saInstances,
int& nArrayIndex,
IWbemClassObjectPtr& smartpParent,
IWbemServices* WMI
)
{
IWbemClassObjectPtr smartpFilter, smartpConsumer, smartpFilterToConsumerBinding;
HRESULT hr;
ULONG nRet;
// First, build paths for the filter & consumer
_bstr_t bstrActionGUID, bstrConsumerPath, bstrFilterPath;
CHECK_ERROR(GetStringProperty(smartpParent, L"GUID", bstrActionGUID));
CHECK_ERROR(GetStringProperty(smartpParent, L"EventConsumer", bstrConsumerPath));
try
{
// The Filter
bstrFilterPath = L"__EventFilter.Name=\"";
bstrFilterPath += bstrActionGUID;
bstrFilterPath += "\"";
}
catch (_com_error e)
{
CHECK_ERROR (e.Error()); // out of memory
}
// ok, now that we've got the paths, let's get the objects themselves
CHECK_ERROR (WMI->GetObject (bstrConsumerPath,
WBEM_FLAG_RETURN_WBEM_COMPLETE,
NULL,
&smartpConsumer,
NULL));
CHECK_ERROR (WMI->GetObject (bstrFilterPath,
WBEM_FLAG_RETURN_WBEM_COMPLETE,
NULL,
&smartpFilter,
NULL));
// Now query to get the Filter To Consumer Binding
_bstr_t strQueryFTCB;
try
{
strQueryFTCB = L"REFERENCES OF {";
strQueryFTCB += bstrFilterPath;
strQueryFTCB += "} WHERE ResultClass = ";
strQueryFTCB += bstrFilterToConsumerBindingClassPath;
}
catch (_com_error e)
{
CHECK_ERROR (e.Error()); // out of memory
}
// now get the Filter-to-consumer binding from WMI.
// note that this must return a valid instance or it's an error!
IEnumWbemClassObjectPtr pEnumFTCB;
CHECK_ERROR (WMI->ExecQuery (strLanguage, strQueryFTCB, 0, NULL, &pEnumFTCB));
hr = pEnumFTCB->Next(5000, 1, &smartpFilterToConsumerBinding, &nRet);
if (FAILED(hr))
CHECK_ERROR (hr); // either S_FALSE (none found) or error
if (hr == WBEM_S_TIMEDOUT)
CHECK_ERROR (RPC_E_TIMEOUT); // it timed out. bad!
if (hr != WBEM_S_NO_ERROR)
CHECK_ERROR (E_FAIL); // no associations found. bad!
// now add'em to the array
CHECK_ERROR (saInstances.SafePutElement(nArrayIndex++, smartpConsumer));
CHECK_ERROR (saInstances.SafePutElement(nArrayIndex++, smartpFilter));
CHECK_ERROR (saInstances.SafePutElement(nArrayIndex++, smartpFilterToConsumerBinding));
return S_OK;
}
static HRESULT BuildInstancesArray (
SafeArrayOneDimWbemClassObject& saInstances,
int& nArrayIndex,
IWbemClassObjectPtr& smartpParent,
IWbemServices* WMI,
bool bCopyActionsOnly = false, // only copy the actions; nothing more
bool bFirstTime = true // recursion?
)
{
ULONG nRet;
HRESULT hr;
ASSERT (smartpParent != NULL);
_bstr_t bstrParentPath;
CHECK_ERROR (GetStringProperty (smartpParent, L"__RELPATH", bstrParentPath));
// Next, deal with children. Depending on what kind of
// configuration class this is, we respond differently
_bstr_t bstrClass;
bool bCanHaveChildren = false;
if (bCopyActionsOnly)
{
bCanHaveChildren = true; // get all the actions
}
else
{
CHECK_ERROR (GetStringProperty(smartpParent,L"__CLASS", bstrClass));
// first time through, we need to store the parent object before we
// recurse to find kids
if (bFirstTime)
{
// if it's an action, store the filter, binding, etc.
// note that we need to store these first, as the
// agent needs to have the configuration show up last.
if (!_wcsicmp (bstrClass, bstrActionConfigurationClassPath))
{
CHECK_ERROR (AddActionOtherInstances (saInstances,
nArrayIndex,
smartpParent,
WMI ));
}
// store the top instance
CHECK_ERROR (saInstances.SafePutElement (nArrayIndex++, smartpParent));
}
if (!_wcsicmp (bstrClass, bstrActionConfigurationClassPath) ||
!_wcsicmp (bstrClass, bstrThresholdConfigurationClassPath))
{
// it's a threshold or action. There are never children.
return WBEM_S_NO_ERROR;
}
else
{
// it may have children. below we will recurse to handle children
bCanHaveChildren = true;
}
}
if (bCanHaveChildren)
{
// build the apppropriate queries to fetch the associators for
// this parent object. Note that there is a weird syntax for the
// ASSOCIATORS OF and REFERENCES OF queries which combines query
// clauses without using AND. See the WMI SDK descripion of
// REFERENCES OF for more details.
_bstr_t bstrQueryChildren;
try
{
// all the actions
if (bCopyActionsOnly)
{
bstrQueryChildren = "SELECT * FROM ";
bstrQueryChildren += bstrActionConfigurationClassPath;
}
else
{
// all the associations referencing us
bstrQueryChildren = L"REFERENCES OF {";
bstrQueryChildren += bstrParentPath;
bstrQueryChildren += "} WHERE ResultClass = ";
bstrQueryChildren += bstrConfigurationAssocClassPath;
bstrQueryChildren += " Role = ParentPath";
}
}
catch (_com_error e)
{
CHECK_ERROR (e.Error()); // out of memory
}
IEnumWbemClassObjectPtr pEnum;
if (FAILED (hr = WMI->ExecQuery (strLanguage, bstrQueryChildren, 0, NULL, &pEnum)))
{
// TRACE (L"%s\n", (LPCTSTR) bstrQueryChildren);
if (hr != WBEM_E_NOT_FOUND) // notfound is OK-- there may be no children. We're done.
{
CHECK_ERROR (hr);
}
}
else // everything is OK. now enumerate
{
_bstr_t bstrParentPath, bstrChildPath;
IWbemClassObjectPtr smartpAssocInstance, smartpInstance;
for ( hr = pEnum->Next(5000, 1, &smartpAssocInstance, &nRet);
SUCCEEDED(hr) && hr == WBEM_S_NO_ERROR;
hr = pEnum->Next(5000, 1, &smartpAssocInstance, &nRet))
{
// if we are just getting the actions,
if (bCopyActionsOnly)
{
// if it's an action, store the filter, binding, etc.
// note that we need to store these first, as the
// agent needs to have the configuration show up last.
CHECK_ERROR (AddActionOtherInstances (saInstances,
nArrayIndex,
smartpAssocInstance,
WMI ));
// store the instance.
CHECK_ERROR (saInstances.SafePutElement (nArrayIndex++, smartpAssocInstance));
}
else
{ // not only the actions
_variant_t var;
// now get the configuration instance that this association
// instance actually points to
CHECK_ERROR (GetStringProperty(smartpAssocInstance,
L"ChildPath",
bstrChildPath));
CHECK_ERROR (WMI->GetObject (bstrChildPath,
WBEM_FLAG_RETURN_WBEM_COMPLETE,
NULL,
&smartpInstance,
NULL));
// If it's an action, store the other subinstances as well
// note that we need to store the subinstances first, as the
// agent needs to have the configuration show up last.
CHECK_ERROR (GetStringProperty(smartpInstance,L"__CLASS", bstrClass));
if (!_wcsicmp (bstrClass, bstrActionConfigurationClassPath))
{
CHECK_ERROR (AddActionOtherInstances (saInstances,
nArrayIndex,
smartpInstance,
WMI ));
}
// store the instance.
CHECK_ERROR (saInstances.SafePutElement (nArrayIndex++, smartpInstance));
// now put the association in the array
CHECK_ERROR (saInstances.SafePutElement(nArrayIndex++, smartpAssocInstance));
// now recurse to deal with *this* object's children
// note that actions and thresholds have no kids
if (_wcsicmp (bstrClass, bstrActionConfigurationClassPath) != 0 &&
_wcsicmp (bstrClass, bstrThresholdConfigurationClassPath) != 0)
{
CHECK_ERROR (BuildInstancesArray (saInstances,
nArrayIndex,
smartpInstance,
WMI,
false // recursion!
));
}
}
}
if (hr == WBEM_S_TIMEDOUT)
CHECK_ERROR (RPC_E_TIMEOUT); // it timed out. bad!
CHECK_ERROR (hr);
}
}
return WBEM_S_NO_ERROR;
}
enum EGuidType {GUID_PLAIN, GUID_PATH, GUID_QUERY};
static HRESULT ReGuidOneProperty (IWbemClassObjectPtr& pObj,
LPCWSTR wszPropName,
StringToStringMap& GuidMap,
EGuidType eType)
{
// get the current value of the property from WMI
_variant_t var;
CIMTYPE CimType;
CHECK_ERROR (pObj->Get(wszPropName, 0, &var, &CimType, NULL));
if (var.vt != VT_BSTR)
{
ASSERT (FALSE);
return E_FAIL; // bad type of prop!
}
// OK, now pull out the BSTR and remove it from the variant
_bstr_t bstrCurrentPropValue(var.bstrVal, false);
var.Detach();
// now locate the GUID in the string
LPCWSTR pGuidStr;
WCHAR szGUID[39]; // enough space to fit a GUID in Unicode
switch (eType)
{
case GUID_PLAIN:
pGuidStr = bstrCurrentPropValue;
ASSERT (CimType == CIM_STRING);
break;
case GUID_PATH:
pGuidStr = wcschr ((LPWSTR)bstrCurrentPropValue, L'{');
ASSERT (CimType == CIM_REFERENCE);
break;
case GUID_QUERY:
pGuidStr = wcschr ((LPWSTR)bstrCurrentPropValue, L'{');
ASSERT (CimType == CIM_STRING);
break;
}
if (pGuidStr == NULL)
{
ASSERT (FALSE);
return E_FAIL; // corrupt WMI value!
}
LPCWSTR pEndBracket = wcschr (pGuidStr, '}');
if (pEndBracket == NULL)
{
ASSERT (FALSE);
return E_FAIL; // corrupt WMI value!
}
else if (pEndBracket - pGuidStr == 2 && pGuidStr[1] == '@')
{
return S_OK; // it's the system configuration instance.
// No need to re-guid.
}
else if (pEndBracket - pGuidStr != 37 )
{
ASSERT (FALSE);
return E_FAIL; // corrupt WMI value!
}
ASSERT (pGuidStr[0] == '{');
ASSERT (pGuidStr[37] == '}');
// now move the string into temp storage
wcsncpy (szGUID, pGuidStr, 38);
szGUID[38] = 0;
// have we seen this one?
_bstr_t bstrGuidNew;
bool bFound;
CHECK_ERROR (GuidMap.Find (szGUID, bstrGuidNew, bFound));
if (bFound)
{
// OK, we found this GUID there already. Use the preset
// replacement GUID
wcsncpy ((LPWSTR) pGuidStr, bstrGuidNew, 38);
}
else
{
// we haven't already seen this. Generate a new GUID, add it
// to the map
CGuidString strNewGuid;
GuidMap.Add (szGUID, strNewGuid);
wcsncpy ((LPWSTR) pGuidStr, strNewGuid, 38);
}
// OK, now we have a BSTR with the new GUID.
CHECK_ERROR (PutStringProperty (pObj, wszPropName, bstrCurrentPropValue));
return S_OK;
}
// walk through the array looking for GUID's. When we find a GUID,
// we will check a mapping table to see if it is already mapped to another
// GUID. If it is, we will replace it and move on through the list. If
// it isn't in the table, we will generate a new GUID, replace the
// one already there, and store the new GUID in the table
static HRESULT ReGuid(SafeArrayOneDimWbemClassObject& saInstances,
StringToStringMap& aGuidMap )
{
HRESULT hr;
for ( int i = 0, nLen = saInstances.GetSize(); i < nLen; i++)
{
IWbemClassObjectPtr pObj;
CHECK_ERROR (saInstances.GetElement(i, &pObj));
ASSERT (pObj != NULL);
// now check the class of this object. Depending on the class type,
_bstr_t bstrClass, bstrPath;
CHECK_ERROR (GetStringProperty (pObj, L"__RELPATH", bstrPath))
CHECK_ERROR (GetStringProperty (pObj, L"__CLASS", bstrClass));
// now re-guid the appropriate properties
if (!_wcsicmp(bstrClass, bstrActionConfigurationClassPath))
{
hr = ReGuidOneProperty (pObj, L"GUID", aGuidMap, GUID_PLAIN);
hr = ReGuidOneProperty (pObj, L"EventConsumer", aGuidMap, GUID_PATH);
}
else if (pObj->InheritsFrom(bstrBaseConfigurationPath)==S_OK)
{
// it's a configuration class, but not action config. Use the GUID property
hr = ReGuidOneProperty (pObj, L"GUID", aGuidMap, GUID_PLAIN);
}
else if (pObj->InheritsFrom(bstrEventConsumerClassPath)==S_OK)
{
hr = ReGuidOneProperty (pObj, L"Name", aGuidMap, GUID_PLAIN);
}
else if (pObj->InheritsFrom(L"__EventFilter")==S_OK)
{
hr = ReGuidOneProperty (pObj, L"Name", aGuidMap, GUID_PLAIN);
hr = ReGuidOneProperty (pObj, L"Query", aGuidMap, GUID_QUERY);
}
else if (!_wcsicmp(bstrClass, bstrFilterToConsumerBindingClassPath))
{
hr = ReGuidOneProperty (pObj, L"Consumer", aGuidMap, GUID_PATH);
hr = ReGuidOneProperty (pObj, L"Filter", aGuidMap, GUID_PATH);
}
else if (!_wcsicmp(bstrClass, bstrActionAssocClassPath))
{
hr = ReGuidOneProperty (pObj, L"ParentPath", aGuidMap, GUID_PATH);
hr = ReGuidOneProperty (pObj, L"ChildPath", aGuidMap, GUID_PATH);
hr = ReGuidOneProperty (pObj, L"EventFilter", aGuidMap, GUID_PATH);
hr = ReGuidOneProperty (pObj, L"Query", aGuidMap, GUID_QUERY);
}
else if (!_wcsicmp(bstrClass, bstrConfigurationAssocClassPath))
{
hr = ReGuidOneProperty (pObj, L"ParentPath", aGuidMap, GUID_PATH);
hr = ReGuidOneProperty (pObj, L"ChildPath", aGuidMap, GUID_PATH);
}
else
{
ASSERT (FALSE); // should never happen. it's an invalid class.
}
CHECK_ERROR (hr);
}
return S_OK;
}
//
// Detect all actions with duplicate names between the clipboard and the target
// Fill up a GUID map with the dupes
//
static HRESULT FillActionGuidMap( SafeArrayOneDimWbemClassObject& saInstances,
StringToStringMap& ActionNameToGuidMap,
StringToStringMap& GuidMap)
{
for ( int i = 0, nLen = saInstances.GetSize(); i < nLen; i++)
{
IWbemClassObjectPtr pObj;
CHECK_ERROR (saInstances.GetElement(i, &pObj));
ASSERT (pObj != NULL);
_bstr_t bstrClass, bstrPath;
CHECK_ERROR (GetStringProperty (pObj, L"__CLASS", bstrClass));
// now re-guid the appropriate properties
if (_wcsicmp(bstrClass, bstrActionConfigurationClassPath) != 0)
continue; // we only care about actions!
_bstr_t bstrGuidCurrentAction, bstrName;
bool bFound = false;
CHECK_ERROR (GetStringProperty(pObj, L"Name", bstrName));
CHECK_ERROR (ActionNameToGuidMap.Find(bstrName, bstrGuidCurrentAction, bFound));
if (bFound)
{
// there's a duplicate! We'll store a mapping of the GUID in
// the clipboard's instance to the duplicate action GUID.
_bstr_t bstrGUID;
CHECK_ERROR (GetStringProperty (pObj, L"GUID", bstrGUID));
CHECK_ERROR (GuidMap.Add(bstrGUID, bstrGuidCurrentAction));
}
}
return S_OK;
}
static HRESULT Copy(LPTSTR szSourceComputer,
LPTSTR szGUID,
bstr_t& bstrParentGUID, // [out]
SafeArrayOneDimWbemClassObject& saInstances,
CSystem& System)
{
// impersonate the caller. Important since we're now calling into
// WMI and need to make sure caller is allowed
CHECK_ERROR(CoImpersonateClient());
_bstr_t bstrNamespace = L"\\\\";
bstrNamespace += szSourceComputer;
bstrNamespace += L"\\";
bstrNamespace += bstrHealthMonNamespace;
IWbemServicesPtr WMI = g_pIWbemServices;
// IWbemLocatorPtr WMILocator;
// IWbemServicesPtr WMI;
// CHECK_ERROR (WMILocator.CreateInstance(__uuidof(WbemLocator),NULL));
// CHECK_ERROR (WMILocator->ConnectServer (bstrNamespace,
// NULL, NULL, NULL, 0, NULL, NULL,
// &WMI));
_bstr_t bstrPath;
try
{
bstrPath = bstrBaseConfigurationPath;
bstrPath += L".GUID=\"";
bstrPath += szGUID;
bstrPath += L"\"";
}
catch (_com_error e)
{
CHECK_ERROR (e.Error()); // out of memory
}
IWbemClassObjectPtr smartpObj;
HRESULT hr = WMI->GetObject (bstrPath,
WBEM_FLAG_RETURN_WBEM_COMPLETE,
NULL,
&smartpObj,
NULL);
if (hr == WBEM_E_NOT_FOUND)
{
return WBEM_E_NOT_FOUND; // it's not there
}
CHECK_ERROR (hr);
// if it's a single action, then we fake the parent to be the system instance
// same if it's the entire system that we're copying
_bstr_t bstrClass;
CHECK_ERROR (GetStringProperty(smartpObj, L"__CLASS", bstrClass));
if (!_wcsicmp (bstrClass, bstrActionConfigurationClassPath)
|| !_wcsicmp (bstrClass, bstrSystemConfigurationClassPath))
{
try
{
bstrParentGUID = bstrTopGUID;
}
catch (_com_error e)
{
CHECK_ERROR (e.Error()); // out of memory
}
}
else
{
// build the query to find our parent
_bstr_t bstrParentQuery;
try
{
bstrParentQuery = L"ASSOCIATORS OF {";
bstrParentQuery += bstrPath;
bstrParentQuery += L"} WHERE";
bstrParentQuery += L" ResultRole = ParentPath";
}
catch (_com_error e)
{
CHECK_ERROR (e.Error()); // out of memory
}
// now exec the query to fetch the parent
ULONG nRet;
IEnumWbemClassObjectPtr pEnumParent;
IWbemClassObjectPtr smartpParent;
CHECK_ERROR (WMI->ExecQuery (strLanguage, bstrParentQuery, 0, NULL, &pEnumParent));
hr = pEnumParent->Next(5000, 1, &smartpParent, &nRet);
if (FAILED(hr))
CHECK_ERROR (hr);
if (hr == WBEM_S_TIMEDOUT)
CHECK_ERROR (RPC_E_TIMEOUT); // it timed out. bad!
if (hr != WBEM_S_NO_ERROR)
CHECK_ERROR (E_FAIL); // no parent found. bad!
CHECK_ERROR (GetStringProperty(smartpParent, L"GUID", bstrParentGUID));
}
// now actually go get the array
int nArrayIndex = 0;
CHECK_ERROR (saInstances.Clear());
CHECK_ERROR (saInstances.Create());
// now build an array of all the instances underneath this one
CHECK_ERROR (BuildInstancesArray (saInstances,
nArrayIndex,
smartpObj,
WMI));
// now, if this was the entire system that we were copying, copy the
// unattached actions as well.
if (!_wcsicmp (bstrClass, bstrSystemConfigurationClassPath))
{
// now add all the actions to the array
CHECK_ERROR (BuildInstancesArray (saInstances,
nArrayIndex,
smartpObj,
WMI,
true));
}
CHECK_ERROR (saInstances.Resize(nArrayIndex));
return S_OK;
}
// the input is an array of instances to be added, in order. Add them.
// Note that actions will be compared to actions currently on the box.
static HRESULT Paste(LPCTSTR pszTargetComputer,
LPCTSTR pszTargetParentGUID,
LPCTSTR pszOriginalComputer,
LPCTSTR pszOriginalParentGUID,
SAFEARRAY* psa,
BOOL bForceReplace,
CSystem& System)
{
// impersonate the caller. Important since we're now calling into
// WMI and need to make sure caller is allowed
CHECK_ERROR(CoImpersonateClient());
_bstr_t bstrNamespace = L"\\\\";
bstrNamespace += pszTargetComputer;
bstrNamespace += L"\\";
bstrNamespace += bstrHealthMonNamespace;
TCHAR szComputerName[1024];
DWORD dwSize = 1024;
::GetComputerName(szComputerName, &dwSize);
if (!_wcsicmp(szComputerName, pszTargetComputer))
pszTargetComputer = L".";
if (!_wcsicmp(szComputerName, pszOriginalComputer))
pszOriginalComputer = L".";
IWbemServicesPtr WMI = g_pIWbemServices;
HRESULT hr;
// Commented-out code below was used when we were not part of the agent.
// IWbemLocatorPtr WMILocator;
// IWbemServicesPtr WMI;
// CHECK_ERROR (WMILocator.CreateInstance(__uuidof(WbemLocator),NULL));
// CHECK_ERROR (WMILocator->ConnectServer (bstrNamespace,
// NULL, NULL, NULL, 0, NULL, NULL,
// &WMI));
// if we didn't create the array, then don't
// bother proceeding
VARIANT varSA;
varSA.vt = VT_ARRAY | VT_UNKNOWN;
varSA.parray = psa;
if (!SafeArrayOneDimWbemClassObject::IsValid(varSA))
CHECK_ERROR (E_INVALIDARG);
// since we know that it's one of ours, and because our SafeArray wrapper
// is the same length as a regular safe array, we can cast it.
SafeArrayOneDimWbemClassObject& saInstancesOriginal = (SafeArrayOneDimWbemClassObject&)varSA;
// if we're pasting in the same parent, then the behavior should
// match Windows, where you add a " (2)" to the name and make a copy
bool bPasteOnSameMachine = !_wcsicmp (pszTargetComputer,pszOriginalComputer);
bool bPasteInSameFolder = bPasteOnSameMachine &&
!_wcsicmp (pszTargetParentGUID, pszOriginalParentGUID);
_bstr_t bstrParentRelPath;
try
{
bstrParentRelPath = bstrBaseConfigurationPath;
bstrParentRelPath += L".GUID=\"";
bstrParentRelPath += pszTargetParentGUID;
bstrParentRelPath += L"\"";
}
catch (_com_error e)
{
CHECK_ERROR (e.Error()); // out of memory
}
// Pasting Algorithm
// ==========================================================
// 1. If child with same name already exists in parent: If bOverwrite = TRUE,
// delete the other guy. If bOverwrite = FALSE, return an error.
// 2. If actions with same name but *different* contents already exists in parent:
// If bOverwrite = TRUE, delete the other actions. If bOverwrite = FALSE,
// return an error.
// 3. If actions with same name and *same* contents already exists in parent,
// reassign the to-be-pasted GUID's to match the target.
// 4. Now simply Put each member of the array.
//===========================================================
// fetch this parent instance
IWbemClassObjectPtr smartpParentInstance;
CHECK_ERROR (WMI->GetObject (bstrParentRelPath,
WBEM_FLAG_RETURN_WBEM_COMPLETE,
NULL,
&smartpParentInstance,
NULL));
// get the full, normalized path
CHECK_ERROR (GetStringProperty(smartpParentInstance, L"__RELPATH", bstrParentRelPath));
// First, get the top instance of the array. This is the parent
IWbemClassObjectPtr smartpTopInstance;
CHECK_ERROR (saInstancesOriginal.GetElement (0, &smartpTopInstance));
int nTopIndex = 0;
// now get the name of that top instance
_bstr_t bstrTopInstanceName, bstrTopInstanceClass;
CHECK_ERROR (GetStringProperty(smartpTopInstance, L"__CLASS", bstrTopInstanceClass));
if (bstrTopInstanceClass == bstrSystemConfigurationClassPath)
{
// we cannot (yet) paste the entire system
CHECK_ERROR (E_INVALIDARG);
}
// now see if we're only copying actions-- which will be the case if an
// action (or the filter, binding, etc.) is first on the list of instances
// coming back from the Copy command.
// BUGBUG: need to do something better than simply looking for the string
// "Consumer" below-- not all event consumers will contain that string!
bool bIsActionTopInstance =
bstrTopInstanceClass == bstrActionConfigurationClassPath
|| bstrTopInstanceClass == bstrFilterToConsumerBindingClassPath
|| wcsstr ((LPCTSTR) bstrTopInstanceClass, L"Consumer") != NULL
|| bstrTopInstanceClass == bstrEventFilterClassPath;
if (bIsActionTopInstance)
{
// we need to find the name of the action. But the action is not on top-- it could
// be any of the top 4 instances (action config, filter, consumer or binding). So go look.
// we've already checked the zeroth, so we can start at 1.
for (int i = 1; i < 4; i++)
{
CHECK_ERROR (saInstancesOriginal.GetElement (i, &smartpTopInstance));
CHECK_ERROR (GetStringProperty(smartpTopInstance, L"__CLASS", bstrTopInstanceClass));
if (bstrTopInstanceClass == bstrActionConfigurationClassPath)
{
// found it! we will get the name
nTopIndex = i;
break;
}
}
if (i == 4)
{
CHECK_ERROR (E_INVALIDARG); // uh-oh. the instance array was corrupted.
}
}
// finally, get the name of the instance
CHECK_ERROR (GetStringProperty(smartpTopInstance, L"Name", bstrTopInstanceName));
// Build a query to look for siblings in the new parent
_bstr_t bstrQueryChildren;
try
{
if (bIsActionTopInstance)
{
// for actions, there is no parent. Just list all actions
bstrQueryChildren = L"SELECT * FROM MicrosoftHM_ActionConfiguration";
}
else
{
// first, make a query for get kids of this parent
bstrQueryChildren = L"ASSOCIATORS OF {";
bstrQueryChildren += bstrParentRelPath;
bstrQueryChildren += L"} WHERE";
bstrQueryChildren += L" ResultRole = ChildPath";
}
}
catch (_com_error e)
{
CHECK_ERROR (e.Error()); // out of memory
}
// now execute this query. Look for siblings with matching (i.e. conflicting) names
StringToStringMap TopLevelNameConflictMap;
_bstr_t bstrConflictGUID;
IEnumWbemClassObjectPtr pEnum;
// TRACE (L"%s\n", (LPCTSTR) bstrQueryChildren);
if (FAILED (hr = WMI->ExecQuery (strLanguage, bstrQueryChildren, 0, NULL, &pEnum)))
{
// TRACE (L"%s\n", (LPCTSTR) bstrQueryChildren);
if (hr != WBEM_E_NOT_FOUND) // notfound is OK-- there may be no children. We're done.
{
CHECK_ERROR (hr);
}
}
else
{
_bstr_t bstrChildName;
IWbemClassObjectPtr smartpInstance;
ULONG nRet;
for ( hr = pEnum->Next(5000, 1, &smartpInstance, &nRet);
SUCCEEDED(hr) && hr == WBEM_S_NO_ERROR;
hr = pEnum->Next(5000, 1, &smartpInstance, &nRet))
{
_bstr_t bstrChildName, bstrChildClass;
CHECK_ERROR (GetStringProperty(smartpInstance, L"Name", bstrChildName));
CHECK_ERROR (GetStringProperty(smartpInstance, L"__CLASS", bstrChildClass));
// if this child is an action, then the only case where we care about
// conflicts is in the top-level. Otherwise, the actions are not really
// "children" (they're just associated via the ConfigActionAssociation class)
// so we can ignore them. But if this is an action as the
// top instance, you bet we want to check for conflicts!
if (!bIsActionTopInstance && (bstrChildClass == bstrActionConfigurationClassPath))
continue;
// store this for later, in case we have to rename the top item.
if (bPasteInSameFolder)
CHECK_ERROR(TopLevelNameConflictMap.Add(bstrChildName, L"NotUsed"));
// check for conflict
if (!_wcsicmp (bstrChildName, bstrTopInstanceName))
{
CHECK_ERROR (GetStringProperty(smartpInstance, L"GUID", bstrConflictGUID));
if (!bPasteInSameFolder)
{
// uh, oh. We have a name conflict. Depending on whether the
// user wants to perform any overwrites, we will either have to
// delete the conflicting one or return an error.
if (bForceReplace)
{
TRACE(L"Paste: Instance Name Conflict. We will delete the conflicting one");
break;
}
else
{
TRACE(L"Paste: Instance Name Conflict. Returning an error.");
return WBEM_E_ALREADY_EXISTS;
}
}
}
}
if (hr == WBEM_S_TIMEDOUT)
CHECK_ERROR (RPC_E_TIMEOUT); // it timed out. bad!
CHECK_ERROR (hr);
}
// OK, Now build a map of the names of actions already on the target
// and their GUID's
try
{
bstrQueryChildren = L"SELECT * FROM ";
bstrQueryChildren += bstrActionConfigurationClassPath;
}
catch (_com_error e)
{
CHECK_ERROR (e.Error()); // out of memory
}
// we will store a hashtable of mapping action names to their
// GUID's. This will help us in re-GUIDing and in identifying conflicts.
StringToStringMap ActionNameToGuidMap;
// now execute this query
if (FAILED (hr = WMI->ExecQuery (strLanguage, bstrQueryChildren, 0, NULL, &pEnum)))
{
// TRACE (L"%s\n", (LPCTSTR) bstrQueryChildren);
if (hr != WBEM_E_NOT_FOUND) // notfound is OK-- there may be no children. We're done.
{
CHECK_ERROR (hr);
}
}
else
{
_bstr_t bstrChildName;
IWbemClassObjectPtr smartpInstance;
ULONG nRet;
for ( hr = pEnum->Next(5000, 1, &smartpInstance, &nRet);
SUCCEEDED(hr) && hr == WBEM_S_NO_ERROR;
hr = pEnum->Next(5000, 1, &smartpInstance, &nRet))
{
_bstr_t bstrActionName, bstrActionGUID;
CHECK_ERROR (GetStringProperty(smartpInstance, L"Name", bstrActionName));
CHECK_ERROR (GetStringProperty(smartpInstance, L"GUID", bstrActionGUID));
CHECK_ERROR (ActionNameToGuidMap.Add(bstrActionName, bstrActionGUID));
}
}
// OK, now we will make a copy of the array.
bool bAlreadyFound = false;
SafeArrayOneDimWbemClassObject saInstancesNew;
CHECK_ERROR (saInstancesNew.Create());
for (int i = 0, nLen = saInstancesOriginal.GetSize(); i < nLen; i++)
{
IWbemClassObjectPtr smartpInstance;
CHECK_ERROR (saInstancesOriginal.GetElement (i, &smartpInstance));
// make a copy & store it. Note that it's a no-no to take instances
// from one machine and PutInstance them on another machine-- they will
// fail intermittently. So we in turn do a "manual clone" here to
// spawn the instance locally and copy the properties one by one
// On the same machine, it's faster and more efficient to use Clone(),
// so we do.
IWbemClassObjectPtr smartpNewInstance;
if (bPasteOnSameMachine)
{
CHECK_ERROR (smartpInstance->Clone(&smartpNewInstance));
}
else
{
CHECK_ERROR (CopyInstance (WMI, smartpInstance, smartpNewInstance));
}
CHECK_ERROR (saInstancesNew.SafePutElement(i, smartpNewInstance));
// reassign, considering that we're re-GUIDing it
if (i == nTopIndex)
smartpTopInstance = smartpNewInstance;
/*
if (!bAlreadyFound)
{
// get name, GUID, and class
_bstr_t bstrClass;
CHECK_ERROR (GetStringProperty(smartpNewInstance, "__CLASS", &bstrClass);
// if it's action-related, see if it's already there
if (!_wcsicmp(bstrFilterToConsumerBindingClassPath, bstrClass)
|| !_wcsicmp(bstrEventConsumerClassPath, bstrClass)
|| !_wcsicmp(bstrActionAssocClassPath, bstrClass)
|| !_wcsicmp(bstrActionConfigurationClassPath, bstrClass) )
{
CHECK_ERROR (GetStringProperty(smartpNewInstance, "Name", &bstrActionName);
_bstr_t bstrGuidNew, bstrName;
bool bFound;
CHECK_ERROR (GetStringProperty(pObj, L"Name", bstrName));
CHECK_ERROR (GuidMap.Find(bstrName, bstrGuidNew, bFound));
if (bFound)
{
}
}
*/
}
// trim the new array
CHECK_ERROR (saInstancesNew.Resize(nLen));
// now find all the actions we're associated to, compare their names
// to actions on the target machine, and fill up a hashtable map
// with the list of conflicts. Note that if this is just one action that
// we're copying locally, there's no need to look for conflicts because
// we're renaming the action anyway.
StringToStringMap GuidMap;
if (! (bIsActionTopInstance && bPasteInSameFolder) )
{
CHECK_ERROR (FillActionGuidMap(saInstancesNew, ActionNameToGuidMap, GuidMap));
int nDuplicateCount = GuidMap.GetSize();
if (nDuplicateCount > 0 && !bPasteOnSameMachine)
{
// uh, oh. We have a name conflict. Depending on whether the
// user wants to perform any overwrites, we will either have to
// delete the conflicting one or return an error.
if (bForceReplace)
{
TRACE(L"Paste: Action Name Conflict. We will delete the conflicting one");
}
else
{
TRACE(L"Paste: Action Name Conflict. Returning an error.");
return WBEM_E_ALREADY_EXISTS;
}
}
}
// Now, let's change all the GUID's (except for the actions
// that we'll be replacing, as above).
CHECK_ERROR (ReGuid (saInstancesNew, GuidMap));
// Now, let's see if I need to rename the top-level item to
// resolve name conflicts
if ((LPCWSTR)bstrConflictGUID != NULL)
{
if (bPasteInSameFolder)
{
// if we get here, we're renaming our top item to avoid
// a name conflict, just like Explorer does
i = 2;
bool bFound = true;
_bstr_t bstrName;
do
{
// compose a name, like Foo (2), Foo (3), etc. like explorer does
WCHAR szNumber[20];
bstrName = bstrTopInstanceName;
bstrName += L" (";
bstrName += _itow (i++, szNumber, 10);
bstrName += L")";
_bstr_t NotUsed;
CHECK_ERROR (TopLevelNameConflictMap.Find(bstrName, NotUsed, bFound));
} while (bFound);
// when we get to here, we've found a free name
CHECK_ERROR (PutStringProperty(smartpTopInstance, L"Name", bstrName));
}
else if (! bIsActionTopInstance)
{
// we're almost there. Now we need to delete the top-level item on the
// target, if present, to make room for us. Use the delete method.
// note that we *do not* go through this codepath for actions, because
// for actions we will just lay down a new action, with the same GUID,
// right over the old one. If we actually did call delete (via the agent)
// the agent would clobber all the action assocations, whereas we want to
// keep them there and have the new action just fall right into place
IWbemClassObjectPtr smartpSystemClass, smartpInParamsClass,
smartpInParamsInstance, smartpResults;
// impersonate the caller. Important since we're now calling into
// WMI and need to make sure caller is allowed
CHECK_ERROR(CoImpersonateClient());
// BUGBUG: For next release, we need to think about how to provide
// safer functionality here, so that we don't actually delete the
// target until we've successfully added the new stuff
// until then, just delete the conflict
CHECK_ERROR(System.FindAndDeleteByGUID(bstrConflictGUID));
/*
// now that this code lives inside the agent, there's no need to call
// an external method on the agent. It's much easier-- just call
// the FindAndDeleteByGUID function on the system class!
// get the system class
CHECK_ERROR (WMI->GetObject (bstrSystemConfigurationClassPath,
WBEM_FLAG_RETURN_WBEM_COMPLETE,
NULL,
&smartpSystemClass,
NULL));
// get the delete method in-params "class"
CHECK_ERROR (smartpSystemClass->GetMethod (L"Delete", 0, &smartpInParamsClass, NULL));
CHECK_ERROR (smartpInParamsClass->SpawnInstance (0, &smartpInParamsInstance));
CHECK_ERROR (PutStringProperty(smartpInParamsInstance, L"TargetGUID", bstrTopInstanceGUID));
CHECK_ERROR (WMI->ExecMethod(bstrSystemConfigurationClassPath,
L"Delete",
WBEM_FLAG_RETURN_WBEM_COMPLETE,
NULL,
smartpInParamsInstance,
&smartpResults,
NULL));
DWORD dwReturnValue;
CHECK_ERROR (GetUint32Property (smartpResults, L"ReturnValue", dwReturnValue));
CHECK_ERROR (dwReturnValue);
*/
}
}
// Now we need to compute the association to link the new instances up to the
// parent instance. This should get the agent to pick up the change and set
// all the balls in motion. Note that actions don't require parent associations--
// actions are just global instances available everywhere.
IWbemClassObjectPtr smartpAssocInstance;
if (! bIsActionTopInstance)
{
IWbemClassObjectPtr smartpAssocClass;
_bstr_t bstrParentPath, bstrTopInstancePath, bstrTopInstanceRelPath;
CHECK_ERROR (GetStringProperty(smartpTopInstance, L"__RELPATH", bstrTopInstanceRelPath));
try
{
bstrParentPath = bstrLocalHealthMonNamespace
+ L":"
+ bstrParentRelPath;
bstrTopInstancePath = bstrLocalHealthMonNamespace
+ L":"
+ bstrTopInstanceRelPath;
}
catch (_com_error e)
{
CHECK_ERROR (e.Error());
}
// now create the association
CHECK_ERROR (WMI->GetObject (bstrConfigurationAssocClassPath,
WBEM_FLAG_RETURN_WBEM_COMPLETE,
NULL,
&smartpAssocClass,
NULL));
CHECK_ERROR(smartpAssocClass->SpawnInstance(0, &smartpAssocInstance));
CHECK_ERROR(PutStringProperty(smartpAssocInstance, L"ParentPath", bstrParentPath));
CHECK_ERROR(PutStringProperty(smartpAssocInstance, L"ChildPath", bstrTopInstancePath));
}
// Whew! Finally, we're ready to paste the new items. Let 'er rip!
for (i = 0, nLen = saInstancesNew.GetSize(); i < nLen; i++)
{
IWbemClassObjectPtr smartpInstance;
CHECK_ERROR (saInstancesNew.GetElement (i, &smartpInstance));
_bstr_t bstrClass, bstrPath;
CHECK_ERROR (GetStringProperty(smartpInstance, L"__RELPATH", bstrPath));
CHECK_ERROR (GetStringProperty(smartpInstance, L"__CLASS", bstrClass));
// If the user is pasting on the same machine, there's no need to write
// actions, since we will simply use associations to the same actions.
// The one exception is if we're pasting a single action (which will
// result in a new action being laid down).
if (bPasteOnSameMachine && ! bIsActionTopInstance)
{
if (!_wcsicmp(bstrFilterToConsumerBindingClassPath, bstrClass)
|| !_wcsicmp(bstrEventConsumerClassPath, bstrClass)
|| !_wcsicmp(bstrEventFilterClassPath, bstrClass)
|| !_wcsicmp(bstrActionConfigurationClassPath, bstrClass) )
{
continue; // skip if it's an action
}
}
// TODO: as an optmization, we should consider not laying down
// multiple copies of the same action. WMI should handle our saves as
// simple instance modification events, but it would speed things up
// to filter out multiple copies of actions.
// impersonate the caller. Important since we're now calling into
// WMI and need to make sure caller is allowed
CHECK_ERROR(CoImpersonateClient());
// finally, lay down the new instance!
HRESULT hr = WMI->PutInstance(smartpInstance,
WBEM_FLAG_RETURN_WBEM_COMPLETE | WBEM_FLAG_CREATE_OR_UPDATE,
NULL,
NULL);
if (hr == WBEM_E_ACCESS_DENIED)
{
// we may have trouble overwriting action instances when those
// were created by another user. But since admin users should
// be able to override this restriction, and only admins can
// use HM in this release, I think that we're OK.
// BUGBUG: in future HM releases, we should consider deleting
// old action instances if the SID's are different from ours.
}
if (FAILED(hr))
{
CHECK_ERROR (hr);
}
if (i == 0 && (bool) smartpAssocInstance)
{
// now that we've stored the top-level parent, let's add the association
// to the parent. There's a bug in the rest of the agent that it won't
// pick up action associations unless the parent instance is already
// associated. Hopefully this will be fixed, but until then we'll link up
// the new top instance right at first.
CHECK_ERROR(WMI->PutInstance(smartpAssocInstance,
WBEM_FLAG_RETURN_WBEM_COMPLETE | WBEM_FLAG_CREATE_OR_UPDATE,
NULL,
NULL));
}
//BUGBUG: shouldn't lay down anything that's already there and the same as us!
//BUGBUG: before calling it a conflict, must compare properties!
}
return S_OK;
}
// Below are the actual entry points for the old agent code
// to call the new copy & paste functionality
HRESULT CSystem::AgentPaste(LPTSTR pszTargetGUID,
SAFEARRAY* psa,
LPTSTR pszOriginalSystem,
LPTSTR pszOriginalParentGUID,
BOOL bForceReplace)
{
HRESULT hr = Paste(L".", pszTargetGUID, pszOriginalSystem,
pszOriginalParentGUID, psa, bForceReplace, *this);
// BUGBUG: the agent returns the HRESULT of the Paste back
// through the __ReturnValue of the method, not through WMI
// errors. This is bad-- needs to be changed.
if (hr == WBEM_E_ALREADY_EXISTS)
return 2; // 2 is what console recognizes here.
else if (FAILED(hr))
return hr;
else
return 0;
}
HRESULT CSystem::AgentCopy(LPTSTR pszGUID,
SAFEARRAY** ppsa,
LPTSTR *pszOriginalParentGUID)
{
SafeArrayOneDimWbemClassObject sa;
CHECK_ERROR (sa.Create());
_bstr_t bstrParentGUID;
// now call the underlying copy routine
HRESULT hr = Copy(L".", pszGUID, bstrParentGUID, sa, *this);
CHECK_ERROR (hr);
VARIANT var = sa.Detach();
*ppsa = var.parray;
// copy the BSTR into a new[] string, as the caller expects.
// should really use a smart pointer or _bstr_t instead....
TCHAR* p = new TCHAR[bstrParentGUID.length()+1];
if (p == NULL)
CHECK_ERROR (E_OUTOFMEMORY);
wcscpy (p, bstrParentGUID);
*pszOriginalParentGUID = p;
return hr;
}