msi: Add support for installing side-by-side assemblies.
This commit is contained in:
parent
3faddc21b3
commit
a23514eece
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue