From a547027b0199ffa96165b75012d23c4ab64f3b30 Mon Sep 17 00:00:00 2001 From: Hans Leidekker Date: Thu, 27 Feb 2014 11:03:48 +0100 Subject: [PATCH] msi: Correctly [un]register progids when associated class and extensions change state. --- dlls/msi/classes.c | 124 ++++++++++++++++++++-------------------- dlls/msi/msipriv.h | 1 - dlls/msi/tests/action.c | 30 ++++++++-- 3 files changed, 86 insertions(+), 69 deletions(-) diff --git a/dlls/msi/classes.c b/dlls/msi/classes.c index 3e096b32159..a5139f8e0d1 100644 --- a/dlls/msi/classes.c +++ b/dlls/msi/classes.c @@ -698,45 +698,6 @@ static UINT load_classes_and_such( MSIPACKAGE *package ) return load_all_mimes( package ); } -static void mark_progid_for_install( MSIPACKAGE* package, MSIPROGID *progid ) -{ - MSIPROGID *child; - - if (!progid) - return; - - if (progid->InstallMe) - return; - - progid->InstallMe = TRUE; - - /* all children if this is a parent also install */ - LIST_FOR_EACH_ENTRY( child, &package->progids, MSIPROGID, entry ) - { - if (child->Parent == progid) - mark_progid_for_install( package, child ); - } -} - -static void mark_progid_for_uninstall( MSIPACKAGE *package, MSIPROGID *progid ) -{ - MSIPROGID *child; - - if (!progid) - return; - - if (!progid->InstallMe) - return; - - progid->InstallMe = FALSE; - - LIST_FOR_EACH_ENTRY( child, &package->progids, MSIPROGID, entry ) - { - if (child->Parent == progid) - mark_progid_for_uninstall( package, child ); - } -} - static UINT register_appid(const MSIAPPID *appid, LPCWSTR app ) { static const WCHAR szRemoteServerName[] = @@ -843,7 +804,6 @@ UINT ACTION_RegisterClassInfo(MSIPACKAGE *package) TRACE("Registering class %s (%p)\n", debugstr_w(cls->clsid), cls); cls->action = INSTALLSTATE_LOCAL; - mark_progid_for_install( package, cls->ProgID ); RegCreateKeyW( hkey, cls->clsid, &hkey2 ); @@ -1001,7 +961,6 @@ UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package ) TRACE("Unregistering class %s (%p)\n", debugstr_w(cls->clsid), cls); cls->action = INSTALLSTATE_ABSENT; - mark_progid_for_uninstall( package, cls->ProgID ); res = RegDeleteTreeW( hkey, cls->clsid ); if (res != ERROR_SUCCESS) @@ -1089,6 +1048,35 @@ static UINT register_progid( const MSIPROGID* progid ) return rc; } +static const MSICLASS *get_progid_class( const MSIPROGID *progid ) +{ + while (progid) + { + if (progid->Parent) progid = progid->Parent; + if (progid->Class) return progid->Class; + if (!progid->Parent) break; + } + return NULL; +} + +static BOOL has_class_installed( const MSIPROGID *progid ) +{ + const MSICLASS *class = get_progid_class( progid ); + if (!class || !class->ProgID) return FALSE; + return (class->action == INSTALLSTATE_LOCAL); +} + +static BOOL has_one_extension_installed( const MSIPACKAGE *package, const MSIPROGID *progid ) +{ + const MSIEXTENSION *extension; + LIST_FOR_EACH_ENTRY( extension, &package->extensions, MSIEXTENSION, entry ) + { + if (extension->ProgID == progid && !list_empty( &extension->verbs ) && + extension->action == INSTALLSTATE_LOCAL) return TRUE; + } + return FALSE; +} + UINT ACTION_RegisterProgIdInfo(MSIPACKAGE *package) { MSIPROGID *progid; @@ -1101,16 +1089,11 @@ UINT ACTION_RegisterProgIdInfo(MSIPACKAGE *package) LIST_FOR_EACH_ENTRY( progid, &package->progids, MSIPROGID, entry ) { - /* check if this progid is to be installed */ - if (progid->Class && progid->Class->action == INSTALLSTATE_LOCAL) - progid->InstallMe = TRUE; - - if (!progid->InstallMe) + if (!has_class_installed( progid ) && !has_one_extension_installed( package, progid )) { TRACE("progid %s not scheduled to be installed\n", debugstr_w(progid->ProgID)); continue; } - TRACE("Registering progid %s\n", debugstr_w(progid->ProgID)); register_progid( progid ); @@ -1123,6 +1106,36 @@ UINT ACTION_RegisterProgIdInfo(MSIPACKAGE *package) return ERROR_SUCCESS; } +static BOOL has_class_removed( const MSIPROGID *progid ) +{ + const MSICLASS *class = get_progid_class( progid ); + if (!class || !class->ProgID) return FALSE; + return (class->action == INSTALLSTATE_ABSENT); +} + +static BOOL has_extensions( const MSIPACKAGE *package, const MSIPROGID *progid ) +{ + const MSIEXTENSION *extension; + LIST_FOR_EACH_ENTRY( extension, &package->extensions, MSIEXTENSION, entry ) + { + if (extension->ProgID == progid && !list_empty( &extension->verbs )) return TRUE; + } + return FALSE; +} + +static BOOL has_all_extensions_removed( const MSIPACKAGE *package, const MSIPROGID *progid ) +{ + BOOL ret = FALSE; + const MSIEXTENSION *extension; + LIST_FOR_EACH_ENTRY( extension, &package->extensions, MSIEXTENSION, entry ) + { + if (extension->ProgID == progid && !list_empty( &extension->verbs ) && + extension->action == INSTALLSTATE_ABSENT) ret = TRUE; + else ret = FALSE; + } + return ret; +} + UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package ) { MSIPROGID *progid; @@ -1136,16 +1149,12 @@ UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package ) LIST_FOR_EACH_ENTRY( progid, &package->progids, MSIPROGID, entry ) { - /* check if this progid is to be removed */ - if (progid->Class && progid->Class->action != INSTALLSTATE_LOCAL) - progid->InstallMe = FALSE; - - if (progid->InstallMe) + if (!has_class_removed( progid ) || + (has_extensions( package, progid ) && !has_all_extensions_removed( package, progid ))) { TRACE("progid %s not scheduled to be removed\n", debugstr_w(progid->ProgID)); continue; } - TRACE("Unregistering progid %s\n", debugstr_w(progid->ProgID)); res = RegDeleteTreeW( HKEY_CLASSES_ROOT, progid->ProgID ); @@ -1289,12 +1298,6 @@ UINT ACTION_RegisterExtensionInfo(MSIPACKAGE *package) ext->action = INSTALLSTATE_LOCAL; - /* this is only registered if the extension has at least 1 verb - * according to MSDN - */ - if (ext->ProgID && !list_empty( &ext->verbs ) ) - mark_progid_for_install( package, ext->ProgID ); - extension = msi_alloc( (strlenW( ext->Extension ) + 2) * sizeof(WCHAR) ); if (extension) { @@ -1393,9 +1396,6 @@ UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package ) ext->action = INSTALLSTATE_ABSENT; - if (ext->ProgID && !list_empty( &ext->verbs )) - mark_progid_for_uninstall( package, ext->ProgID ); - extension = msi_alloc( (strlenW( ext->Extension ) + 2) * sizeof(WCHAR) ); if (extension) { diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h index b6647661939..75d1920d81f 100644 --- a/dlls/msi/msipriv.h +++ b/dlls/msi/msipriv.h @@ -642,7 +642,6 @@ struct tagMSIPROGID LPWSTR Description; LPWSTR IconPath; /* not in the table, set during installation */ - BOOL InstallMe; MSIPROGID *CurVer; MSIPROGID *VersionInd; }; diff --git a/dlls/msi/tests/action.c b/dlls/msi/tests/action.c index 371860d4eb4..c6b45ec7cc5 100644 --- a/dlls/msi/tests/action.c +++ b/dlls/msi/tests/action.c @@ -1372,7 +1372,14 @@ static const char rpi_class_dat[] = static const char rpi_extension_dat[] = "Extension\tComponent_\tProgId_\tMIME_\tFeature_\n" "s255\ts72\tS255\tS64\ts38\n" - "Extension\tExtension\tComponent_\n"; + "Extension\tExtension\tComponent_\n" + "winetest\tprogid\tWinetest.Extension\t\tprogid\n"; + +static const char rpi_verb_dat[] = + "Extension_\tVerb\tSequence\tCommand\tArgument\n" + "s255\ts32\tI2\tL255\tL255\n" + "Verb\tExtension_\tVerb\n" + "winetest\tOpen\t1\t&Open\t/argument\n"; static const char rpi_progid_dat[] = "ProgId\tProgId_Parent\tClass_\tDescription\tIcon_\tIconIndex\n" @@ -1386,7 +1393,8 @@ static const char rpi_progid_dat[] = "Winetest.NoProgIdClass.1\t\t{57C413FB-CA02-498A-81F6-7E769BDB7C97}\tdescription\t\t\n" "Winetest.NoProgIdClass\tWinetest.NoProgIdClass.1\t\tdescription\t\t\n" "Winetest.Orphaned\t\t\tdescription\t\t\n" - "Winetest.Orphaned2\t\t\tdescription\t\t\n"; + "Winetest.Orphaned2\t\t\tdescription\t\t\n" + "Winetest.Extension\t\t\tdescription\t\t\n"; static const char rpi_install_exec_seq_dat[] = "Action\tCondition\tSequence\n" @@ -1400,10 +1408,12 @@ static const char rpi_install_exec_seq_dat[] = "InstallInitialize\t\t1500\n" "ProcessComponents\t\t1600\n" "RemoveFiles\t\t1700\n" - "InstallFiles\t\t2000\n" "UnregisterClassInfo\t\t3000\n" + "UnregisterExtensionInfo\t\t3200\n" "UnregisterProgIdInfo\t\t3400\n" + "InstallFiles\t\t3600\n" "RegisterClassInfo\t\t4000\n" + "RegisterExtensionInfo\t\t4200\n" "RegisterProgIdInfo\t\t4400\n" "RegisterProduct\t\t5000\n" "PublishFeatures\t\t5100\n" @@ -1984,6 +1994,7 @@ static const msi_table rpi_tables[] = ADD_TABLE(rpi_appid), ADD_TABLE(rpi_class), ADD_TABLE(rpi_extension), + ADD_TABLE(rpi_verb), ADD_TABLE(rpi_progid), ADD_TABLE(rpi_install_exec_seq), ADD_TABLE(media), @@ -6371,19 +6382,23 @@ static void test_register_progid_info(void) RegCloseKey(hkey); res = RegOpenKeyA(HKEY_CLASSES_ROOT, "Winetest.NoProgIdClass.1", &hkey); - todo_wine ok(res == ERROR_FILE_NOT_FOUND, "key created\n"); + ok(res == ERROR_FILE_NOT_FOUND, "key created\n"); if (res == ERROR_SUCCESS) RegCloseKey(hkey); res = RegOpenKeyA(HKEY_CLASSES_ROOT, "Winetest.NoProgIdClass", &hkey); ok(res == ERROR_FILE_NOT_FOUND, "key created\n"); res = RegOpenKeyA(HKEY_CLASSES_ROOT, "Winetest.Orphaned", &hkey); - todo_wine ok(res == ERROR_SUCCESS, "key deleted\n"); + ok(res == ERROR_SUCCESS, "key deleted\n"); if (res == ERROR_SUCCESS) RegCloseKey(hkey); res = RegOpenKeyA(HKEY_CLASSES_ROOT, "Winetest.Orphaned2", &hkey); ok(res == ERROR_FILE_NOT_FOUND, "key created\n"); + res = RegOpenKeyA(HKEY_CLASSES_ROOT, "Winetest.Extension", &hkey); + ok(res == ERROR_SUCCESS, "key not created\n"); + RegCloseKey(hkey); + r = MsiInstallProductA(msifile, "REMOVE=ALL"); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); @@ -6415,12 +6430,15 @@ static void test_register_progid_info(void) ok(res == ERROR_FILE_NOT_FOUND, "key not removed\n"); res = RegOpenKeyA(HKEY_CLASSES_ROOT, "Winetest.Orphaned", &hkey); - todo_wine ok(res == ERROR_SUCCESS, "key deleted\n"); + ok(res == ERROR_SUCCESS, "key deleted\n"); if (res == ERROR_SUCCESS) RegCloseKey(hkey); res = RegOpenKeyA(HKEY_CLASSES_ROOT, "Winetest.Orphaned2", &hkey); ok(res == ERROR_FILE_NOT_FOUND, "key not removed\n"); + res = RegOpenKeyA(HKEY_CLASSES_ROOT, "Winetest.Extension", &hkey); + ok(res == ERROR_FILE_NOT_FOUND, "key not removed\n"); + ok(!delete_pf("msitest\\progid.txt", TRUE), "file not removed\n"); ok(!delete_pf("msitest", FALSE), "directory not removed\n");