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.
#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.
#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.
#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.
#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.
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
.
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.
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