4
\$\begingroup\$

I decided to try and recreate Microsoft's REG.exe query functionality."Reg.exe" is Microsoft's Console Registry Tool.

I used Microsoft's documentation of "reg query" to recreate it's functionality within C++.

I thought that it would be a great way to test myself and help to get a better grasp of Microsoft API's as well as better my programming skills.

I know it is not the best code. But if you could please provide constructive criticism as well as explain what you would do differently.

Main.cpp

/*
     Main.cpp
*/
#include <iostream>
#include "AquireInput.h"
#include "Error.h"
#include "Help.h"
#include "RegAdd.h"
#include "RegQuery.h"

int wmain(int argc, wchar_t* argv[])
{
    RegBlock aVal;
    int result = getInput(argc, argv, aVal);

    switch (result) 
    {
        case REG_ADD:           add(aVal);    break;
        case REG_QUERY:         query(aVal);  break;
        case REG_DELETE:                      break;
        case REG_HELP_GENERAL: 
        case REG_HELP_ADD: 
        case REG_HELP_QUERY: 
        case REG_HELP_DELETE: help(result); break;
        default: error(result); break;
    }

    return 0;
}

AquireInput.h

#pragma once
#include <windows.h>
#include <iostream>
#include "Error.h"
#include <map>

// Switch macro's
#define REG_ADD (1)
#define REG_QUERY (2)
#define REG_DELETE (3)

struct RegBlock
{
    std::wstring lHkey = L"";
    std::wstring lHkeyPath = L"";
    std::wstring lValueName = L"";
    std::wstring lDataType = L"";
    std::wstring lData = L"";
    bool lViewed[12] = { false };
    std::map<std::string, bool> lOptions = {
        { "/k",false },
        { "/d",false },
        { "/s",false },
        { "/c",false },
        { "/e",false },
        { "/z",false },
        { "/f",false }
    };
};

int getInput(int argc, wchar_t* argv[], RegBlock& aVal);

AquireInput.cpp

/*
     AquireInput.cpp
*/
#include <queue>
#include "AquireInput.h"
#include "Help.h"

// Parameter handles
#define V (0)
#define VE (1)
#define T (2)
#define D (3)
#define F (4)
#define S (5)
#define K (6)
#define C (7)
#define E (8)
#define Z (9)
#define VA (10)

uint8_t checkIdentifer(std::wstring &arg, bool viewed[])
{
    if (arg == L"/v" && !viewed[V]) 
    {
      viewed[VE] = true;    //  Deactivate value empty arg
      return V;
    }
    if (arg == L"/ve" && !viewed[VE]) 
    {
        viewed[V] = true;   //  Deactivate value specify arg
        return VE;
    }

    if (arg == L"/k" && !viewed[K])
    {
        viewed[D] = true;     //  Deactivate data search arg
        return K;
    } 
    if (arg == L"/d" && !viewed[D])
    {
        viewed[K] = true;    //  Deactivate key value search arg
        return D;
    }

    if (arg == L"/t" && !viewed[T]) return T;
    if (arg == L"/f" && !viewed[F]) return F;
    if (arg == L"/s" && !viewed[S]) return S;
    if (arg == L"/c" && !viewed[C]) return C;
    if (arg == L"/e" && !viewed[E]) return E;
    if (arg == L"/z" && !viewed[Z]) return Z;

    return L'x';
}

bool validDataType(std::wstring &aDataType)
{
    if (getDataTypeMacro(aDataType) != NULL) return true;
    return false;
}

bool validKeyName(std::wstring s)
{
    // Test if a minimum string length has been met
    if (s.size() < 3) return false;

    if (s.size() == 3)
    {
        if (s.substr(0, 3) == L"HKU")  return true;
        return false;
    }
    else
    {
        std::wstring lHkey = s.substr(0, 4);
        if (lHkey == L"HKCR") return true;
        if (lHkey == L"HKCC") return true;
        if (lHkey == L"HKCU") return true;
        if (lHkey == L"HKLM") return true;
        if (lHkey.substr(0, 3) == L"HKU")  return true;

        return false;
    }
}

