//***************************************************************************
//
//  Copyright (c) 1998-2000 Microsoft Corporation.
//
//  File:  cwbemdsp.cpp
//
//	Description :
//				Implementation of the IDispatch interface for Wbem Objects.
//				This is mostly standard, except for the additional support for
//				specifying the name of a Wbem class property/method directly as if it
//				was a property/method of the actual CWbemObject class ("dot notation")
//
//	Part of :	WBEM automation interface layer
//
//  History:	
//		corinaf			4/3/98		Created
//
//***************************************************************************

#include "precomp.h"

const unsigned long		CWbemDispID::s_wmiDispIdTypeMask = 0x03000000;
const unsigned long		CWbemDispID::s_wmiDispIdTypeStatic = 0x00000000;
const unsigned long		CWbemDispID::s_wmiDispIdTypeSchema = 0x01000000;
const unsigned long		CWbemDispID::s_wmiDispIdSchemaTypeMask = 0x00800000;
const unsigned long		CWbemDispID::s_wmiDispIdSchemaTypeProperty = 0x00800000;
const unsigned long		CWbemDispID::s_wmiDispIdSchemaTypeMethod = 0x00000000;
const unsigned long		CWbemDispID::s_wmiDispIdSchemaElementIDMask = 0x007FFFFF;

//Forward declaration

HRESULT assignArrayElementToVariant(SAFEARRAY *psa, VARTYPE vt, long inx, VARIANT *pvResult);
void assignVariantToArrayElement(SAFEARRAY *psa, VARTYPE vt, long inx, VARIANT *pvNewVal);
VARTYPE CimTypeToVtType(CIMTYPE lType);
HRESULT VariantChangeByValToByRef(VARIANT *dest, VARIANT *source, VARTYPE destType);

class CWbemSchemaIDCache
{
private:
	typedef map<CComBSTR, CWbemDispID, BSTRless> DispIDNameMap;

	unsigned long				m_nextId;
	DispIDNameMap				m_cache;
	CWbemDispatchMgr			*m_pDispatchMgr;

	bool	FindPropertyName (BSTR bsName);
	bool	GetMethod (BSTR bstrMethodName, SAFEARRAY **ppsaInParams, SAFEARRAY **ppsaOutParams,
						CComPtr<IWbemClassObject> & pInParams, CComPtr<IWbemClassObject> & pOutParams);
	bool	GetIdOfMethodParameter(BSTR bstrParamName, CComPtr<IWbemClassObject> & pParams, long *pId);

	static bool FindMemberInArray(BSTR bstrName, SAFEARRAY *psaNames);

public:
	CWbemSchemaIDCache (CWbemDispatchMgr *pDispMgr) :
				m_nextId (0),
				m_pDispatchMgr (pDispMgr) {}
	virtual ~CWbemSchemaIDCache ();

	HRESULT	GetDispID (LPWSTR FAR* rgszNames, unsigned int cNames, DISPID FAR* rgdispid); 
	bool	GetName (DISPID dispId, CComBSTR & bsName);
};


CWbemDispatchMgr::CWbemDispatchMgr(CSWbemServices *pWbemServices,
								   CSWbemObject *pSWbemObject) :
			m_pWbemServices (pWbemServices),
			m_pSWbemObject (pSWbemObject),	// Backpointer to parent (not AddRef'd)
			m_pWbemClass (NULL),
			m_pTypeInfo (NULL),
			m_pCTypeInfo (NULL),
			m_hResult (S_OK)
{
	m_pSchemaCache = new CWbemSchemaIDCache (this);

	if (m_pWbemServices)
		m_pWbemServices->AddRef ();

	m_pWbemObject = pSWbemObject->GetIWbemClassObject ();
}

CWbemDispatchMgr::~CWbemDispatchMgr()
{
	RELEASEANDNULL(m_pWbemServices)
	RELEASEANDNULL(m_pWbemObject)
	RELEASEANDNULL(m_pWbemClass)
	RELEASEANDNULL(m_pTypeInfo)
	RELEASEANDNULL(m_pCTypeInfo)
	DELETEANDNULL(m_pSchemaCache)
}

void	CWbemDispatchMgr::SetNewObject (IWbemClassObject *pNewObject)
{
	if (m_pWbemObject && pNewObject)
	{
		m_pWbemObject->Release ();
		m_pWbemObject = pNewObject;
		m_pWbemObject->AddRef ();

		CComVariant var;

		if (SUCCEEDED(pNewObject->Get (WBEMS_SP_GENUS, 0, &var, NULL, NULL)) &&
			(WBEM_GENUS_CLASS == var.lVal))
		{
			// This is a class, so update the class object too
			if (m_pWbemClass)
				m_pWbemClass->Release ();

			m_pWbemClass = pNewObject;
			m_pWbemClass->AddRef ();
		}

		// Clear out the caches
		if (m_pSchemaCache)
		{
			delete m_pSchemaCache;
			m_pSchemaCache = new CWbemSchemaIDCache (this);
		}
	}
}

STDMETHODIMP
CWbemDispatchMgr::GetTypeInfoCount(unsigned int FAR* pctinfo)
{
    *pctinfo = 1;
	return NOERROR;
}


STDMETHODIMP CWbemDispatchMgr::GetTypeInfo(unsigned int itinfo, 
							  LCID lcid,
							  ITypeInfo FAR* FAR* pptinfo)
{
	HRESULT hr;
	ITypeLib *pTypeLib = NULL;

	//If Type Info is not cached already - load the library and 
	//get the Type Info, then cache it for further access
	if (!m_pTypeInfo)
	{

		// Load Type Library. 
		hr = LoadRegTypeLib(LIBID_WbemScripting, 1, 0, lcid, &pTypeLib);
		if (FAILED(hr)) 
		{   
			// if it wasn't registered, try to load it from the path
			// if this succeeds, it will have registered the type library for us
			// for the next time.  
			hr = LoadTypeLib(OLESTR("wbemdisp.tlb"), &pTypeLib); 
			if(FAILED(hr))        
				return hr;   
		}
    
		// Get type information for interface of the object.  
		hr = pTypeLib->GetTypeInfoOfGuid(IID_ISWbemObjectEx, &m_pTypeInfo);
		pTypeLib->Release();
		if (FAILED(hr))  
			return hr;

	}

	//AddRef whenever returning another pointer to this
	m_pTypeInfo->AddRef();
	*pptinfo = m_pTypeInfo;

    return NOERROR;
}

STDMETHODIMP CWbemDispatchMgr::GetClassInfo(ITypeInfo FAR* FAR* pptinfo)
{
	HRESULT hr;
	ITypeLib *pTypeLib = NULL;

	//If Type Info is not cached already - load the library and 
	//get the Type Info, then cache it for further access
	if (!m_pCTypeInfo)
	{

		// Load Type Library. 
		hr = LoadRegTypeLib(LIBID_WbemScripting, 1, 0, 0, &pTypeLib);
		if (FAILED(hr)) 
		{   
			// if it wasn't registered, try to load it from the path
			// if this succeeds, it will have registered the type library for us
			// for the next time.  
			hr = LoadTypeLib(OLESTR("wbemdisp.tlb"), &pTypeLib); 
			if(FAILED(hr))        
				return hr;   
		}
    
		// Get type information for coclass of the object.  
		hr = pTypeLib->GetTypeInfoOfGuid(CLSID_SWbemObjectEx, &m_pCTypeInfo);
		pTypeLib->Release();
		if (FAILED(hr))  
			return hr;

	}

	//AddRef whenever returning another pointer to this
	m_pCTypeInfo->AddRef();
	*pptinfo = m_pCTypeInfo;

    return NOERROR;
}

