This is the second in series of articles about monitoring batteries in Windows XP. In the first article, Windows XP: Monitoring Batteries in C Sharp, I showed how to use the Windows Management Instrumentation (WMI) to get battery status for multiple batteries using C#. Now what I didn’t tell you was that we found a potential bug in the .NET Framework and the battery driver on the device we were working on. The bug appears to be that the battery driver reports two newline characters in the battery device id, which causes something to go wrong in reading any of the WMI WIN32_Battery properties (we have a case open with Microsoft on this.)
So, to get around the problem, I rewrote my WIN32_Battery class in C++, and included some C wrapper functions so that I could use the class from C#.
 Just like with the C# class, I created a WIN32_Battery class:
class WIN32_Battery
{
public:
      WIN32_Battery();
      ~WIN32_Battery();
      void Refresh();
      int NumberOfBatteries();
      int GetPercentChargeRemaining(int BatteryNumber);
 
private:
      int GetBatteryProperty(int BatteryNumber, TCHAR *Property);
      BOOL Connect();
      IEnumWbemClassObject* Batteries;
      IWbemLocator *pLoc;
      IWbemServices *pSvc;
      int Count;
};
Then started by creating a constructor, destructor and then getting the data:
WIN32_Battery::WIN32_Battery()
{
      Count = 0;
      Batteries = NULL;
      pLoc = NULL;
      pSvc = NULL;
      Connect();
      Refresh();
}
 
WIN32_Battery::~WIN32_Battery()
{
      if( Batteries != NULL )
      {
            Batteries->Release();
      }
      if( pLoc != NULL )
            pLoc->Release();
      if( pSvc != NULL )
            pSvc->Release();
      CoUninitialize();
}
 
void WIN32_Battery::Refresh()
{
      HRESULT hres;
      if( Batteries != NULL )
      {
            Batteries->Release();
      }
 
      hres = pSvc->ExecQuery(
        bstr_t("WQL"),
        bstr_t("SELECT * FROM Win32_Battery"),
        WBEM_FLAG_RETURN_IMMEDIATELY,
        NULL,
        &Batteries);
   
    if (FAILED(hres))
    {
            Batteries = NULL;
    }
   //Count the number of batteries
    IEnumWbemClassObject *BatteryEnum;
    Batteries->Clone( &BatteryEnum );
    this->Count = 0;
    IWbemClassObject *pclsObj;
    ULONG uReturn = 0;
    while (Batteries)
    {
        HRESULT hr = BatteryEnum->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
 
        if(0 == uReturn)
            break;
            this->Count++;
            pclsObj->Release();
 
      }
      BatteryEnum->Release();      
}
A couple of notes on the code differences from the C# code:
1.       The constructor calls a new function, Connect(). More on this later.
2.       In C# I didn’t need to count the batteries, but in C++ I needed to enumerate and count the batteries.
The WMI inteface is handled using COM, so we need to connect COM before using it. Note that I am not a COM expert, or even close, so this code may have some problems. I do know that this code is not meant to be called twice by the application, which also means as I am implementing it there should not be multiple instances of my WIN32_Battery class. The Connect() code is:
BOOL WIN32_Battery::Connect()
{
    HRESULT hres;
 
    hres = CoInitializeEx(0, COINIT_MULTITHREADED);
      // If it failed try becuase the thread model is wrong, try again with a different model
      if( hres == RPC_E_CHANGED_MODE)
            hres = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
   if (FAILED(hres))
    {
        return 1
    }
 
    hres = CoInitializeSecurity(
        NULL,
        -1,                          // COM authentication
        NULL,                        // Authentication services
        NULL,                        // Reserved
        RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication
        RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation 
        NULL,                        // Authentication info
        EOAC_NONE,                   // Additional capabilities
        NULL                         // Reserved
        );
 
                     
    if (FAILED(hres))
    {
            TCHAR ErrorMessage[1000];
            wsprintf( ErrorMessage, L"Error in CoinitializeSecturity %X (%d)", hres, hres );
            MessageBox(NULL,ErrorMessage,L"Error",0);
           
        CoUninitialize();
        return 2;
    }
   
    hres = CoCreateInstance(
        CLSID_WbemLocator,            
        0,
        CLSCTX_INPROC_SERVER,
        IID_IWbemLocator, (LPVOID *) &pLoc);
 
    if (FAILED(hres))
    {
        CoUninitialize();
        return 3;
    }
 
    hres = pLoc->ConnectServer(
         _bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace
         NULL,                    // User name. NULL = current user
         NULL,                    // User password. NULL = current
         0,                       // Locale. NULL indicates current
         NULL,                    // Security flags.
         0,                       // Authority (e.g. Kerberos)
         0,                       // Context object
         &pSvc                    // pointer to IWbemServices proxy
         );
   
    if (FAILED(hres))
    {
        pLoc->Release();    
        CoUninitialize();
        return 4;
    }
 
    hres = CoSetProxyBlanket(
       pSvc,                        // Indicates the proxy to set
       RPC_C_AUTHN_WINNT,           // RPC_C_AUTHN_xxx
       RPC_C_AUTHZ_NONE,            // RPC_C_AUTHZ_xxx
       NULL,                        // Server principal name
       RPC_C_AUTHN_LEVEL_CALL,      // RPC_C_AUTHN_LEVEL_xxx
       RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
       NULL,                        // client identity
       EOAC_NONE                    // proxy capabilities
    );
 
    if (FAILED(hres))
    {
        pSvc->Release();
        pLoc->Release();    
        CoUninitialize();          
        return 5;
    }
      return 0;
}
 
