[5/5] msi: Add support for installing side-by-side assemblies.
Hans Leidekker
hans at codeweavers.com
Thu Nov 11 03:45:42 CST 2010
---
dlls/msi/Makefile.in | 1 +
dlls/msi/action.c | 533 ++++----------------------------------------------
dlls/msi/assembly.c | 371 +++++++++++++++++++++++++++++++++++
dlls/msi/files.c | 33 +++-
dlls/msi/msipriv.h | 19 ++
dlls/msi/package.c | 12 ++
6 files changed, 469 insertions(+), 500 deletions(-)
create mode 100644 dlls/msi/assembly.c
diff --git a/dlls/msi/Makefile.in b/dlls/msi/Makefile.in
index b824836..f41a92a 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 e8ad30b..6292f4f 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 0000000..ef178ff
--- /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 <stdarg.h>
+
+#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 68714f9..47aa74f 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 4d9fea7..986097f 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 f3f5071..50a7dbb 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)
--
1.7.1
More information about the wine-patches
mailing list