STDMETHODIMP
CWbemDispatchMgr::GetIDsOfNames(REFIID iid,  //always IID_NULL
								LPWSTR FAR* rgszNames,
								unsigned int cNames, 
								LCID lcid, 
								DISPID FAR* rgdispid)
{
    HRESULT hr = E_FAIL;
	CComPtr<ITypeInfo> pITypeInfo;

	if (SUCCEEDED(hr = GetTypeInfo(0, lcid, &pITypeInfo)))
	{
		// See if this is a static property or method
		if (FAILED(hr = DispGetIDsOfNames(pITypeInfo,
							   rgszNames,
							   cNames,
							   rgdispid)))
		{
			// Not static - try schema
			if (m_pSchemaCache && FAILED(hr = m_pSchemaCache->GetDispID (rgszNames, cNames, rgdispid)))
			{
				rgdispid[0] = DISPID_UNKNOWN;
				hr = DISP_E_UNKNOWNNAME;
			}
		}
	}

	return hr;

}


STDMETHODIMP CWbemDispatchMgr::Invoke(DISPID dispidMember, 
						 REFIID iid, LCID lcid,
						 unsigned short wFlags, 
						 DISPPARAMS FAR* pdispparams,
						 VARIANT FAR* pvarResult, 
						 EXCEPINFO FAR* pexcepinfo,
						 unsigned int FAR* puArgErr)
{
	HRESULT hr;
	ITypeInfo *pTypeInfo = NULL;

	//Get the type info
	hr = GetTypeInfo(0, lcid, &pTypeInfo);
	if (FAILED(hr))
		return hr;

	m_hResult = S_OK;

	CWbemDispID dispId (dispidMember);

	// Is this a regular dispId
	if (dispId.IsStatic ())
	{
		// Check for inbound NULLs masquerading as defaulted parameters
		if (wFlags & DISPATCH_METHOD)
			MapNulls (pdispparams);

		hr = DispInvoke((IDispatch *) ((ISWbemObjectEx *)m_pSWbemObject),
				        pTypeInfo,
						dispidMember,
						wFlags,
						pdispparams,
						pvarResult,
						pexcepinfo,
						puArgErr
						);

		if (FAILED(hr))
		{
			// Try the error handler for this object in case it can handle this
			hr = HandleError (dispidMember, wFlags, pdispparams, pvarResult, puArgErr, hr);
		}
	}
	else if (dispId.IsSchema ())
	{
		//Otherwise - this is a WBEM property or method, so we implement
		//the invocation ourselves...

		ResetLastErrors ();
	
		if (dispId.IsSchemaMethod ()) //WBEM method
			hr = InvokeWbemMethod(dispidMember, 
								  pdispparams,
								  pvarResult);
		else if (dispId.IsSchemaProperty ()) //WBEM property
			hr = InvokeWbemProperty(dispidMember, 
									wFlags, 
									pdispparams, 
									pvarResult,
									pexcepinfo,
									puArgErr);
		else
			hr = DISP_E_MEMBERNOTFOUND;

		if (FAILED(hr))
			RaiseException (hr);
	}

	if (FAILED (m_hResult))
	{
		if (NULL != pexcepinfo)
			SetException (pexcepinfo, m_hResult, L"SWbemObjectEx");

		hr = DISP_E_EXCEPTION;
	}

	if (pTypeInfo)
		pTypeInfo->Release();

	return hr;
}

HRESULT
CWbemDispatchMgr::InvokeWbemProperty(DISPID dispid, 
									 unsigned short wFlags, 
								     DISPPARAMS FAR* pdispparams, 
									 VARIANT FAR* pvarResult,
									 EXCEPINFO FAR* pexcepinfo,
									 unsigned int FAR* puArgErr)
{
	HRESULT hr = E_FAIL;

	if (m_pSchemaCache)
	{
		BOOL bIsGetOperation = (DISPATCH_PROPERTYGET & wFlags);

		if (bIsGetOperation)
		{
			//Check that the output parameter is valid
			if (pvarResult == NULL)
				return E_INVALIDARG;
		}
		else
		{
			//Check input parameters
			if ((pdispparams->cArgs < 1) || (pdispparams->cArgs > 2)) 
				return DISP_E_BADPARAMCOUNT;

			if ((pdispparams->cNamedArgs != 1) ||
				(pdispparams->cNamedArgs == 1 && 
				 pdispparams->rgdispidNamedArgs[0] != DISPID_PROPERTYPUT))
				return DISP_E_PARAMNOTOPTIONAL;
		}	

		//For both get & put, we need to first get the property 
		//             (for put we need to validate the syntax)
		CComBSTR bsPropertyName;

		if (m_pSchemaCache->GetName (dispid, bsPropertyName))
		{
			SAFEARRAY *psaNames = NULL;
			long inx;
			VARIANT vPropVal;
			long lArrayPropInx;
			CIMTYPE lPropType;

			//Get the value of this property
			//-------------------------------------
			VariantInit(&vPropVal);
			if (FAILED (hr = m_pWbemObject->Get(bsPropertyName, 0, &vPropVal, &lPropType, NULL)))
			{
				return hr;
			}

			// The expected VT type for the proposed property value
			VARTYPE expectedVarType =  CimTypeToVtType (lPropType & ~CIM_FLAG_ARRAY);

			//If we are in a get operation
			//----------------------------------
			if (bIsGetOperation)
			{
				//If the property is an embedded object, we might need to convert it from 
				//a VT_UNKNOWN to a VT_DISPATCH
				if (SUCCEEDED(hr = MapFromCIMOMObject(m_pWbemServices, &vPropVal, 
										m_pSWbemObject, bsPropertyName)))
				{
					//If the property is an array, need to check for index and get that element
					if ((lPropType & CIM_FLAG_ARRAY) && (pdispparams->cArgs > 0))
					{
						//Note: currently we support single dimension arrays only, so we only
						//      look for one index
						VARIANT indexVar;
						VariantInit (&indexVar);
						// Attempt to coerce the index argument into a value suitable for an array index
						if (S_OK == VariantChangeType (&indexVar, &pdispparams->rgvarg[0], 0, VT_I4)) 
						{
							lArrayPropInx = V_I4(&indexVar);

							//Fill in the result variant with the requested array element
							hr = assignArrayElementToVariant(vPropVal.parray, (V_VT(&vPropVal) & ~VT_ARRAY),
													lArrayPropInx, pvarResult);
						}
						else
							hr = DISP_E_TYPEMISMATCH;

						VariantClear (&indexVar);
					}
					else //If it's not an array index - copy to output param and we're done
					{
						// Check if it's an array value and convert as necessary
						if (V_ISARRAY(&vPropVal))
							hr = ConvertArrayRev(pvarResult, &vPropVal);
           				else
							hr = VariantCopy (pvarResult, &vPropVal);
					}
				}
			} //Property Get

			//Otherwise (put operation)
			//---------------------------------
			else
			{
				/*
				 * Need to translate this into a call to SWbemProperty.put_Value: easiest way
				 * to do this is to 
				 * (A) get the SWbemProperty object for this property
				 * (B) Call IDispatch::Invoke on that object, passing in the value
				 * This way we get the error handling behavior too.
				 */

				CComPtr<ISWbemPropertySet> pISWbemPropertySet;

				if (SUCCEEDED(hr = m_pSWbemObject->get_Properties_ (&pISWbemPropertySet)))
				{
					CComPtr<ISWbemProperty> pISWbemProperty;

					if (SUCCEEDED(hr = pISWbemPropertySet->Item (bsPropertyName, 0, &pISWbemProperty)))
					{
						// NB: The Value property of ISWbemProperty is the "default" automation property
						hr = pISWbemProperty->Invoke (
										DISPID_VALUE,
										IID_NULL, 
										0,
										wFlags,
										pdispparams, 
										pvarResult,
										pexcepinfo,
										puArgErr);

						// Use our more specific error here if we have one
						if (FAILED(hr) && pexcepinfo)
							hr = pexcepinfo->scode;
					}
				}

			} //Property Put

			VariantClear(&vPropVal);
		}
	}

	return hr;

} 

//***************************************************************************
//
//  SCODE CWbemDispatchMgr::InvokeWbemMethod
//
//  DESCRIPTION:
//
//  Invoke the method via direct access.  
//
//  PARAMETERS:
//
//		dispid			The dispid od the method
//		pdispparams		Pointer to DISPPARAMS for this invocation
//		pvarResult		On successful return holds return value (if any)
//
//  RETURN VALUES:
//
//***************************************************************************

HRESULT CWbemDispatchMgr::InvokeWbemMethod(
	DISPID dispid, 
	DISPPARAMS FAR* pdispparams, 
	VARIANT FAR* pvarResult
)
{
	HRESULT hr = E_FAIL;

	if (m_pWbemServices && m_pSchemaCache)
	{
		//Currently we don't support named arguments
		if (pdispparams->cNamedArgs > 0)
			return DISP_E_NONAMEDARGS;

		//Map the dispid to a method name
		CComBSTR bsMethodName;

		if (m_pSchemaCache->GetName (dispid, bsMethodName))
		{
			// Build up the inparameters (if any)
			CComPtr<IWbemClassObject> pInParameters;
			CComPtr<IWbemClassObject> pOutParameters;

			//Get the input parameters object of the method (may be NULL)
			if (SUCCEEDED (hr = m_pWbemClass->GetMethod(bsMethodName, 0, &pInParameters, 
															&pOutParameters)))
			{
				CComPtr<IWbemClassObject> pInParamsInstance;

				if (pInParameters)
					hr = MapInParameters (pdispparams, pInParameters, &pInParamsInstance);
				
				if (SUCCEEDED (hr))
				{
					CComPtr<IWbemServices> pService = m_pWbemServices->GetIWbemServices ();

					if (pService)
					{
						// Need the RELPATH to specify the target class or instance
						VARIANT vObjectPathVal;
						VariantInit(&vObjectPathVal);
			
						if (SUCCEEDED (hr = m_pWbemObject->Get
											(WBEMS_SP_RELPATH, 0, &vObjectPathVal, NULL, NULL)))
						{
							/*
							 * If a "keyless" object slips through the net its __RELPATH
							 * value will be VT_NULL.  At this point we should fail gracefully.
							 */
							if 	(VT_BSTR == V_VT(&vObjectPathVal))
							{
								// Execute the CIMOM method 
								CComPtr<IWbemClassObject> pOutParamsInstance;
									
								bool needToResetSecurity = false;
								HANDLE hThreadToken = NULL;
								CSWbemSecurity *pSecurityInfo = m_pWbemServices->GetSecurityInfo ();
			
								if (pSecurityInfo && pSecurityInfo->SetSecurity (needToResetSecurity, hThreadToken))
								{
			
									if (SUCCEEDED(hr = pService->ExecMethod(V_BSTR(&vObjectPathVal), 
											bsMethodName, 0, NULL,
											pInParamsInstance, &pOutParamsInstance, NULL)))
										hr = MapOutParameters (pdispparams, pOutParameters,
																pOutParamsInstance,	pvarResult);

									SetWbemError (m_pWbemServices);
								}

								if (pSecurityInfo)
								{
									// Restore original privileges on this thread
									if (needToResetSecurity)
										pSecurityInfo->ResetSecurity (hThreadToken);

									pSecurityInfo->Release ();
								}
							}
							else
								hr = WBEM_E_FAILED;
						}

						VariantClear (&vObjectPathVal);
					}
				}		
			}
		}
	}

	return hr;
}

//***************************************************************************
//
//  SCODE CWbemDispatchMgr::MapOutParameters
//
//  DESCRIPTION:
//
//  Invoke the method via direct access.  
//
//  PARAMETERS:
//
//		dispparams			Pointer to DISPPARAMS for this invocation
//		pOutParameters		Class template for out parameters
//		pOutParamsInstance	Addresses the IWbemClassObject to hold the
//							out parameters (if any) - may be NULL
//		pvarResult			On successful return holds return value (if any)
//
//  RETURN VALUES:
//
//***************************************************************************

HRESULT CWbemDispatchMgr::MapOutParameters (
	DISPPARAMS FAR* pdispparams,
	IWbemClassObject *pOutParameters,
	IWbemClassObject *pOutParamsInstance,
	VARIANT FAR* pvarResult
)
{
	HRESULT hr = S_OK;

	//For each "out" parameter in the output parameters object (if there is one), 
	//find it's id, then look for the parameter with this id in the arguments array
	//and set the return parameter value accordingly
	//----------------------------------------------------------------------------

	if (pOutParameters && pOutParamsInstance)
	{
		//Start an enumeration through the "out" parameters class template
		if (SUCCEEDED (hr = pOutParameters->BeginEnumeration(WBEM_FLAG_NONSYSTEM_ONLY)))
		{
			BSTR bstrId = SysAllocString(L"id");
			BSTR bstrParamName = NULL;
						
			/*
			 * For each property in the outparams class template, get the [id]
			 * to map the relevant posistional value in the pdispparams.
			 */
			while (WBEM_S_NO_ERROR == 
				(hr != pOutParameters->Next(0, &bstrParamName, NULL, NULL, NULL)))
			{
				// Get the returned parameter value from the instance
				VARIANT vParamVal;
				VariantInit(&vParamVal);
				
				if (SUCCEEDED (pOutParamsInstance->Get (bstrParamName, 0, &vParamVal, NULL, NULL)))
				{
					//If this is the return value, set it separately
					if (!_wcsicmp(bstrParamName, L"ReturnValue"))
					{
						if (pvarResult)
							hr = MapReturnValue (pvarResult, &vParamVal);
					}
					//Otherwise - regular out parameter
					else
					{
						IWbemQualifierSet *pQualSet = NULL;
						
						//Get the id of this parameter (it's the "id" qualifier)
						if (SUCCEEDED (hr = pOutParameters->GetPropertyQualifierSet
													(bstrParamName, &pQualSet)))
						{
							VARIANT vIdVal;
							VariantInit(&vIdVal);

							if (SUCCEEDED (hr = pQualSet->Get(bstrId, 0, &vIdVal, NULL)))
							{
								//Calculate the position of this id in the arguments array
								long pos = (pdispparams->cArgs - 1) - V_I4(&vIdVal);

								// If its out of range, too bad
								if ((0 <= pos) && (pos < (long) pdispparams->cArgs))
									hr = MapOutParameter (&pdispparams->rgvarg[pos], &vParamVal);
							}

							VariantClear(&vIdVal);
							pQualSet->Release();	
						}
					}
				}

				VariantClear (&vParamVal);
				SysFreeString (bstrParamName);
				bstrParamName = NULL;
			} //while

			SysFreeString (bstrId);
		}
	} //if pOutParameters
		
	return hr;
} 

