#include "testwmi.h"

//
// The function connects to the namespace.
//

IWbemServices *ConnectToNamespace (VOID)
{
  IWbemServices *pIWbemServices = NULL;
  IWbemLocator *pIWbemLocator = NULL;
  HRESULT      hr;


  //
  // Create an instance of WbemLocator interface.
  //

  hr = CoCreateInstance( CLSID_WbemLocator,
                         NULL,
                         CLSCTX_INPROC_SERVER,
                         IID_IWbemLocator,
                         (LPVOID *)&pIWbemLocator );
  if ( hr == S_OK ) {

     //
     // Using the locator, connect to COM in the given namespace.
     //

     hr = pIWbemLocator->ConnectServer( (BSTR)((PVOID)DEFAULT_NAMESPACE),
                                        NULL,   // current account.
                                        NULL,   // current password.
                                        0L,     // locale
                                        0L,     // securityFlags
                                        NULL,   // domain for NTLM
                                        NULL,   // context
                                        &pIWbemServices );

     if ( hr == WBEM_S_NO_ERROR) {
         
        //
        // Switch the security level to IMPERSONATE so that provider(s)
        // will grant access to system-level objects, and so that
        // CALL authorization will be used.
        //

        hr = CoSetProxyBlanket( (IUnknown *)pIWbemServices, // proxy
                                RPC_C_AUTHN_WINNT,  // authentication service
                                RPC_C_AUTHZ_NONE,   // authorization service
                                NULL,               // server principle name
                                RPC_C_AUTHN_LEVEL_CALL, // authentication level
                                RPC_C_IMP_LEVEL_IMPERSONATE, // impersonation
                                NULL,            // identity of the client
                                EOAC_NONE );  // capability flags

        if ( hr != S_OK ) {

           pIWbemServices->Release();
           pIWbemServices  = NULL;

           PrintError( hr,
                     __LINE__,
                     TEXT(__FILE__),
                     TEXT("Couldn't impersonate, program exiting...") );
        }
     }
     else {
        PrintError( hr,
                  __LINE__,
                  TEXT(__FILE__),
                  TEXT("Couldn't connect to root\\wmi, program exiting...") );
     }

     //
     // Done with IWbemLocator.
     //

     pIWbemLocator->Release();
  }
  else {
     PrintError( hr,
               __LINE__,
               TEXT(__FILE__),
               TEXT("Couldn't create an instance of ")
               TEXT("IWbemLocator interface, programm exiting...") );
  }

  return pIWbemServices;
}

//
// Given a class name, the function populates the combo box with all
// the instances of the class.
//

VOID EnumInstances (IWbemServices *pIWbemServices,
                    LPTSTR        lpszClass,
                    HWND          hwndInstTree)
{
  IEnumWbemClassObject *pEnumInst;
  IWbemClassObject     *pInst;
  VARIANT              varInstanceName;
  BSTR                 bstrClass;
  LPTSTR               lpszInstance;
  ULONG                ulFound;
  HRESULT              hr;


  bstrClass = StringToBstr( lpszClass,
                            -1 );
  if ( !bstrClass ) {

     PrintError( HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY),
               __LINE__,
               TEXT(__FILE__),
               TEXT("Not enough memory to enumerate instances of %s"),
                    lpszClass );

     return;
  }

  hr = pIWbemServices->CreateInstanceEnum(
                  bstrClass,              // Name of the root class.
                  WBEM_FLAG_SHALLOW |     // Enumerate at current root only.
                  WBEM_FLAG_FORWARD_ONLY, // Forward-only enumeration.
                  NULL,                   // Context.
                  &pEnumInst );          // pointer to class enumerator

  if ( hr == WBEM_S_NO_ERROR ) {

     //
     // Begin enumerating instances.
     //

     ulFound = 0;

     hr = pEnumInst->Next( 2000,      // two seconds timeout
                           1,         // return just one instance.
                           &pInst,    // pointer to instance.
                           &ulFound); // Number of instances returned.
     
     while ( (hr == WBEM_S_NO_ERROR) && (ulFound == 1) ) {

        VariantInit( &varInstanceName );

        //
        // Get the instance name stored in __RELPATH property.
        //

        hr = pInst->Get( L"__RELPATH", // property name 
                         0L,                // Reserved, must be zero.
                         &varInstanceName,  // property value returned.
                         NULL,              // CIM type not needed.
                         NULL );            // Flavor not needed.

        if ( hr == WBEM_S_NO_ERROR ) {

           lpszInstance = BstrToString( V_BSTR(&varInstanceName),
                                        -1 );
           if ( lpszInstance ) {

              InsertItem( hwndInstTree,
                          lpszInstance );

               SysFreeString( (BSTR)((PVOID)lpszInstance) );
           }
           else {
              hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);

              PrintError( hr,
                          __LINE__,
                          TEXT(__FILE__),
                          TEXT("Out of memory while enumerating instaces of")
                          TEXT(" %s, no more instances will")
                          TEXT(" be listed."),
                          lpszClass );
           }

           VariantClear( &varInstanceName );
        }
        else {
           PrintError( hr,
                     __LINE__,
                     TEXT(__FILE__),
                     TEXT("Couldn't retrieve __RELPATH of an instance")
                     TEXT(" of %s, no more instances will be listed."),
                     lpszClass );
        }

        //
        // Done with this instance.
        //

        pInst->Release();

        if ( hr == WBEM_S_NO_ERROR ) {

           hr = pEnumInst->Next( 2000,       // two seconds timeout.
                                 1,          // return just one class.
                                 &pInst,     // pointer to returned class.
                                 &ulFound);  // Number of classes returned.
        }
     }
 
     pEnumInst->Release();

  }
  else {
     PrintError( hr,
               __LINE__,
               TEXT(__FILE__),
               TEXT("Couldn't create an instance of ")
               TEXT("IEnumWbemClassObject interface, instances of %s ")
               TEXT("will not be listed."),
               lpszClass );
  }

  SysFreeString( bstrClass );

  return;
}

//
// Given a class name and __RELPATH of an instance, the function lists all the
// local non-system properties in a tree list.
//

VOID EnumProperties (IWbemServices *pIWbemServices,
                     LPTSTR        lpszClass,
                     LPTSTR        lpszInstance,
                     HWND          hwndPropTree)
{
  IWbemClassObject  *pInst;
  SAFEARRAY         *psaPropNames;
  BSTR              bstrProperty;
  long              lLower;
  long              lUpper;
  long              i;
  HRESULT           hr;
  LPTSTR            lpszProperty;

  //
  // Get a pointer to the instance.
  //

  pInst = GetInstanceReference( pIWbemServices,
                                lpszClass,
                                lpszInstance );

  if ( pInst ) {

     //
     // psaPropNames must be null prior to making the call.
     //

     psaPropNames = NULL;

     //
     // Get all the properties.
     //

     hr = pInst->GetNames( NULL,              // No qualifier names.
                           WBEM_FLAG_ALWAYS | // All non-system properties
                           WBEM_FLAG_LOCAL_ONLY, 
                           NULL,             // No qualifier values.
                           &psaPropNames);   // Returned property names

     if ( hr == WBEM_S_NO_ERROR ) {

        //
        // Get the number of properties returned.
        //

        SafeArrayGetLBound( psaPropNames, 1, &lLower );
        SafeArrayGetUBound( psaPropNames, 1, &lUpper );

        //
        // List all properties or stop when encountered an error.
        //

        for (i=lLower; (hr == WBEM_S_NO_ERROR) && (i <= lUpper); i++) {

           //
           // Add the property name into the list box.
           //

           bstrProperty = NULL;

           hr = SafeArrayGetElement( psaPropNames,
                                     &i,
                                     &bstrProperty);

           if ( SUCCEEDED(hr) ) {

               lpszProperty = BstrToString( bstrProperty,
                                          -1 );

               if ( lpszProperty ) {

                  InsertItem( hwndPropTree,
                              lpszProperty );

                  SysFreeString( (BSTR)((PVOID)lpszProperty) );
               }
               else {
                  PrintError( HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY),
                            __LINE__,
                            TEXT(__FILE__),
                            TEXT("Out of memory while enumerating")
                            TEXT(" properties of %s, no more properties")
                            TEXT(" will be listed"),
                            lpszInstance );
               }

              //
              // Done with the property name.
              //

              SysFreeString( bstrProperty );
           }
           else {
              PrintError( hr,
                          __LINE__,
                          TEXT(__FILE__),
                          TEXT("Couldn't get the name of a property(%d). ")
                          TEXT("No more properties will be listed."),
                          i );
           }
        }

        //
        // Done with the array of properties.
        //

        SafeArrayDestroy( psaPropNames );
     }
     else {
        PrintError( hr,
                    __LINE__,
                    TEXT(__FILE__),
                    TEXT("Couldn't retrieve the properties of %s, ")
                    TEXT("an instance of class %s. Properties will not be ")
                    TEXT("listed."),
                    lpszInstance, lpszClass );
     }

  }
  else {
     PrintError( HRESULT_FROM_WIN32(ERROR_WMI_INSTANCE_NOT_FOUND),
                 __LINE__,
                 TEXT(__FILE__),
                 TEXT("Couldn't retrieve a pointer to instance %s of class %s.")
                 TEXT("Its properties will not be listed."),
                 lpszInstance, lpszClass );
  }

  return;
}

//
// Given a class name and __RELPATH of an instance, the function returns a
// pointer to the instance.
//

IWbemClassObject *GetInstanceReference (IWbemServices *pIWbemServices,
                                        LPTSTR        lpszClass,
                                        LPTSTR        lpszInstance)
{
  IWbemClassObject     *pInst;
  IEnumWbemClassObject *pEnumInst;
  ULONG                ulCount;
  BSTR                 bstrClass;
  BOOL                 bFound;
  HRESULT              hr;
  

  hr = 0;

  bstrClass = StringToBstr( lpszClass,
                            -1 );
  if ( !bstrClass ) {

     PrintError( HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY),
               __LINE__,
               TEXT(__FILE__),
               TEXT("Not enough memory to get a pointer to %s."),
               lpszInstance );

     return NULL;
  }

  //
  // pInst pointer must be NULL initially,
  //

  pInst = NULL;

  // 
  // Get Instance Enumerator Interface.
  //

  pEnumInst = NULL;

  hr = pIWbemServices->CreateInstanceEnum(bstrClass,
                                          WBEM_FLAG_SHALLOW | 
                                          WBEM_FLAG_FORWARD_ONLY,
                                          NULL,         
                                          &pEnumInst );

  if ( hr == WBEM_S_NO_ERROR ) {

     //
     // Get a pointer to the instance.
     //
     // We enumerate all the instances and compare their __RELPATH with
     // the specified __RELPATH. If we find a match then, that is the one
     // we are looking for.
     //
     // The other more efficient way is to create a WQL query and execute
     // it.
     //

     hr = WBEM_S_NO_ERROR;
     bFound = FALSE;

     while ( (hr == WBEM_S_NO_ERROR) && (bFound == FALSE) ) {

        hr = pEnumInst->Next( 2000,      // two seconds timeout
                              1,         // return just one instance.
                              &pInst,    // pointer to instance.
                              &ulCount); // Number of instances returned.

        if ( ulCount > 0 ) {

           bFound = IsInstance( pInst,
                                lpszInstance );

           if ( bFound == FALSE ) {
              pInst->Release();
           }
        }
     }

     if ( bFound == FALSE )
        pInst = NULL;

     //
     // Done with the instance enumerator.
     //

     pEnumInst->Release();
  }

  SysFreeString( bstrClass );
  return pInst;
}

//
// Given a pointer, the function returns TRUE if the pointer points to
// the instance specified by lpszInstance.
//

BOOL IsInstance (IWbemClassObject *pInst,
                 LPTSTR           lpszInstance)
{
  VARIANT              varPropVal;
  LPTSTR               lpInstance;
  BOOL                 bRet;

  bRet = GetPropertyValue( pInst,
                           TEXT("__RELPATH"),
                           &varPropVal,
                           NULL );

  if ( bRet == TRUE ) {

     lpInstance = BstrToString( V_BSTR(&varPropVal),
                                       -1 );
     if ( !lpInstance ) {

        PrintError( HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY),
                  __LINE__,
                  TEXT(__FILE__),
                  TEXT("Not enough memory to search for an instance.") );

        bRet = FALSE;
     }
     else {
        bRet = _tcsicmp( lpszInstance, lpInstance ) == 0;

        SysFreeString( (BSTR)((PVOID)lpInstance) );
     }

     VariantClear( &varPropVal );
  }

  return bRet;
}


//
// The function returns property value and its type of a given class/instance.
//

BOOL GetPropertyValue (IWbemClassObject *pRef,
                       LPTSTR           lpszProperty, 
                       VARIANT          *pvaPropertyValue,
                       LPTSTR           *lppszPropertyType)
{
  IWbemQualifierSet *pQual;
  VARIANT           vaQual;
  BSTR              bstrProperty;
  HRESULT           hr;
  BOOL              bRet;


  //
  // Get the property value.
  //

  bstrProperty = StringToBstr( lpszProperty,
                               -1 );

  if ( !bstrProperty ) {

     return FALSE;

  }

  bRet = FALSE;

  if ( lppszPropertyType ) {

     //
     // Get the textual name of the property type.
     //

     hr = pRef->GetPropertyQualifierSet( bstrProperty,
                                         &pQual );

     if ( hr == WBEM_S_NO_ERROR ) {

        //
        // Get the textual name of the property type.
        //

        hr = pQual->Get( L"CIMTYPE",
                         0,
                         &vaQual,
                         NULL );

        if ( hr == WBEM_S_NO_ERROR ) {
           *lppszPropertyType = BstrToString( V_BSTR(&vaQual),
                                              -1 );

           VariantClear( &vaQual );
        }

        pQual->Release();
     }
  }

  VariantInit( pvaPropertyValue );

  hr = pRef->Get( bstrProperty,
                  0,
                  pvaPropertyValue,
                  NULL,
                  NULL );

  if ( hr == WBEM_S_NO_ERROR ) {
     bRet = TRUE;
  }
  else {
     if ( lppszPropertyType && *lppszPropertyType ) {
        SysFreeString( (BSTR)((PVOID)*lppszPropertyType) );
     }
  }

  SysFreeString( bstrProperty );
  return bRet;
}

