在现代编程中,经常需要遍历各种数据集合,如Windows注册表键/值、文件夹中的文件以及Windows管理指令(WMI)查询结果。传统方法通常需要编写大量的初始化代码,并且需要深入了解底层的Win32 API。幸运的是,C++11引入了范围for循环(range for-loop),它提供了一种更简洁、更直观的方式来遍历集合。本文将介绍如何利用C++11范围for循环来枚举Windows注册表键/值、文件夹中的文件以及WMI查询结果,而无需编写繁琐的初始化代码或了解底层Win32 API。
这个库基于Marius Bancila的文章“Enabling MFC Collections to Work in Range-based for Loops”。Marius Bancila使得MFC集合可以在基于范围的for循环中使用,而这个库将他的概念应用到了非集合对象上,如注册表、文件夹中的文件和WMI查询。
优点:
枚举文件夹示例:
#include "EnumFolder.h"
EnumFolder enumFolder(L"c:\\temp");
for(auto const& ffd : enumFolder) {
if(IsFolder(ffd)) {
std::wcout << L"" << ffd.cFileName << L"
枚举注册表键示例:
与MSDN提供的原始Win32注册表枚举示例相比,下面的示例更加简洁,开发者无需编写那么多的样板代码。
#include "EnumRegistryKey.h"
EnumRegistryKey enumRegistryKey(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft");
for(auto const& szKey : enumRegistryKey) {
std::wcout << szKey << L"\n";
}
枚举注册表值示例:
这是一个使用范围for循环枚举注册表值的示例。
#include "EnumRegistryValue.h"
EnumRegistryValue enumRegistryValue(HKEY_CURRENT_USER, L"Software\\7-Zip\\Compression");
for(auto const& szValueName : enumRegistryValue) {
std::wcout << szValueName << L"\n";
}
枚举WMI示例:
这是一个使用范围for循环枚举系统正在运行的进程的WMI查询结果的示例。注意:需要初始化和反初始化COM运行时。
#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();
EnumFolder类的实现: 要启用C++11范围for循环,基本上需要编写两个类:作为“集合”类的枚举器类和一个迭代“集合”的迭代器类。由于其他涉及注册表和WMI的枚举器类需要深入了解它们的工作原理,这里只解释EnumFolder类。使用了Marius Bancila文章中提到的MFC CStringArray的迭代器类作为参考,以实现C++11范围for循环。
迭代器类的实现:
class CStringArrayIterator {
public:
CStringArrayIterator(CStringArray& collection, INT_PTR const index)
: m_index(index), m_collection(collection) { }
bool operator!= (const CStringArrayIterator& 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范围for循环在幕后调用begin()和end()来获取起始和结束迭代器。EnumFolder类就是这样实现的,使用FindFirstFile、FindNextFile和FindClose。
EnumFolder类的实现:
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;
};
迭代器类的实现:
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!= (const EnumFolderIterator& 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);
}