//***************************************************************************
//
//  SCODE CWbemDispatchMgr::MapReturnValue
//
//  DESCRIPTION:
//
//  Map the method return value
//
//  PARAMETERS:
//
//		pDest	On successful return holds return value (if any)
//		pSrc	The variant value to map	
//		
//
//  RETURN VALUES:
//
//***************************************************************************

HRESULT CWbemDispatchMgr::MapReturnValue (
	VARIANT FAR* pDest,
	VARIANT FAR* pSrc
)
{
	HRESULT hr = S_OK;

	//If the return value is a VT_UNKNOWN, we need to wrap into a 
	//VT_DISPATCH before passing it back
	if (SUCCEEDED (hr = MapFromCIMOMObject(m_pWbemServices, pSrc)))
	{
		// Handle arrays correctly (must always be VT_ARRAY|VT_VARIANT)
		if(V_VT(pSrc) & VT_ARRAY)
			hr = ConvertArrayRev(pDest, pSrc);
		else
			hr = VariantCopy (pDest, pSrc);
	}
		
	return hr;
}

//***************************************************************************
//
//  SCODE CWbemDispatchMgr::MapOutParameter
//
//  DESCRIPTION:
//
//  Map a (possibly by reference) out parameter
//
//  PARAMETERS:
//
//		pDest	On successful return holds return value (if any)
//		pVal	The variant value to map	
//		
//
//  RETURN VALUES:
//
//***************************************************************************

HRESULT CWbemDispatchMgr::MapOutParameter (
	VARIANT FAR* pDest,
	VARIANT FAR* pSrc
)
{
	HRESULT hr = S_OK;

	//If the return value is a VT_UNKNOWN, we need to wrap into a 
	//VT_DISPATCH before passing it back
	if (SUCCEEDED (hr = MapFromCIMOMObject(m_pWbemServices, pSrc)))
	{
		VARIANT tempVal;
		VariantInit (&tempVal);
		
		// Handle arrays correctly (must always be VT_ARRAY|VT_VARIANT)
		if(V_VT(pSrc) & VT_ARRAY)
			hr = ConvertArrayRev(&tempVal, pSrc);
		else
			hr = VariantCopy (&tempVal, pSrc);
			
		// Finally take care of ensuring we produce BYREFs if necessary
		if (SUCCEEDED (hr))
			 hr = VariantChangeByValToByRef(pDest, &tempVal, V_VT(pDest));

		VariantClear (&tempVal);
	}

	return hr;
}

								
//***************************************************************************
//
//  SCODE CWbemDispatchMgr::MapInParameters
//
//  DESCRIPTION:
//
//  Map the in parameters to a method
//
//  PARAMETERS:
//
//		pdispparams			DISPPARAMS containing the in parameters
//		pInParameters		Class template for method input parameters
//		ppInParamsInstance	On successful return holds the mapped parameters
//
//  RETURN VALUES:
//
//***************************************************************************

HRESULT CWbemDispatchMgr::MapInParameters (
	DISPPARAMS FAR* pdispparams, 
	IWbemClassObject *pInParameters,
	IWbemClassObject **ppInParamsInstance
)
{
	HRESULT hr = S_OK;
	*ppInParamsInstance = NULL;

	//Spawn an instance to fill in with values
	if (SUCCEEDED (hr = pInParameters->SpawnInstance(0, ppInParamsInstance)))
	{
		/*
		 * Iterate through the "in" parameters object properties in the class to find the
		 * ID positional qualifier.  Note we do this in the InParams class rather than
		 * the spawned instance to protect ourselves against the case where the [id]
		 * qualifier has been declared without the "propagate to instance" flavor setting,
		 */
		if (SUCCEEDED (hr = pInParameters->BeginEnumeration(WBEM_FLAG_NONSYSTEM_ONLY)))
		{
			BSTR bstrParamName = NULL;
			BSTR bstrId = SysAllocString(L"id");
			CIMTYPE lType;

			//For each property in the inparams object
			while (WBEM_S_NO_ERROR == 
						(hr = pInParameters->Next(0, &bstrParamName, NULL, &lType, NULL)))
			{
				IWbemQualifierSet *pQualSet = NULL;
			
				//Get the id of this parameter (it's the "id" qualifier)
				if (SUCCEEDED(hr = 
						pInParameters->GetPropertyQualifierSet(bstrParamName, &pQualSet)))
				{
					VARIANT vIdVal;
					VariantInit(&vIdVal);
				
					if (SUCCEEDED(hr = pQualSet->Get(bstrId, 0, &vIdVal, NULL)))
					{
						//Calculate the position of this id in the arguments array
						long pos = (pdispparams->cArgs - 1) - V_I4(&vIdVal);

						// If no argument specified, we won't set it in ppInParamsInstance
						// and just assume it will be defaulted
						if ((0 <= pos) && (pos < (long) pdispparams->cArgs))
						{
							VARIANT vParamVal;
							VariantInit (&vParamVal);
							
							if (SUCCEEDED (hr = MapInParameter 
										(&vParamVal, &pdispparams->rgvarg[pos], lType)))
							{
								// If we have a VT_ERROR with DISP_E_PARAMNOTFOUND this
								// is a "missing" parameter - we just fail to set it and 
								// let it default in the instance

								if ((VT_ERROR == V_VT(&vParamVal)) && (DISP_E_PARAMNOTFOUND == vParamVal.scode))
								{
									// Let it default
								}
								else
								{
									//Copy the value for this parameter from the argument array
									//into the inparamsinstance object property
									hr = (*ppInParamsInstance)->Put(bstrParamName, 0, &vParamVal, NULL);
								}
							}

							VariantClear (&vParamVal);
						}
					}

					VariantClear(&vIdVal);
					pQualSet->Release();
					pQualSet = NULL;
				}

				SysFreeString (bstrParamName);						
				bstrParamName = NULL;

				if (FAILED(hr))
					break;
			} //while

			SysFreeString (bstrId);
		}
	}

	return hr;
}

//***************************************************************************
//
//  SCODE CWbemDispatchMgr::MapInParameter
//
//  DESCRIPTION:
//
//  Map a in parameter
//
//  PARAMETERS:
//
//		pDest	On successful return holds return value
//		pVal	The variant value to map	
//		lType	CIMTYPE of target property value
//		
//
//  RETURN VALUES:
//
//***************************************************************************

