// sequence.cpp: implementation of the various classes related to sequencing
//
// Copyright (c)1997-2001 Microsoft Corporation
//
//////////////////////////////////////////////////////////////////////

#include "sequence.h"
#include "persistmgr.h"
#include "requestobject.h"

/*
Routine Description: 

Name:

    CNameList::~CNameList

Functionality:

    Destructor. Simply cleans up the vector, which contains heap allocated strings.

Virtual:
    
    No

Arguments:

    None.

Return Value:

    None as any destructor

Notes:
    if you create any local members, think about releasing them here.

*/

CNameList::~CNameList()
{
    int iCount = m_vList.size();
    for (int i = 0; i < iCount; ++i)
    {
        delete [] m_vList[i];
    }
    m_vList.clear();
}

/*
Routine Description: 

Name:

    COrderNameList::COrderNameList

Functionality:

    Contructor. Simple initialization.

Virtual:
    
    No

Arguments:

    None.

Return Value:

    None as any constructor

Notes:
    if you create any local members, think about initializing them here.

*/

COrderNameList::COrderNameList() : m_ppList(NULL)
{
}

/*
Routine Description: 

Name:

    COrderNameList::~COrderNameList

Functionality:

    Detructor. Simple Cleanup.

Virtual:
    
    No

Arguments:

    None.

Return Value:

    None as any destructor

Notes:
    if you create any local members, think about releasing them in Cleanup method.

*/

COrderNameList::~COrderNameList()
{
    Cleanup();
}

/*
Routine Description: 

Name:

    COrderNameList::Cleanup

Functionality:

    Clean up the map and the vector, both of which hold heap memory resources.

Virtual:
    
    No

Arguments:

    None.

Return Value:

    None.

Notes:
    if you create any local members, think about initializing them here.

*/

void 
COrderNameList::Cleanup()
{
    PriToNamesIter it = m_mapPriNames.begin();
    PriToNamesIter itEnd = m_mapPriNames.end();

    while (it != itEnd)
    {
        delete (*it).second;
        ++it;
    }
    m_mapPriNames.clear();

    m_listPriority.clear();

    delete [] m_ppList;
    m_ppList = NULL;
}

/*
Routine Description: 

Name:

    COrderNameList::EndCreation

Functionality:

    As name lists are added, we don't sort them (by priority). This function
    is the trigger for such sorting. You must call this function after all names
    lists are added.

Virtual:
    
    No

Arguments:

    None.

Return Value:

    Success: various success code. No guarantee it will be WBEM_NO_ERROR. Use SUCCEEDED(hr) to test.

    Failure: various failure code. It means the sorting effort has failed.

Notes:

*/

HRESULT 
COrderNameList::EndCreation () 
{
    m_listPriority.sort();

    //
    // now we are going to create an array for easy management.
    // Memory resource is not managed by m_ppList. It's managed by the map and the m_listPriority.
    // m_ppList is merely a array (of size m_listPriority.size()) of pointers.
    //

    delete [] m_ppList;
    m_ppList = new CNameList*[m_listPriority.size()];

    if (m_ppList == NULL)
    {
        return WBEM_E_OUT_OF_MEMORY;
    }

    //
    // now build the list. Since we have sorted the priority list,
    // it has the natural order!
    //

    ListIter it = m_listPriority.begin();
    ListIter itEnd = m_listPriority.end();

    int iIndex = 0;
    while (it != itEnd)
    {
        PriToNamesIter itList = m_mapPriNames.find(*it);

        if (itList != m_mapPriNames.end())
        {
            m_ppList[iIndex] = (*itList).second;
        }
        else
        {
            m_ppList[iIndex] = NULL;
        }

        ++it;
        ++iIndex;
    }

    return WBEM_NO_ERROR;
}

/*
Routine Description: 

Name:

    COrderNameList::CreateOrderList

Functionality:

    Given a priority, together with its list information string, this will add
    a CNameList object to our map.

Virtual:
    
    No

Arguments:

    dwPriority      - The priority value.

    pszListInfo     - string containing the order information. The names are separated by
                      wchCookieSep (colon char ':').

Return Value:

    Success: WBEM_NO_ERROR.

    Failure: various failure code. It means the creation has failed.

Notes:
    (1) This function merely pushes the created CNameList to the map and the priority
        to the list. It doesn't sort the list. So, this is a function a caller calls
        amid its creation. EndCreation does that sorting.
*/

