6

I'm trying to delete certain registry keys, via python script.
i have no problems reading and deleting keys from the "HKEY_CURRENT_USER", but trying to do the same from the "HKEY_LOCAL_MACHINE", gives me the dreaded WindowsError: [Error 5] Access is denied.
i'm running the script via the IDLE IDE, with admin privileges.
here's the code:

from _winreg import *    
ConnectRegistry(None,HKEY_LOCAL_MACHINE)
OpenKey(HKEY_LOCAL_MACHINE,r'software\wow6432node\App',0,KEY_ALL_ACCESS)
DeleteKey(OpenKey(HKEY_LOCAL_MACHINE,r'software\wow6432node'),'App')
Tom Kidron
  • 177
  • 2
  • 2
  • 11
  • Can you delete that key using `regedit` from the same user? – cdarke Jul 05 '16 at 14:53
  • @cdarke - yes. when i open `regedit` it runs as admin (the UAC dialog is shown). i can navigate to the desired key and delete it, along with it's subkeys. maybe the problem with the script is that it fails because 'software\wow6432node\App' contains additional subkeys. – Tom Kidron Jul 06 '16 at 12:22

5 Answers5

6

You need to remove all subkeys before you can delete the key.

def deleteSubkey(key0, key1, key2=""):
    import _winreg
    if key2=="":
        currentkey = key1
    else:
        currentkey = key1+ "\\" +key2

    open_key = _winreg.OpenKey(key0, currentkey ,0,_winreg.KEY_ALL_ACCESS)
    infokey = _winreg.QueryInfoKey(open_key)
    for x in range(0, infokey[0]):
        #NOTE:: This code is to delete the key and all subkeys.
        #  If you just want to walk through them, then 
        #  you should pass x to EnumKey. subkey = _winreg.EnumKey(open_key, x)
        #  Deleting the subkey will change the SubKey count used by EnumKey. 
        #  We must always pass 0 to EnumKey so we 
        #  always get back the new first SubKey.
        subkey = _winreg.EnumKey(open_key, 0)
        try:
            _winreg.DeleteKey(open_key, subkey)
            print "Removed %s\\%s " % ( currentkey, subkey)
        except:
            deleteSubkey( key0, currentkey, subkey )
            # no extra delete here since each call 
            #to deleteSubkey will try to delete itself when its empty.

    _winreg.DeleteKey(open_key,"")
    open_key.Close()
    print "Removed %s" % (currentkey)
    return

Here is an how you run it:

deleteSubkey(_winreg.HKEY_CURRENT_USER, "software\\wow6432node", "App")
deleteSubkey(_winreg.HKEY_CURRENT_USER, "software\\wow6432node\\App")
ChrisHiebert
  • 191
  • 2
  • 4
  • Hi this recursion does not end, i got "maximum recursion depth exceeded " exception, when trying to delete winreg.HKEY_CURRENT_USER, 'Software\\Microsoft\\Office\\'+str(ver)+'\\Excel\\Resiliency' – Omer Anisfeld Sep 23 '20 at 16:46
2

[EDIT] I have created a pip package that handles registry keys. Install with: pip install windows_tools.registry

Usage:

from windows_tools.registry import delete_sub_key, KEY_WOW64_32KEY, KEY_WOW64_64KEY

keys = ['SOFTWARE\MyInstalledApp', 'SOFTWARE\SomeKey\SomeOtherKey']
for key in keys:
    delete_sub_key(key, arch=KEY_WOW64_32KEY | KEY_WOW64_64KEY)

[/EDIT]

Unburying this old question, here's an updated version of ChrisHiebert's recursive function that:

  • Handles Python 3 (tested with Python 3.7.1)
  • Handles multiple registry architectures (eg Wow64 for Python 32 on Windows 64)
  • Is PEP-8 compliant

