[3/3] msi: Correctly [un]register progids when associated class and extensions change state.

Hans Leidekker hans at codeweavers.com
Thu Feb 27 04:03:48 CST 2014


---
 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 3e096b3..a5139f8 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 b664766..75d1920 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 371860d..c6b45ec 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");
 
-- 
1.8.5.3






More information about the wine-patches mailing list