1 /* 2 * Implementation of the Microsoft Installer (msi.dll) 3 * 4 * Copyright 2010 Hans Leidekker for CodeWeavers 5 * 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. 10 * 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. 15 * 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 19 */ 20 21 #include <stdarg.h> 22 23 #define COBJMACROS 24 25 #include "windef.h" 26 #include "winbase.h" 27 #include "winreg.h" 28 #include "wine/debug.h" 29 #include "msipriv.h" 30 31 WINE_DEFAULT_DEBUG_CHANNEL(msi); 32 33 static void load_fusion_dlls( MSIPACKAGE *package ) 34 { 35 HRESULT (WINAPI *pLoadLibraryShim)( const WCHAR *, const WCHAR *, void *, HMODULE * ); 36 WCHAR path[MAX_PATH]; 37 DWORD len = GetSystemDirectoryW( path, MAX_PATH ); 38 39 lstrcpyW( path + len, L"\\mscoree.dll" ); 40 if (!package->hmscoree && !(package->hmscoree = LoadLibraryW( path ))) return; 41 if (!(pLoadLibraryShim = (void *)GetProcAddress( package->hmscoree, "LoadLibraryShim" ))) 42 { 43 FreeLibrary( package->hmscoree ); 44 package->hmscoree = NULL; 45 return; 46 } 47 48 if (!package->hfusion10) pLoadLibraryShim( L"fusion.dll", L"v1.0.3705", NULL, &package->hfusion10 ); 49 if (!package->hfusion11) pLoadLibraryShim( L"fusion.dll", L"v1.1.4322", NULL, &package->hfusion11 ); 50 if (!package->hfusion20) pLoadLibraryShim( L"fusion.dll", L"v2.0.50727", NULL, &package->hfusion20 ); 51 if (!package->hfusion40) pLoadLibraryShim( L"fusion.dll", L"v4.0.30319", NULL, &package->hfusion40 ); 52 } 53 54 static BOOL init_assembly_caches( MSIPACKAGE *package ) 55 { 56 HRESULT (WINAPI *pCreateAssemblyCache)( IAssemblyCache **, DWORD ); 57 58 if (!package->cache_sxs && CreateAssemblyCache( &package->cache_sxs, 0 ) != S_OK) return FALSE; 59 60 load_fusion_dlls( package ); 61 package->pGetFileVersion = (void *)GetProcAddress( package->hmscoree, "GetFileVersion" ); /* missing from v1.0.3705 */ 62 63 if (package->hfusion10 && !package->cache_net[CLR_VERSION_V10]) 64 { 65 pCreateAssemblyCache = (void *)GetProcAddress( package->hfusion10, "CreateAssemblyCache" ); 66 pCreateAssemblyCache( &package->cache_net[CLR_VERSION_V10], 0 ); 67 } 68 if (package->hfusion11 && !package->cache_net[CLR_VERSION_V11]) 69 { 70 pCreateAssemblyCache = (void *)GetProcAddress( package->hfusion11, "CreateAssemblyCache" ); 71 pCreateAssemblyCache( &package->cache_net[CLR_VERSION_V11], 0 ); 72 } 73 if (package->hfusion20 && !package->cache_net[CLR_VERSION_V20]) 74 { 75 pCreateAssemblyCache = (void *)GetProcAddress( package->hfusion20, "CreateAssemblyCache" ); 76 pCreateAssemblyCache( &package->cache_net[CLR_VERSION_V20], 0 ); 77 } 78 if (package->hfusion40 && !package->cache_net[CLR_VERSION_V40]) 79 { 80 pCreateAssemblyCache = (void *)GetProcAddress( package->hfusion40, "CreateAssemblyCache" ); 81 pCreateAssemblyCache( &package->cache_net[CLR_VERSION_V40], 0 ); 82 package->pCreateAssemblyNameObject = (void *)GetProcAddress( package->hfusion40, "CreateAssemblyNameObject" ); 83 package->pCreateAssemblyEnum = (void *)GetProcAddress( package->hfusion40, "CreateAssemblyEnum" ); 84 } 85 86 return TRUE; 87 } 88 89 void msi_destroy_assembly_caches( MSIPACKAGE *package ) 90 { 91 UINT i; 92 93 if (package->cache_sxs) 94 { 95 IAssemblyCache_Release( package->cache_sxs ); 96 package->cache_sxs = NULL; 97 } 98 for (i = 0; i < CLR_VERSION_MAX; i++) 99 { 100 if (package->cache_net[i]) 101 { 102 IAssemblyCache_Release( package->cache_net[i] ); 103 package->cache_net[i] = NULL; 104 } 105 } 106 package->pGetFileVersion = NULL; 107 package->pCreateAssemblyNameObject = NULL; 108 package->pCreateAssemblyEnum = NULL; 109 FreeLibrary( package->hfusion10 ); 110 FreeLibrary( package->hfusion11 ); 111 FreeLibrary( package->hfusion20 ); 112 FreeLibrary( package->hfusion40 ); 113 FreeLibrary( package->hmscoree ); 114 package->hfusion10 = NULL; 115 package->hfusion11 = NULL; 116 package->hfusion20 = NULL; 117 package->hfusion40 = NULL; 118 package->hmscoree = NULL; 119 } 120 121 static MSIRECORD *get_assembly_record( MSIPACKAGE *package, const WCHAR *comp ) 122 { 123 MSIQUERY *view; 124 MSIRECORD *rec; 125 UINT r; 126 127 r = MSI_OpenQuery( package->db, &view, L"SELECT * FROM `MsiAssembly` WHERE `Component_` = '%s'", comp ); 128 if (r != ERROR_SUCCESS) 129 return NULL; 130 131 r = MSI_ViewExecute( view, NULL ); 132 if (r != ERROR_SUCCESS) 133 { 134 msiobj_release( &view->hdr ); 135 return NULL; 136 } 137 r = MSI_ViewFetch( view, &rec ); 138 if (r != ERROR_SUCCESS) 139 { 140 msiobj_release( &view->hdr ); 141 return NULL; 142 } 143 if (!MSI_RecordGetString( rec, 4 )) 144 TRACE("component is a global assembly\n"); 145 146 msiobj_release( &view->hdr ); 147 return rec; 148 } 149 150 struct assembly_name 151 { 152 DWORD count; 153 UINT index; 154 WCHAR **attrs; 155 }; 156 157 static UINT get_assembly_name_attribute( MSIRECORD *rec, LPVOID param ) 158 { 159 struct assembly_name *name = param; 160 const WCHAR *attr = MSI_RecordGetString( rec, 2 ); 161 const WCHAR *value = MSI_RecordGetString( rec, 3 ); 162 int len = lstrlenW( L"%s=\"%s\"" ) + lstrlenW( attr ) + lstrlenW( value ); 163 164 if (!(name->attrs[name->index] = malloc( len * sizeof(WCHAR) ))) 165 return ERROR_OUTOFMEMORY; 166 167 if (!wcsicmp( attr, L"name" )) lstrcpyW( name->attrs[name->index++], value ); 168 else swprintf( name->attrs[name->index++], len, L"%s=\"%s\"", attr, value ); 169 return ERROR_SUCCESS; 170 } 171 172 static WCHAR *get_assembly_display_name( MSIDATABASE *db, const WCHAR *comp, MSIASSEMBLY *assembly ) 173 { 174 struct assembly_name name; 175 WCHAR *display_name = NULL; 176 MSIQUERY *view; 177 UINT i, r; 178 int len; 179 180 r = MSI_OpenQuery( db, &view, L"SELECT * FROM `MsiAssemblyName` WHERE `Component_` = '%s'", comp ); 181 if (r != ERROR_SUCCESS) 182 return NULL; 183 184 name.count = 0; 185 name.index = 0; 186 name.attrs = NULL; 187 MSI_IterateRecords( view, &name.count, NULL, NULL ); 188 if (!name.count) goto done; 189 190 name.attrs = malloc( name.count * sizeof(WCHAR *) ); 191 if (!name.attrs) goto done; 192 193 MSI_IterateRecords( view, NULL, get_assembly_name_attribute, &name ); 194 195 len = 0; 196 for (i = 0; i < name.count; i++) len += lstrlenW( name.attrs[i] ) + 1; 197 198 display_name = malloc( (len + 1) * sizeof(WCHAR) ); 199 if (display_name) 200 { 201 display_name[0] = 0; 202 for (i = 0; i < name.count; i++) 203 { 204 lstrcatW( display_name, name.attrs[i] ); 205 if (i < name.count - 1) lstrcatW( display_name, L"," ); 206 } 207 } 208 209 done: 210 msiobj_release( &view->hdr ); 211 if (name.attrs) 212 { 213 for (i = 0; i < name.count; i++) free( name.attrs[i] ); 214 free( name.attrs ); 215 } 216 return display_name; 217 } 218 219 WCHAR *msi_get_assembly_path( MSIPACKAGE *package, const WCHAR *displayname ) 220 { 221 HRESULT hr; 222 ASSEMBLY_INFO info; 223 IAssemblyCache *cache; 224 225 if (!init_assembly_caches( package ) || !(cache = package->cache_net[CLR_VERSION_V40])) return NULL; 226 227 memset( &info, 0, sizeof(info) ); 228 info.cbAssemblyInfo = sizeof(info); 229 hr = IAssemblyCache_QueryAssemblyInfo( cache, 0, displayname, &info ); 230 if (hr != E_NOT_SUFFICIENT_BUFFER) return NULL; 231 232 if (!(info.pszCurrentAssemblyPathBuf = malloc( info.cchBuf * sizeof(WCHAR) ))) return NULL; 233 234 hr = IAssemblyCache_QueryAssemblyInfo( cache, 0, displayname, &info ); 235 if (FAILED( hr )) 236 { 237 free( info.pszCurrentAssemblyPathBuf ); 238 return NULL; 239 } 240 TRACE("returning %s\n", debugstr_w(info.pszCurrentAssemblyPathBuf)); 241 return info.pszCurrentAssemblyPathBuf; 242 } 243 244 IAssemblyEnum *msi_create_assembly_enum( MSIPACKAGE *package, const WCHAR *displayname ) 245 { 246 HRESULT hr; 247 IAssemblyName *name; 248 IAssemblyEnum *ret; 249 WCHAR *str; 250 DWORD len = 0; 251 252 if (!init_assembly_caches( package ) || !package->pCreateAssemblyNameObject || !package->pCreateAssemblyEnum) 253 return NULL; 254 255 hr = package->pCreateAssemblyNameObject( &name, displayname, CANOF_PARSE_DISPLAY_NAME, NULL ); 256 if (FAILED( hr )) return NULL; 257 258 hr = IAssemblyName_GetName( name, &len, NULL ); 259 if (hr != E_NOT_SUFFICIENT_BUFFER || !(str = malloc( len * sizeof(WCHAR) ))) 260 { 261 IAssemblyName_Release( name ); 262 return NULL; 263 } 264 265 hr = IAssemblyName_GetName( name, &len, str ); 266 IAssemblyName_Release( name ); 267 if (FAILED( hr )) 268 { 269 free( str ); 270 return NULL; 271 } 272 273 hr = package->pCreateAssemblyNameObject( &name, str, 0, NULL ); 274 free( str ); 275 if (FAILED( hr )) return NULL; 276 277 hr = package->pCreateAssemblyEnum( &ret, NULL, name, ASM_CACHE_GAC, NULL ); 278 IAssemblyName_Release( name ); 279 if (FAILED( hr )) return NULL; 280 281 return ret; 282 } 283 284 static const WCHAR *clr_version[] = 285 { 286 L"v1.0.3705", 287 L"v1.2.4322", 288 L"v2.0.50727", 289 L"v4.0.30319" 290 }; 291 292 MSIASSEMBLY *msi_load_assembly( MSIPACKAGE *package, MSICOMPONENT *comp ) 293 { 294 MSIRECORD *rec; 295 MSIASSEMBLY *a; 296 297 if (!(rec = get_assembly_record( package, comp->Component ))) return NULL; 298 if (!(a = calloc( 1, sizeof(MSIASSEMBLY) ))) 299 { 300 msiobj_release( &rec->hdr ); 301 return NULL; 302 } 303 a->feature = wcsdup( MSI_RecordGetString( rec, 2 ) ); 304 TRACE("feature %s\n", debugstr_w(a->feature)); 305 306 a->manifest = wcsdup( MSI_RecordGetString( rec, 3 ) ); 307 TRACE("manifest %s\n", debugstr_w(a->manifest)); 308 309 a->application = wcsdup( MSI_RecordGetString( rec, 4 ) ); 310 TRACE("application %s\n", debugstr_w(a->application)); 311 312 a->attributes = MSI_RecordGetInteger( rec, 5 ); 313 TRACE( "attributes %lu\n", a->attributes ); 314 315 if (!(a->display_name = get_assembly_display_name( package->db, comp->Component, a ))) 316 { 317 WARN("can't get display name\n"); 318 msiobj_release( &rec->hdr ); 319 free( a->feature ); 320 free( a->manifest ); 321 free( a->application ); 322 free( a ); 323 return NULL; 324 } 325 TRACE("display name %s\n", debugstr_w(a->display_name)); 326 327 msiobj_release( &rec->hdr ); 328 return a; 329 } 330 331 static enum clr_version get_clr_version( MSIPACKAGE *package, const WCHAR *filename ) 332 { 333 DWORD len; 334 HRESULT hr; 335 enum clr_version version = CLR_VERSION_V11; 336 WCHAR *strW; 337 338 if (!package->pGetFileVersion) return CLR_VERSION_V10; 339 340 hr = package->pGetFileVersion( filename, NULL, 0, &len ); 341 if (hr != E_NOT_SUFFICIENT_BUFFER) return CLR_VERSION_V11; 342 if ((strW = malloc( len * sizeof(WCHAR) ))) 343 { 344 hr = package->pGetFileVersion( filename, strW, len, &len ); 345 if (hr == S_OK) 346 { 347 UINT i; 348 for (i = 0; i < CLR_VERSION_MAX; i++) 349 if (!wcscmp( strW, clr_version[i] )) version = i; 350 } 351 free( strW ); 352 } 353 return version; 354 } 355 356 UINT msi_install_assembly( MSIPACKAGE *package, MSICOMPONENT *comp ) 357 { 358 HRESULT hr; 359 const WCHAR *manifest; 360 IAssemblyCache *cache; 361 MSIASSEMBLY *assembly = comp->assembly; 362 MSIFEATURE *feature = NULL; 363 364 if (!init_assembly_caches( package )) return ERROR_FUNCTION_FAILED; 365 366 if (comp->assembly->feature) 367 feature = msi_get_loaded_feature( package, comp->assembly->feature ); 368 369 if (assembly->application) 370 { 371 if (feature) feature->Action = INSTALLSTATE_LOCAL; 372 return ERROR_SUCCESS; 373 } 374 if (assembly->attributes == msidbAssemblyAttributesWin32) 375 { 376 if (!assembly->manifest) 377 { 378 WARN("no manifest\n"); 379 return ERROR_FUNCTION_FAILED; 380 } 381 manifest = msi_get_loaded_file( package, assembly->manifest )->TargetPath; 382 cache = package->cache_sxs; 383 } 384 else 385 { 386 manifest = msi_get_loaded_file( package, comp->KeyPath )->TargetPath; 387 cache = package->cache_net[get_clr_version( package, manifest )]; 388 if (!cache) return ERROR_SUCCESS; 389 } 390 TRACE("installing assembly %s\n", debugstr_w(manifest)); 391 392 hr = IAssemblyCache_InstallAssembly( cache, 0, manifest, NULL ); 393 if (hr != S_OK) 394 { 395 ERR( "failed to install assembly %s (%#lx)\n", debugstr_w(manifest), hr ); 396 return ERROR_FUNCTION_FAILED; 397 } 398 if (feature) feature->Action = INSTALLSTATE_LOCAL; 399 return ERROR_SUCCESS; 400 } 401 402 UINT msi_uninstall_assembly( MSIPACKAGE *package, MSICOMPONENT *comp ) 403 { 404 HRESULT hr; 405 IAssemblyCache *cache; 406 MSIASSEMBLY *assembly = comp->assembly; 407 MSIFEATURE *feature = NULL; 408 409 if (!init_assembly_caches( package )) return ERROR_FUNCTION_FAILED; 410 411 if (comp->assembly->feature) 412 feature = msi_get_loaded_feature( package, comp->assembly->feature ); 413 414 if (assembly->application) 415 { 416 if (feature) feature->Action = INSTALLSTATE_ABSENT; 417 return ERROR_SUCCESS; 418 } 419 TRACE("removing %s\n", debugstr_w(assembly->display_name)); 420 421 if (assembly->attributes == msidbAssemblyAttributesWin32) 422 { 423 cache = package->cache_sxs; 424 hr = IAssemblyCache_UninstallAssembly( cache, 0, assembly->display_name, NULL, NULL ); 425 if (FAILED( hr )) WARN( "failed to uninstall assembly %#lx\n", hr ); 426 } 427 else 428 { 429 unsigned int i; 430 for (i = 0; i < CLR_VERSION_MAX; i++) 431 { 432 if (!assembly->clr_version[i]) continue; 433 cache = package->cache_net[i]; 434 if (cache) 435 { 436 hr = IAssemblyCache_UninstallAssembly( cache, 0, assembly->display_name, NULL, NULL ); 437 if (FAILED( hr )) WARN( "failed to uninstall assembly %#lx\n", hr ); 438 } 439 } 440 } 441 if (feature) feature->Action = INSTALLSTATE_ABSENT; 442 return ERROR_SUCCESS; 443 } 444 445 static WCHAR *build_local_assembly_path( const WCHAR *filename ) 446 { 447 UINT i; 448 WCHAR *ret; 449 450 if (!(ret = malloc( (wcslen( filename ) + 1) * sizeof(WCHAR) ))) 451 return NULL; 452 453 for (i = 0; filename[i]; i++) 454 { 455 if (filename[i] == '\\' || filename[i] == '/') ret[i] = '|'; 456 else ret[i] = filename[i]; 457 } 458 ret[i] = 0; 459 return ret; 460 } 461 462 static LONG open_assemblies_key( UINT context, BOOL win32, HKEY *hkey ) 463 { 464 HKEY root; 465 const WCHAR *path; 466 467 if (context == MSIINSTALLCONTEXT_MACHINE) 468 { 469 root = HKEY_CLASSES_ROOT; 470 if (win32) path = L"Installer\\Win32Assemblies\\"; 471 else path = L"Installer\\Assemblies\\"; 472 } 473 else 474 { 475 root = HKEY_CURRENT_USER; 476 if (win32) path = L"Software\\Microsoft\\Installer\\Win32Assemblies\\"; 477 else path = L"Software\\Microsoft\\Installer\\Assemblies\\"; 478 } 479 return RegCreateKeyW( root, path, hkey ); 480 } 481 482 static LONG open_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename, HKEY *hkey ) 483 { 484 LONG res; 485 HKEY root; 486 WCHAR *path; 487 488 if (!(path = build_local_assembly_path( filename ))) 489 return ERROR_OUTOFMEMORY; 490 491 if ((res = open_assemblies_key( context, win32, &root ))) 492 { 493 free( path ); 494 return res; 495 } 496 res = RegCreateKeyW( root, path, hkey ); 497 RegCloseKey( root ); 498 free( path ); 499 return res; 500 } 501 502 static LONG delete_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename ) 503 { 504 LONG res; 505 HKEY root; 506 WCHAR *path; 507 508 if (!(path = build_local_assembly_path( filename ))) 509 return ERROR_OUTOFMEMORY; 510 511 if ((res = open_assemblies_key( context, win32, &root ))) 512 { 513 free( path ); 514 return res; 515 } 516 res = RegDeleteKeyW( root, path ); 517 RegCloseKey( root ); 518 free( path ); 519 return res; 520 } 521 522 static LONG open_global_assembly_key( UINT context, BOOL win32, HKEY *hkey ) 523 { 524 HKEY root; 525 const WCHAR *path; 526 527 if (context == MSIINSTALLCONTEXT_MACHINE) 528 { 529 root = HKEY_CLASSES_ROOT; 530 if (win32) path = L"Installer\\Win32Assemblies\\Global"; 531 else path = L"Installer\\Assemblies\\Global"; 532 } 533 else 534 { 535 root = HKEY_CURRENT_USER; 536 if (win32) path = L"Software\\Microsoft\\Installer\\Win32Assemblies\\Global"; 537 else path = L"Software\\Microsoft\\Installer\\Assemblies\\Global"; 538 } 539 return RegCreateKeyW( root, path, hkey ); 540 } 541 542 UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package ) 543 { 544 MSICOMPONENT *comp; 545 546 if (package->script == SCRIPT_NONE) 547 return msi_schedule_action(package, SCRIPT_INSTALL, L"MsiPublishAssemblies"); 548 549 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry) 550 { 551 LONG res; 552 HKEY hkey; 553 GUID guid; 554 DWORD size; 555 WCHAR buffer[43]; 556 MSIRECORD *uirow; 557 MSIASSEMBLY *assembly = comp->assembly; 558 BOOL win32; 559 560 if (!assembly || !comp->ComponentId) continue; 561 562 comp->Action = msi_get_component_action( package, comp ); 563 if (comp->Action != INSTALLSTATE_LOCAL) 564 { 565 TRACE("component not scheduled for installation %s\n", debugstr_w(comp->Component)); 566 continue; 567 } 568 TRACE("publishing %s\n", debugstr_w(comp->Component)); 569 570 CLSIDFromString( package->ProductCode, &guid ); 571 encode_base85_guid( &guid, buffer ); 572 buffer[20] = '>'; 573 CLSIDFromString( comp->ComponentId, &guid ); 574 encode_base85_guid( &guid, buffer + 21 ); 575 buffer[42] = 0; 576 577 win32 = assembly->attributes & msidbAssemblyAttributesWin32; 578 if (assembly->application) 579 { 580 MSIFILE *file = msi_get_loaded_file( package, assembly->application ); 581 if (!file) 582 { 583 WARN("no matching file %s for local assembly\n", debugstr_w(assembly->application)); 584 continue; 585 } 586 if ((res = open_local_assembly_key( package->Context, win32, file->TargetPath, &hkey ))) 587 { 588 WARN( "failed to open local assembly key %ld\n", res ); 589 return ERROR_FUNCTION_FAILED; 590 } 591 } 592 else 593 { 594 if ((res = open_global_assembly_key( package->Context, win32, &hkey ))) 595 { 596 WARN( "failed to open global assembly key %ld\n", res ); 597 return ERROR_FUNCTION_FAILED; 598 } 599 } 600 size = sizeof(buffer); 601 if ((res = RegSetValueExW( hkey, assembly->display_name, 0, REG_MULTI_SZ, (const BYTE *)buffer, size ))) 602 { 603 WARN( "failed to set assembly value %ld\n", res ); 604 } 605 RegCloseKey( hkey ); 606 607 uirow = MSI_CreateRecord( 2 ); 608 MSI_RecordSetStringW( uirow, 2, assembly->display_name ); 609 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow); 610 msiobj_release( &uirow->hdr ); 611 } 612 return ERROR_SUCCESS; 613 } 614 615 UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package ) 616 { 617 MSICOMPONENT *comp; 618 619 if (package->script == SCRIPT_NONE) 620 return msi_schedule_action(package, SCRIPT_INSTALL, L"MsiUnpublishAssemblies"); 621 622 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry) 623 { 624 LONG res; 625 MSIRECORD *uirow; 626 MSIASSEMBLY *assembly = comp->assembly; 627 BOOL win32; 628 629 if (!assembly || !comp->ComponentId) continue; 630 631 comp->Action = msi_get_component_action( package, comp ); 632 if (comp->Action != INSTALLSTATE_ABSENT) 633 { 634 TRACE("component not scheduled for removal %s\n", debugstr_w(comp->Component)); 635 continue; 636 } 637 TRACE("unpublishing %s\n", debugstr_w(comp->Component)); 638 639 win32 = assembly->attributes & msidbAssemblyAttributesWin32; 640 if (assembly->application) 641 { 642 MSIFILE *file = msi_get_loaded_file( package, assembly->application ); 643 if (!file) 644 { 645 WARN("no matching file %s for local assembly\n", debugstr_w(assembly->application)); 646 continue; 647 } 648 if ((res = delete_local_assembly_key( package->Context, win32, file->TargetPath ))) 649 WARN( "failed to delete local assembly key %ld\n", res ); 650 } 651 else 652 { 653 HKEY hkey; 654 if ((res = open_global_assembly_key( package->Context, win32, &hkey ))) 655 WARN( "failed to delete global assembly key %ld\n", res ); 656 else 657 { 658 if ((res = RegDeleteValueW( hkey, assembly->display_name ))) 659 WARN( "failed to delete global assembly value %ld\n", res ); 660 RegCloseKey( hkey ); 661 } 662 } 663 664 uirow = MSI_CreateRecord( 2 ); 665 MSI_RecordSetStringW( uirow, 2, assembly->display_name ); 666 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow); 667 msiobj_release( &uirow->hdr ); 668 } 669 return ERROR_SUCCESS; 670 } 671