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

This commit is contained in:
Hans Leidekker 2014-02-27 11:03:48 +01:00 committed by Alexandre Julliard
parent 28ecbec88d
commit a547027b01
3 changed files with 86 additions and 69 deletions

View File

@ -698,45 +698,6 @@ static UINT load_classes_and_such( MSIPACKAGE *package )
return load_all_mimes( 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 UINT register_appid(const MSIAPPID *appid, LPCWSTR app )
{ {
static const WCHAR szRemoteServerName[] = static const WCHAR szRemoteServerName[] =
@ -843,7 +804,6 @@ UINT ACTION_RegisterClassInfo(MSIPACKAGE *package)
TRACE("Registering class %s (%p)\n", debugstr_w(cls->clsid), cls); TRACE("Registering class %s (%p)\n", debugstr_w(cls->clsid), cls);
cls->action = INSTALLSTATE_LOCAL; cls->action = INSTALLSTATE_LOCAL;
mark_progid_for_install( package, cls->ProgID );
RegCreateKeyW( hkey, cls->clsid, &hkey2 ); 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); TRACE("Unregistering class %s (%p)\n", debugstr_w(cls->clsid), cls);
cls->action = INSTALLSTATE_ABSENT; cls->action = INSTALLSTATE_ABSENT;
mark_progid_for_uninstall( package, cls->ProgID );
res = RegDeleteTreeW( hkey, cls->clsid ); res = RegDeleteTreeW( hkey, cls->clsid );
if (res != ERROR_SUCCESS) if (res != ERROR_SUCCESS)
@ -1089,6 +1048,35 @@ static UINT register_progid( const MSIPROGID* progid )
return rc; 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) UINT ACTION_RegisterProgIdInfo(MSIPACKAGE *package)
{ {
MSIPROGID *progid; MSIPROGID *progid;
@ -1101,16 +1089,11 @@ UINT ACTION_RegisterProgIdInfo(MSIPACKAGE *package)
LIST_FOR_EACH_ENTRY( progid, &package->progids, MSIPROGID, entry ) LIST_FOR_EACH_ENTRY( progid, &package->progids, MSIPROGID, entry )
{ {
/* check if this progid is to be installed */ if (!has_class_installed( progid ) && !has_one_extension_installed( package, progid ))
if (progid->Class && progid->Class->action == INSTALLSTATE_LOCAL)
progid->InstallMe = TRUE;
if (!progid->InstallMe)
{ {
TRACE("progid %s not scheduled to be installed\n", debugstr_w(progid->ProgID)); TRACE("progid %s not scheduled to be installed\n", debugstr_w(progid->ProgID));
continue; continue;
} }
TRACE("Registering progid %s\n", debugstr_w(progid->ProgID)); TRACE("Registering progid %s\n", debugstr_w(progid->ProgID));
register_progid( progid ); register_progid( progid );
@ -1123,6 +1106,36 @@ UINT ACTION_RegisterProgIdInfo(MSIPACKAGE *package)
return ERROR_SUCCESS; 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 ) UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
{ {
MSIPROGID *progid; MSIPROGID *progid;
@ -1136,16 +1149,12 @@ UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
LIST_FOR_EACH_ENTRY( progid, &package->progids, MSIPROGID, entry ) LIST_FOR_EACH_ENTRY( progid, &package->progids, MSIPROGID, entry )
{ {
/* check if this progid is to be removed */ if (!has_class_removed( progid ) ||
if (progid->Class && progid->Class->action != INSTALLSTATE_LOCAL) (has_extensions( package, progid ) && !has_all_extensions_removed( package, progid )))
progid->InstallMe = FALSE;
if (progid->InstallMe)
{ {
TRACE("progid %s not scheduled to be removed\n", debugstr_w(progid->ProgID)); TRACE("progid %s not scheduled to be removed\n", debugstr_w(progid->ProgID));
continue; continue;
} }
TRACE("Unregistering progid %s\n", debugstr_w(progid->ProgID)); TRACE("Unregistering progid %s\n", debugstr_w(progid->ProgID));
res = RegDeleteTreeW( HKEY_CLASSES_ROOT, progid->ProgID ); res = RegDeleteTreeW( HKEY_CLASSES_ROOT, progid->ProgID );
@ -1289,12 +1298,6 @@ UINT ACTION_RegisterExtensionInfo(MSIPACKAGE *package)
ext->action = INSTALLSTATE_LOCAL; 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) ); extension = msi_alloc( (strlenW( ext->Extension ) + 2) * sizeof(WCHAR) );
if (extension) if (extension)
{ {
@ -1393,9 +1396,6 @@ UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
ext->action = INSTALLSTATE_ABSENT; 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) ); extension = msi_alloc( (strlenW( ext->Extension ) + 2) * sizeof(WCHAR) );
if (extension) if (extension)
{ {

View File

@ -642,7 +642,6 @@ struct tagMSIPROGID
LPWSTR Description; LPWSTR Description;
LPWSTR IconPath; LPWSTR IconPath;
/* not in the table, set during installation */ /* not in the table, set during installation */
BOOL InstallMe;
MSIPROGID *CurVer; MSIPROGID *CurVer;
MSIPROGID *VersionInd; MSIPROGID *VersionInd;
}; };

View File

@ -1372,7 +1372,14 @@ static const char rpi_class_dat[] =
static const char rpi_extension_dat[] = static const char rpi_extension_dat[] =
"Extension\tComponent_\tProgId_\tMIME_\tFeature_\n" "Extension\tComponent_\tProgId_\tMIME_\tFeature_\n"
"s255\ts72\tS255\tS64\ts38\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[] = static const char rpi_progid_dat[] =
"ProgId\tProgId_Parent\tClass_\tDescription\tIcon_\tIconIndex\n" "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.1\t\t{57C413FB-CA02-498A-81F6-7E769BDB7C97}\tdescription\t\t\n"
"Winetest.NoProgIdClass\tWinetest.NoProgIdClass.1\t\tdescription\t\t\n" "Winetest.NoProgIdClass\tWinetest.NoProgIdClass.1\t\tdescription\t\t\n"
"Winetest.Orphaned\t\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[] = static const char rpi_install_exec_seq_dat[] =
"Action\tCondition\tSequence\n" "Action\tCondition\tSequence\n"
@ -1400,10 +1408,12 @@ static const char rpi_install_exec_seq_dat[] =
"InstallInitialize\t\t1500\n" "InstallInitialize\t\t1500\n"
"ProcessComponents\t\t1600\n" "ProcessComponents\t\t1600\n"
"RemoveFiles\t\t1700\n" "RemoveFiles\t\t1700\n"
"InstallFiles\t\t2000\n"
"UnregisterClassInfo\t\t3000\n" "UnregisterClassInfo\t\t3000\n"
"UnregisterExtensionInfo\t\t3200\n"
"UnregisterProgIdInfo\t\t3400\n" "UnregisterProgIdInfo\t\t3400\n"
"InstallFiles\t\t3600\n"
"RegisterClassInfo\t\t4000\n" "RegisterClassInfo\t\t4000\n"
"RegisterExtensionInfo\t\t4200\n"
"RegisterProgIdInfo\t\t4400\n" "RegisterProgIdInfo\t\t4400\n"
"RegisterProduct\t\t5000\n" "RegisterProduct\t\t5000\n"
"PublishFeatures\t\t5100\n" "PublishFeatures\t\t5100\n"
@ -1984,6 +1994,7 @@ static const msi_table rpi_tables[] =
ADD_TABLE(rpi_appid), ADD_TABLE(rpi_appid),
ADD_TABLE(rpi_class), ADD_TABLE(rpi_class),
ADD_TABLE(rpi_extension), ADD_TABLE(rpi_extension),
ADD_TABLE(rpi_verb),
ADD_TABLE(rpi_progid), ADD_TABLE(rpi_progid),
ADD_TABLE(rpi_install_exec_seq), ADD_TABLE(rpi_install_exec_seq),
ADD_TABLE(media), ADD_TABLE(media),
@ -6371,19 +6382,23 @@ static void test_register_progid_info(void)
RegCloseKey(hkey); RegCloseKey(hkey);
res = RegOpenKeyA(HKEY_CLASSES_ROOT, "Winetest.NoProgIdClass.1", &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); if (res == ERROR_SUCCESS) RegCloseKey(hkey);
res = RegOpenKeyA(HKEY_CLASSES_ROOT, "Winetest.NoProgIdClass", &hkey); res = RegOpenKeyA(HKEY_CLASSES_ROOT, "Winetest.NoProgIdClass", &hkey);
ok(res == ERROR_FILE_NOT_FOUND, "key created\n"); ok(res == ERROR_FILE_NOT_FOUND, "key created\n");
res = RegOpenKeyA(HKEY_CLASSES_ROOT, "Winetest.Orphaned", &hkey); 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); if (res == ERROR_SUCCESS) RegCloseKey(hkey);
res = RegOpenKeyA(HKEY_CLASSES_ROOT, "Winetest.Orphaned2", &hkey); res = RegOpenKeyA(HKEY_CLASSES_ROOT, "Winetest.Orphaned2", &hkey);
ok(res == ERROR_FILE_NOT_FOUND, "key created\n"); 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"); r = MsiInstallProductA(msifile, "REMOVE=ALL");
ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); 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"); ok(res == ERROR_FILE_NOT_FOUND, "key not removed\n");
res = RegOpenKeyA(HKEY_CLASSES_ROOT, "Winetest.Orphaned", &hkey); 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); if (res == ERROR_SUCCESS) RegCloseKey(hkey);
res = RegOpenKeyA(HKEY_CLASSES_ROOT, "Winetest.Orphaned2", &hkey); res = RegOpenKeyA(HKEY_CLASSES_ROOT, "Winetest.Orphaned2", &hkey);
ok(res == ERROR_FILE_NOT_FOUND, "key not removed\n"); 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\\progid.txt", TRUE), "file not removed\n");
ok(!delete_pf("msitest", FALSE), "directory not removed\n"); ok(!delete_pf("msitest", FALSE), "directory not removed\n");