HRESULT 
COrderNameList::CreateOrderList (
    IN DWORD dwPriority,
    IN LPCWSTR pszListInfo
    )
{
    if (pszListInfo == NULL || *pszListInfo == L'\0')
    {
        return WBEM_S_FALSE;
    }

    HRESULT hr = WBEM_S_FALSE;

    CNameList* pTheList = new CNameList;
    if (pTheList == NULL)
    {
        return WBEM_E_OUT_OF_MEMORY;
    }

    //
    // pszCur is the current point of parsing
    //

    LPCWSTR pszCur = pszListInfo;

    //
    // pszNext is the next token's point of the current parsing
    //

    LPCWSTR pszNext = pszCur;

    while (*pszNext != L'\0')
    {
        //
        // seek to the separater
        //

        while (*pszNext != L'\0' && *pszNext != wchCookieSep)
        {
            ++pszNext;
        }

        int iLen = pszNext - pszCur;
        if (iLen > 0)
        {
            LPWSTR pszName = new WCHAR[iLen + 1];

            if (pszName == NULL)
            {
                hr = WBEM_E_OUT_OF_MEMORY;
                break;
            }

            //
            // copy, but no white spaces
            //

            ::TrimCopy(pszName, pszCur, iLen);

            //
            // if we have a non-empty name, then add to our list
            //

            if (*pszName == L'\0')
            {
                delete [] pszName;
            }
            else    
            {
                //
                // give it to the list and the list manages the memory from this point
                //

                pTheList->m_vList.push_back(pszName);
            }
        }

        //
        // either skip wchNameSep or stop
        //

        if (*pszNext == wchCookieSep)
        {
            ++pszNext;
        }
        else if (*pszNext == L'\0')
        {
            //
            // end
            //

            break;
        }
        else
        {
            hr = WBEM_E_INVALID_SYNTAX;
            break;
        }

        pszCur = pszNext;
    }

    //
    // if failed
    //

    if (FAILED(hr))
    {
        delete pTheList;
    }
    else if (pTheList->m_vList.size() == 0)
    { 
        //
        // nothing ahs been added
        //

        hr = WBEM_S_FALSE;
    }
    else
    {
        //
        // we need to push this to our map and list
        //

        hr = WBEM_NO_ERROR;
        m_mapPriNames.insert(MapPriorityToNames::value_type(dwPriority, pTheList));
        m_listPriority.insert(m_listPriority.end(), dwPriority);
    }

    return hr;
}

/*
Routine Description: 

Name:

    COrderNameList::GetNext

Functionality:

    Enumerating what is managed by the class.

Virtual:
    
    No

Arguments:

    ppList          - Receives the CNameList of the enumeration.

    pdwEnumHandle   - in-bound value == where to start the enumeration. Out-bound value ==
                      where to start for the caller's next enumeration.

Return Value:

    Success: (1) WBEM_NO_ERROR if the enumeration is successful.
             (2) WBEM_S_NO_MORE_DATA if there is no more data.

    Failure: WBEM_E_INVALID_PARAMETER.

Notes:
    (1) Internally, the pdwEnumHandle is used as the index. But it is an opaque data to caller.
    (2) For maximum robustness, you should also check against *ppList == NULL.
    (3) As the parameter indicates, the returned *ppList must not be deleted by caller.
*/

HRESULT 
COrderNameList::GetNext (
    IN const CNameList  ** ppList,
    IN OUT DWORD        *  pdwEnumHandle
    )const
{
    if (ppList == NULL || pdwEnumHandle == NULL)
    {
        return WBEM_E_INVALID_PARAMETER;
    }

    *ppList = NULL;

    HRESULT hr = WBEM_NO_ERROR;

    if (m_ppList && *pdwEnumHandle < m_listPriority.size())
    {
        *ppList = m_ppList[*pdwEnumHandle];
        ++(*pdwEnumHandle);
    }
    else
    {
        *ppList = NULL;
        hr = WBEM_S_NO_MORE_DATA;
    }

    return hr;
}

