diff --git a/dlls/msi/action.c b/dlls/msi/action.c index e7b292120bb..b2564393244 100644 --- a/dlls/msi/action.c +++ b/dlls/msi/action.c @@ -2582,6 +2582,38 @@ static WCHAR *get_keypath( MSICOMPONENT *comp, HKEY root, const WCHAR *path ) return strdupW( path ); } +static HKEY open_key( HKEY root, const WCHAR *path, BOOL create ) +{ + REGSAM access = KEY_ALL_ACCESS; + WCHAR *subkey, *p, *q; + HKEY hkey, ret = NULL; + LONG res; + + if (is_wow64) access |= KEY_WOW64_64KEY; + + if (!(subkey = strdupW( path ))) return NULL; + p = subkey; + if ((q = strchrW( p, '\\' ))) *q = 0; + if (create) + res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL ); + else + res = RegOpenKeyExW( root, subkey, 0, access, &hkey ); + if (res) + { + TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res); + msi_free( subkey ); + return NULL; + } + if (q && q[1]) + { + ret = open_key( hkey, q + 1, create ); + RegCloseKey( hkey ); + } + else ret = hkey; + msi_free( subkey ); + return ret; +} + static BOOL is_special_entry( const WCHAR *name ) { return (name && (name[0] == '*' || name[0] == '+') && !name[1]); @@ -2640,7 +2672,7 @@ static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param) keypath = get_keypath( comp, root_key, deformated ); msi_free( deformated ); - if (RegCreateKeyExW( root_key, keypath, 0, NULL, 0, KEY_ALL_ACCESS|KEY_WOW64_64KEY, NULL, &hkey, NULL )) + if (!(hkey = open_key( root_key, keypath, TRUE ))) { ERR("Could not create key %s\n", debugstr_w(keypath)); msi_free(uikey); @@ -2711,44 +2743,67 @@ static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package) return rc; } -static void delete_reg_value( HKEY root, const WCHAR *keypath, const WCHAR *value ) +static void delete_key( HKEY root, const WCHAR *path ) +{ + REGSAM access = 0; + WCHAR *subkey, *p; + HKEY hkey; + LONG res; + + if (is_wow64) access |= KEY_WOW64_64KEY; + + if (!(subkey = strdupW( path ))) return; + for (;;) + { + if ((p = strrchrW( subkey, '\\' ))) *p = 0; + hkey = open_key( root, subkey, FALSE ); + if (!hkey) break; + if (p && p[1]) + res = RegDeleteKeyExW( hkey, p + 1, access, 0 ); + else + res = RegDeleteKeyExW( root, subkey, access, 0 ); + if (res) + { + TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res); + break; + } + if (p && p[1]) RegCloseKey( hkey ); + else break; + } + msi_free( subkey ); +} + +static void delete_value( HKEY root, const WCHAR *path, const WCHAR *value ) { LONG res; HKEY hkey; DWORD num_subkeys, num_values; - if (!(res = RegOpenKeyExW( root, keypath, 0, KEY_ALL_ACCESS|KEY_WOW64_64KEY, &hkey ))) + if ((hkey = open_key( root, path, FALSE ))) { if ((res = RegDeleteValueW( hkey, value ))) - { TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res); - } + res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values, NULL, NULL, NULL, NULL ); RegCloseKey( hkey ); if (!res && !num_subkeys && !num_values) { - TRACE("removing empty key %s\n", debugstr_w(keypath)); - RegDeleteKeyExW( root, keypath, KEY_WOW64_64KEY, 0 ); + TRACE("removing empty key %s\n", debugstr_w(path)); + delete_key( root, path ); } - return; } - TRACE("failed to open key %s (%d)\n", debugstr_w(keypath), res); } -static void delete_reg_key( HKEY root, const WCHAR *keypath ) +static void delete_tree( HKEY root, const WCHAR *path ) { + LONG res; HKEY hkey; - LONG res = RegOpenKeyExW( root, keypath, 0, KEY_ALL_ACCESS|KEY_WOW64_64KEY, &hkey ); - if (res) - { - TRACE("failed to open key %s (%d)\n", debugstr_w(keypath), res); - return; - } + + if (!(hkey = open_key( root, path, FALSE ))) return; res = RegDeleteTreeW( hkey, NULL ); - if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(keypath), res); - res = RegDeleteKeyExW( root, keypath, KEY_WOW64_64KEY, 0 ); - if (res) TRACE("failed to delete key %s (%d)\n", debugstr_w(keypath), res); + if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res); + delete_key( root, path ); RegCloseKey( hkey ); } @@ -2807,8 +2862,8 @@ static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID para keypath = get_keypath( comp, hkey_root, deformated_key ); msi_free( deformated_key ); - if (delete_key) delete_reg_key( hkey_root, keypath ); - else delete_reg_value( hkey_root, keypath, deformated_name ); + if (delete_key) delete_tree( hkey_root, keypath ); + else delete_value( hkey_root, keypath, deformated_name ); msi_free( keypath ); uirow = MSI_CreateRecord( 2 ); @@ -2872,8 +2927,8 @@ static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param keypath = get_keypath( comp, hkey_root, deformated_key ); msi_free( deformated_key ); - if (delete_key) delete_reg_key( hkey_root, keypath ); - else delete_reg_value( hkey_root, keypath, deformated_name ); + if (delete_key) delete_tree( hkey_root, keypath ); + else delete_value( hkey_root, keypath, deformated_name ); msi_free( keypath ); uirow = MSI_CreateRecord( 2 ); diff --git a/dlls/msi/tests/action.c b/dlls/msi/tests/action.c index 0bd65a0871b..82afd1830d4 100644 --- a/dlls/msi/tests/action.c +++ b/dlls/msi/tests/action.c @@ -490,7 +490,8 @@ static const char wrv_registry_dat[] = "regdata\t2\tSOFTWARE\\Wine\\msitest\tValue\t[~]one[~]two[~]three\taugustus\n" "regdata1\t2\tSOFTWARE\\Wine\\msitest\t*\t\taugustus\n" "regdata2\t2\tSOFTWARE\\Wine\\msitest\t*\t#%\taugustus\n" - "regdata3\t2\tSOFTWARE\\Wine\\msitest\t*\t#x\taugustus\n"; + "regdata3\t2\tSOFTWARE\\Wine\\msitest\t*\t#x\taugustus\n" + "regdata4\t2\tSOFTWARE\\Wine\\msitest\\VisualStudio\\10.0\\AD7Metrics\\Exception\\{049EC4CC-30D2-4032-9256-EE18EB41B62B}\\Common Language Runtime Exceptions\\System.Workflow.ComponentModel.Serialization\\System.Workflow.ComponentModel.Serialization.WorkflowMarkupSerializationException\tlong\tkey\taugustus\n"; static const char cf_directory_dat[] = "Directory\tDirectory_Parent\tDefaultDir\n" @@ -1818,6 +1819,75 @@ static const msi_table pa_tables[] = ADD_TABLE(property) }; +/* based on RegDeleteTreeW from dlls/advapi32/registry.c */ +static LSTATUS action_RegDeleteTreeA(HKEY hKey, LPCSTR lpszSubKey, REGSAM access) +{ + LONG ret; + DWORD dwMaxSubkeyLen, dwMaxValueLen; + DWORD dwMaxLen, dwSize; + char szNameBuf[MAX_PATH], *lpszName = szNameBuf; + HKEY hSubKey = hKey; + + if(lpszSubKey) + { + ret = RegOpenKeyExA(hKey, lpszSubKey, 0, access, &hSubKey); + if (ret) return ret; + } + + ret = RegQueryInfoKeyA(hSubKey, NULL, NULL, NULL, NULL, + &dwMaxSubkeyLen, NULL, NULL, &dwMaxValueLen, NULL, NULL, NULL); + if (ret) goto cleanup; + + dwMaxSubkeyLen++; + dwMaxValueLen++; + dwMaxLen = max(dwMaxSubkeyLen, dwMaxValueLen); + if (dwMaxLen > sizeof(szNameBuf)) + { + /* Name too big: alloc a buffer for it */ + if (!(lpszName = HeapAlloc( GetProcessHeap(), 0, dwMaxLen))) + { + ret = ERROR_NOT_ENOUGH_MEMORY; + goto cleanup; + } + } + + /* Recursively delete all the subkeys */ + while (TRUE) + { + dwSize = dwMaxLen; + if (RegEnumKeyExA(hSubKey, 0, lpszName, &dwSize, NULL, + NULL, NULL, NULL)) break; + + ret = action_RegDeleteTreeA(hSubKey, lpszName, access); + if (ret) goto cleanup; + } + + if (lpszSubKey) + { + if (pRegDeleteKeyExA) + ret = pRegDeleteKeyExA(hKey, lpszSubKey, access, 0); + else + ret = RegDeleteKeyA(hKey, lpszSubKey); + } + else + while (TRUE) + { + dwSize = dwMaxLen; + if (RegEnumValueA(hKey, 0, lpszName, &dwSize, + NULL, NULL, NULL, NULL)) break; + + ret = RegDeleteValueA(hKey, lpszName); + if (ret) goto cleanup; + } + +cleanup: + if (lpszName != szNameBuf) + HeapFree(GetProcessHeap(), 0, lpszName); + if(lpszSubKey) + RegCloseKey(hSubKey); + return ret; +} + /* cabinet definitions */ /* make the max size large so there is only one cab file */ @@ -4586,6 +4656,9 @@ static void test_write_registry_values(void) res = RegQueryValueExA(hkey, "", NULL, NULL, NULL, NULL); ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res); + res = action_RegDeleteTreeA(hkey, "VisualStudio", KEY_ALL_ACCESS); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + RegDeleteValueA(hkey, "Value"); RegCloseKey(hkey); RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine\\msitest");