From 27adf49cce42616882d969ac50c5f2db2607af63 Mon Sep 17 00:00:00 2001 From: AN Long Date: Sun, 16 Nov 2025 23:37:29 +0900 Subject: [PATCH] gh-90012: Add winreg.GetValue --- Doc/library/winreg.rst | 113 ++++++++++++++++++ Lib/test/test_winreg.py | 35 ++++++ ...5-11-16-22-53-19.gh-issue-90012.belDmD.rst | 2 + PC/clinic/winreg.c.h | 98 ++++++++++++++- PC/winreg.c | 108 +++++++++++++++++ 5 files changed, 355 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2025-11-16-22-53-19.gh-issue-90012.belDmD.rst diff --git a/Doc/library/winreg.rst b/Doc/library/winreg.rst index b150c53735d634..504517c0258f57 100644 --- a/Doc/library/winreg.rst +++ b/Doc/library/winreg.rst @@ -420,6 +420,42 @@ This module offers the following functions: .. audit-event:: winreg.QueryValue key,sub_key,value_name winreg.QueryValueEx +.. function:: GetValue(key, sub_key, value_name, flags=RRF_RT_ANY) + + Retrieves the type and data for a specified registry value without requiring + the key to be opened first. + + *key* is an already open key, or one of the predefined + :ref:`HKEY_* constants `. + + *sub_key* is a string that holds the name of the subkey with which the value + is associated. If this parameter is ``None`` or empty, the value will be read + from the key specified by *key*. + + *value_name* is a string indicating the value to query, or ``None`` for the + default value of the subkey. + + *flags* is an integer that specifies the restrictions on the value type to + be queried (see :ref:`RRF_* constants `). Default is + ``RRF_RT_ANY``. + + The result is a tuple of 2 items: + + +-------+-----------------------------------------+ + | Index | Meaning | + +=======+=========================================+ + | ``0`` | The value of the registry item. | + +-------+-----------------------------------------+ + | ``1`` | An integer giving the registry type for | + | | this value (see table in docs for | + | | :meth:`SetValueEx`) | + +-------+-----------------------------------------+ + + .. audit-event:: winreg.GetValue key,sub_key,value_name,flags winreg.GetValue + + .. versionadded:: next + + .. function:: SaveKey(key, file_name) Saves the specified key, and all its subkeys to the specified file. @@ -751,6 +787,83 @@ For more information, see `Registry Value Types A null-terminated string. +.. _rrf-constants: + +RRF_* Constants ++++++++++++++++ + +Registry value retrieval flags used with :func:`GetValue`. + +.. data:: RRF_RT_ANY + + No type restriction. All registry value types will be returned. + + +.. data:: RRF_RT_DWORD + + Restrict query to 32-bit values. + + +.. data:: RRF_RT_QWORD + + Restrict query to 64-bit values. + + +.. data:: RRF_RT_REG_BINARY + + Restrict query to binary data (REG_BINARY). + + +.. data:: RRF_RT_REG_DWORD + + Restrict query to REG_DWORD values. + + +.. data:: RRF_RT_REG_QWORD + + Restrict query to REG_QWORD values. + + +.. data:: RRF_RT_REG_EXPAND_SZ + + Restrict query to expandable strings (REG_EXPAND_SZ). + + +.. data:: RRF_RT_REG_MULTI_SZ + + Restrict query to multi-strings (REG_MULTI_SZ). + + +.. data:: RRF_RT_REG_NONE + + Restrict query to null values (REG_NONE). + + +.. data:: RRF_RT_REG_SZ + + Restrict query to null-terminated strings (REG_SZ). + + +.. data:: RRF_NOEXPAND + + Do not automatically expand environment variables in REG_EXPAND_SZ values. + + +.. data:: RRF_ZEROONFAILURE + + Set buffer contents to zeroes on failure. + + +.. data:: RRF_SUBKEY_WOW6464KEY + + Query the 64-bit registry view on 64-bit Windows. + + +.. data:: RRF_SUBKEY_WOW6432KEY + + Query the 32-bit registry view on 64-bit Windows. + + .. _handle-object: Registry Handle Objects diff --git a/Lib/test/test_winreg.py b/Lib/test/test_winreg.py index 733d30b3922d35..8b97d28caf06f2 100644 --- a/Lib/test/test_winreg.py +++ b/Lib/test/test_winreg.py @@ -559,6 +559,41 @@ def test_delete_tree(self): with self.assertRaises(OSError): OpenKey(HKEY_CURRENT_USER, test_key_name) + def test_getvalue(self): + test_subkey = test_key_name + "\\GetValueTest" + key = CreateKey(HKEY_CURRENT_USER, test_subkey) + self.addCleanup(CloseKey, key) + self.addCleanup(DeleteTree, HKEY_CURRENT_USER, test_key_name) + + SetValueEx(key, "test_string", 0, REG_SZ, "Hello World") + SetValueEx(key, "test_dword", 0, REG_DWORD, 12345) + SetValueEx(key, "test_binary", 0, REG_BINARY, b"binary_data") + SetValueEx(key, None, 0, REG_SZ, "Default Value") + + result, typ = GetValue(HKEY_CURRENT_USER, test_subkey, "test_string") + self.assertEqual(result, "Hello World") + self.assertEqual(typ, REG_SZ) + + result, typ = GetValue(HKEY_CURRENT_USER, test_subkey, "test_dword") + self.assertEqual(result, 12345) + self.assertEqual(typ, REG_DWORD) + + result, typ = GetValue(HKEY_CURRENT_USER, test_subkey, "test_binary") + self.assertEqual(result, b"binary_data") + self.assertEqual(typ, REG_BINARY) + + result, typ = GetValue(HKEY_CURRENT_USER, test_subkey, None) + self.assertEqual(result, "Default Value") + self.assertEqual(typ, REG_SZ) + + result, typ = GetValue(HKEY_CURRENT_USER, test_subkey, "") + self.assertEqual(result, "Default Value") + self.assertEqual(typ, REG_SZ) + + result, typ = GetValue(HKEY_CURRENT_USER, test_subkey, "test_string", RRF_RT_REG_SZ) + self.assertEqual(result, "Hello World") + self.assertEqual(typ, REG_SZ) + if __name__ == "__main__": if not REMOTE_NAME: diff --git a/Misc/NEWS.d/next/Library/2025-11-16-22-53-19.gh-issue-90012.belDmD.rst b/Misc/NEWS.d/next/Library/2025-11-16-22-53-19.gh-issue-90012.belDmD.rst new file mode 100644 index 00000000000000..9c12cf69b2b93e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-11-16-22-53-19.gh-issue-90012.belDmD.rst @@ -0,0 +1,2 @@ +Add :func:`winreg.GetValue` function to retrieve registry values without opening +keys first. Includes RRF_* constants for type restriction flags. diff --git a/PC/clinic/winreg.c.h b/PC/clinic/winreg.c.h index d76a8d8aef8cb8..7260c30915fe8a 100644 --- a/PC/clinic/winreg.c.h +++ b/PC/clinic/winreg.c.h @@ -1697,6 +1697,98 @@ winreg_DeleteTree(PyObject *module, PyObject *const *args, Py_ssize_t nargs) #if (defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM) || defined(MS_WINDOWS_GAMES)) && (defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM)) +PyDoc_STRVAR(winreg_GetValue__doc__, +"GetValue($module, key, sub_key, name, flags=winreg.RRF_RT_ANY, /)\n" +"--\n" +"\n" +"Retrieves the type and data for the specified registry value.\n" +"\n" +" key\n" +" An already open key, or any one of the predefined HKEY_* constants.\n" +" sub_key\n" +" A string that names the subkey with which the value is associated.\n" +" If this parameter is None or empty, the value will be read from key.\n" +" name\n" +" A string indicating the value to query.\n" +" flags\n" +" Restrict the data type of value to be queried.\n" +"\n" +"Behaves mostly like QueryValueEx(), but you needn\'t OpenKey() and CloseKey()\n" +"if the key is any one of the predefined HKEY_* constants.\n" +"\n" +"The return value is a tuple of the value and the type_id."); + +#define WINREG_GETVALUE_METHODDEF \ + {"GetValue", _PyCFunction_CAST(winreg_GetValue), METH_FASTCALL, winreg_GetValue__doc__}, + +static PyObject * +winreg_GetValue_impl(PyObject *module, HKEY key, const wchar_t *sub_key, + const wchar_t *name, int flags); + +static PyObject * +winreg_GetValue(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + HKEY key; + const wchar_t *sub_key = NULL; + const wchar_t *name = NULL; + int flags = RRF_RT_ANY; + + if (!_PyArg_CheckPositional("GetValue", nargs, 3, 4)) { + goto exit; + } + if (!clinic_HKEY_converter(_PyModule_GetState(module), args[0], &key)) { + goto exit; + } + if (args[1] == Py_None) { + sub_key = NULL; + } + else if (PyUnicode_Check(args[1])) { + sub_key = PyUnicode_AsWideCharString(args[1], NULL); + if (sub_key == NULL) { + goto exit; + } + } + else { + _PyArg_BadArgument("GetValue", "argument 2", "str or None", args[1]); + goto exit; + } + if (args[2] == Py_None) { + name = NULL; + } + else if (PyUnicode_Check(args[2])) { + name = PyUnicode_AsWideCharString(args[2], NULL); + if (name == NULL) { + goto exit; + } + } + else { + _PyArg_BadArgument("GetValue", "argument 3", "str or None", args[2]); + goto exit; + } + if (nargs < 4) { + goto skip_optional; + } + flags = PyLong_AsInt(args[3]); + if (flags == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional: + return_value = winreg_GetValue_impl(module, key, sub_key, name, flags); + +exit: + /* Cleanup for sub_key */ + PyMem_Free((void *)sub_key); + /* Cleanup for name */ + PyMem_Free((void *)name); + + return return_value; +} + +#endif /* (defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM) || defined(MS_WINDOWS_GAMES)) && (defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM)) */ + +#if (defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM) || defined(MS_WINDOWS_GAMES)) && (defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM)) + PyDoc_STRVAR(winreg_QueryReflectionKey__doc__, "QueryReflectionKey($module, key, /)\n" "--\n" @@ -1839,7 +1931,11 @@ winreg_QueryReflectionKey(PyObject *module, PyObject *arg) #define WINREG_DELETETREE_METHODDEF #endif /* !defined(WINREG_DELETETREE_METHODDEF) */ +#ifndef WINREG_GETVALUE_METHODDEF + #define WINREG_GETVALUE_METHODDEF +#endif /* !defined(WINREG_GETVALUE_METHODDEF) */ + #ifndef WINREG_QUERYREFLECTIONKEY_METHODDEF #define WINREG_QUERYREFLECTIONKEY_METHODDEF #endif /* !defined(WINREG_QUERYREFLECTIONKEY_METHODDEF) */ -/*[clinic end generated code: output=ce7e8e38884851fb input=a9049054013a1b77]*/ +/*[clinic end generated code: output=2d216be22c6219ba input=a9049054013a1b77]*/ diff --git a/PC/winreg.c b/PC/winreg.c index c1be920fc1d92f..ba3c6719285994 100644 --- a/PC/winreg.c +++ b/PC/winreg.c @@ -68,6 +68,8 @@ PyDoc_STRVAR(module_doc, " specified key in the registry.\n" "QueryValueEx() - Retrieves the type and data for a specified value name\n" " associated with an open registry key.\n" +"GetValue() - Retrieves the type and data for a specified registry value\n" +" without requiring the key to be opened first.\n" "QueryInfoKey() - Returns information about the specified key.\n" "SaveKey() - Saves the specified key, and all its subkeys a file.\n" "SetValue() - Associates a value with a specified key.\n" @@ -2032,6 +2034,97 @@ winreg_DeleteTree_impl(PyObject *module, HKEY key, const wchar_t *sub_key) Py_RETURN_NONE; } +/*[clinic input] +winreg.GetValue + + key: HKEY + An already open key, or any one of the predefined HKEY_* constants. + sub_key: Py_UNICODE(accept={str, NoneType}) + A string that names the subkey with which the value is associated. + If this parameter is None or empty, the value will be read from key. + name: Py_UNICODE(accept={str, NoneType}) + A string indicating the value to query. + flags: int(c_default='RRF_RT_ANY') = winreg.RRF_RT_ANY + Restrict the data type of value to be queried. + / + +Retrieves the type and data for the specified registry value. + +Behaves mostly like QueryValueEx(), but you needn't OpenKey() and CloseKey() +if the key is any one of the predefined HKEY_* constants. + +The return value is a tuple of the value and the type_id. +[clinic start generated code]*/ + +static PyObject * +winreg_GetValue_impl(PyObject *module, HKEY key, const wchar_t *sub_key, + const wchar_t *name, int flags) +/*[clinic end generated code: output=31668fd98e5cd5dc input=9f879d56439779e9]*/ +{ + LONG rc; + BYTE *retBuf, *tmp; + DWORD bufSize = 0, retSize; + DWORD typ; + PyObject *obData; + PyObject *result; + + if (PySys_Audit("winreg.GetValue", "nuui", + (Py_ssize_t)key, sub_key, name, flags) < 0) { + return NULL; + } + + /* First call to get the required buffer size */ + Py_BEGIN_ALLOW_THREADS + rc = RegGetValueW(key, sub_key, name, flags, &typ, NULL, &bufSize); + Py_END_ALLOW_THREADS + + if (rc == ERROR_MORE_DATA) { + bufSize = 256; + } + else if (rc != ERROR_SUCCESS) { + return PyErr_SetFromWindowsErrWithFunction(rc, "RegGetValue"); + } + + retBuf = (BYTE *)PyMem_Malloc(bufSize); + if (retBuf == NULL) { + return PyErr_NoMemory(); + } + + /* Second call to get the actual data */ + while (1) { + retSize = bufSize; + Py_BEGIN_ALLOW_THREADS + rc = RegGetValueW(key, sub_key, name, flags, &typ, + (BYTE *)retBuf, &retSize); + Py_END_ALLOW_THREADS + if (rc != ERROR_MORE_DATA) { + break; + } + + bufSize *= 2; + tmp = (char *) PyMem_Realloc(retBuf, bufSize); + if (tmp == NULL) { + PyMem_Free(retBuf); + return PyErr_NoMemory(); + } + retBuf = tmp; + } + + if (rc != ERROR_SUCCESS) { + PyMem_Free(retBuf); + return PyErr_SetFromWindowsErrWithFunction(rc, "RegGetValue"); + } + + obData = Reg2Py(retBuf, retSize, typ); + PyMem_Free(retBuf); + if (obData == NULL) { + return NULL; + } + result = Py_BuildValue("Oi", obData, typ); + Py_DECREF(obData); + return result; +} + /*[clinic input] winreg.QueryReflectionKey @@ -2107,6 +2200,7 @@ static struct PyMethodDef winreg_methods[] = { WINREG_SAVEKEY_METHODDEF WINREG_SETVALUE_METHODDEF WINREG_SETVALUEEX_METHODDEF + WINREG_GETVALUE_METHODDEF {NULL}, }; @@ -2204,6 +2298,20 @@ exec_module(PyObject *m) ADD_INT(REG_RESOURCE_LIST); ADD_INT(REG_FULL_RESOURCE_DESCRIPTOR); ADD_INT(REG_RESOURCE_REQUIREMENTS_LIST); + ADD_INT(RRF_RT_ANY); + ADD_INT(RRF_RT_DWORD); + ADD_INT(RRF_RT_QWORD); + ADD_INT(RRF_RT_REG_BINARY); + ADD_INT(RRF_RT_REG_DWORD); + ADD_INT(RRF_RT_REG_EXPAND_SZ); + ADD_INT(RRF_RT_REG_MULTI_SZ); + ADD_INT(RRF_RT_REG_NONE); + ADD_INT(RRF_RT_REG_QWORD); + ADD_INT(RRF_RT_REG_SZ); + ADD_INT(RRF_NOEXPAND); + ADD_INT(RRF_ZEROONFAILURE); + ADD_INT(RRF_SUBKEY_WOW6464KEY); + ADD_INT(RRF_SUBKEY_WOW6432KEY); #undef ADD_INT return 0;