Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenfiles.c
Go to the documentation of this file.
00001 /* 00002 * Implementation of the Microsoft Installer (msi.dll) 00003 * 00004 * Copyright 2005 Aric Stewart for CodeWeavers 00005 * 00006 * This library is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU Lesser General Public 00008 * License as published by the Free Software Foundation; either 00009 * version 2.1 of the License, or (at your option) any later version. 00010 * 00011 * This library is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 * Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public 00017 * License along with this library; if not, write to the Free Software 00018 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 00019 */ 00020 00021 00022 /* 00023 * Actions dealing with files: 00024 * 00025 * InstallFiles 00026 * DuplicateFiles 00027 * MoveFiles 00028 * PatchFiles 00029 * RemoveDuplicateFiles 00030 * RemoveFiles 00031 */ 00032 00033 #include <stdarg.h> 00034 00035 #include "windef.h" 00036 #include "winbase.h" 00037 #include "winerror.h" 00038 #include "wine/debug.h" 00039 #include "fdi.h" 00040 #include "msi.h" 00041 #include "msidefs.h" 00042 #include "msipriv.h" 00043 #include "winuser.h" 00044 #include "winreg.h" 00045 #include "shlwapi.h" 00046 #include "wine/unicode.h" 00047 00048 WINE_DEFAULT_DEBUG_CHANNEL(msi); 00049 00050 static HMODULE hmspatcha; 00051 static BOOL (WINAPI *ApplyPatchToFileW)(LPCWSTR, LPCWSTR, LPCWSTR, ULONG); 00052 00053 static void msi_file_update_ui( MSIPACKAGE *package, MSIFILE *f, const WCHAR *action ) 00054 { 00055 MSIRECORD *uirow; 00056 00057 uirow = MSI_CreateRecord( 9 ); 00058 MSI_RecordSetStringW( uirow, 1, f->FileName ); 00059 MSI_RecordSetStringW( uirow, 9, f->Component->Directory ); 00060 MSI_RecordSetInteger( uirow, 6, f->FileSize ); 00061 msi_ui_actiondata( package, action, uirow ); 00062 msiobj_release( &uirow->hdr ); 00063 msi_ui_progress( package, 2, f->FileSize, 0, 0 ); 00064 } 00065 00066 static msi_file_state calculate_install_state( MSIPACKAGE *package, MSIFILE *file ) 00067 { 00068 MSICOMPONENT *comp = file->Component; 00069 VS_FIXEDFILEINFO *file_version; 00070 WCHAR *font_version; 00071 msi_file_state state; 00072 DWORD file_size; 00073 00074 comp->Action = msi_get_component_action( package, comp ); 00075 if (comp->Action != INSTALLSTATE_LOCAL || (comp->assembly && comp->assembly->installed)) 00076 { 00077 TRACE("file %s is not scheduled for install\n", debugstr_w(file->File)); 00078 return msifs_skipped; 00079 } 00080 if ((comp->assembly && !comp->assembly->application && !comp->assembly->installed) || 00081 GetFileAttributesW( file->TargetPath ) == INVALID_FILE_ATTRIBUTES) 00082 { 00083 TRACE("file %s is missing\n", debugstr_w(file->File)); 00084 return msifs_missing; 00085 } 00086 if (file->Version) 00087 { 00088 if ((file_version = msi_get_disk_file_version( file->TargetPath ))) 00089 { 00090 TRACE("new %s old %u.%u.%u.%u\n", debugstr_w(file->Version), 00091 HIWORD(file_version->dwFileVersionMS), 00092 LOWORD(file_version->dwFileVersionMS), 00093 HIWORD(file_version->dwFileVersionLS), 00094 LOWORD(file_version->dwFileVersionLS)); 00095 00096 if (msi_compare_file_versions( file_version, file->Version ) < 0) 00097 state = msifs_overwrite; 00098 else 00099 { 00100 TRACE("destination file version equal or greater, not overwriting\n"); 00101 state = msifs_present; 00102 } 00103 msi_free( file_version ); 00104 return state; 00105 } 00106 else if ((font_version = msi_font_version_from_file( file->TargetPath ))) 00107 { 00108 TRACE("new %s old %s\n", debugstr_w(file->Version), debugstr_w(font_version)); 00109 00110 if (msi_compare_font_versions( font_version, file->Version ) < 0) 00111 state = msifs_overwrite; 00112 else 00113 { 00114 TRACE("destination file version equal or greater, not overwriting\n"); 00115 state = msifs_present; 00116 } 00117 msi_free( font_version ); 00118 return state; 00119 } 00120 } 00121 if ((file_size = msi_get_disk_file_size( file->TargetPath )) != file->FileSize) 00122 { 00123 return msifs_overwrite; 00124 } 00125 if (file->hash.dwFileHashInfoSize) 00126 { 00127 if (msi_file_hash_matches( file )) 00128 { 00129 TRACE("file hashes match, not overwriting\n"); 00130 return msifs_hashmatch; 00131 } 00132 else 00133 { 00134 TRACE("file hashes do not match, overwriting\n"); 00135 return msifs_overwrite; 00136 } 00137 } 00138 /* assume present */ 00139 return msifs_present; 00140 } 00141 00142 static void schedule_install_files(MSIPACKAGE *package) 00143 { 00144 MSIFILE *file; 00145 00146 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry) 00147 { 00148 MSICOMPONENT *comp = file->Component; 00149 00150 file->state = calculate_install_state( package, file ); 00151 if (file->state == msifs_overwrite && (comp->Attributes & msidbComponentAttributesNeverOverwrite)) 00152 { 00153 TRACE("not overwriting %s\n", debugstr_w(file->TargetPath)); 00154 file->state = msifs_skipped; 00155 } 00156 } 00157 } 00158 00159 static UINT copy_file(MSIFILE *file, LPWSTR source) 00160 { 00161 BOOL ret; 00162 00163 ret = CopyFileW(source, file->TargetPath, FALSE); 00164 if (!ret) 00165 return GetLastError(); 00166 00167 SetFileAttributesW(file->TargetPath, FILE_ATTRIBUTE_NORMAL); 00168 00169 file->state = msifs_installed; 00170 return ERROR_SUCCESS; 00171 } 00172 00173 static UINT copy_install_file(MSIPACKAGE *package, MSIFILE *file, LPWSTR source) 00174 { 00175 UINT gle; 00176 00177 TRACE("Copying %s to %s\n", debugstr_w(source), debugstr_w(file->TargetPath)); 00178 00179 gle = copy_file(file, source); 00180 if (gle == ERROR_SUCCESS) 00181 return gle; 00182 00183 if (gle == ERROR_ALREADY_EXISTS && file->state == msifs_overwrite) 00184 { 00185 TRACE("overwriting existing file\n"); 00186 return ERROR_SUCCESS; 00187 } 00188 else if (gle == ERROR_ACCESS_DENIED) 00189 { 00190 SetFileAttributesW(file->TargetPath, FILE_ATTRIBUTE_NORMAL); 00191 00192 gle = copy_file(file, source); 00193 TRACE("Overwriting existing file: %d\n", gle); 00194 } 00195 if (gle == ERROR_SHARING_VIOLATION || gle == ERROR_USER_MAPPED_FILE) 00196 { 00197 WCHAR *tmpfileW, *pathW, *p; 00198 DWORD len; 00199 00200 TRACE("file in use, scheduling rename operation\n"); 00201 00202 if (!(pathW = strdupW( file->TargetPath ))) return ERROR_OUTOFMEMORY; 00203 if ((p = strrchrW(pathW, '\\'))) *p = 0; 00204 len = strlenW( pathW ) + 16; 00205 if (!(tmpfileW = msi_alloc(len * sizeof(WCHAR)))) 00206 { 00207 msi_free( pathW ); 00208 return ERROR_OUTOFMEMORY; 00209 } 00210 if (!GetTempFileNameW( pathW, szMsi, 0, tmpfileW )) tmpfileW[0] = 0; 00211 msi_free( pathW ); 00212 00213 if (CopyFileW(source, tmpfileW, FALSE) && 00214 MoveFileExW(file->TargetPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT) && 00215 MoveFileExW(tmpfileW, file->TargetPath, MOVEFILE_DELAY_UNTIL_REBOOT)) 00216 { 00217 file->state = msifs_installed; 00218 package->need_reboot_at_end = 1; 00219 gle = ERROR_SUCCESS; 00220 } 00221 else 00222 { 00223 gle = GetLastError(); 00224 WARN("failed to schedule rename operation: %d)\n", gle); 00225 DeleteFileW( tmpfileW ); 00226 } 00227 msi_free(tmpfileW); 00228 } 00229 00230 return gle; 00231 } 00232 00233 static UINT msi_create_directory( MSIPACKAGE *package, const WCHAR *dir ) 00234 { 00235 MSIFOLDER *folder; 00236 const WCHAR *install_path; 00237 00238 install_path = msi_get_target_folder( package, dir ); 00239 if (!install_path) return ERROR_FUNCTION_FAILED; 00240 00241 folder = msi_get_loaded_folder( package, dir ); 00242 if (folder->State == FOLDER_STATE_UNINITIALIZED) 00243 { 00244 msi_create_full_path( install_path ); 00245 folder->State = FOLDER_STATE_CREATED; 00246 } 00247 return ERROR_SUCCESS; 00248 } 00249 00250 static MSIFILE *find_file( MSIPACKAGE *package, const WCHAR *filename ) 00251 { 00252 MSIFILE *file; 00253 00254 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) 00255 { 00256 if (file->state != msifs_installed && !strcmpiW( filename, file->File )) return file; 00257 } 00258 return NULL; 00259 } 00260 00261 static BOOL installfiles_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action, 00262 LPWSTR *path, DWORD *attrs, PVOID user) 00263 { 00264 static MSIFILE *f = NULL; 00265 UINT_PTR disk_id = (UINT_PTR)user; 00266 00267 if (action == MSICABEXTRACT_BEGINEXTRACT) 00268 { 00269 if (!(f = find_file( package, file ))) 00270 { 00271 TRACE("unknown file in cabinet (%s)\n", debugstr_w(file)); 00272 return FALSE; 00273 } 00274 if (f->disk_id != disk_id || (f->state != msifs_missing && f->state != msifs_overwrite)) 00275 return FALSE; 00276 00277 if (!f->Component->assembly || f->Component->assembly->application) 00278 { 00279 msi_create_directory(package, f->Component->Directory); 00280 } 00281 *path = strdupW(f->TargetPath); 00282 *attrs = f->Attributes; 00283 } 00284 else if (action == MSICABEXTRACT_FILEEXTRACTED) 00285 { 00286 f->state = msifs_installed; 00287 f = NULL; 00288 } 00289 00290 return TRUE; 00291 } 00292 00293 WCHAR *msi_resolve_file_source( MSIPACKAGE *package, MSIFILE *file ) 00294 { 00295 WCHAR *p, *path; 00296 00297 TRACE("Working to resolve source of file %s\n", debugstr_w(file->File)); 00298 00299 if (file->IsCompressed) return NULL; 00300 00301 p = msi_resolve_source_folder( package, file->Component->Directory, NULL ); 00302 path = msi_build_directory_name( 2, p, file->ShortName ); 00303 00304 if (file->LongName && GetFileAttributesW( path ) == INVALID_FILE_ATTRIBUTES) 00305 { 00306 msi_free( path ); 00307 path = msi_build_directory_name( 2, p, file->LongName ); 00308 } 00309 msi_free( p ); 00310 TRACE("file %s source resolves to %s\n", debugstr_w(file->File), debugstr_w(path)); 00311 return path; 00312 } 00313 00314 /* 00315 * ACTION_InstallFiles() 00316 * 00317 * For efficiency, this is done in two passes: 00318 * 1) Correct all the TargetPaths and determine what files are to be installed. 00319 * 2) Extract Cabinets and copy files. 00320 */ 00321 UINT ACTION_InstallFiles(MSIPACKAGE *package) 00322 { 00323 MSIMEDIAINFO *mi; 00324 MSICOMPONENT *comp; 00325 UINT rc = ERROR_SUCCESS; 00326 MSIFILE *file; 00327 00328 schedule_install_files(package); 00329 mi = msi_alloc_zero( sizeof(MSIMEDIAINFO) ); 00330 00331 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) 00332 { 00333 msi_file_update_ui( package, file, szInstallFiles ); 00334 00335 rc = msi_load_media_info( package, file->Sequence, mi ); 00336 if (rc != ERROR_SUCCESS) 00337 { 00338 ERR("Unable to load media info for %s (%u)\n", debugstr_w(file->File), rc); 00339 return ERROR_FUNCTION_FAILED; 00340 } 00341 if (!file->Component->Enabled) continue; 00342 00343 if (file->state != msifs_hashmatch && 00344 file->state != msifs_skipped && 00345 (file->state != msifs_present || !msi_get_property_int( package->db, szInstalled, 0 )) && 00346 (rc = ready_media( package, file->IsCompressed, mi ))) 00347 { 00348 ERR("Failed to ready media for %s\n", debugstr_w(file->File)); 00349 goto done; 00350 } 00351 00352 if (file->state != msifs_missing && !mi->is_continuous && file->state != msifs_overwrite) 00353 continue; 00354 00355 if (file->Sequence > mi->last_sequence || mi->is_continuous || 00356 (file->IsCompressed && !mi->is_extracted)) 00357 { 00358 MSICABDATA data; 00359 00360 data.mi = mi; 00361 data.package = package; 00362 data.cb = installfiles_cb; 00363 data.user = (PVOID)(UINT_PTR)mi->disk_id; 00364 00365 if (file->IsCompressed && 00366 !msi_cabextract(package, mi, &data)) 00367 { 00368 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet)); 00369 rc = ERROR_INSTALL_FAILURE; 00370 goto done; 00371 } 00372 } 00373 00374 if (!file->IsCompressed) 00375 { 00376 WCHAR *source = msi_resolve_file_source(package, file); 00377 00378 TRACE("copying %s to %s\n", debugstr_w(source), debugstr_w(file->TargetPath)); 00379 00380 if (!file->Component->assembly || file->Component->assembly->application) 00381 { 00382 msi_create_directory(package, file->Component->Directory); 00383 } 00384 rc = copy_install_file(package, file, source); 00385 if (rc != ERROR_SUCCESS) 00386 { 00387 ERR("Failed to copy %s to %s (%d)\n", debugstr_w(source), 00388 debugstr_w(file->TargetPath), rc); 00389 rc = ERROR_INSTALL_FAILURE; 00390 msi_free(source); 00391 goto done; 00392 } 00393 msi_free(source); 00394 } 00395 else if (file->state != msifs_installed && !(file->Attributes & msidbFileAttributesPatchAdded)) 00396 { 00397 ERR("compressed file wasn't installed (%s)\n", debugstr_w(file->TargetPath)); 00398 rc = ERROR_INSTALL_FAILURE; 00399 goto done; 00400 } 00401 } 00402 msi_init_assembly_caches( package ); 00403 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry ) 00404 { 00405 comp->Action = msi_get_component_action( package, comp ); 00406 if (comp->Action == INSTALLSTATE_LOCAL && comp->assembly && !comp->assembly->installed) 00407 { 00408 rc = msi_install_assembly( package, comp ); 00409 if (rc != ERROR_SUCCESS) 00410 { 00411 ERR("Failed to install assembly\n"); 00412 rc = ERROR_INSTALL_FAILURE; 00413 break; 00414 } 00415 } 00416 } 00417 msi_destroy_assembly_caches( package ); 00418 00419 done: 00420 msi_free_media_info(mi); 00421 return rc; 00422 } 00423 00424 static BOOL load_mspatcha(void) 00425 { 00426 hmspatcha = LoadLibraryA("mspatcha.dll"); 00427 if (!hmspatcha) 00428 { 00429 ERR("Failed to load mspatcha.dll: %d\n", GetLastError()); 00430 return FALSE; 00431 } 00432 00433 ApplyPatchToFileW = (void*)GetProcAddress(hmspatcha, "ApplyPatchToFileW"); 00434 if(!ApplyPatchToFileW) 00435 { 00436 ERR("GetProcAddress(ApplyPatchToFileW) failed: %d.\n", GetLastError()); 00437 return FALSE; 00438 } 00439 00440 return TRUE; 00441 } 00442 00443 static void unload_mspatch(void) 00444 { 00445 FreeLibrary(hmspatcha); 00446 } 00447 00448 static BOOL patchfiles_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action, 00449 LPWSTR *path, DWORD *attrs, PVOID user) 00450 { 00451 static MSIFILEPATCH *p = NULL; 00452 static WCHAR patch_path[MAX_PATH] = {0}; 00453 static WCHAR temp_folder[MAX_PATH] = {0}; 00454 00455 if (action == MSICABEXTRACT_BEGINEXTRACT) 00456 { 00457 if (temp_folder[0] == '\0') 00458 GetTempPathW(MAX_PATH, temp_folder); 00459 00460 p = msi_get_loaded_filepatch(package, file); 00461 if (!p) 00462 { 00463 TRACE("unknown file in cabinet (%s)\n", debugstr_w(file)); 00464 return FALSE; 00465 } 00466 GetTempFileNameW(temp_folder, NULL, 0, patch_path); 00467 00468 *path = strdupW(patch_path); 00469 *attrs = p->File->Attributes; 00470 } 00471 else if (action == MSICABEXTRACT_FILEEXTRACTED) 00472 { 00473 WCHAR patched_file[MAX_PATH]; 00474 BOOL br; 00475 00476 GetTempFileNameW(temp_folder, NULL, 0, patched_file); 00477 00478 br = ApplyPatchToFileW(patch_path, p->File->TargetPath, patched_file, 0); 00479 if (br) 00480 { 00481 /* FIXME: baseline cache */ 00482 00483 DeleteFileW( p->File->TargetPath ); 00484 MoveFileW( patched_file, p->File->TargetPath ); 00485 00486 p->IsApplied = TRUE; 00487 } 00488 else 00489 ERR("Failed patch %s: %d.\n", debugstr_w(p->File->TargetPath), GetLastError()); 00490 00491 DeleteFileW(patch_path); 00492 p = NULL; 00493 } 00494 00495 return TRUE; 00496 } 00497 00498 UINT ACTION_PatchFiles( MSIPACKAGE *package ) 00499 { 00500 MSIFILEPATCH *patch; 00501 MSIMEDIAINFO *mi; 00502 UINT rc = ERROR_SUCCESS; 00503 BOOL mspatcha_loaded = FALSE; 00504 00505 TRACE("%p\n", package); 00506 00507 mi = msi_alloc_zero( sizeof(MSIMEDIAINFO) ); 00508 00509 LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry ) 00510 { 00511 MSIFILE *file = patch->File; 00512 MSICOMPONENT *comp = file->Component; 00513 00514 rc = msi_load_media_info( package, patch->Sequence, mi ); 00515 if (rc != ERROR_SUCCESS) 00516 { 00517 ERR("Unable to load media info for %s (%u)\n", debugstr_w(file->File), rc); 00518 return ERROR_FUNCTION_FAILED; 00519 } 00520 comp->Action = msi_get_component_action( package, comp ); 00521 if (!comp->Enabled || comp->Action != INSTALLSTATE_LOCAL) continue; 00522 00523 if (!patch->IsApplied) 00524 { 00525 MSICABDATA data; 00526 00527 rc = ready_media( package, TRUE, mi ); 00528 if (rc != ERROR_SUCCESS) 00529 { 00530 ERR("Failed to ready media for %s\n", debugstr_w(file->File)); 00531 goto done; 00532 } 00533 00534 if (!mspatcha_loaded && !load_mspatcha()) 00535 { 00536 rc = ERROR_FUNCTION_FAILED; 00537 goto done; 00538 } 00539 mspatcha_loaded = TRUE; 00540 00541 data.mi = mi; 00542 data.package = package; 00543 data.cb = patchfiles_cb; 00544 data.user = (PVOID)(UINT_PTR)mi->disk_id; 00545 00546 if (!msi_cabextract(package, mi, &data)) 00547 { 00548 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet)); 00549 rc = ERROR_INSTALL_FAILURE; 00550 goto done; 00551 } 00552 } 00553 00554 if (!patch->IsApplied && !(patch->Attributes & msidbPatchAttributesNonVital)) 00555 { 00556 ERR("Failed to apply patch to file: %s\n", debugstr_w(file->File)); 00557 rc = ERROR_INSTALL_FAILURE; 00558 goto done; 00559 } 00560 } 00561 00562 done: 00563 msi_free_media_info(mi); 00564 if (mspatcha_loaded) 00565 unload_mspatch(); 00566 return rc; 00567 } 00568 00569 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0)))) 00570 00571 typedef struct 00572 { 00573 struct list entry; 00574 LPWSTR sourcename; 00575 LPWSTR destname; 00576 LPWSTR source; 00577 LPWSTR dest; 00578 } FILE_LIST; 00579 00580 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options) 00581 { 00582 BOOL ret; 00583 00584 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY || 00585 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY) 00586 { 00587 WARN("Source or dest is directory, not moving\n"); 00588 return FALSE; 00589 } 00590 00591 if (options == msidbMoveFileOptionsMove) 00592 { 00593 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest)); 00594 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING); 00595 if (!ret) 00596 { 00597 WARN("MoveFile failed: %d\n", GetLastError()); 00598 return FALSE; 00599 } 00600 } 00601 else 00602 { 00603 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest)); 00604 ret = CopyFileW(source, dest, FALSE); 00605 if (!ret) 00606 { 00607 WARN("CopyFile failed: %d\n", GetLastError()); 00608 return FALSE; 00609 } 00610 } 00611 00612 return TRUE; 00613 } 00614 00615 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename) 00616 { 00617 LPWSTR path, ptr; 00618 DWORD dirlen, pathlen; 00619 00620 ptr = strrchrW(wildcard, '\\'); 00621 dirlen = ptr - wildcard + 1; 00622 00623 pathlen = dirlen + lstrlenW(filename) + 1; 00624 path = msi_alloc(pathlen * sizeof(WCHAR)); 00625 00626 lstrcpynW(path, wildcard, dirlen + 1); 00627 lstrcatW(path, filename); 00628 00629 return path; 00630 } 00631 00632 static void free_file_entry(FILE_LIST *file) 00633 { 00634 msi_free(file->source); 00635 msi_free(file->dest); 00636 msi_free(file); 00637 } 00638 00639 static void free_list(FILE_LIST *list) 00640 { 00641 while (!list_empty(&list->entry)) 00642 { 00643 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry); 00644 00645 list_remove(&file->entry); 00646 free_file_entry(file); 00647 } 00648 } 00649 00650 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest) 00651 { 00652 FILE_LIST *new, *file; 00653 LPWSTR ptr, filename; 00654 DWORD size; 00655 00656 new = msi_alloc_zero(sizeof(FILE_LIST)); 00657 if (!new) 00658 return FALSE; 00659 00660 new->source = strdupW(source); 00661 ptr = strrchrW(dest, '\\') + 1; 00662 filename = strrchrW(new->source, '\\') + 1; 00663 00664 new->sourcename = filename; 00665 00666 if (*ptr) 00667 new->destname = ptr; 00668 else 00669 new->destname = new->sourcename; 00670 00671 size = (ptr - dest) + lstrlenW(filename) + 1; 00672 new->dest = msi_alloc(size * sizeof(WCHAR)); 00673 if (!new->dest) 00674 { 00675 free_file_entry(new); 00676 return FALSE; 00677 } 00678 00679 lstrcpynW(new->dest, dest, ptr - dest + 1); 00680 lstrcatW(new->dest, filename); 00681 00682 if (list_empty(&files->entry)) 00683 { 00684 list_add_head(&files->entry, &new->entry); 00685 return TRUE; 00686 } 00687 00688 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry) 00689 { 00690 if (strcmpW( source, file->source ) < 0) 00691 { 00692 list_add_before(&file->entry, &new->entry); 00693 return TRUE; 00694 } 00695 } 00696 00697 list_add_after(&file->entry, &new->entry); 00698 return TRUE; 00699 } 00700 00701 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options) 00702 { 00703 WIN32_FIND_DATAW wfd; 00704 HANDLE hfile; 00705 LPWSTR path; 00706 BOOL res; 00707 FILE_LIST files, *file; 00708 DWORD size; 00709 00710 hfile = FindFirstFileW(source, &wfd); 00711 if (hfile == INVALID_HANDLE_VALUE) return FALSE; 00712 00713 list_init(&files.entry); 00714 00715 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd)) 00716 { 00717 if (is_dot_dir(wfd.cFileName)) continue; 00718 00719 path = wildcard_to_file(source, wfd.cFileName); 00720 if (!path) 00721 { 00722 res = FALSE; 00723 goto done; 00724 } 00725 00726 add_wildcard(&files, path, dest); 00727 msi_free(path); 00728 } 00729 00730 /* no files match the wildcard */ 00731 if (list_empty(&files.entry)) 00732 goto done; 00733 00734 /* only the first wildcard match gets renamed to dest */ 00735 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry); 00736 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2; 00737 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR)); 00738 if (!file->dest) 00739 { 00740 res = FALSE; 00741 goto done; 00742 } 00743 00744 /* file->dest may be shorter after the reallocation, so add a NULL 00745 * terminator. This is needed for the call to strrchrW, as there will no 00746 * longer be a NULL terminator within the bounds of the allocation in this case. 00747 */ 00748 file->dest[size - 1] = '\0'; 00749 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname); 00750 00751 while (!list_empty(&files.entry)) 00752 { 00753 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry); 00754 00755 msi_move_file(file->source, file->dest, options); 00756 00757 list_remove(&file->entry); 00758 free_file_entry(file); 00759 } 00760 00761 res = TRUE; 00762 00763 done: 00764 free_list(&files); 00765 FindClose(hfile); 00766 return res; 00767 } 00768 00769 void msi_reduce_to_long_filename( WCHAR *filename ) 00770 { 00771 WCHAR *p = strchrW( filename, '|' ); 00772 if (p) memmove( filename, p + 1, (strlenW( p + 1 ) + 1) * sizeof(WCHAR) ); 00773 } 00774 00775 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param ) 00776 { 00777 MSIPACKAGE *package = param; 00778 MSIRECORD *uirow; 00779 MSICOMPONENT *comp; 00780 LPCWSTR sourcename, component; 00781 LPWSTR sourcedir, destname = NULL, destdir = NULL, source = NULL, dest = NULL; 00782 int options; 00783 DWORD size; 00784 BOOL ret, wildcards; 00785 00786 component = MSI_RecordGetString(rec, 2); 00787 comp = msi_get_loaded_component(package, component); 00788 if (!comp) 00789 return ERROR_SUCCESS; 00790 00791 comp->Action = msi_get_component_action( package, comp ); 00792 if (comp->Action != INSTALLSTATE_LOCAL) 00793 { 00794 TRACE("component not scheduled for installation %s\n", debugstr_w(component)); 00795 return ERROR_SUCCESS; 00796 } 00797 00798 sourcename = MSI_RecordGetString(rec, 3); 00799 options = MSI_RecordGetInteger(rec, 7); 00800 00801 sourcedir = msi_dup_property(package->db, MSI_RecordGetString(rec, 5)); 00802 if (!sourcedir) 00803 goto done; 00804 00805 destdir = msi_dup_property(package->db, MSI_RecordGetString(rec, 6)); 00806 if (!destdir) 00807 goto done; 00808 00809 if (!sourcename) 00810 { 00811 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES) 00812 goto done; 00813 00814 source = strdupW(sourcedir); 00815 if (!source) 00816 goto done; 00817 } 00818 else 00819 { 00820 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2; 00821 source = msi_alloc(size * sizeof(WCHAR)); 00822 if (!source) 00823 goto done; 00824 00825 lstrcpyW(source, sourcedir); 00826 if (source[lstrlenW(source) - 1] != '\\') 00827 lstrcatW(source, szBackSlash); 00828 lstrcatW(source, sourcename); 00829 } 00830 00831 wildcards = strchrW(source, '*') || strchrW(source, '?'); 00832 00833 if (MSI_RecordIsNull(rec, 4)) 00834 { 00835 if (!wildcards) 00836 { 00837 destname = strdupW(sourcename); 00838 if (!destname) 00839 goto done; 00840 } 00841 } 00842 else 00843 { 00844 destname = strdupW(MSI_RecordGetString(rec, 4)); 00845 if (destname) msi_reduce_to_long_filename(destname); 00846 } 00847 00848 size = 0; 00849 if (destname) 00850 size = lstrlenW(destname); 00851 00852 size += lstrlenW(destdir) + 2; 00853 dest = msi_alloc(size * sizeof(WCHAR)); 00854 if (!dest) 00855 goto done; 00856 00857 lstrcpyW(dest, destdir); 00858 if (dest[lstrlenW(dest) - 1] != '\\') 00859 lstrcatW(dest, szBackSlash); 00860 00861 if (destname) 00862 lstrcatW(dest, destname); 00863 00864 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES) 00865 { 00866 if (!(ret = msi_create_full_path(destdir))) 00867 { 00868 WARN("failed to create directory %u\n", GetLastError()); 00869 goto done; 00870 } 00871 } 00872 00873 if (!wildcards) 00874 msi_move_file(source, dest, options); 00875 else 00876 move_files_wildcard(source, dest, options); 00877 00878 done: 00879 uirow = MSI_CreateRecord( 9 ); 00880 MSI_RecordSetStringW( uirow, 1, MSI_RecordGetString(rec, 1) ); 00881 MSI_RecordSetInteger( uirow, 6, 1 ); /* FIXME */ 00882 MSI_RecordSetStringW( uirow, 9, destdir ); 00883 msi_ui_actiondata( package, szMoveFiles, uirow ); 00884 msiobj_release( &uirow->hdr ); 00885 00886 msi_free(sourcedir); 00887 msi_free(destdir); 00888 msi_free(destname); 00889 msi_free(source); 00890 msi_free(dest); 00891 00892 return ERROR_SUCCESS; 00893 } 00894 00895 UINT ACTION_MoveFiles( MSIPACKAGE *package ) 00896 { 00897 static const WCHAR query[] = { 00898 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', 00899 '`','M','o','v','e','F','i','l','e','`',0}; 00900 MSIQUERY *view; 00901 UINT rc; 00902 00903 rc = MSI_DatabaseOpenViewW(package->db, query, &view); 00904 if (rc != ERROR_SUCCESS) 00905 return ERROR_SUCCESS; 00906 00907 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package); 00908 msiobj_release(&view->hdr); 00909 return rc; 00910 } 00911 00912 static WCHAR *get_duplicate_filename( MSIPACKAGE *package, MSIRECORD *row, const WCHAR *file_key, const WCHAR *src ) 00913 { 00914 DWORD len; 00915 WCHAR *dst_name, *dst_path, *dst; 00916 00917 if (MSI_RecordIsNull( row, 4 )) 00918 { 00919 len = strlenW( src ) + 1; 00920 if (!(dst_name = msi_alloc( len * sizeof(WCHAR)))) return NULL; 00921 strcpyW( dst_name, strrchrW( src, '\\' ) + 1 ); 00922 } 00923 else 00924 { 00925 MSI_RecordGetStringW( row, 4, NULL, &len ); 00926 if (!(dst_name = msi_alloc( ++len * sizeof(WCHAR) ))) return NULL; 00927 MSI_RecordGetStringW( row, 4, dst_name, &len ); 00928 msi_reduce_to_long_filename( dst_name ); 00929 } 00930 00931 if (MSI_RecordIsNull( row, 5 )) 00932 { 00933 WCHAR *p; 00934 dst_path = strdupW( src ); 00935 p = strrchrW( dst_path, '\\' ); 00936 if (p) *p = 0; 00937 } 00938 else 00939 { 00940 const WCHAR *dst_key = MSI_RecordGetString( row, 5 ); 00941 00942 dst_path = strdupW( msi_get_target_folder( package, dst_key ) ); 00943 if (!dst_path) 00944 { 00945 /* try a property */ 00946 dst_path = msi_dup_property( package->db, dst_key ); 00947 if (!dst_path) 00948 { 00949 FIXME("Unable to get destination folder, try AppSearch properties\n"); 00950 msi_free( dst_name ); 00951 return NULL; 00952 } 00953 } 00954 } 00955 00956 dst = msi_build_directory_name( 2, dst_path, dst_name ); 00957 msi_create_full_path( dst_path ); 00958 00959 msi_free( dst_name ); 00960 msi_free( dst_path ); 00961 return dst; 00962 } 00963 00964 static UINT ITERATE_DuplicateFiles(MSIRECORD *row, LPVOID param) 00965 { 00966 MSIPACKAGE *package = param; 00967 LPWSTR dest; 00968 LPCWSTR file_key, component; 00969 MSICOMPONENT *comp; 00970 MSIRECORD *uirow; 00971 MSIFILE *file; 00972 00973 component = MSI_RecordGetString(row,2); 00974 comp = msi_get_loaded_component(package, component); 00975 if (!comp) 00976 return ERROR_SUCCESS; 00977 00978 comp->Action = msi_get_component_action( package, comp ); 00979 if (comp->Action != INSTALLSTATE_LOCAL) 00980 { 00981 TRACE("component not scheduled for installation %s\n", debugstr_w(component)); 00982 return ERROR_SUCCESS; 00983 } 00984 00985 file_key = MSI_RecordGetString(row,3); 00986 if (!file_key) 00987 { 00988 ERR("Unable to get file key\n"); 00989 return ERROR_FUNCTION_FAILED; 00990 } 00991 00992 file = msi_get_loaded_file( package, file_key ); 00993 if (!file) 00994 { 00995 ERR("Original file unknown %s\n", debugstr_w(file_key)); 00996 return ERROR_SUCCESS; 00997 } 00998 00999 dest = get_duplicate_filename( package, row, file_key, file->TargetPath ); 01000 if (!dest) 01001 { 01002 WARN("Unable to get duplicate filename\n"); 01003 return ERROR_SUCCESS; 01004 } 01005 01006 TRACE("Duplicating file %s to %s\n", debugstr_w(file->TargetPath), debugstr_w(dest)); 01007 01008 if (!CopyFileW( file->TargetPath, dest, TRUE )) 01009 { 01010 WARN("Failed to copy file %s -> %s (%u)\n", 01011 debugstr_w(file->TargetPath), debugstr_w(dest), GetLastError()); 01012 } 01013 01014 FIXME("We should track these duplicate files as well\n"); 01015 01016 uirow = MSI_CreateRecord( 9 ); 01017 MSI_RecordSetStringW( uirow, 1, MSI_RecordGetString( row, 1 ) ); 01018 MSI_RecordSetInteger( uirow, 6, file->FileSize ); 01019 MSI_RecordSetStringW( uirow, 9, MSI_RecordGetString( row, 5 ) ); 01020 msi_ui_actiondata( package, szDuplicateFiles, uirow ); 01021 msiobj_release( &uirow->hdr ); 01022 01023 msi_free(dest); 01024 return ERROR_SUCCESS; 01025 } 01026 01027 UINT ACTION_DuplicateFiles(MSIPACKAGE *package) 01028 { 01029 static const WCHAR query[] = { 01030 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', 01031 '`','D','u','p','l','i','c','a','t','e','F','i','l','e','`',0}; 01032 MSIQUERY *view; 01033 UINT rc; 01034 01035 rc = MSI_DatabaseOpenViewW(package->db, query, &view); 01036 if (rc != ERROR_SUCCESS) 01037 return ERROR_SUCCESS; 01038 01039 rc = MSI_IterateRecords(view, NULL, ITERATE_DuplicateFiles, package); 01040 msiobj_release(&view->hdr); 01041 return rc; 01042 } 01043 01044 static UINT ITERATE_RemoveDuplicateFiles( MSIRECORD *row, LPVOID param ) 01045 { 01046 MSIPACKAGE *package = param; 01047 LPWSTR dest; 01048 LPCWSTR file_key, component; 01049 MSICOMPONENT *comp; 01050 MSIRECORD *uirow; 01051 MSIFILE *file; 01052 01053 component = MSI_RecordGetString( row, 2 ); 01054 comp = msi_get_loaded_component( package, component ); 01055 if (!comp) 01056 return ERROR_SUCCESS; 01057 01058 comp->Action = msi_get_component_action( package, comp ); 01059 if (comp->Action != INSTALLSTATE_ABSENT) 01060 { 01061 TRACE("component not scheduled for removal %s\n", debugstr_w(component)); 01062 return ERROR_SUCCESS; 01063 } 01064 01065 file_key = MSI_RecordGetString( row, 3 ); 01066 if (!file_key) 01067 { 01068 ERR("Unable to get file key\n"); 01069 return ERROR_FUNCTION_FAILED; 01070 } 01071 01072 file = msi_get_loaded_file( package, file_key ); 01073 if (!file) 01074 { 01075 ERR("Original file unknown %s\n", debugstr_w(file_key)); 01076 return ERROR_SUCCESS; 01077 } 01078 01079 dest = get_duplicate_filename( package, row, file_key, file->TargetPath ); 01080 if (!dest) 01081 { 01082 WARN("Unable to get duplicate filename\n"); 01083 return ERROR_SUCCESS; 01084 } 01085 01086 TRACE("Removing duplicate %s of %s\n", debugstr_w(dest), debugstr_w(file->TargetPath)); 01087 01088 if (!DeleteFileW( dest )) 01089 { 01090 WARN("Failed to delete duplicate file %s (%u)\n", debugstr_w(dest), GetLastError()); 01091 } 01092 01093 uirow = MSI_CreateRecord( 9 ); 01094 MSI_RecordSetStringW( uirow, 1, MSI_RecordGetString( row, 1 ) ); 01095 MSI_RecordSetStringW( uirow, 9, MSI_RecordGetString( row, 5 ) ); 01096 msi_ui_actiondata( package, szRemoveDuplicateFiles, uirow ); 01097 msiobj_release( &uirow->hdr ); 01098 01099 msi_free(dest); 01100 return ERROR_SUCCESS; 01101 } 01102 01103 UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package ) 01104 { 01105 static const WCHAR query[] = { 01106 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', 01107 '`','D','u','p','l','i','c','a','t','e','F','i','l','e','`',0}; 01108 MSIQUERY *view; 01109 UINT rc; 01110 01111 rc = MSI_DatabaseOpenViewW( package->db, query, &view ); 01112 if (rc != ERROR_SUCCESS) 01113 return ERROR_SUCCESS; 01114 01115 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveDuplicateFiles, package ); 01116 msiobj_release( &view->hdr ); 01117 return rc; 01118 } 01119 01120 static BOOL verify_comp_for_removal(MSICOMPONENT *comp, UINT install_mode) 01121 { 01122 /* special case */ 01123 if (comp->Action != INSTALLSTATE_SOURCE && 01124 comp->Attributes & msidbComponentAttributesSourceOnly && 01125 (install_mode == msidbRemoveFileInstallModeOnRemove || 01126 install_mode == msidbRemoveFileInstallModeOnBoth)) return TRUE; 01127 01128 switch (comp->Action) 01129 { 01130 case INSTALLSTATE_LOCAL: 01131 case INSTALLSTATE_SOURCE: 01132 if (install_mode == msidbRemoveFileInstallModeOnInstall || 01133 install_mode == msidbRemoveFileInstallModeOnBoth) return TRUE; 01134 break; 01135 case INSTALLSTATE_ABSENT: 01136 if (install_mode == msidbRemoveFileInstallModeOnRemove || 01137 install_mode == msidbRemoveFileInstallModeOnBoth) return TRUE; 01138 break; 01139 default: break; 01140 } 01141 return FALSE; 01142 } 01143 01144 static UINT ITERATE_RemoveFiles(MSIRECORD *row, LPVOID param) 01145 { 01146 MSIPACKAGE *package = param; 01147 MSICOMPONENT *comp; 01148 MSIRECORD *uirow; 01149 LPCWSTR component, dirprop; 01150 UINT install_mode; 01151 LPWSTR dir = NULL, path = NULL, filename = NULL; 01152 DWORD size; 01153 UINT ret = ERROR_SUCCESS; 01154 01155 component = MSI_RecordGetString(row, 2); 01156 dirprop = MSI_RecordGetString(row, 4); 01157 install_mode = MSI_RecordGetInteger(row, 5); 01158 01159 comp = msi_get_loaded_component(package, component); 01160 if (!comp) 01161 return ERROR_SUCCESS; 01162 01163 comp->Action = msi_get_component_action( package, comp ); 01164 if (!verify_comp_for_removal(comp, install_mode)) 01165 { 01166 TRACE("Skipping removal due to install mode\n"); 01167 return ERROR_SUCCESS; 01168 } 01169 if (comp->assembly && !comp->assembly->application) 01170 { 01171 return ERROR_SUCCESS; 01172 } 01173 if (comp->Attributes & msidbComponentAttributesPermanent) 01174 { 01175 TRACE("permanent component, not removing file\n"); 01176 return ERROR_SUCCESS; 01177 } 01178 01179 dir = msi_dup_property(package->db, dirprop); 01180 if (!dir) 01181 { 01182 WARN("directory property has no value\n"); 01183 return ERROR_SUCCESS; 01184 } 01185 size = 0; 01186 if ((filename = strdupW( MSI_RecordGetString(row, 3) ))) 01187 { 01188 msi_reduce_to_long_filename( filename ); 01189 size = lstrlenW( filename ); 01190 } 01191 size += lstrlenW(dir) + 2; 01192 path = msi_alloc(size * sizeof(WCHAR)); 01193 if (!path) 01194 { 01195 ret = ERROR_OUTOFMEMORY; 01196 goto done; 01197 } 01198 01199 if (filename) 01200 { 01201 lstrcpyW(path, dir); 01202 PathAddBackslashW(path); 01203 lstrcatW(path, filename); 01204 01205 TRACE("Deleting misc file: %s\n", debugstr_w(path)); 01206 DeleteFileW(path); 01207 } 01208 else 01209 { 01210 TRACE("Removing misc directory: %s\n", debugstr_w(dir)); 01211 RemoveDirectoryW(dir); 01212 } 01213 01214 done: 01215 uirow = MSI_CreateRecord( 9 ); 01216 MSI_RecordSetStringW( uirow, 1, MSI_RecordGetString(row, 1) ); 01217 MSI_RecordSetStringW( uirow, 9, dir ); 01218 msi_ui_actiondata( package, szRemoveFiles, uirow ); 01219 msiobj_release( &uirow->hdr ); 01220 01221 msi_free(filename); 01222 msi_free(path); 01223 msi_free(dir); 01224 return ret; 01225 } 01226 01227 static void remove_folder( MSIFOLDER *folder ) 01228 { 01229 FolderList *fl; 01230 01231 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry ) 01232 { 01233 remove_folder( fl->folder ); 01234 } 01235 if (!folder->persistent && folder->State != FOLDER_STATE_REMOVED) 01236 { 01237 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED; 01238 } 01239 } 01240 01241 UINT ACTION_RemoveFiles( MSIPACKAGE *package ) 01242 { 01243 static const WCHAR query[] = { 01244 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', 01245 '`','R','e','m','o','v','e','F','i','l','e','`',0}; 01246 MSIQUERY *view; 01247 MSICOMPONENT *comp; 01248 MSIFILE *file; 01249 UINT r; 01250 01251 r = MSI_DatabaseOpenViewW(package->db, query, &view); 01252 if (r == ERROR_SUCCESS) 01253 { 01254 r = MSI_IterateRecords(view, NULL, ITERATE_RemoveFiles, package); 01255 msiobj_release(&view->hdr); 01256 if (r != ERROR_SUCCESS) 01257 return r; 01258 } 01259 01260 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) 01261 { 01262 MSIRECORD *uirow; 01263 VS_FIXEDFILEINFO *ver; 01264 01265 comp = file->Component; 01266 msi_file_update_ui( package, file, szRemoveFiles ); 01267 01268 comp->Action = msi_get_component_action( package, comp ); 01269 if (comp->Action != INSTALLSTATE_ABSENT || comp->Installed == INSTALLSTATE_SOURCE) 01270 continue; 01271 01272 if (comp->assembly && !comp->assembly->application) 01273 continue; 01274 01275 if (comp->Attributes & msidbComponentAttributesPermanent) 01276 { 01277 TRACE("permanent component, not removing file\n"); 01278 continue; 01279 } 01280 01281 if (file->Version) 01282 { 01283 ver = msi_get_disk_file_version( file->TargetPath ); 01284 if (ver && msi_compare_file_versions( ver, file->Version ) > 0) 01285 { 01286 TRACE("newer version detected, not removing file\n"); 01287 msi_free( ver ); 01288 continue; 01289 } 01290 msi_free( ver ); 01291 } 01292 01293 if (file->state == msifs_installed) 01294 WARN("removing installed file %s\n", debugstr_w(file->TargetPath)); 01295 01296 TRACE("removing %s\n", debugstr_w(file->File) ); 01297 01298 SetFileAttributesW( file->TargetPath, FILE_ATTRIBUTE_NORMAL ); 01299 if (!DeleteFileW( file->TargetPath )) 01300 { 01301 WARN("failed to delete %s (%u)\n", debugstr_w(file->TargetPath), GetLastError()); 01302 } 01303 file->state = msifs_missing; 01304 01305 uirow = MSI_CreateRecord( 9 ); 01306 MSI_RecordSetStringW( uirow, 1, file->FileName ); 01307 MSI_RecordSetStringW( uirow, 9, comp->Directory ); 01308 msi_ui_actiondata( package, szRemoveFiles, uirow ); 01309 msiobj_release( &uirow->hdr ); 01310 } 01311 01312 msi_init_assembly_caches( package ); 01313 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry ) 01314 { 01315 comp->Action = msi_get_component_action( package, comp ); 01316 if (comp->Action != INSTALLSTATE_ABSENT) continue; 01317 01318 if (comp->Attributes & msidbComponentAttributesPermanent) 01319 { 01320 TRACE("permanent component, not removing directory\n"); 01321 continue; 01322 } 01323 if (comp->assembly && !comp->assembly->application) 01324 msi_uninstall_assembly( package, comp ); 01325 else 01326 { 01327 MSIFOLDER *folder = msi_get_loaded_folder( package, comp->Directory ); 01328 remove_folder( folder ); 01329 } 01330 } 01331 msi_destroy_assembly_caches( package ); 01332 return ERROR_SUCCESS; 01333 } Generated on Fri May 25 2012 04:20:46 for ReactOS by
1.7.6.1
|