bool setHKey(std::wstring &s, RegBlock &aVal)
{
    // Test if a minimum string length has been met
    if (s.size() < 3) return false;

    // Addresses case in which user only input 'ADD HKU'
    if (s.size() == 3)
    {
        if (s.substr(0, 3) == L"HKU")
        {
            aVal.lHkey = L"HKU";
        }
        return true;
    }
    else
    { 

        std::wstring lHkey = s.substr(0, 4);

        // Test if a lHkeyPath has been inputted
        if (s.size() > 4)
        {
            aVal.lHkeyPath = s.substr(5);
        }

        if (lHkey == L"HKCR") aVal.lHkey = L"HKCR";
        if (lHkey == L"HKCC") aVal.lHkey = L"HKCC";
        if (lHkey == L"HKCU") aVal.lHkey = L"HKCU";
        if (lHkey == L"HKLM") aVal.lHkey = L"HKLM";
        if (lHkey.substr(0, 3) == L"HKU") 
        {
            aVal.lHkey = L"HKU";
            if (s.size() > 4)
            {
                aVal.lHkeyPath = s.substr(4);
            }
        }

        return true;
    }
}

int getInput(int argc, wchar_t* argv[], RegBlock& aVal)
{
    std::queue<std::wstring> lQueue;
    // Add argv values to lQueue 
    for (int i = 1; i < argc; i++)
    {
        lQueue.push(argv[i]);
    }

    if (lQueue.empty()) return MISSING_CORE_PARAMETER;

    // Get action (ADD,DELETE,QUERY..ect);
    std::wstring lCurrentVal = lQueue.front();
    lQueue.pop();

    // Guard against repeat parameter/sytax e.g /v Hello /ve /f /f  
    bool lViewed[12] = { false };

    if (lCurrentVal == L"ADD")
    {
        if (lQueue.empty()) return MISSING_CORE_PARAMETER;

        lCurrentVal = lQueue.front();

        // Check if ADD HELP was requested
        if (lCurrentVal == L"/?") return REG_HELP_ADD;

        // Check & Set HKEY value
        if (!validKeyName(lCurrentVal)) return NON_VALID_HKEY;
        setHKey(lCurrentVal, aVal);
        lQueue.pop();

        // Check validity of parameter
        while (lQueue.size())
        {
            lCurrentVal = lQueue.front();
            lQueue.pop();

            switch (checkIdentifer(lCurrentVal, lViewed))
            {
                case V:  lViewed[V] = true;
                         aVal.lValueName = lQueue.front();
                         lQueue.pop();
                         break;
                case VE: lViewed[VE] = true;
                         break;
                case T:  lViewed[T] = true;
                         lCurrentVal = lQueue.front();
                         if (!validDataType(lCurrentVal)) return NON_VALID_DATA_TYPE;
                         aVal.lDataType = lCurrentVal;
                         lQueue.pop();
                         break;
                case D:  lViewed[D] = true;
                         aVal.lData = lQueue.front();
                         lQueue.pop();
                         break;
                case F:  lViewed[F] = true;
                         aVal.lOptions["/f"] = true;
                         break;
                default: return UNKNOWN_PARAMETER;
            }

        }
        return REG_ADD;
    }

    if (lCurrentVal == L"QUERY")
    {
        if (lQueue.empty()) return MISSING_CORE_PARAMETER;

        lCurrentVal = lQueue.front();

        // Check if ADD HELP was requested
        if (lCurrentVal == L"/?") return REG_HELP_QUERY;

        // Check & Set HKEY value
        if (!validKeyName(lCurrentVal)) return NON_VALID_HKEY;
        setHKey(lCurrentVal, aVal);
        lQueue.pop();

        // Check validity of parameter
        while (lQueue.size())
        {
            lCurrentVal = lQueue.front();
            lQueue.pop();

            switch (checkIdentifer(lCurrentVal, lViewed))
            {
                case V:  lViewed[V] = true;
                         aVal.lValueName = lQueue.front();
                         lQueue.pop();
                         break;
                case VE: lViewed[VE] = true;
                         break;
                case S:  lViewed[S] = true;
                         aVal.lOptions["/s"] = true;
                         break;
                case F:  lViewed[F] = true;
                         aVal.lOptions["/f"] = true;
                         aVal.lData = lQueue.front();
                         lQueue.pop();
                         break;
                case K:  lViewed[K] = true;
                         aVal.lOptions["/k"] = true;
                         break;
                case D:  lViewed[D] = true;
                         aVal.lOptions["/d"] = true;
                         break;
                case C:  lViewed[C] = true;
                         aVal.lOptions["/c"] = true;
                         break;
                case E:  lViewed[E] = true;
                         aVal.lOptions["/e"] = true;
                         break;
                case T:  lViewed[T] = true;
                         lCurrentVal = lQueue.front();
                         if (!validDataType(lCurrentVal)) return NON_VALID_DATA_TYPE;
                         aVal.lDataType = lCurrentVal;
                         lQueue.pop();
                         break;
                case Z:  lViewed[Z] = true;
                         aVal.lOptions["/z"] = true;
                         break;
                default: return UNKNOWN_PARAMETER;
            }

        }

        // Make sure that search options are not enabled without first specifing a search criteria
        if (lViewed[K] || lViewed[D] || lViewed[C] || lViewed[E] || lViewed[T])
        {
            if (!lViewed[F]) return UNKNOWN_PARAMETER;
        }

        return REG_QUERY;
    }

    if (lCurrentVal == L"DELETE")
    {
        return REG_DELETE;
    }

    if (lCurrentVal == L"/?")
    {
        return REG_HELP_GENERAL;
    }
    return UNKNOWN_ACTION;
}