//
// Given a pointer to an instance, its property and and variant specifying
// the value for the property, the function updates the property and the
// instance.
//

HRESULT UpdatePropertyValue (IWbemServices *pIWbemServices,
                             IWbemClassObject *pInstance,
                             LPTSTR lpszProperty,
                             LPVARIANT pvaNewValue)
{
  BSTR           bstrProperty;
  HRESULT hr;


  bstrProperty = StringToBstr( lpszProperty,
                               -1 );

  if ( !bstrProperty ) {

     PrintError( HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY),
                 __LINE__,
                 TEXT(__FILE__),
                 TEXT("Not enough memory to update %s."),
                 lpszProperty );

     return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  }
  
  hr = pInstance->Put( bstrProperty,
                       0,
                       pvaNewValue,
                       0 );

  if ( hr == WBEM_S_NO_ERROR ) {
     hr = pIWbemServices->PutInstance( pInstance,
                                       WBEM_FLAG_UPDATE_ONLY,
                                       NULL,
                                       NULL );

     if ( hr != WBEM_S_NO_ERROR ) {
        PrintError(  hr,
                     __LINE__,
                     TEXT(__FILE__),
                     TEXT("Failed to save the instance,")
                     TEXT(" %s will not be updated."),
                     lpszProperty );
     }
  }
  else {
     PrintError(  hr,
                  __LINE__,
                  TEXT(__FILE__),
                  TEXT("Couldn't update %s."),
                  lpszProperty );
  }

  SysFreeString( bstrProperty );

  return hr;
}

BSTR StringToBstr (LPTSTR lpSrc,
                  int nLenSrc)
{
  BSTR lpDest;

  //
  // In case of ANSI version, we need to change the ANSI string to UNICODE since
  // BSTRs are essentially UNICODE strings.
  //

  #if !defined(UNICODE) || !defined(_UNICODE)

     int  nLenDest;

     nLenDest = MultiByteToWideChar( CP_ACP, 0, lpSrc, nLenSrc, NULL, 0);

     lpDest = SysAllocStringLen( NULL, nLenDest );

     if ( lpDest ) {
        MultiByteToWideChar( CP_ACP, 0, lpSrc, nLenSrc, lpDest, nLenDest );
     }

  //
  // In case of UNICODE version, we simply allocate memory and copy the string.
  //

  #else
     if ( lpSrc == NULL ) {
        nLenSrc = 0;
     }
     else {
        if ( nLenSrc == -1 ) {
           nLenSrc = _tcslen( lpSrc ) + 1;
        }
     }

     lpDest = SysAllocStringLen( lpSrc, nLenSrc );
  #endif

  return lpDest;
}

//
// The function converts a BSTR string into ANSI and returns it in an allocated
// memory. The memory must be freed by the caller using SysFreeString()
// function. If nLenSrc is -1, the string is null terminated.
//

LPTSTR BstrToString (BSTR lpSrc,
                    int nLenSrc)
{
  LPTSTR lpDest;

  //
  // In case of ANSI version, we need to change BSTRs which are UNICODE strings
  // into ANSI version.
  //

  #if !defined(UNICODE) || !defined(_UNICODE)

     int   nLenDest;

     nLenDest = WideCharToMultiByte( CP_ACP, 0, lpSrc, nLenSrc, NULL,
                                     0, NULL, NULL );
     lpDest = (LPTSTR)SysAllocStringLen( NULL, (size_t)nLenDest );

     if ( lpDest ) {
        WideCharToMultiByte( CP_ACP, 0, lpSrc, nLenSrc, lpDest,
                             nLenDest, NULL, NULL );
     }
  //
  // In case of UNICODE version, we simply allocate memory and copy the BSTR
  // into allocate memory and return its address.
  //

  #else
     if ( lpSrc ) {
        if ( nLenSrc == -1 ) {
           nLenSrc = _tcslen( lpSrc ) + 1;
        }
     }
     else {
        nLenSrc = 0;
     }

     lpDest = (LPTSTR)SysAllocStringLen( lpSrc, nLenSrc );
  #endif

  return lpDest;
}