This is a more complete answer, but suffers in readability because of the compact style I had to use to keep it under the 3k character limit.
It also uses a C++ class (Udc) which automatically manages UNICODE_STRING data and allows seamless use of null-embedded string literals.  This helps with usability, but the extra abstraction acts to obfuscate what is happening on a lower level.
So, I made this a separate answer to preserve the original's straightforward logic.
This new version's embedded API includes creating keys, querying and setting values, deleting values and keys, handling null-embedded key and value names, and finding the path for HKEY_CURRENT_USER.  The user may select from several demos to run -- launch without command line arguments to see a menu. 
#include <iostream>
#include <string>
#include <vector>
#include <utility>
namespace nt {
    typedef int BOOL;
    typedef unsigned short USHORT, WORD;
    typedef unsigned long NTSTATUS, ULONG, DWORD;
    typedef struct HMODULE__ {int unused;} *HMODULE;
    typedef struct HANDLE__ {int unused;} *HANDLE;
    enum { STATUS_SUCCESS=0, STATUS_OBJECT_NAME_NOT_FOUND=0xC0000034 };
    enum {
        KEY_QUERY_VALUE=1, KEY_SET_VALUE=2, KEY_ENUMERATE_SUB_KEYS=8,
        KEY_WOW64_64KEY=0x100, KEY_WOW64_32KEY=0x200, DELETE=0x10000
    };
    enum { REG_OPTION_NON_VOLATILE=0, REG_OPTION_VOLATILE=1 };
    enum { REG_CREATED_NEW_KEY=1, REG_OPENED_EXISTING_KEY=2 };
    enum { REG_NONE=0, REG_SZ=1, REG_EXPAND_SZ=2, REG_BINARY=3, REG_DWORD=4, REG_MULTI_SZ=7 };
    static const ULONG OBJ_CASE_INSENSITIVE = 0x40;
    struct UNICODE_STRING {
        USHORT   Length, MaximumLength;
        wchar_t* Buffer;
    };
    struct OBJECT_ATTRIBUTES {
        ULONG           Length;
        HANDLE          RootDirectory;
        UNICODE_STRING* ObjectName;
        ULONG           Attributes;
        void            *SecurityDescriptor, *SecurityQualityOfService;
        OBJECT_ATTRIBUTES() {
            Length = sizeof(OBJECT_ATTRIBUTES);
            RootDirectory = 0;
            ObjectName = 0;
            Attributes = OBJ_CASE_INSENSITIVE;
            SecurityQualityOfService = SecurityDescriptor = 0;
        }
    };
    enum KEY_VALUE_INFORMATION_CLASS { KeyValueBasicInformation=0, KeyValueFullInformation };
    enum KEY_INFORMATION_CLASS       { KeyBasicInformation=0 };
    template <int max_length>
    struct KEY_VALUE_BASIC_INFORMATION {
        ULONG TitleIndex, Type, NameLength;
        wchar_t Name[max_length+1];
    };
    template <int max_name_and_data_length>
    struct KEY_VALUE_FULL_INFORMATION {
        ULONG TitleIndex, Type, DataOffset, DataLength, NameLength;
        wchar_t Name[max_name_and_data_length+2];
        const void* GetData() const {
            return (const void*)((const char*)this + DataOffset);
        }
    };
    template <int max_length>
    struct KEY_BASIC_INFORMATION {
        long long  LastWriteTime;
        ULONG      TitleIndex, NameLength;
        wchar_t    Name[max_length+1];
    };
    extern "C" {
        // Kernel32 imports (assumed linked by default)
        typedef int(__stdcall *FARPROC)();
        HMODULE  __stdcall LoadLibraryA(const char*);
        BOOL     __stdcall FreeLibrary(HMODULE);
        FARPROC  __stdcall GetProcAddress(HMODULE, const char*);
        // Native NT API Functions
        typedef NTSTATUS(__stdcall RTLFORMATCURRENTUSERKEYPATH)(UNICODE_STRING*);
        typedef NTSTATUS(__stdcall NTCREATEKEY)(HANDLE*, ULONG DesiredAccess, OBJECT_ATTRIBUTES*, ULONG, UNICODE_STRING* Class, ULONG CreateOptions, ULONG* Disposition);
        typedef NTSTATUS(__stdcall NTOPENKEY)(HANDLE*, ULONG DesiredAccess, OBJECT_ATTRIBUTES*);
        typedef NTSTATUS(__stdcall NTENUMERATEKEY)(HANDLE, ULONG Index, KEY_INFORMATION_CLASS, void* KeyInformation, ULONG KeyInformationLength, ULONG* ResultLength);
        typedef NTSTATUS(__stdcall NTENUMERATEVALUEKEY)(HANDLE, ULONG Index, KEY_VALUE_INFORMATION_CLASS, void* KeyValueInformation, ULONG KeyValueInformationLength, ULONG* ResultLength);
        typedef NTSTATUS(__stdcall NTQUERYVALUEKEY)(HANDLE, UNICODE_STRING* ValueName, KEY_VALUE_INFORMATION_CLASS, void* KeyValueInformation, ULONG Length, ULONG* ResultLength);
        typedef NTSTATUS(__stdcall NTSETVALUEKEY)(HANDLE, UNICODE_STRING* ValueName, ULONG TitleIndex, ULONG Type, void* Data, ULONG DataSize);
        typedef NTSTATUS(__stdcall NTDELETEVALUEKEY)(HANDLE, UNICODE_STRING* ValueName);
        typedef NTSTATUS(__stdcall NTDELETEKEY)(HANDLE);
        typedef NTSTATUS(__stdcall NTCLOSE)(HANDLE);
    }
    static RTLFORMATCURRENTUSERKEYPATH* RtlFormatCurrentUserKeyPath;
    static NTCREATEKEY*          NtCreateKey;
    static NTOPENKEY*            NtOpenKey;
    static NTENUMERATEKEY*       NtEnumerateKey;
    static NTENUMERATEVALUEKEY*  NtEnumerateValueKey;
    static NTQUERYVALUEKEY*      NtQueryValueKey;
    static NTSETVALUEKEY*        NtSetValueKey;
    static NTDELETEVALUEKEY*     NtDeleteValueKey;
    static NTDELETEKEY*          NtDeleteKey;
    static NTCLOSE*              NtClose;
    class NtDllScopedLoader {
        HMODULE hNtDll;
    public:
        NtDllScopedLoader() {
            hNtDll = LoadLibraryA("ntdll.dll");
            if(!hNtDll) {
                std::wcout << L"LoadLibraryA failed loading ntdll.dll\n";
                return;
            }
            RtlFormatCurrentUserKeyPath = (RTLFORMATCURRENTUSERKEYPATH*)GetProcAddress(hNtDll, "RtlFormatCurrentUserKeyPath");
            NtCreateKey          = (NTCREATEKEY*)GetProcAddress(hNtDll, "NtCreateKey");
            NtOpenKey            = (NTOPENKEY*)GetProcAddress(hNtDll, "NtOpenKey");
            NtEnumerateKey       = (NTENUMERATEKEY*)GetProcAddress(hNtDll, "NtEnumerateKey");
            NtEnumerateValueKey  = (NTENUMERATEVALUEKEY*)GetProcAddress(hNtDll, "NtEnumerateValueKey");
            NtQueryValueKey      = (NTQUERYVALUEKEY*)GetProcAddress(hNtDll, "NtQueryValueKey");
            NtSetValueKey        = (NTSETVALUEKEY*)GetProcAddress(hNtDll, "NtSetValueKey");
            NtDeleteValueKey     = (NTDELETEVALUEKEY*)GetProcAddress(hNtDll, "NtDeleteValueKey");
            NtDeleteKey          = (NTDELETEKEY*)GetProcAddress(hNtDll, "NtDeleteKey");
            NtClose              = (NTCLOSE*)GetProcAddress(hNtDll, "NtClose");
        }
        ~NtDllScopedLoader() { if(hNtDll) FreeLibrary(hNtDll); }
    };
    // everything happens during static initialization and destruction
    static const NtDllScopedLoader static_ntdll_loader;
}
// The C++ wrappers below provide an intuitive interface for common Registry Tasks
namespace nt_cpp {
    typedef nt::HANDLE           HANDLE;
    typedef nt::ULONG            ULONG,Tt;
    typedef nt::USHORT           USHORT;
    typedef nt::DWORD            DWORD;
    typedef std::string          string;
    typedef std::wstring         wstring;
    typedef wstring::size_type   St;
    // Udc: Universal Data Class
    class Udc {
        enum {None=0,Str=1,StrEx=2,Bin=3,Dw=4};
        nt::UNICODE_STRING ucstr;
        wstring buf;
        void SyUsSz(St size) { ucstr.Buffer=&buf[0]; ucstr.Length=USHORT(size); ucstr.MaximumLength=USHORT(buf.length()*2); }
        void SyUs(St len) {SyUsSz(len*2);}
        static void Cpy(void*d, const void*s, St sz) { for(St i=0;i<sz;++i) {*((char*)d+i)=*((const char*)s+i);}}
        void unalgn_asgn(const void*s, St sz) { buf.assign((sz+3)/2, L'\0'); Cpy(&buf[0], s, sz); SyUsSz(sz); }
        static wstring hex(char b) {
            static const wchar_t hd[]=L"0123456789abcdef";
            return wstring(1,hd[(b>>4)&15]) + hd[b&15];
        }
    public:
        ULONG type;
        const static St npos = ~St(0);
        St size() const {return ucstr.Length;}
        St length() const {return size()/2;}
        bool empty() const {return !size();}
        void* data() {return ucstr.Buffer;}
        const void* data() const {return ucstr.Buffer;}
        Udc(St reserve=0) : buf(reserve+1,L'\0'),type(reserve?Str:None) { SyUs(0); }
        Udc(const Udc& s) : type(s.type) { unalgn_asgn(s.data(), s.size()); }
        Udc(const wstring& ws) : buf(ws+L'\0'),type(Str) { SyUs(ws.length()); }
        template <St ct> Udc(const wchar_t(&s)[ct]) : buf(s,ct),type(Str) { SyUs(ct - !s[ct-1]); }
        Udc(const void* s, St sz, ULONG tp=Bin) : buf((sz+3)/2, L'\0'),type(tp){SyUsSz(sz); Cpy(data(),s,sz);}
        Udc(const wchar_t*s, St len) : buf(s,len),type(Str) {buf+=L'\0';SyUs(len);}
        Udc(St len, wchar_t wc) : buf(len+1,wc),type(Str) {buf[len]=0; SyUs(len);}
        void pop_back() {if(ucstr.Length>=2) ucstr.Length-=2;}
        wchar_t back() const {return length()?ucstr.Buffer[length()-1]:L'\0';}
        wchar_t* begin() { return ucstr.Buffer; }
        wchar_t* end()   { return begin() + length(); }
        const wchar_t* begin() const { return ucstr.Buffer; }
        const wchar_t* end()   const { return begin() + length(); }
        Udc& operator = (Udc s) {type=s.type; unalgn_asgn(s.data(), s.size()); return *this;}
        operator nt::UNICODE_STRING* () {return &ucstr;}
        operator void* () { return data(); }
        operator wstring () const {
            switch(type) {
                case Str: case StrEx: {
                    wstring ws(length(), L'\0');
                    Cpy(&ws[0], data(), length()*2);
                    return ws;
                }
                case Bin: {
                    const char* p = (const char*)data();
                    wstring ws;
                    for(St i=0; i<size(); ++i) {if(i)ws+=L' ';ws+=hex(p[i]);}
                    return ws;
                }
                case Dw: {
                    if(size()<4) return wstring();
                    const char* p = (const char*)data();
                    return L"0x"+hex(p[3])+hex(p[2])+hex(p[1])+hex(p[0]);
                }
                case None: default: return L"REG_NONE";
            }
        }
        friend std::wostream& operator << (std::wostream& os, const Udc& udc) {return os << wstring(udc);}
        Udc& operator += (const Udc& s) {
            if(s.type == None) return *this;
            if(type == None) return *this = s;
            buf = wstring(*this) + wstring(s) + L'\0';
            type=Str;
            SyUs(buf.length()-1);
            return *this;
        }
        Udc& operator += (wchar_t wc) { return operator += (Udc(1,wc)); }
        friend Udc operator + (Udc a, const Udc &b) { return a += b; }
        friend Udc operator + (wchar_t a, const Udc& b) { Udc r(1,a); return r += b; }
        friend Udc operator + (Udc a, wchar_t b) { return a += b; }
        template <St ct> friend Udc operator + (Udc a, const wchar_t(&s)[ct]) { return a += Udc(s); }
        template <St ct> friend Udc operator + (const wchar_t(&s)[ct], const Udc& b) { Udc a(s); return a += b; }
        DWORD dword() const { return size()==4?*(const DWORD*)data():0; }
        St find(wchar_t wc) const { return wstring(*this).find(wc); }
        St rfind(wchar_t wc) const { return wstring(*this).rfind(wc); }
        Udc substr(St start, St len=npos) const { return wstring(*this).substr(start,len); }
    };
    inline Udc GetEscaped(Udc str) {
        Udc r;
        for(auto ch : str) {
            switch(ch) {
                case L'\\': r += L"\\\\"; break;
                case L'"':  r += L"\\\""; break;
                case L'\n': r += L"\\n"; break;
                case L'\r': r += L"\\r"; break;
                case L'\t': r += L"\\t"; break;
                case 0:     r += L"\\0"; break;
                default:
                    if(ch < L' ') {
                        static const wchar_t hd[] = L"0123456789abcdef";
                        r += L"\\x";
                        r += hd[ch / 16];
                        r += hd[ch % 16];
                    }
                    else { r += ch; }
                    break;
        }   }
        return r;
    }
    inline Udc EscapeData(Udc data) {
        if(data.type == nt::REG_SZ || data.type == nt::REG_EXPAND_SZ) return L'"' + GetEscaped(data) + L'"';
        return wstring(data);
    }
    inline std::pair<Udc, Udc> SplitFullPath(Udc full_path) {
        const auto delim = full_path.rfind(L'\\');
        if(delim == Udc::npos) return std::pair<Udc, Udc>(full_path, Udc());
        return std::pair<Udc, Udc>(full_path.substr(0, delim), full_path.substr(delim + 1));
    }
    static void CloseKey(HANDLE hkey) { nt::NtClose(hkey); }
    static HANDLE OpenKey(Udc key_path, ULONG desired_access) {
        while(key_path.back() == L'\\') key_path.pop_back();
        nt::OBJECT_ATTRIBUTES path;
        path.ObjectName = key_path;
        HANDLE hkey = 0;
        return !nt::NtOpenKey(&hkey, desired_access, &path) ? hkey : 0;
    }
    // CreateKey wraps NtCreateKey.  returns 0 on failure.
    // CreateKey will try to recursively create all missing parent keys in key_path.
    static HANDLE CreateKey(Udc key_path, ULONG desired_access=nt::KEY_QUERY_VALUE|nt::KEY_SET_VALUE|nt::KEY_ENUMERATE_SUB_KEYS, ULONG create_options=nt::REG_OPTION_NON_VOLATILE) {
        using namespace nt;
        while(key_path.back() == L'\\') { key_path.pop_back(); }
        OBJECT_ATTRIBUTES path;
        path.ObjectName = key_path;
        ULONG disposition = 0;
        for(int i=0; i<2; ++i) {
            HANDLE hkey = 0;
            NTSTATUS status = NtCreateKey(&hkey, desired_access, &path, 0, 0, create_options, &disposition);
            if(!status) return hkey;
            if(i || status != STATUS_OBJECT_NAME_NOT_FOUND) return 0;
            auto i_path_up = key_path.rfind(L'\\');
            if(i_path_up == Udc::npos) return 0;
            Udc path_up = key_path.substr(0, i_path_up);
            hkey = CreateKey(path_up, desired_access, create_options);
            if(!hkey) return 0;
            CloseKey(hkey);
        }
        return 0;
    }
    // GetSubkeyNames gets a vector of wstrings containing the names of a key's sub-keys
    std::vector<Udc> static GetSubkeyNames(Udc key_path, bool include_parent=false, ULONG bitness_flag=0) {
        using namespace nt;
        HANDLE hKey = OpenKey(key_path, KEY_ENUMERATE_SUB_KEYS | bitness_flag);
        std::vector<Udc> result;
        if(!hKey) return result;
        for(ULONG index=0;; ++index) {
            KEY_BASIC_INFORMATION<256> ki;
            ULONG result_size = 0;
            NTSTATUS status = NtEnumerateKey(hKey, index, KeyBasicInformation, &ki, sizeof(ki), &result_size);
            if(status) break;
            Udc subkey_name(ki.Name, ki.NameLength/2);
            if(include_parent) { subkey_name = key_path + L'\\' + subkey_name; }
            result.push_back(subkey_name);
        }
        CloseKey(hKey);
        return result;
    }
    // GetValues enumerates a key's values, returning a vector of std::pair<StrArg, ValueData>
    // containing the value's name and data
    std::vector<std::pair<Udc, Udc> > static GetValues(Udc key_path, ULONG bitness_flag=0) {
        using namespace nt;
        HANDLE hKey = OpenKey(key_path, KEY_QUERY_VALUE | bitness_flag);
        std::vector<std::pair<Udc, Udc> > result;
        if(!hKey) return result;
        for(ULONG index=0;; ++index) {
            KEY_VALUE_FULL_INFORMATION<2048> vi;
            ULONG result_size = 0;
            NTSTATUS status = NtEnumerateValueKey(hKey, index, KeyValueFullInformation, &vi, sizeof(vi), &result_size);
            if(status) break;
            Udc value_name(vi.Name, vi.NameLength/2);
            // Value data for registry strings includes the terminating null character,
            result.push_back(std::pair<Udc, Udc>(value_name, Udc(vi.GetData(), vi.DataLength, vi.Type)));
        }
        CloseKey(hKey);
        return result;
    }
    // NT paths for Win32 base keys:
    // HKEY_LOCAL_MACHINE   \Registry\Machine\
    // HKEY_USERS           \Registry\User\
    // HKEY_CURRENT_USER    (use RtlFormatCurrentUserKeyPath)
    // HKEY_CLASSES_ROOT    \Registry\Machine\SOFTWARE\Classes
    // HKEY_CURRENT_CONFIG  \Registry\Machine\SYSTEM\CurrentControlSet\Hardware Profiles\Current
    static Udc GetCurrentUserPath() {
        Udc path(512);
        nt::RtlFormatCurrentUserKeyPath(path);
        return path;
    }
    // This version of QueryValue takes an open key handle and a value name
    static Udc QueryValue(HANDLE hkey, Udc value_name) {
        using namespace nt;
        KEY_VALUE_FULL_INFORMATION<2048> vi; // TODO: allow arbitrary size
        ULONG result_size = 0;
        NTSTATUS status = NtQueryValueKey(hkey, value_name, KeyValueFullInformation, &vi, sizeof(vi), &result_size);
        if(status) return Udc();
        return Udc(vi.GetData(), vi.DataLength, vi.Type);
    }
    // This QueryValue takes a <key name>\<value name> path.  bitness_flag may be supplied to force a view.
    static Udc QueryValue(Udc full_path, ULONG bitness_flag=0) {
        auto key_value_name = SplitFullPath(full_path);
        HANDLE hkey = OpenKey(key_value_name.first, nt::KEY_QUERY_VALUE | bitness_flag);
        if(!hkey) return Udc();
        const Udc value = QueryValue(hkey, key_value_name.second);
        CloseKey(hkey);
        return value;
    }
    // SetValue and DeleteValue (from a path string) try to open their keys with KEY_SET_VALUE
    // access.  This will fail for system-owned paths unless the app is run with
    // Administrator access ("run as Administrator" in the File Explorer context menu).
    // The logged-in user's path, under \Registry\User\ does not need elevation, however.
    static bool SetValue(HANDLE hkey, Udc value_name, Udc data) {
        return !nt::NtSetValueKey(hkey, value_name, 0, data.type, data, data.size());
    }
    static bool SetValue(Udc full_path, Udc data, ULONG bitness_flag=0) {
        using namespace nt;
        auto key_value_name = SplitFullPath(full_path);
        HANDLE hkey = OpenKey(key_value_name.first, nt::KEY_SET_VALUE | bitness_flag);
        if(!hkey) return false;
        bool success = SetValue(hkey, key_value_name.second, data);
        CloseKey(hkey);
        return success;
    }
    static bool DeleteValue(HANDLE hkey, Udc value_name) {
        return !nt::NtDeleteValueKey(hkey, value_name);
    }
    static bool DeleteValue(Udc full_path, ULONG bitness_flag=0) {
        auto key_value_name = SplitFullPath(full_path);
        HANDLE hkey = OpenKey(key_value_name.first, nt::KEY_SET_VALUE | bitness_flag);
        if(!hkey) return false;
        bool success = DeleteValue(hkey, key_value_name.second);
        CloseKey(hkey);
        return success;
    }
    static bool DeleteKey(HANDLE hkey) {
        return !nt::NtDeleteKey(hkey);
    }
    static bool DeleteKey(Udc key_path, ULONG bitness_flag=0) {
        HANDLE hkey = OpenKey(key_path, nt::DELETE | bitness_flag);
        if(!hkey) return false;
        bool success = DeleteKey(hkey);
        CloseKey(hkey);
        return success;
    }
}
namespace {
    // Examples and Native API Parlor Tricks
    void EnumerateRuns() {
        using namespace nt_cpp;
        for(ULONG bitness_flag : { nt::KEY_WOW64_32KEY, nt::KEY_WOW64_64KEY }) {
            // Display the current view
            std::wcout << (bitness_flag == nt::KEY_WOW64_32KEY ? L"\n32 Bit View:\n" : L"\n64 Bit View:\n");
            // Get a list of subkeys under HK_USERS
            auto subkeys = GetSubkeyNames(L"\\Registry\\User", true, bitness_flag);
            // Append the Run path to each user key
            for(auto& keypath : subkeys) {
                keypath += L"\\Software\\Microsoft\\Windows\\CurrentVersion\\Run";
            }
            // add the HKLM Run key path to the list
            subkeys.push_back(L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run");
            // Iterate over all paths in subkeys, get a list of each subkey's values, then print their names
            for(const auto& key_path : subkeys) {
                auto values = GetValues(key_path, bitness_flag);
                // The subkey path is only displayed if it contains values
                if(!values.empty()) {
                    std::wcout << L"    " << key_path << L'\n';
                    for(const auto& value_pair : values) {
                        // Display the "escaped" name and data
                        std::wcout << L"        \"" << GetEscaped(value_pair.first) << L"\" = " << EscapeData(value_pair.second) << L'\n';
    }   }   }   }   }
    const wchar_t demo_key[] = L"\\Software\\NT Registry Demo";
    nt_cpp::Udc HKCU_path() { return nt_cpp::Udc(L"HKEY_CURRENT_USER") + demo_key; }
    nt::HANDLE ReportCreateKey(nt_cpp::Udc key_path,
        nt::ULONG desired_access=nt::KEY_QUERY_VALUE|nt::KEY_SET_VALUE|nt::KEY_ENUMERATE_SUB_KEYS)
    {
        using namespace nt_cpp;
        HANDLE hkey = CreateKey(key_path, desired_access);
        if(!hkey) std::wcout << L"CreateKey failed for \"" << GetEscaped(key_path) << L"\"\n";
        return hkey;
    }
    void ReportSetValue(nt::HANDLE hkey, nt_cpp::Udc value_name, const nt_cpp::Udc& data) {
        using namespace nt_cpp;
        std::wcout << L"    Value \"" << GetEscaped(value_name) << '"';
        if(SetValue(hkey, value_name, data)) { std::wcout << L" Successfully Set\n"; }
        else                                 { std::wcout << L" Set Failed\n"; }
    }
    void ReportDeleteValue(nt::HANDLE hkey, nt_cpp::Udc value_name) {
        using namespace nt_cpp;
        std::wcout << L"    Value \"" << GetEscaped(value_name) << '"';
        if(DeleteValue(hkey, value_name)) { std::wcout << L" Successfully Deleted\n"; }
        else                              { std::wcout << L" Delete Failed\n"; }
    }
    void ReportDeleteKey(nt::HANDLE hkey, nt_cpp::Udc key_name) {
        using namespace nt_cpp;
        std::wcout << L"    Subkey \"" << GetEscaped(key_name) << '"';
        if(DeleteKey(hkey))  { std::wcout << L" Successfully Deleted\n"; }
        else                 { std::wcout << L" Delete Failed\n"; }
    }
    void ReportVerifyData(nt_cpp::Udc value_path) {
        using namespace nt_cpp;
        auto i = value_path.rfind(L'\\');
        Udc value_name = i == Udc::npos ? value_path : value_path.substr(i+1);
        Udc rb = QueryValue(value_path);
        std::wcout << L"    Value \"" << GetEscaped(value_name) << L"\" data readback: " << EscapeData(rb) << L'\n';
    }
    void NullInValueData() {
        using namespace nt_cpp;
        Udc nt_key_path    = GetCurrentUserPath() + demo_key;
        Udc value_name     = L"NullInData";
        Udc nt_value_path  = nt_key_path + L'\\' + value_name;
        HANDLE hkey = ReportCreateKey(nt_key_path);
        if(!hkey) return;
        auto data = QueryValue(hkey, value_name);
        if(data.type == nt::REG_NONE) {
            // Win32 automatically places a null at the end of string values,
            // but with the Native API, the trailing null can be omitted.
            // So here, string values must explicitly include a trailing null
            // if one is desired.
            data = L"This string has a null here ->\0<- there!\0";
            std::wcout <<
            L"    In \"" << HKCU_path() << L"\"\n"
            L"    creating a string value named \"" << value_name << L"\" with a null in its data:\n"
            L"        " << EscapeData(data) << L"\n"
            L"    o RegEdit displays just the string before the null, but you\n"
            L"      can use \"Modify Binary Data\" to see the whole thing.\n"
            L"    o Repeat this command to remove the value.\n\n";
            ReportSetValue(hkey, value_name, data);
        } else {
            std::wcout << L"    Cleaning Up:\n";
            ReportDeleteValue(hkey, value_name);
        }
        CloseKey(hkey);
        ReportVerifyData(nt_value_path);
    }
    void NullInValueName() {
        using namespace nt_cpp;
        Udc nt_key_path    = GetCurrentUserPath() + demo_key;
        Udc value_name     = L"Null\0InName";
        Udc nt_value_path  = nt_key_path + L'\\' + value_name;
        HANDLE hkey = ReportCreateKey(nt_key_path);
        if(!hkey) return;
        auto data = QueryValue(hkey, value_name);
        if(data.type == nt::REG_NONE) {
            std::wcout <<
            L"    In \"" << HKCU_path() << L"\"\n"
            L"    creating a string value whose name contains an embedded null.\n"
            L"    o Causes RegEdit to glitch because it can only see part\n"
            L"      of the value's name and is unable to find its data\n"
            L"    o Repeat this command to remove the value.\n\n";
            data = L"A Normal String Value\0";
            ReportSetValue(hkey, value_name, data);
        } else {
            std::wcout << L"    Cleaning Up:\n";
            ReportDeleteValue(hkey, value_name);
        }
        CloseKey(hkey);
        ReportVerifyData(nt_value_path);
    }
    void NullInKeyName() {
        using namespace nt_cpp;
        Udc nt_key_path     = GetCurrentUserPath() + demo_key;
        Udc subkey_name     = L"Null\0InKeyName";
        Udc value_name      = L"NormalValueName";
        Udc nt_subkey_path  = nt_key_path + L'\\' + subkey_name;
        Udc nt_value_path   = nt_subkey_path + L'\\' + value_name;
        HANDLE hkey = ReportCreateKey(nt_subkey_path, nt::KEY_QUERY_VALUE|nt::KEY_SET_VALUE|nt::DELETE);
        if(!hkey) return;
        auto data = QueryValue(hkey, value_name);
        if(data.type == nt::REG_NONE) {
            std::wcout << 
            L"    In \"" << HKCU_path() << L"\"\n"
            L"    created subkey \"" << GetEscaped(subkey_name) << L"\"\n"
            L"    whose name contains an embedded null.\n"
            L"    o RegEdit will report an error when this key is selected\n"
            L"    o Repeat this command to remove the key.\n\n";
            data = L"A normal string value, hidden in a key which Win32 cannot access\0";
            ReportSetValue(hkey, value_name, data);
        } else {
            std::wcout << L"    Cleaning Up:\n";
            ReportDeleteValue(hkey, value_name);
            ReportDeleteKey(hkey, subkey_name);
        }
        CloseKey(hkey);
        ReportVerifyData(nt_value_path);
    }
    void TwoValuesOneName() {
        using namespace nt_cpp;
        Udc nt_key_path    = GetCurrentUserPath() + demo_key;
        Udc normal_name    = L"ValueName";
        Udc abnormal_name  = L"ValueName\0OhYeah";
        Udc normal_path    = nt_key_path + L'\\' + normal_name;
        Udc abnormal_path  = nt_key_path + L'\\' + abnormal_name;
        HANDLE hkey = ReportCreateKey(nt_key_path);
        if(!hkey) return;
        Udc normal_data = QueryValue(hkey, normal_name);
        if(normal_data.type == nt::REG_NONE) {
            std::wcout << 
            L"    In \"" << HKCU_path() << L"\"\n"
            L"    creating two values with different names,\n"
            L"    but because of an embedded null in one of the names,\n"
            L"    both appear identical to Win32\n"
            L"    o RegEdit does not report an error, but it sees two values with the same name.\n"
            L"      And both values appear to share the same string data.\n"
            L"    o Repeat this command to remove the values.\n";
            normal_data       = L"Normal Name's Data\0";
            Udc abnormal_data = L"AbNorMal nAme'S daTA\0";
            ReportSetValue(hkey, normal_name, normal_data);
            ReportSetValue(hkey, abnormal_name, abnormal_data);
        } else {
            std::wcout << L"    Cleaning Up:\n";
            ReportDeleteValue(hkey, normal_name);
            ReportDeleteValue(hkey, abnormal_name);
        }
        CloseKey(hkey);
        ReportVerifyData(normal_path);
        ReportVerifyData(abnormal_path);
    }
    std::vector<nt_cpp::Udc> RecursiveEmbeddedNullSearch(nt_cpp::Udc key_path) {
        using namespace nt_cpp;
        while(key_path.back() == L'\\') { key_path.pop_back(); }
        std::vector<Udc> results;
        auto values = GetValues(key_path);
        for(const auto& nv : values) {
            if(nv.first.find(L'\0') != Udc::npos) {
                results.push_back(key_path + L'\\' + nv.first);
        }   }
        auto subkeys = GetSubkeyNames(key_path, false);
        for(const auto& kn : subkeys) {
            if(kn.find(L'\0') != Udc::npos) {
                results.push_back(key_path + L'\\' + kn + L'\\');
        }   }
        for(const auto& kn : subkeys) {
            auto sub_results = RecursiveEmbeddedNullSearch(key_path + L'\\' + kn);
            if(!sub_results.empty()) {
                results.insert(results.end(), sub_results.begin(), sub_results.end());
        }   }
        return results;
    }
    bool IsAffirmative(std::wstring answer) {
        for(auto wc : answer) {
            if(wc>=L'A'||wc<=L'Z'||wc>=L'a'||wc<=L'z'||wc==L'0'||wc==L'1') {
                return wc==L'y'||wc==L'Y'||wc==L'1';
        }   }
        return false;
    }
    void FixMistakes() {
        using namespace nt_cpp;
        using std::wcout;
        Udc root_key = GetCurrentUserPath();
        auto paths = RecursiveEmbeddedNullSearch(root_key);
        wcout << L'\n';
        for(const auto& path : paths) {
            if(path.back() == L'\\') {
                wcout << L"Found Key: \"" << GetEscaped(path) << L"\"\nDelete It?\n";
                std::wstring answer;
                std::getline(std::wcin, answer);
                if(IsAffirmative(answer)) {
                    HANDLE hkey = OpenKey(path, nt::DELETE);
                    if(hkey) {
                        if(DeleteKey(hkey)) { wcout << L"Successfully deleted " << GetEscaped(path) << L"\n"; }
                        else                { wcout << L"Failed to delete " << GetEscaped(path) << L"\n"; }
                        CloseKey(hkey);
                    }
                    else { wcout << L"Failed to open " << GetEscaped(path) << L'\n'; }
            }   }
            else {
                wcout << 
                    L"Found Value: \"" << GetEscaped(path) << L"\"\n"
                    L"Containing:  " << EscapeData(QueryValue(path)) << L"\n"
                    L"Delete It?\n";
                std::wstring answer;
                std::getline(std::wcin, answer);
                if(IsAffirmative(answer)) {
                    auto kv = SplitFullPath(path);
                    HANDLE hkey = OpenKey(kv.first, nt::KEY_SET_VALUE);
                    if(hkey) {
                        if(DeleteValue(hkey, kv.second)) wcout << L"Successfully deleted";
                        else wcout << L"Failed to delete";
                        wcout << L" value \"" << GetEscaped(kv.second) << L"\"\n";
                    }
                    else { wcout << L"Failed to open key \"" << kv.first << L"\"\n"; }
    }   }   }   }
    struct { const char *id, *desc; void(*handler)(); } const demos[] ={
        {"runs", "Displays the contents of Windows' \"Run\" keys", EnumerateRuns},
        {"nullinstring", "Creates a null-embedded string value in current user's Registry", NullInValueData},
        {"nullinvalname", "Creates a string with a null-embedded value name in current user's Registry", NullInValueName},
        {"nullinkeyname", "Creates a sub-key with a null-embedded name in current user's Registry", NullInKeyName},
        {"samename", "Creates two values in current user's Registry whose names cannot be distingushed by Win32", TwoValuesOneName},
        {"fix", "Performs recursive search for names with nulls and gives option to delete", FixMistakes}
    };
    void Usage(const char* exe) {
        using std::cout;
        for(const char* p=exe; *p; ++p) { if(*p == '\\') exe = p+1; }
        cout << "\n    Usage:   " << exe << " <command>\n\n";
        cout << "    Available Commands:\n";
        for(int i=0; i<sizeof(demos)/sizeof(demos[0]); ++i) {
            cout << "             " << demos[i].id;
            int len;
            for(len=0; demos[i].id[len]; ++len) {}
            for(; len < 16; ++len) { cout << ' '; }
            cout << demos[i].desc << '\n';
    }   }
    void ExecDemo(std::string id) {
        for(int i=0; i<sizeof(demos)/sizeof(demos[0]); ++i) {
            if(id == demos[i].id) {
                demos[i].handler();
                return;
        }   }
        std::cout << "Command ID not recognized: " << id << '\n';
    }
}
int main(int argc, const char* argv[]) {
    if(argc < 2)              { Usage(argv[0]); }
    for(int i=1; i<argc; ++i) { ExecDemo(argv[i]); }
}