Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++11

Use C++11 Range For-Loop to Enumerate Registry, Folder and WMI

5.00/5 (11 votes)
16 Oct 2022CPOL3 min read 9.6K   340  
C++11 Range For-Loop to Enumerate Registry, Folder and WMI
Learn how to implement C++11 range for-loop to enumerate registry keys/values, files in a folder and results of Windows Management Instrumentation (WMI) queries.

The example code is hosted on Github.

Introduction

Wouldn't it be nice if we can use C++11 range for-loop to enumerate Windows registry keys/values, files in a folder and Windows Management Instrumentation (WMI) queries without the boilerplate initialization code or knowing the underlying Win32 API. This library does just that based on Marius Bancila's article: Enabling MFC Collections to Work in Range-based for Loops. Marius enables range-based for-loop for MFC collections whereas this library applies his concept to non-collections to like registry, files in a folder and WMI queries.
Note: This is a header-only library.

Pros

  • Resource management is accomplished with RAII: handles and resources are released in the Enumerator's destructor.
  • The Windows API usage details are abstracted away from the user.
  • Less boilerplate code to be written and just focus on your business logic.

Cons

  • Iterators produced by the Enumerator cannot be used in STL algorithms because the underlying enumerated object is not a collection.
  • This cannot be applied to enumeration done with asynchronous callbacks like EnumWindows because callbacks cannot be refactored into range for-loop iterators.

Enumerate Folder Example

EnumFolder class took MSDN Example of Raw Win32 API Folder Enumeration to enumerate folder and is a thin wrapper over that example. EnumFolder implementation is shown in the later section.

C++
#include "EnumFolder.h"

EnumFolder enumFolder(L"c:\\temp");
for (auto const& ffd : enumFolder)
{
    if (IsFolder(ffd))
    {
        std::wcout << L"  " << ffd.cFileName << "   <DIR>\n";
    }
    else
    {
        LARGE_INTEGER filesize;
        filesize.LowPart = ffd.nFileSizeLow;
        filesize.HighPart = ffd.nFileSizeHigh;
        std::wcout << L"  " << ffd.cFileName << "   " 
            << filesize.QuadPart << L" bytes\n";
    }
}

Enumerate Registry Keys Example

Compared with MSDN Example of Raw Win32 Registry Enumeration with this concise example below, the developer is saved from writing that many lines of boilerplate code.

C++
#include "EnumRegistryKey.h"

EnumRegistryKey enumRegistryKey(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft");
for (auto const& szKey : enumRegistryKey)
{
    std::wcout << szKey << L"\n";
}

Enumerate Registry Values Example

This is an example of enumerating registry values with range for-loop.

C++
#include "EnumRegistryValue.h"

EnumRegistryValue enumRegistryValue(HKEY_CURRENT_USER, L"Software\\7-Zip\\Compression");
for (auto const& szValueName : enumRegistryValue)
{
    std::wcout << szValueName << L"\n";
}

Enumerate WMI Example

This is the example of enumerating results of WMI query of processes running in the system with ranged for-loop.
Note: COM runtime has to be initialized and deinitialized for WMI. You can try changing the SQL query and see what result it returns.

C++
#include "EnumWmi.h"

if (!InitializeCOM())
{
    std::cerr << "InitializeCOM() fails! Program exits.\n";
    return 1;
}
{
    EnumWmi enumWmi(L"SELECT * FROM Win32_Process");
    for (const auto& process : enumWmi)
    {
        _bstr_t str = process[L"Name"].bstrVal;
        std::cout << "Program name: " << str << std::endl;
    }
}
CoUninitialize();

How EnumFolder Class is Implemented

To enable C++11 range for-loop, basically two classes need to be written: the Enumerator class that acts as a 'collection' class and an iterator class that iterates over the 'collection'. For its simplicity, only EnumFolder class is explained here because other Enumerator class involving registry and WMI requires a deep understanding of their workings. We used the iterator class for MFC CStringArray featured in Marius Bancila's article is used as a reference to enable for C++11 range for-loop.

C++
class CStringArrayIterator
{
public:
   CStringArrayIterator(CStringArray& collection, INT_PTR const index):
      m_index(index),
      m_collection(collection)
   {
   }

   bool operator!= (CStringArrayIterator const & other) const
   {
      return m_index != other.m_index;
   }

   CString& operator* () const
   {
      return m_collection[m_index];
   }

   CStringArrayIterator const & operator++ ()
   {
      ++m_index;
      return *this;
   }

private:
   INT_PTR        m_index;
   CStringArray&  m_collection;
};

inline CStringArrayIterator begin(CStringArray& collection)
{
   return CStringArrayIterator(collection, 0);
}

inline CStringArrayIterator end(CStringArray& collection)
{
   return CStringArrayIterator(collection, collection.GetCount());
}

C++11 Range for loop calls the begin() and end() behind the scene to get the beginning and ending iterators.

EnumFolder class is implemented in this way with FindFirstFile, FindNextFile and FindClose.

C++
class EnumFolder
{
public:
    EnumFolder(const std::wstring& folder)
        : m_Folder(folder)
        , m_hFind(INVALID_HANDLE_VALUE)
    {
        m_Folder += L"\\*";
        ::ZeroMemory(&m_Ffd, sizeof(m_Ffd));
    }
    ~EnumFolder()
    {
        if (m_hFind != INVALID_HANDLE_VALUE)
            FindClose(m_hFind);

        m_hFind = INVALID_HANDLE_VALUE;
    }
    bool Init()
    {
        m_hFind = FindFirstFile(m_Folder.c_str(), &m_Ffd);
        return m_hFind != INVALID_HANDLE_VALUE;
    }
    bool Next()
    {
        return FindNextFile(m_hFind, &m_Ffd) != 0;
    }
    const WIN32_FIND_DATA& GetFFD() const
    {
        return m_Ffd;
    }
private:
    std::wstring m_Folder;
    HANDLE m_hFind;
    WIN32_FIND_DATA m_Ffd;
};

While its iterator class, EnumFolderIterator, is implemented in this way.

C++
class EnumFolderIterator
{
public:
    EnumFolderIterator(EnumFolder& collection, INT_PTR const index) :
        m_Index(index),
        m_Collection(collection)
    {
        if (index == 0)
        {
            if(!m_Collection.Init())
                m_Index = -1;
        }
    }

    bool operator!= (EnumFolderIterator const& other) const
    {
        return m_Index != other.m_Index;
    }

    const WIN32_FIND_DATA& operator* () const
    {
        return m_Collection.GetFFD();
    }

    EnumFolderIterator const& operator++ ()
    {
        if (m_Index != -1)
        {
            if (m_Collection.Next())
                ++m_Index;
            else
                m_Index = -1;
        }
        return *this;
    }

private:
    INT_PTR        m_Index;
    EnumFolder& m_Collection;
};

inline EnumFolderIterator begin(EnumFolder& collection)
{
    return EnumFolderIterator(collection, 0);
}

inline EnumFolderIterator end(EnumFolder& collection)
{
    return EnumFolderIterator(collection, -1);
}

The begin() returns with a EnumFolderIterator with an index of 0 while the end() gives it an index of -1 because the actual children count is not provided by WinAPI. This concludes our explanation of the design of EnumFolder and its iterator. Scroll back to Enumerate Folder Example to see the range for-loop in action.

History

  • 16th October, 2022: Uploaded the example of enumerating window handles. However, it is not using the iterator technique listed here but by saving every window handle into a vector inside the callback function.
  • 26th September, 2022: First release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)