/*
Routine Description: 

Name:

    CSequencer::GetOrderList

Functionality:

    Access to the COrderNameList object. Caller will use this object directly.

Virtual:
    
    No

Arguments:

    pList   - Receives the COrderNameList.

Return Value:

    WBEM_NO_ERROR.

Notes:
    (1) As the parameter indicates, the returned *pList must not be deleted by caller.
*/

HRESULT 
CSequencer::GetOrderList (
    OUT const COrderNameList** pList
    )
{
    *pList = &m_ClassList;
    return WBEM_NO_ERROR;
}


/*
Routine Description: 

Name:

    CSequencer::Create

Functionality:

    This creates the SCE namespace-wise sequencing order for embedding classes, plus
    template (pszStore) wise class ordering.

    As indicated before, template-wise class ordering takes precedence over namespace-wise
    class ordering.

Virtual:
    
    No

Arguments:

    pNamespace   - The namespace pointer we rely on to query Sce_Sequence instances.

    pszStore    - the store's path

Return Value:

    Success: various success code. Use SUCCEEDED(hr) to test.
    Failure: various failure code. All means that the sequencer can't be created.

Notes:
    (1) As the parameter indicates, the returned *pList must not be deleted by caller.
*/
//--------------------------------------------------------------------
// we need to query all instances of Sce_Sequence class and then
// build our class list for each namespace. The ordering of
// classes is determined by Sce_Sequence's priority member,
// The smaller of priority value, the higher its priority
//--------------------------------------------------------------------
HRESULT 
CSequencer::Create (
    IN IWbemServices    * pNamespace,
    IN LPCWSTR            pszStore,
    IN LPCWSTR            pszMethod
    )
{
    if (pNamespace == NULL || pszMethod == NULL || *pszMethod == L'\0')
    {
        return WBEM_E_INVALID_PARAMETER;
    }

    //
    // need the template sequencing first. This sequencing order, if present, will take precedence
    // over the template independent sequencing.
    //

    //
    // Prepare a store (for persistence) for this store path (file)
    //

    CSceStore SceStore;
    HRESULT hr = SceStore.SetPersistPath(pszStore);
    if (FAILED(hr))
    {
        return hr;
    }

    // we just need to read ClassOrder string out.
    LPWSTR pszTemplateClassOrder = NULL;
    DWORD dwRead = 0;

    //
    // try to create a template-wise class order. Since such an order may not exist,
    // we will tolerate WBEM_E_NOT_FOUND.
    // Need to free the pszTemplateClassOrder.
    //

    hr = SceStore.GetPropertyFromStore(SCEWMI_CLASSORDER_CLASS, pClassOrder, &pszTemplateClassOrder, &dwRead);

    if (hr == WBEM_E_NOT_FOUND)
    {
        hr = WBEM_NO_ERROR;
    }
    else if (FAILED(hr))
    {
        return hr;
    }

    //
    // try to get all sequencing instances.
    //

    LPCWSTR pszQueryFmt = L"SELECT * FROM Sce_Sequence WHERE Method=\"%s\"";
    DWORD dwFmtLen = wcslen(pszQueryFmt);
    DWORD dwClassLen = wcslen(pszMethod);

    //
    // don't forget to ::SysFreeString of this bstrQuery
    //

    BSTR bstrQuery= ::SysAllocStringLen(NULL, dwClassLen + dwFmtLen + 1);
    if ( bstrQuery == NULL ) 
    {
        hr = WBEM_E_OUT_OF_MEMORY;
    }
    
    CComPtr<IEnumWbemClassObject> srpEnum;
    if (SUCCEEDED(hr))
    {
        //
        // this won't overrun the buffer, total length allocated is greater than needed
        //

        wsprintf(bstrQuery, pszQueryFmt, pszMethod);

        hr = pNamespace->ExecQuery(L"WQL", bstrQuery, WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, NULL, &srpEnum);

        //
        // free the bstr
        //

        ::SysFreeString(bstrQuery);
    }

    //
    // the previous query will allow us to enumerate through all Sce_Sequence instances
    //

    if (SUCCEEDED(hr))
    {
        //
        // ready to create the list. We will follow the usage of COrderNameList
        // to call BeginCreation first and end all creation by EndCreation.
        //

        m_ClassList.BeginCreation();

        //
        // now, if there is a template-wise sequencing, then, use it
        // we use absolute priority 0 for this sequencing list. All other
        // sequencing list is 1 lower than what they claimed themselves
        //

        if (pszTemplateClassOrder != NULL)
        {
            //
            // we will allow this to fail
            //

            m_ClassList.CreateOrderList(0, pszTemplateClassOrder);
        }

        //
        // CScePropertyMgr helps us to access WMI object's properties.
        //

        CScePropertyMgr ScePropMgr;

        DWORD dwPriority;

        //
        // this will hold the individual Sce_Sequence instance.
        //

        CComPtr<IWbemClassObject> srpObj;
        ULONG nEnum = 0;
        hr = srpEnum->Next(WBEM_INFINITE, 1, &srpObj, &nEnum);

        //
        // for each Sce_Sequence, let's parse its order property to build a list
        //

        while (SUCCEEDED(hr) && hr != WBEM_S_NO_MORE_DATA && srpObj)
        {
            CComBSTR bstrSeq;

            //
            // attach a different WMI object to the property mgr.
            // This will always succeed.
            //

            ScePropMgr.Attach(srpObj);
            dwPriority = 0;

            //
            // we must have priority property, it's a key property
            //

            hr = ScePropMgr.GetProperty(L"Priority", &dwPriority);
            if (SUCCEEDED(hr))
            {
                //
                // we will ignore those instances that has no "sequence" property
                //

                if (SUCCEEDED(ScePropMgr.GetProperty(L"Order", &bstrSeq)))
                {
                    //
                    // ask the list to add the names encoded in this string. Don't cleanup the existing ones.
                    // add 1 more to the claimed priority so that no Sce_Sequence instance can really have
                    // 0 (highest) priority. We reserve 0 for the template's sequencing list.
                    //

                    dwPriority = (dwPriority + 1 == 0) ? dwPriority : dwPriority + 1;
                    hr = m_ClassList.CreateOrderList(dwPriority, bstrSeq);
                }
            }

            if (SUCCEEDED(hr))
            {   
                //
                // get it ready to be reused
                //

                srpObj.Release();

                //
                // ready to loop to the next item
                //

                hr = srpEnum->Next(WBEM_INFINITE, 1, &srpObj, &nEnum);
            }
        }

        //
        // this is the good result
        //

        if (hr == WBEM_S_NO_MORE_DATA)
        {
            hr = WBEM_NO_ERROR;
        }

        //
        // EndCreation will only return WBEM_E_OUT_OF_MEMORY or WBEM_NO_ERROR
        //

        if (WBEM_E_OUT_OF_MEMORY == m_ClassList.EndCreation())
        {
            hr = WBEM_E_OUT_OF_MEMORY;
        }
    }

    delete [] pszTemplateClassOrder;
 
    return hr;
}