HRESULT CWbemDispatchMgr::MapInParameter (
	VARIANT FAR* pDest,
	VARIANT FAR* pSrc,
	CIMTYPE		 lType
)
{
	HRESULT hr = S_OK;

	if ((NULL == pSrc) || (VT_EMPTY == V_VT(pSrc)) 
							|| (VT_NULL == V_VT(pSrc)))
	{
		// Map all of these to a VT_NULL
		pDest->vt = VT_NULL;
	}
	else if (((VT_ARRAY | VT_VARIANT) == V_VT(pSrc)) ||
			 ((VT_ARRAY | VT_VARIANT | VT_BYREF) == V_VT(pSrc)))
	{
		// Arrays need to be mapped "down" to their raw form (and watch out
		// for embedded objects!)
		if (SUCCEEDED(hr = ConvertArray(pDest, pSrc)))
            hr = MapToCIMOMObject(pDest);
	}
	else if ((CIM_FLAG_ARRAY & lType) && 
			((VT_DISPATCH == V_VT(pSrc)) 
			 || ((VT_DISPATCH|VT_BYREF) == V_VT(pSrc))))
	{
		// Look for a JScript-style IDispatch that needs to be mapped to an array
		hr = ConvertDispatchToArray (pDest, pSrc, lType & ~CIM_FLAG_ARRAY);
	}
	else if ((VT_BYREF | VT_VARIANT) == V_VT(pSrc))
	{
		// May be used if the scripting language supports functions that can change
		// the type of a reference.  CIMOM won't do this, wo we unwrap the
		// variant before proceeding
		hr = MapInParameter (pDest, pSrc->pvarVal, lType);
	}
	else
	{
		// A "straightforward" value - all we have to watch for is an embedded object
		// and a possible byRef
		if (SUCCEEDED(hr = VariantCopy (pDest, pSrc)))
		{
			hr = MapToCIMOMObject(pDest);

			// Is it byref - if so remove the indirection
			if (VT_BYREF & V_VT(pDest))
				hr = VariantChangeType(pDest, pDest, 0, V_VT(pDest) & ~VT_BYREF);
		}
	}			

	return hr;
}

//-------------------------------------------------------------
// CWbemDispatchMgr::RaiseException
//
// Description : signal exception to automation client
//
// Parameters : hr - HRESULT
//-------------------------------------------------------------
void CWbemDispatchMgr::RaiseException (HRESULT hr)
{
	// Store the HRESULT for processing in the Invoke routine
	m_hResult = hr;

	// Set a WMI scripting error on this thread for the client
	ICreateErrorInfo *pCreateErrorInfo = NULL;

	if (SUCCEEDED (CreateErrorInfo (&pCreateErrorInfo)))
	{
		BSTR bsDescr = MapHresultToWmiDescription (hr);
		pCreateErrorInfo->SetDescription (bsDescr);
		SysFreeString (bsDescr);
		pCreateErrorInfo->SetGUID (IID_ISWbemObjectEx);
		pCreateErrorInfo->SetSource (L"SWbemObjectEx");
	
		IErrorInfo *pErrorInfo = NULL;

		if (SUCCEEDED (pCreateErrorInfo->QueryInterface(IID_IErrorInfo, (void**) &pErrorInfo)))
		{
			SetErrorInfo (0, pErrorInfo);
			pErrorInfo->Release ();
		}

		pCreateErrorInfo->Release ();
	}
}					

//-------------------------------------------------------------
// Name : assignArrayElementToVariant
//
// Description : According to the type of the array elements,
//			     retrieves the requested element from the array
//				 into a variant
//
// Parameters : psa - pointer to the SAFEARRAY
//				vt -  vartype of array elements
//				inx - index of the element in the array
//				pvResult - resulting variant
//-------------------------------------------------------------
HRESULT assignArrayElementToVariant(SAFEARRAY *psa, VARTYPE vt, long inx, VARIANT *pvResult)
{
	HRESULT hr = WBEM_S_NO_ERROR;

	switch (vt)
	{
		case VT_I2 :
			V_VT(pvResult) = VT_I2;
			SafeArrayGetElement(psa, &inx, &V_I2(pvResult));
			break;
		case VT_I4 :
			V_VT(pvResult) = VT_I4;
			SafeArrayGetElement(psa, &inx, &V_I4(pvResult));
			break;
		case VT_R4 :
			V_VT(pvResult) = VT_R4;
			SafeArrayGetElement(psa, &inx, &V_R4(pvResult));
			break;
		case VT_R8 :
			V_VT(pvResult) = VT_R8;
			SafeArrayGetElement(psa, &inx, &V_R8(pvResult));
			break;
		case VT_DATE :
			V_VT(pvResult) = VT_DATE;
			SafeArrayGetElement(psa, &inx, &V_DATE(pvResult));
			break;
		case VT_BSTR : 
			V_VT(pvResult) = VT_BSTR;
			SafeArrayGetElement(psa, &inx, &V_BSTR(pvResult));
			break;
		case VT_DISPATCH :
			V_VT(pvResult) = VT_DISPATCH;
			SafeArrayGetElement(psa, &inx, &V_DISPATCH(pvResult));
			break;
		case VT_UNKNOWN :
			V_VT(pvResult) = VT_UNKNOWN;
			SafeArrayGetElement(psa, &inx, &V_UNKNOWN(pvResult));
			break;
		case VT_BOOL :
			V_VT(pvResult) = VT_BOOL;
			SafeArrayGetElement(psa, &inx, &V_BOOL(pvResult));
			break;
		case VT_VARIANT :
		{
			V_VT(pvResult) = VT_BYREF | VT_VARIANT;
			VARIANT *pVar = new VARIANT;

			if (pVar)
			{
				VariantInit (pVar);
				SafeArrayGetElement(psa, &inx, pVar);
				V_VARIANTREF(pvResult) = pVar;
			}
			else
				hr = WBEM_E_OUT_OF_MEMORY;
		}
			break;
		case VT_UI1 : 
			V_VT(pvResult) = VT_UI1;
			SafeArrayGetElement(psa, &inx, &V_UI1(pvResult));
			break;
		default :
			V_VT(pvResult) = VT_ERROR;
			break;
	}

	return hr;
}

//-------------------------------------------------------------
// Name : CheckArrayBounds
//
// Description : Check that index is within bounds and if not
//				 Redim the array
//
// Parameters : psa - pointer to the SAFEARRAY
//				inx - putative index
//-------------------------------------------------------------
void CheckArrayBounds(SAFEARRAY *psa, long inx)
{
	long lBound, uBound;
	SafeArrayGetUBound (psa, 1, &uBound);
	SafeArrayGetLBound (psa, 1, &lBound);

	if ((inx < lBound) || (inx > uBound))
	{
		// Need to redim
		SAFEARRAYBOUND psaBound;
	
		psaBound.cElements = ((inx < lBound) ? 
			(uBound + 1 - inx) : (inx + 1 - lBound));

		psaBound.lLbound = (inx < lBound) ? inx : lBound;
		SafeArrayRedim (psa, &psaBound);
	}
}
	
