#define WIN32_LEAN_AND_MEAN #include #include #include #include #ifndef REG_QWORD #define REG_QWORD 12 #endif #ifdef UNICODE #define tstring wstring #define tostream wostream #define tcerr wcerr #define tcout wcout #else #define tstring string #define tostream ostream #define tcerr cerr #define tcout cout #endif #ifndef ARRAYSIZE #define ARRAYSIZE(x) (sizeof(x) / sizeof(*x)) #endif #if _MSC_VER <= 1300 // VC 6 std::tostream& operator<<(std::tostream& out, ULONGLONG ull) { return out << (unsigned __int64)ull; } #endif struct RegTreeData { std::tstring keyWithMostValues; // maxValues std::tstring keyWithMostSubkeys; // maxSubkeys std::tstring keyWithMostData; // maxData std::tstring longestValueName; // longestvalueNameLen std::tstring longestKeyName; // longestKeyNameLen std::tstring longestDataValue; // longestvaluedataLen std::map valueTypeCounts; // totalValues is the count of entries in here ULONGLONG totalSubkeys; ULONGLONG totalValueBytes; ULONG longestValueDataLen; ULONG longestValueNameLen; ULONG longestKeyNameLen; ULONG maxSubKeys; ULONG maxValues; ULONG maxData; RegTreeData(const std::tstring& pName) : keyWithMostValues(pName), keyWithMostSubkeys(keyWithMostValues), keyWithMostData(keyWithMostValues), totalSubkeys(0), totalValueBytes(0), longestValueDataLen(0), longestValueNameLen(0), longestKeyNameLen(0), maxSubKeys(0), maxValues(0), maxData(0) {} void Merge(const RegTreeData& subTree) { totalSubkeys += subTree.totalSubkeys; totalValueBytes += subTree.totalValueBytes; for(DWORD i = REG_NONE; i <= REG_QWORD; ++i) { std::map::const_iterator iter = subTree.valueTypeCounts.find(i); size_t numVals = (iter == subTree.valueTypeCounts.end()) ? 0 : iter->second; if(numVals) { valueTypeCounts[i] += numVals; } } if(subTree.longestValueDataLen > longestValueDataLen) { longestValueDataLen = subTree.longestValueDataLen; longestDataValue = subTree.longestDataValue; } if(subTree.longestValueNameLen > longestValueNameLen) { longestValueName = subTree.longestValueName; longestValueNameLen = subTree.longestValueNameLen; } if(subTree.longestKeyNameLen > longestKeyNameLen) { longestKeyName = subTree.longestKeyName; longestKeyNameLen = subTree.longestKeyNameLen; } if(subTree.maxSubKeys > maxSubKeys) { maxSubKeys = subTree.maxSubKeys; keyWithMostSubkeys = subTree.keyWithMostSubkeys; } if(subTree.maxValues > maxValues) { maxValues = subTree.maxValues; keyWithMostValues = subTree.keyWithMostValues; } if(subTree.maxData > maxData) { maxData = subTree.maxData; keyWithMostData = subTree.keyWithMostData; } } }; void RegKeyGetData(HKEY hKey, RegTreeData& rtd) { std::tstring currentKeyName = rtd.keyWithMostValues + TEXT('\\'); std::tcerr << TEXT("Processing ") << currentKeyName << TEXT('\n'); DWORD values = 0, subKeys = 0, maxSubKeyLen = 0, maxValueNameLen = 0, maxValueLen = 0; RegQueryInfoKey(hKey, NULL, NULL, NULL, &subKeys, &maxSubKeyLen, NULL, &values, &maxValueNameLen, &maxValueLen, NULL, NULL); LSTATUS lStat = 0; rtd.totalSubkeys += subKeys; rtd.maxSubKeys = subKeys; rtd.maxValues = values; ULONG totalDataLen = 0; DWORD fullSizeValue = maxValueNameLen + 1; std::tstring valueName(fullSizeValue, 0); DWORD i = 0; for(; i < values; ++i) { DWORD sizeOfThisValueName = fullSizeValue, type = 0, dataLen = 0; if((lStat = RegEnumValue(hKey, i, &valueName[0], &sizeOfThisValueName, NULL, &type, NULL, &dataLen)) == ERROR_SUCCESS) { rtd.valueTypeCounts[type]++; totalDataLen += dataLen; if(sizeOfThisValueName == maxValueNameLen) { rtd.longestValueName = currentKeyName + valueName.c_str(); rtd.longestValueNameLen = sizeOfThisValueName; } if(dataLen == maxValueLen) { rtd.longestDataValue = currentKeyName + valueName.c_str(); rtd.longestValueDataLen = dataLen; } std::fill(valueName.begin(), valueName.begin() + sizeOfThisValueName, WCHAR(0)); } else { std::tcerr << TEXT("Failed to enum value ") << i << TEXT(" from key ") << currentKeyName << TEXT(" because of error ") << lStat << '\n'; } } rtd.totalValueBytes += totalDataLen; rtd.maxData = totalDataLen; DWORD fullSizeSubKey = maxSubKeyLen + 1; std::tstring keyName(fullSizeSubKey, 0); for(i = 0; i < subKeys; ++i) { DWORD sizeOfThisSubKeyName = fullSizeSubKey; if((lStat = RegEnumKeyEx(hKey, i, &keyName[0], &sizeOfThisSubKeyName, NULL, NULL, NULL, NULL)) == ERROR_SUCCESS) { HKEY hKeyChild = NULL; if((lStat = RegOpenKeyEx(hKey, keyName.c_str(), 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hKeyChild)) == ERROR_SUCCESS) { std::tstring fullKeyName = currentKeyName + keyName.c_str(); if(sizeOfThisSubKeyName == maxSubKeyLen) { rtd.longestKeyName = fullKeyName; rtd.longestKeyNameLen = sizeOfThisSubKeyName; } RegTreeData subTreeData(fullKeyName); RegKeyGetData(hKeyChild, subTreeData); RegCloseKey(hKeyChild); rtd.Merge(subTreeData); } else { std::tcerr << TEXT("Failed to open ") << currentKeyName << TEXT('\\') << keyName << TEXT(" because of error ") << lStat << TEXT('\n'); } std::fill(keyName.begin(), keyName.begin() + sizeOfThisSubKeyName, WCHAR(0)); } else { std::tcerr << TEXT("Failed to enum subkey ") << i << TEXT(" from ") << currentKeyName << TEXT(" because of error ") << lStat << TEXT('\n'); } } } void DumpStats(const RegTreeData& data, const TCHAR* topKeyName) { std::tcout << TEXT("Report for ") << topKeyName << TEXT('\n'); std::tcout << TEXT("Longest key name: ") << data.longestKeyName << TEXT(" (") << data.longestKeyNameLen << TEXT(" chars)\n"); std::tcout << TEXT("Longest value name: ") << data.longestValueName << TEXT(" (") << data.longestValueNameLen << TEXT(" chars)\n"); std::tcout << TEXT("Value with most data: ") << data.longestDataValue << TEXT(" (") << data.longestValueDataLen << TEXT(" bytes)\n"); std::tcout << TEXT("Key with most subkeys: ") << data.keyWithMostSubkeys << TEXT(" (") << data.maxSubKeys << TEXT(" subkeys)\n"); std::tcout << TEXT("Key with most values: ") << data.keyWithMostValues << TEXT(" (") << data.maxValues << TEXT(" values)\n"); std::tcout << TEXT("Key holding most data in its values: ") << data.keyWithMostData << TEXT(" (") << data.maxData << TEXT(" bytes)\n"); std::tcout << TEXT("Total Subkeys: ") << data.totalSubkeys << TEXT('\n'); std::tcout << TEXT("Total Data held in bytes: ") << data.totalValueBytes << TEXT('\n'); std::tcout << TEXT("Total number of values: ") << data.valueTypeCounts.size() << TEXT('\n'); static const TCHAR* regValueTypes[] = { TEXT("REG_NONE"), TEXT("REG_SZ"), TEXT("REG_EXPAND_SZ"), TEXT("REG_BINARY"), TEXT("REG_DWORD"), TEXT("REG_DWORD_BIG_ENDIAN"), TEXT("REG_LINK"), TEXT("REG_MULTI_SZ"), TEXT("REG_RESOURCE_LIST"), TEXT("REG_FULL_RESOURCE_DESCRIPTOR"), TEXT("REG_RESOURCE_REQUIREMENTS_LIST"), TEXT("REG_QWORD") }; std::tcout << TEXT("Values by type:\n"); for(DWORD i = REG_NONE; i <= REG_QWORD; ++i) { std::map::const_iterator iter = data.valueTypeCounts.find(i); size_t numVals = iter == data.valueTypeCounts.end() ? 0 : iter->second; if(numVals) { std::tcout << regValueTypes[i] << TEXT(": ") << numVals << TEXT('\n'); } } std::tcout << std::endl; } int __cdecl wmain(int argc, wchar_t** argv) { { HKEY baseKeys[] = { HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CURRENT_CONFIG }; const TCHAR* baseKeyNames[] = { TEXT("HKEY_CLASSES_ROOT"), TEXT("HKEY_CURRENT_USER"), TEXT("HKEY_LOCAL_MACHINE"), TEXT("HKEY_USERS"), TEXT("HKEY_CURRENT_CONFIG") }; RegTreeData baseKeyInfo[] = { RegTreeData(baseKeyNames[0]), RegTreeData(baseKeyNames[1]), RegTreeData(baseKeyNames[2]), RegTreeData(baseKeyNames[3]), RegTreeData(baseKeyNames[4]), }; ULONG i = 0; for(; i < ARRAYSIZE(baseKeys); ++i) { RegKeyGetData(baseKeys[i], baseKeyInfo[i]); DumpStats(baseKeyInfo[i], baseKeyNames[i]); } for(i = 1; i < ARRAYSIZE(baseKeyInfo); ++i) { baseKeyInfo[0].Merge(baseKeyInfo[i]); } DumpStats(baseKeyInfo[0], TEXT("Totals")); return 0; } }