2014-08-25 05:38:35 +02:00
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright ( C ) 2014 Р у с л а н И ж б у л а т о в < lrn1986 @ gmail . com >
*
2022-05-18 10:12:45 +02:00
* SPDX - License - Identifier : LGPL - 2.1 - or - later
*
2014-08-25 05:38:35 +02:00
* 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
2017-05-27 18:21:30 +02:00
* version 2.1 of the License , or ( at your option ) any later version .
2014-08-25 05:38:35 +02:00
*
* 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>
2015-05-05 04:09:10 +02:00
# ifdef _MSC_VER
# pragma warning ( disable:4005 )
# endif
2014-08-25 05:38:35 +02:00
# 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
2015-05-05 04:09:10 +02:00
# if !defined (__OBJECT_ATTRIBUTES_DEFINED) && defined (__MINGW32_)
2014-08-25 05:38:35 +02:00
# 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
2015-05-05 04:09:10 +02:00
# if !defined (__UNICODE_STRING_DEFINED) && defined (__MINGW32_)
2014-08-25 05:38:35 +02:00
# 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 ;
2015-05-05 04:09:10 +02:00
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 ) ;
2014-08-25 05:38:35 +02:00
typedef NTSTATUS
2017-04-14 08:09:35 +02:00
( 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 ) ;
2014-08-25 05:38:35 +02:00
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 ;
2021-02-04 14:50:37 +01:00
static gsize
g_utf16_len ( const gunichar2 * str )
{
gsize result ;
for ( result = 0 ; str [ 0 ] ! = 0 ; str + + , result + + )
;
return result ;
}
2014-08-25 05:38:35 +02:00
static gunichar2 *
2021-02-04 14:50:37 +01:00
g_wcsdup ( const gunichar2 * str , gssize str_len )
2014-08-25 05:38:35 +02:00
{
2021-02-04 14:50:37 +01:00
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 ) ;
2014-08-25 05:38:35 +02:00
}
/**
* 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 ( )
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
* */
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 .
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
* */
void
g_win32_registry_subkey_iter_free ( GWin32RegistrySubkeyIter * iter )
{
2015-05-05 04:09:10 +02:00
g_return_if_fail ( iter ! = NULL ) ;
2014-08-25 05:38:35 +02:00
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 .
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
* */
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 ( ) .
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
* */
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 )
2021-02-04 14:37:56 +01:00
new_iter - > value_data = g_memdup2 ( iter - > value_data , iter - > value_data_size ) ;
2014-08-25 05:38:35 +02:00
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 )
2021-02-04 14:37:56 +01:00
new_iter - > value_data_expanded_u8 = g_memdup2 ( iter - > value_data_expanded_u8 ,
iter - > value_data_expanded_charsize ) ;
2014-08-25 05:38:35 +02:00
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 .
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
* */
void
g_win32_registry_value_iter_free ( GWin32RegistryValueIter * iter )
{
2015-05-05 04:09:10 +02:00
g_return_if_fail ( iter ! = NULL ) ;
2014-08-25 05:38:35 +02:00
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 .
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
* */
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 )
/**
2023-11-02 17:29:00 +01:00
* GWin32RegistryKey :
2014-08-25 05:38:35 +02:00
*
2023-11-02 17:29:00 +01:00
* ` GWin32RegistryKey ` represents a single Windows Registry key .
2014-08-25 05:38:35 +02:00
*
2023-11-02 17:29:00 +01:00
* ` GWin32RegistryKey ` is used by a number of helper functions that read
2014-08-25 05:38:35 +02:00
* 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 .
*
2023-11-02 17:29:00 +01:00
* ` 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 ` .
2014-08-25 05:38:35 +02:00
*
* 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 .
*
2023-11-02 17:29:00 +01:00
* All functions operate in the current user ’ s context ( it is not possible to
* access the registry tree of a different user ) .
2014-08-25 05:38:35 +02:00
*
2023-11-02 17:29:00 +01:00
* 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 ` \ \ ` .
2014-08-25 05:38:35 +02:00
*
* Key and value names are not case sensitive .
*
2023-11-02 17:29:00 +01:00
* 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
2014-08-25 05:38:35 +02:00
* 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 )
2017-04-28 13:29:44 +02:00
* @ error : ( inout ) ( optional ) ( nullable ) : a pointer to a % NULL # GError , or % NULL
2014-08-25 05:38:35 +02:00
*
* 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 ;
2015-05-05 04:09:10 +02:00
g_return_val_if_fail ( G_IS_WIN32_REGISTRY_KEY ( initable ) , FALSE ) ;
g_return_val_if_fail ( error = = NULL | | * error = = NULL , FALSE ) ;
2014-08-25 05:38:35 +02:00
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
2017-04-28 13:29:44 +02:00
* @ error : ( inout ) ( optional ) ( nullable ) : a pointer to a % NULL # GError , or % NULL
2014-08-25 05:38:35 +02:00
*
* 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
2017-04-28 13:29:44 +02:00
* @ error : ( inout ) ( optional ) ( nullable ) : a pointer to a % NULL # GError , or % NULL
2014-08-25 05:38:35 +02:00
*
* 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
2017-04-28 13:29:44 +02:00
* @ error : ( inout ) ( optional ) ( nullable ) : a pointer to % NULL # GError , or % NULL
2014-08-25 05:38:35 +02:00
*
* 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 .
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
* */
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 .
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
* */
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
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
* */
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 .
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
* */
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 .
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
* */
gboolean
g_win32_registry_subkey_iter_get_name_w ( GWin32RegistrySubkeyIter * iter ,
2019-12-29 12:35:49 +01:00
const gunichar2 * * subkey_name ,
2014-08-25 05:38:35 +02:00
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 .
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
* */
gboolean
g_win32_registry_subkey_iter_get_name ( GWin32RegistrySubkeyIter * iter ,
2019-12-29 12:35:49 +01:00
const gchar * * subkey_name ,
2014-08-25 05:38:35 +02:00
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 ) ;
2020-10-01 18:16:34 +02:00
if ( iter - > subkey_name_u8 = = NULL )
return FALSE ;
* subkey_name = iter - > subkey_name_u8 ;
2014-08-25 05:38:35 +02:00
2020-10-01 18:16:34 +02:00
if ( subkey_name_len )
* subkey_name_len = subkey_name_len_glong ;
return TRUE ;
2014-08-25 05:38:35 +02:00
}
/**
* 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 .
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
* */
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 1 MB ! ) . 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 .
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
* */
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
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
* */
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 .
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
* */
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 .
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
* */
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 .
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
* */
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 .
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
* */
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
2015-09-21 12:27:07 +02:00
* G_WIN32_REGISTRY_VALUE_STR
2014-08-25 05:38:35 +02:00
* @ value_data : ( out callee - allocates ) ( optional ) ( transfer none ) : Pointer to a
2015-09-21 12:27:07 +02:00
* 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 ) .
2014-08-25 05:38:35 +02:00
* % 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 .
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
* */
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
2015-09-21 12:27:07 +02:00
* @ auto_expand : ( in ) : % TRUE to automatically expand G_WIN32_REGISTRY_VALUE_EXPAND_STR to
* G_WIN32_REGISTRY_VALUE_STR
2014-08-25 05:38:35 +02:00
* @ value_data : ( out callee - allocates ) ( optional ) ( transfer none ) : Pointer to a
2015-09-21 12:27:07 +02:00
* location to store the data of the value ( in UTF - 8 , if it ' s a string )
2014-08-25 05:38:35 +02:00
* @ value_data_size : ( out ) ( optional ) : Pointer to a location to store the length
2015-09-21 12:27:07 +02:00
* of @ value_data , in bytes ( including any NUL - terminators , if it ' s a string ) .
* % NULL if length is not needed
2014-08-25 05:38:35 +02:00
* @ 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 .
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
* */
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 )
{
2023-09-12 08:59:52 +02:00
if ( g_once_init_enter_pointer ( & nt_query_key ) )
2014-08-25 05:38:35 +02:00
{
NtQueryKeyFunc func ;
HMODULE ntdll = GetModuleHandleW ( L " ntdll.dll " ) ;
if ( ntdll ! = NULL )
func = ( NtQueryKeyFunc ) GetProcAddress ( ntdll , " NtQueryKey " ) ;
else
func = NULL ;
2023-09-12 08:59:52 +02:00
g_once_init_leave_pointer ( & nt_query_key , func ) ;
2014-08-25 05:38:35 +02:00
}
/* 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 )
2015-05-05 04:09:10 +02:00
_g_win32_registry_key_reread_kernel ( key , buf ) ;
2014-08-25 05:38:35 +02:00
else
2015-05-05 04:09:10 +02:00
_g_win32_registry_key_reread_user ( key , buf ) ;
2014-08-25 05:38:35 +02:00
}
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.
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
* */
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 )
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
* */
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 ;
}
2020-01-27 00:28:19 +01:00
/**
* 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 ;
2023-09-12 08:59:52 +02:00
if ( g_once_init_enter_pointer ( & mui_os_dirs ) )
2020-01-27 00:28:19 +01:00
{
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 ;
2023-09-12 08:59:52 +02:00
g_once_init_leave_pointer ( & mui_os_dirs , new_mui_os_dirs ) ;
2020-01-27 00:28:19 +01:00
}
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 ;
2023-09-12 08:59:52 +02:00
if ( g_once_init_enter_pointer ( & mui_os_dirs ) )
2020-01-27 00:28:19 +01:00
{
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 ;
2020-01-27 00:42:31 +01:00
else
g_critical ( " Failed to convert to a system directory #%zu to UTF-8 " , array_index ) ;
2020-01-27 00:28:19 +01:00
}
2023-09-12 08:59:52 +02:00
g_once_init_leave_pointer ( & mui_os_dirs , new_mui_os_dirs ) ;
2020-01-27 00:28:19 +01:00
}
return ( const gchar * const * ) mui_os_dirs ;
}
2014-08-25 05:38:35 +02:00
/**
* g_win32_registry_key_get_value :
* @ key : ( in ) ( transfer none ) : a # GWin32RegistryKey
2020-01-27 00:28:19 +01:00
* @ 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
2014-08-25 05:38:35 +02:00
* @ 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.
*
2020-01-27 00:28:19 +01:00
* 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 .
*
2014-08-25 05:38:35 +02:00
* Returns : % TRUE on success , % FALSE on failure .
*
2020-01-27 00:28:19 +01:00
* Since : 2.66
2014-08-25 05:38:35 +02:00
* */
gboolean
g_win32_registry_key_get_value ( GWin32RegistryKey * key ,
2020-01-27 00:28:19 +01:00
const gchar * const * mui_dll_dirs ,
2014-08-25 05:38:35 +02:00
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 ;
2020-01-27 00:28:19 +01:00
gsize mui_dll_dirs_count ;
gunichar2 * * mui_dll_dirs_utf16 ;
const gchar * const * mui_os_dirs ;
2014-08-25 05:38:35 +02:00
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 ;
2020-01-27 00:28:19 +01:00
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 )
{
2020-01-27 00:42:31 +01:00
gsize i ;
2020-01-27 00:28:19 +01:00
mui_dll_dirs_count = g_strv_length ( ( gchar * * ) mui_dll_dirs ) ;
2020-01-27 00:42:31 +01:00
mui_dll_dirs_utf16 = g_new0 ( gunichar2 * , mui_dll_dirs_count + 1 ) ;
2020-01-27 00:28:19 +01:00
2020-01-27 00:42:31 +01:00
for ( i = 0 ; mui_dll_dirs [ i ] ! = NULL ; i + + )
2020-01-27 00:28:19 +01:00
{
2020-01-27 00:42:31 +01:00
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 ;
2020-01-27 00:28:19 +01:00
}
}
else if ( mui_dll_dirs ! = NULL & &
mui_dll_dirs = = mui_os_dirs )
{
mui_dll_dirs_utf16 = ( gunichar2 * * ) g_win32_registry_get_os_dirs_w ( ) ;
}
2014-08-25 05:38:35 +02:00
result = g_win32_registry_key_get_value_w ( key ,
2020-01-27 00:28:19 +01:00
( const gunichar2 * const * ) mui_dll_dirs_utf16 ,
2014-08-25 05:38:35 +02:00
auto_expand ,
value_name_w ,
& value_type_g ,
& value_data_w ,
& value_data_w_size ,
error ) ;
g_free ( value_name_w ) ;
2020-01-27 00:28:19 +01:00
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 ) ;
}
2014-08-25 05:38:35 +02:00
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 ;
}
2020-01-27 00:28:19 +01:00
/* 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 ;
}
2014-08-25 05:38:35 +02:00
/**
* g_win32_registry_key_get_value_w :
* @ key : ( in ) ( transfer none ) : a # GWin32RegistryKey
2020-01-27 00:28:19 +01:00
* @ 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
2014-08-25 05:38:35 +02:00
* @ 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
2020-06-12 15:02:30 +02:00
* termination cannot be checked and fixed unless the data is retrieved
2014-08-25 05:38:35 +02:00
* too .
*
2020-01-27 00:28:19 +01:00
* 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 .
*
2014-08-25 05:38:35 +02:00
* Returns : % TRUE on success , % FALSE on failure .
*
2020-01-27 00:28:19 +01:00
* Since : 2.66
2014-08-25 05:38:35 +02:00
* */
gboolean
g_win32_registry_key_get_value_w ( GWin32RegistryKey * key ,
2020-01-27 00:28:19 +01:00
const gunichar2 * const * mui_dll_dirs ,
2014-08-25 05:38:35 +02:00
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 ;
2020-01-27 00:28:19 +01:00
status = MuiRegQueryValueExW ( key - > priv - > handle ,
value_name ,
NULL ,
& value_type_w ,
NULL ,
& req_value_data_size ,
mui_dll_dirs ) ;
2014-08-25 05:38:35 +02:00
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 ;
2020-01-27 00:28:19 +01:00
status = MuiRegQueryValueExW ( key - > priv - > handle ,
value_name ,
NULL ,
& value_type_w2 ,
( gpointer ) req_value_data ,
& req_value_data_size2 ,
mui_dll_dirs ) ;
2014-08-25 05:38:35 +02:00
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 ) ;
2021-07-31 12:57:44 +02:00
gpointer user_data ;
GWin32RegistryKeyWatchCallbackFunc callback ;
callback = g_steal_pointer ( & key - > priv - > callback ) ;
user_data = g_steal_pointer ( & key - > priv - > user_data ) ;
2014-08-25 05:38:35 +02:00
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 ;
2021-07-31 12:57:44 +02:00
if ( callback )
callback ( key , user_data ) ;
2014-08-25 05:38:35 +02:00
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 .
2015-09-21 12:27:07 +02:00
* @ watch_flags : ( in ) : specifies the types of changes to watch for .
2014-08-25 05:38:35 +02:00
* @ 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 .
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
* */
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 ;
}
2023-09-12 08:59:52 +02:00
if ( g_once_init_enter_pointer ( & nt_notify_change_multiple_keys ) )
2014-08-25 05:38:35 +02:00
{
NtNotifyChangeMultipleKeysFunc func ;
2018-08-15 22:25:08 +02:00
HMODULE ntdll = GetModuleHandleW ( L " ntdll.dll " ) ;
2014-08-25 05:38:35 +02:00
if ( ntdll ! = NULL )
func = ( NtNotifyChangeMultipleKeysFunc ) GetProcAddress ( ntdll , " NtNotifyChangeMultipleKeys " ) ;
else
func = NULL ;
2023-09-12 08:59:52 +02:00
g_once_init_leave_pointer ( & nt_notify_change_multiple_keys , func ) ;
2014-08-25 05:38:35 +02:00
}
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 ) ;
2021-07-31 12:50:12 +02:00
if ( status = = STATUS_PENDING | | status = = STATUS_SUCCESS )
2014-08-25 05:38:35 +02:00
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 .
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
*/
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 .
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
*/
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 ;
2020-06-04 20:06:36 +02:00
/* Construct only */
g_assert ( priv - > absolute_path_w = = NULL ) ;
g_assert ( priv - > absolute_path = = NULL ) ;
2014-08-25 05:38:35 +02:00
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 ;
2020-06-04 20:06:36 +02:00
/* Construct only */
g_assert ( priv - > absolute_path_w = = NULL ) ;
2014-08-25 05:38:35 +02:00
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.
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
*/
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.
*
2015-05-05 04:12:25 +02:00
* Since : 2.46
2014-08-25 05:38:35 +02:00
*/
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 ;
}