Search code examples
windowsrustwinreg

Comparing and deleting orphans registry keys in Rust using winreg


I am struggling a bit with my code: the idea is to compare two different registry keys, one located in the machine hive (being the master key) and the other in the current user hive. For each subkey present in the user key, if the same name is missing from the machine key, then delete the user subkey.

At this point to make it clearer let's build a simple example: HKEY_CURRENT_USER\Software\MyKey contains 5 subkeys named 1 2 3 4 5 For each, if HKEY_LOCAL_MACHINE\Software\MyKey\<subkey> is missing then delete HKCU\Software\MyKey\<subkey>

My initial idea is to parse HKEY_LOCAL_MACHINE\Software\MyKey once (in my example there is only 5 keys but it could be much more) Then, for each user key, test if the same name is present in the machine .iter(), and delete the user key if not.

Now the code:

    let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
    let hkcu = RegKey::predef(HKEY_CURRENT_USER);

    let machine_subkey: Vec<_> = hklm.open_subkey(&m_key)?.enum_keys().collect();
    for user_subkey in hkcu.open_subkey(&u_key)?.enum_keys().map(|x| x.unwrap()) {
        let found = machine_subkey.iter().any(|result| {
            if let Ok(key) = result {
                key == &user_subkey
            } else {
                false
            }
        });
        if !found {
            let path: PathBuf = [&u_key, &user_subkey].iter().collect();
            hkcu.delete_subkey(&path)?;
        }
    }

When running, this code deletes one subkey out of two: keys 1, 3 and 5 are getting deleted, a println! shows that key 2 and 4 are not even parsed by the loop. when supressing the hkcu.delete_subkey() line, all 5 keys are parsed in the loop.


Solution

  • I'd say that's a classic example of mutating a container while iterating over it, which Rust doesn't protect you from in this case because the container is the Windows registry (well actually the HKCU\Software\MyKey key in the registry) and so is not implemented in Rust. The solution is to first collect all the subkeys, and then iterate over the collected values and deleting them. I.e. something like:

    let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
    let hkcu = RegKey::predef(HKEY_CURRENT_USER);
    
    let machine_subkey: Vec<_> = hklm.open_subkey(&m_key)?.enum_keys().collect();
    let user_subkey: Vec<_> = hkcu
        .open_subkey(&u_key)?
        .enum_keys()
        .map(|x| x.unwrap())
        .collect();
    for user_subkey in user_subkey {
        let found = machine_subkey.iter().any(|result| {
            if let Ok(key) = result {
                key == &user_subkey
            } else {
                false
            }
        });
        if !found {
            let path: PathBuf = [&u_key, &user_subkey].iter().collect();
            hkcu.delete_subkey(&path)?;
        }
    }