/* GIO - GLib Input, Output and Streaming Library * * Copyright (C) 2014 Руслан Ижбулатов * * SPDX-License-Identifier: LGPL-2.1-or-later * * 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 . * */ #include "config.h" #include "ginitable.h" #include "gwin32registrykey.h" #include #ifdef _MSC_VER #pragma warning ( disable:4005 ) #endif #include #include #include #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(): * |[ * // 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(): * |[ * // 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_pointer (&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_pointer (&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_pointer (&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_pointer (&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_pointer (&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_pointer (&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); gpointer user_data; GWin32RegistryKeyWatchCallbackFunc callback; callback = g_steal_pointer (&key->priv->callback); user_data = g_steal_pointer (&key->priv->user_data); 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 (callback) callback (key, user_data); 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_pointer (&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_pointer (&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); if (status == STATUS_PENDING || status == STATUS_SUCCESS) 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; }