//=========================================================================
// implementation for template-wise class sequencing
//=========================================================================

/*
Routine Description: 

Name:

    CClassOrder::CClassOrder

Functionality:

    This is the constructor. Pass along the parameters to the base class

Virtual:
    
    No (you know that, constructor won't be virtual!)

Arguments:

    pKeyChain - Pointer to the ISceKeyChain COM interface which is prepared
        by the caller who constructs this instance.

    pNamespace - Pointer to WMI namespace of our provider (COM interface).
        Passed along by the caller. Must not be NULL.

    pCtx - Pointer to WMI context object (COM interface). Passed along
        by the caller. It's up to WMI whether this interface pointer is NULL or not.

Return Value:

    None as any constructor

Notes:
    if you create any local members, think about initialize them here

*/

CClassOrder::CClassOrder (
    IN ISceKeyChain *pKeyChain, 
    IN IWbemServices *pNamespace,
    IN IWbemContext *pCtx
    )
    :
    CGenericClass(pKeyChain, pNamespace, pCtx)
{
}

/*
Routine Description: 

Name:

    CClassOrder::~CClassOrder

Functionality:
    
    Destructor. Necessary as good C++ discipline since we have virtual functions.

Virtual:
    
    Yes.
    
Arguments:

    none as any destructor

Return Value:

    None as any destructor

Notes:
    if you create any local members, think about whether
    there is any need for a non-trivial destructor

*/

CClassOrder::~CClassOrder()
{
}

