blob: 398d8f45b8f1eb201b934691c34fb12e58727b4d [file] [log] [blame] [edit]
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2014 Руслан Ижбулатов <lrn1986@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include "ginitable.h"
#include "gwin32registrykey.h"
#include <gio/gioerror.h>
#ifdef _MSC_VER
#pragma warning ( disable:4005 )
#endif
#include <windows.h>
#include <ntstatus.h>
#include <winternl.h>
#ifndef _WDMDDK_
typedef enum _KEY_INFORMATION_CLASS {
KeyBasicInformation,
KeyNodeInformation,
KeyFullInformation,
KeyNameInformation,
KeyCachedInformation,
KeyFlagsInformation,
KeyVirtualizationInformation,
KeyHandleTagsInformation,
MaxKeyInfoClass
} KEY_INFORMATION_CLASS;
typedef struct _KEY_BASIC_INFORMATION {
LARGE_INTEGER LastWriteTime;
ULONG TitleIndex;
ULONG NameLength;
WCHAR Name[1];
} KEY_BASIC_INFORMATION, *PKEY_BASIC_INFORMATION;
#endif
#if !defined (__OBJECT_ATTRIBUTES_DEFINED) && defined (__MINGW32_)
#define __OBJECT_ATTRIBUTES_DEFINED
typedef struct _OBJECT_ATTRIBUTES {
ULONG Length;
#ifdef _WIN64
ULONG pad1;
#endif
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
#ifdef _WIN64
ULONG pad2;
#endif
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
#endif
#ifndef HKEY_CURRENT_USER_LOCAL_SETTINGS
#define HKEY_CURRENT_USER_LOCAL_SETTINGS ((HKEY) (ULONG_PTR)((LONG)0x80000007))
#endif
#if !defined (__UNICODE_STRING_DEFINED) && defined (__MINGW32_)
#define __UNICODE_STRING_DEFINED
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
#endif
typedef const UNICODE_STRING* PCUNICODE_STRING;
typedef NTSTATUS
(NTAPI * NtQueryKeyFunc)(HANDLE key_handle,
KEY_INFORMATION_CLASS key_info_class,
PVOID key_info_buffer,
ULONG key_info_buffer_size,
PULONG result_size);
typedef NTSTATUS
(NTAPI * NtNotifyChangeMultipleKeysFunc)(HANDLE key_handle,
ULONG subkey_count,
POBJECT_ATTRIBUTES subkeys,
HANDLE event,
PIO_APC_ROUTINE apc_routine,
PVOID apc_closure,
PIO_STATUS_BLOCK status_block,
ULONG filter,
BOOLEAN watch_tree,
PVOID buffer,
ULONG buffer_size,
BOOLEAN async);
static NtQueryKeyFunc nt_query_key = NULL;
static NtNotifyChangeMultipleKeysFunc nt_notify_change_multiple_keys = NULL;
#define G_WIN32_KEY_UNWATCHED 0
#define G_WIN32_KEY_WATCHED 1
#define G_WIN32_KEY_UNCHANGED 0
#define G_WIN32_KEY_CHANGED 1
#define G_WIN32_KEY_UNKNOWN -1
enum
{
PROP_0,
PROP_PATH,
PROP_PATH_UTF16,
PROP_MAX,
};
typedef enum
{
G_WIN32_REGISTRY_UPDATED_NOTHING = 0,
G_WIN32_REGISTRY_UPDATED_PATH = 1,
} GWin32RegistryKeyUpdateFlag;
static gsize
g_utf16_len (const gunichar2 *str)
{
gsize result;
for (result = 0; str[0] != 0; str++, result++)
;
return result;
}
static gunichar2 *
g_wcsdup (const gunichar2 *str, gssize str_len)
{
gsize str_len_unsigned;
gsize str_size;
g_return_val_if_fail (str != NULL, NULL);
if (str_len < 0)
str_len_unsigned = g_utf16_len (str);
else
str_len_unsigned = (gsize) str_len;
g_assert (str_len_unsigned <= G_MAXSIZE / sizeof (gunichar2) - 1);
str_size = (str_len_unsigned + 1) * sizeof (gunichar2);
return g_memdup2 (str, str_size);
}
/**
* g_win32_registry_subkey_iter_copy:
* @iter: an iterator
*
* Creates a dynamically-allocated copy of an iterator. Dynamically-allocated
* state of the iterator is duplicated too.
*
* Returns: (transfer full): a copy of the @iter,
* free with g_win32_registry_subkey_iter_free ()
*
* Since: 2.46
**/
GWin32RegistrySubkeyIter *
g_win32_registry_subkey_iter_copy (const GWin32RegistrySubkeyIter *iter)
{
GWin32RegistrySubkeyIter *new_iter;
g_return_val_if_fail (iter != NULL, NULL);
new_iter = g_new0 (GWin32RegistrySubkeyIter, 1);
new_iter->key = g_object_ref (iter->key);
new_iter->counter = iter->counter;
new_iter->subkey_count = iter->subkey_count;
new_iter->subkey_name = g_wcsdup (iter->subkey_name, iter->subkey_name_size);
new_iter->subkey_name_size = iter->subkey_name_size;
if (iter->subkey_name_u8)
new_iter->subkey_name_u8 = iter->subkey_name_u8;
else
new_iter->subkey_name_u8 = NULL;
return new_iter;
}
/**
* g_win32_registry_subkey_iter_free:
* @iter: a dynamically-allocated iterator
*
* Free an iterator allocated on the heap. For iterators that are allocated
* on the stack use g_win32_registry_subkey_iter_clear () instead.
*
* Since: 2.46
**/
void
g_win32_registry_subkey_iter_free (GWin32RegistrySubkeyIter *iter)
{
g_return_if_fail (iter != NULL);
g_object_unref (iter->key);
g_free (iter->subkey_name);
g_free (iter->subkey_name_u8);
g_free (iter);
}
/**
* g_win32_registry_subkey_iter_assign:
* @iter: a #GWin32RegistrySubkeyIter
* @other: another #GWin32RegistrySubkeyIter
*
* Assigns the value of @other to @iter. This function
* is not useful in applications, because iterators can be assigned
* with `GWin32RegistrySubkeyIter i = j;`. The
* function is used by language bindings.
*
* Since: 2.46
**/
void
g_win32_registry_subkey_iter_assign (GWin32RegistrySubkeyIter *iter,
const GWin32RegistrySubkeyIter *other)
{
g_return_if_fail (iter != NULL);
g_return_if_fail (other != NULL);
*iter = *other;
}
G_DEFINE_BOXED_TYPE (GWin32RegistrySubkeyIter, g_win32_registry_subkey_iter,
g_win32_registry_subkey_iter_copy,
g_win32_registry_subkey_iter_free)
/**
* g_win32_registry_value_iter_copy:
* @iter: an iterator
*
* Creates a dynamically-allocated copy of an iterator. Dynamically-allocated
* state of the iterator is duplicated too.
*
* Returns: (transfer full): a copy of the @iter,
* free with g_win32_registry_value_iter_free ().
*
* Since: 2.46
**/
GWin32RegistryValueIter *
g_win32_registry_value_iter_copy (const GWin32RegistryValueIter *iter)
{
GWin32RegistryValueIter *new_iter;
g_return_val_if_fail (iter != NULL, NULL);
new_iter = g_new0 (GWin32RegistryValueIter, 1);
new_iter->key = g_object_ref (iter->key);
new_iter->counter = iter->counter;
new_iter->value_count = iter->value_count;
new_iter->value_name = g_wcsdup (iter->value_name, iter->value_name_size);
new_iter->value_name_size = iter->value_name_size;
if (iter->value_data != NULL)
new_iter->value_data = g_memdup2 (iter->value_data, iter->value_data_size);
new_iter->value_data_size = iter->value_data_size;
if (iter->value_name_u8 != NULL)
new_iter->value_name_u8 = g_strdup (iter->value_name_u8);
new_iter->value_name_u8_len = iter->value_name_u8_len;
if (iter->value_data_u8 != NULL)
new_iter->value_data_u8 = g_strdup (iter->value_data_u8);
new_iter->value_data_u8_size = iter->value_data_u8_size;
if (iter->value_data_expanded != NULL)
new_iter->value_data_expanded = g_wcsdup ((gunichar2 *) iter->value_data_expanded,
iter->value_data_expanded_charsize * sizeof (gunichar2));
new_iter->value_data_expanded_charsize = iter->value_data_expanded_charsize;
if (iter->value_data_expanded_u8 != NULL)
new_iter->value_data_expanded_u8 = g_memdup2 (iter->value_data_expanded_u8,
iter->value_data_expanded_charsize);
new_iter->value_data_expanded_u8_size = iter->value_data_expanded_charsize;
return new_iter;
}
/**
* g_win32_registry_value_iter_free:
* @iter: a dynamically-allocated iterator
*
* Free an iterator allocated on the heap. For iterators that are allocated
* on the stack use g_win32_registry_value_iter_clear () instead.
*
* Since: 2.46
**/
void
g_win32_registry_value_iter_free (GWin32RegistryValueIter *iter)
{
g_return_if_fail (iter != NULL);
g_object_unref (iter->key);
g_free (iter->value_name);
g_free (iter->value_data);
g_free (iter->value_data_expanded);
g_free (iter->value_name_u8);
g_free (iter->value_data_u8);
g_free (iter->value_data_expanded_u8);
g_free (iter);
}
/**
* g_win32_registry_value_iter_assign:
* @iter: a #GWin32RegistryValueIter
* @other: another #GWin32RegistryValueIter
*
* Assigns the value of @other to @iter. This function
* is not useful in applications, because iterators can be assigned
* with `GWin32RegistryValueIter i = j;`. The
* function is used by language bindings.
*
* Since: 2.46
**/
void
g_win32_registry_value_iter_assign (GWin32RegistryValueIter *iter,
const GWin32RegistryValueIter *other)
{
g_return_if_fail (iter != NULL);
g_return_if_fail (other != NULL);
*iter = *other;
}
G_DEFINE_BOXED_TYPE (GWin32RegistryValueIter, g_win32_registry_value_iter,
g_win32_registry_value_iter_copy,
g_win32_registry_value_iter_free)
/**
* SECTION:gwin32registrykey
* @title: GWin32RegistryKey
* @short_description: W32 registry access helper
* @include: gio/win32/gwin32registrykey.h
*
* #GWin32RegistryKey represents a single Windows Registry key.
*
* #GWin32RegistryKey is used by a number of helper functions that read
* Windows Registry. All keys are opened with read-only access, and at
* the moment there is no API for writing into registry keys or creating
* new ones.
*
* #GWin32RegistryKey implements the #GInitable interface, so if it is manually
* constructed by e.g. g_object_new() you must call g_initable_init() and check
* the results before using the object. This is done automatically
* in g_win32_registry_key_new() and g_win32_registry_key_get_child(), so these
* functions can return %NULL.
*
* To increase efficiency, a UTF-16 variant is available for all functions
* that deal with key or value names in the registry. Use these to perform
* deep registry queries or other operations that require querying a name
* of a key or a value and then opening it (or querying its data). The use
* of UTF-16 functions avoids the overhead of converting names to UTF-8 and
* back.
*
* All functions operate in current user's context (it is not possible to
* access registry tree of a different user).
*
* Key paths must use '\\' as a separator, '/' is not supported. Key names
* must not include '\\', because it's used as a separator. Value names
* can include '\\'.
*
* Key and value names are not case sensitive.
*
* Full key name (excluding the pre-defined ancestor's name) can't exceed
* 255 UTF-16 characters, give or take. Value name can't exceed 16383 UTF-16
* characters. Tree depth is limited to 512 levels.
**/
struct _GWin32RegistryKeyPrivate {
/* Ancestor of this key. May not be the immediate parent, because
* RegOpenKeyEx() allows grand*-children to be opened transitively.
* May be NULL.
*/
GWin32RegistryKey *ancestor;
/* Handle to the key */
HKEY handle;
/* Full absolute path of the key, in UTF-16. Always allocated.
* Can become out of sync if the key is renamed from while we have it
* open, check watch_indicator to see if anything changed.
*/
gunichar2 *absolute_path_w;
/* Full absolute path of the key, in UTF-8. Allocated when needed by
* converting the UTF-16 value from absolute_path_w. */
gchar *absolute_path;
/* TRUE if this object represents one of the pre-defined keys
* (and thus must not be closed).
*/
gboolean predefined;
/* Set to G_WIN32_KEY_UNWATCHED if the key is not being watched.
* Set to G_WIN32_KEY_WATCHED when the key is put on watch notification.
*/
gint watch_indicator;
/* Set to G_WIN32_KEY_UNKNOWN while the key is not being watched.
* Set to G_WIN32_KEY_UNCHANGED once the key is put under watch.
* Set to G_WIN32_KEY_CHANGED by the watch notification APC on key change.
*/
gint change_indicator;
/* Unset after the key is changed, individual bits are set when their
* respective key parameters are updated from the registry.
* This prevents GLib from re-querying things like key name each time
* one is requested by the client while key is in G_WIN32_KEY_CHANGED state.
*/
GWin32RegistryKeyUpdateFlag update_flags;
GWin32RegistryKeyWatchCallbackFunc callback;
gpointer user_data;
};
static void g_win32_registry_key_initable_iface_init (GInitableIface *iface);
static gboolean g_win32_registry_key_initable_init (GInitable *initable,
GCancellable *cancellable,
GError **error);
G_DEFINE_TYPE_WITH_CODE (GWin32RegistryKey, g_win32_registry_key, G_TYPE_OBJECT,
G_ADD_PRIVATE (GWin32RegistryKey)
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
g_win32_registry_key_initable_iface_init));
static void
g_win32_registry_key_dispose (GObject *object)
{
GWin32RegistryKey *key;
GWin32RegistryKeyPrivate *priv;
key = G_WIN32_REGISTRY_KEY (object);
priv = key->priv;
g_clear_object (&priv->ancestor);
g_clear_pointer (&priv->absolute_path_w, g_free);
g_clear_pointer (&priv->absolute_path, g_free);
if (!priv->predefined && priv->handle != INVALID_HANDLE_VALUE)
{
RegCloseKey (priv->handle);
priv->handle = INVALID_HANDLE_VALUE;
}
G_OBJECT_CLASS (g_win32_registry_key_parent_class)->dispose (object);
}
/**
* g_win32_registry_key_new:
* @path: absolute full name of a key to open (in UTF-8)
* @error: (nullable): a pointer to a %NULL #GError, or %NULL
*
* Creates an object that represents a registry key specified by @path.
* @path must start with one of the following pre-defined names:
* - HKEY_CLASSES_ROOT
* - HKEY_CURRENT_CONFIG
* - HKEY_CURRENT_USER
* - HKEY_CURRENT_USER_LOCAL_SETTINGS
* - HKEY_LOCAL_MACHINE
* - HKEY_PERFORMANCE_DATA
* - HKEY_PERFORMANCE_NLSTEXT
* - HKEY_PERFORMANCE_TEXT
* - HKEY_USERS
* @path must not end with '\\'.
*
* Returns: (nullable) (transfer full): a #GWin32RegistryKey or %NULL if can't
* be opened. Free with g_object_unref().
*/
GWin32RegistryKey *
g_win32_registry_key_new (const gchar *path,
GError **error)
{
g_return_val_if_fail (path != NULL, NULL);
return g_initable_new (G_TYPE_WIN32_REGISTRY_KEY,
NULL,
error,
"path",
path,
NULL);
}
/**
* g_win32_registry_key_new_w:
* @path: (in) (transfer none): absolute full name of a key to open (in UTF-16)
* @error: (inout) (optional) (nullable): a pointer to a %NULL #GError, or %NULL
*
* Creates an object that represents a registry key specified by @path.
* @path must start with one of the following pre-defined names:
* - HKEY_CLASSES_ROOT
* - HKEY_CURRENT_CONFIG
* - HKEY_CURRENT_USER
* - HKEY_CURRENT_USER_LOCAL_SETTINGS
* - HKEY_LOCAL_MACHINE
* - HKEY_PERFORMANCE_DATA
* - HKEY_PERFORMANCE_NLSTEXT
* - HKEY_PERFORMANCE_TEXT
* - HKEY_USERS
* @path must not end with L'\\'.
*
* Returns: (nullable) (transfer full): a #GWin32RegistryKey or %NULL if can't
* be opened. Free with g_object_unref().
*/
GWin32RegistryKey *
g_win32_registry_key_new_w (const gunichar2 *path,
GError **error)
{
GObject *result;
g_return_val_if_fail (path != NULL, NULL);
result = g_initable_new (G_TYPE_WIN32_REGISTRY_KEY,
NULL,
error,
"path-utf16",
g_wcsdup (path, -1),
NULL);
return result ? G_WIN32_REGISTRY_KEY (result) : NULL;
}
static void
g_win32_registry_key_initable_iface_init (GInitableIface *iface)
{
iface->init = g_win32_registry_key_initable_init;
}
static gboolean
g_win32_registry_key_initable_init (GInitable *initable,
GCancellable *cancellable,
GError **error)
{
GWin32RegistryKey *key;
GWin32RegistryKeyPrivate *priv;
gunichar2 *path;
gunichar2 *first_chunk_end;
gsize first_chunk_len;
gunichar2 *second_chunk_begin;
gunichar2 *first_chunk;
HKEY ancestor;
HKEY key_handle;
LONG opened;
g_return_val_if_fail (G_IS_WIN32_REGISTRY_KEY (initable), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
key = G_WIN32_REGISTRY_KEY (initable);
priv = key->priv;
if (priv->absolute_path_w == NULL)
{
priv->absolute_path_w = g_utf8_to_utf16 (priv->absolute_path,
-1,
NULL,
NULL,
error);
if (priv->absolute_path_w == NULL)
return FALSE;
}
path = priv->absolute_path_w;
first_chunk_end = wcschr (path, L'\\');
if (first_chunk_end == NULL)
first_chunk_end = &path[wcslen (path)];
first_chunk_len = first_chunk_end - path;
first_chunk = g_wcsdup (path, -1);
first_chunk[first_chunk_len] = L'\0';
if (wcscmp (first_chunk, L"HKEY_CLASSES_ROOT") == 0)
ancestor = HKEY_CLASSES_ROOT;
else if (wcscmp (first_chunk, L"HKEY_LOCAL_MACHINE") == 0)
ancestor = HKEY_LOCAL_MACHINE;
else if (wcscmp (first_chunk, L"HKEY_CURRENT_USER") == 0)
ancestor = HKEY_CURRENT_USER;
else if (wcscmp (first_chunk, L"HKEY_CURRENT_CONFIG") == 0)
ancestor = HKEY_CURRENT_CONFIG;
else if (wcscmp (first_chunk, L"HKEY_CURRENT_USER_LOCAL_SETTINGS") == 0)
ancestor = HKEY_CURRENT_USER_LOCAL_SETTINGS;
else if (wcscmp (first_chunk, L"HKEY_USERS") == 0)
ancestor = HKEY_USERS;
else if (wcscmp (first_chunk, L"HKEY_PERFORMANCE_DATA") == 0)
ancestor = HKEY_PERFORMANCE_DATA;
else if (wcscmp (first_chunk, L"HKEY_PERFORMANCE_NLSTEXT") == 0)
ancestor = HKEY_PERFORMANCE_NLSTEXT;
else if (wcscmp (first_chunk, L"HKEY_PERFORMANCE_TEXT") == 0)
ancestor = HKEY_PERFORMANCE_TEXT;
else
{
g_critical ("Root key '%S' is not a pre-defined key", first_chunk);
g_free (first_chunk);
return FALSE;
}
g_free (first_chunk);
second_chunk_begin = first_chunk_end;
while (second_chunk_begin[0] != L'\0' && second_chunk_begin[0] == L'\\')
second_chunk_begin++;
if (second_chunk_begin != first_chunk_end && second_chunk_begin[0] == L'\0')
{
g_critical ("Key name '%S' ends with '\\'", path);
return FALSE;
}
opened = RegOpenKeyExW (ancestor, second_chunk_begin, 0, KEY_READ, &key_handle);
if (opened != ERROR_SUCCESS)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_win32_error (opened),
"Failed to open registry key '%S'", path);
return FALSE;
}
priv->ancestor = NULL;
priv->handle = key_handle;
priv->predefined = (second_chunk_begin[0] == L'\0');
return TRUE;
}
/**
* g_win32_registry_key_get_child:
* @key: (in) (transfer none): a parent #GWin32RegistryKey
* @subkey: (in) (transfer none): name of a child key to open (in UTF-8), relative to @key
* @error: (inout) (optional) (nullable): a pointer to a %NULL #GError, or %NULL
*
* Opens a @subkey of the @key.
*
* Returns: (nullable): a #GWin32RegistryKey or %NULL if can't be opened. Free
* with g_object_unref().
*/
GWin32RegistryKey *
g_win32_registry_key_get_child (GWin32RegistryKey *key,
const gchar *subkey,
GError **error)
{
gunichar2 *subkey_w;
GWin32RegistryKey *result = NULL;
g_return_val_if_fail (G_IS_WIN32_REGISTRY_KEY (key), NULL);
g_return_val_if_fail (subkey != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
subkey_w = g_utf8_to_utf16 (subkey, -1, NULL, NULL, error);
if (subkey_w != NULL)
{
result = g_win32_registry_key_get_child_w (key, subkey_w, error);
g_free (subkey_w);
}
return result;
}
/**
* g_win32_registry_key_get_child_w:
* @key: (in) (transfer none): a parent #GWin32RegistryKey
* @subkey: (in) (transfer none): name of a child key to open (in UTF-8), relative to @key
* @error: (inout) (optional) (nullable): a pointer to a %NULL #GError, or %NULL
*
* Opens a @subkey of the @key.
*
* Returns: (nullable): a #GWin32RegistryKey or %NULL if can't be opened. Free
* with g_object_unref().
*/
GWin32RegistryKey *
g_win32_registry_key_get_child_w (GWin32RegistryKey *key,
const gunichar2 *subkey,
GError **error)
{
HKEY key_handle;
LONG opened;
const gunichar2 *end_of_subkey;
gsize subkey_len;
GWin32RegistryKey *result;
const gunichar2 *key_path;
g_return_val_if_fail (G_IS_WIN32_REGISTRY_KEY (key), NULL);
g_return_val_if_fail (subkey != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
if (subkey[0] == L'\\')
{
g_critical ("Subkey name '%S' starts with '\\'", subkey);
return NULL;
}
subkey_len = wcslen (subkey);
end_of_subkey = &subkey[subkey_len];
if (subkey_len == 0)
end_of_subkey = subkey;
if (end_of_subkey[0] == L'\\')
{
g_critical ("Subkey name '%S' ends with '\\'", subkey);
return NULL;
}
key_path = g_win32_registry_key_get_path_w (key);
opened = RegOpenKeyExW (key->priv->handle, subkey, 0, KEY_READ, &key_handle);
if (opened != ERROR_SUCCESS)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_win32_error (opened),
"Failed to open registry subkey '%S' of key '%S'",
subkey, key_path);
return NULL;
}
result = g_object_new (G_TYPE_WIN32_REGISTRY_KEY, NULL);
result->priv->handle = key_handle;
result->priv->absolute_path_w =
g_malloc ((wcslen (key_path) + 2 + subkey_len) * sizeof (gunichar2));
result->priv->absolute_path_w[0] = L'\0';
wcscat (&result->priv->absolute_path_w[0], key_path);
wcscat (&result->priv->absolute_path_w[wcslen (key_path)], L"\\");
wcscat (&result->priv->absolute_path_w[wcslen (key_path) + 1], subkey);
result->priv->predefined = (subkey[0] == L'\0' && key->priv->predefined);
if (subkey[0] != L'\0')
result->priv->ancestor = g_object_ref (key);
else
result->priv->ancestor = NULL;
result->priv->change_indicator = G_WIN32_KEY_UNKNOWN;
return result;
}
/**
* g_win32_registry_subkey_iter_init:
* @iter: (in) (transfer none): a pointer to a #GWin32RegistrySubkeyIter
* @key: (in) (transfer none): a #GWin32RegistryKey to iterate over
* @error: (inout) (optional) (nullable): a pointer to %NULL #GError, or %NULL
*
* Initialises (without allocating) a #GWin32RegistrySubkeyIter. @iter may be
* completely uninitialised prior to this call; its old value is
* ignored.
*
* The iterator remains valid for as long as @key exists.
* Clean up its internal buffers with a call to
* g_win32_registry_subkey_iter_clear() when done.
*
* Returns: %TRUE if iterator was initialized successfully, %FALSE on error.
*
* Since: 2.46
**/
gboolean
g_win32_registry_subkey_iter_init (GWin32RegistrySubkeyIter *iter,
GWin32RegistryKey *key,
GError **error)
{
LONG status;
DWORD subkey_count;
DWORD max_subkey_len;
g_return_val_if_fail (iter != NULL, FALSE);
g_return_val_if_fail (G_IS_WIN32_REGISTRY_KEY (key), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
status = RegQueryInfoKeyW (key->priv->handle,
NULL, NULL, NULL,
&subkey_count, &max_subkey_len,
NULL, NULL, NULL, NULL, NULL, NULL);
if (status != ERROR_SUCCESS)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_win32_error (status),
"Failed to query info for registry key '%S'",
g_win32_registry_key_get_path_w (key));
return FALSE;
}
iter->key = g_object_ref (key);
iter->counter = -1;
iter->subkey_count = subkey_count;
iter->subkey_name_size = sizeof (gunichar2) * (max_subkey_len + 1);
iter->subkey_name = g_malloc (iter->subkey_name_size);
iter->subkey_name_u8 = NULL;
return TRUE;
}
/**
* g_win32_registry_subkey_iter_clear:
* @iter: (in) (transfer none): a #GWin32RegistrySubkeyIter
*
* Frees internal buffers of a #GWin32RegistrySubkeyIter.
*
* Since: 2.46
**/
void
g_win32_registry_subkey_iter_clear (GWin32RegistrySubkeyIter *iter)
{
g_return_if_fail (iter != NULL);
g_free (iter->subkey_name);
g_free (iter->subkey_name_u8);
g_clear_object (&iter->key);
}
/**
* g_win32_registry_subkey_iter_n_subkeys:
* @iter: (in) (transfer none): a #GWin32RegistrySubkeyIter
*
* Queries the number of subkeys items in the key that we are
* iterating over. This is the total number of subkeys -- not the number
* of items remaining.
*
* This information is accurate at the point of iterator initialization,
* and may go out of sync with reality even while subkeys are enumerated.
*
* Returns: the number of subkeys in the key
*
* Since: 2.46
**/
gsize
g_win32_registry_subkey_iter_n_subkeys (GWin32RegistrySubkeyIter *iter)
{
g_return_val_if_fail (iter != NULL, 0);
return iter->subkey_count;
}
/**
* g_win32_registry_subkey_iter_next:
* @iter: (in) (transfer none): a #GWin32RegistrySubkeyIter
* @skip_errors: (in): %TRUE if iterator should silently ignore errors (such as
* the actual number of subkeys being less than expected) and
* proceed forward
* @error: (nullable): a pointer to %NULL #GError, or %NULL
*
* Moves iterator to the next subkey.
* Enumeration errors can be ignored if @skip_errors is %TRUE
*
* Here is an example for iterating with g_win32_registry_subkey_iter_next():
* |[<!-- language="C" -->
* // recursively iterate a key
* void
* iterate_key_recursive (GWin32RegistryKey *key)
* {
* GWin32RegistrySubkeyIter iter;
* gchar *name;
* GWin32RegistryKey *child;
*
* if (!g_win32_registry_subkey_iter_init (&iter, key, NULL))
* return;
*
* while (g_win32_registry_subkey_iter_next (&iter, TRUE, NULL))
* {
* if (!g_win32_registry_subkey_iter_get_name (&iter, &name, NULL, NULL))
* continue;
*
* g_print ("subkey '%s'\n", name);
* child = g_win32_registry_key_get_child (key, name, NULL);
*
* if (child)
* iterate_key_recursive (child);
* }
*
* g_win32_registry_subkey_iter_clear (&iter);
* }
* ]|
*
* Returns: %TRUE if next subkey info was retrieved, %FALSE otherwise.
*
* Since: 2.46
**/
gboolean
g_win32_registry_subkey_iter_next (GWin32RegistrySubkeyIter *iter,
gboolean skip_errors,
GError **error)
{
LONG status;
DWORD subkey_len;
g_return_val_if_fail (iter != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if G_UNLIKELY (iter->counter >= iter->subkey_count)
{
g_critical ("g_win32_registry_subkey_iter_get_next_w: must not be called again "
"after FALSE has already been returned.");
return FALSE;
}
while (TRUE)
{
iter->counter += 1;
if (iter->counter >= iter->subkey_count)
return FALSE;
/* Including 0-terminator */
subkey_len = iter->subkey_name_size;
status = RegEnumKeyExW (iter->key->priv->handle,
iter->counter,
iter->subkey_name,
&subkey_len,
NULL, NULL, NULL, NULL);
if (status == ERROR_SUCCESS)
{
iter->subkey_name_len = subkey_len;
return TRUE;
}
if (!skip_errors)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_win32_error (status),
"Failed to enumerate subkey #%d for key '%S'",
iter->counter, g_win32_registry_key_get_path_w (iter->key));
iter->subkey_count = 0;
return FALSE;
}
}
}
/**
* g_win32_registry_subkey_iter_get_name_w:
* @iter: (in) (transfer none): a #GWin32RegistrySubkeyIter
* @subkey_name: (out callee-allocates) (transfer none): Pointer to a location
* to store the name of a subkey (in UTF-16).
* @subkey_name_len: (out) (optional) (transfer none): Pointer to a location
* to store the length of @subkey_name, in gunichar2s, excluding
* NUL-terminator.
* %NULL if length is not needed.
* @error: (nullable): a pointer to %NULL #GError, or %NULL
*
* Same as g_win32_registry_subkey_iter_get_next(), but outputs UTF-16-encoded
* data, without converting it to UTF-8 first.
*
* Returns: %TRUE if the name was retrieved, %FALSE otherwise.
*
* Since: 2.46
**/
gboolean
g_win32_registry_subkey_iter_get_name_w (GWin32RegistrySubkeyIter *iter,
const gunichar2 **subkey_name,
gsize *subkey_name_len,
GError **error)
{
g_return_val_if_fail (iter != NULL, FALSE);
g_return_val_if_fail (subkey_name != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if G_UNLIKELY (iter->counter >= iter->subkey_count)
{
g_critical ("g_win32_registry_subkey_iter_get_name_w: must not be called "
"after FALSE has already been returned by "
"g_win32_registry_subkey_iter_next.");
return FALSE;
}
*subkey_name = iter->subkey_name;
if (subkey_name_len)
*subkey_name_len = iter->subkey_name_len;
return TRUE;
}
/**
* g_win32_registry_subkey_iter_get_name:
* @iter: (in) (transfer none): a #GWin32RegistrySubkeyIter
* @subkey_name: (out callee-allocates) (transfer none): Pointer to a location
* to store the name of a subkey (in UTF-8). Free with g_free().
* @subkey_name_len: (out) (optional): Pointer to a location to store the
* length of @subkey_name, in gchars, excluding NUL-terminator.
* %NULL if length is not needed.
* @error: (nullable): a pointer to %NULL #GError, or %NULL
*
* Gets the name of the subkey at the @iter potision.
*
* Returns: %TRUE if the name was retrieved, %FALSE otherwise.
*
* Since: 2.46
**/
gboolean
g_win32_registry_subkey_iter_get_name (GWin32RegistrySubkeyIter *iter,
const gchar **subkey_name,
gsize *subkey_name_len,
GError **error)
{
glong subkey_name_len_glong;
g_return_val_if_fail (iter != NULL, FALSE);
g_return_val_if_fail (subkey_name != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if G_UNLIKELY (iter->counter >= iter->subkey_count)
{
g_critical ("g_win32_registry_subkey_iter_get_name_w: must not be called "
"after FALSE has already been returned by "
"g_win32_registry_subkey_iter_next.");
return FALSE;
}
g_clear_pointer (&iter->subkey_name_u8, g_free);
iter->subkey_name_u8 = g_utf16_to_utf8 (iter->subkey_name,
iter->subkey_name_len,
NULL,
&subkey_name_len_glong,
error);
if (iter->subkey_name_u8 == NULL)
return FALSE;
*subkey_name = iter->subkey_name_u8;
if (subkey_name_len)
*subkey_name_len = subkey_name_len_glong;
return TRUE;
}
/**
* g_win32_registry_value_iter_init:
* @iter: (in) (transfer none): a pointer to a #GWin32RegistryValueIter
* @key: (in) (transfer none): a #GWin32RegistryKey to iterate over
* @error: (nullable): a pointer to %NULL #GError, or %NULL
*
* Initialises (without allocating) a #GWin32RegistryValueIter. @iter may be
* completely uninitialised prior to this call; its old value is
* ignored.
*
* The iterator remains valid for as long as @key exists.
* Clean up its internal buffers with a call to
* g_win32_registry_value_iter_clear() when done.
*
* Returns: %TRUE if iterator was initialized successfully, %FALSE on error.
*
* Since: 2.46
**/
gboolean
g_win32_registry_value_iter_init (GWin32RegistryValueIter *iter,
GWin32RegistryKey *key,
GError **error)
{
LONG status;
DWORD value_count;
DWORD max_value_len;
DWORD max_data_len;
g_return_val_if_fail (iter != NULL, FALSE);
g_return_val_if_fail (G_IS_WIN32_REGISTRY_KEY (key), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
status = RegQueryInfoKeyW (key->priv->handle,
NULL, NULL, NULL, NULL, NULL, NULL,
&value_count, &max_value_len,
&max_data_len, NULL, NULL);
if (status != ERROR_SUCCESS)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_win32_error (status),
"Failed to query info for registry key '%S'",
g_win32_registry_key_get_path_w (key));
return FALSE;
}
iter->key = g_object_ref (key);
iter->counter = -1;
iter->value_count = value_count;
iter->value_name_size = sizeof (gunichar2) * (max_value_len + 1);
iter->value_name = g_malloc (iter->value_name_size);
/* FIXME: max_value_data_len is said to have no size limit in newer W32
* versions (and its size limit in older ones is 1MB!). Consider limiting it
* with a hard-coded value, or by allowing the user to choose a limit.
*/
/* Two extra gunichar2s is for cases when a string was stored in the
* Registry without a 0-terminator (for multiline strings - 00-terminator),
* and we need to terminate it ourselves.
*/
iter->value_data_size = max_data_len + sizeof (gunichar2) * 2;
iter->value_data = g_malloc (iter->value_data_size);
iter->value_name_u8 = NULL;
iter->value_data_u8 = NULL;
iter->value_data_expanded = NULL;
iter->value_data_expanded_charsize = 0;
iter->value_data_expanded_u8 = NULL;
iter->value_data_expanded_u8_size = 0;
return TRUE;
}
/**
* g_win32_registry_value_iter_clear:
* @iter: (in) (transfer none): a #GWin32RegistryValueIter
*
* Frees internal buffers of a #GWin32RegistryValueIter.
*
* Since: 2.46
**/
void
g_win32_registry_value_iter_clear (GWin32RegistryValueIter *iter)
{
g_return_if_fail (iter != NULL);
g_free (iter->value_name);
g_free (iter->value_data);
g_free (iter->value_name_u8);
g_free (iter->value_data_u8);
g_free (iter->value_data_expanded);
g_free (iter->value_data_expanded_u8);
g_clear_object (&iter->key);
}
/**
* g_win32_registry_value_iter_n_values:
* @iter: (in) (transfer none): a #GWin32RegistryValueIter
*
* Queries the number of values items in the key that we are
* iterating over. This is the total number of values -- not the number
* of items remaining.
*
* This information is accurate at the point of iterator initialization,
* and may go out of sync with reality even while values are enumerated.
*
* Returns: the number of values in the key
*
* Since: 2.46
**/
gsize
g_win32_registry_value_iter_n_values (GWin32RegistryValueIter *iter)
{
g_return_val_if_fail (iter != NULL, 0);
return iter->value_count;
}
static GWin32RegistryValueType
_g_win32_registry_type_w_to_g (DWORD value_type)
{
switch (value_type)
{
case REG_BINARY:
return G_WIN32_REGISTRY_VALUE_BINARY;
case REG_DWORD:
return G_WIN32_REGISTRY_VALUE_UINT32;
#if G_BYTE_ORDER == G_BIG_ENDIAN
case REG_DWORD_LITTLE_ENDIAN:
return G_WIN32_REGISTRY_VALUE_UINT32LE;
#else
case REG_DWORD_BIG_ENDIAN:
return G_WIN32_REGISTRY_VALUE_UINT32BE;
#endif
case REG_EXPAND_SZ:
return G_WIN32_REGISTRY_VALUE_EXPAND_STR;
case REG_LINK:
return G_WIN32_REGISTRY_VALUE_LINK;
case REG_MULTI_SZ:
return G_WIN32_REGISTRY_VALUE_MULTI_STR;
case REG_NONE:
return G_WIN32_REGISTRY_VALUE_NONE;
case REG_QWORD:
return G_WIN32_REGISTRY_VALUE_UINT64;
#if G_BYTE_ORDER == G_BIG_ENDIAN
case REG_QWORD_LITTLE_ENDIAN:
return G_WIN32_REGISTRY_VALUE_UINT64LE;
#endif
case REG_SZ:
return G_WIN32_REGISTRY_VALUE_STR;
default:
return G_WIN32_REGISTRY_VALUE_NONE;
}
}
static gsize
ensure_nul_termination (GWin32RegistryValueType value_type,
guint8 *value_data,
gsize value_data_size)
{
gsize new_size = value_data_size;
if (value_type == G_WIN32_REGISTRY_VALUE_EXPAND_STR ||
value_type == G_WIN32_REGISTRY_VALUE_LINK ||
value_type == G_WIN32_REGISTRY_VALUE_STR)
{
if ((value_data_size < 2) ||
(value_data[value_data_size - 1] != 0) ||
(value_data[value_data_size - 2] != 0))
{
value_data[value_data_size] = 0;
value_data[value_data_size + 1] = 0;
new_size += 2;
}
}
else if (value_type == G_WIN32_REGISTRY_VALUE_MULTI_STR)
{
if ((value_data_size < 4) ||
(value_data[value_data_size - 1] != 0) ||
(value_data[value_data_size - 2] != 0) ||
(value_data[value_data_size - 3] != 0) ||
(value_data[value_data_size - 4] != 0))
{
value_data[value_data_size] = 0;
value_data[value_data_size + 1] = 0;
value_data[value_data_size + 2] = 0;
value_data[value_data_size + 3] = 0;
new_size += 4;
}
}
return new_size;
}
/**
* g_win32_registry_value_iter_next:
* @iter: (in) (transfer none): a #GWin32RegistryValueIter
* @skip_errors: (in): %TRUE if iterator should silently ignore errors (such as
* the actual number of values being less than expected) and
* proceed forward
* @error: (nullable): a pointer to %NULL #GError, or %NULL
*
* Advances iterator to the next value in the key. If no more values remain then
* FALSE is returned.
* Enumeration errors can be ignored if @skip_errors is %TRUE
*
* Here is an example for iterating with g_win32_registry_value_iter_next():
* |[<!-- language="C" -->
* // iterate values of a key
* void
* iterate_values_recursive (GWin32RegistryKey *key)
* {
* GWin32RegistryValueIter iter;
* gchar *name;
* GWin32RegistryValueType val_type;
* gchar *val_data;
*
* if (!g_win32_registry_value_iter_init (&iter, key, NULL))
* return;
*
* while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
* {
* if ((!g_win32_registry_value_iter_get_value_type (&iter, &value)) ||
* ((val_type != G_WIN32_REGISTRY_VALUE_STR) &&
* (val_type != G_WIN32_REGISTRY_VALUE_EXPAND_STR)))
* continue;
*
* if (g_win32_registry_value_iter_get_value (&iter, TRUE, &name, NULL,
* &val_data, NULL, NULL))
* g_print ("value '%s' = '%s'\n", name, val_data);
* }
*
* g_win32_registry_value_iter_clear (&iter);
* }
* ]|
*
* Returns: %TRUE if next value info was retrieved, %FALSE otherwise.
*
* Since: 2.46
**/
gboolean
g_win32_registry_value_iter_next (GWin32RegistryValueIter *iter,
gboolean skip_errors,
GError **error)
{
LONG status;
DWORD value_name_len_w;
DWORD value_data_size_w;
DWORD value_type_w;
GWin32RegistryValueType value_type_g;
g_return_val_if_fail (iter != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if G_UNLIKELY (iter->counter >= iter->value_count)
{
g_critical ("g_win32_registry_value_iter_next: must not be called "
"again after FALSE has already been returned.");
return FALSE;
}
while (TRUE)
{
iter->counter += 1;
if (iter->counter >= iter->value_count)
return FALSE;
g_clear_pointer (&iter->value_name_u8, g_free);
g_clear_pointer (&iter->value_data_u8, g_free);
g_clear_pointer (&iter->value_data_expanded_u8, g_free);
/* Including 0-terminator */
value_name_len_w = iter->value_name_size / sizeof (gunichar2);
value_data_size_w = iter->value_data_size;
status = RegEnumValueW (iter->key->priv->handle,
iter->counter,
iter->value_name,
&value_name_len_w,
NULL,
&value_type_w,
(LPBYTE) iter->value_data,
&value_data_size_w);
if (status != ERROR_SUCCESS && !skip_errors)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_win32_error (status),
"Failed to enumerate value #%d for key '%S'",
iter->counter, g_win32_registry_key_get_path_w (iter->key));
iter->value_count = 0;
return FALSE;
}
else if (status != ERROR_SUCCESS && skip_errors)
continue;
value_type_g = _g_win32_registry_type_w_to_g (value_type_w);
value_data_size_w = ensure_nul_termination (value_type_g,
iter->value_data,
value_data_size_w);
iter->value_type = value_type_g;
iter->value_expanded_type = value_type_g;
iter->value_actual_data_size = value_data_size_w;
iter->value_name_len = value_name_len_w;
return TRUE;
}
}
/**
* g_win32_registry_value_iter_get_value_type:
* @iter: (in) (transfer none): a #GWin32RegistryValueIter
* @value_type: (out): Pointer to a location to store the type of
* the value.
* @error: (nullable): a pointer to %NULL #GError, or %NULL
*
* Stores the type of the value currently being iterated over in @value_type.
*
* Returns: %TRUE if value type was retrieved, %FALSE otherwise.
*
* Since: 2.46
**/
gboolean
g_win32_registry_value_iter_get_value_type (GWin32RegistryValueIter *iter,
GWin32RegistryValueType *value_type,
GError **error)
{
g_return_val_if_fail (iter != NULL, FALSE);
g_return_val_if_fail (value_type != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if G_UNLIKELY (iter->counter >= iter->value_count)
{
g_critical ("g_win32_registry_value_iter_get_type: must not be called "
"again after NULL has already been returned.");
return FALSE;
}
*value_type = iter->value_type;
return TRUE;
}
/**
* g_win32_registry_value_iter_get_name_w:
* @iter: (in) (transfer none): a #GWin32RegistryValueIter
* @value_name: (out callee-allocates) (transfer none): Pointer to a location
* to store the name of a value (in UTF-16).
* @value_name_len: (out) (optional): Pointer to a location to store the length
* of @value_name, in gunichar2s, excluding NUL-terminator.
* %NULL if length is not needed.
* @error: (nullable): a pointer to %NULL #GError, or %NULL
*
* Stores the name of the value currently being iterated over in @value_name,
* and its length - in @value_name (if not %NULL).
*
* Returns: %TRUE if value name was retrieved, %FALSE otherwise.
*
* Since: 2.46
**/
gboolean
g_win32_registry_value_iter_get_name_w (GWin32RegistryValueIter *iter,
gunichar2 **value_name,
gsize *value_name_len,
GError **error)
{
g_return_val_if_fail (iter != NULL, FALSE);
g_return_val_if_fail (value_name != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if G_UNLIKELY (iter->counter >= iter->value_count)
{
g_critical ("g_win32_registry_value_iter_get_name_w: must not be called "
"again after NULL has already been returned.");
return FALSE;
}
*value_name = iter->value_name;
if (value_name_len)
*value_name_len = iter->value_name_len;
return TRUE;
}
/**
* g_win32_registry_value_iter_get_name:
* @iter: (in) (transfer none): a #GWin32RegistryValueIter
* @value_name: (out callee-allocates) (transfer none): Pointer to a location
* to store the name of a value (in UTF-8).
* @value_name_len: (out) (optional): Pointer to a location to store the length
* of @value_name, in gchars, excluding NUL-terminator.
* %NULL if length is not needed.
* @error: (nullable): a pointer to %NULL #GError, or %NULL
*
* Stores the name of the value currently being iterated over in @value_name,
* and its length - in @value_name_len (if not %NULL).
*
* Returns: %TRUE if value name was retrieved, %FALSE otherwise.
*
* Since: 2.46
**/
gboolean
g_win32_registry_value_iter_get_name (GWin32RegistryValueIter *iter,
gchar **value_name,
gsize *value_name_len,
GError **error)
{
glong value_name_len_glong;
g_return_val_if_fail (iter != NULL, FALSE);
g_return_val_if_fail (value_name != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if G_UNLIKELY (iter->counter >= iter->value_count)
{
g_critical ("g_win32_registry_value_iter_get_name: must not be called "
"again after NULL has already been returned.");
return FALSE;
}
if (iter->value_name_u8 == NULL)
{
iter->value_name_u8 = g_utf16_to_utf8 (iter->value_name, iter->value_name_len, NULL,
&value_name_len_glong, error);
if (iter->value_name_u8 == NULL)
return FALSE;
}
*value_name = iter->value_name_u8;
if (value_name_len)
*value_name_len = iter->value_name_u8_len;
return TRUE;
}
static gboolean
expand_value (gunichar2 *value,
const gunichar2 *value_name,
gpointer *expanded_value,
gsize *expanded_charsize,
GError **error)
{
DWORD value_data_expanded_charsize_w;
value_data_expanded_charsize_w =
ExpandEnvironmentStringsW (value,
(gunichar2 *) *expanded_value,
*expanded_charsize);
if (value_data_expanded_charsize_w > *expanded_charsize)
{
*expanded_value = g_realloc (*expanded_value,
value_data_expanded_charsize_w * sizeof (gunichar2));
*expanded_charsize = value_data_expanded_charsize_w;
value_data_expanded_charsize_w =
ExpandEnvironmentStringsW (value,
(gunichar2 *) *expanded_value,
*expanded_charsize);
}
if (value_data_expanded_charsize_w == 0)
{
g_set_error (error, G_IO_ERROR,
g_io_error_from_win32_error (GetLastError ()),
"Failed to expand data '%S' of value %S",
value, value_name);
return FALSE;
}
return TRUE;
}
/**
* g_win32_registry_value_iter_get_data_w:
* @iter: (in) (transfer none): a #GWin32RegistryValueIter
* @auto_expand: (in): %TRUE to automatically expand G_WIN32_REGISTRY_VALUE_EXPAND_STR to
* G_WIN32_REGISTRY_VALUE_STR
* @value_data: (out callee-allocates) (optional) (transfer none): Pointer to a
* location to store the data of the value (in UTF-16, if it's a string)
* @value_data_size: (out) (optional): Pointer to a location to store the size
* of @value_data, in bytes (including any NUL-terminators, if it's a string).
* %NULL if length is not needed.
* @error: (nullable): a pointer to %NULL #GError, or %NULL
*
* Stores the data of the value currently being iterated over in @value_data,
* and its length - in @value_data_len (if not %NULL).
*
* Returns: %TRUE if value data was retrieved, %FALSE otherwise.
*
* Since: 2.46
**/
gboolean
g_win32_registry_value_iter_get_data_w (GWin32RegistryValueIter *iter,
gboolean auto_expand,
gpointer *value_data,
gsize *value_data_size,
GError **error)
{
g_return_val_if_fail (iter != NULL, FALSE);
g_return_val_if_fail (value_data != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if G_UNLIKELY (iter->counter >= iter->value_count)
{
g_critical ("g_win32_registry_value_iter_get_data_w: must not be called "
"again after FALSE has already been returned.");
return FALSE;
}
if (!auto_expand || (iter->value_type != G_WIN32_REGISTRY_VALUE_EXPAND_STR))
{
*value_data = iter->value_data;
if (value_data_size)
*value_data_size = iter->value_actual_data_size;
return TRUE;
}
if (iter->value_type == iter->value_expanded_type)
{
if (!expand_value ((gunichar2 *) iter->value_data,
iter->value_name,
(gpointer *) &iter->value_data_expanded,
&iter->value_data_expanded_charsize,
error))
return FALSE;
iter->value_expanded_type = G_WIN32_REGISTRY_VALUE_STR;
}
*value_data = iter->value_data_expanded;
if (value_data_size)
*value_data_size = iter->value_data_expanded_charsize * sizeof (gunichar2);
return TRUE;
}
/**
* g_win32_registry_value_iter_get_data:
* @iter: (in) (transfer none): a #GWin32RegistryValueIter
* @auto_expand: (in): %TRUE to automatically expand G_WIN32_REGISTRY_VALUE_EXPAND_STR to
* G_WIN32_REGISTRY_VALUE_STR
* @value_data: (out callee-allocates) (optional) (transfer none): Pointer to a
* location to store the data of the value (in UTF-8, if it's a string)
* @value_data_size: (out) (optional): Pointer to a location to store the length
* of @value_data, in bytes (including any NUL-terminators, if it's a string).
* %NULL if length is not needed
* @error: (nullable): a pointer to %NULL #GError, or %NULL
*
* Stores the data of the value currently being iterated over in @value_data,
* and its length - in @value_data_len (if not %NULL).
*
* Returns: %TRUE if value data was retrieved, %FALSE otherwise.
*
* Since: 2.46
**/
gboolean
g_win32_registry_value_iter_get_data (GWin32RegistryValueIter *iter,
gboolean auto_expand,
gpointer *value_data,
gsize *value_data_size,
GError **error)
{
gsize value_data_len_gsize;
gpointer tmp;
gsize tmp_size;
g_return_val_if_fail (iter != NULL, FALSE);
g_return_val_if_fail (value_data != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if G_UNLIKELY (iter->counter >= iter->value_count)
{
g_critical ("g_win32_registry_value_iter_get_data: must not be called "
"again after FALSE has already been returned.");
return FALSE;
}
if (iter->value_type != G_WIN32_REGISTRY_VALUE_EXPAND_STR &&
iter->value_type != G_WIN32_REGISTRY_VALUE_LINK &&
iter->value_type != G_WIN32_REGISTRY_VALUE_STR &&
iter->value_type != G_WIN32_REGISTRY_VALUE_MULTI_STR)
{
*value_data = iter->value_data;
if (value_data_size != NULL)
*value_data_size = iter->value_actual_data_size;
return TRUE;
}
if (!auto_expand || (iter->value_type != G_WIN32_REGISTRY_VALUE_EXPAND_STR))
{
if (iter->value_data_u8 == NULL)
{
iter->value_data_u8 = g_convert ((const gchar *) iter->value_data,
iter->value_actual_data_size - sizeof (gunichar2) /* excl. 0 */,
"UTF8", "UTF16", NULL,
&value_data_len_gsize,
error);
if (iter->value_data_u8 == NULL)
return FALSE;
iter->value_data_u8_size = value_data_len_gsize + 1; /* incl. 0 */
}
*value_data = iter->value_data_u8;
if (value_data_size != NULL)
*value_data_size = iter->value_data_u8_size;
return TRUE;
}
if (iter->value_data_expanded_u8 == NULL)
{
if (!g_win32_registry_value_iter_get_data_w (iter,
TRUE,
&tmp,
&tmp_size,
error))
return FALSE;
iter->value_data_expanded_u8 = g_convert ((const gchar *) iter->value_data_expanded,
iter->value_data_expanded_charsize * sizeof (gunichar2) - sizeof (gunichar2) /* excl. 0 */,
"UTF8", "UTF16", NULL,
&value_data_len_gsize,
error);
if (iter->value_data_expanded_u8 == NULL)
return FALSE;
iter->value_data_u8_size = value_data_len_gsize + 1; /* incl. 0 */
}
*value_data = iter->value_data_expanded_u8;
if (value_data_size != NULL)
*value_data_size = iter->value_data_expanded_u8_size;
return TRUE;
}
static void
_g_win32_registry_key_reread_kernel (GWin32RegistryKey *key,
GWin32RegistryKeyPrivate *buf)
{
NTSTATUS status;
KEY_BASIC_INFORMATION *basic_info;
ULONG basic_info_size;
ULONG datasize;
basic_info_size = 256 * sizeof (gunichar2) + sizeof (KEY_BASIC_INFORMATION);
basic_info = g_malloc (basic_info_size + sizeof (gunichar2));
status = nt_query_key (key->priv->handle,
KeyBasicInformation,
basic_info,
basic_info_size,
&datasize);
if (status == STATUS_BUFFER_OVERFLOW || status == STATUS_BUFFER_TOO_SMALL)
{
g_free (basic_info);
basic_info_size = datasize;
/* +1 for 0-terminator */
basic_info = g_malloc (basic_info_size + sizeof (gunichar2));
status = nt_query_key (key->priv->handle,
KeyBasicInformation,
basic_info,
basic_info_size,
&datasize);
}
if (status != STATUS_SUCCESS)
{
g_free (basic_info);
return;
}
/* Ensure 0-termination */
((char *) basic_info)[datasize] = 0;
((char *) basic_info)[datasize + 1] = 0;
buf->absolute_path_w = g_wcsdup (&basic_info->Name[0],
basic_info->NameLength + sizeof (gunichar2));
g_free (basic_info);
}
static void
_g_win32_registry_key_reread_user (GWin32RegistryKey *key,
GWin32RegistryKeyPrivate *buf)
{
/* Use RegQueryInfoKey(). It's just like NtQueryKey(), but can't query
* key name.
* Since right now we only need the name, this function is a noop.
*/
}
static void
_g_win32_registry_key_reread (GWin32RegistryKey *key,
GWin32RegistryKeyPrivate *buf)
{
if (g_once_init_enter (&nt_query_key))
{
NtQueryKeyFunc func;
HMODULE ntdll = GetModuleHandleW (L"ntdll.dll");
if (ntdll != NULL)
func = (NtQueryKeyFunc) GetProcAddress (ntdll, "NtQueryKey");
else
func = NULL;
g_once_init_leave (&nt_query_key, func);
}
/* Assume that predefined keys never get renamed. Also, their handles probably
* won't be accepted by NtQueryKey(), i suspect.
*/
if (nt_query_key != NULL && !key->priv->predefined)
_g_win32_registry_key_reread_kernel (key, buf);
else
_g_win32_registry_key_reread_user (key, buf);
}
static gboolean
_g_win32_registry_key_update_path (GWin32RegistryKey *key)
{
GWin32RegistryKeyPrivate tmp;
gboolean changed;
gint change_indicator;
change_indicator = g_atomic_int_get (&key->priv->change_indicator);
if (change_indicator == G_WIN32_KEY_UNCHANGED)
return FALSE;
tmp.absolute_path_w = NULL;
_g_win32_registry_key_reread (key, &tmp);
changed = FALSE;
if (wcscmp (key->priv->absolute_path_w, tmp.absolute_path_w) == 0)
g_free (tmp.absolute_path_w);
else
{
g_free (key->priv->absolute_path_w);
key->priv->absolute_path_w = tmp.absolute_path_w;
changed = TRUE;
}
return changed;
}
/**
* g_win32_registry_key_get_path:
* @key: (in) (transfer none): a #GWin32RegistryKey
*
* Get full path to the key
*
* Returns: (transfer none): a full path to the key (in UTF-8),
* or %NULL if it can't be converted to UTF-8.
*
* Since: 2.46
**/
const gchar *
g_win32_registry_key_get_path (GWin32RegistryKey *key)
{
gint change_indicator;
g_return_val_if_fail (G_IS_WIN32_REGISTRY_KEY (key), NULL);
change_indicator = g_atomic_int_get (&key->priv->change_indicator);
if (change_indicator == G_WIN32_KEY_CHANGED &&
!(key->priv->update_flags & G_WIN32_REGISTRY_UPDATED_PATH))
{
_g_win32_registry_key_update_path (key);
key->priv->update_flags |= G_WIN32_REGISTRY_UPDATED_PATH;
}
if (key->priv->absolute_path == NULL)
{
g_free (key->priv->absolute_path);
key->priv->absolute_path =
g_utf16_to_utf8 (key->priv->absolute_path_w, -1,
NULL, NULL, NULL);
}
return key->priv->absolute_path;
}
/**
* g_win32_registry_key_get_path_w:
* @key: (in) (transfer none): a #GWin32RegistryKey
*
* Get full path to the key
*
* Returns: (transfer none): a full path to the key (in UTF-16)
*
* Since: 2.46
**/
const gunichar2 *
g_win32_registry_key_get_path_w (GWin32RegistryKey *key)
{
gint change_indicator;
g_return_val_if_fail (G_IS_WIN32_REGISTRY_KEY (key), NULL);
change_indicator = g_atomic_int_get (&key->priv->change_indicator);
if (change_indicator == G_WIN32_KEY_CHANGED)
_g_win32_registry_key_update_path (key);
return key->priv->absolute_path_w;
}
/**
* g_win32_registry_get_os_dirs_w:
*
* Returns a list of directories for DLL lookups.
* Can be used with g_win32_registry_key_get_value_w().
*
* Returns: (array zero-terminated=1) (transfer none): a %NULL-terminated array of UTF-16 strings.
*
* Since: 2.66
*/
const gunichar2 * const *
g_win32_registry_get_os_dirs_w (void)
{
static gunichar2 **mui_os_dirs = NULL;
if (g_once_init_enter (&mui_os_dirs))
{
gunichar2 **new_mui_os_dirs;
gunichar2 *system32 = NULL;
gunichar2 *syswow64 = NULL;
UINT buffer_size;
gsize array_index = 0;
buffer_size = GetSystemWow64DirectoryW (NULL, 0);
if (buffer_size > 0)
{
UINT copied;
syswow64 = g_malloc (buffer_size * sizeof (gunichar2));
copied = GetSystemWow64DirectoryW (syswow64, buffer_size);
if (copied <= 0)
g_clear_pointer (&syswow64, g_free);
}
buffer_size = GetSystemDirectoryW (NULL, 0);
if (buffer_size > 0)
{
UINT copied;
system32 = g_malloc (buffer_size * sizeof (gunichar2));
copied = GetSystemDirectoryW (system32, buffer_size);
if (copied <= 0)
g_clear_pointer (&system32, g_free);
}
new_mui_os_dirs = g_new0 (gunichar2 *, 3);
if (system32 != NULL)
new_mui_os_dirs[array_index++] = system32;
if (syswow64 != NULL)
new_mui_os_dirs[array_index++] = syswow64;
new_mui_os_dirs[array_index++] = NULL;
g_once_init_leave (&mui_os_dirs, new_mui_os_dirs);
}
return (const gunichar2 * const *) mui_os_dirs;
}
/**
* g_win32_registry_get_os_dirs:
*
* Returns a list of directories for DLL lookups.
* Can be used with g_win32_registry_key_get_value().
*
* Returns: (array zero-terminated=1) (transfer none): a %NULL-terminated array of UTF-8 strings.
*
* Since: 2.66
*/
const gchar * const *
g_win32_registry_get_os_dirs (void)
{
static gchar **mui_os_dirs = NULL;
if (g_once_init_enter (&mui_os_dirs))
{
gchar **new_mui_os_dirs;
gsize array_index;
gsize new_array_index;
const gunichar2 * const *mui_os_dirs_utf16 = g_win32_registry_get_os_dirs_w ();
for (array_index = 0; mui_os_dirs_utf16[array_index] != NULL; array_index++)
;
new_mui_os_dirs = g_new0 (gchar *, array_index + 1);
for (array_index = 0, new_array_index = 0;
mui_os_dirs_utf16[array_index] != NULL;
array_index++)
{
new_mui_os_dirs[new_array_index] = g_utf16_to_utf8 (mui_os_dirs_utf16[array_index],
-1, NULL, NULL, NULL);
if (new_mui_os_dirs[new_array_index] != NULL)
new_array_index += 1;
else
g_critical ("Failed to convert to a system directory #%zu to UTF-8", array_index);
}
g_once_init_leave (&mui_os_dirs, new_mui_os_dirs);
}
return (const gchar * const *) mui_os_dirs;
}
/**
* g_win32_registry_key_get_value:
* @key: (in) (transfer none): a #GWin32RegistryKey
* @mui_dll_dirs: (in) (transfer none) (array zero-terminated=1) (optional): a %NULL-terminated
* array of directory names where the OS
* should look for a DLL indicated in a MUI string, if the
* DLL path in the string is not absolute
* @auto_expand: (in) %TRUE to automatically expand G_WIN32_REGISTRY_VALUE_EXPAND_STR
* to G_WIN32_REGISTRY_VALUE_STR.
* @value_name: (in) (transfer none): name of the value to get (in UTF-8).
* Empty string means the '(Default)' value.
* @value_type: (out) (optional): type of the value retrieved.
* @value_data: (out callee-allocates) (optional): contents of the value.
* @value_data_size: (out) (optional): size of the buffer pointed
* by @value_data.
* @error: (nullable): a pointer to %NULL #GError, or %NULL
*
* Get data from a value of a key. String data is guaranteed to be
* appropriately terminated and will be in UTF-8.
*
* When not %NULL, @mui_dll_dirs indicates that `RegLoadMUIStringW()` API
* should be used instead of the usual `RegQueryValueExW()`. This implies
* that the value being queried is of type `REG_SZ` or `REG_EXPAND_SZ` (if it is not, the function
* falls back to `RegQueryValueExW()`), and that this string must undergo special processing
* (see [`SHLoadIndirectString()` documentation](https://docs.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-shloadindirectstring) for an explanation on what
* kinds of strings are processed) to get the result.
*
* If no specific MUI DLL directories need to be used, pass
* the return value of g_win32_registry_get_os_dirs() as @mui_dll_dirs
* (as an bonus, the value from g_win32_registry_get_os_dirs()
* does not add any extra UTF8->UTF16 conversion overhead).
*
* @auto_expand works with @mui_dll_dirs, but only affects the processed
* string, making it somewhat useless. The unprocessed string is always expanded
* internally, if its type is `REG_EXPAND_SZ` - there is no need to enable
* @auto_expand for this to work.
*
* The API for this function changed in GLib 2.66 to add the @mui_dll_dirs argument.
*
* Returns: %TRUE on success, %FALSE on failure.
*
* Since: 2.66
**/
gboolean
g_win32_registry_key_get_value (GWin32RegistryKey *key,
const gchar * const *mui_dll_dirs,
gboolean auto_expand,
const gchar *value_name,
GWin32RegistryValueType *value_type,
gpointer *value_data,
gsize *value_data_size,
GError **error)
{
GWin32RegistryValueType value_type_g;
gpointer value_data_w;
gsize value_data_w_size;
gunichar2 *value_name_w;
gchar *value_data_u8;
gsize value_data_u8_len;
gboolean result;
gsize mui_dll_dirs_count;
gunichar2 **mui_dll_dirs_utf16;
const gchar * const *mui_os_dirs;
g_return_val_if_fail (G_IS_WIN32_REGISTRY_KEY (key), FALSE);
g_return_val_if_fail (value_name != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
/* No sense calling this function with all of these set to NULL */
g_return_val_if_fail (value_type != NULL ||
value_data != NULL ||
value_data_size != NULL, FALSE);
value_name_w = g_utf8_to_utf16 (value_name, -1, NULL, NULL, error);
if (value_name_w == NULL)
return FALSE;
mui_dll_dirs_utf16 = NULL;
mui_os_dirs = g_win32_registry_get_os_dirs ();
if (mui_dll_dirs != NULL &&
mui_dll_dirs != mui_os_dirs)
{
gsize i;
mui_dll_dirs_count = g_strv_length ((gchar **) mui_dll_dirs);
mui_dll_dirs_utf16 = g_new0 (gunichar2 *, mui_dll_dirs_count + 1);
for (i = 0; mui_dll_dirs[i] != NULL; i++)
{
mui_dll_dirs_utf16[i] = g_utf8_to_utf16 (mui_dll_dirs[i], -1, NULL, NULL, error);
if (mui_dll_dirs_utf16[i] == NULL)
break;
}
if (mui_dll_dirs[i] != NULL)
{
g_prefix_error (error,
"A mui_dll_dirs string #%zu `%s' failed to convert: ",
i, mui_dll_dirs[i]);
for (i = 0; i < mui_dll_dirs_count; i++)
g_free (mui_dll_dirs_utf16[i]);
g_free (mui_dll_dirs_utf16);
g_free (value_name_w);
return FALSE;
}
}
else if (mui_dll_dirs != NULL &&
mui_dll_dirs == mui_os_dirs)
{
mui_dll_dirs_utf16 = (gunichar2 **) g_win32_registry_get_os_dirs_w ();
}
result = g_win32_registry_key_get_value_w (key,
(const gunichar2 * const *) mui_dll_dirs_utf16,
auto_expand,
value_name_w,
&value_type_g,
&value_data_w,
&value_data_w_size,
error);
g_free (value_name_w);
if (mui_dll_dirs_utf16 != NULL &&
mui_dll_dirs != mui_os_dirs)
{
gsize array_index;
for (array_index = 0; mui_dll_dirs_utf16[array_index] != NULL; array_index++)
g_free (mui_dll_dirs_utf16[array_index]);
g_free (mui_dll_dirs_utf16);
}
if (!result)
return FALSE;
if (value_type_g == G_WIN32_REGISTRY_VALUE_EXPAND_STR ||
value_type_g == G_WIN32_REGISTRY_VALUE_LINK ||
value_type_g == G_WIN32_REGISTRY_VALUE_STR ||
value_type_g == G_WIN32_REGISTRY_VALUE_MULTI_STR)
{
value_data_u8 = g_convert ((const gchar *) value_data_w,
value_data_w_size - sizeof (gunichar2) /* excl. 0 */,
"UTF8",
"UTF16",
NULL,
&value_data_u8_len,
error);
g_free (value_data_w);
if (value_data_u8 == NULL)
return FALSE;
if (value_data)
*value_data = value_data_u8;
else
g_free (value_data_u8);
if (value_data_size)
*value_data_size = value_data_u8_len + 1;
}
else
{
if (value_data)
*value_data = value_data_w;
else
g_free (value_data_w);
if (value_data_size)
*value_data_size = value_data_w_size;
}
if (value_type)
*value_type = value_type_g;
return TRUE;
}
/* A wrapper that calls either RegQueryValueExW() or
* RegLoadMUIStringW() depending on the value of the
* last argument.
* Apart from the extra argument, the function behaves
* just like RegQueryValueExW(), with a few caveats.
*/
static LSTATUS
MuiRegQueryValueExW (HKEY hKey,
LPCWSTR lpValueName,
LPDWORD lpReserved,
LPDWORD lpType,
LPBYTE lpData,
LPDWORD lpcbData,
const gunichar2 * const *mui_dll_dirs)
{
gsize dir_index;
LSTATUS result = ERROR_PATH_NOT_FOUND;
DWORD bufsize;
DWORD data_size;
PVOID old_value;
if (mui_dll_dirs == NULL)
return RegQueryValueExW (hKey, lpValueName, lpReserved, lpType, lpData, lpcbData);
bufsize = 0;
if (lpcbData != NULL)
bufsize = *lpcbData;
if (mui_dll_dirs[0] != NULL)
{
/* Optimization: check that the value actually exists,
* before we start trying different mui dll dirs
*/
result = RegQueryValueExW (hKey, lpValueName, NULL, NULL, NULL, 0);
if (result == ERROR_FILE_NOT_FOUND)
return result;
}
Wow64DisableWow64FsRedirection (&old_value);
/* Try with NULL dir first */
result = RegLoadMUIStringW (hKey,
lpValueName,
(wchar_t *) lpData,
bufsize,
&data_size,
0,
NULL);
/* Not a MUI value, load normally */
if (result == ERROR_INVALID_DATA)
{
Wow64RevertWow64FsRedirection (old_value);
return RegQueryValueExW (hKey, lpValueName, lpReserved, lpType, lpData, lpcbData);
}
for (dir_index = 0;
result == ERROR_FILE_NOT_FOUND &&
mui_dll_dirs[dir_index] != NULL;
dir_index++)
result = RegLoadMUIStringW (hKey,
lpValueName,
(wchar_t *) lpData,
bufsize,
&data_size,
0,
mui_dll_dirs[dir_index]);
Wow64RevertWow64FsRedirection (old_value);
if (lpcbData != NULL &&
result == ERROR_MORE_DATA)
*lpcbData = data_size;
if (lpType != NULL &&
result != ERROR_INVALID_DATA &&
result != ERROR_FILE_NOT_FOUND)
*lpType = REG_SZ;
return result;
}
/**
* g_win32_registry_key_get_value_w:
* @key: (in) (transfer none): a #GWin32RegistryKey
* @mui_dll_dirs: (in) (transfer none) (array zero-terminated=1) (optional): a %NULL-terminated
* array of directory names where the OS
* should look for a DLL indicated in a MUI string, if the
* DLL path in the string is not absolute
* @auto_expand: (in) %TRUE to automatically expand G_WIN32_REGISTRY_VALUE_EXPAND_STR
* to G_WIN32_REGISTRY_VALUE_STR.
* @value_name: (in) (transfer none): name of the value to get (in UTF-16).
* Empty string means the '(Default)' value.
* @value_type: (out) (optional): type of the value retrieved.
* @value_data: (out callee-allocates) (optional): contents of the value.
* @value_data_size: (out) (optional): size of the buffer pointed
* by @value_data.
* @error: (nullable): a pointer to %NULL #GError, or %NULL
*
* Get data from a value of a key. String data is guaranteed to be
* appropriately terminated and will be in UTF-16.
*
* When calling with value_data == NULL (to get data size without getting
* the data itself) remember that returned size corresponds to possibly
* unterminated string data (if value is some kind of string), because
* termination cannot be checked and fixed unless the data is retrieved
* too.
*
* When not %NULL, @mui_dll_dirs indicates that `RegLoadMUIStringW()` API
* should be used instead of the usual `RegQueryValueExW()`. This implies
* that the value being queried is of type `REG_SZ` or `REG_EXPAND_SZ` (if it is not, the function
* falls back to `RegQueryValueExW()`), and that this string must undergo special processing
* (see [`SHLoadIndirectString()` documentation](https://docs.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-shloadindirectstring) for an explanation on what
* kinds of strings are processed) to get the result.
*
* If no specific MUI DLL directories need to be used, pass
* the return value of g_win32_registry_get_os_dirs_w() as @mui_dll_dirs.
*
* @auto_expand works with @mui_dll_dirs, but only affects the processed
* string, making it somewhat useless. The unprocessed string is always expanded
* internally, if its type is `REG_EXPAND_SZ` - there is no need to enable
* @auto_expand for this to work.
*
* The API for this function changed in GLib 2.66 to add the @mui_dll_dirs argument.
*
* Returns: %TRUE on success, %FALSE on failure.
*
* Since: 2.66
**/
gboolean
g_win32_registry_key_get_value_w (GWin32RegistryKey *key,
const gunichar2 * const *mui_dll_dirs,
gboolean auto_expand,
const gunichar2 *value_name,
GWin32RegistryValueType *value_type,
gpointer *value_data,
gsize *value_data_size,
GError **error)
{
LONG status;
DWORD value_type_w;
DWORD value_type_w2;
char *req_value_data;
GWin32RegistryValueType value_type_g;
GWin32RegistryValueType value_type_g2;
DWORD req_value_data_size;
DWORD req_value_data_size2;
g_return_val_if_fail (G_IS_WIN32_REGISTRY_KEY (key), FALSE);
g_return_val_if_fail (value_name != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
/* No sense calling this functions with all of these set to NULL */
g_return_val_if_fail (value_type != NULL ||
value_data != NULL ||
value_data_size != NULL, FALSE);
req_value_data_size = 0;
status = MuiRegQueryValueExW (key->priv->handle,
value_name,
NULL,
&value_type_w,
NULL,
&req_value_data_size,
mui_dll_dirs);
if (status != ERROR_MORE_DATA && status != ERROR_SUCCESS)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_win32_error (status),
"Failed to query value '%S' for key '%S'",
value_name, g_win32_registry_key_get_path_w (key));
return FALSE;
}
value_type_g = _g_win32_registry_type_w_to_g (value_type_w);
if (value_data == NULL &&
(!auto_expand || value_type_g != G_WIN32_REGISTRY_VALUE_EXPAND_STR))
{
if (value_type)
*value_type = value_type_g;
if (value_data_size)
*value_data_size = req_value_data_size;
return TRUE;
}
req_value_data = g_malloc (req_value_data_size + sizeof (gunichar2) * 2);
req_value_data_size2 = req_value_data_size;
status = MuiRegQueryValueExW (key->priv->handle,
value_name,
NULL,
&value_type_w2,
(gpointer) req_value_data,
&req_value_data_size2,
mui_dll_dirs);
if (status != ERROR_SUCCESS)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_win32_error (status),
"Failed to query value '%S' of size %lu for key '%S'",
value_name,
req_value_data_size,
g_win32_registry_key_get_path_w (key));
g_free (req_value_data);
return FALSE;
}
value_type_g2 = _g_win32_registry_type_w_to_g (value_type_w2);
if (value_type_w != value_type_w2)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Type of value '%S' of key '%S' changed from %u to %u"
" between calls",
value_name,
g_win32_registry_key_get_path_w (key),
value_type_g, value_type_g2);
g_free (req_value_data);
return FALSE;
}
req_value_data_size = ensure_nul_termination (value_type_g,
(guint8 *) req_value_data,
req_value_data_size2);
if (value_type_g == G_WIN32_REGISTRY_VALUE_EXPAND_STR && auto_expand)
{
gsize value_data_expanded_charsize_w = 0;
gunichar2 *value_data_expanded = NULL;
if (!expand_value ((gunichar2 *) req_value_data,
value_name,
(gpointer *) &value_data_expanded,
&value_data_expanded_charsize_w,
error))
return FALSE;
g_free (req_value_data);
if (value_type)
*value_type = G_WIN32_REGISTRY_VALUE_STR;
if (value_data)
*value_data = value_data_expanded;
else
g_free (value_data_expanded);
if (value_data_size)
*value_data_size = value_data_expanded_charsize_w * sizeof (gunichar2);
return TRUE;
}
if (value_type)
*value_type = value_type_g;
if (value_data_size)
*value_data_size = req_value_data_size;
if (value_data)
*value_data = req_value_data;
else
g_free (req_value_data);
return TRUE;
}
static VOID NTAPI
key_changed (PVOID closure,
PIO_STATUS_BLOCK status_block,
ULONG reserved)
{
GWin32RegistryKey *key = G_WIN32_REGISTRY_KEY (closure);
g_free (status_block);
g_atomic_int_set (&key->priv->change_indicator, G_WIN32_KEY_CHANGED);
g_atomic_int_set (&key->priv->watch_indicator, G_WIN32_KEY_UNWATCHED);
key->priv->update_flags = G_WIN32_REGISTRY_UPDATED_NOTHING;
if (key->priv->callback)
key->priv->callback (key, key->priv->user_data);
key->priv->callback = NULL;
key->priv->user_data = NULL;
g_object_unref (key);
}
/**
* g_win32_registry_key_watch:
* @key: (in) (transfer none): a #GWin32RegistryKey
* @watch_children: (in) %TRUE also watch the children of the @key, %FALSE
* to watch the key only.
* @watch_flags: (in): specifies the types of changes to watch for.
* @callback: (in) (nullable): a function to invoke when a change occurs.
* @user_data: (in) (nullable): a pointer to pass to @callback on invocation.
* @error: (nullable): a pointer to %NULL #GError, or %NULL
*
* Puts @key under a watch.
*
* When the key changes, an APC will be queued in the current thread. The APC
* will run when the current thread enters alertable state (GLib main loop
* should do that; if you are not using it, see MSDN documentation for W32API
* calls that put thread into alertable state). When it runs, it will
* atomically switch an indicator in the @key. If a callback was specified,
* it is invoked at that point. Subsequent calls to
* g_win32_registry_key_has_changed() will return %TRUE, and the callback (if
* it was specified) will not be invoked anymore.
* Calling g_win32_registry_key_erase_change_indicator() will reset the indicator,
* and g_win32_registry_key_has_changed() will start returning %FALSE.
* To resume the watch, call g_win32_registry_key_watch_for_changes() again.
*
* Calling g_win32_registry_key_watch_for_changes() for a key that is already
* being watched is allowed and affects nothing.
*
* The fact that the key is being watched will be used internally to update
* key path (if it changes).
*
* Returns: %TRUE on success, %FALSE on failure.
*
* Since: 2.46
**/
gboolean
g_win32_registry_key_watch (GWin32RegistryKey *key,
gboolean watch_children,
GWin32RegistryKeyWatcherFlags watch_flags,
GWin32RegistryKeyWatchCallbackFunc callback,
gpointer user_data,
GError **error)
{
ULONG filter;
gboolean started_to_watch;
NTSTATUS status;
PIO_STATUS_BLOCK status_block;
g_return_val_if_fail (G_IS_WIN32_REGISTRY_KEY (key), FALSE);
filter = ((watch_flags & G_WIN32_REGISTRY_WATCH_NAME) ? REG_NOTIFY_CHANGE_NAME : 0) |
((watch_flags & G_WIN32_REGISTRY_WATCH_ATTRIBUTES) ? REG_NOTIFY_CHANGE_ATTRIBUTES : 0) |
((watch_flags & G_WIN32_REGISTRY_WATCH_VALUES) ? REG_NOTIFY_CHANGE_LAST_SET : 0) |
((watch_flags & G_WIN32_REGISTRY_WATCH_SECURITY) ? REG_NOTIFY_CHANGE_SECURITY : 0);
if (filter == 0)
{
g_critical ("No supported flags specified in watch_flags (%x)", (guint) watch_flags);
return FALSE;
}
if (g_once_init_enter (&nt_notify_change_multiple_keys))
{
NtNotifyChangeMultipleKeysFunc func;
HMODULE ntdll = GetModuleHandleW (L"ntdll.dll");
if (ntdll != NULL)
func = (NtNotifyChangeMultipleKeysFunc) GetProcAddress (ntdll, "NtNotifyChangeMultipleKeys");
else
func = NULL;
g_once_init_leave (&nt_notify_change_multiple_keys, func);
}
if (nt_notify_change_multiple_keys== NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
"Couldn't get NtNotifyChangeMultipleKeys() from ntdll");
return FALSE;
}
started_to_watch =
g_atomic_int_compare_and_exchange (&key->priv->watch_indicator,
G_WIN32_KEY_UNWATCHED,
G_WIN32_KEY_WATCHED);
if (!started_to_watch)
return TRUE;
key->priv->callback = callback;
key->priv->user_data = user_data;
g_atomic_int_set (&key->priv->change_indicator, G_WIN32_KEY_UNCHANGED);
/* Keep it alive until APC is called */
g_object_ref (key);
status_block = g_malloc (sizeof (IO_STATUS_BLOCK));
status = nt_notify_change_multiple_keys (key->priv->handle,
0,
NULL,
NULL,
key_changed,
(PVOID) key,
status_block,
filter,
watch_children,
NULL,
0,
TRUE);
g_assert (status != STATUS_SUCCESS);
if (status == STATUS_PENDING)
return TRUE;
g_atomic_int_set (&key->priv->change_indicator, G_WIN32_KEY_UNKNOWN);
g_atomic_int_set (&key->priv->watch_indicator, G_WIN32_KEY_UNWATCHED);
g_object_unref (key);
g_free (status_block);
return FALSE;
}
/**
* g_win32_registry_key_erase_change_indicator:
* @key: (in) (transfer none): a #GWin32RegistryKey
*
* Erases change indicator of the @key.
*
* Subsequent calls to g_win32_registry_key_has_changed() will return %FALSE
* until the key is put on watch again by calling
* g_win32_registry_key_watch() again.
*
* Since: 2.46
*/
void
g_win32_registry_key_erase_change_indicator (GWin32RegistryKey *key)
{
g_return_if_fail (G_IS_WIN32_REGISTRY_KEY (key));
g_atomic_int_set (&key->priv->change_indicator, G_WIN32_KEY_UNKNOWN);
}
/**
* g_win32_registry_key_has_changed:
* @key: (in) (transfer none): a #GWin32RegistryKey
*
* Check the @key's status indicator.
*
* Returns: %TRUE if the @key was put under watch at some point and has changed
* since then, %FALSE if it either wasn't changed or wasn't watched at all.
*
* Since: 2.46
*/
gboolean
g_win32_registry_key_has_changed (GWin32RegistryKey *key)
{
gint changed;
g_return_val_if_fail (G_IS_WIN32_REGISTRY_KEY (key), FALSE);
changed = g_atomic_int_get (&key->priv->change_indicator);
return (changed == G_WIN32_KEY_CHANGED ? TRUE : FALSE);
}
static void
g_win32_registry_key_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GWin32RegistryKey *key = G_WIN32_REGISTRY_KEY (object);
switch (prop_id)
{
case PROP_PATH:
g_value_set_string (value, g_win32_registry_key_get_path (key));
break;
case PROP_PATH_UTF16:
g_value_set_pointer (value, (gpointer) g_win32_registry_key_get_path_w (key));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
g_win32_registry_key_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GWin32RegistryKey *key = G_WIN32_REGISTRY_KEY (object);
GWin32RegistryKeyPrivate *priv = key->priv;
const gchar *path;
gunichar2 *path_w;
switch (prop_id)
{
case PROP_PATH:
path = g_value_get_string (value);
if (path == NULL)
break;
path_w = g_utf8_to_utf16 (path, -1, NULL, NULL, NULL);
if (path_w == NULL)
break;
/* Construct only */
g_assert (priv->absolute_path_w == NULL);
g_assert (priv->absolute_path == NULL);
priv->absolute_path_w = path_w;
priv->absolute_path = g_value_dup_string (value);
break;
case PROP_PATH_UTF16:
path_w = (gunichar2 *) g_value_get_pointer (value);
if (path_w == NULL)
break;
/* Construct only */
g_assert (priv->absolute_path_w == NULL);
priv->absolute_path_w = g_wcsdup (path_w, -1);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
g_win32_registry_key_class_init (GWin32RegistryKeyClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->dispose = g_win32_registry_key_dispose;
gobject_class->set_property = g_win32_registry_key_set_property;
gobject_class->get_property = g_win32_registry_key_get_property;
/**
* GWin32RegistryKey:path:
*
* A path to the key in the registry, in UTF-8.
*
* Since: 2.46
*/
g_object_class_install_property (gobject_class,
PROP_PATH,
g_param_spec_string ("path",
"Path",
"Path to the key in the registry",
NULL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
/**
* GWin32RegistryKey:path-utf16:
*
* A path to the key in the registry, in UTF-16.
*
* Since: 2.46
*/
g_object_class_install_property (gobject_class,
PROP_PATH_UTF16,
g_param_spec_pointer ("path-utf16",
"Path (UTF-16)",
"Path to the key in the registry, in UTF-16",
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
}
static void
g_win32_registry_key_init (GWin32RegistryKey *key)
{
key->priv = g_win32_registry_key_get_instance_private (key);
key->priv->change_indicator = G_WIN32_KEY_UNKNOWN;
}