diff --git a/dlls/msi/action.c b/dlls/msi/action.c index 44fae9d..749da32 100644 --- a/dlls/msi/action.c +++ b/dlls/msi/action.c @@ -33,6 +33,10 @@ #include "msipriv.h" #include "winuser.h" #include "shlobj.h" +#include "objbase.h" +#include "mscoree.h" +#include "fusion.h" +#include "shlwapi.h" #include "wine/unicode.h" #include "winver.h" @@ -1246,6 +1250,9 @@ static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name ) { MSIFEATURE *feature; + if ( !name ) + return NULL; + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) { if ( !lstrcmpW( feature->Feature, name ) ) @@ -5542,6 +5549,154 @@ static UINT ACTION_MoveFiles( MSIPACKAGE *package ) return rc; } +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(LPWSTR path) +{ + IAssemblyCache *cache; + HRESULT hr; + UINT r = ERROR_FUNCTION_FAILED; + + if (!init_functionpointers() || !pCreateAssemblyCache) + return ERROR_FUNCTION_FAILED; + + 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; +} + +static UINT ITERATE_PublishAssembly( MSIRECORD *rec, LPVOID param ) +{ + MSIPACKAGE *package = param; + MSICOMPONENT *comp; + MSIFEATURE *feature; + MSIFILE *file; + WCHAR path[MAX_PATH]; + LPCWSTR app; + DWORD attr; + UINT r; + + comp = get_loaded_component(package, MSI_RecordGetString(rec, 1)); + if (!comp || !comp->Enabled || + !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE))) + { + ERR("Component not set for install, not publishing assembly\n"); + return ERROR_SUCCESS; + } + + feature = find_feature_by_name(package, MSI_RecordGetString(rec, 2)); + if (feature) + msi_feature_set_state(feature, INSTALLSTATE_LOCAL); + + if (MSI_RecordGetString(rec, 3)) + FIXME("Manifest unhandled\n"); + + app = MSI_RecordGetString(rec, 4); + if (app) + { + FIXME("Assembly should be privately installed\n"); + return ERROR_SUCCESS; + } + + attr = MSI_RecordGetInteger(rec, 5); + if (attr == msidbAssemblyAttributesWin32) + { + FIXME("Win32 assemblies not handled\n"); + return ERROR_SUCCESS; + } + + /* FIXME: extract all files belonging to this component */ + file = msi_find_file(package, comp->KeyPath); + + GetTempPathW(MAX_PATH, path); + r = msi_extract_file(package, file, path); + if (r != ERROR_SUCCESS) + { + ERR("Failed to extract temporary assembly\n"); + return r; + } + + PathAddBackslashW(path); + lstrcatW(path, file->FileName); + + r = install_assembly(path); + if (r != ERROR_SUCCESS) + ERR("Failed to install assembly\n"); + + /* FIXME: write Installer assembly reg values */ + + return r; +} + +static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package ) +{ + UINT rc; + MSIQUERY *view; + + static const WCHAR ExecSeqQuery[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','M','s','i','A','s','s','e','m','b','l','y','`',0}; + + rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords(view, NULL, ITERATE_PublishAssembly, package); + msiobj_release(&view->hdr); + + return rc; +} + static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table ) { @@ -5630,13 +5785,6 @@ static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package ) return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table ); } -static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package ) -{ - static const WCHAR table[] = { - 'M','s','i','A','s','s','e','m','b','l','y',0 }; - return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table ); -} - static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package ) { static const WCHAR table[] = { diff --git a/dlls/msi/files.c b/dlls/msi/files.c index 1aae66d..d2bef7c 100644 --- a/dlls/msi/files.c +++ b/dlls/msi/files.c @@ -57,20 +57,7 @@ extern const WCHAR szRemoveFiles[]; static const WCHAR cszTempFolder[]= {'T','e','m','p','F','o','l','d','e','r',0}; -struct media_info { - UINT disk_id; - UINT type; - UINT last_sequence; - LPWSTR disk_prompt; - LPWSTR cabinet; - LPWSTR first_volume; - LPWSTR volume_label; - BOOL is_continuous; - BOOL is_extracted; - WCHAR source[MAX_PATH]; -}; - -static BOOL source_matches_volume(struct media_info *mi, LPWSTR source_root) +static BOOL source_matches_volume(MSIMEDIAINFO *mi, LPWSTR source_root) { WCHAR volume_name[MAX_PATH + 1]; @@ -84,7 +71,7 @@ static BOOL source_matches_volume(struct media_info *mi, LPWSTR source_root) return !lstrcmpW(mi->volume_label, volume_name); } -static UINT msi_change_media( MSIPACKAGE *package, struct media_info *mi ) +static UINT msi_change_media( MSIPACKAGE *package, MSIMEDIAINFO *mi ) { LPSTR msg; LPWSTR error, error_dialog; @@ -169,7 +156,7 @@ end: typedef struct { MSIPACKAGE* package; - struct media_info *mi; + MSIMEDIAINFO *mi; } CabData; static void * cabinet_alloc(ULONG cb) @@ -265,7 +252,7 @@ static void msi_file_update_ui( MSIPACKAGE *package, MSIFILE *f, const WCHAR *ac ui_progress( package, 2, f->FileSize, 0, 0); } -static UINT msi_media_get_disk_info( MSIPACKAGE *package, struct media_info *mi ) +static UINT msi_media_get_disk_info( MSIPACKAGE *package, MSIMEDIAINFO *mi ) { MSIRECORD *row; LPWSTR ptr; @@ -311,7 +298,7 @@ static INT_PTR cabinet_notify(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin) case fdintNEXT_CABINET: { CabData *data = (CabData *)pfdin->pv; - struct media_info *mi = data->mi; + MSIMEDIAINFO *mi = data->mi; LPWSTR cab = strdupAtoW(pfdin->psz1); UINT rc; @@ -423,18 +410,18 @@ static INT_PTR cabinet_notify(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin) } /*********************************************************************** - * extract_cabinet_file + * msi_cabextract * * Extract files from a cab file. */ -static BOOL extract_cabinet_file(MSIPACKAGE* package, struct media_info *mi) +BOOL msi_cabextract(MSIPACKAGE* package, MSIMEDIAINFO *mi, + PFNFDINOTIFY notify, LPVOID data) { LPSTR cabinet, cab_path = NULL; LPWSTR ptr; HFDI hfdi; ERF erf; BOOL ret = FALSE; - CabData data; TRACE("Extracting %s\n", debugstr_w(mi->source)); @@ -457,10 +444,7 @@ static BOOL extract_cabinet_file(MSIPACKAGE* package, struct media_info *mi) cab_path[ptr - mi->source] = '\0'; - data.package = package; - data.mi = mi; - - ret = FDICopy(hfdi, cabinet, cab_path, 0, cabinet_notify, NULL, &data); + ret = FDICopy(hfdi, cabinet, cab_path, 0, notify, NULL, data); if (!ret) ERR("FDICopy failed\n"); @@ -495,7 +479,7 @@ static VOID set_file_source(MSIPACKAGE* package, MSIFILE* file, LPCWSTR path) file->SourcePath = build_directory_name(2, path, file->File); } -static void free_media_info( struct media_info *mi ) +void msi_free_media_info( MSIMEDIAINFO *mi ) { msi_free( mi->disk_prompt ); msi_free( mi->cabinet ); @@ -504,7 +488,7 @@ static void free_media_info( struct media_info *mi ) msi_free( mi ); } -static UINT load_media_info(MSIPACKAGE *package, MSIFILE *file, struct media_info *mi) +UINT msi_load_media_info(MSIPACKAGE *package, MSIFILE *file, MSIMEDIAINFO *mi) { MSIRECORD *row; LPWSTR source_dir; @@ -593,7 +577,7 @@ static UINT load_media_info(MSIPACKAGE *package, MSIFILE *file, struct media_inf } /* FIXME: search NETWORK and URL sources as well */ -static UINT find_published_source(MSIPACKAGE *package, struct media_info *mi) +static UINT find_published_source(MSIPACKAGE *package, MSIMEDIAINFO *mi) { WCHAR source[MAX_PATH]; WCHAR volume[MAX_PATH]; @@ -634,7 +618,7 @@ static UINT find_published_source(MSIPACKAGE *package, struct media_info *mi) return ERROR_FUNCTION_FAILED; } -static UINT ready_media(MSIPACKAGE *package, MSIFILE *file, struct media_info *mi) +static UINT ready_media(MSIPACKAGE *package, MSIFILE *file, MSIMEDIAINFO *mi) { UINT rc = ERROR_SUCCESS; @@ -642,7 +626,7 @@ static UINT ready_media(MSIPACKAGE *package, MSIFILE *file, struct media_info *m if (mi->is_continuous) return ERROR_SUCCESS; - rc = load_media_info(package, file, mi); + rc = msi_load_media_info(package, file, mi); if (rc != ERROR_SUCCESS) { ERR("Unable to load media info\n"); @@ -808,7 +792,7 @@ static BOOL check_dest_hash_matches(MSIFILE *file) */ UINT ACTION_InstallFiles(MSIPACKAGE *package) { - struct media_info *mi; + MSIMEDIAINFO *mi; UINT rc = ERROR_SUCCESS; MSIFILE *file; @@ -825,7 +809,7 @@ UINT ACTION_InstallFiles(MSIPACKAGE *package) */ msi_create_component_directories( package ); - mi = msi_alloc_zero( sizeof(struct media_info) ); + mi = msi_alloc_zero( sizeof(MSIMEDIAINFO) ); LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) { @@ -841,6 +825,8 @@ UINT ACTION_InstallFiles(MSIPACKAGE *package) if (file->Sequence > mi->last_sequence || mi->is_continuous || (file->IsCompressed && !mi->is_extracted)) { + CabData data; + rc = ready_media(package, file, mi); if (rc != ERROR_SUCCESS) { @@ -848,7 +834,11 @@ UINT ACTION_InstallFiles(MSIPACKAGE *package) break; } - if (file->IsCompressed && !extract_cabinet_file(package, mi)) + data.mi = mi; + data.package = package; + + if (file->IsCompressed && + !msi_cabextract(package, mi, cabinet_notify, &data)) { ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet)); rc = ERROR_FUNCTION_FAILED; @@ -881,7 +871,7 @@ UINT ACTION_InstallFiles(MSIPACKAGE *package) } } - free_media_info( mi ); + msi_free_media_info( mi ); return rc; } diff --git a/dlls/msi/helpers.c b/dlls/msi/helpers.c index 25c1c71..0dc02b4 100644 --- a/dlls/msi/helpers.c +++ b/dlls/msi/helpers.c @@ -31,6 +31,8 @@ #include "wine/debug.h" #include "msipriv.h" #include "winuser.h" +#include "winreg.h" +#include "shlwapi.h" #include "wine/unicode.h" #include "msidefs.h" @@ -1054,3 +1056,122 @@ void msi_ui_error( DWORD msg_id, DWORD type ) MessageBoxW( NULL, text, title, type ); } + +typedef struct +{ + MSIPACKAGE *package; + MSIMEDIAINFO *mi; + MSIFILE *file; + LPWSTR destination; +} CabData; + +static INT_PTR cabinet_notify(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin) +{ + TRACE("(%d)\n", fdint); + + switch (fdint) + { + case fdintNEXT_CABINET: + { + ERR("continuous cabinets not handled\n"); + return 0; + } + + case fdintCOPY_FILE: + { + CabData *data = (CabData*) pfdin->pv; + LPWSTR file, path; + DWORD attrs, size; + HANDLE handle; + MSIFILE *f; + + file = strdupAtoW(pfdin->psz1); + f = get_loaded_file(data->package, file); + msi_free(file); + + if (!f) + { + WARN("unknown file in cabinet (%s)\n",debugstr_a(pfdin->psz1)); + return 0; + } + + if (lstrcmpW(f->File, data->file->File)) + return 0; + + size = lstrlenW(data->destination) + lstrlenW(file) + 2; + path = msi_alloc(size * sizeof(WCHAR)); + lstrcpyW(path, data->destination); + PathAddBackslashW(path); + lstrcatW(path, data->file->FileName); + + TRACE("extracting %s\n", debugstr_w(path)); + + attrs = f->Attributes & (FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM); + if (!attrs) attrs = FILE_ATTRIBUTE_NORMAL; + + handle = CreateFileW(path, GENERIC_READ | GENERIC_WRITE, 0, + NULL, CREATE_ALWAYS, attrs, NULL); + if (handle == INVALID_HANDLE_VALUE) + { + if (GetFileAttributesW(path) == INVALID_FILE_ATTRIBUTES) + ERR("failed to create %s (error %d)\n", + debugstr_w(path), GetLastError()); + + msi_free(path); + return 0; + } + + msi_free(path); + return (INT_PTR)handle; + } + + case fdintCLOSE_FILE_INFO: + { + FILETIME ft; + FILETIME ftLocal; + HANDLE handle = (HANDLE)pfdin->hf; + + if (!DosDateTimeToFileTime(pfdin->date, pfdin->time, &ft)) + return -1; + if (!LocalFileTimeToFileTime(&ft, &ftLocal)) + return -1; + if (!SetFileTime(handle, &ftLocal, 0, &ftLocal)) + return -1; + CloseHandle(handle); + return 1; + } + + default: + return 0; + } +} + +UINT msi_extract_file(MSIPACKAGE *package, MSIFILE *file, LPWSTR destdir) +{ + MSIMEDIAINFO *mi; + CabData data; + UINT r; + + mi = msi_alloc_zero(sizeof(MSIMEDIAINFO)); + if (!mi) + return ERROR_OUTOFMEMORY; + + r = msi_load_media_info(package, file, mi); + if (r != ERROR_SUCCESS) + goto done; + + data.package = package; + data.mi = mi; + data.file = file; + data.destination = destdir; + + if (!msi_cabextract(package, mi, cabinet_notify, &data)) + { + ERR("Failed to extract cabinet file\n"); + r = ERROR_FUNCTION_FAILED; + } + +done: + msi_free_media_info(mi); + return r; +} diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h index 58877ba..90eaa19 100644 --- a/dlls/msi/msipriv.h +++ b/dlls/msi/msipriv.h @@ -26,6 +26,7 @@ #include "windef.h" #include "winbase.h" +#include "fdi.h" #include "msi.h" #include "msiquery.h" #include "objbase.h" @@ -133,6 +134,20 @@ typedef struct tagMSIMEDIADISK LPWSTR disk_prompt; } MSIMEDIADISK; +typedef struct tagMSIMEDIAINFO +{ + UINT disk_id; + UINT type; + UINT last_sequence; + LPWSTR disk_prompt; + LPWSTR cabinet; + LPWSTR first_volume; + LPWSTR volume_label; + BOOL is_continuous; + BOOL is_extracted; + WCHAR source[MAX_PATH]; +} MSIMEDIAINFO; + typedef struct _column_info { LPCWSTR table; @@ -889,6 +904,10 @@ extern UINT msi_create_component_directories( MSIPACKAGE *package ); extern void msi_ui_error( DWORD msg_id, DWORD type ); extern UINT msi_set_last_used_source(LPCWSTR product, LPCWSTR usersid, MSIINSTALLCONTEXT context, DWORD options, LPCWSTR value); +extern UINT msi_load_media_info(MSIPACKAGE *package, MSIFILE *file, MSIMEDIAINFO *mi); +extern void msi_free_media_info(MSIMEDIAINFO *mi); +extern BOOL msi_cabextract(MSIPACKAGE* package, MSIMEDIAINFO *mi, PFNFDINOTIFY notify, LPVOID data); +extern UINT msi_extract_file(MSIPACKAGE *package, MSIFILE *file, LPWSTR destdir); /* control event stuff */ extern VOID ControlEvent_FireSubscribedEvent(MSIPACKAGE *package, LPCWSTR event, diff --git a/include/msidefs.h b/include/msidefs.h index 379a22e..12b607d 100644 --- a/include/msidefs.h +++ b/include/msidefs.h @@ -201,6 +201,12 @@ enum msidbMoveFileOptions msidbMoveFileOptionsMove = 0x00000001, }; +enum msidbAssemblyAttributes +{ + msidbAssemblyAttributesURT = 0x00000000, + msidbAssemblyAttributesWin32 = 0x00000001, +}; + /* * Windows SDK braindamage alert * -- 1.5.4.3