WMI Win32_PhysicalMemory code flakey

I have a short program (below) that tries to get the Capacity of the machine's DIMMs, using WMI from C++.  The program works fine on some machines but on others retuns an error from the line

     hRes = pWbemServices->ExecQuery()

where the code tries to enumerate the instances of Win32_PhysicalMemory. On one of the machines that fails it works of the OS is W2K but not W2K3. Another machine that fails, however, is running W2K, so it doesn't seem to be OS specific.

If anyone sees the problem I would be most grateful.



#include <stdio.h>
#include <stdlib.h>
#include <wbemidl.h>

void main()

      UINT bank = 1;

      CoInitialize(NULL);      // Initialize the COM library for this thread
      IWbemLocator *pIWbemLocator = NULL;
      IWbemServices *pWbemServices = NULL;
      IEnumWbemClassObject *pEnumObject = NULL;
      BSTR bstrNameSpace = (L"root\\cimv2");

            Ceate an instance of the class ID CLSID_WbemAdministrativeLocator.
            The interface to the newly created object is returned in the last parameter,
            pIWbemLocator here.

      if (CoCreateInstance(
                  ( void **) & pIWbemLocator) != S_OK) {
                  printf("\n\tFailed to create instance of WbemLocator");

            Get the interface pointer to the desired namespace
            (root\cimv2) by calling the ConnectServer method of pIWbemLocator.

      if (pIWbemLocator->ConnectServer(
            bstrNameSpace,                  // "root\cimv2"
            &pWbemServices) != S_OK) {
            printf("\n\tFailed to get pointer to root\\cimv2");

      HRESULT hRes;
      BSTR strQuery = (L"Select * from Win32_PhysicalMemory");      // query request: find all in win32_PhysicalMemory
      BSTR strQL = (L"WQL");      // query language will be WQL: WMI Query Language

            Below we execute the query method of WebServices to ask for all instances
            of Win32_PhysicalMemory. Results are put into the enumerator object, pEnumObject.

      hRes = pWbemServices->ExecQuery(strQL, strQuery, 0, NULL, &pEnumObject);   //  <-- Hit this error on some machines
      if (hRes != S_OK) {
            printf("\n\tCould not enumerate instances of Win32_PhysicalMemory");
      printf("\n\tEnumerated instances of Win32_PhysicalMemory OK");

      ULONG uCount = 1;      // we will ask for one object at a time
      ULONG uReturned;      // the number actually returned by Next
      IWbemClassObject *pClassObject = NULL;      // pClassObject must point to a NULL
      VARIANT val;

            Below we call the Next method of the EnumObject to get the next instance of
            Win32_PhysicalMemory. We will get back S_OK as long as there's another
            object to hand to us and we'll get back S_FALSE when there are no more.
            At this point we'll also get back 0 in uReturned instead of 1.
      printf("\n\tCalling pEnumObject->Next()");
      while(hRes = pEnumObject->Next(INFINITE,                  // no timeout
                                         uCount,                        // get one objecd at a time
                                           &pClassObject,                  // pointer to storage for the object
                                           &uReturned) == S_OK) {                  // pointer to return the number of obects returned (might not be uCount)

            printf("\n\tBack from pEnumObject->Next(), Calling pClassObject->Get()");
            if (hRes = pClassObject->Get(L"Capacity", 0L, &val, NULL, NULL) != S_OK) {
                  printf("\n\tFailed to get Capacitry for this DIMM");

                        The BSTR in val, below, is a wide character NULL-terminated string, while
                        the Cstring str holds ANSI characters. Happily, the assign operator converts
                        the wide characters to ANSI, as if there were a cast involved.  
                  printf("\n\tBank %d = %lsMB\n", bank++, val.bstrVal);
                  VariantClear (&val);

      printf("\n\nPress Enter to exit...");

      getc(stdin);                  // pause for user entry

Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

What is the value of hRes when the call fails?
stevaAuthor Commented:
WBEM_E_ACCESS_DENIED = 0x80041003.
It would appear that whatever account you are logged in to on the machines that don't work doesn't have sufficient privileges to access WMI. If you are using the same domain\account everywhere, there is probably a local policy causing the problem, or some-such.
Get expert help—faster!

Need expert help—fast? Use the Help Bell for personalized assistance getting answers to your important questions.

stevaAuthor Commented:
But I log in as Adminstrator on all the machines.

The only other thing that occurs to me is that perhaps some hardware platforms don't support being able to enumerate the individual dimms, and the error it gives is an access denied.

In these cases, you may have to settle for using Win32_OperatingSystem and looking at the  "Total VisibleMemorySize" property, or Win32_ComputerSystem and "TotalPhysicalMemory".

Although, interestingly enough, neither of those gives exactly the right answer on my computer. They are both off by exactly 2060 kilobytes.
stevaAuthor Commented:
Both Win32_OperatingSystem\TotalPhysicalMemory and Win32_ComputerSystem\TotalPhysicalMemory show what the OS thinks it has,
rather than what's physically plugged into the machine. Setting /MAXMEM to a lower value in boot.ini reduces both those values.  I need to know how much memory is physically plugged into the machine, regardless of the value of /MAXMEM.

I believe my problem is related to setting permissions on the COM objects with calls like CoInitializeSecurity() and/or CoSetProxyBlanket(), but digging out the right calls and the right parameters is like pulling teeth.  The calls don't even compile unless you define _WIN32_DCOM_ (discovered only by looking through OBJBASE.H to find out how it could be included and the calls still not defined.)


Is it only Win32_PhysicalMemory that fails, or does it fail with any class?

This stuff is an order of magnitude easier in Visual Basic, plus you can find tons of sample VB code that does WMI on Microsoft's web site.

It might be worth taking a look at some of the scripts and trying them on the machines where WMI is failing. Once you get it to work with VB, then you can translate that back to C++:



Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
stevaAuthor Commented:
I have the code working now.  It needed a couple of calls to set the permissions on the WMI proxy so that I could access it: CoInitializeSecurity() and CoSetProxyBlanket().

I released the points to you just for trying to help out.  Thanks.

It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.