diff -r 7e86b296deeb Doc/library/winreg.rst --- a/Doc/library/winreg.rst Fri Mar 13 16:21:29 2015 -0500 +++ b/Doc/library/winreg.rst Sat Mar 14 13:35:26 2015 +0200 @@ -274,6 +274,24 @@ If *key* is a handle returned by :func:`ConnectRegistry`, then the path specified in *file_name* is relative to the remote computer. +.. function:: UnLoadKey(key, sub_key) + + Unloads the specified registry key and its subkeys from the registry. + It's the counterpart of :func:`LoadKey`. + + *key* is a handle returned by :func:`ConnectRegistry` or one of the constants + :const:`HKEY_USERS` or :const:`HKEY_LOCAL_MACHINE`. + + *sub_key* is a string that identifies the subkey to load. + + A call to :func:`UnLoadKey` fails if the calling process does not have the + :const:`SE_RESTORE_PRIVILEGE` privilege. Note that privileges are different + from permissions -- see the `RegUnLoadKey documentation + `__ for + more details. + + .. versionadded:: 3.5 + .. function:: OpenKey(key, sub_key, reserved=0, access=KEY_READ) OpenKeyEx(key, sub_key, reserved=0, access=KEY_READ) diff -r 7e86b296deeb Lib/test/test_winreg.py --- a/Lib/test/test_winreg.py Fri Mar 13 16:21:29 2015 -0500 +++ b/Lib/test/test_winreg.py Sat Mar 14 13:35:26 2015 +0200 @@ -10,7 +10,10 @@ # Do this first so test will be skipped if module doesn't exist support.import_module('winreg', required_on=['win']) # Now import everything +import winreg from winreg import * +from test.support.windows_helper import ( + acquired_privilege, skip_unless_privilege) try: REMOTE_NAME = sys.argv[sys.argv.index("--remote")+1] @@ -34,6 +37,8 @@ test_key_name = "SOFTWARE\\" + test_key_base # On OS'es that support reflection we should test with a reflected key test_reflect_key_name = "SOFTWARE\\Classes\\" + test_key_base +ERROR_PRIVILEGE_NOT_HELD = 1314 +ERROR_INVALID_PARAMETER = 87 test_data = [ ("Int Value", 45, REG_DWORD), @@ -367,6 +372,41 @@ finally: DeleteKey(HKEY_CURRENT_USER, test_key_name) + def test_unload_key_no_privilege(self): + with self.assertRaises(OSError) as cm: + winreg.UnLoadKey(winreg.HKEY_USERS, test_key_base) + self.assertIn(cm.exception.winerror, + (ERROR_PRIVILEGE_NOT_HELD, ERROR_INVALID_PARAMETER)) + + @skip_unless_privilege("SeRestorePrivilege") + @skip_unless_privilege("SeBackupPrivilege") + def test_unload_key(self): + self.addCleanup(support.unlink, support.TESTFN) + with acquired_privilege("SeRestorePrivilege"): + # only HKEY_USERS and HKEY_LOCAL_MACHINE are allowed + for key in (HKEY_CURRENT_USER, HKEY_CLASSES_ROOT): + with self.assertRaisesRegex(OSError, + "The parameter is incorrect"): + UnLoadKey(key, test_key_base) + # the key does not exist + with self.assertRaisesRegex(OSError, + "The parameter is incorrect"): + UnLoadKey(HKEY_USERS, "nanana") + + with acquired_privilege("SeBackupPrivilege"): + SaveKey(HKEY_CURRENT_USER, support.TESTFN) + for key in (HKEY_USERS, HKEY_LOCAL_MACHINE): + LoadKey(key, test_key_base, support.TESTFN) + try: + UnLoadKey(key, test_key_base) + finally: + # If test_key_base was mounted in HKEY_USERS, + # the reflected key will appear in HKEY_CURRENT_USER + # as well. The only problem is that we can't use + # UnLoadKey to detach it, so we'll just remove it. + if key == HKEY_USERS: + DeleteKey(HKEY_CURRENT_USER, test_key_base) + @unittest.skipUnless(REMOTE_NAME, "Skipping remote registry tests") diff -r 7e86b296deeb PC/winreg.c --- a/PC/winreg.c Fri Mar 13 16:21:29 2015 -0500 +++ b/PC/winreg.c Sat Mar 14 13:35:26 2015 +0200 @@ -50,6 +50,8 @@ "FlushKey() - Writes all the attributes of the specified key to the registry.\n" "LoadKey() - Creates a subkey under HKEY_USER or HKEY_LOCAL_MACHINE and stores\n" " registration information from a specified file into that subkey.\n" +"UnLoadKey() - Unloads the specified registry key and its subkeys \n" +" from the registry.\n" "OpenKey() - Opens the specified key.\n" "OpenKeyEx() - Alias of OpenKey().\n" "QueryValue() - Retrieves the value associated with the unnamed value for a\n" @@ -229,6 +231,20 @@ "\n" "The docs imply key must be in the HKEY_USER or HKEY_LOCAL_MACHINE tree"); +PyDoc_STRVAR(UnLoadKey_doc, +"UnLoadKey(key, sub_key)\n" +"Unloads the specified registry key and its subkeys from the registry.\n" +"\n" +"key is an already open key, or any one of the predefined HKEY_* constants.\n" +"sub_key is a string that identifies the sub_key to unload.\n" +"\n" +"A call to UnLoadKey() fails if the calling process does not have the\n" +"SE_RESTORE_PRIVILEGE privilege.\n" +"If key is a handle returned by ConnectRegistry(), then the path specified\n" +"in fileName is relative to the remote computer.\n" +"\n" +"The docs imply key must be in the HKEY_USER or HKEY_LOCAL_MACHINE tree"); + PyDoc_STRVAR(OpenKey_doc, "OpenKey(key, sub_key, reserved=0, access=KEY_READ) -> key\n" "Opens the specified key.\n" @@ -1334,6 +1350,27 @@ } static PyObject * +PyUnLoadKey(PyObject *self, PyObject *args) +{ + HKEY hKey; + PyObject *obKey; + wchar_t *subKey; + + long rc; + if (!PyArg_ParseTuple(args, "Ou:UnLoadKey", &obKey, &subKey)) + return NULL; + if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE)) + return NULL; + Py_BEGIN_ALLOW_THREADS + rc = RegUnLoadKeyW(hKey, subKey); + Py_END_ALLOW_THREADS + if (rc != ERROR_SUCCESS) + return PyErr_SetFromWindowsErrWithFunction(rc, "RegUnLoadKey"); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * PyOpenKey(PyObject *self, PyObject *args, PyObject *kwargs) { HKEY hKey; @@ -1735,6 +1772,7 @@ ExpandEnvironmentStrings_doc }, {"FlushKey", PyFlushKey, METH_VARARGS, FlushKey_doc}, {"LoadKey", PyLoadKey, METH_VARARGS, LoadKey_doc}, + {"UnLoadKey", PyUnLoadKey, METH_VARARGS, UnLoadKey_doc}, {"OpenKey", (PyCFunction)PyOpenKey, METH_VARARGS | METH_KEYWORDS, OpenKey_doc}, {"OpenKeyEx", (PyCFunction)PyOpenKey, METH_VARARGS | METH_KEYWORDS,