The following example shows function usage to delete two keys in all registry architectures (standard and redirected WOW6432Node) by using architecture key masks. Hopefully this will help someone:

    import winreg


    def delete_sub_key(key0, current_key, arch_key=0):

        open_key = winreg.OpenKey(key0, current_key, 0, winreg.KEY_ALL_ACCESS | arch_key)
        info_key = winreg.QueryInfoKey(open_key)
        for x in range(0, info_key[0]):
            # NOTE:: This code is to delete the key and all sub_keys.
            # If you just want to walk through them, then
            # you should pass x to EnumKey. sub_key = winreg.EnumKey(open_key, x)
            # Deleting the sub_key will change the sub_key count used by EnumKey.
            # We must always pass 0 to EnumKey so we
            # always get back the new first sub_key.
            sub_key = winreg.EnumKey(open_key, 0)
            try:
                winreg.DeleteKey(open_key, sub_key)
                print("Removed %s\\%s " % (current_key, sub_key))
            except OSError:
                delete_sub_key(key0, "\\".join([current_key,sub_key]), arch_key)
                # No extra delete here since each call
                # to delete_sub_key will try to delete itself when its empty.

        winreg.DeleteKey(open_key, "")
        open_key.Close()
        print("Removed %s" % current_key)
        return


    # Allows to specify if operating in redirected 32 bit mode or 64 bit, set arch_keys to 0 to disable
    arch_keys = [winreg.KEY_WOW64_32KEY, winreg.KEY_WOW64_64KEY]
    
    # Base key
    root = winreg.HKEY_LOCAL_MACHINE
    
    # List of keys to delete
    keys = ['SOFTWARE\MyInstalledApp', 'SOFTWARE\SomeKey\SomeOtherKey']

    for key in keys:
        for arch_key in arch_keys:
            try:
                delete_sub_key(root, key, arch_key)
            except OSError as e:
                print(e)
Orsiris de Jong
  • 2,819
  • 1
  • 26
  • 48
2

Just my two cents on the topic, but I recurse to the lowest subkey and delete on unravel:

def delete_sub_key(root, sub):
    
    try:
        open_key = winreg.OpenKey(root, sub, 0, winreg.KEY_ALL_ACCESS)
        num, _, _ = winreg.QueryInfoKey(open_key)
        for i in range(num):
            child = winreg.EnumKey(open_key, 0)
            delete_sub_key(open_key, child)
        try:
           winreg.DeleteKey(open_key, '')
        except Exception:
           # log deletion failure
        finally:
           winreg.CloseKey(open_key)
    except Exception:
        # log opening/closure failure

The difference between the other posts is that I do not try to delete if num is >0 because it will fail implicitly (as stated in the docs). So I don't waste time to try if there are subkeys.

pstatix
  • 3,611
  • 4
  • 18
  • 40
  • +1 Short and concise. Could still use a `with winreg.OpenKey(..) as open_key:` context to save yourself the `finally:` block! – Jeronimo Feb 07 '23 at 14:06
1

This is my solution. I like to use with statements in order to not have to close the key manually. First I check for sub keys and delete them before I delete the key itself. EnumKey raises an OSError if no sub key exists. I use this to break out of the loop.

from winreg import *

def delete_key(key: Union[HKEYType, int], sub_key_name: str):
    with OpenKey(key, sub_key_name) as sub_key:
        while True:
            try:
                sub_sub_key_name = EnumKey(sub_key, 0)
                delete_key(sub_key, sub_sub_key_name)
            except OSError:
                break

    DeleteKey(key, sub_key_name)
Meinrad
  • 71
  • 1
  • 7
  • **This is 100x more beautiful than all the other answers. Thank you!** I even love how you loop until `OSError` which is exactly what `EnumKey` docs suggest: "It is typically called repeatedly until an OSError exception is raised, indicating no more values are available." – Mitch McMabers Jun 22 '23 at 02:34
0

Figured it out!
turns out the registry key wasn't empty and contained multiple subkeys.
i had to enumerate and delete the subkeys first, and only then i was able to delete the main key from HKLM. (also added "try...except", so it wouldn't break the whole code, it case there were problems).

Tom Kidron
  • 177
  • 2
  • 2
  • 11