RegQuery.h

#pragma once
#include "AquireInput.h"

bool query(RegBlock &aVal);

RegQuery.cpp

/*
   RegQuery.cpp
*/
#include "RegQuery.h"
#include <queue>
#include <iomanip>
#include <string>
#include <io.h>
#include <fcntl.h>
#include  <algorithm>
#include <fstream>

#define MAX_KEY_LENGTH 255
#define MAX_VALUE_NAME 32786
#define MAX_DATA_VALUE 2000000  
std::wstring NULLWST = L"";  // Placeholder null value

inline unsigned hexValidInt(int aVal)
{
    if (aVal < 0)
    {
        aVal += 256;
        return aVal;
    }
    return aVal;
}

template <typename I> std::string n2hexstr(I w, size_t hex_len = 2) {
    static const char* digits = "0123456789ABCDEF";
    std::string rc(hex_len, '0');
    for (size_t i = 0, j = (hex_len - 1) * 4; i < hex_len; ++i, j -= 4)
    {
        rc[i] = digits[(w >> j) & 0x0f];
    }
    // Corrects the order of the hex numbers e.g 34FE is corrected to FE34
    std::swap_ranges(std::begin(rc)+2, std::end(rc), std::begin(rc));
    return rc;
}

inline unsigned int convertData(DWORD &aRegType, DWORD &aDataLength, wchar_t *aData, std::wstring &aDataResult)
{
    std::wstring lDataResult;
    if (aRegType == REG_DWORD ||
        aRegType == REG_QWORD ||
        aRegType == REG_DWORD_LITTLE_ENDIAN ||
        aRegType == REG_QWORD_LITTLE_ENDIAN ||
        aRegType == REG_DWORD_BIG_ENDIAN)
    {
        // Reverse loop 
        for (int i = (aDataLength * 0.5) - 1; i >= 0; i--)
        {
            if (aData[i] == 0) continue;
            lDataResult += convertToWstr(n2hexstr(hexValidInt(aData[i]),2));
        }
    }

    if (aRegType == REG_BINARY || aRegType == REG_NONE)
    {
        for (int i = 0; i < (aDataLength * 0.5); i++)
        {
            lDataResult += convertToWstr(n2hexstr(hexValidInt(aData[i]),4));
        }
    }

    if (aRegType == REG_SZ || aRegType == REG_MULTI_SZ)
    {
        int lEnd = (aDataLength * 0.5) - 1;

        // Avoid adding trailing zero
        if (aRegType == REG_MULTI_SZ)
        {
            lEnd = (aDataLength * 0.5) - 2;
        }

        for (int i = 0; i < lEnd; i++)
        {
            if (aData[i] == 0)
            {
             lDataResult += L"\\0";
             continue;
            }

            lDataResult += aData[i];
        }
    }

    aDataResult = lDataResult;
    return 0;
}

struct RegValue
{
    std::wstring lValueName;
    unsigned int lRegType;
    std::wstring lDataValue;

};

inline std::wstring constructKeyPath(std::wstring &aHkeyPath, std::wstring &aSubKey)
{
    std::wstring lSlash = L"\\";
    std::wstring lFullPath;

    if (aSubKey == L"")
    {
        lFullPath = aHkeyPath;
    }
    else
    {
        lFullPath = aHkeyPath + lSlash + aSubKey;
    }

    return lFullPath;
}

inline unsigned int getValueNameList(HKEY hKey, std::wstring &aHkeyPath, std::wstring &aSubKey, std::queue<std::wstring> &aValueList)
{
    std::wstring lFullPath = constructKeyPath(aHkeyPath, aSubKey);
    LPCTSTR lpSubKey = lFullPath.c_str();

    int lResult = RegOpenKeyEx(
        hKey,
        lpSubKey,
        0,                      //add var
        KEY_QUERY_VALUE,        //add var
        &hKey
    );

    if (lResult != ERROR_SUCCESS)
    {
        return lResult;
    }

    // Variables related to RegEnumValue
    LPDWORD               lpReserved = NULL;
    LPDWORD               lpType = NULL;
    LPBYTE                lpData = NULL;
    LPDWORD               lpcbData = NULL;
    DWORD                 dwIndex = 0;

    // Avoid adding lpValueName initial value 
    bool lSkip = true;

    while (lResult != ERROR_NO_MORE_ITEMS)
    {
        wchar_t lpValueName[MAX_VALUE_NAME];
        DWORD   lpcchValueName = MAX_VALUE_NAME;

        // Add the list of value names to a vector list
        if (!lSkip)
        {
            aValueList.push(lpValueName);
        }
        else
        {
            lSkip = false;
        }

        lResult = RegEnumValue(
            hKey,
            dwIndex,
            lpValueName,
            &lpcchValueName,
            lpReserved,
            lpType,
            lpData,
            lpcbData
        );

        dwIndex++;

        // Return an error if lResult is not an acceptable error
        if (lResult != ERROR_SUCCESS && lResult != ERROR_NO_MORE_ITEMS)
        {
            RegCloseKey(hKey);
            return lResult;
        }
    }

    //Close key
    RegCloseKey(hKey);

    return lResult = 0;
}

inline unsigned int getSubKeyList(HKEY hKey, std::wstring &aHkeyPath, std::wstring &aSubKey, std::deque<std::wstring> &aSubKeyList)
{   
    std::wstring lFullPath = constructKeyPath(aHkeyPath, aSubKey);
    LPCTSTR lpSubKey = lFullPath.c_str();

    DWORD i, lResult;
    lResult = RegOpenKeyEx(
        hKey,
        lpSubKey,
        0,                       
        KEY_READ,         
        &hKey
    );

    TCHAR    achKey[MAX_KEY_LENGTH];   // buffer for subkey name
    DWORD    cbName;                   // size of name string 
    TCHAR    achClass[MAX_PATH] = TEXT("");  // buffer for class name 
    DWORD    cchClassName = MAX_PATH;  // size of class string 
    DWORD    cSubKeys = 0;               // number of subkeys 
    DWORD    cbMaxSubKey;              // longest subkey size 
    DWORD    cchMaxClass;              // longest class string 
    DWORD    cValues;              // number of values for key 
    DWORD    cchMaxValue;          // longest value name 
    DWORD    cbMaxValueData;       // longest value data 
    DWORD    cbSecurityDescriptor; // size of security descriptor 
    FILETIME ftLastWriteTime;      // last write time 



    TCHAR  achValue[MAX_VALUE_NAME];
    DWORD cchValue = MAX_VALUE_NAME;

    // Get the class name and the value count. 
    lResult = RegQueryInfoKey(
        hKey,                    // key handle 
        achClass,                // buffer for class name 
        &cchClassName,           // size of class string 
        NULL,                    // reserved 
        &cSubKeys,               // number of subkeys 
        &cbMaxSubKey,            // longest subkey size 
        &cchMaxClass,            // longest class string 
        &cValues,                // number of values for this key 
        &cchMaxValue,            // longest value name 
        &cbMaxValueData,         // longest value data 
        &cbSecurityDescriptor,   // security descriptor 
        &ftLastWriteTime);       // last write time 


                                 // Enumerate the subkeys, until RegEnumKeyEx fails.
    if (cSubKeys)
    {
        for (i = 0; i<cSubKeys; i++)
        {
            cbName = MAX_KEY_LENGTH;
            lResult = RegEnumKeyEx(hKey, i,
                achKey,
                &cbName,
                NULL,
                NULL,
                NULL,
                &ftLastWriteTime);
            if (lResult == ERROR_SUCCESS)
            {
                // Attach parent key to child sub keys
                // e.g: key01/subkey01
                std::wstring lRelativePath = achKey;
                if (aSubKey != L"")
                {
                    lRelativePath = aSubKey + L"\\" + achKey;
                }

                aSubKeyList.push_back(lRelativePath);
            }
        }
    }

    //Close key
    RegCloseKey(hKey);

    return lResult;
}

inline unsigned int getValueData(HKEY aHkey, std::wstring &aHkeyPath, std::wstring &aSubKey, std::wstring &aValueName, RegValue &aValueData)
{
    std::wstring lFullPath = constructKeyPath(aHkeyPath, aSubKey);
    LPCTSTR lSubKey = lFullPath.c_str();
    LPCTSTR lValueName = aValueName.c_str();

    DWORD                      dwFlags = RRF_RT_ANY;        
    DWORD                      pdwType;                    
    wchar_t                   *pvData = new wchar_t[MAX_DATA_VALUE];
    DWORD                      pcbData = MAX_DATA_VALUE;

    int lResult = RegGetValue(
        aHkey,
        lSubKey,
        lValueName,
        dwFlags,
        &pdwType,
        pvData,
        &pcbData
    );

    // Check if lResult is okay
    if (lResult != ERROR_SUCCESS)
    {
        delete[] pvData;
        return lResult;
    }

    if (pcbData >= MAX_DATA_VALUE)
    {
        std::cout << "Data Value Exceeded! Registery data value > 2MB!" << std::endl;
        return lResult;
    }

    // Convert data values & Store results
    std::wstring lDataValue;
    convertData(pdwType, pcbData, pvData, lDataValue);
    aValueData.lDataValue = lDataValue;
    aValueData.lValueName = lValueName;
    aValueData.lRegType = pdwType;

    delete[] pvData;
    return lResult;
}

/*
Show related functions
*/
inline std::wstring getHkeyStringName(HKEY aHkey)
{
    if (aHkey == HKEY_CLASSES_ROOT) return L"HKEY_CLASSES_ROOT\\";
    if (aHkey == HKEY_CURRENT_CONFIG) return L"HKEY_CURRENT_CONFIG\\";
    if (aHkey == HKEY_CURRENT_USER) return L"HKEY_CURRENT_USER\\";
    if (aHkey == HKEY_LOCAL_MACHINE) return L"HKEY_LOCAL_MACHINE\\";
    if (aHkey == HKEY_USERS)  return L"HKEY_USERS\\";

    return L"NULL";
}

inline std::wstring getDataTypeStringName(unsigned int &aDataType)
{
    if (aDataType == REG_SZ) return L"REG_SZ";
    if (aDataType == REG_MULTI_SZ) return L"REG_MULTI_SZ";
    if (aDataType == REG_EXPAND_SZ) return L"REG_EXPAND_SZ";

    if (aDataType == REG_DWORD) return L"REG_DWORD";
    if (aDataType == REG_DWORD_BIG_ENDIAN) return L"REG_DWORD_BIG_ENDIAN";
    if (aDataType == REG_DWORD_LITTLE_ENDIAN) return L"REG_DWORD_LITTLE_ENDIAN";

    if (aDataType == REG_QWORD) return L"REG_QWORD";
    if (aDataType == REG_QWORD_LITTLE_ENDIAN) return L"REG_QWORD_LITTLE_ENDIAN";

    if (aDataType == REG_BINARY) return L"REG_BINARY";

    if (aDataType == REG_NONE) return L"REG_NONE";

    return L"NULL";
}

unsigned int WriteToConsole(std::wstring &aString)
{
    int lResult = ERROR_SUCCESS;
    static HANDLE lConsoleAccess = GetStdHandle(STD_OUTPUT_HANDLE);

    lResult = WriteConsoleW(
                    lConsoleAccess,
                    aString.c_str(),
                    aString.size(),
                    NULL,
                    NULL
                );

    return lResult;
}

// Testing
//#define __nonEnglishMode
//#define __writeToDisk

#if defined (__writeToDisk)
std::wofstream file;
#endif

inline void show(HKEY aHkey, std::wstring &aHkeyPath, std::wstring &aSubKey, RegValue &aValueData, bool aDisplayPath, bool aDisplayValue, bool aLastItem)
{
    // Display Key Path HKEY/key/key..ect 
    if (aDisplayPath)
    {
        std::wstring lSlash = L"\\";
        std::wstring lFullPath;

        #if defined (__writeToDisk) 
        file << L"\n";
        #endif

        // Only attach subkey to path  name if it is supplied
        if (aSubKey == L"")
        {
            lFullPath = getHkeyStringName(aHkey) + aHkeyPath + L"\n";
        }
        else
        {
            lFullPath = getHkeyStringName(aHkey) + aHkeyPath + lSlash + aSubKey + L"\n";
        }

        //fwprintf(stdout, L"%s",lFullPath.c_str());
        WriteToConsole(lFullPath);
        #if defined (__writeToDisk) 
        file << lFullPath;
        #endif

    }

    if (aDisplayValue)
    {
        // Show registry value name
        std::wstring lValueName = (aValueData.lValueName);
        if (lValueName == TEXT("")) lValueName = TEXT("(Default)");

        // Show registry type
        std::wstring lDataType = getDataTypeStringName(aValueData.lRegType);
        std::wstring lDataValue;

        // Show registry data
        if (lDataType == L"REG_DWORD" ||
            lDataType == L"REG_QWORD" ||
            lDataType == L"REG_DWORD_LITTLE_ENDIAN" ||
            lDataType == L"REG_QWORD_LITTLE_ENDIAN" ||
            lDataType == L"REG_DWORD_BIG_ENDIAN")
        {
            lDataValue = L"0x0" + (aValueData.lDataValue);
        }
        else
        {
            lDataValue = (aValueData.lDataValue);
        }

        std::wstring lKeyValue = L"    " + lValueName + L"    " + lDataType + L"    " + lDataValue + L"\n";
        //fwprintf(stdout,L"%s",lKeyValue.c_str());
        WriteToConsole(lKeyValue);
        #if defined (__writeToDisk) 
        file << lKeyValue;
        #endif
    }

    #if defined (__writeToDisk) 
    if (file.fail())
    {
        file.clear();
    }
    #endif
}

DWORD getDataTypeFilterMacro(std::string &aDataType)
{
    if (aDataType == "REG_SZ") return RRF_RT_REG_SZ;
    if (aDataType == "REG_MULTI_SZ") return RRF_RT_REG_MULTI_SZ;
    if (aDataType == "REG_EXPAND_SZ") return RRF_RT_REG_EXPAND_SZ;

    if (aDataType == "REG_DWORD") return RRF_RT_DWORD;
    if (aDataType == "REG_DWORD_BIG_ENDIAN") return RRF_RT_DWORD;
    if (aDataType == "REG_DWORD_LITTLE_ENDIAN") return RRF_RT_DWORD;

    if (aDataType == "REG_QWORD") return RRF_RT_QWORD;
    if (aDataType == "REG_QWORD_LITTLE_ENDIAN") return RRF_RT_QWORD;

    if (aDataType == "REG_BINARY") return RRF_RT_REG_BINARY;
    if (aDataType == "REG_NONE") return RRF_RT_REG_NONE;

    return RRF_RT_ANY;
}

bool isMatch(RegValue &aRegValueData, std::wstring &aDataType, std::wstring &aSearchItem, std::wstring &aKeyName, bool lCaseSensitive = false, bool lExactMatch = false, bool aSearchKeyOnly = false, bool aSearchDataOnly = false)
{   
    bool lResult = false;
    std::wstring lSearchItem = aSearchItem;
    std::wstring lValueName = aRegValueData.lValueName;
    std::wstring lDataValue = aRegValueData.lDataValue;
    std::wstring lKeyName = aKeyName;
    // Convert to lowercase
    if (!lCaseSensitive || !lExactMatch)
    {
        std::transform(lSearchItem.begin(), lSearchItem.end(), lSearchItem.begin(), ::tolower);
        std::transform(lValueName.begin(), lValueName.end(), lValueName.begin(), ::tolower);
        std::transform(lDataValue.begin(), lDataValue.end(), lDataValue.begin(), ::tolower);
        std::transform(lKeyName.begin(), lKeyName.end(), lKeyName.begin(), ::tolower);
    }

    // Search Key Names
    if (aSearchKeyOnly)
    {
        // Split and store the last key 
        // ...MyCo//Key1//Key3  ->> Key3
        auto const pos = lKeyName.find_last_of('\\');
        lKeyName = lKeyName.substr(pos + 1);

        if (lKeyName == lSearchItem)
        {
            return true;
        }

        return false;
    }

    // Search Data 
    if (aSearchDataOnly)
    {
        if (lDataValue == lSearchItem)
        {
            return true;
        }

        return false;
    }

    // Search both key name and data value
    if (!aSearchKeyOnly && !aSearchDataOnly)
    {
        if (lValueName == lSearchItem)
        {
            return true;
        }

        if (lDataValue == lSearchItem)
        {
            return true;
        }

        return false;
    }

    return false;
}

