diff --git a/dlls/msi/Makefile.in b/dlls/msi/Makefile.in index b82483697ce..f41a92ae0bf 100644 --- a/dlls/msi/Makefile.in +++ b/dlls/msi/Makefile.in @@ -7,6 +7,7 @@ C_SRCS = \ action.c \ alter.c \ appsearch.c \ + assembly.c \ automation.c \ classes.c \ create.c \ diff --git a/dlls/msi/action.c b/dlls/msi/action.c index e8ad30b01a2..6292f4f4601 100644 --- a/dlls/msi/action.c +++ b/dlls/msi/action.c @@ -35,7 +35,6 @@ #include "shlobj.h" #include "objbase.h" #include "mscoree.h" -#include "fusion.h" #include "shlwapi.h" #include "wine/unicode.h" #include "winver.h" @@ -110,8 +109,6 @@ static const WCHAR szIsolateComponents[] = {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0}; static const WCHAR szMigrateFeatureStates[] = {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0}; -static const WCHAR szMsiPublishAssemblies[] = - {'M','s','i','P','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0}; static const WCHAR szMsiUnpublishAssemblies[] = {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0}; static const WCHAR szInstallODBC[] = @@ -1227,6 +1224,7 @@ static UINT load_component( MSIRECORD *row, LPVOID param ) comp->Installed = INSTALLSTATE_UNKNOWN; msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN); + comp->assembly = load_assembly( package, comp ); return ERROR_SUCCESS; } @@ -2151,6 +2149,43 @@ static BOOL hash_matches( MSIFILE *file ) return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) ); } +static WCHAR *get_temp_dir( void ) +{ + static UINT id; + WCHAR tmp[MAX_PATH], dir[MAX_PATH]; + + GetTempPathW( MAX_PATH, tmp ); + for (;;) + { + if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL; + if (CreateDirectoryW( dir, NULL )) break; + } + return strdupW( dir ); +} + +static void set_target_path( MSIPACKAGE *package, MSIFILE *file ) +{ + MSIASSEMBLY *assembly = file->Component->assembly; + + TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName)); + + msi_free( file->TargetPath ); + if (assembly) + { + if (!assembly->tempdir) assembly->tempdir = get_temp_dir(); + file->TargetPath = build_directory_name( 2, assembly->tempdir, file->FileName ); + track_tempfile( package, file->TargetPath ); + } + else + { + WCHAR *dir = resolve_folder( package, file->Component->Directory, FALSE, FALSE, TRUE, NULL ); + file->TargetPath = build_directory_name( 2, dir, file->FileName ); + msi_free( dir ); + } + + TRACE("resolves to %s\n", debugstr_w(file->TargetPath)); +} + static UINT set_file_install_states( MSIPACKAGE *package ) { VS_FIXEDFILEINFO *file_version; @@ -2158,27 +2193,18 @@ static UINT set_file_install_states( MSIPACKAGE *package ) LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) { - MSICOMPONENT* comp = file->Component; + MSICOMPONENT *comp = file->Component; DWORD file_size; - LPWSTR p; if (!comp->Enabled) continue; if (file->IsCompressed) comp->ForceLocalState = TRUE; - /* calculate target */ - p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL); - msi_free(file->TargetPath); + set_target_path( package, file ); - TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName)); - - file->TargetPath = build_directory_name(2, p, file->FileName); - msi_free(p); - - TRACE("file %s resolves to %s\n", debugstr_w(file->File), debugstr_w(file->TargetPath)); - - if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES) + if ((comp->assembly && !comp->assembly->installed) || + GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES) { file->state = msifs_missing; comp->Cost += file->FileSize; @@ -6698,481 +6724,6 @@ static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package ) return rc; } -typedef struct tagMSIASSEMBLY -{ - struct list entry; - MSICOMPONENT *component; - MSIFEATURE *feature; - MSIFILE *file; - LPWSTR manifest; - LPWSTR application; - LPWSTR display_name; - DWORD attributes; - BOOL installed; -} MSIASSEMBLY; - -static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache, - DWORD dwReserved); -static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion, - LPVOID pvReserved, HMODULE *phModDll); - -static BOOL init_functionpointers(void) -{ - HRESULT hr; - HMODULE hfusion; - HMODULE hmscoree; - - static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0}; - - hmscoree = LoadLibraryA("mscoree.dll"); - if (!hmscoree) - { - WARN("mscoree.dll not available\n"); - return FALSE; - } - - pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim"); - if (!pLoadLibraryShim) - { - WARN("LoadLibraryShim not available\n"); - FreeLibrary(hmscoree); - return FALSE; - } - - hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion); - if (FAILED(hr)) - { - WARN("fusion.dll not available\n"); - FreeLibrary(hmscoree); - return FALSE; - } - - pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache"); - - FreeLibrary(hmscoree); - return TRUE; -} - -static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly, - LPWSTR path) -{ - IAssemblyCache *cache; - MSIRECORD *uirow; - HRESULT hr; - UINT r = ERROR_FUNCTION_FAILED; - - TRACE("installing assembly: %s\n", debugstr_w(path)); - - uirow = MSI_CreateRecord( 2 ); - MSI_RecordSetStringW( uirow, 2, assembly->display_name ); - ui_actiondata( package, szMsiPublishAssemblies, uirow ); - msiobj_release( &uirow->hdr ); - - if (assembly->feature) - msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL); - - if (assembly->manifest) - FIXME("Manifest unhandled\n"); - - if (assembly->application) - { - FIXME("Assembly should be privately installed\n"); - return ERROR_SUCCESS; - } - - if (assembly->attributes == msidbAssemblyAttributesWin32) - { - FIXME("Win32 assemblies not handled\n"); - return ERROR_SUCCESS; - } - - hr = pCreateAssemblyCache(&cache, 0); - if (FAILED(hr)) - goto done; - - hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL); - if (FAILED(hr)) - ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr); - - r = ERROR_SUCCESS; - -done: - IAssemblyCache_Release(cache); - return r; -} - -typedef struct tagASSEMBLY_LIST -{ - MSIPACKAGE *package; - IAssemblyCache *cache; - struct list *assemblies; -} ASSEMBLY_LIST; - -typedef struct tagASSEMBLY_NAME -{ - LPWSTR name; - LPWSTR version; - LPWSTR culture; - LPWSTR pubkeytoken; -} ASSEMBLY_NAME; - -static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param) -{ - ASSEMBLY_NAME *asmname = param; - LPCWSTR name = MSI_RecordGetString(rec, 2); - LPWSTR val = msi_dup_record_field(rec, 3); - - static const WCHAR Name[] = {'N','a','m','e',0}; - static const WCHAR Version[] = {'V','e','r','s','i','o','n',0}; - static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0}; - static const WCHAR PublicKeyToken[] = { - 'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0}; - - if (!strcmpiW(name, Name)) - asmname->name = val; - else if (!strcmpiW(name, Version)) - asmname->version = val; - else if (!strcmpiW(name, Culture)) - asmname->culture = val; - else if (!strcmpiW(name, PublicKeyToken)) - asmname->pubkeytoken = val; - else - msi_free(val); - - return ERROR_SUCCESS; -} - -static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append) -{ - if (!*str) - { - *size = lstrlenW(append) + 1; - *str = msi_alloc((*size) * sizeof(WCHAR)); - lstrcpyW(*str, append); - return; - } - - (*size) += lstrlenW(append); - *str = msi_realloc(*str, (*size) * sizeof(WCHAR)); - lstrcatW(*str, append); -} - -static WCHAR *get_assembly_display_name( MSIDATABASE *db, MSICOMPONENT *comp ) -{ - static const WCHAR separator[] = {',',' ',0}; - static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0}; - static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0}; - static const WCHAR PublicKeyToken[] = {'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0}; - static const WCHAR query[] = { - 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', - '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ', - 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`', - '=','\'','%','s','\'',0}; - ASSEMBLY_NAME name; - MSIQUERY *view; - LPWSTR display_name; - DWORD size; - UINT r; - - display_name = NULL; - memset( &name, 0, sizeof(ASSEMBLY_NAME) ); - - r = MSI_OpenQuery( db, &view, query, comp->Component ); - if (r != ERROR_SUCCESS) - return NULL; - - MSI_IterateRecords( view, NULL, parse_assembly_name, &name ); - msiobj_release( &view->hdr ); - - if (!name.name) - { - ERR("No assembly name specified!\n"); - return NULL; - } - - append_str( &display_name, &size, name.name ); - - if (name.version) - { - append_str( &display_name, &size, separator ); - append_str( &display_name, &size, Version ); - append_str( &display_name, &size, name.version ); - } - if (name.culture) - { - append_str( &display_name, &size, separator ); - append_str( &display_name, &size, Culture ); - append_str( &display_name, &size, name.culture ); - } - if (name.pubkeytoken) - { - append_str( &display_name, &size, separator ); - append_str( &display_name, &size, PublicKeyToken ); - append_str( &display_name, &size, name.pubkeytoken ); - } - - msi_free( name.name ); - msi_free( name.version ); - msi_free( name.culture ); - msi_free( name.pubkeytoken ); - - return display_name; -} - -static BOOL check_assembly_installed( MSIDATABASE *db, IAssemblyCache *cache, MSICOMPONENT *comp ) -{ - ASSEMBLY_INFO asminfo; - LPWSTR disp; - BOOL found = FALSE; - HRESULT hr; - - disp = get_assembly_display_name( db, comp ); - if (!disp) - return FALSE; - - memset( &asminfo, 0, sizeof(ASSEMBLY_INFO) ); - asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO); - - hr = IAssemblyCache_QueryAssemblyInfo( cache, QUERYASMINFO_FLAG_VALIDATE, disp, &asminfo ); - if (SUCCEEDED(hr)) - found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED); - - msi_free( disp ); - return found; -} - -static UINT load_assembly(MSIRECORD *rec, LPVOID param) -{ - ASSEMBLY_LIST *list = param; - MSIASSEMBLY *assembly; - LPCWSTR component; - - assembly = msi_alloc_zero(sizeof(MSIASSEMBLY)); - if (!assembly) - return ERROR_OUTOFMEMORY; - - component = MSI_RecordGetString(rec, 1); - assembly->component = get_loaded_component(list->package, component); - if (!assembly->component) - return ERROR_SUCCESS; - - if (assembly->component->ActionRequest != INSTALLSTATE_LOCAL && - assembly->component->ActionRequest != INSTALLSTATE_SOURCE) - { - TRACE("Component not scheduled for installation: %s\n", debugstr_w(component)); - assembly->component->Action = assembly->component->Installed; - return ERROR_SUCCESS; - } - assembly->component->Action = assembly->component->ActionRequest; - - assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2)); - assembly->file = get_loaded_file(list->package, assembly->component->KeyPath); - - if (!assembly->file) - { - ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath)); - return ERROR_FUNCTION_FAILED; - } - - assembly->manifest = strdupW(MSI_RecordGetString(rec, 3)); - assembly->application = strdupW(MSI_RecordGetString(rec, 4)); - assembly->attributes = MSI_RecordGetInteger(rec, 5); - - if (assembly->application) - { - WCHAR version[24]; - DWORD size = sizeof(version)/sizeof(WCHAR); - - /* FIXME: we should probably check the manifest file here */ - - if (!MsiGetFileVersionW(assembly->file->TargetPath, version, &size, NULL, NULL) && - (!assembly->file->Version || strcmpW(version, assembly->file->Version) >= 0)) - { - assembly->installed = TRUE; - } - } - else - assembly->installed = check_assembly_installed(list->package->db, - list->cache, - assembly->component); - - list_add_head(list->assemblies, &assembly->entry); - return ERROR_SUCCESS; -} - -static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies) -{ - IAssemblyCache *cache = NULL; - ASSEMBLY_LIST list; - MSIQUERY *view; - HRESULT hr; - UINT r; - - static const WCHAR query[] = - {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', - '`','M','s','i','A','s','s','e','m','b','l','y','`',0}; - - r = MSI_DatabaseOpenViewW(package->db, query, &view); - if (r != ERROR_SUCCESS) - return ERROR_SUCCESS; - - hr = pCreateAssemblyCache(&cache, 0); - if (FAILED(hr)) - return ERROR_FUNCTION_FAILED; - - list.package = package; - list.cache = cache; - list.assemblies = assemblies; - - r = MSI_IterateRecords(view, NULL, load_assembly, &list); - msiobj_release(&view->hdr); - - IAssemblyCache_Release(cache); - - return r; -} - -static void free_assemblies(struct list *assemblies) -{ - struct list *item, *cursor; - - LIST_FOR_EACH_SAFE(item, cursor, assemblies) - { - MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry); - - list_remove(&assembly->entry); - msi_free(assembly->application); - msi_free(assembly->manifest); - msi_free(assembly->display_name); - msi_free(assembly); - } -} - -static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out) -{ - MSIASSEMBLY *assembly; - - LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry) - { - if (!strcmpW( assembly->file->File, file )) - { - *out = assembly; - return TRUE; - } - } - - return FALSE; -} - -static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action, - LPWSTR *path, DWORD *attrs, PVOID user) -{ - MSIASSEMBLY *assembly; - WCHAR temppath[MAX_PATH]; - struct list *assemblies = user; - UINT r; - - if (!find_assembly(assemblies, file, &assembly)) - return FALSE; - - GetTempPathW(MAX_PATH, temppath); - PathAddBackslashW(temppath); - lstrcatW(temppath, assembly->file->FileName); - - if (action == MSICABEXTRACT_BEGINEXTRACT) - { - if (assembly->installed) - return FALSE; - - *path = strdupW(temppath); - *attrs = assembly->file->Attributes; - } - else if (action == MSICABEXTRACT_FILEEXTRACTED) - { - assembly->installed = TRUE; - - r = install_assembly(package, assembly, temppath); - if (r != ERROR_SUCCESS) - ERR("Failed to install assembly\n"); - } - - return TRUE; -} - -static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package ) -{ - UINT r; - struct list assemblies = LIST_INIT(assemblies); - MSIASSEMBLY *assembly; - MSIMEDIAINFO *mi; - - if (!init_functionpointers() || !pCreateAssemblyCache) - return ERROR_FUNCTION_FAILED; - - r = load_assemblies(package, &assemblies); - if (r != ERROR_SUCCESS) - goto done; - - if (list_empty(&assemblies)) - goto done; - - mi = msi_alloc_zero(sizeof(MSIMEDIAINFO)); - if (!mi) - { - r = ERROR_OUTOFMEMORY; - goto done; - } - - LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry) - { - if (assembly->installed && !mi->is_continuous) - continue; - - if (assembly->file->IsCompressed) - { - if (assembly->file->disk_id != mi->disk_id || mi->is_continuous) - { - MSICABDATA data; - - r = ready_media(package, assembly->file, mi); - if (r != ERROR_SUCCESS) - { - ERR("Failed to ready media\n"); - break; - } - - data.mi = mi; - data.package = package; - data.cb = installassembly_cb; - data.user = &assemblies; - - if (!msi_cabextract(package, mi, &data)) - { - ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet)); - r = ERROR_FUNCTION_FAILED; - break; - } - } - } - else - { - LPWSTR source = resolve_file_source(package, assembly->file); - - r = install_assembly(package, assembly, source); - if (r != ERROR_SUCCESS) - ERR("Failed to install assembly\n"); - - msi_free(source); - } - - /* FIXME: write Installer assembly reg values */ - } - -done: - free_assemblies(&assemblies); - return r; -} - static UINT ACTION_ValidateProductID( MSIPACKAGE *package ) { LPWSTR key, template, id; diff --git a/dlls/msi/assembly.c b/dlls/msi/assembly.c new file mode 100644 index 00000000000..ae2338357ea --- /dev/null +++ b/dlls/msi/assembly.c @@ -0,0 +1,371 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2010 Hans Leidekker for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "wine/debug.h" +#include "wine/unicode.h" +#include "msipriv.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +static HRESULT (WINAPI *pCreateAssemblyCacheNet)( IAssemblyCache **, DWORD ); +static HRESULT (WINAPI *pCreateAssemblyCacheSxs)( IAssemblyCache **, DWORD ); +static HRESULT (WINAPI *pLoadLibraryShim)( LPCWSTR, LPCWSTR, LPVOID, HMODULE * ); + +static BOOL init_function_pointers( void ) +{ + static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0}; + HMODULE hfusion, hmscoree, hsxs; + + if (pCreateAssemblyCacheNet) return TRUE; + + if (!(hmscoree = LoadLibraryA( "mscoree.dll" ))) + { + WARN("mscoree.dll not available\n"); + return FALSE; + } + if (!(pLoadLibraryShim = (void *)GetProcAddress( hmscoree, "LoadLibraryShim" ))) + { + WARN("LoadLibraryShim not available\n"); + FreeLibrary( hmscoree ); + return FALSE; + } + if (FAILED( pLoadLibraryShim( szFusion, NULL, NULL, &hfusion ))) + { + WARN("fusion.dll not available\n"); + FreeLibrary( hmscoree ); + return FALSE; + } + pCreateAssemblyCacheNet = (void *)GetProcAddress( hfusion, "CreateAssemblyCache" ); + FreeLibrary( hmscoree ); + if (!(hsxs = LoadLibraryA( "sxs.dll" ))) + { + WARN("sxs.dll not available\n"); + FreeLibrary( hfusion ); + return FALSE; + } + pCreateAssemblyCacheSxs = (void *)GetProcAddress( hsxs, "CreateAssemblyCache" ); + return TRUE; +} + +static BOOL init_assembly_caches( MSIPACKAGE *package ) +{ + HRESULT hr; + + if (!init_function_pointers()) return FALSE; + if (package->cache_net) return TRUE; + + hr = pCreateAssemblyCacheNet( &package->cache_net, 0 ); + if (hr != S_OK) return FALSE; + + hr = pCreateAssemblyCacheSxs( &package->cache_sxs, 0 ); + if (hr != S_OK) + { + IAssemblyCache_Release( package->cache_net ); + package->cache_net = NULL; + return FALSE; + } + return TRUE; +} + +MSIRECORD *get_assembly_record( MSIPACKAGE *package, const WCHAR *comp ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','M','s','i','A','s','s','e','m','b','l','y','`',' ', + 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`', + ' ','=',' ','\'','%','s','\'',0}; + MSIQUERY *view; + MSIRECORD *rec; + UINT r; + + r = MSI_OpenQuery( package->db, &view, query, comp ); + if (r != ERROR_SUCCESS) + return NULL; + + r = MSI_ViewExecute( view, NULL ); + if (r != ERROR_SUCCESS) + { + msiobj_release( &view->hdr ); + return NULL; + } + r = MSI_ViewFetch( view, &rec ); + if (r != ERROR_SUCCESS) + { + msiobj_release( &view->hdr ); + return NULL; + } + if (!MSI_RecordGetString( rec, 4 )) + TRACE("component is a global assembly\n"); + + msiobj_release( &view->hdr ); + return rec; +} + +struct assembly_name +{ + WCHAR *type; + WCHAR *name; + WCHAR *version; + WCHAR *culture; + WCHAR *token; + WCHAR *arch; +}; + +static UINT get_assembly_name_attribute( MSIRECORD *rec, LPVOID param ) +{ + static const WCHAR typeW[] = {'t','y','p','e',0}; + static const WCHAR nameW[] = {'n','a','m','e',0}; + static const WCHAR versionW[] = {'v','e','r','s','i','o','n',0}; + static const WCHAR cultureW[] = {'c','u','l','t','u','r','e',0}; + static const WCHAR tokenW[] = {'p','u','b','l','i','c','K','e','y','T','o','k','e','n',0}; + static const WCHAR archW[] = {'p','r','o','c','e','s','s','o','r','A','r','c','h','i','t','e','c','t','u','r','e',0}; + struct assembly_name *name = param; + const WCHAR *attr = MSI_RecordGetString( rec, 2 ); + WCHAR *value = msi_dup_record_field( rec, 3 ); + + if (!strcmpiW( attr, typeW )) + name->type = value; + else if (!strcmpiW( attr, nameW )) + name->name = value; + else if (!strcmpiW( attr, versionW )) + name->version = value; + else if (!strcmpiW( attr, cultureW )) + name->culture = value; + else if (!strcmpiW( attr, tokenW )) + name->token = value; + else if (!strcmpiW( attr, archW )) + name->arch = value; + else + msi_free( value ); + + return ERROR_SUCCESS; +} + +static WCHAR *get_assembly_display_name( MSIDATABASE *db, const WCHAR *comp, MSIASSEMBLY *assembly ) +{ + static const WCHAR fmt_netW[] = { + '%','s',',',' ','v','e','r','s','i','o','n','=','%','s',',',' ', + 'c','u','l','t','u','r','e','=','%','s',',',' ', + 'p','u','b','l','i','c','K','e','y','T','o','k','e','n','=','%','s',0}; + static const WCHAR fmt_sxsW[] = { + '%','s',',',' ','v','e','r','s','i','o','n','=','%','s',',',' ', + 'p','u','b','l','i','c','K','e','y','T','o','k','e','n','=','%','s',',',' ', + 'p','r','o','c','e','s','s','o','r','A','r','c','h','i','t','e','c','t','u','r','e','=','%','s',0}; + static const WCHAR queryW[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ', + 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`', + ' ','=',' ','\'','%','s','\'',0}; + struct assembly_name name; + WCHAR *display_name = NULL; + MSIQUERY *view; + int len; + UINT r; + + memset( &name, 0, sizeof(name) ); + + r = MSI_OpenQuery( db, &view, queryW, comp ); + if (r != ERROR_SUCCESS) + return NULL; + + MSI_IterateRecords( view, NULL, get_assembly_name_attribute, &name ); + msiobj_release( &view->hdr ); + + if (assembly->attributes == msidbAssemblyAttributesWin32) + { + if (!name.type || !name.name || !name.version || !name.token || !name.arch) + { + WARN("invalid win32 assembly name\n"); + goto done; + } + len = strlenW( fmt_sxsW ); + len += strlenW( name.name ); + len += strlenW( name.version ); + len += strlenW( name.token ); + len += strlenW( name.arch ); + if (!(display_name = msi_alloc( len * sizeof(WCHAR) ))) goto done; + sprintfW( display_name, fmt_sxsW, name.name, name.version, name.token, name.arch ); + } + else + { + if (!name.name || !name.version || !name.culture || !name.token) + { + WARN("invalid assembly name\n"); + goto done; + } + len = strlenW( fmt_netW ); + len += strlenW( name.name ); + len += strlenW( name.version ); + len += strlenW( name.culture ); + len += strlenW( name.token ); + if (!(display_name = msi_alloc( len * sizeof(WCHAR) ))) goto done; + sprintfW( display_name, fmt_netW, name.name, name.version, name.culture, name.token ); + } + +done: + msi_free( name.type ); + msi_free( name.name ); + msi_free( name.version ); + msi_free( name.culture ); + msi_free( name.token ); + msi_free( name.arch ); + + return display_name; +} + +static BOOL check_assembly_installed( MSIPACKAGE *package, MSIASSEMBLY *assembly ) +{ + IAssemblyCache *cache; + ASSEMBLY_INFO info; + HRESULT hr; + + if (assembly->application) + { + /* FIXME: we should probably check the manifest file here */ + return FALSE; + } + + if (!init_assembly_caches( package )) + return FALSE; + + if (assembly->attributes == msidbAssemblyAttributesWin32) + cache = package->cache_sxs; + else + cache = package->cache_net; + + memset( &info, 0, sizeof(info) ); + info.cbAssemblyInfo = sizeof(info); + hr = IAssemblyCache_QueryAssemblyInfo( cache, QUERYASMINFO_FLAG_VALIDATE, assembly->display_name, &info ); + if (hr != S_OK) + return FALSE; + + return (info.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED); +} + +MSIASSEMBLY *load_assembly( MSIPACKAGE *package, MSICOMPONENT *comp ) +{ + MSIRECORD *rec; + MSIASSEMBLY *a; + + if (!(rec = get_assembly_record( package, comp->Component ))) + return NULL; + + if (!(a = msi_alloc_zero( sizeof(MSIASSEMBLY) ))) + { + msiobj_release( &rec->hdr ); + return NULL; + } + a->feature = strdupW( MSI_RecordGetString( rec, 2 ) ); + TRACE("feature %s\n", debugstr_w(a->feature)); + + a->manifest = strdupW( MSI_RecordGetString( rec, 3 ) ); + TRACE("manifest %s\n", debugstr_w(a->manifest)); + + a->application = strdupW( MSI_RecordGetString( rec, 4 ) ); + TRACE("application %s\n", debugstr_w(a->application)); + + a->attributes = MSI_RecordGetInteger( rec, 5 ); + TRACE("attributes %u\n", a->attributes); + + if (!(a->display_name = get_assembly_display_name( package->db, comp->Component, a ))) + { + WARN("can't get display name\n"); + msiobj_release( &rec->hdr ); + msi_free( a ); + return NULL; + } + TRACE("display name %s\n", debugstr_w(a->display_name)); + + a->installed = check_assembly_installed( package, a ); + TRACE("assembly is %s\n", a->installed ? "installed" : "not installed"); + + msiobj_release( &rec->hdr ); + return a; +} + +UINT install_assembly( MSIPACKAGE *package, MSICOMPONENT *comp ) +{ + HRESULT hr; + const WCHAR *manifest; + IAssemblyCache *cache; + MSIASSEMBLY *assembly = comp->assembly; + MSIFEATURE *feature = NULL; + + if (comp->assembly->feature) + feature = get_loaded_feature( package, comp->assembly->feature ); + + if (assembly->application) + { + if (feature) feature->Action = INSTALLSTATE_LOCAL; + return ERROR_SUCCESS; + } + if (assembly->attributes == msidbAssemblyAttributesWin32) + { + if (!assembly->manifest) + { + WARN("no manifest\n"); + return ERROR_FUNCTION_FAILED; + } + manifest = get_loaded_file( package, assembly->manifest )->TargetPath; + cache = package->cache_sxs; + } + else + { + manifest = get_loaded_file( package, comp->KeyPath )->TargetPath; + cache = package->cache_net; + } + TRACE("installing assembly %s\n", debugstr_w(manifest)); + + hr = IAssemblyCache_InstallAssembly( cache, 0, manifest, NULL ); + if (hr != S_OK) + { + ERR("Failed to install assembly %s (0x%08x)\n", debugstr_w(manifest), hr); + return ERROR_FUNCTION_FAILED; + } + if (feature) feature->Action = INSTALLSTATE_LOCAL; + assembly->installed = TRUE; + return ERROR_SUCCESS; +} + +UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package ) +{ + MSIRECORD *uirow; + MSICOMPONENT *comp; + + LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry) + { + if (!comp->assembly || !comp->Enabled) + continue; + + /* FIXME: write assembly registry values */ + + uirow = MSI_CreateRecord( 2 ); + MSI_RecordSetStringW( uirow, 2, comp->assembly->display_name ); + ui_actiondata( package, szMsiPublishAssemblies, uirow ); + msiobj_release( &uirow->hdr ); + } + return ERROR_SUCCESS; +} diff --git a/dlls/msi/files.c b/dlls/msi/files.c index 68714f985d8..47aa74fbb80 100644 --- a/dlls/msi/files.c +++ b/dlls/msi/files.c @@ -186,7 +186,8 @@ static BOOL installfiles_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action, return FALSE; msi_file_update_ui(package, f, szInstallFiles); - msi_create_directory(package, f->Component->Directory); + if (!f->Component->assembly) + msi_create_directory(package, f->Component->Directory); *path = strdupW(f->TargetPath); *attrs = f->Attributes; @@ -210,6 +211,7 @@ static BOOL installfiles_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action, UINT ACTION_InstallFiles(MSIPACKAGE *package) { MSIMEDIAINFO *mi; + MSICOMPONENT *comp; UINT rc = ERROR_SUCCESS; MSIFILE *file; @@ -234,7 +236,7 @@ UINT ACTION_InstallFiles(MSIPACKAGE *package) if (rc != ERROR_SUCCESS) { ERR("Failed to ready media for %s\n", debugstr_w(file->File)); - break; + goto done; } data.mi = mi; @@ -247,7 +249,7 @@ UINT ACTION_InstallFiles(MSIPACKAGE *package) { ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet)); rc = ERROR_INSTALL_FAILURE; - break; + goto done; } } @@ -255,11 +257,11 @@ UINT ACTION_InstallFiles(MSIPACKAGE *package) { LPWSTR source = resolve_file_source(package, file); - TRACE("file paths %s to %s\n", debugstr_w(source), - debugstr_w(file->TargetPath)); + TRACE("copying %s to %s\n", debugstr_w(source), debugstr_w(file->TargetPath)); msi_file_update_ui(package, file, szInstallFiles); - msi_create_directory(package, file->Component->Directory); + if (!file->Component->assembly) + msi_create_directory(package, file->Component->Directory); rc = copy_install_file(package, file, source); if (rc != ERROR_SUCCESS) @@ -268,19 +270,32 @@ UINT ACTION_InstallFiles(MSIPACKAGE *package) debugstr_w(file->TargetPath), rc); rc = ERROR_INSTALL_FAILURE; msi_free(source); - break; + goto done; } - msi_free(source); } else if (file->state != msifs_installed) { ERR("compressed file wasn't installed (%s)\n", debugstr_w(file->TargetPath)); rc = ERROR_INSTALL_FAILURE; - break; + goto done; + } + } + LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry ) + { + if (comp->Enabled && comp->assembly && !comp->assembly->installed) + { + rc = install_assembly( package, comp ); + if (rc != ERROR_SUCCESS) + { + ERR("Failed to install assembly\n"); + rc = ERROR_INSTALL_FAILURE; + break; + } } } +done: msi_free_media_info(mi); return rc; } diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h index 4d9fea7830c..986097febdb 100644 --- a/dlls/msi/msipriv.h +++ b/dlls/msi/msipriv.h @@ -32,6 +32,7 @@ #include "msidefs.h" #include "objbase.h" #include "objidl.h" +#include "fusion.h" #include "winnls.h" #include "winver.h" #include "wine/list.h" @@ -332,6 +333,8 @@ typedef struct tagMSIPACKAGE LPWSTR ActionFormat; LPWSTR LastAction; HANDLE log_file; + IAssemblyCache *cache_net; + IAssemblyCache *cache_sxs; struct list classes; struct list extensions; @@ -402,6 +405,17 @@ typedef struct tagMSIFEATURE struct list Components; } MSIFEATURE; +typedef struct tagMSIASSEMBLY +{ + LPWSTR feature; + LPWSTR manifest; + LPWSTR application; + DWORD attributes; + LPWSTR display_name; + LPWSTR tempdir; + BOOL installed; +} MSIASSEMBLY; + typedef struct tagMSICOMPONENT { struct list entry; @@ -420,6 +434,7 @@ typedef struct tagMSICOMPONENT INT RefCount; LPWSTR FullKeypath; LPWSTR AdvertiseString; + MSIASSEMBLY *assembly; unsigned int anyAbsent:1; unsigned int hasAdvertiseFeature:1; @@ -908,6 +923,7 @@ extern UINT ACTION_UnregisterExtensionInfo(MSIPACKAGE *package); extern UINT ACTION_UnregisterFonts(MSIPACKAGE *package); extern UINT ACTION_UnregisterMIMEInfo(MSIPACKAGE *package); extern UINT ACTION_UnregisterProgIdInfo(MSIPACKAGE *package); +extern UINT ACTION_MsiPublishAssemblies(MSIPACKAGE *package); /* Helpers */ extern DWORD deformat_string(MSIPACKAGE *package, LPCWSTR ptr, WCHAR** data ); @@ -942,6 +958,8 @@ extern UINT msi_get_local_package_name(LPWSTR path, LPCWSTR suffix); extern UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace); extern void msi_component_set_state(MSIPACKAGE *, MSICOMPONENT *, INSTALLSTATE); extern void msi_feature_set_state(MSIPACKAGE *, MSIFEATURE *, INSTALLSTATE); +extern MSIASSEMBLY *load_assembly(MSIPACKAGE *, MSICOMPONENT *); +extern UINT install_assembly(MSIPACKAGE *, MSICOMPONENT *); /* media */ @@ -1060,6 +1078,7 @@ static const WCHAR szWow6432NodeCLSID[] = {'W','o','w','6','4','3','2','N','o',' static const WCHAR szWow6432Node[] = {'W','o','w','6','4','3','2','N','o','d','e',0}; static const WCHAR szStreams[] = {'_','S','t','r','e','a','m','s',0}; static const WCHAR szStorages[] = {'_','S','t','o','r','a','g','e','s',0}; +static const WCHAR szMsiPublishAssemblies[] = {'M','s','i','P','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0}; /* memory allocation macro functions */ static void *msi_alloc( size_t len ) __WINE_ALLOC_SIZE(1); diff --git a/dlls/msi/package.c b/dlls/msi/package.c index f3f5071de6b..50a7dbbe931 100644 --- a/dlls/msi/package.c +++ b/dlls/msi/package.c @@ -111,6 +111,14 @@ static void free_extension( MSIEXTENSION *ext ) msi_free( ext ); } +static void free_assembly( MSIASSEMBLY *assembly ) +{ + msi_free( assembly->display_name ); + if (assembly->tempdir) RemoveDirectoryW( assembly->tempdir ); + msi_free( assembly->tempdir ); + msi_free( assembly ); +} + static void free_package_structures( MSIPACKAGE *package ) { INT i; @@ -154,6 +162,7 @@ static void free_package_structures( MSIPACKAGE *package ) msi_free( comp->Condition ); msi_free( comp->KeyPath ); msi_free( comp->FullKeypath ); + if (comp->assembly) free_assembly( comp->assembly ); msi_free( comp ); } @@ -296,6 +305,9 @@ static void MSI_FreePackage( MSIOBJECTHDR *arg) msiobj_release( &package->db->hdr ); free_package_structures(package); CloseHandle( package->log_file ); + + if (package->cache_net) IAssemblyCache_Release( package->cache_net ); + if (package->cache_sxs) IAssemblyCache_Release( package->cache_sxs ); } static UINT create_temp_property_table(MSIPACKAGE *package)