//-------------------------------------------------------------
// Name : assignVariantToArrayElement
//
// Description : According to the type of the array elements,
//			     puts the new value from the variant into the
//				 requested element of the array
//
// Parameters : psa - pointer to the SAFEARRAY
//				vt -  vartype of array elements
//				inx - index of the element in the array
//				pvNewVal - variant containing the new value
//-------------------------------------------------------------
void assignVariantToArrayElement(SAFEARRAY *psa, VARTYPE vt, long inx, VARIANT *pvNewVal)
{
	HRESULT hr = E_FAIL;

	// Firstly check for out-of-bounds case and grow accordingly
	CheckArrayBounds (psa, inx);
	
	switch (vt)
	{
		case VT_I2 :
			hr = SafeArrayPutElement(psa, &inx, &V_I2(pvNewVal));
			break;
		case VT_I4 :
			hr = SafeArrayPutElement(psa, &inx, &V_I4(pvNewVal));
			break;
		case VT_R4 :
			hr = SafeArrayPutElement(psa, &inx, &V_R4(pvNewVal));
			break;
		case VT_R8 :
			hr = SafeArrayPutElement(psa, &inx, &V_R8(pvNewVal));
			break;
		case VT_DATE :
			hr = SafeArrayPutElement(psa, &inx, &V_DATE(pvNewVal));
			break;
		case VT_BSTR : 
			hr = SafeArrayPutElement(psa, &inx, V_BSTR(pvNewVal));
			break;
		case VT_DISPATCH :
			hr = SafeArrayPutElement(psa, &inx, V_DISPATCH(pvNewVal));
			break;
		case VT_UNKNOWN:
			hr = SafeArrayPutElement(psa, &inx, V_UNKNOWN(pvNewVal));
			break;
		case VT_BOOL :
			hr = SafeArrayPutElement(psa, &inx, &V_BOOL(pvNewVal));
			break;
		case VT_VARIANT :
			hr = SafeArrayPutElement(psa, &inx, V_VARIANTREF(pvNewVal));
			break;
		case VT_UI1 : 
			hr = SafeArrayPutElement(psa, &inx, &V_UI1(pvNewVal));
			break;
		default :
			//????????????
			break;
	} //switch
}


//-------------------------------------------------------------
// Name : CimTypeToVtType
//
// Description : Returns the coresponding VARTYPE for
//				 a given CIMTYPE
// Parameters : lType - the CIMTYPE we want to convert
//-------------------------------------------------------------
VARTYPE CimTypeToVtType(CIMTYPE lType)
{
	VARTYPE ret = VT_EMPTY;

	if (lType & CIM_FLAG_ARRAY)
		ret = VT_ARRAY;

	switch(lType & ~CIM_FLAG_ARRAY)
	{
		case CIM_EMPTY :	ret = (ret | VT_EMPTY); break;
		case CIM_SINT8 :	ret = (ret | VT_I2); break;
		case CIM_UINT8 :	ret = (ret | VT_UI1); break;
		case CIM_SINT16 :	ret = (ret | VT_I2); break;
		case CIM_UINT16 :	ret = (ret | VT_I4); break;
		case CIM_SINT32 :	ret = (ret | VT_I4); break;
		case CIM_UINT32 :	ret = (ret | VT_I4); break;
		case CIM_SINT64 :	ret = (ret | VT_BSTR); break;
		case CIM_UINT64 :	ret = (ret | VT_BSTR); break;
		case CIM_REAL32 :	ret = (ret | VT_R4); break;
		case CIM_REAL64 :	ret = (ret | VT_R8); break;
		case CIM_BOOLEAN :	ret = (ret | VT_BOOL); break;
		case CIM_STRING :	ret = (ret | VT_BSTR); break;
		case CIM_DATETIME :	ret = (ret | VT_BSTR); break;
		case CIM_REFERENCE :ret = (ret | VT_BSTR); break;
		case CIM_CHAR16 :	ret = (ret | VT_I2); break;
		case CIM_OBJECT :	ret = (ret | VT_UNKNOWN); break;
		default : ret = VT_ERROR;
	}

	return ret;
}


//-------------------------------------------------------------
// Name : VariantChangeByValToByRef
//
// Description : Copies a variant, while converting a "byval" to a 
//				 "byref" if the destination type requires it
//
// Parameters : dest - destination variant to hold the result
//				source - source variant to be copied
//				destType - the VARTYPE required for the result.
//					       when this type is a BY_REF, the appropriate
//						   conversion is made from the source.
//-------------------------------------------------------------
HRESULT VariantChangeByValToByRef(VARIANT *dest, VARIANT *source, VARTYPE destType)
{
	HRESULT hr = S_OK;

	if (!(destType & VT_BYREF)) //the destination is not by ref. we can do a straight copy
		hr = VariantCopy(dest, source);
	else
	{
		if ((destType & ~VT_BYREF) & VT_ARRAY)
			hr = SafeArrayCopy(V_ARRAY(source), V_ARRAYREF(dest));
		else
		{
			switch (destType & ~VT_BYREF)
			{
				case VT_UI1 :  *V_UI1REF(dest) = V_UI1(source); break;
				case VT_I2 :   *V_I2REF(dest) = V_I2(source); break;
				case VT_I4 :   *V_I4REF(dest) = V_I4(source); break;
				case VT_R4 :   *V_R4REF(dest) = V_R4(source); break;
				case VT_R8 :   *V_R8REF(dest) = V_R8(source); break;
				case VT_CY :   *V_CYREF(dest) = V_CY(source); break;
				case VT_BSTR : SysReAllocString(V_BSTRREF(dest), V_BSTR(source)); break;
				case VT_BOOL : *V_BOOLREF(dest) = V_BOOL(source); break;
				case VT_DATE : *V_DATEREF(dest) = V_DATE(source); break;
				case VT_DISPATCH : 
						//I need to addref the object behind this interface so
						//that it doesn't get released when we release the original VARIANT
						//that's holding it
						V_DISPATCH(source)->AddRef();
						*V_DISPATCHREF(dest) = V_DISPATCH(source); 
						break;
				case VT_UNKNOWN : 
						//Again, need to addref so that the object doesn't get released
						V_UNKNOWN(source)->AddRef();
						*V_UNKNOWNREF(dest) = V_UNKNOWN(source); break;
						break;
				case VT_VARIANT : hr = VariantCopy(V_VARIANTREF(dest), source); break;
				default : hr = DISP_E_TYPEMISMATCH;
			}
		}
	}

	return hr;

}

//***************************************************************************
//
//  void CWbemDispatchMgr::EnsureClassRetrieved
//
//  DESCRIPTION:
//
//  Make sure we have a class pointer
//
//***************************************************************************

