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 \
|
action.c \
|
||||||
alter.c \
|
alter.c \
|
||||||
appsearch.c \
|
appsearch.c \
|
||||||
|
assembly.c \
|
||||||
automation.c \
|
automation.c \
|
||||||
classes.c \
|
classes.c \
|
||||||
create.c \
|
create.c \
|
||||||
|
|
|
@ -35,7 +35,6 @@
|
||||||
#include "shlobj.h"
|
#include "shlobj.h"
|
||||||
#include "objbase.h"
|
#include "objbase.h"
|
||||||
#include "mscoree.h"
|
#include "mscoree.h"
|
||||||
#include "fusion.h"
|
|
||||||
#include "shlwapi.h"
|
#include "shlwapi.h"
|
||||||
#include "wine/unicode.h"
|
#include "wine/unicode.h"
|
||||||
#include "winver.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};
|
{'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
|
||||||
static const WCHAR szMigrateFeatureStates[] =
|
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};
|
{'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[] =
|
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};
|
{'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[] =
|
static const WCHAR szInstallODBC[] =
|
||||||
|
@ -1227,6 +1224,7 @@ static UINT load_component( MSIRECORD *row, LPVOID param )
|
||||||
comp->Installed = INSTALLSTATE_UNKNOWN;
|
comp->Installed = INSTALLSTATE_UNKNOWN;
|
||||||
msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
|
msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
|
||||||
|
|
||||||
|
comp->assembly = load_assembly( package, comp );
|
||||||
return ERROR_SUCCESS;
|
return ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2151,6 +2149,43 @@ static BOOL hash_matches( MSIFILE *file )
|
||||||
return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
|
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 )
|
static UINT set_file_install_states( MSIPACKAGE *package )
|
||||||
{
|
{
|
||||||
VS_FIXEDFILEINFO *file_version;
|
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 )
|
LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
|
||||||
{
|
{
|
||||||
MSICOMPONENT* comp = file->Component;
|
MSICOMPONENT *comp = file->Component;
|
||||||
DWORD file_size;
|
DWORD file_size;
|
||||||
LPWSTR p;
|
|
||||||
|
|
||||||
if (!comp->Enabled) continue;
|
if (!comp->Enabled) continue;
|
||||||
|
|
||||||
if (file->IsCompressed)
|
if (file->IsCompressed)
|
||||||
comp->ForceLocalState = TRUE;
|
comp->ForceLocalState = TRUE;
|
||||||
|
|
||||||
/* calculate target */
|
set_target_path( package, file );
|
||||||
p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
|
|
||||||
msi_free(file->TargetPath);
|
|
||||||
|
|
||||||
TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
|
if ((comp->assembly && !comp->assembly->installed) ||
|
||||||
|
GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
file->state = msifs_missing;
|
file->state = msifs_missing;
|
||||||
comp->Cost += file->FileSize;
|
comp->Cost += file->FileSize;
|
||||||
|
@ -6698,481 +6724,6 @@ static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
|
||||||
return rc;
|
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 )
|
static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
|
||||||
{
|
{
|
||||||
LPWSTR key, template, id;
|
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,6 +186,7 @@ static BOOL installfiles_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
msi_file_update_ui(package, f, szInstallFiles);
|
msi_file_update_ui(package, f, szInstallFiles);
|
||||||
|
if (!f->Component->assembly)
|
||||||
msi_create_directory(package, f->Component->Directory);
|
msi_create_directory(package, f->Component->Directory);
|
||||||
|
|
||||||
*path = strdupW(f->TargetPath);
|
*path = strdupW(f->TargetPath);
|
||||||
|
@ -210,6 +211,7 @@ static BOOL installfiles_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
|
||||||
UINT ACTION_InstallFiles(MSIPACKAGE *package)
|
UINT ACTION_InstallFiles(MSIPACKAGE *package)
|
||||||
{
|
{
|
||||||
MSIMEDIAINFO *mi;
|
MSIMEDIAINFO *mi;
|
||||||
|
MSICOMPONENT *comp;
|
||||||
UINT rc = ERROR_SUCCESS;
|
UINT rc = ERROR_SUCCESS;
|
||||||
MSIFILE *file;
|
MSIFILE *file;
|
||||||
|
|
||||||
|
@ -234,7 +236,7 @@ UINT ACTION_InstallFiles(MSIPACKAGE *package)
|
||||||
if (rc != ERROR_SUCCESS)
|
if (rc != ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
ERR("Failed to ready media for %s\n", debugstr_w(file->File));
|
ERR("Failed to ready media for %s\n", debugstr_w(file->File));
|
||||||
break;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.mi = mi;
|
data.mi = mi;
|
||||||
|
@ -247,7 +249,7 @@ UINT ACTION_InstallFiles(MSIPACKAGE *package)
|
||||||
{
|
{
|
||||||
ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
|
ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
|
||||||
rc = ERROR_INSTALL_FAILURE;
|
rc = ERROR_INSTALL_FAILURE;
|
||||||
break;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,10 +257,10 @@ UINT ACTION_InstallFiles(MSIPACKAGE *package)
|
||||||
{
|
{
|
||||||
LPWSTR source = resolve_file_source(package, file);
|
LPWSTR source = resolve_file_source(package, file);
|
||||||
|
|
||||||
TRACE("file paths %s to %s\n", debugstr_w(source),
|
TRACE("copying %s to %s\n", debugstr_w(source), debugstr_w(file->TargetPath));
|
||||||
debugstr_w(file->TargetPath));
|
|
||||||
|
|
||||||
msi_file_update_ui(package, file, szInstallFiles);
|
msi_file_update_ui(package, file, szInstallFiles);
|
||||||
|
if (!file->Component->assembly)
|
||||||
msi_create_directory(package, file->Component->Directory);
|
msi_create_directory(package, file->Component->Directory);
|
||||||
|
|
||||||
rc = copy_install_file(package, file, source);
|
rc = copy_install_file(package, file, source);
|
||||||
|
@ -268,19 +270,32 @@ UINT ACTION_InstallFiles(MSIPACKAGE *package)
|
||||||
debugstr_w(file->TargetPath), rc);
|
debugstr_w(file->TargetPath), rc);
|
||||||
rc = ERROR_INSTALL_FAILURE;
|
rc = ERROR_INSTALL_FAILURE;
|
||||||
msi_free(source);
|
msi_free(source);
|
||||||
break;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
msi_free(source);
|
msi_free(source);
|
||||||
}
|
}
|
||||||
else if (file->state != msifs_installed)
|
else if (file->state != msifs_installed)
|
||||||
{
|
{
|
||||||
ERR("compressed file wasn't installed (%s)\n", debugstr_w(file->TargetPath));
|
ERR("compressed file wasn't installed (%s)\n", debugstr_w(file->TargetPath));
|
||||||
rc = ERROR_INSTALL_FAILURE;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
msi_free_media_info(mi);
|
msi_free_media_info(mi);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include "msidefs.h"
|
#include "msidefs.h"
|
||||||
#include "objbase.h"
|
#include "objbase.h"
|
||||||
#include "objidl.h"
|
#include "objidl.h"
|
||||||
|
#include "fusion.h"
|
||||||
#include "winnls.h"
|
#include "winnls.h"
|
||||||
#include "winver.h"
|
#include "winver.h"
|
||||||
#include "wine/list.h"
|
#include "wine/list.h"
|
||||||
|
@ -332,6 +333,8 @@ typedef struct tagMSIPACKAGE
|
||||||
LPWSTR ActionFormat;
|
LPWSTR ActionFormat;
|
||||||
LPWSTR LastAction;
|
LPWSTR LastAction;
|
||||||
HANDLE log_file;
|
HANDLE log_file;
|
||||||
|
IAssemblyCache *cache_net;
|
||||||
|
IAssemblyCache *cache_sxs;
|
||||||
|
|
||||||
struct list classes;
|
struct list classes;
|
||||||
struct list extensions;
|
struct list extensions;
|
||||||
|
@ -402,6 +405,17 @@ typedef struct tagMSIFEATURE
|
||||||
struct list Components;
|
struct list Components;
|
||||||
} MSIFEATURE;
|
} MSIFEATURE;
|
||||||
|
|
||||||
|
typedef struct tagMSIASSEMBLY
|
||||||
|
{
|
||||||
|
LPWSTR feature;
|
||||||
|
LPWSTR manifest;
|
||||||
|
LPWSTR application;
|
||||||
|
DWORD attributes;
|
||||||
|
LPWSTR display_name;
|
||||||
|
LPWSTR tempdir;
|
||||||
|
BOOL installed;
|
||||||
|
} MSIASSEMBLY;
|
||||||
|
|
||||||
typedef struct tagMSICOMPONENT
|
typedef struct tagMSICOMPONENT
|
||||||
{
|
{
|
||||||
struct list entry;
|
struct list entry;
|
||||||
|
@ -420,6 +434,7 @@ typedef struct tagMSICOMPONENT
|
||||||
INT RefCount;
|
INT RefCount;
|
||||||
LPWSTR FullKeypath;
|
LPWSTR FullKeypath;
|
||||||
LPWSTR AdvertiseString;
|
LPWSTR AdvertiseString;
|
||||||
|
MSIASSEMBLY *assembly;
|
||||||
|
|
||||||
unsigned int anyAbsent:1;
|
unsigned int anyAbsent:1;
|
||||||
unsigned int hasAdvertiseFeature:1;
|
unsigned int hasAdvertiseFeature:1;
|
||||||
|
@ -908,6 +923,7 @@ extern UINT ACTION_UnregisterExtensionInfo(MSIPACKAGE *package);
|
||||||
extern UINT ACTION_UnregisterFonts(MSIPACKAGE *package);
|
extern UINT ACTION_UnregisterFonts(MSIPACKAGE *package);
|
||||||
extern UINT ACTION_UnregisterMIMEInfo(MSIPACKAGE *package);
|
extern UINT ACTION_UnregisterMIMEInfo(MSIPACKAGE *package);
|
||||||
extern UINT ACTION_UnregisterProgIdInfo(MSIPACKAGE *package);
|
extern UINT ACTION_UnregisterProgIdInfo(MSIPACKAGE *package);
|
||||||
|
extern UINT ACTION_MsiPublishAssemblies(MSIPACKAGE *package);
|
||||||
|
|
||||||
/* Helpers */
|
/* Helpers */
|
||||||
extern DWORD deformat_string(MSIPACKAGE *package, LPCWSTR ptr, WCHAR** data );
|
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 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace);
|
||||||
extern void msi_component_set_state(MSIPACKAGE *, MSICOMPONENT *, INSTALLSTATE);
|
extern void msi_component_set_state(MSIPACKAGE *, MSICOMPONENT *, INSTALLSTATE);
|
||||||
extern void msi_feature_set_state(MSIPACKAGE *, MSIFEATURE *, INSTALLSTATE);
|
extern void msi_feature_set_state(MSIPACKAGE *, MSIFEATURE *, INSTALLSTATE);
|
||||||
|
extern MSIASSEMBLY *load_assembly(MSIPACKAGE *, MSICOMPONENT *);
|
||||||
|
extern UINT install_assembly(MSIPACKAGE *, MSICOMPONENT *);
|
||||||
|
|
||||||
/* media */
|
/* 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 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 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 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 */
|
/* memory allocation macro functions */
|
||||||
static void *msi_alloc( size_t len ) __WINE_ALLOC_SIZE(1);
|
static void *msi_alloc( size_t len ) __WINE_ALLOC_SIZE(1);
|
||||||
|
|
|
@ -111,6 +111,14 @@ static void free_extension( MSIEXTENSION *ext )
|
||||||
msi_free( 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 )
|
static void free_package_structures( MSIPACKAGE *package )
|
||||||
{
|
{
|
||||||
INT i;
|
INT i;
|
||||||
|
@ -154,6 +162,7 @@ static void free_package_structures( MSIPACKAGE *package )
|
||||||
msi_free( comp->Condition );
|
msi_free( comp->Condition );
|
||||||
msi_free( comp->KeyPath );
|
msi_free( comp->KeyPath );
|
||||||
msi_free( comp->FullKeypath );
|
msi_free( comp->FullKeypath );
|
||||||
|
if (comp->assembly) free_assembly( comp->assembly );
|
||||||
msi_free( comp );
|
msi_free( comp );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,6 +305,9 @@ static void MSI_FreePackage( MSIOBJECTHDR *arg)
|
||||||
msiobj_release( &package->db->hdr );
|
msiobj_release( &package->db->hdr );
|
||||||
free_package_structures(package);
|
free_package_structures(package);
|
||||||
CloseHandle( package->log_file );
|
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)
|
static UINT create_temp_property_table(MSIPACKAGE *package)
|
||||||
|
|
Loading…
Reference in New Issue