/*
Routine Description: 

Name:

    CClassOrder::PutInst

Functionality:
    
    Put an instance as instructed by WMI. Since this class implements Sce_ClassOrder,
    which is persistence oriented, this will cause the Sce_ClassOrder object's property 
    information to be saved in our store.

Virtual:
    
    Yes.
    
Arguments:

    pInst       - COM interface pointer to the WMI class (Sce_ClassOrder) object.

    pHandler    - COM interface pointer for notifying WMI of any events.

    pCtx        - COM interface pointer. This interface is just something we pass around.
                    WMI may mandate it (not now) in the future. But we never construct
                    such an interface and so, we just pass around for various WMI API's

Return Value:

    Success: it must return success code (use SUCCEEDED to test). It is
    not guaranteed to return WBEM_NO_ERROR.

    Failure: Various errors may occurs. Any such error should indicate the failure of persisting
    the instance.

Notes:
    Since GetProperty will return a success code (WBEM_S_RESET_TO_DEFAULT) when the
    requested property is not present, don't simply use SUCCEEDED or FAILED macros
    to test for the result of retrieving a property.

*/

HRESULT CClassOrder::PutInst (
    IN IWbemClassObject * pInst, 
    IN IWbemObjectSink  * pHandler,
    IN IWbemContext     * pCtx
    )
{

    //
    // CScePropertyMgr helps us to access WMI object's properties
    // create an instance and attach the WMI object to it.
    // This will always succeed.
    //

    CScePropertyMgr ScePropMgr;
    ScePropMgr.Attach(pInst);

    //
    // must have a store path
    //

    CComBSTR vbstrStorePath;
    HRESULT hr = ScePropMgr.GetProperty(pStorePath, &vbstrStorePath);
    if (SUCCEEDED(hr))
    {
        CComBSTR bstrOrder;
        hr = ScePropMgr.GetProperty(pClassOrder, &bstrOrder);

        //
        // if everything is fine, we need to save it
        //

        if (SUCCEEDED(hr))
        {
            //
            // Attach the WMI object instance to the store and let the store know that
            // it's store is given by the pStorePath property of the instance.
            //

            CSceStore SceStore;
            SceStore.SetPersistProperties(pInst, pStorePath);

            DWORD dwDump;

            //
            // For a new .inf file. Write an empty buffer to the file
            // will creates the file with right header/signature/unicode format
            // this is harmless for existing files.
            // For database store, this is a no-op.
            //

            hr = SceStore.WriteSecurityProfileInfo(AreaBogus, (PSCE_PROFILE_INFO)&dwDump, NULL, false);

            //
            // also, we need to write it to attachment section because it's not a native core object
            // without an entry in the attachment section, inf file tempalte can't be imported to
            // database stores. For database store, this is no-op
            //

            if (SUCCEEDED(hr))
            {
                hr = SceStore.WriteAttachmentSection(SCEWMI_CLASSORDER_CLASS, pszAttachSectionValue);
            }

            //
            // final save
            //

            if (SUCCEEDED(hr))
            {
                hr = SceStore.SavePropertyToStore(SCEWMI_CLASSORDER_CLASS, pClassOrder, (LPCWSTR)bstrOrder);
            }
        }
    }

    return hr;
}

/*
Routine Description: 

Name:

    CClassOrder::CreateObject

Functionality:
    
    Create WMI objects (Sce_ClassOrder). Depending on parameter atAction,
    this creation may mean:
        (a) Get a single instance (atAction == ACTIONTYPE_GET)
        (b) Get several instances satisfying some criteria (atAction == ACTIONTYPE_QUERY)
        (c) Delete an instance (atAction == ACTIONTYPE_DELETE)

Virtual:
    
    Yes.
    
Arguments:

    pHandler - COM interface pointer for notifying WMI for creation result.

    atAction -  Get single instance ACTIONTYPE_GET
                Get several instances ACTIONTYPE_QUERY
                Delete a single instance ACTIONTYPE_DELETE

Return Value:

    Success: it must return success code (use SUCCEEDED to test). It is
    not guaranteed to return WBEM_NO_ERROR. The returned objects are indicated to WMI,
    not directly passed back via parameters.

    Failure: Various errors may occurs. Except WBEM_E_NOT_FOUND, any such error should indicate 
    the failure of getting the wanted instance. If WBEM_E_NOT_FOUND is returned in querying
    situations, this may not be an error depending on caller's intention.

Notes:

*/

HRESULT CClassOrder::CreateObject (
    IN IWbemObjectSink * pHandler, 
    IN ACTIONTYPE        atAction
    )
{
    // 
    // we know how to:
    //      Get single instance ACTIONTYPE_GET
    //      Delete a single instance ACTIONTYPE_DELETE
    //      Get several instances ACTIONTYPE_QUERY
    //

    if ( ACTIONTYPE_GET     != atAction &&
         ACTIONTYPE_DELETE  != atAction &&
         ACTIONTYPE_QUERY   != atAction ) 
    {
        return WBEM_E_NOT_SUPPORTED;
    }

    //
    // We must have the pStorePath property because that is where
    // our instance is stored. 
    // m_srpKeyChain->GetKeyPropertyValue WBEM_S_FALSE if the key is not recognized
    // So, we need to test against WBEM_S_FALSE if the property is mandatory
    //

    CComVariant varStorePath;
    HRESULT hr = m_srpKeyChain->GetKeyPropertyValue(pStorePath, &varStorePath);

    if (SUCCEEDED(hr) && hr != WBEM_S_FALSE && varStorePath.vt == VT_BSTR)
    {
        //
        // Prepare a store (for persistence) for this store path (file)
        //

        CSceStore SceStore;
        hr = SceStore.SetPersistPath(varStorePath.bstrVal);

        if ( SUCCEEDED(hr) ) 
        {
            //
            // make sure the store (just a file) really exists. The raw path
            // may contain env variables, so we need the expanded path
            //

            DWORD dwAttrib = GetFileAttributes(SceStore.GetExpandedPath());

            //
            // if the file exist
            //

            if ( dwAttrib != -1 ) 
            {
                if ( ACTIONTYPE_DELETE == atAction )
                {
                    //
                    // just save a blank section because we only has one instance
                    //

                    hr = SceStore.SavePropertyToStore(SCEWMI_CLASSORDER_CLASS, (LPCWSTR)NULL, (LPCWSTR)NULL);
                }
                else
                {
                    //
                    // we need to read out the ClassOrder property
                    //

                    LPWSTR pszClassOrder = NULL;    
                    DWORD dwRead = 0;

                    //
                    // need to free pszClassOrder!
                    //

                    hr = SceStore.GetPropertyFromStore(SCEWMI_CLASSORDER_CLASS, pClassOrder, &pszClassOrder, &dwRead);

                    //
                    // read is successful
                    //

                    if (SUCCEEDED(hr) && dwRead > 0)
                    {
                        //
                        // create a blank new instance to fill in properties
                        //

                        CComPtr<IWbemClassObject> srpObj;
                        hr = SpawnAnInstance(&srpObj);

                        //
                        // if successful, then ready to fill in the properties
                        //

                        if (SUCCEEDED(hr))
                        {
                            //
                            // CScePropertyMgr helps us to access WMI object's properties
                            // create an instance and attach the WMI object to it.
                            // This will always succeed.
                            //

                            CScePropertyMgr ScePropMgr;
                            ScePropMgr.Attach(srpObj);
                            hr = ScePropMgr.PutProperty(pStorePath, SceStore.GetExpandedPath());

                            if (SUCCEEDED(hr))
                            {
                                hr = ScePropMgr.PutProperty(pClassOrder, pszClassOrder);
                            }
                        }

                        //
                        // pass the new instance to WMI if we are successful
                        //

                        if (SUCCEEDED(hr))
                        {
                            hr = pHandler->Indicate(1, &srpObj);
                        }

                        delete [] pszClassOrder;
                    }
                }
            } 
            else
            {
                hr = WBEM_E_NOT_FOUND;
            }
        }
    }

    if (SUCCEEDED(hr))
    {
        //
        // do the necessary gestures to WMI.
        // the use of WBEM_STATUS_REQUIREMENTS in SetStatus is not documented by WMI
        // at this point. Consult WMI team for detail if you suspect problems with
        // the use of WBEM_STATUS_REQUIREMENTS
        //

        if ( ACTIONTYPE_QUERY == atAction )
        {
            pHandler->SetStatus(WBEM_STATUS_REQUIREMENTS, S_FALSE, NULL, NULL);
        }
        else if (ACTIONTYPE_GET  == atAction)
        {
            pHandler->SetStatus(WBEM_STATUS_REQUIREMENTS, S_OK, NULL, NULL);
        }
    }

    return hr;
}