2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 * Actions dealing with files:
28 * RemoveDuplicateFiles
47 #include "wine/debug.h"
49 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
51 BOOL
msi_get_temp_file_name( MSIPACKAGE
*package
, const WCHAR
*tmp_path
, const WCHAR
*prefix
, WCHAR
*tmp_filename
)
54 msi_disable_fs_redirection( package
);
55 ret
= GetTempFileNameW( tmp_path
, prefix
, 0, tmp_filename
);
56 msi_revert_fs_redirection( package
);
60 HANDLE
msi_create_file( MSIPACKAGE
*package
, const WCHAR
*filename
, DWORD access
, DWORD sharing
, DWORD creation
,
64 msi_disable_fs_redirection( package
);
65 handle
= CreateFileW( filename
, access
, sharing
, NULL
, creation
, flags
, NULL
);
66 msi_revert_fs_redirection( package
);
70 static BOOL
msi_copy_file( MSIPACKAGE
*package
, const WCHAR
*src
, const WCHAR
*dst
, BOOL fail_if_exists
)
73 msi_disable_fs_redirection( package
);
74 ret
= CopyFileW( src
, dst
, fail_if_exists
);
75 msi_revert_fs_redirection( package
);
79 BOOL
msi_delete_file( MSIPACKAGE
*package
, const WCHAR
*filename
)
82 msi_disable_fs_redirection( package
);
83 ret
= DeleteFileW( filename
);
84 msi_revert_fs_redirection( package
);
88 static BOOL
msi_create_directory( MSIPACKAGE
*package
, const WCHAR
*path
)
91 msi_disable_fs_redirection( package
);
92 ret
= CreateDirectoryW( path
, NULL
);
93 msi_revert_fs_redirection( package
);
97 BOOL
msi_remove_directory( MSIPACKAGE
*package
, const WCHAR
*path
)
100 msi_disable_fs_redirection( package
);
101 ret
= RemoveDirectoryW( path
);
102 msi_revert_fs_redirection( package
);
106 BOOL
msi_set_file_attributes( MSIPACKAGE
*package
, const WCHAR
*filename
, DWORD attrs
)
109 msi_disable_fs_redirection( package
);
110 ret
= SetFileAttributesW( filename
, attrs
);
111 msi_revert_fs_redirection( package
);
115 DWORD
msi_get_file_attributes( MSIPACKAGE
*package
, const WCHAR
*path
)
118 msi_disable_fs_redirection( package
);
119 attrs
= GetFileAttributesW( path
);
120 msi_revert_fs_redirection( package
);
124 HANDLE
msi_find_first_file( MSIPACKAGE
*package
, const WCHAR
*filename
, WIN32_FIND_DATAW
*data
)
127 msi_disable_fs_redirection( package
);
128 handle
= FindFirstFileW( filename
, data
);
129 msi_revert_fs_redirection( package
);
133 BOOL
msi_find_next_file( MSIPACKAGE
*package
, HANDLE handle
, WIN32_FIND_DATAW
*data
)
136 msi_disable_fs_redirection( package
);
137 ret
= FindNextFileW( handle
, data
);
138 msi_revert_fs_redirection( package
);
142 BOOL
msi_move_file( MSIPACKAGE
*package
, const WCHAR
*from
, const WCHAR
*to
, DWORD flags
)
145 msi_disable_fs_redirection( package
);
146 ret
= MoveFileExW( from
, to
, flags
);
147 msi_revert_fs_redirection( package
);
151 static BOOL
msi_apply_filepatch( MSIPACKAGE
*package
, const WCHAR
*patch
, const WCHAR
*old
, const WCHAR
*new )
154 msi_disable_fs_redirection( package
);
155 ret
= ApplyPatchToFileW( patch
, old
, new, 0 );
156 msi_revert_fs_redirection( package
);
160 DWORD
msi_get_file_version_info( MSIPACKAGE
*package
, const WCHAR
*path
, DWORD buflen
, BYTE
*buffer
)
163 msi_disable_fs_redirection( package
);
164 if (buffer
) size
= GetFileVersionInfoW( path
, 0, buflen
, buffer
);
165 else size
= GetFileVersionInfoSizeW( path
, &handle
);
166 msi_revert_fs_redirection( package
);
170 VS_FIXEDFILEINFO
*msi_get_disk_file_version( MSIPACKAGE
*package
, const WCHAR
*filename
)
172 VS_FIXEDFILEINFO
*ptr
, *ret
;
177 if (!(version_size
= msi_get_file_version_info( package
, filename
, 0, NULL
))) return NULL
;
178 if (!(version
= msi_alloc( version_size
))) return NULL
;
180 msi_get_file_version_info( package
, filename
, version_size
, version
);
182 if (!VerQueryValueW( version
, L
"\\", (void **)&ptr
, &size
))
188 if (!(ret
= msi_alloc( size
)))
194 memcpy( ret
, ptr
, size
);
199 DWORD
msi_get_disk_file_size( MSIPACKAGE
*package
, const WCHAR
*filename
)
203 file
= msi_create_file( package
, filename
, GENERIC_READ
, FILE_SHARE_READ
, OPEN_EXISTING
, 0 );
204 if (file
== INVALID_HANDLE_VALUE
) return INVALID_FILE_SIZE
;
205 size
= GetFileSize( file
, NULL
);
210 /* Recursively create all directories in the path. */
211 BOOL
msi_create_full_path( MSIPACKAGE
*package
, const WCHAR
*path
)
217 if (!(new_path
= msi_alloc( (lstrlenW( path
) + 1) * sizeof(WCHAR
) ))) return FALSE
;
218 lstrcpyW( new_path
, path
);
220 while ((len
= lstrlenW( new_path
)) && new_path
[len
- 1] == '\\')
221 new_path
[len
- 1] = 0;
223 while (!msi_create_directory( package
, new_path
))
226 DWORD last_error
= GetLastError();
227 if (last_error
== ERROR_ALREADY_EXISTS
) break;
228 if (last_error
!= ERROR_PATH_NOT_FOUND
)
233 if (!(slash
= wcsrchr( new_path
, '\\' )))
238 len
= slash
- new_path
;
240 if (!msi_create_full_path( package
, new_path
))
245 new_path
[len
] = '\\';
247 msi_free( new_path
);
251 static void msi_file_update_ui( MSIPACKAGE
*package
, MSIFILE
*f
, const WCHAR
*action
)
255 uirow
= MSI_CreateRecord( 9 );
256 MSI_RecordSetStringW( uirow
, 1, f
->FileName
);
257 MSI_RecordSetStringW( uirow
, 9, f
->Component
->Directory
);
258 MSI_RecordSetInteger( uirow
, 6, f
->FileSize
);
259 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONDATA
, uirow
);
260 msiobj_release( &uirow
->hdr
);
261 msi_ui_progress( package
, 2, f
->FileSize
, 0, 0 );
264 static BOOL
is_registered_patch_media( MSIPACKAGE
*package
, UINT disk_id
)
268 LIST_FOR_EACH_ENTRY( patch
, &package
->patches
, MSIPATCHINFO
, entry
)
270 if (patch
->disk_id
== disk_id
&& patch
->registered
) return TRUE
;
275 static BOOL
is_obsoleted_by_patch( MSIPACKAGE
*package
, MSIFILE
*file
)
277 if (!list_empty( &package
->patches
) && file
->disk_id
< MSI_INITIAL_MEDIA_TRANSFORM_DISKID
)
279 if (!msi_get_property_int( package
->db
, L
"Installed", 0 )) return FALSE
;
282 if (is_registered_patch_media( package
, file
->disk_id
)) return TRUE
;
286 static BOOL
file_hash_matches( MSIPACKAGE
*package
, MSIFILE
*file
)
289 MSIFILEHASHINFO hash
;
291 hash
.dwFileHashInfoSize
= sizeof(hash
);
292 r
= msi_get_filehash( package
, file
->TargetPath
, &hash
);
293 if (r
!= ERROR_SUCCESS
)
296 return !memcmp( &hash
, &file
->hash
, sizeof(hash
) );
299 static msi_file_state
calculate_install_state( MSIPACKAGE
*package
, MSIFILE
*file
)
301 MSICOMPONENT
*comp
= file
->Component
;
302 VS_FIXEDFILEINFO
*file_version
;
304 msi_file_state state
;
307 comp
->Action
= msi_get_component_action( package
, comp
);
308 if (!comp
->Enabled
|| comp
->Action
!= INSTALLSTATE_LOCAL
|| (comp
->assembly
&& comp
->assembly
->installed
))
310 TRACE("skipping %s (not scheduled for install)\n", debugstr_w(file
->File
));
311 return msifs_skipped
;
313 if (is_obsoleted_by_patch( package
, file
))
315 TRACE("skipping %s (obsoleted by patch)\n", debugstr_w(file
->File
));
316 return msifs_skipped
;
318 if ((msi_is_global_assembly( comp
) && !comp
->assembly
->installed
) ||
319 msi_get_file_attributes( package
, file
->TargetPath
) == INVALID_FILE_ATTRIBUTES
)
321 TRACE("installing %s (missing)\n", debugstr_w(file
->File
));
322 return msifs_missing
;
326 if ((file_version
= msi_get_disk_file_version( package
, file
->TargetPath
)))
328 if (msi_compare_file_versions( file_version
, file
->Version
) < 0)
330 TRACE("overwriting %s (new version %s old version %u.%u.%u.%u)\n",
331 debugstr_w(file
->File
), debugstr_w(file
->Version
),
332 HIWORD(file_version
->dwFileVersionMS
), LOWORD(file_version
->dwFileVersionMS
),
333 HIWORD(file_version
->dwFileVersionLS
), LOWORD(file_version
->dwFileVersionLS
));
334 state
= msifs_overwrite
;
338 TRACE("keeping %s (new version %s old version %u.%u.%u.%u)\n",
339 debugstr_w(file
->File
), debugstr_w(file
->Version
),
340 HIWORD(file_version
->dwFileVersionMS
), LOWORD(file_version
->dwFileVersionMS
),
341 HIWORD(file_version
->dwFileVersionLS
), LOWORD(file_version
->dwFileVersionLS
));
342 state
= msifs_present
;
344 msi_free( file_version
);
347 else if ((font_version
= msi_get_font_file_version( package
, file
->TargetPath
)))
349 if (msi_compare_font_versions( font_version
, file
->Version
) < 0)
351 TRACE("overwriting %s (new version %s old version %s)\n",
352 debugstr_w(file
->File
), debugstr_w(file
->Version
), debugstr_w(font_version
));
353 state
= msifs_overwrite
;
357 TRACE("keeping %s (new version %s old version %s)\n",
358 debugstr_w(file
->File
), debugstr_w(file
->Version
), debugstr_w(font_version
));
359 state
= msifs_present
;
361 msi_free( font_version
);
365 if ((size
= msi_get_disk_file_size( package
, file
->TargetPath
)) != file
->FileSize
)
367 TRACE("overwriting %s (old size %lu new size %d)\n", debugstr_w(file
->File
), size
, file
->FileSize
);
368 return msifs_overwrite
;
370 if (file
->hash
.dwFileHashInfoSize
)
372 if (file_hash_matches( package
, file
))
374 TRACE("keeping %s (hash match)\n", debugstr_w(file
->File
));
375 return msifs_hashmatch
;
379 TRACE("overwriting %s (hash mismatch)\n", debugstr_w(file
->File
));
380 return msifs_overwrite
;
384 TRACE("keeping %s\n", debugstr_w(file
->File
));
385 return msifs_present
;
388 static void schedule_install_files(MSIPACKAGE
*package
)
392 LIST_FOR_EACH_ENTRY(file
, &package
->files
, MSIFILE
, entry
)
394 MSICOMPONENT
*comp
= file
->Component
;
396 file
->state
= calculate_install_state( package
, file
);
397 if (file
->state
== msifs_overwrite
&& (comp
->Attributes
& msidbComponentAttributesNeverOverwrite
))
399 TRACE("not overwriting %s\n", debugstr_w(file
->TargetPath
));
400 file
->state
= msifs_skipped
;
405 static UINT
copy_file( MSIPACKAGE
*package
, MSIFILE
*file
, WCHAR
*source
)
409 ret
= msi_copy_file( package
, source
, file
->TargetPath
, FALSE
);
411 return GetLastError();
413 msi_set_file_attributes( package
, file
->TargetPath
, FILE_ATTRIBUTE_NORMAL
);
414 return ERROR_SUCCESS
;
417 static UINT
copy_install_file(MSIPACKAGE
*package
, MSIFILE
*file
, LPWSTR source
)
421 TRACE("Copying %s to %s\n", debugstr_w(source
), debugstr_w(file
->TargetPath
));
423 gle
= copy_file( package
, file
, source
);
424 if (gle
== ERROR_SUCCESS
)
427 if (gle
== ERROR_ALREADY_EXISTS
&& file
->state
== msifs_overwrite
)
429 TRACE("overwriting existing file\n");
430 return ERROR_SUCCESS
;
432 else if (gle
== ERROR_ACCESS_DENIED
)
434 msi_set_file_attributes( package
, file
->TargetPath
, FILE_ATTRIBUTE_NORMAL
);
436 gle
= copy_file( package
, file
, source
);
437 TRACE("Overwriting existing file: %d\n", gle
);
439 if (gle
== ERROR_SHARING_VIOLATION
|| gle
== ERROR_USER_MAPPED_FILE
)
441 WCHAR
*tmpfileW
, *pathW
, *p
;
444 TRACE("file in use, scheduling rename operation\n");
446 if (!(pathW
= strdupW( file
->TargetPath
))) return ERROR_OUTOFMEMORY
;
447 if ((p
= wcsrchr(pathW
, '\\'))) *p
= 0;
448 len
= lstrlenW( pathW
) + 16;
449 if (!(tmpfileW
= msi_alloc(len
* sizeof(WCHAR
))))
452 return ERROR_OUTOFMEMORY
;
454 if (!GetTempFileNameW( pathW
, L
"msi", 0, tmpfileW
)) tmpfileW
[0] = 0;
457 if (msi_copy_file( package
, source
, tmpfileW
, FALSE
) &&
458 msi_move_file( package
, file
->TargetPath
, NULL
, MOVEFILE_DELAY_UNTIL_REBOOT
) &&
459 msi_move_file( package
, tmpfileW
, file
->TargetPath
, MOVEFILE_DELAY_UNTIL_REBOOT
))
461 package
->need_reboot_at_end
= 1;
466 gle
= GetLastError();
467 WARN("failed to schedule rename operation: %d)\n", gle
);
468 DeleteFileW( tmpfileW
);
476 static UINT
create_directory( MSIPACKAGE
*package
, const WCHAR
*dir
)
479 const WCHAR
*install_path
;
481 install_path
= msi_get_target_folder( package
, dir
);
482 if (!install_path
) return ERROR_FUNCTION_FAILED
;
484 folder
= msi_get_loaded_folder( package
, dir
);
485 if (folder
->State
== FOLDER_STATE_UNINITIALIZED
)
487 msi_create_full_path( package
, install_path
);
488 folder
->State
= FOLDER_STATE_CREATED
;
490 return ERROR_SUCCESS
;
493 static MSIFILE
*find_file( MSIPACKAGE
*package
, UINT disk_id
, const WCHAR
*filename
)
497 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
499 if (file
->disk_id
== disk_id
&&
500 file
->state
!= msifs_installed
&&
501 !wcsicmp( filename
, file
->File
)) return file
;
506 static BOOL
installfiles_cb(MSIPACKAGE
*package
, LPCWSTR filename
, DWORD action
,
507 LPWSTR
*path
, DWORD
*attrs
, PVOID user
)
509 MSIFILE
*file
= *(MSIFILE
**)user
;
511 if (action
== MSICABEXTRACT_BEGINEXTRACT
)
513 if (!(file
= find_file( package
, file
->disk_id
, filename
)))
515 TRACE("unknown file in cabinet (%s)\n", debugstr_w(filename
));
518 if (file
->state
!= msifs_missing
&& file
->state
!= msifs_overwrite
)
521 if (!msi_is_global_assembly( file
->Component
))
523 create_directory( package
, file
->Component
->Directory
);
525 *path
= strdupW( file
->TargetPath
);
526 *attrs
= file
->Attributes
;
527 *(MSIFILE
**)user
= file
;
529 else if (action
== MSICABEXTRACT_FILEEXTRACTED
)
531 if (!msi_is_global_assembly( file
->Component
)) file
->state
= msifs_installed
;
537 WCHAR
*msi_resolve_file_source( MSIPACKAGE
*package
, MSIFILE
*file
)
541 TRACE("Working to resolve source of file %s\n", debugstr_w(file
->File
));
543 if (file
->IsCompressed
) return NULL
;
545 p
= msi_resolve_source_folder( package
, file
->Component
->Directory
, NULL
);
546 path
= msi_build_directory_name( 2, p
, file
->ShortName
);
548 if (file
->LongName
&& msi_get_file_attributes( package
, path
) == INVALID_FILE_ATTRIBUTES
)
551 path
= msi_build_directory_name( 2, p
, file
->LongName
);
554 TRACE("file %s source resolves to %s\n", debugstr_w(file
->File
), debugstr_w(path
));
559 * ACTION_InstallFiles()
561 * For efficiency, this is done in two passes:
562 * 1) Correct all the TargetPaths and determine what files are to be installed.
563 * 2) Extract Cabinets and copy files.
565 UINT
ACTION_InstallFiles(MSIPACKAGE
*package
)
568 UINT rc
= ERROR_SUCCESS
;
571 msi_set_sourcedir_props(package
, FALSE
);
573 if (package
->script
== SCRIPT_NONE
)
574 return msi_schedule_action(package
, SCRIPT_INSTALL
, L
"InstallFiles");
576 schedule_install_files(package
);
577 mi
= msi_alloc_zero( sizeof(MSIMEDIAINFO
) );
579 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
581 BOOL is_global_assembly
= msi_is_global_assembly( file
->Component
);
583 msi_file_update_ui( package
, file
, L
"InstallFiles" );
585 rc
= msi_load_media_info( package
, file
->Sequence
, mi
);
586 if (rc
!= ERROR_SUCCESS
)
588 ERR("Unable to load media info for %s (%u)\n", debugstr_w(file
->File
), rc
);
589 rc
= ERROR_FUNCTION_FAILED
;
593 if (file
->state
!= msifs_hashmatch
&&
594 file
->state
!= msifs_skipped
&&
595 (file
->state
!= msifs_present
|| !msi_get_property_int( package
->db
, L
"Installed", 0 )) &&
596 (rc
= ready_media( package
, file
->IsCompressed
, mi
)))
598 ERR("Failed to ready media for %s\n", debugstr_w(file
->File
));
602 if (file
->state
!= msifs_missing
&& !mi
->is_continuous
&& file
->state
!= msifs_overwrite
)
605 if (file
->Sequence
> mi
->last_sequence
|| mi
->is_continuous
||
606 (file
->IsCompressed
&& !mi
->is_extracted
))
609 MSIFILE
*cursor
= file
;
612 data
.package
= package
;
613 data
.cb
= installfiles_cb
;
616 if (file
->IsCompressed
&& !msi_cabextract(package
, mi
, &data
))
618 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi
->cabinet
));
619 rc
= ERROR_INSTALL_FAILURE
;
624 if (!file
->IsCompressed
)
626 WCHAR
*source
= msi_resolve_file_source(package
, file
);
628 TRACE("copying %s to %s\n", debugstr_w(source
), debugstr_w(file
->TargetPath
));
630 if (!is_global_assembly
)
632 create_directory(package
, file
->Component
->Directory
);
634 rc
= copy_install_file(package
, file
, source
);
635 if (rc
!= ERROR_SUCCESS
)
637 ERR("Failed to copy %s to %s (%u)\n", debugstr_w(source
), debugstr_w(file
->TargetPath
), rc
);
638 rc
= ERROR_INSTALL_FAILURE
;
642 if (!is_global_assembly
) file
->state
= msifs_installed
;
645 else if (!is_global_assembly
&& file
->state
!= msifs_installed
&&
646 !(file
->Attributes
& msidbFileAttributesPatchAdded
))
648 ERR("compressed file wasn't installed (%s)\n", debugstr_w(file
->File
));
649 rc
= ERROR_INSTALL_FAILURE
;
653 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
655 MSICOMPONENT
*comp
= file
->Component
;
657 if (!msi_is_global_assembly( comp
) || comp
->assembly
->installed
||
658 (file
->state
!= msifs_missing
&& file
->state
!= msifs_overwrite
)) continue;
660 rc
= msi_install_assembly( package
, comp
);
661 if (rc
!= ERROR_SUCCESS
)
663 ERR("Failed to install assembly\n");
664 rc
= ERROR_INSTALL_FAILURE
;
667 file
->state
= msifs_installed
;
671 msi_free_media_info(mi
);
675 static MSIFILEPATCH
*find_filepatch( MSIPACKAGE
*package
, UINT disk_id
, const WCHAR
*key
)
679 LIST_FOR_EACH_ENTRY( patch
, &package
->filepatches
, MSIFILEPATCH
, entry
)
681 if (!patch
->extracted
&& patch
->disk_id
== disk_id
&& !wcscmp( key
, patch
->File
->File
))
687 static BOOL
patchfiles_cb(MSIPACKAGE
*package
, LPCWSTR file
, DWORD action
,
688 LPWSTR
*path
, DWORD
*attrs
, PVOID user
)
690 MSIFILEPATCH
*patch
= *(MSIFILEPATCH
**)user
;
692 if (action
== MSICABEXTRACT_BEGINEXTRACT
)
696 if (is_registered_patch_media( package
, patch
->disk_id
) ||
697 !(patch
= find_filepatch( package
, patch
->disk_id
, file
))) return FALSE
;
699 comp
= patch
->File
->Component
;
700 comp
->Action
= msi_get_component_action( package
, comp
);
701 if (!comp
->Enabled
|| comp
->Action
!= INSTALLSTATE_LOCAL
)
703 TRACE("file %s component %s not installed or disabled\n",
704 debugstr_w(patch
->File
->File
), debugstr_w(comp
->Component
));
708 patch
->path
= msi_create_temp_file( package
->db
);
709 *path
= strdupW( patch
->path
);
710 *attrs
= patch
->File
->Attributes
;
711 *(MSIFILEPATCH
**)user
= patch
;
713 else if (action
== MSICABEXTRACT_FILEEXTRACTED
)
715 patch
->extracted
= TRUE
;
721 static UINT
patch_file( MSIPACKAGE
*package
, MSIFILEPATCH
*patch
)
723 UINT r
= ERROR_SUCCESS
;
724 WCHAR
*tmpfile
= msi_create_temp_file( package
->db
);
726 if (!tmpfile
) return ERROR_INSTALL_FAILURE
;
727 if (msi_apply_filepatch( package
, patch
->path
, patch
->File
->TargetPath
, tmpfile
))
729 msi_delete_file( package
, patch
->File
->TargetPath
);
730 msi_move_file( package
, tmpfile
, patch
->File
->TargetPath
, 0 );
734 WARN( "failed to patch %s: %#lx\n", debugstr_w(patch
->File
->TargetPath
), GetLastError() );
735 r
= ERROR_INSTALL_FAILURE
;
737 DeleteFileW( patch
->path
);
738 DeleteFileW( tmpfile
);
743 static UINT
patch_assembly( MSIPACKAGE
*package
, MSIASSEMBLY
*assembly
, MSIFILEPATCH
*patch
)
745 UINT r
= ERROR_FUNCTION_FAILED
;
749 if (!(iter
= msi_create_assembly_enum( package
, assembly
->display_name
)))
750 return ERROR_FUNCTION_FAILED
;
752 while ((IAssemblyEnum_GetNextAssembly( iter
, NULL
, &name
, 0 ) == S_OK
))
754 WCHAR
*displayname
, *path
;
758 hr
= IAssemblyName_GetDisplayName( name
, NULL
, &len
, 0 );
759 if (hr
!= E_NOT_SUFFICIENT_BUFFER
|| !(displayname
= msi_alloc( len
* sizeof(WCHAR
) )))
762 hr
= IAssemblyName_GetDisplayName( name
, displayname
, &len
, 0 );
765 msi_free( displayname
);
769 if ((path
= msi_get_assembly_path( package
, displayname
)))
771 if (!msi_copy_file( package
, path
, patch
->File
->TargetPath
, FALSE
))
773 ERR( "failed to copy file %s -> %s (%lu)\n", debugstr_w(path
),
774 debugstr_w(patch
->File
->TargetPath
), GetLastError() );
776 msi_free( displayname
);
777 IAssemblyName_Release( name
);
780 r
= patch_file( package
, patch
);
784 msi_free( displayname
);
785 IAssemblyName_Release( name
);
786 if (r
== ERROR_SUCCESS
) break;
789 IAssemblyEnum_Release( iter
);
793 UINT
ACTION_PatchFiles( MSIPACKAGE
*package
)
797 UINT rc
= ERROR_SUCCESS
;
799 TRACE("%p\n", package
);
801 if (package
->script
== SCRIPT_NONE
)
802 return msi_schedule_action(package
, SCRIPT_INSTALL
, L
"PatchFiles");
804 mi
= msi_alloc_zero( sizeof(MSIMEDIAINFO
) );
806 TRACE("extracting files\n");
808 LIST_FOR_EACH_ENTRY( patch
, &package
->filepatches
, MSIFILEPATCH
, entry
)
810 MSIFILE
*file
= patch
->File
;
811 MSICOMPONENT
*comp
= file
->Component
;
813 rc
= msi_load_media_info( package
, patch
->Sequence
, mi
);
814 if (rc
!= ERROR_SUCCESS
)
816 ERR("Unable to load media info for %s (%u)\n", debugstr_w(file
->File
), rc
);
817 rc
= ERROR_FUNCTION_FAILED
;
820 comp
->Action
= msi_get_component_action( package
, comp
);
821 if (!comp
->Enabled
|| comp
->Action
!= INSTALLSTATE_LOCAL
) continue;
823 if (!patch
->extracted
)
826 MSIFILEPATCH
*cursor
= patch
;
828 rc
= ready_media( package
, TRUE
, mi
);
829 if (rc
!= ERROR_SUCCESS
)
831 ERR("Failed to ready media for %s\n", debugstr_w(file
->File
));
835 data
.package
= package
;
836 data
.cb
= patchfiles_cb
;
839 if (!msi_cabextract( package
, mi
, &data
))
841 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi
->cabinet
));
842 rc
= ERROR_INSTALL_FAILURE
;
848 TRACE("applying patches\n");
850 LIST_FOR_EACH_ENTRY( patch
, &package
->filepatches
, MSIFILEPATCH
, entry
)
852 MSICOMPONENT
*comp
= patch
->File
->Component
;
854 if (!patch
->path
) continue;
856 if (msi_is_global_assembly( comp
))
857 rc
= patch_assembly( package
, comp
->assembly
, patch
);
859 rc
= patch_file( package
, patch
);
861 if (rc
&& !(patch
->Attributes
& msidbPatchAttributesNonVital
))
863 ERR("Failed to apply patch to file: %s\n", debugstr_w(patch
->File
->File
));
867 if (msi_is_global_assembly( comp
))
869 if ((rc
= msi_install_assembly( package
, comp
)))
871 ERR("Failed to install patched assembly\n");
878 msi_free_media_info(mi
);
882 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
893 static BOOL
move_file( MSIPACKAGE
*package
, const WCHAR
*source
, const WCHAR
*dest
, int options
)
897 if (msi_get_file_attributes( package
, source
) == FILE_ATTRIBUTE_DIRECTORY
||
898 msi_get_file_attributes( package
, dest
) == FILE_ATTRIBUTE_DIRECTORY
)
900 WARN("Source or dest is directory, not moving\n");
904 if (options
== msidbMoveFileOptionsMove
)
906 TRACE("moving %s -> %s\n", debugstr_w(source
), debugstr_w(dest
));
907 ret
= msi_move_file( package
, source
, dest
, MOVEFILE_REPLACE_EXISTING
);
910 WARN( "msi_move_file failed: %lu\n", GetLastError() );
916 TRACE("copying %s -> %s\n", debugstr_w(source
), debugstr_w(dest
));
917 ret
= msi_copy_file( package
, source
, dest
, FALSE
);
920 WARN( "msi_copy_file failed: %lu\n", GetLastError() );
928 static WCHAR
*wildcard_to_file( const WCHAR
*wildcard
, const WCHAR
*filename
)
932 DWORD dirlen
, pathlen
;
934 ptr
= wcsrchr(wildcard
, '\\');
935 dirlen
= ptr
- wildcard
+ 1;
937 pathlen
= dirlen
+ lstrlenW(filename
) + 1;
938 if (!(path
= msi_alloc(pathlen
* sizeof(WCHAR
)))) return NULL
;
940 lstrcpynW(path
, wildcard
, dirlen
+ 1);
941 lstrcatW(path
, filename
);
946 static void free_file_entry(FILE_LIST
*file
)
948 msi_free(file
->source
);
949 msi_free(file
->dest
);
953 static void free_list(FILE_LIST
*list
)
955 while (!list_empty(&list
->entry
))
957 FILE_LIST
*file
= LIST_ENTRY(list_head(&list
->entry
), FILE_LIST
, entry
);
959 list_remove(&file
->entry
);
960 free_file_entry(file
);
964 static BOOL
add_wildcard( FILE_LIST
*files
, const WCHAR
*source
, WCHAR
*dest
)
966 FILE_LIST
*new, *file
;
967 WCHAR
*ptr
, *filename
;
970 new = msi_alloc_zero(sizeof(FILE_LIST
));
974 new->source
= strdupW(source
);
975 ptr
= wcsrchr(dest
, '\\') + 1;
976 filename
= wcsrchr(new->source
, '\\') + 1;
978 new->sourcename
= filename
;
983 new->destname
= new->sourcename
;
985 size
= (ptr
- dest
) + lstrlenW(filename
) + 1;
986 new->dest
= msi_alloc(size
* sizeof(WCHAR
));
989 free_file_entry(new);
993 lstrcpynW(new->dest
, dest
, ptr
- dest
+ 1);
994 lstrcatW(new->dest
, filename
);
996 if (list_empty(&files
->entry
))
998 list_add_head(&files
->entry
, &new->entry
);
1002 LIST_FOR_EACH_ENTRY(file
, &files
->entry
, FILE_LIST
, entry
)
1004 if (wcscmp( source
, file
->source
) < 0)
1006 list_add_before(&file
->entry
, &new->entry
);
1011 list_add_after(&file
->entry
, &new->entry
);
1015 static BOOL
move_files_wildcard( MSIPACKAGE
*package
, const WCHAR
*source
, WCHAR
*dest
, int options
)
1017 WIN32_FIND_DATAW wfd
;
1021 FILE_LIST files
, *file
;
1024 hfile
= msi_find_first_file( package
, source
, &wfd
);
1025 if (hfile
== INVALID_HANDLE_VALUE
) return FALSE
;
1027 list_init(&files
.entry
);
1029 for (res
= TRUE
; res
; res
= msi_find_next_file( package
, hfile
, &wfd
))
1031 if (is_dot_dir(wfd
.cFileName
)) continue;
1033 path
= wildcard_to_file( source
, wfd
.cFileName
);
1040 add_wildcard(&files
, path
, dest
);
1044 /* no files match the wildcard */
1045 if (list_empty(&files
.entry
))
1048 /* only the first wildcard match gets renamed to dest */
1049 file
= LIST_ENTRY(list_head(&files
.entry
), FILE_LIST
, entry
);
1050 size
= (wcsrchr(file
->dest
, '\\') - file
->dest
) + lstrlenW(file
->destname
) + 2;
1051 file
->dest
= msi_realloc(file
->dest
, size
* sizeof(WCHAR
));
1058 /* file->dest may be shorter after the reallocation, so add a NULL
1059 * terminator. This is needed for the call to wcsrchr, as there will no
1060 * longer be a NULL terminator within the bounds of the allocation in this case.
1062 file
->dest
[size
- 1] = '\0';
1063 lstrcpyW(wcsrchr(file
->dest
, '\\') + 1, file
->destname
);
1065 while (!list_empty(&files
.entry
))
1067 file
= LIST_ENTRY(list_head(&files
.entry
), FILE_LIST
, entry
);
1069 move_file( package
, file
->source
, file
->dest
, options
);
1071 list_remove(&file
->entry
);
1072 free_file_entry(file
);
1083 void msi_reduce_to_long_filename( WCHAR
*filename
)
1085 WCHAR
*p
= wcschr( filename
, '|' );
1086 if (p
) memmove( filename
, p
+ 1, (lstrlenW( p
+ 1 ) + 1) * sizeof(WCHAR
) );
1089 static UINT
ITERATE_MoveFiles( MSIRECORD
*rec
, LPVOID param
)
1091 MSIPACKAGE
*package
= param
;
1094 LPCWSTR sourcename
, component
;
1095 LPWSTR sourcedir
, destname
= NULL
, destdir
= NULL
, source
= NULL
, dest
= NULL
;
1100 component
= MSI_RecordGetString(rec
, 2);
1101 comp
= msi_get_loaded_component(package
, component
);
1103 return ERROR_SUCCESS
;
1105 comp
->Action
= msi_get_component_action( package
, comp
);
1106 if (comp
->Action
!= INSTALLSTATE_LOCAL
)
1108 TRACE("component not scheduled for installation %s\n", debugstr_w(component
));
1109 return ERROR_SUCCESS
;
1112 sourcename
= MSI_RecordGetString(rec
, 3);
1113 options
= MSI_RecordGetInteger(rec
, 7);
1115 sourcedir
= msi_dup_property(package
->db
, MSI_RecordGetString(rec
, 5));
1119 destdir
= msi_dup_property(package
->db
, MSI_RecordGetString(rec
, 6));
1125 if (msi_get_file_attributes( package
, sourcedir
) == INVALID_FILE_ATTRIBUTES
)
1128 source
= strdupW(sourcedir
);
1134 size
= lstrlenW(sourcedir
) + lstrlenW(sourcename
) + 2;
1135 source
= msi_alloc(size
* sizeof(WCHAR
));
1139 lstrcpyW(source
, sourcedir
);
1140 if (source
[lstrlenW(source
) - 1] != '\\')
1141 lstrcatW(source
, L
"\\");
1142 lstrcatW(source
, sourcename
);
1145 wildcards
= wcschr(source
, '*') || wcschr(source
, '?');
1147 if (MSI_RecordIsNull(rec
, 4))
1153 destname
= strdupW(sourcename
);
1154 else if ((p
= wcsrchr(sourcedir
, '\\')))
1155 destname
= strdupW(p
+ 1);
1157 destname
= strdupW(sourcedir
);
1164 destname
= strdupW(MSI_RecordGetString(rec
, 4));
1165 if (destname
) msi_reduce_to_long_filename(destname
);
1170 size
= lstrlenW(destname
);
1172 size
+= lstrlenW(destdir
) + 2;
1173 dest
= msi_alloc(size
* sizeof(WCHAR
));
1177 lstrcpyW(dest
, destdir
);
1178 if (dest
[lstrlenW(dest
) - 1] != '\\')
1179 lstrcatW(dest
, L
"\\");
1182 lstrcatW(dest
, destname
);
1184 if (msi_get_file_attributes( package
, destdir
) == INVALID_FILE_ATTRIBUTES
)
1186 if (!msi_create_full_path( package
, destdir
))
1188 WARN( "failed to create directory %lu\n", GetLastError() );
1194 move_file( package
, source
, dest
, options
);
1196 move_files_wildcard( package
, source
, dest
, options
);
1199 uirow
= MSI_CreateRecord( 9 );
1200 MSI_RecordSetStringW( uirow
, 1, MSI_RecordGetString(rec
, 1) );
1201 MSI_RecordSetInteger( uirow
, 6, 1 ); /* FIXME */
1202 MSI_RecordSetStringW( uirow
, 9, destdir
);
1203 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONDATA
, uirow
);
1204 msiobj_release( &uirow
->hdr
);
1206 msi_free(sourcedir
);
1212 return ERROR_SUCCESS
;
1215 UINT
ACTION_MoveFiles( MSIPACKAGE
*package
)
1220 if (package
->script
== SCRIPT_NONE
)
1221 return msi_schedule_action(package
, SCRIPT_INSTALL
, L
"MoveFiles");
1223 rc
= MSI_DatabaseOpenViewW(package
->db
, L
"SELECT * FROM `MoveFile`", &view
);
1224 if (rc
!= ERROR_SUCCESS
)
1225 return ERROR_SUCCESS
;
1227 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_MoveFiles
, package
);
1228 msiobj_release(&view
->hdr
);
1232 static WCHAR
*get_duplicate_filename( MSIPACKAGE
*package
, MSIRECORD
*row
, const WCHAR
*file_key
, const WCHAR
*src
)
1235 WCHAR
*dst_name
, *dst_path
, *dst
;
1237 if (MSI_RecordIsNull( row
, 4 ))
1239 len
= lstrlenW( src
) + 1;
1240 if (!(dst_name
= msi_alloc( len
* sizeof(WCHAR
)))) return NULL
;
1241 lstrcpyW( dst_name
, wcsrchr( src
, '\\' ) + 1 );
1245 MSI_RecordGetStringW( row
, 4, NULL
, &len
);
1246 if (!(dst_name
= msi_alloc( ++len
* sizeof(WCHAR
) ))) return NULL
;
1247 MSI_RecordGetStringW( row
, 4, dst_name
, &len
);
1248 msi_reduce_to_long_filename( dst_name
);
1251 if (MSI_RecordIsNull( row
, 5 ))
1254 dst_path
= strdupW( src
);
1255 p
= wcsrchr( dst_path
, '\\' );
1260 const WCHAR
*dst_key
= MSI_RecordGetString( row
, 5 );
1262 dst_path
= strdupW( msi_get_target_folder( package
, dst_key
) );
1265 /* try a property */
1266 dst_path
= msi_dup_property( package
->db
, dst_key
);
1269 FIXME("Unable to get destination folder, try AppSearch properties\n");
1270 msi_free( dst_name
);
1276 dst
= msi_build_directory_name( 2, dst_path
, dst_name
);
1277 msi_create_full_path( package
, dst_path
);
1279 msi_free( dst_name
);
1280 msi_free( dst_path
);
1284 static UINT
ITERATE_DuplicateFiles(MSIRECORD
*row
, LPVOID param
)
1286 MSIPACKAGE
*package
= param
;
1288 LPCWSTR file_key
, component
;
1293 component
= MSI_RecordGetString(row
,2);
1294 comp
= msi_get_loaded_component(package
, component
);
1296 return ERROR_SUCCESS
;
1298 comp
->Action
= msi_get_component_action( package
, comp
);
1299 if (comp
->Action
!= INSTALLSTATE_LOCAL
)
1301 TRACE("component not scheduled for installation %s\n", debugstr_w(component
));
1302 return ERROR_SUCCESS
;
1305 file_key
= MSI_RecordGetString(row
,3);
1308 ERR("Unable to get file key\n");
1309 return ERROR_FUNCTION_FAILED
;
1312 file
= msi_get_loaded_file( package
, file_key
);
1315 ERR("Original file unknown %s\n", debugstr_w(file_key
));
1316 return ERROR_SUCCESS
;
1319 dest
= get_duplicate_filename( package
, row
, file_key
, file
->TargetPath
);
1322 WARN("Unable to get duplicate filename\n");
1323 return ERROR_SUCCESS
;
1326 TRACE("Duplicating file %s to %s\n", debugstr_w(file
->TargetPath
), debugstr_w(dest
));
1327 if (!msi_copy_file( package
, file
->TargetPath
, dest
, TRUE
))
1329 WARN( "failed to copy file %s -> %s (%lu)\n",
1330 debugstr_w(file
->TargetPath
), debugstr_w(dest
), GetLastError() );
1332 FIXME("We should track these duplicate files as well\n");
1334 uirow
= MSI_CreateRecord( 9 );
1335 MSI_RecordSetStringW( uirow
, 1, MSI_RecordGetString( row
, 1 ) );
1336 MSI_RecordSetInteger( uirow
, 6, file
->FileSize
);
1337 MSI_RecordSetStringW( uirow
, 9, MSI_RecordGetString( row
, 5 ) );
1338 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONDATA
, uirow
);
1339 msiobj_release( &uirow
->hdr
);
1342 return ERROR_SUCCESS
;
1345 UINT
ACTION_DuplicateFiles(MSIPACKAGE
*package
)
1350 if (package
->script
== SCRIPT_NONE
)
1351 return msi_schedule_action(package
, SCRIPT_INSTALL
, L
"DuplicateFiles");
1353 rc
= MSI_DatabaseOpenViewW(package
->db
, L
"SELECT * FROM `DuplicateFile`", &view
);
1354 if (rc
!= ERROR_SUCCESS
)
1355 return ERROR_SUCCESS
;
1357 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_DuplicateFiles
, package
);
1358 msiobj_release(&view
->hdr
);
1362 static UINT
ITERATE_RemoveDuplicateFiles( MSIRECORD
*row
, LPVOID param
)
1364 MSIPACKAGE
*package
= param
;
1366 LPCWSTR file_key
, component
;
1371 component
= MSI_RecordGetString( row
, 2 );
1372 comp
= msi_get_loaded_component( package
, component
);
1374 return ERROR_SUCCESS
;
1376 comp
->Action
= msi_get_component_action( package
, comp
);
1377 if (comp
->Action
!= INSTALLSTATE_ABSENT
)
1379 TRACE("component not scheduled for removal %s\n", debugstr_w(component
));
1380 return ERROR_SUCCESS
;
1383 file_key
= MSI_RecordGetString( row
, 3 );
1386 ERR("Unable to get file key\n");
1387 return ERROR_FUNCTION_FAILED
;
1390 file
= msi_get_loaded_file( package
, file_key
);
1393 ERR("Original file unknown %s\n", debugstr_w(file_key
));
1394 return ERROR_SUCCESS
;
1397 dest
= get_duplicate_filename( package
, row
, file_key
, file
->TargetPath
);
1400 WARN("Unable to get duplicate filename\n");
1401 return ERROR_SUCCESS
;
1404 TRACE("Removing duplicate %s of %s\n", debugstr_w(dest
), debugstr_w(file
->TargetPath
));
1405 if (!msi_delete_file( package
, dest
))
1407 WARN( "failed to delete duplicate file %s (%lu)\n", debugstr_w(dest
), GetLastError() );
1410 uirow
= MSI_CreateRecord( 9 );
1411 MSI_RecordSetStringW( uirow
, 1, MSI_RecordGetString( row
, 1 ) );
1412 MSI_RecordSetStringW( uirow
, 9, MSI_RecordGetString( row
, 5 ) );
1413 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONDATA
, uirow
);
1414 msiobj_release( &uirow
->hdr
);
1417 return ERROR_SUCCESS
;
1420 UINT
ACTION_RemoveDuplicateFiles( MSIPACKAGE
*package
)
1425 if (package
->script
== SCRIPT_NONE
)
1426 return msi_schedule_action(package
, SCRIPT_INSTALL
, L
"RemoveDuplicateFiles");
1428 rc
= MSI_DatabaseOpenViewW( package
->db
, L
"SELECT * FROM `DuplicateFile`", &view
);
1429 if (rc
!= ERROR_SUCCESS
)
1430 return ERROR_SUCCESS
;
1432 rc
= MSI_IterateRecords( view
, NULL
, ITERATE_RemoveDuplicateFiles
, package
);
1433 msiobj_release( &view
->hdr
);
1437 static BOOL
verify_comp_for_removal(MSICOMPONENT
*comp
, UINT install_mode
)
1440 if (comp
->Action
!= INSTALLSTATE_SOURCE
&&
1441 comp
->Attributes
& msidbComponentAttributesSourceOnly
&&
1442 (install_mode
== msidbRemoveFileInstallModeOnRemove
||
1443 install_mode
== msidbRemoveFileInstallModeOnBoth
)) return TRUE
;
1445 switch (comp
->Action
)
1447 case INSTALLSTATE_LOCAL
:
1448 case INSTALLSTATE_SOURCE
:
1449 if (install_mode
== msidbRemoveFileInstallModeOnInstall
||
1450 install_mode
== msidbRemoveFileInstallModeOnBoth
) return TRUE
;
1452 case INSTALLSTATE_ABSENT
:
1453 if (install_mode
== msidbRemoveFileInstallModeOnRemove
||
1454 install_mode
== msidbRemoveFileInstallModeOnBoth
) return TRUE
;
1461 static UINT
ITERATE_RemoveFiles(MSIRECORD
*row
, LPVOID param
)
1463 MSIPACKAGE
*package
= param
;
1466 LPCWSTR component
, dirprop
;
1468 LPWSTR dir
= NULL
, path
= NULL
, filename
= NULL
;
1470 UINT ret
= ERROR_SUCCESS
;
1472 component
= MSI_RecordGetString(row
, 2);
1473 dirprop
= MSI_RecordGetString(row
, 4);
1474 install_mode
= MSI_RecordGetInteger(row
, 5);
1476 comp
= msi_get_loaded_component(package
, component
);
1478 return ERROR_SUCCESS
;
1480 comp
->Action
= msi_get_component_action( package
, comp
);
1481 if (!verify_comp_for_removal(comp
, install_mode
))
1483 TRACE("Skipping removal due to install mode\n");
1484 return ERROR_SUCCESS
;
1486 if (comp
->assembly
&& !comp
->assembly
->application
)
1488 return ERROR_SUCCESS
;
1490 if (comp
->Attributes
& msidbComponentAttributesPermanent
)
1492 TRACE("permanent component, not removing file\n");
1493 return ERROR_SUCCESS
;
1496 dir
= msi_dup_property(package
->db
, dirprop
);
1499 WARN("directory property has no value\n");
1500 return ERROR_SUCCESS
;
1503 if ((filename
= strdupW( MSI_RecordGetString(row
, 3) )))
1505 msi_reduce_to_long_filename( filename
);
1506 size
= lstrlenW( filename
);
1508 size
+= lstrlenW(dir
) + 2;
1509 path
= msi_alloc(size
* sizeof(WCHAR
));
1512 ret
= ERROR_OUTOFMEMORY
;
1518 lstrcpyW(path
, dir
);
1519 PathAddBackslashW(path
);
1520 lstrcatW(path
, filename
);
1522 TRACE("Deleting misc file: %s\n", debugstr_w(path
));
1523 msi_delete_file( package
, path
);
1527 TRACE("Removing misc directory: %s\n", debugstr_w(dir
));
1528 msi_remove_directory( package
, dir
);
1532 uirow
= MSI_CreateRecord( 9 );
1533 MSI_RecordSetStringW( uirow
, 1, MSI_RecordGetString(row
, 1) );
1534 MSI_RecordSetStringW( uirow
, 9, dir
);
1535 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONDATA
, uirow
);
1536 msiobj_release( &uirow
->hdr
);
1544 static void remove_folder( MSIFOLDER
*folder
)
1548 LIST_FOR_EACH_ENTRY( fl
, &folder
->children
, FolderList
, entry
)
1550 remove_folder( fl
->folder
);
1552 if (!folder
->persistent
&& folder
->State
!= FOLDER_STATE_REMOVED
)
1554 if (RemoveDirectoryW( folder
->ResolvedTarget
)) folder
->State
= FOLDER_STATE_REMOVED
;
1558 UINT
ACTION_RemoveFiles( MSIPACKAGE
*package
)
1565 if (package
->script
== SCRIPT_NONE
)
1566 return msi_schedule_action(package
, SCRIPT_INSTALL
, L
"RemoveFiles");
1568 r
= MSI_DatabaseOpenViewW(package
->db
, L
"SELECT * FROM `RemoveFile`", &view
);
1569 if (r
== ERROR_SUCCESS
)
1571 r
= MSI_IterateRecords(view
, NULL
, ITERATE_RemoveFiles
, package
);
1572 msiobj_release(&view
->hdr
);
1573 if (r
!= ERROR_SUCCESS
)
1577 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
1580 VS_FIXEDFILEINFO
*ver
;
1582 comp
= file
->Component
;
1583 msi_file_update_ui( package
, file
, L
"RemoveFiles" );
1585 comp
->Action
= msi_get_component_action( package
, comp
);
1586 if (comp
->Action
!= INSTALLSTATE_ABSENT
|| comp
->Installed
== INSTALLSTATE_SOURCE
)
1589 if (comp
->assembly
&& !comp
->assembly
->application
)
1592 if (comp
->Attributes
& msidbComponentAttributesPermanent
)
1594 TRACE("permanent component, not removing file\n");
1600 ver
= msi_get_disk_file_version( package
, file
->TargetPath
);
1601 if (ver
&& msi_compare_file_versions( ver
, file
->Version
) > 0)
1603 TRACE("newer version detected, not removing file\n");
1610 if (file
->state
== msifs_installed
)
1611 WARN("removing installed file %s\n", debugstr_w(file
->TargetPath
));
1613 TRACE("removing %s\n", debugstr_w(file
->File
) );
1615 msi_set_file_attributes( package
, file
->TargetPath
, FILE_ATTRIBUTE_NORMAL
);
1616 if (!msi_delete_file( package
, file
->TargetPath
))
1618 WARN( "failed to delete %s (%lu)\n", debugstr_w(file
->TargetPath
), GetLastError() );
1620 file
->state
= msifs_missing
;
1622 uirow
= MSI_CreateRecord( 9 );
1623 MSI_RecordSetStringW( uirow
, 1, file
->FileName
);
1624 MSI_RecordSetStringW( uirow
, 9, comp
->Directory
);
1625 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONDATA
, uirow
);
1626 msiobj_release( &uirow
->hdr
);
1629 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
1631 comp
->Action
= msi_get_component_action( package
, comp
);
1632 if (comp
->Action
!= INSTALLSTATE_ABSENT
) continue;
1634 if (comp
->Attributes
& msidbComponentAttributesPermanent
)
1636 TRACE("permanent component, not removing directory\n");
1639 if (comp
->assembly
&& !comp
->assembly
->application
)
1640 msi_uninstall_assembly( package
, comp
);
1643 MSIFOLDER
*folder
= msi_get_loaded_folder( package
, comp
->Directory
);
1644 if (folder
) remove_folder( folder
);
1647 return ERROR_SUCCESS
;