Commit a23514ee authored by Hans Leidekker's avatar Hans Leidekker Committed by Alexandre Julliard

msi: Add support for installing side-by-side assemblies.

parent 3faddc21
......@@ -7,6 +7,7 @@ C_SRCS = \
action.c \
alter.c \
appsearch.c \
assembly.c \
automation.c \
classes.c \
create.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;
......
/*
* 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;
}
......@@ -186,6 +186,7 @@ static BOOL installfiles_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
return FALSE;
msi_file_update_ui(package, f, szInstallFiles);
if (!f->Component->assembly)
msi_create_directory(package, f->Component->Directory);
*path = strdupW(f->TargetPath);
......@@ -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,10 +257,10 @@ 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);
if (!file->Component->assembly)
msi_create_directory(package, file->Component->Directory);
rc = copy_install_file(package, file, source);
......@@ -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;
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;
}
......
......@@ -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);
......
......@@ -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)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment