Offer: More flexible and consistent WindowsRegistry

Hi Jules,

below you'll find a slightly modified version of the WindowsRegistry class.

The current version of the WindowsRegistry class doesn't offer the following features:

  • write/delete/read the registry values in the 32 bit registry store from a 64 bit application.
  • write/delete the registry values in the 64 bit registry store from a 32 bit application.

​I modified the class to enable the user to decide which registry store he wants to access or if the OS should decide which store will be accessed.

These features are very helpful if you have plugins as 32 bit and 64 bit versions installed on the same system and don't want to manage the registry information twice. Without the modifications the System would access the 32 bit registry store from 32 bit plugin version and 64 bit registry store from the 64 bit plugin version.

Some background info:

  • KEY_WOW64_64KEY on a 64-bit OS means that the process, no matter if it's a 32 or 64 bit process, will access the 64 bit registry view.
  • KEY_WOW64_32KEY on a 64-bit OS means that the process, no matter if it's a 32 or 64 bit process, will access the 32 bit registry view.
  • Neither of them have any effect on a 32-bit OS.
  • Leaving the flag out (the default) on a 64-bit OS will send registry accesses from 32-bit processes to the 32 bit registry view, and accesses from 64-bit processes to the 64 bit registry view.

​Here is the modified WindowsRegistry class. It would be nice if you could integrate that into the official JUCE.

.h file:



/*
  ==============================================================================
   This file is part of the JUCE library - "Jules' Utility Class Extensions"
   Copyright 2004-11 by Raw Material Software Ltd.
  ------------------------------------------------------------------------------
   JUCE can be redistributed and/or modified under the terms of the GNU General
   Public License (Version 2), as published by the Free Software Foundation.
   A copy of the license is included in the JUCE distribution, or can be found
   online at www.gnu.org/licenses.
   JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
  ------------------------------------------------------------------------------
   To release a closed-source product which uses JUCE, commercial licenses are
   available: visit www.rawmaterialsoftware.com/juce for more information.
  ==============================================================================
*/
#ifndef __JUCE_WINDOWSREGISTRY_JUCEHEADER__
#define __JUCE_WINDOWSREGISTRY_JUCEHEADER__
#if JUCE_WINDOWS || DOXYGEN
/**
    Contains some static helper functions for manipulating the MS Windows registry
    (Only available on Windows, of course!)
*/
class WindowsRegistry
{
public:
    /** These optinal flags can be used to explicitly address the 32 bit or 64 bit registry store. 
        The flags have no effect on 32 bit Operating Systems.*/
    enum WowFlags
    {
        /**    Default handling: 32 bit apps address 32 registry store 
               and 64 bit apps address 64 bit registry store */
        WowDefault = 0,
        /**    Always address the 64 bit registry store. (KEY_WOW64_64KEY)*/
        Wow64_64 = 0x0100,
        /**    Always address the 32 bit registry store. (KEY_WOW64_32KEY)*/
        Wow64_32 = 0x0200
    };
    //==============================================================================
    /** Returns a string from the registry.
        The path is a string for the entire path of a value in the registry,
        e.g. "HKEY_CURRENT_USER\Software\foo\bar"
    */
    static String getValue (const String& regValuePath,
                            const String& defaultValue = String::empty,
                            WowFlags flags = WowDefault);
    /** DEPRECATED: use getValue() and the WowFlags instead. */
    static String getValueWow64 (const String& regValuePath,
                                 const String& defaultValue = String::empty);
    /** Reads a binary block from the registry.
        The path is a string for the entire path of a value in the registry,
        e.g. "HKEY_CURRENT_USER\Software\foo\bar"
        @returns a DWORD indicating the type of the key.
    */
    static uint32 getBinaryValue (const String& regValuePath, MemoryBlock& resultData, WowFlags flags = WowDefault);
    /** Sets a registry value as a string.
        This will take care of creating any groups needed to get to the given registry value.
    */
    static bool setValue (const String& regValuePath, const String& value, WowFlags flags = WowDefault);
    /** Sets a registry value as a DWORD.
        This will take care of creating any groups needed to get to the given registry value.
    */
    static bool setValue (const String& regValuePath, uint32 value, WowFlags flags = WowDefault);
    /** Sets a registry value as a QWORD.
        This will take care of creating any groups needed to get to the given registry value.
    */
    static bool setValue (const String& regValuePath, uint64 value, WowFlags flags = WowDefault);
    /** Sets a registry value as a binary block.
        This will take care of creating any groups needed to get to the given registry value.
    */
    static bool setValue (const String& regValuePath, const MemoryBlock& value, WowFlags flags = WowDefault);
    /** Returns true if the given value exists in the registry. */
    static bool valueExists (const String& regValuePath, WowFlags flags = WowDefault);
     /** DEPRECATED: use getValue() and the WowFlags instead. */
    static bool valueExistsWow64 (const String& regValuePath);
    /** Deletes a registry value. */
    static void deleteValue (const String& regValuePath, WowFlags flags = WowDefault);
    /** Deletes a registry key (which is registry-talk for 'folder'). */
    static void deleteKey (const String& regKeyPath, WowFlags flags = WowDefault);
    /** Creates a file association in the registry.
        This lets you set the executable that should be launched by a given file extension.
        @param fileExtension        the file extension to associate, including the
                                    initial dot, e.g. ".txt"
        @param symbolicDescription  a space-free short token to identify the file type
        @param fullDescription      a human-readable description of the file type
        @param targetExecutable     the executable that should be launched
        @param iconResourceNumber   the icon that gets displayed for the file type will be
                                    found by looking up this resource number in the
                                    executable. Pass 0 here to not use an icon
        @param registerForCurrentUserOnly   if false, this will try to register the association
                                    for all users (you might not have permission to do this
                                    unless running in an installer). If true, it will register the
                                    association in HKEY_CURRENT_USER.
    */
    static bool registerFileAssociation (const String& fileExtension,
                                         const String& symbolicDescription,
                                         const String& fullDescription,
                                         const File& targetExecutable,
                                         int iconResourceNumber,
                                         bool registerForCurrentUserOnly, 
                                         WowFlags flags = WowDefault);

private:
    WindowsRegistry();
    JUCE_DECLARE_NON_COPYABLE (WindowsRegistry)
};
#endif
#endif   // __JUCE_WINDOWSREGISTRY_JUCEHEADER__



