From 92203f10530645792a1ad5964d00b4fefba04de4 Mon Sep 17 00:00:00 2001 From: Hans Leidekker Date: Mon, 24 Jan 2011 15:22:11 +0100 Subject: [PATCH] msi: Implement the MsiPublishAssemblies and MsiUnpublishAssemblies standard actions. --- dlls/msi/action.c | 7 -- dlls/msi/assembly.c | 241 +++++++++++++++++++++++++++++++++++++++- dlls/msi/msipriv.h | 1 + dlls/msi/tests/action.c | 6 +- 4 files changed, 238 insertions(+), 17 deletions(-) diff --git a/dlls/msi/action.c b/dlls/msi/action.c index 5a97d17dfdc..061d2f397e5 100644 --- a/dlls/msi/action.c +++ b/dlls/msi/action.c @@ -7172,13 +7172,6 @@ static UINT ACTION_IsolateComponents( MSIPACKAGE *package ) return msi_unimplemented_action_stub( package, "IsolateComponents", table ); } -static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package ) -{ - static const WCHAR table[] = { - 'M','s','i','A','s','s','e','m','b','l','y',0 }; - return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table ); -} - static UINT ACTION_RMCCPSearch( MSIPACKAGE *package ) { static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 }; diff --git a/dlls/msi/assembly.c b/dlls/msi/assembly.c index f59d2eb0cc0..a4d58d17c06 100644 --- a/dlls/msi/assembly.c +++ b/dlls/msi/assembly.c @@ -24,6 +24,7 @@ #include "windef.h" #include "winbase.h" +#include "winreg.h" #include "wine/debug.h" #include "wine/unicode.h" #include "msipriv.h" @@ -318,20 +319,250 @@ UINT install_assembly( MSIPACKAGE *package, MSICOMPONENT *comp ) return ERROR_SUCCESS; } +static WCHAR *build_local_assembly_path( const WCHAR *filename ) +{ + UINT i; + WCHAR *ret; + + if (!(ret = msi_alloc( (strlenW( filename ) + 1) * sizeof(WCHAR) ))) + return NULL; + + for (i = 0; filename[i]; i++) + { + if (filename[i] == '\\' || filename[i] == '/') ret[i] = '|'; + else ret[i] = filename[i]; + } + ret[i] = 0; + return ret; +} + +static LONG open_assemblies_key( UINT context, BOOL win32, HKEY *hkey ) +{ + static const WCHAR path_win32[] = + {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\', + 'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',0}; + static const WCHAR path_dotnet[] = + {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\', + 'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0}; + static const WCHAR classes_path_win32[] = + {'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',0}; + static const WCHAR classes_path_dotnet[] = + {'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0}; + HKEY root; + const WCHAR *path; + + if (context == MSIINSTALLCONTEXT_MACHINE) + { + root = HKEY_CLASSES_ROOT; + if (win32) path = classes_path_win32; + else path = classes_path_dotnet; + } + else + { + root = HKEY_CURRENT_USER; + if (win32) path = path_win32; + else path = path_dotnet; + } + return RegCreateKeyW( root, path, hkey ); +} + +static LONG open_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename, HKEY *hkey ) +{ + LONG res; + HKEY root; + WCHAR *path; + + if (!(path = build_local_assembly_path( filename ))) + return ERROR_OUTOFMEMORY; + + if ((res = open_assemblies_key( context, win32, &root ))) + { + msi_free( path ); + return res; + } + res = RegCreateKeyW( root, path, hkey ); + RegCloseKey( root ); + msi_free( path ); + return res; +} + +static LONG delete_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename ) +{ + LONG res; + HKEY root; + WCHAR *path; + + if (!(path = build_local_assembly_path( filename ))) + return ERROR_OUTOFMEMORY; + + if ((res = open_assemblies_key( context, win32, &root ))) + { + msi_free( path ); + return res; + } + res = RegDeleteKeyW( root, path ); + RegCloseKey( root ); + msi_free( path ); + return res; +} + +static LONG open_global_assembly_key( UINT context, BOOL win32, HKEY *hkey ) +{ + static const WCHAR path_win32[] = + {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\', + 'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\', + 'G','l','o','b','a','l',0}; + static const WCHAR path_dotnet[] = + {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\', + 'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\', + 'G','l','o','b','a','l',0}; + static const WCHAR classes_path_win32[] = + {'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\', + 'G','l','o','b','a','l',0}; + static const WCHAR classes_path_dotnet[] = + {'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\','G','l','o','b','a','l',0}; + HKEY root; + const WCHAR *path; + + if (context == MSIINSTALLCONTEXT_MACHINE) + { + root = HKEY_CLASSES_ROOT; + if (win32) path = classes_path_win32; + else path = classes_path_dotnet; + } + else + { + root = HKEY_CURRENT_USER; + if (win32) path = path_win32; + else path = path_dotnet; + } + return RegCreateKeyW( root, path, hkey ); +} + UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package ) { - MSIRECORD *uirow; MSICOMPONENT *comp; LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry) { - if (!comp->assembly || !comp->Enabled) - continue; + LONG res; + HKEY hkey; + GUID guid; + DWORD size; + WCHAR buffer[43]; + MSIRECORD *uirow; + MSIASSEMBLY *assembly = comp->assembly; + BOOL win32; - /* FIXME: write assembly registry values */ + if (!assembly || !comp->ComponentId) continue; + + if (!comp->Enabled) + { + TRACE("component is disabled: %s\n", debugstr_w(comp->Component)); + continue; + } + + if (comp->ActionRequest != INSTALLSTATE_LOCAL) + { + TRACE("Component not scheduled for installation: %s\n", debugstr_w(comp->Component)); + comp->Action = comp->Installed; + continue; + } + comp->Action = INSTALLSTATE_LOCAL; + + TRACE("publishing %s\n", debugstr_w(comp->Component)); + + CLSIDFromString( package->ProductCode, &guid ); + encode_base85_guid( &guid, buffer ); + buffer[20] = '>'; + CLSIDFromString( comp->ComponentId, &guid ); + encode_base85_guid( &guid, buffer + 21 ); + buffer[42] = 0; + + win32 = assembly->attributes & msidbAssemblyAttributesWin32; + if (assembly->application) + { + MSIFILE *file = get_loaded_file( package, assembly->application ); + if ((res = open_local_assembly_key( package->Context, win32, file->TargetPath, &hkey ))) + { + WARN("failed to open local assembly key %d\n", res); + return ERROR_FUNCTION_FAILED; + } + } + else + { + if ((res = open_global_assembly_key( package->Context, win32, &hkey ))) + { + WARN("failed to open global assembly key %d\n", res); + return ERROR_FUNCTION_FAILED; + } + } + size = sizeof(buffer); + if ((res = RegSetValueExW( hkey, assembly->display_name, 0, REG_MULTI_SZ, (const BYTE *)buffer, size ))) + { + WARN("failed to set assembly value %d\n", res); + } + RegCloseKey( hkey ); uirow = MSI_CreateRecord( 2 ); - MSI_RecordSetStringW( uirow, 2, comp->assembly->display_name ); + MSI_RecordSetStringW( uirow, 2, assembly->display_name ); + ui_actiondata( package, szMsiPublishAssemblies, uirow ); + msiobj_release( &uirow->hdr ); + } + return ERROR_SUCCESS; +} + +UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package ) +{ + MSICOMPONENT *comp; + + LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry) + { + LONG res; + MSIRECORD *uirow; + MSIASSEMBLY *assembly = comp->assembly; + BOOL win32; + + if (!assembly || !comp->ComponentId) continue; + + if (!comp->Enabled) + { + TRACE("component is disabled: %s\n", debugstr_w(comp->Component)); + continue; + } + + if (comp->ActionRequest != INSTALLSTATE_ABSENT) + { + TRACE("Component not scheduled for removal: %s\n", debugstr_w(comp->Component)); + comp->Action = comp->Installed; + continue; + } + comp->Action = INSTALLSTATE_ABSENT; + + TRACE("unpublishing %s\n", debugstr_w(comp->Component)); + + win32 = assembly->attributes & msidbAssemblyAttributesWin32; + if (assembly->application) + { + MSIFILE *file = get_loaded_file( package, assembly->application ); + if ((res = delete_local_assembly_key( package->Context, win32, file->TargetPath ))) + WARN("failed to delete local assembly key %d\n", res); + } + else + { + HKEY hkey; + if ((res = open_global_assembly_key( package->Context, win32, &hkey ))) + WARN("failed to delete global assembly key %d\n", res); + else + { + if ((res = RegDeleteValueW( hkey, assembly->display_name ))) + WARN("failed to delete global assembly value %d\n", res); + RegCloseKey( hkey ); + } + } + + uirow = MSI_CreateRecord( 2 ); + MSI_RecordSetStringW( uirow, 2, assembly->display_name ); ui_actiondata( package, szMsiPublishAssemblies, uirow ); msiobj_release( &uirow->hdr ); } diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h index b9eb3ea6ddb..0f5a33cbcdc 100644 --- a/dlls/msi/msipriv.h +++ b/dlls/msi/msipriv.h @@ -922,6 +922,7 @@ extern UINT ACTION_UnregisterFonts(MSIPACKAGE *package); extern UINT ACTION_UnregisterMIMEInfo(MSIPACKAGE *package); extern UINT ACTION_UnregisterProgIdInfo(MSIPACKAGE *package); extern UINT ACTION_MsiPublishAssemblies(MSIPACKAGE *package); +extern UINT ACTION_MsiUnpublishAssemblies(MSIPACKAGE *package); /* Helpers */ extern DWORD deformat_string(MSIPACKAGE *package, LPCWSTR ptr, WCHAR** data ); diff --git a/dlls/msi/tests/action.c b/dlls/msi/tests/action.c index d0184390a16..9d90b425fe4 100644 --- a/dlls/msi/tests/action.c +++ b/dlls/msi/tests/action.c @@ -5903,7 +5903,6 @@ static void test_publish_assemblies(void) } ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); - todo_wine { res = RegOpenKeyA(HKEY_CURRENT_USER, path_dotnet, &hkey); ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); CHECK_REG_STR(hkey, name_dotnet, "rcHQPHq?CA@Uv-XqMI1e>Z'q,T*76M@=YEg6My?~]"); @@ -5925,7 +5924,6 @@ static void test_publish_assemblies(void) ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); CHECK_REG_STR(hkey, name_win32_local, "rcHQPHq?CA@Uv-XqMI1e>C)Uvlj*53A)u(QQ9=)X!"); RegCloseKey(hkey); - } r = MsiInstallProductA(msifile, "REMOVE=ALL"); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); @@ -5959,7 +5957,6 @@ static void test_publish_assemblies(void) r = MsiInstallProductA(msifile, "ALLUSERS=1"); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); - todo_wine { res = RegOpenKeyA(HKEY_CLASSES_ROOT, classes_path_dotnet, &hkey); ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); CHECK_REG_STR(hkey, name_dotnet, "rcHQPHq?CA@Uv-XqMI1e>Z'q,T*76M@=YEg6My?~]"); @@ -5981,9 +5978,8 @@ static void test_publish_assemblies(void) ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); CHECK_REG_STR(hkey, name_win32_local, "rcHQPHq?CA@Uv-XqMI1e>C)Uvlj*53A)u(QQ9=)X!"); RegCloseKey(hkey); - } - r = MsiInstallProductA(msifile, "REMOVE=ALL"); + r = MsiInstallProductA(msifile, "REMOVE=ALL ALLUSERS=1"); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); res = RegOpenKeyA(HKEY_CLASSES_ROOT, classes_path_dotnet, &hkey);