| /* GIO - GLib Input, Output and Streaming Library |
| * |
| * Copyright (C) 2014 Руслан Ижбулатов <lrn1986@gmail.com> |
| * |
| * 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 <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) |
| |
| /** |
| * GWin32RegistryKey: |
| * |
| * `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 [iface@Gio.Initable] interface, so if it |
| * is manually constructed by e.g. [ctor@GObject.Object.new] you must call |
| * [method@Gio.Initable.init] and check the results before using the object. |
| * This is done automatically in [ctor@Gio.Win32RegistryKey.new] and |
| * [method@Gio.Win32RegistryKey.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 the current user’s context (it is not possible to |
| * access the 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. |
| * |
| * A full key name (excluding the pre-defined ancestor’s name) can’t exceed |
| * 255 UTF-16 characters, give or take. A 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_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", NULL, NULL, |
| 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", NULL, NULL, |
| 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; |
| } |