void CWbemDispatchMgr::EnsureClassRetrieved ()
{
	if (!m_pWbemClass)
	{
		CComVariant vGenusVal, vClassName;
		bool bIsClass;

		if (SUCCEEDED(m_pWbemObject->Get(WBEMS_SP_GENUS, 0, &vGenusVal, NULL, NULL)))
		{
			bIsClass = (WBEM_GENUS_CLASS == vGenusVal.lVal);

			//If the object is a class, point the class pointer to it as well
			if (bIsClass)
			{
				m_pWbemClass = m_pWbemObject;
				m_pWbemClass->AddRef () ;
			}
			//Otherwise (it's an instance) we need to get the class
			else
			{
				// Check we have an IWbemServices pointer

				if (m_pWbemServices)
				{
					/*
					 * Note we must check that returned value is a BSTR - it could be a VT_NULL if
					 * the __CLASS property has not yet been set.
					 */
							
					if (SUCCEEDED(m_pWbemObject->Get(WBEMS_SP_CLASS, 0, &vClassName, NULL, NULL)) 
						&& (VT_BSTR == V_VT(&vClassName)))
					{
						CComPtr<IWbemServices> pIWbemServices = m_pWbemServices->GetIWbemServices ();

						if (pIWbemServices)
						{
							CSWbemSecurity *pSecurity = m_pWbemServices->GetSecurityInfo ();

							if (pSecurity)
							{
								bool needToResetSecurity = false;
								HANDLE hThreadToken = NULL;
						
								if (pSecurity->SetSecurity (needToResetSecurity, hThreadToken))
									pIWbemServices->GetObject (vClassName.bstrVal, 0, NULL, &m_pWbemClass, NULL);
												
								// Restore original privileges on this thread
								if (needToResetSecurity)
									pSecurity->ResetSecurity (hThreadToken);
										
								pSecurity->Release ();
							}
						}
					}
				}
			}
		}
	}
}

//***************************************************************************
//
//  SCODE CWbemDispatchMgr::HandleError
//
//  DESCRIPTION:
//
//  Provide bespoke handling of error conditions in the bolierplate
//	Dispatch implementation.
//
//  PARAMETERS:
//
//		dispidMember, wFlags,
//		pdispparams, pvarResult,
//		puArgErr,					All passed directly from IDispatch::Invoke
//		hr							The return code from the bolierplate invoke
//
//  RETURN VALUES:
//		The new return code (to be ultimately returned from Invoke)
//
//  WBEM_S_NO_ERROR				success
//	WBEM_E_INVALID_PARAMETER	bad input parameters
//  WBEM_E_FAILED				otherwise
//
//***************************************************************************

HRESULT CWbemDispatchMgr::HandleError (
	DISPID dispidMember,
	unsigned short wFlags,
	DISPPARAMS FAR* pdispparams,
	VARIANT FAR* pvarResult,
	UINT FAR* puArgErr,
	HRESULT hr
)
{
	/*
	 * We are looking for GET calls on the Derivation_ property which
	 * supplied an argument.  Since this property returns a SAFEARRAY, this may
	 * be legal but undetectable by the standard Dispatch mechanism. It is meaningful 
	 * to pass an index (the interpretation is that the index specifies an offset in
	 * the SAFEARRAY structure that represents the derivation value).
	 */
	if ((dispidMember == WBEMS_DISPID_DERIVATION) && (DISP_E_NOTACOLLECTION == hr) && (1 == pdispparams->cArgs)
		&& (DISPATCH_PROPERTYGET & wFlags))
	{
		// Looks promising - get the __DERIVATION property to try and resolve this
		if (m_pWbemObject)
		{
			VARIANT var;
			VariantInit (&var);
			
			if (WBEM_S_NO_ERROR == m_pWbemObject->Get (WBEMS_SP_DERIVATION, 0, &var, NULL, NULL))
			{
				/* The value should be a VT_BSTR|VT_ARRAY */
				if (((VT_ARRAY | VT_BSTR) == var.vt) && (NULL != var.parray))
				{
					VARIANT indexVar;
					VariantInit (&indexVar);

					// Attempt to coerce the index argument into a value suitable for an array index
					if (S_OK == VariantChangeType (&indexVar, &pdispparams->rgvarg[0], 0, VT_I4)) 
					{
						long lArrayPropInx = V_I4(&indexVar);

						// We should have a VT_ARRAY|VT_BSTR value at this point; extract the
						// BSTR and set it into the VARIANT
						VariantInit (pvarResult);
						BSTR nameValue = NULL;
						if (SUCCEEDED(hr = SafeArrayGetElement (var.parray, &lArrayPropInx, &nameValue)))
						{
							VariantInit (pvarResult);
							pvarResult->vt = VT_BSTR;
							pvarResult->bstrVal = nameValue;
						}
					}
					else
					{
							hr = DISP_E_TYPEMISMATCH;
							if (puArgErr)
								*puArgErr = 0;
					}

					VariantClear (&indexVar);
				}
			}

			VariantClear (&var);
		}
	}
	
	return hr;
}


// IDispatchEx methods
HRESULT STDMETHODCALLTYPE CWbemDispatchMgr::GetDispID( 
	/* [in] */ BSTR bstrName,
	/* [in] */ DWORD grfdex,
	/* [out] */ DISPID __RPC_FAR *pid)
{
	return GetIDsOfNames(IID_NULL, &((OLECHAR *)bstrName), 1, ENGLISH_LOCALE, pid);
}

//***************************************************************************
//
//  SCODE CWbemSchemaIDCache::~CWbemSchemaIDCache
//
//  DESCRIPTION:
//
//		Destructor
//
//***************************************************************************

CWbemSchemaIDCache::~CWbemSchemaIDCache ()
{
	DispIDNameMap::iterator next; 

	while ((next = m_cache.begin ()) != m_cache.end ())
		next = m_cache.erase (next);
}

//***************************************************************************
//
//  SCODE CWbemSchemaIDCache::GetDispID
//
//  DESCRIPTION:
//
//  Attempts to resolves a set of names to DISP IDs based on WMI schema.
//
//  PARAMETERS:
//
//		rgszNames				Array of names
//		cNames					Length of above array
//		rgdispid				Pointer to array to hold resolved DISPIDs
//
//  RETURN VALUES:
//
//***************************************************************************

HRESULT CWbemSchemaIDCache::GetDispID (
	LPWSTR* rgszNames, 
	unsigned int cNames, 
	DISPID* rgdispid
)
{
	HRESULT hr = E_FAIL;	

	if (0 < cNames)
	{
		DispIDNameMap::iterator theIterator = m_cache.find (rgszNames [0]);

		if (theIterator != m_cache.end ())
		{
			hr = S_OK;
			rgdispid [0] = (*theIterator).second;
		}
		else
		{
			if ((1 == cNames) && FindPropertyName (rgszNames [0]))
			{
				// Get a new dispid and add it to the cache
				CWbemDispID dispId;
		
				if (dispId.SetAsSchemaID (++m_nextId))
				{
					rgdispid [0] = dispId;
					m_cache.insert (DispIDNameMap::value_type (rgszNames [0], 
											dispId));
					hr = S_OK;
				}
			}
			else
			{
				//If no property name matches, go on to methods
				SAFEARRAY *psaInParams = NULL;	//array of in parameters names
				SAFEARRAY *psaOutParams = NULL; //array of out parameter names
				CComPtr<IWbemClassObject> pInParams;
				CComPtr<IWbemClassObject> pOutParams;
				bool bMethodFound = false;
				long id = 0;
				bool bUnknownParameterFound = false;

				//Get the names of all method parameters (in and out)
				if (GetMethod (rgszNames[0], &psaInParams, &psaOutParams,
											pInParams, pOutParams))
				{	
					bMethodFound = true;
					unsigned long ulParamCount;
					bool ok = true;
		
					//For each named parameter, search for it in the method parameters
					for (ulParamCount=1; ok && (ulParamCount < cNames); ulParamCount++)
					{
						//If we find this name in the "in" parameters list, attach the id and go on
						if (psaInParams && FindMemberInArray(rgszNames[ulParamCount], psaInParams))
						{
							if (GetIdOfMethodParameter(rgszNames[ulParamCount], //param name
														pInParams, 
														&id))
								rgdispid[ulParamCount] = id;
							else
								ok = false;
						}
						//If it's not in the "in" parameters, check the "out" parameters list
						else if (psaOutParams && FindMemberInArray(rgszNames[ulParamCount], psaOutParams))
						{
							if (GetIdOfMethodParameter(rgszNames[ulParamCount], //param name
														pOutParams, 
														&id))
								rgdispid[ulParamCount] = id;
							else 
								ok = false;
						}
						//If it's not there either - we can't find it
						else
						{
							rgdispid[ulParamCount] = DISPID_UNKNOWN;
							bUnknownParameterFound = true;
						}
					} //walk parameters

					if (!ok)
						bMethodFound = false;
				}

				if (psaInParams)
					SafeArrayDestroy(psaInParams);

				if (psaOutParams)
					SafeArrayDestroy(psaOutParams);

				if (!bMethodFound)
					hr = E_FAIL;
				else if (bUnknownParameterFound) 
					hr = DISP_E_UNKNOWNNAME;
				else
					hr = S_OK;

				// Finally, if this all worked add it to the cache as a method
				if (SUCCEEDED(hr))
				{
					CWbemDispID dispId;
					
					if (dispId.SetAsSchemaID (++m_nextId, false))
					{
						rgdispid [0] = dispId;
						m_cache.insert (DispIDNameMap::value_type (rgszNames [0], 
									dispId));
					}
					else
						hr = E_FAIL;
				}
			}
		}
	}

	return hr;
}