.cpp file:


/*
  ==============================================================================
   This file is part of the JUCE library - "Jules' Utility Class Extensions"
   Copyright 2004-11 by Raw Material Software Ltd.
  ------------------------------------------------------------------------------
   JUCE can be redistributed and/or modified under the terms of the GNU General
   Public License (Version 2), as published by the Free Software Foundation.
   A copy of the license is included in the JUCE distribution, or can be found
   online at www.gnu.org/licenses.
   JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
  ------------------------------------------------------------------------------
   To release a closed-source product which uses JUCE, commercial licenses are
   available: visit www.rawmaterialsoftware.com/juce for more information.
  ==============================================================================
*/
struct RegistryKeyWrapper
{
    RegistryKeyWrapper (String name, const bool createForWriting, const DWORD wow64Flags)
        : key (0), wideCharValueName (nullptr)
    {
        HKEY rootKey = 0;
        if (name.startsWithIgnoreCase ("HKEY_CURRENT_USER\\"))        rootKey = HKEY_CURRENT_USER;
        else if (name.startsWithIgnoreCase ("HKEY_LOCAL_MACHINE\\"))  rootKey = HKEY_LOCAL_MACHINE;
        else if (name.startsWithIgnoreCase ("HKEY_CLASSES_ROOT\\"))   rootKey = HKEY_CLASSES_ROOT;
        if (rootKey != 0)
        {
            name = name.substring (name.indexOfChar ('\\') + 1);
            const int lastSlash = name.lastIndexOfChar ('\\');
            valueName = name.substring (lastSlash + 1);
            wideCharValueName = valueName.toWideCharPointer();
            name = name.substring (0, lastSlash);
            const wchar_t* const wideCharName = name.toWideCharPointer();
            DWORD result;
            if (createForWriting)
                RegCreateKeyEx (rootKey, wideCharName, 0, 0, REG_OPTION_NON_VOLATILE,
                                KEY_WRITE | KEY_QUERY_VALUE | wow64Flags, 0, &key, &result);
            else
                RegOpenKeyEx (rootKey, wideCharName, 0, KEY_READ | wow64Flags, &key);
        }
    }
    ~RegistryKeyWrapper()
    {
        if (key != 0)
            RegCloseKey (key);
    }
    static bool setValue (const String& regValuePath, const DWORD type,
                          const void* data, size_t dataSize, const DWORD wow64Flags)
    {
        const RegistryKeyWrapper key (regValuePath, true, wow64Flags);
        return key.key != 0
                && RegSetValueEx (key.key, key.wideCharValueName, 0, type,
                                  reinterpret_cast <const BYTE*> (data),
                                  (DWORD) dataSize) == ERROR_SUCCESS;
    }
    static uint32 getBinaryValue (const String& regValuePath, MemoryBlock& result, DWORD wow64Flags)
    {
        const RegistryKeyWrapper key (regValuePath, false, wow64Flags);
        if (key.key != 0)
        {
            for (unsigned long bufferSize = 1024; ; bufferSize *= 2)
            {
                result.setSize (bufferSize, false);
                DWORD type = REG_NONE;
                const LONG err = RegQueryValueEx (key.key, key.wideCharValueName, 0, &type,
                                                  (LPBYTE) result.getData(), &bufferSize);
                if (err == ERROR_SUCCESS)
                {
                    result.setSize (bufferSize, false);
                    return type;
                }
                if (err != ERROR_MORE_DATA)
                    break;
            }
        }
        return REG_NONE;
    }
    static String getValue (const String& regValuePath, const String& defaultValue, DWORD wow64Flags)
    {
        MemoryBlock buffer;
        switch (getBinaryValue (regValuePath, buffer, wow64Flags))
        {
            case REG_SZ:    return static_cast <const WCHAR*> (buffer.getData());
            case REG_DWORD: return String ((int) *reinterpret_cast<const DWORD*> (buffer.getData()));
            default:        break;
        }
        return defaultValue;
    }
    static bool valueExists (const String& regValuePath, const DWORD wow64Flags)
    {
        const RegistryKeyWrapper key (regValuePath, false, wow64Flags);
        if (key.key == 0)
            return false;
        unsigned char buffer [512];
        unsigned long bufferSize = sizeof (buffer);
        DWORD type = 0;
        const LONG result = RegQueryValueEx (key.key, key.wideCharValueName,
                                             0, &type, buffer, &bufferSize);
        return result == ERROR_SUCCESS || result == ERROR_MORE_DATA;
    }
    HKEY key;
    const wchar_t* wideCharValueName;
    String valueName;
    JUCE_DECLARE_NON_COPYABLE (RegistryKeyWrapper)
};
uint32 WindowsRegistry::getBinaryValue (const String& regValuePath, MemoryBlock& result, WowFlags flags)
{
    return RegistryKeyWrapper::getBinaryValue (regValuePath, result, flags);
}
String WindowsRegistry::getValue (const String& regValuePath, const String& defaultValue, WowFlags flags)
{
    return RegistryKeyWrapper::getValue (regValuePath, defaultValue, flags);
}
String WindowsRegistry::getValueWow64 (const String& regValuePath, const String& defaultValue)
{
    return RegistryKeyWrapper::getValue (regValuePath, defaultValue, 0x100 /*KEY_WOW64_64KEY*/);
}
bool WindowsRegistry::valueExistsWow64 (const String& regValuePath)
{
    return RegistryKeyWrapper::valueExists (regValuePath, 0x100 /*KEY_WOW64_64KEY*/);
}
bool WindowsRegistry::setValue (const String& regValuePath, const String& value, WowFlags flags)
{
    return RegistryKeyWrapper::setValue (regValuePath, REG_SZ, value.toWideCharPointer(),
                                         CharPointer_UTF16::getBytesRequiredFor (value.getCharPointer()), flags);
}
bool WindowsRegistry::setValue (const String& regValuePath, const uint32 value, WowFlags flags)
{
    return RegistryKeyWrapper::setValue (regValuePath, REG_DWORD, &value, sizeof (value), flags);
}
bool WindowsRegistry::setValue (const String& regValuePath, const uint64 value, WowFlags flags)
{
    return RegistryKeyWrapper::setValue (regValuePath, REG_QWORD, &value, sizeof (value), flags);
}
bool WindowsRegistry::setValue (const String& regValuePath, const MemoryBlock& value, WowFlags flags)
{
    return RegistryKeyWrapper::setValue (regValuePath, REG_BINARY, value.getData(), value.getSize(), flags);
}
bool WindowsRegistry::valueExists (const String& regValuePath, WowFlags flags)
{
    return RegistryKeyWrapper::valueExists (regValuePath, flags);
}
void WindowsRegistry::deleteValue (const String& regValuePath, WowFlags flags)
{
    const RegistryKeyWrapper key (regValuePath, true, flags);
    if (key.key != 0)
        RegDeleteValue (key.key, key.wideCharValueName);
}
void WindowsRegistry::deleteKey (const String& regKeyPath, WowFlags flags)
{
    const RegistryKeyWrapper key (regKeyPath, true, flags);
    if (key.key != 0)
        RegDeleteKey (key.key, key.wideCharValueName);
}
bool WindowsRegistry::registerFileAssociation (const String& fileExtension,
                                               const String& symbolicDescription,
                                               const String& fullDescription,
                                               const File& targetExecutable,
                                               const int iconResourceNumber,
                                               const bool registerForCurrentUserOnly,
                                               WowFlags flags)
{
    const char* const root = registerForCurrentUserOnly ? "HKEY_CURRENT_USER\\Software\\Classes\\"
                                                        : "HKEY_CLASSES_ROOT\\";
    const String key (root + symbolicDescription);
    return setValue (root + fileExtension + "\\", symbolicDescription, flags)
        && setValue (key + "\\", fullDescription, flags)
        && setValue (key + "\\shell\\open\\command\\", targetExecutable.getFullPathName() + " \"%1\"", flags)
        && (iconResourceNumber == 0
              || setValue (key + "\\DefaultIcon\\",
                           targetExecutable.getFullPathName() + "," + String (-iconResourceNumber)));
}

Cheers,

Jan

A very good idea, thanks!