Then just like in the C# code, I added functions to get the number of batteries in the system and get the percent of battery charge remaining:

 
int WIN32_Battery::NumberOfBatteries()
{
      return Count;
}
 
int WIN32_Battery::GetPercentChargeRemaining(int BatteryNumber)
{
      int RetVal = 0;
      RetVal = GetBatteryProperty(BatteryNumber, L"EstimatedChargeRemaining" );
      return RetVal;
}
 
int WIN32_Battery::GetBatteryProperty(int BatteryNumber, TCHAR *Property)
{
    int RetVal = 0;
    IWbemClassObject *pclsObj;
    IEnumWbemClassObject *BatteryEnum;
    Batteries->Clone( &BatteryEnum );
    ULONG uReturn = 0;
  
    int BatteryCounter = 0;
    while (BatteryEnum)
    {
        HRESULT hr = BatteryEnum->Next(WBEM_INFINITE, 1,
            &pclsObj, &uReturn);
 
        if(0 == uReturn)
        {
            break;
        }
            if( BatteryCounter == BatteryNumber )
            {
                  VARIANT vtProp;
                  hr = pclsObj->Get(Property, 0, &vtProp, 0, 0);
                  RetVal = vtProp.uintVal;
                  VariantClear(&vtProp);
                  pclsObj->Release();
                  break;
            }
            pclsObj->Release();
            BatteryCounter++;
    }
      BatteryEnum->Release();
     
      return RetVal;
}
 
Then so that I could use the WIN32_Battery class as a native DLL from C#, I added some wrapper functions. These map to the constructor, destructor, get number of batteries and get percent. They are:
HANDLE InitWIN32_Battery()
{
      WIN32_Battery *Batteries = new WIN32_Battery();
      return (HANDLE)Batteries;
}
 
void DeinitWIN32_Battery( HANDLE Batts )
{
      WIN32_Battery *Batteries = (WIN32_Battery *)Batts;
      delete( Batteries );
}
 
void WIN32_Battery_Refresh(HANDLE Batts)
{
      WIN32_Battery *Batteries = (WIN32_Battery *)Batts;
      Batteries->Refresh();
}
 
int WIN32_Battery_NumberOfBatteries(HANDLE Batts)
{
      WIN32_Battery *Batteries = (WIN32_Battery *)Batts;
      return Batteries->NumberOfBatteries();
}
 
int WIN32_Battery_GetPercentChargeRemaining( HANDLE Batts, int BatteryNumber )
{    
      WIN32_Battery *Batteries = (WIN32_Battery *)Batts;
      return Batteries->GetPercentChargeRemaining(BatteryNumber);
}
 
The function InitWIN32_Battery() returns a pointer to the class as a HANDLE to the C# app. The C# app passes the HANDLE back in to each of the other functions.
In the next articles, look for a series of applications that use these classes.
Copyright © 2010 – Bruce Eitman
All Rights Reserved