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