bool query(RegBlock &aVal)
{
    int lResult;

    #if defined (__writeToDisk)
    file.open("screen_output.txt", std::wofstream::app | std::wofstream::in);
    #endif

    // Variables related to Queries/Find
    HKEY                       hkey = getHkeyMacro(aVal.lHkey);
    std::wstring               lHkeyPath = aVal.lHkeyPath;
    bool                       lValueNameEmpty = aVal.lValueName.empty();
    bool                       lFind = aVal.lOptions["/f"];
    bool                       lSearchRecursive = aVal.lOptions["/s"];
    bool                       lFilterDataType = !(aVal.lDataType.empty());
    bool                       lCaseSensitive = aVal.lOptions["/c"];
    bool                       lExactMatch = aVal.lOptions["/e"];
    bool                       lSearchKeyOnly = aVal.lOptions["/k"];
    bool                       lSearchDataOnly = aVal.lOptions["/d"];

    // Specify the search value to use, either the key name or search param input 
    std::wstring lSearchItem = aVal.lData;
    if (!lValueNameEmpty)
    {
        lSearchItem = aVal.lValueName;
    }

    std::deque<std::wstring>  lSubKeyList;
    std::queue<std::wstring>  lValueList;

    // Get the list of value names if none is provided
    if (lValueNameEmpty)
    {
        lResult = getValueNameList(hkey, lHkeyPath, NULLWST, lValueList);

        if (lResult != ERROR_SUCCESS)
        {
            error(lResult);
            return false;
        }
    }
    else
    {
        std::wstring  lValueName = aVal.lValueName;
        lValueList.push(lValueName);
    }

    // Get inital list of sub keys
    lResult = getSubKeyList(hkey, lHkeyPath, NULLWST, lSubKeyList);

    if (lResult != ERROR_SUCCESS)
    {
        error(lResult);
        return false;
    }

    RegValue lRegValueData;
    std::wstring lCurrentSubKey = NULLWST;
    std::wstring lCurrentValue = NULLWST;
    unsigned int lMatchTotal = 0;

    // Allows for the last subkey within the dequeue to be shown
    lSubKeyList.push_back(L"End");

    bool lDisplayPath = false;
    bool lDisplayValue = true;
    bool lNewLine = false;
    bool lMatch = false;

    while (lSubKeyList.size())
    {    
        // Search and Display keys when searching in KEY ONLY mode
        if (lSearchKeyOnly)
        {        
            lMatch = isMatch(
                lRegValueData,
                aVal.lDataType,
                lSearchItem,
                lCurrentSubKey,
                lCaseSensitive,
                lExactMatch,
                lSearchKeyOnly,
                lSearchDataOnly 
            );

            if (lMatch)
            {
                lDisplayPath = true;
                lDisplayValue = false;
                show(hkey, lHkeyPath, lCurrentSubKey, lRegValueData, lDisplayPath, lDisplayValue, false);
                lDisplayPath = false;
                lDisplayValue = true;

                lNewLine = true;
                lMatchTotal++;
            }
        }

        // Display the key path in the case of empty key && list keys when not in recursive/search mode
        if (lValueList.size() == 0 && !(lFilterDataType || lFind) && lValueNameEmpty)
        {
            show(hkey, lHkeyPath, lCurrentSubKey, lRegValueData, true, false, false);
        }
        else
        {
            lDisplayPath = true;
        }

        while (lValueList.size() && !lSearchKeyOnly)
        {
            lCurrentValue = lValueList.front();
            lValueList.pop();
            lResult = getValueData(hkey, lHkeyPath, lCurrentSubKey, lCurrentValue, lRegValueData);

            if (lResult != ERROR_SUCCESS)
            {
                error(lResult);
                return false;
            }

            if (lFind || lFilterDataType)
            {
                lMatch = isMatch(
                    lRegValueData, 
                    aVal.lDataType, 
                    lSearchItem,
                    lCurrentSubKey, 
                    lCaseSensitive, 
                    lExactMatch, 
                    lSearchKeyOnly, 
                    lSearchDataOnly 
                    );

                if (lMatch)
                {
                    if (lSearchKeyOnly)
                    {
                        lDisplayPath = true;
                        lDisplayValue = false;
                    }
                    show(hkey, lHkeyPath, lCurrentSubKey, lRegValueData, lDisplayPath, lDisplayValue, false);
                    lDisplayPath = false;
                    lDisplayValue = true;

                    lMatchTotal++;
                    lNewLine = true;
                }       
            }
            else
            {
                show(hkey, lHkeyPath, lCurrentSubKey, lRegValueData, lDisplayPath, lDisplayValue, false);
                lDisplayPath = false;
            }
        }

        if (lSearchRecursive && !(lFilterDataType || lFind))
        {
            lNewLine = true;
        }

        if (lNewLine)
        {
            fwprintf(stdout, L"\n");
            lNewLine = false;
        }

        // Remove visted Sub-Key from deque 
        lCurrentSubKey = lSubKeyList.front();
        lSubKeyList.pop_front();

        if (lSearchRecursive && lCurrentSubKey != L"End")
        {
            // If the parent key contains sub-keys, add them to lSubKeyList, 
            // in the order they were retrieved from RegEnumEx.
            std::deque<std::wstring> lTemp;
            lResult = getSubKeyList(hkey, lHkeyPath, lCurrentSubKey, lTemp);

            while (lTemp.size())
            {
                lSubKeyList.push_front(lTemp.back());
                lTemp.pop_back();
            }

            // A non-error. Error: "Invalid Handle"
            // This error code just states that the current key being accessed 
            // does not contain any sub-keys.
            if (lResult == 6) lResult = 0;

            if (lResult != ERROR_SUCCESS)
            {
                error(lResult);
                return false;
            }

            // Get the current keys value names
            if (!lSearchKeyOnly)
            {
                lResult = getValueNameList(hkey, lHkeyPath, lCurrentSubKey, lValueList);

                // Ignore key values that cannot be accessed. Error "Access Denied" 
                // Keys cannot be accessed in regedit (admin), so this is a UAC issue.
                if (lResult == 5) lResult = 0;

                if (lResult != ERROR_SUCCESS)
                {
                    error(lResult);
                    return false;
                }
            }
        }
    }

    #if defined (__writeToDisk)
    file.close();
    #endif

    if (lFind || lFilterDataType)
    {
        std::cout << "End of search : " << lMatchTotal << " match(es) found." << std::endl;
    }

    return true;
}
\$\endgroup\$
  • \$\begingroup\$ It would be very helpful for reviewers if you added the missing header files (and possibly cpp files) to the question. Currently, it's hard to evaluate, since important information are missing. \$\endgroup\$ – hoffmale Jul 20 '18 at 11:01
  • \$\begingroup\$ What exactly is REG/your program doing? \$\endgroup\$ – yuri Jul 20 '18 at 15:06
  • \$\begingroup\$ @yuri Essentially, this program queries(searches) through the registry keys. I have been following/implementing the features contained within this link: docs.microsoft.com/en-us/windows-server/administration/… \$\endgroup\$ – user36278 Jul 20 '18 at 23:07
  • \$\begingroup\$ @hoffmale I've added in the missing header files. Hopefully, I will be a bit easier to evaluate now. \$\endgroup\$ – user36278 Jul 20 '18 at 23:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.