From dc2228305c821897091836b4d9593551a5ff9a65 Mon Sep 17 00:00:00 2001 From: Hans Leidekker Date: Fri, 10 Apr 2015 12:59:01 +0200 Subject: [PATCH] msi: Add support for patching global assembly files. --- dlls/msi/assembly.c | 71 +++++++++++++++++++++++++++- dlls/msi/files.c | 110 +++++++++++++++++++++++++++++++++++--------- dlls/msi/msipriv.h | 2 + 3 files changed, 161 insertions(+), 22 deletions(-) diff --git a/dlls/msi/assembly.c b/dlls/msi/assembly.c index a25b19db3b7..7151a8bd564 100644 --- a/dlls/msi/assembly.c +++ b/dlls/msi/assembly.c @@ -38,6 +38,8 @@ static HRESULT (WINAPI *pCreateAssemblyCacheNet40)( IAssemblyCache **, DWORD ); static HRESULT (WINAPI *pCreateAssemblyCacheSxs)( IAssemblyCache **, DWORD ); static HRESULT (WINAPI *pLoadLibraryShim)( LPCWSTR, LPCWSTR, LPVOID, HMODULE * ); static HRESULT (WINAPI *pGetFileVersion)( LPCWSTR, LPWSTR, DWORD, DWORD * ); +static HRESULT (WINAPI *pCreateAssemblyNameObject)( IAssemblyName **, LPCWSTR, DWORD, LPVOID ); +static HRESULT (WINAPI *pCreateAssemblyEnum)( IAssemblyEnum **, IUnknown *, IAssemblyName *, DWORD, LPVOID ); static HMODULE hfusion10, hfusion11, hfusion20, hfusion40, hmscoree, hsxs; @@ -79,8 +81,11 @@ static BOOL init_function_pointers( void ) pCreateAssemblyCacheNet20 = (void *)GetProcAddress( hfusion20, "CreateAssemblyCache" ); if (!pLoadLibraryShim( szFusion, szVersion40, NULL, &hfusion40 )) + { pCreateAssemblyCacheNet40 = (void *)GetProcAddress( hfusion40, "CreateAssemblyCache" ); - + pCreateAssemblyNameObject = (void *)GetProcAddress( hfusion40, "CreateAssemblyNameObject" ); + pCreateAssemblyEnum = (void *)GetProcAddress( hfusion40, "CreateAssemblyEnum" ); + } return TRUE; } @@ -259,6 +264,70 @@ static BOOL is_assembly_installed( IAssemblyCache *cache, const WCHAR *display_n return FALSE; } +WCHAR *msi_get_assembly_path( MSIPACKAGE *package, const WCHAR *displayname ) +{ + HRESULT hr; + ASSEMBLY_INFO info; + IAssemblyCache *cache = package->cache_net[CLR_VERSION_V40]; + + if (!cache) return NULL; + + memset( &info, 0, sizeof(info) ); + info.cbAssemblyInfo = sizeof(info); + hr = IAssemblyCache_QueryAssemblyInfo( cache, 0, displayname, &info ); + if (hr != E_NOT_SUFFICIENT_BUFFER) return NULL; + + if (!(info.pszCurrentAssemblyPathBuf = msi_alloc( info.cchBuf * sizeof(WCHAR) ))) return NULL; + + hr = IAssemblyCache_QueryAssemblyInfo( cache, 0, displayname, &info ); + if (FAILED( hr )) + { + msi_free( info.pszCurrentAssemblyPathBuf ); + return NULL; + } + TRACE("returning %s\n", debugstr_w(info.pszCurrentAssemblyPathBuf)); + return info.pszCurrentAssemblyPathBuf; +} + +IAssemblyEnum *msi_create_assembly_enum( MSIPACKAGE *package, const WCHAR *displayname ) +{ + HRESULT hr; + IAssemblyName *name; + IAssemblyEnum *ret; + WCHAR *str; + UINT len = 0; + + if (!pCreateAssemblyNameObject || !pCreateAssemblyEnum) return NULL; + + hr = pCreateAssemblyNameObject( &name, displayname, CANOF_PARSE_DISPLAY_NAME, NULL ); + if (FAILED( hr )) return NULL; + + hr = IAssemblyName_GetName( name, &len, NULL ); + if (hr != E_NOT_SUFFICIENT_BUFFER || !(str = msi_alloc( len * sizeof(WCHAR) ))) + { + IAssemblyName_Release( name ); + return NULL; + } + + hr = IAssemblyName_GetName( name, &len, str ); + IAssemblyName_Release( name ); + if (FAILED( hr )) + { + msi_free( str ); + return NULL; + } + + hr = pCreateAssemblyNameObject( &name, str, 0, NULL ); + msi_free( str ); + if (FAILED( hr )) return NULL; + + hr = pCreateAssemblyEnum( &ret, NULL, name, ASM_CACHE_GAC, NULL ); + IAssemblyName_Release( name ); + if (FAILED( hr )) return NULL; + + return ret; +} + static const WCHAR clr_version_v10[] = {'v','1','.','0','.','3','7','0','5',0}; static const WCHAR clr_version_v11[] = {'v','1','.','1','.','4','3','2','2',0}; static const WCHAR clr_version_v20[] = {'v','2','.','0','.','5','0','7','2','7',0}; diff --git a/dlls/msi/files.c b/dlls/msi/files.c index ca758dd9605..26e4cb12212 100644 --- a/dlls/msi/files.c +++ b/dlls/msi/files.c @@ -32,6 +32,8 @@ #include +#define COBJMACROS + #include "windef.h" #include "winbase.h" #include "winerror.h" @@ -493,6 +495,78 @@ static BOOL patchfiles_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action, return TRUE; } +static UINT patch_file( MSIPACKAGE *package, MSIFILEPATCH *patch ) +{ + UINT r = ERROR_SUCCESS; + WCHAR *tmpfile = msi_create_temp_file( package->db ); + + if (!tmpfile) return ERROR_INSTALL_FAILURE; + if (ApplyPatchToFileW( patch->path, patch->File->TargetPath, tmpfile, 0 )) + { + DeleteFileW( patch->File->TargetPath ); + MoveFileW( tmpfile, patch->File->TargetPath ); + } + else + { + WARN("failed to patch %s: %08x\n", debugstr_w(patch->File->TargetPath), GetLastError()); + r = ERROR_INSTALL_FAILURE; + } + DeleteFileW( patch->path ); + DeleteFileW( tmpfile ); + msi_free( tmpfile ); + return r; +} + +static UINT patch_assembly( MSIPACKAGE *package, MSIASSEMBLY *assembly, MSIFILEPATCH *patch ) +{ + UINT r = ERROR_FUNCTION_FAILED; + IAssemblyName *name; + IAssemblyEnum *iter; + + if (!(iter = msi_create_assembly_enum( package, assembly->display_name ))) + return ERROR_FUNCTION_FAILED; + + while ((IAssemblyEnum_GetNextAssembly( iter, NULL, &name, 0 ) == S_OK)) + { + WCHAR *displayname, *path; + UINT len = 0; + HRESULT hr; + + hr = IAssemblyName_GetDisplayName( name, NULL, &len, 0 ); + if (hr != E_NOT_SUFFICIENT_BUFFER || !(displayname = msi_alloc( len * sizeof(WCHAR) ))) + break; + + hr = IAssemblyName_GetDisplayName( name, displayname, &len, 0 ); + if (FAILED( hr )) + { + msi_free( displayname ); + break; + } + + if ((path = msi_get_assembly_path( package, displayname ))) + { + if (!CopyFileW( path, patch->File->TargetPath, FALSE )) + { + ERR("Failed to copy file %s -> %s (%u)\n", debugstr_w(path), + debugstr_w(patch->File->TargetPath), GetLastError() ); + msi_free( path ); + msi_free( displayname ); + IAssemblyName_Release( name ); + break; + } + r = patch_file( package, patch ); + msi_free( path ); + } + + msi_free( displayname ); + IAssemblyName_Release( name ); + if (r == ERROR_SUCCESS) break; + } + + IAssemblyEnum_Release( iter ); + return r; +} + UINT ACTION_PatchFiles( MSIPACKAGE *package ) { MSIFILEPATCH *patch; @@ -549,34 +623,28 @@ UINT ACTION_PatchFiles( MSIPACKAGE *package ) LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry ) { - WCHAR *tmpfile; - BOOL ret; + MSICOMPONENT *comp = patch->File->Component; if (!patch->path) continue; - if (!(tmpfile = msi_create_temp_file( package->db ))) - { - rc = ERROR_INSTALL_FAILURE; - goto done; - } - ret = ApplyPatchToFileW( patch->path, patch->File->TargetPath, tmpfile, 0 ); - if (ret) - { - DeleteFileW( patch->File->TargetPath ); - MoveFileW( tmpfile, patch->File->TargetPath ); - } + if (msi_is_global_assembly( comp )) + rc = patch_assembly( package, comp->assembly, patch ); else - WARN("failed to patch %s: %08x\n", debugstr_w(patch->File->TargetPath), GetLastError()); + rc = patch_file( package, patch ); - DeleteFileW( patch->path ); - DeleteFileW( tmpfile ); - msi_free( tmpfile ); - - if (!ret && !(patch->Attributes & msidbPatchAttributesNonVital)) + if (rc && !(patch->Attributes & msidbPatchAttributesNonVital)) { ERR("Failed to apply patch to file: %s\n", debugstr_w(patch->File->File)); - rc = ERROR_INSTALL_FAILURE; - goto done; + break; + } + + if (msi_is_global_assembly( comp )) + { + if ((rc = msi_install_assembly( package, comp ))) + { + ERR("Failed to install patched assembly\n"); + break; + } } } diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h index 9bec75f6239..d457a54c737 100644 --- a/dlls/msi/msipriv.h +++ b/dlls/msi/msipriv.h @@ -1042,6 +1042,8 @@ extern UINT msi_uninstall_assembly(MSIPACKAGE *, MSICOMPONENT *) DECLSPEC_HIDDEN extern BOOL msi_init_assembly_caches(MSIPACKAGE *) DECLSPEC_HIDDEN; extern void msi_destroy_assembly_caches(MSIPACKAGE *) DECLSPEC_HIDDEN; extern BOOL msi_is_global_assembly(MSICOMPONENT *) DECLSPEC_HIDDEN; +extern IAssemblyEnum *msi_create_assembly_enum(MSIPACKAGE *, const WCHAR *) DECLSPEC_HIDDEN; +extern WCHAR *msi_get_assembly_path(MSIPACKAGE *, const WCHAR *) DECLSPEC_HIDDEN; extern WCHAR *msi_font_version_from_file(const WCHAR *) DECLSPEC_HIDDEN; extern WCHAR **msi_split_string(const WCHAR *, WCHAR) DECLSPEC_HIDDEN; extern UINT msi_set_original_database_property(MSIDATABASE *, const WCHAR *) DECLSPEC_HIDDEN;