This is necessary for correct logic of MsiProvideAssembly[AW]().
From: Ratchanan Srirattanamet peathot+winehq@hotmail.com
This is necessary for correct logic of MsiProvideAssembly[AW]().
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58578 --- dlls/msi/assembly.c | 35 +++++++++++++--- dlls/msi/tests/action.c | 91 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+), 5 deletions(-)
diff --git a/dlls/msi/assembly.c b/dlls/msi/assembly.c index 6f386d64a88..a751ded9b45 100644 --- a/dlls/msi/assembly.c +++ b/dlls/msi/assembly.c @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include <assert.h> #include <stdarg.h>
#define COBJMACROS @@ -569,10 +570,13 @@ UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package ) HKEY hkey; GUID guid; DWORD size; - WCHAR buffer[43]; + DWORD buffer_len; + DWORD i; + WCHAR * buffer; MSIRECORD *uirow; MSIASSEMBLY *assembly = comp->assembly; BOOL win32; + BOOL wants_feature_in_descriptor;
if (!assembly || !comp->ComponentId) continue;
@@ -584,12 +588,29 @@ UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package ) } TRACE("publishing %s\n", debugstr_w(comp->Component));
+ wants_feature_in_descriptor = + (list_count(&package->features) >= 2 && assembly->feature); + buffer_len = 43 + (wants_feature_in_descriptor ? lstrlenW(assembly->feature) : 0); + size = buffer_len * sizeof(WCHAR); + buffer = malloc(size); + + i = 0; CLSIDFromString( package->ProductCode, &guid ); encode_base85_guid( &guid, buffer ); - buffer[20] = '>'; + i += 20; + + if (wants_feature_in_descriptor) { + lstrcpyW(buffer + i, assembly->feature); + i += lstrlenW(assembly->feature); + } + + buffer[i++] = '>'; CLSIDFromString( comp->ComponentId, &guid ); - encode_base85_guid( &guid, buffer + 21 ); - buffer[42] = 0; + encode_base85_guid( &guid, buffer + i ); i += 20; + buffer[i++] = 0; + buffer[i++] = 0; /* REG_MULTI_SZ */ + + assert(i == buffer_len);
win32 = assembly->attributes & msidbAssemblyAttributesWin32; if (assembly->application) @@ -598,11 +619,13 @@ UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package ) if (!file) { WARN("no matching file %s for local assembly\n", debugstr_w(assembly->application)); + free(buffer); continue; } if ((res = open_local_assembly_key( package->Context, win32, file->TargetPath, &hkey ))) { WARN( "failed to open local assembly key %ld\n", res ); + free(buffer); return ERROR_FUNCTION_FAILED; } } @@ -611,15 +634,17 @@ UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package ) if ((res = open_global_assembly_key( package->Context, win32, &hkey ))) { WARN( "failed to open global assembly key %ld\n", res ); + free(buffer); 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 %ld\n", res ); } RegCloseKey( hkey ); + free(buffer);
uirow = MSI_CreateRecord( 2 ); MSI_RecordSetStringW( uirow, 2, assembly->display_name ); diff --git a/dlls/msi/tests/action.c b/dlls/msi/tests/action.c index 43e66b7c8ac..abf9332c45f 100644 --- a/dlls/msi/tests/action.c +++ b/dlls/msi/tests/action.c @@ -1800,6 +1800,13 @@ static const char pa_feature_dat[] = "Feature\tFeature\n" "assembly\t\t\tassembly feature\t1\t2\tMSITESTDIR\t0\n";
+static const char pa_feature_with_2nd_feature_dat[] = + "Feature\tFeature_Parent\tTitle\tDescription\tDisplay\tLevel\tDirectory_\tAttributes\n" + "s38\tS38\tL64\tL255\tI2\ti2\tS72\ti2\n" + "Feature\tFeature\n" + "assembly\t\t\tassembly feature\t1\t2\tMSITESTDIR\t0\n" + "2nd_feature\t\tSecond feature\tSpace filler\t1\t2\tMSITESTDIR\t0\n"; + static const char pa_feature_comp_dat[] = "Feature_\tComponent_\n" "s38\ts72\n" @@ -2336,6 +2343,21 @@ static const msi_table pa_tables[] = ADD_TABLE(property) };
+static const msi_table pa_tables_with_2nd_feature[] = +{ + ADD_TABLE(directory), + ADD_TABLE(pa_component), + ADD_TABLE(pa_feature_with_2nd_feature), + ADD_TABLE(pa_feature_comp), + ADD_TABLE(pa_file), + ADD_TABLE(pa_msi_assembly), + ADD_TABLE(pa_msi_assembly_name), + ADD_TABLE(pa_install_exec_seq), + ADD_TABLE(pa_custom_action), + ADD_TABLE(media), + ADD_TABLE(property) +}; + static const msi_table rep_tables[] = { ADD_TABLE(directory), @@ -6400,6 +6422,75 @@ static void test_publish_assemblies(void) res = RegOpenKeyA(HKEY_CLASSES_ROOT, path, &hkey); ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %ld\n", res);
+ /* When there are more than 1 feature in MSI package, the feature has to be recorded in + the assembly publication entry. */ + DeleteFileA(msifile); + create_database(msifile, pa_tables_with_2nd_feature, ARRAY_SIZE(pa_tables_with_2nd_feature)); + + r = MsiInstallProductA(msifile, "ALLUSERS=1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + + access = KEY_QUERY_VALUE; + res = RegOpenKeyExA(HKEY_CLASSES_ROOT, classes_path_dotnet, 0, access, &hkey); + if (res == ERROR_FILE_NOT_FOUND && is_wow64) /* Vista WOW64 */ + { + trace("Using 64-bit registry view for HKCR\Installer\n"); + access = KEY_QUERY_VALUE | KEY_WOW64_64KEY; + res = RegOpenKeyExA(HKEY_CLASSES_ROOT, classes_path_dotnet, 0, access, &hkey); + } + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %ld\n", res); + CHECK_REG_MULTI(hkey, name_dotnet, "rcHQPHq?CA@Uv-XqMI1eassembly>Z'q,T*76M@=YEg6My?~]\0"); + RegCloseKey(hkey); + + path = (is_wow64 || is_64bit) ? classes_path_dotnet_local_wow64 : classes_path_dotnet_local; + res = RegOpenKeyExA(HKEY_CLASSES_ROOT, path, 0, access, &hkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %ld\n", res); + CHECK_REG_MULTI(hkey, name_dotnet_local, "rcHQPHq?CA@Uv-XqMI1eassembly>LF,8A?0d.AW@vcZ$Cgox\0"); + RegCloseKey(hkey); + + res = RegOpenKeyExA(HKEY_CLASSES_ROOT, classes_path_win32, 0, access, &hkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %ld\n", res); + CHECK_REG_MULTI(hkey, name_win32, "rcHQPHq?CA@Uv-XqMI1eassembly>}NJjwR'%D9v1p!v{WV(%\0"); + RegCloseKey(hkey); + + path = (is_wow64 || is_64bit) ? classes_path_win32_local_wow64 : classes_path_win32_local; + res = RegOpenKeyExA(HKEY_CLASSES_ROOT, path, 0, access, &hkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %ld\n", res); + CHECK_REG_MULTI(hkey, name_win32_local, "rcHQPHq?CA@Uv-XqMI1eassembly>C)Uvlj*53A)u(QQ9=)X!\0"); + RegCloseKey(hkey); + + /* No registration is done for a local assembly with no matching file */ + path = (is_wow64 || is_64bit) ? classes_path_fake_local_wow64 : classes_path_fake_local; + res = RegOpenKeyExA(HKEY_CLASSES_ROOT, path, 0, access, &hkey); + ok(res == ERROR_FILE_NOT_FOUND, "got %ld\n", res); + + 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); + if (res == ERROR_SUCCESS) + { + res = RegDeleteValueA(hkey, name_dotnet); + ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %ld\n", res); + RegCloseKey(hkey); + } + + path = (is_wow64 || is_64bit) ? classes_path_dotnet_local_wow64 : classes_path_dotnet_local; + res = RegOpenKeyA(HKEY_CLASSES_ROOT, path, &hkey); + ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %ld\n", res); + + res = RegOpenKeyA(HKEY_CLASSES_ROOT, classes_path_win32, &hkey); + if (res == ERROR_SUCCESS) + { + res = RegDeleteValueA(hkey, name_win32); + ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %ld\n", res); + RegCloseKey(hkey); + } + + path = (is_wow64 || is_64bit) ? classes_path_win32_local_wow64 : classes_path_win32_local; + res = RegOpenKeyA(HKEY_CLASSES_ROOT, path, &hkey); + ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %ld\n", res); + done: DeleteFileA("msitest\win32.txt"); DeleteFileA("msitest\win32_local.txt");