[5/5] msi: Add support for installing side-by-side assemblies.

Hans Leidekker hans at codeweavers.com
Thu Nov 11 03:45:42 CST 2010


---
 dlls/msi/Makefile.in |    1 +
 dlls/msi/action.c    |  533 ++++----------------------------------------------
 dlls/msi/assembly.c  |  371 +++++++++++++++++++++++++++++++++++
 dlls/msi/files.c     |   33 +++-
 dlls/msi/msipriv.h   |   19 ++
 dlls/msi/package.c   |   12 ++
 6 files changed, 469 insertions(+), 500 deletions(-)
 create mode 100644 dlls/msi/assembly.c

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






More information about the wine-patches mailing list