1420 lines
46 KiB
C++
1420 lines
46 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;
|
|
}
|