//***************************************************************************
//
//  bool CWbemSchemaIDCache::FindPropertyName
//
//  DESCRIPTION:
//
//  Determine whether the property exists for this object and is not 
//	a system property
//
//  PARAMETERS:
//
//		bsName - name of specified property
//
//  RETURN VALUES:
//
//***************************************************************************

bool CWbemSchemaIDCache::FindPropertyName(
	BSTR bsName
)
{
	bool result = false;;

	if (m_pDispatchMgr)
	{
		CComPtr<IWbemClassObject> pIWbemClassObject = m_pDispatchMgr->GetObject ();

		if (pIWbemClassObject)
		{
			//Note : This limits the support to non-system properties only !!! 
			LONG lFlavor = 0;

			if (SUCCEEDED(pIWbemClassObject->Get(bsName, 0, NULL, NULL, &lFlavor))
				&& !(WBEM_FLAVOR_ORIGIN_SYSTEM & lFlavor))
				result = true;
		}
	}

	return result;
}

//***************************************************************************
//
//  bool CWbemSchemaIDCache::GetMethod
//
//  DESCRIPTION:
//
//  returns the parameter names of a method in two
//				 safearrays - one for in and one for out
//
//  PARAMETERS:
//
//		bstrMethodName - name of method requested
//		ppsaInParams -   pointer to safearray to return
//								  in parameters
//		ppsaOutParams -  pointer to safearray to return
//								  out parameters
//
//  RETURN VALUES:
//
//***************************************************************************

bool CWbemSchemaIDCache::GetMethod(
	BSTR bstrMethodName, 
	SAFEARRAY **ppsaInParams, 
	SAFEARRAY **ppsaOutParams,
	CComPtr<IWbemClassObject> & pInParamsObject,
	CComPtr<IWbemClassObject> & pOutParamsObject
)
{
	bool result = false;
	CComPtr<IWbemClassObject> pIWbemClassObject = m_pDispatchMgr->GetClassObject ();

	if (pIWbemClassObject)
	{
		if (SUCCEEDED(pIWbemClassObject->GetMethod(bstrMethodName, 0, &pInParamsObject, &pOutParamsObject)))
		{
			*ppsaInParams = NULL;
			*ppsaOutParams = NULL;
			bool ok = true;

			if (pInParamsObject)
			{
				if (FAILED(pInParamsObject->GetNames(NULL, 0, NULL, ppsaInParams)))
					ok = false;
			}

			if (ok && pOutParamsObject)
			{
				if (FAILED(pOutParamsObject->GetNames(NULL, 0, NULL, ppsaOutParams)))
					ok = false;
			}

			result = ok;
		}
	}

	return result;
}

//***************************************************************************
//
//  bool CWbemSchemaIDCache::GetIdOfMethodParameter
//
//  DESCRIPTION:
//
//  gets the id of a given parameter for a given method
//	(this is a qualifier on the parameter property in the
//				  InParameters/OutParameters object)
//
//  PARAMETERS:
//
//		bstrParamName	-  parameter name
//		pParams			-  IWbemClassObject containing parameters
//		pId				-  pointer to long to receive the ID for this
//						   parameter of this method
//
//  RETURN VALUES:
//
//***************************************************************************

bool CWbemSchemaIDCache::GetIdOfMethodParameter(
	BSTR bstrParamName, 
	CComPtr<IWbemClassObject> &pParams, 
	long *pId
)
{
	bool result = false;

	if (pParams)
	{
		CComPtr<IWbemQualifierSet> pQualSet;
	
		//Get qualifier set for the required parameter property
		if (SUCCEEDED(pParams->GetPropertyQualifierSet(bstrParamName, &pQualSet)))
		{
			CComVariant vIdVal;
	
			//Get the "id" qualifier value
			if (SUCCEEDED(pQualSet->Get(L"id", 0, &vIdVal, NULL)))
			{
				result = true;
				*pId = vIdVal.lVal;
			}
		}
	}

	return result;
}

//***************************************************************************
//
//  bool CWbemSchemaIDCache::GetName
//
//  DESCRIPTION:
//
//  gets the name of the item given a DISPID
//
//  PARAMETERS:
//
//		dispId			- id whose name we require
//		bsName			- the name (on successful return)
//
//  RETURN VALUES:
//
//***************************************************************************

bool	CWbemSchemaIDCache::GetName (
	DISPID dispId, 
	CComBSTR & bsName
)
{
	bool result = false;

	DispIDNameMap::iterator theIterator = m_cache.begin ();

	while (theIterator != m_cache.end ())
	{
		if (dispId == (*theIterator).second)
		{
			bsName = (*theIterator).first;
			result = true;
			break;
		}
		else
			theIterator++;
	}

	return result;
}

//***************************************************************************
//
//  bool CWbemSchemaIDCache::FindMemberInArray
//
//  DESCRIPTION:
//
//		determine whether a name is present in a SAFEARRAY
//
//  PARAMETERS:
//
//		bstrName			- the name we're looking for
//		psaNames			- SAFEARRAY we're looking in
//
//  RETURN VALUES:
//		true if found, false o/w
//
//***************************************************************************

bool CWbemSchemaIDCache::FindMemberInArray(BSTR bstrName, SAFEARRAY *psaNames)
{
	long lUBound;
	long i;
	
	//Walk the array and check if the requested name exists
	SafeArrayGetUBound(psaNames, 1, &lUBound);

	for (i=0; i <= lUBound; i++)
	{
		CComBSTR bstrMemberName;
		SafeArrayGetElement(psaNames, &i, &bstrMemberName);

		if (!_wcsicmp(bstrMemberName, bstrName)) //found the property
			break;
	}

	return (i <= lUBound);
}