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 "wine/unicode.h" 30 #include "msipriv.h" 31 32 WINE_DEFAULT_DEBUG_CHANNEL(msi); 33 34 static HRESULT (WINAPI *pCreateAssemblyCacheNet10)( IAssemblyCache **, DWORD ); 35 static HRESULT (WINAPI *pCreateAssemblyCacheNet11)( IAssemblyCache **, DWORD ); 36 static HRESULT (WINAPI *pCreateAssemblyCacheNet20)( IAssemblyCache **, DWORD ); 37 static HRESULT (WINAPI *pCreateAssemblyCacheNet40)( IAssemblyCache **, DWORD ); 38 static HRESULT (WINAPI *pCreateAssemblyCacheSxs)( IAssemblyCache **, DWORD ); 39 static HRESULT (WINAPI *pLoadLibraryShim)( LPCWSTR, LPCWSTR, LPVOID, HMODULE * ); 40 static HRESULT (WINAPI *pGetFileVersion)( LPCWSTR, LPWSTR, DWORD, DWORD * ); 41 static HRESULT (WINAPI *pCreateAssemblyNameObject)( IAssemblyName **, LPCWSTR, DWORD, LPVOID ); 42 static HRESULT (WINAPI *pCreateAssemblyEnum)( IAssemblyEnum **, IUnknown *, IAssemblyName *, DWORD, LPVOID ); 43 44 static HMODULE hfusion10, hfusion11, hfusion20, hfusion40, hmscoree, hsxs; 45 46 static BOOL init_function_pointers( void ) 47 { 48 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0}; 49 static const WCHAR szMscoree[] = {'\\','m','s','c','o','r','e','e','.','d','l','l',0}; 50 static const WCHAR szSxs[] = {'s','x','s','.','d','l','l',0}; 51 static const WCHAR szVersion10[] = {'v','1','.','0','.','3','7','0','5',0}; 52 static const WCHAR szVersion11[] = {'v','1','.','1','.','4','3','2','2',0}; 53 static const WCHAR szVersion20[] = {'v','2','.','0','.','5','0','7','2','7',0}; 54 static const WCHAR szVersion40[] = {'v','4','.','0','.','3','0','3','1','9',0}; 55 WCHAR path[MAX_PATH]; 56 DWORD len = GetSystemDirectoryW( path, MAX_PATH ); 57 58 if (!hsxs && !(hsxs = LoadLibraryW( szSxs ))) return FALSE; 59 if (!(pCreateAssemblyCacheSxs = (void *)GetProcAddress( hsxs, "CreateAssemblyCache" ))) 60 { 61 FreeLibrary( hsxs ); 62 hsxs = NULL; 63 return FALSE; 64 } 65 strcpyW( path + len, szMscoree ); 66 if (hmscoree || !(hmscoree = LoadLibraryW( path ))) return TRUE; 67 pGetFileVersion = (void *)GetProcAddress( hmscoree, "GetFileVersion" ); /* missing from v1.0.3705 */ 68 if (!(pLoadLibraryShim = (void *)GetProcAddress( hmscoree, "LoadLibraryShim" ))) 69 { 70 FreeLibrary( hmscoree ); 71 hmscoree = NULL; 72 return TRUE; 73 } 74 if (!pLoadLibraryShim( szFusion, szVersion10, NULL, &hfusion10 )) 75 pCreateAssemblyCacheNet10 = (void *)GetProcAddress( hfusion10, "CreateAssemblyCache" ); 76 77 if (!pLoadLibraryShim( szFusion, szVersion11, NULL, &hfusion11 )) 78 pCreateAssemblyCacheNet11 = (void *)GetProcAddress( hfusion11, "CreateAssemblyCache" ); 79 80 if (!pLoadLibraryShim( szFusion, szVersion20, NULL, &hfusion20 )) 81 pCreateAssemblyCacheNet20 = (void *)GetProcAddress( hfusion20, "CreateAssemblyCache" ); 82 83 if (!pLoadLibraryShim( szFusion, szVersion40, NULL, &hfusion40 )) 84 { 85 pCreateAssemblyCacheNet40 = (void *)GetProcAddress( hfusion40, "CreateAssemblyCache" ); 86 pCreateAssemblyNameObject = (void *)GetProcAddress( hfusion40, "CreateAssemblyNameObject" ); 87 pCreateAssemblyEnum = (void *)GetProcAddress( hfusion40, "CreateAssemblyEnum" ); 88 } 89 return TRUE; 90 } 91 92 BOOL msi_init_assembly_caches( MSIPACKAGE *package ) 93 { 94 if (!init_function_pointers()) return FALSE; 95 if (pCreateAssemblyCacheSxs( &package->cache_sxs, 0 ) != S_OK) return FALSE; 96 if (pCreateAssemblyCacheNet10) pCreateAssemblyCacheNet10( &package->cache_net[CLR_VERSION_V10], 0 ); 97 if (pCreateAssemblyCacheNet11) pCreateAssemblyCacheNet11( &package->cache_net[CLR_VERSION_V11], 0 ); 98 if (pCreateAssemblyCacheNet20) pCreateAssemblyCacheNet20( &package->cache_net[CLR_VERSION_V20], 0 ); 99 if (pCreateAssemblyCacheNet40) pCreateAssemblyCacheNet40( &package->cache_net[CLR_VERSION_V40], 0 ); 100 return TRUE; 101 } 102 103 void msi_destroy_assembly_caches( MSIPACKAGE *package ) 104 { 105 UINT i; 106 107 if (package->cache_sxs) 108 { 109 IAssemblyCache_Release( package->cache_sxs ); 110 package->cache_sxs = NULL; 111 } 112 for (i = 0; i < CLR_VERSION_MAX; i++) 113 { 114 if (package->cache_net[i]) 115 { 116 IAssemblyCache_Release( package->cache_net[i] ); 117 package->cache_net[i] = NULL; 118 } 119 } 120 pCreateAssemblyCacheNet10 = NULL; 121 pCreateAssemblyCacheNet11 = NULL; 122 pCreateAssemblyCacheNet20 = NULL; 123 pCreateAssemblyCacheNet40 = NULL; 124 FreeLibrary( hfusion10 ); 125 FreeLibrary( hfusion11 ); 126 FreeLibrary( hfusion20 ); 127 FreeLibrary( hfusion40 ); 128 FreeLibrary( hmscoree ); 129 FreeLibrary( hsxs ); 130 hfusion10 = NULL; 131 hfusion11 = NULL; 132 hfusion20 = NULL; 133 hfusion40 = NULL; 134 hmscoree = NULL; 135 hsxs = NULL; 136 } 137 138 static MSIRECORD *get_assembly_record( MSIPACKAGE *package, const WCHAR *comp ) 139 { 140 static const WCHAR query[] = { 141 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', 142 '`','M','s','i','A','s','s','e','m','b','l','y','`',' ', 143 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`', 144 ' ','=',' ','\'','%','s','\'',0}; 145 MSIQUERY *view; 146 MSIRECORD *rec; 147 UINT r; 148 149 r = MSI_OpenQuery( package->db, &view, query, comp ); 150 if (r != ERROR_SUCCESS) 151 return NULL; 152 153 r = MSI_ViewExecute( view, NULL ); 154 if (r != ERROR_SUCCESS) 155 { 156 msiobj_release( &view->hdr ); 157 return NULL; 158 } 159 r = MSI_ViewFetch( view, &rec ); 160 if (r != ERROR_SUCCESS) 161 { 162 msiobj_release( &view->hdr ); 163 return NULL; 164 } 165 if (!MSI_RecordGetString( rec, 4 )) 166 TRACE("component is a global assembly\n"); 167 168 msiobj_release( &view->hdr ); 169 return rec; 170 } 171 172 struct assembly_name 173 { 174 UINT count; 175 UINT index; 176 WCHAR **attrs; 177 }; 178 179 static UINT get_assembly_name_attribute( MSIRECORD *rec, LPVOID param ) 180 { 181 static const WCHAR fmtW[] = {'%','s','=','"','%','s','"',0}; 182 static const WCHAR nameW[] = {'n','a','m','e',0}; 183 struct assembly_name *name = param; 184 const WCHAR *attr = MSI_RecordGetString( rec, 2 ); 185 const WCHAR *value = MSI_RecordGetString( rec, 3 ); 186 int len = strlenW( fmtW ) + strlenW( attr ) + strlenW( value ); 187 188 if (!(name->attrs[name->index] = msi_alloc( len * sizeof(WCHAR) ))) 189 return ERROR_OUTOFMEMORY; 190 191 if (!strcmpiW( attr, nameW )) strcpyW( name->attrs[name->index++], value ); 192 else sprintfW( name->attrs[name->index++], fmtW, attr, value ); 193 return ERROR_SUCCESS; 194 } 195 196 static WCHAR *get_assembly_display_name( MSIDATABASE *db, const WCHAR *comp, MSIASSEMBLY *assembly ) 197 { 198 static const WCHAR commaW[] = {',',0}; 199 static const WCHAR queryW[] = { 200 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', 201 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ', 202 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`', 203 ' ','=',' ','\'','%','s','\'',0}; 204 struct assembly_name name; 205 WCHAR *display_name = NULL; 206 MSIQUERY *view; 207 UINT i, r; 208 int len; 209 210 r = MSI_OpenQuery( db, &view, queryW, comp ); 211 if (r != ERROR_SUCCESS) 212 return NULL; 213 214 name.count = 0; 215 name.index = 0; 216 name.attrs = NULL; 217 MSI_IterateRecords( view, &name.count, NULL, NULL ); 218 if (!name.count) goto done; 219 220 name.attrs = msi_alloc( name.count * sizeof(WCHAR *) ); 221 if (!name.attrs) goto done; 222 223 MSI_IterateRecords( view, NULL, get_assembly_name_attribute, &name ); 224 225 len = 0; 226 for (i = 0; i < name.count; i++) len += strlenW( name.attrs[i] ) + 1; 227 228 display_name = msi_alloc( (len + 1) * sizeof(WCHAR) ); 229 if (display_name) 230 { 231 display_name[0] = 0; 232 for (i = 0; i < name.count; i++) 233 { 234 strcatW( display_name, name.attrs[i] ); 235 if (i < name.count - 1) strcatW( display_name, commaW ); 236 } 237 } 238 239 done: 240 msiobj_release( &view->hdr ); 241 if (name.attrs) 242 { 243 for (i = 0; i < name.count; i++) msi_free( name.attrs[i] ); 244 msi_free( name.attrs ); 245 } 246 return display_name; 247 } 248 249 static BOOL is_assembly_installed( IAssemblyCache *cache, const WCHAR *display_name ) 250 { 251 HRESULT hr; 252 ASSEMBLY_INFO info; 253 254 if (!cache) return FALSE; 255 256 memset( &info, 0, sizeof(info) ); 257 info.cbAssemblyInfo = sizeof(info); 258 hr = IAssemblyCache_QueryAssemblyInfo( cache, 0, display_name, &info ); 259 if (hr == S_OK /* sxs version */ || hr == E_NOT_SUFFICIENT_BUFFER) 260 { 261 return (info.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED); 262 } 263 TRACE("QueryAssemblyInfo returned 0x%08x\n", hr); 264 return FALSE; 265 } 266 267 WCHAR *msi_get_assembly_path( MSIPACKAGE *package, const WCHAR *displayname ) 268 { 269 HRESULT hr; 270 ASSEMBLY_INFO info; 271 IAssemblyCache *cache = package->cache_net[CLR_VERSION_V40]; 272 273 if (!cache) return NULL; 274 275 memset( &info, 0, sizeof(info) ); 276 info.cbAssemblyInfo = sizeof(info); 277 hr = IAssemblyCache_QueryAssemblyInfo( cache, 0, displayname, &info ); 278 if (hr != E_NOT_SUFFICIENT_BUFFER) return NULL; 279 280 if (!(info.pszCurrentAssemblyPathBuf = msi_alloc( info.cchBuf * sizeof(WCHAR) ))) return NULL; 281 282 hr = IAssemblyCache_QueryAssemblyInfo( cache, 0, displayname, &info ); 283 if (FAILED( hr )) 284 { 285 msi_free( info.pszCurrentAssemblyPathBuf ); 286 return NULL; 287 } 288 TRACE("returning %s\n", debugstr_w(info.pszCurrentAssemblyPathBuf)); 289 return info.pszCurrentAssemblyPathBuf; 290 } 291 292 IAssemblyEnum *msi_create_assembly_enum( MSIPACKAGE *package, const WCHAR *displayname ) 293 { 294 HRESULT hr; 295 IAssemblyName *name; 296 IAssemblyEnum *ret; 297 WCHAR *str; 298 UINT len = 0; 299 300 if (!pCreateAssemblyNameObject || !pCreateAssemblyEnum) return NULL; 301 302 hr = pCreateAssemblyNameObject( &name, displayname, CANOF_PARSE_DISPLAY_NAME, NULL ); 303 if (FAILED( hr )) return NULL; 304 305 hr = IAssemblyName_GetName( name, &len, NULL ); 306 if (hr != E_NOT_SUFFICIENT_BUFFER || !(str = msi_alloc( len * sizeof(WCHAR) ))) 307 { 308 IAssemblyName_Release( name ); 309 return NULL; 310 } 311 312 hr = IAssemblyName_GetName( name, &len, str ); 313 IAssemblyName_Release( name ); 314 if (FAILED( hr )) 315 { 316 msi_free( str ); 317 return NULL; 318 } 319 320 hr = pCreateAssemblyNameObject( &name, str, 0, NULL ); 321 msi_free( str ); 322 if (FAILED( hr )) return NULL; 323 324 hr = pCreateAssemblyEnum( &ret, NULL, name, ASM_CACHE_GAC, NULL ); 325 IAssemblyName_Release( name ); 326 if (FAILED( hr )) return NULL; 327 328 return ret; 329 } 330 331 static const WCHAR clr_version_v10[] = {'v','1','.','0','.','3','7','0','5',0}; 332 static const WCHAR clr_version_v11[] = {'v','1','.','1','.','4','3','2','2',0}; 333 static const WCHAR clr_version_v20[] = {'v','2','.','0','.','5','0','7','2','7',0}; 334 static const WCHAR clr_version_v40[] = {'v','4','.','0','.','3','0','3','1','9',0}; 335 static const WCHAR clr_version_unknown[] = {'u','n','k','n','o','w','n',0}; 336 337 static const WCHAR *clr_version[] = 338 { 339 clr_version_v10, 340 clr_version_v11, 341 clr_version_v20, 342 clr_version_v40 343 }; 344 345 static const WCHAR *get_clr_version_str( enum clr_version version ) 346 { 347 if (version >= sizeof(clr_version)/sizeof(clr_version[0])) return clr_version_unknown; 348 return clr_version[version]; 349 } 350 351 /* assembly caches must be initialized */ 352 MSIASSEMBLY *msi_load_assembly( MSIPACKAGE *package, MSICOMPONENT *comp ) 353 { 354 MSIRECORD *rec; 355 MSIASSEMBLY *a; 356 357 if (!(rec = get_assembly_record( package, comp->Component ))) return NULL; 358 if (!(a = msi_alloc_zero( sizeof(MSIASSEMBLY) ))) 359 { 360 msiobj_release( &rec->hdr ); 361 return NULL; 362 } 363 a->feature = strdupW( MSI_RecordGetString( rec, 2 ) ); 364 TRACE("feature %s\n", debugstr_w(a->feature)); 365 366 a->manifest = strdupW( MSI_RecordGetString( rec, 3 ) ); 367 TRACE("manifest %s\n", debugstr_w(a->manifest)); 368 369 a->application = strdupW( MSI_RecordGetString( rec, 4 ) ); 370 TRACE("application %s\n", debugstr_w(a->application)); 371 372 a->attributes = MSI_RecordGetInteger( rec, 5 ); 373 TRACE("attributes %u\n", a->attributes); 374 375 if (!(a->display_name = get_assembly_display_name( package->db, comp->Component, a ))) 376 { 377 WARN("can't get display name\n"); 378 msiobj_release( &rec->hdr ); 379 msi_free( a->feature ); 380 msi_free( a->manifest ); 381 msi_free( a->application ); 382 msi_free( a ); 383 return NULL; 384 } 385 TRACE("display name %s\n", debugstr_w(a->display_name)); 386 387 if (a->application) 388 { 389 /* We can't check the manifest here because the target path may still change. 390 So we assume that the assembly is not installed and lean on the InstallFiles 391 action to determine which files need to be installed. 392 */ 393 a->installed = FALSE; 394 } 395 else 396 { 397 if (a->attributes == msidbAssemblyAttributesWin32) 398 a->installed = is_assembly_installed( package->cache_sxs, a->display_name ); 399 else 400 { 401 UINT i; 402 for (i = 0; i < CLR_VERSION_MAX; i++) 403 { 404 a->clr_version[i] = is_assembly_installed( package->cache_net[i], a->display_name ); 405 if (a->clr_version[i]) 406 { 407 TRACE("runtime version %s\n", debugstr_w(get_clr_version_str( i ))); 408 a->installed = TRUE; 409 break; 410 } 411 } 412 } 413 } 414 TRACE("assembly is %s\n", a->installed ? "installed" : "not installed"); 415 msiobj_release( &rec->hdr ); 416 return a; 417 } 418 419 static enum clr_version get_clr_version( const WCHAR *filename ) 420 { 421 DWORD len; 422 HRESULT hr; 423 enum clr_version version = CLR_VERSION_V11; 424 WCHAR *strW; 425 426 if (!pGetFileVersion) return CLR_VERSION_V10; 427 428 hr = pGetFileVersion( filename, NULL, 0, &len ); 429 if (hr != E_NOT_SUFFICIENT_BUFFER) return CLR_VERSION_V11; 430 if ((strW = msi_alloc( len * sizeof(WCHAR) ))) 431 { 432 hr = pGetFileVersion( filename, strW, len, &len ); 433 if (hr == S_OK) 434 { 435 UINT i; 436 for (i = 0; i < CLR_VERSION_MAX; i++) 437 if (!strcmpW( strW, clr_version[i] )) version = i; 438 } 439 msi_free( strW ); 440 } 441 return version; 442 } 443 444 UINT msi_install_assembly( MSIPACKAGE *package, MSICOMPONENT *comp ) 445 { 446 HRESULT hr; 447 const WCHAR *manifest; 448 IAssemblyCache *cache; 449 MSIASSEMBLY *assembly = comp->assembly; 450 MSIFEATURE *feature = NULL; 451 452 if (comp->assembly->feature) 453 feature = msi_get_loaded_feature( package, comp->assembly->feature ); 454 455 if (assembly->application) 456 { 457 if (feature) feature->Action = INSTALLSTATE_LOCAL; 458 return ERROR_SUCCESS; 459 } 460 if (assembly->attributes == msidbAssemblyAttributesWin32) 461 { 462 if (!assembly->manifest) 463 { 464 WARN("no manifest\n"); 465 return ERROR_FUNCTION_FAILED; 466 } 467 manifest = msi_get_loaded_file( package, assembly->manifest )->TargetPath; 468 cache = package->cache_sxs; 469 } 470 else 471 { 472 manifest = msi_get_loaded_file( package, comp->KeyPath )->TargetPath; 473 cache = package->cache_net[get_clr_version( manifest )]; 474 if (!cache) return ERROR_SUCCESS; 475 } 476 TRACE("installing assembly %s\n", debugstr_w(manifest)); 477 478 hr = IAssemblyCache_InstallAssembly( cache, 0, manifest, NULL ); 479 if (hr != S_OK) 480 { 481 ERR("Failed to install assembly %s (0x%08x)\n", debugstr_w(manifest), hr); 482 return ERROR_FUNCTION_FAILED; 483 } 484 if (feature) feature->Action = INSTALLSTATE_LOCAL; 485 assembly->installed = TRUE; 486 return ERROR_SUCCESS; 487 } 488 489 UINT msi_uninstall_assembly( MSIPACKAGE *package, MSICOMPONENT *comp ) 490 { 491 HRESULT hr; 492 IAssemblyCache *cache; 493 MSIASSEMBLY *assembly = comp->assembly; 494 MSIFEATURE *feature = NULL; 495 496 if (comp->assembly->feature) 497 feature = msi_get_loaded_feature( package, comp->assembly->feature ); 498 499 if (assembly->application) 500 { 501 if (feature) feature->Action = INSTALLSTATE_ABSENT; 502 return ERROR_SUCCESS; 503 } 504 TRACE("removing %s\n", debugstr_w(assembly->display_name)); 505 506 if (assembly->attributes == msidbAssemblyAttributesWin32) 507 { 508 cache = package->cache_sxs; 509 hr = IAssemblyCache_UninstallAssembly( cache, 0, assembly->display_name, NULL, NULL ); 510 if (FAILED( hr )) WARN("failed to uninstall assembly 0x%08x\n", hr); 511 } 512 else 513 { 514 unsigned int i; 515 for (i = 0; i < CLR_VERSION_MAX; i++) 516 { 517 if (!assembly->clr_version[i]) continue; 518 cache = package->cache_net[i]; 519 if (cache) 520 { 521 hr = IAssemblyCache_UninstallAssembly( cache, 0, assembly->display_name, NULL, NULL ); 522 if (FAILED( hr )) WARN("failed to uninstall assembly 0x%08x\n", hr); 523 } 524 } 525 } 526 if (feature) feature->Action = INSTALLSTATE_ABSENT; 527 assembly->installed = FALSE; 528 return ERROR_SUCCESS; 529 } 530 531 static WCHAR *build_local_assembly_path( const WCHAR *filename ) 532 { 533 UINT i; 534 WCHAR *ret; 535 536 if (!(ret = msi_alloc( (strlenW( filename ) + 1) * sizeof(WCHAR) ))) 537 return NULL; 538 539 for (i = 0; filename[i]; i++) 540 { 541 if (filename[i] == '\\' || filename[i] == '/') ret[i] = '|'; 542 else ret[i] = filename[i]; 543 } 544 ret[i] = 0; 545 return ret; 546 } 547 548 static LONG open_assemblies_key( UINT context, BOOL win32, HKEY *hkey ) 549 { 550 static const WCHAR path_win32[] = 551 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\', 552 'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',0}; 553 static const WCHAR path_dotnet[] = 554 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\', 555 'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0}; 556 static const WCHAR classes_path_win32[] = 557 {'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',0}; 558 static const WCHAR classes_path_dotnet[] = 559 {'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0}; 560 HKEY root; 561 const WCHAR *path; 562 563 if (context == MSIINSTALLCONTEXT_MACHINE) 564 { 565 root = HKEY_CLASSES_ROOT; 566 if (win32) path = classes_path_win32; 567 else path = classes_path_dotnet; 568 } 569 else 570 { 571 root = HKEY_CURRENT_USER; 572 if (win32) path = path_win32; 573 else path = path_dotnet; 574 } 575 return RegCreateKeyW( root, path, hkey ); 576 } 577 578 static LONG open_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename, HKEY *hkey ) 579 { 580 LONG res; 581 HKEY root; 582 WCHAR *path; 583 584 if (!(path = build_local_assembly_path( filename ))) 585 return ERROR_OUTOFMEMORY; 586 587 if ((res = open_assemblies_key( context, win32, &root ))) 588 { 589 msi_free( path ); 590 return res; 591 } 592 res = RegCreateKeyW( root, path, hkey ); 593 RegCloseKey( root ); 594 msi_free( path ); 595 return res; 596 } 597 598 static LONG delete_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename ) 599 { 600 LONG res; 601 HKEY root; 602 WCHAR *path; 603 604 if (!(path = build_local_assembly_path( filename ))) 605 return ERROR_OUTOFMEMORY; 606 607 if ((res = open_assemblies_key( context, win32, &root ))) 608 { 609 msi_free( path ); 610 return res; 611 } 612 res = RegDeleteKeyW( root, path ); 613 RegCloseKey( root ); 614 msi_free( path ); 615 return res; 616 } 617 618 static LONG open_global_assembly_key( UINT context, BOOL win32, HKEY *hkey ) 619 { 620 static const WCHAR path_win32[] = 621 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\', 622 'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\', 623 'G','l','o','b','a','l',0}; 624 static const WCHAR path_dotnet[] = 625 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\', 626 'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\', 627 'G','l','o','b','a','l',0}; 628 static const WCHAR classes_path_win32[] = 629 {'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\', 630 'G','l','o','b','a','l',0}; 631 static const WCHAR classes_path_dotnet[] = 632 {'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\','G','l','o','b','a','l',0}; 633 HKEY root; 634 const WCHAR *path; 635 636 if (context == MSIINSTALLCONTEXT_MACHINE) 637 { 638 root = HKEY_CLASSES_ROOT; 639 if (win32) path = classes_path_win32; 640 else path = classes_path_dotnet; 641 } 642 else 643 { 644 root = HKEY_CURRENT_USER; 645 if (win32) path = path_win32; 646 else path = path_dotnet; 647 } 648 return RegCreateKeyW( root, path, hkey ); 649 } 650 651 UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package ) 652 { 653 MSICOMPONENT *comp; 654 655 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry) 656 { 657 LONG res; 658 HKEY hkey; 659 GUID guid; 660 DWORD size; 661 WCHAR buffer[43]; 662 MSIRECORD *uirow; 663 MSIASSEMBLY *assembly = comp->assembly; 664 BOOL win32; 665 666 if (!assembly || !comp->ComponentId) continue; 667 668 comp->Action = msi_get_component_action( package, comp ); 669 if (comp->Action != INSTALLSTATE_LOCAL) 670 { 671 TRACE("component not scheduled for installation %s\n", debugstr_w(comp->Component)); 672 continue; 673 } 674 TRACE("publishing %s\n", debugstr_w(comp->Component)); 675 676 CLSIDFromString( package->ProductCode, &guid ); 677 encode_base85_guid( &guid, buffer ); 678 buffer[20] = '>'; 679 CLSIDFromString( comp->ComponentId, &guid ); 680 encode_base85_guid( &guid, buffer + 21 ); 681 buffer[42] = 0; 682 683 win32 = assembly->attributes & msidbAssemblyAttributesWin32; 684 if (assembly->application) 685 { 686 MSIFILE *file = msi_get_loaded_file( package, assembly->application ); 687 if ((res = open_local_assembly_key( package->Context, win32, file->TargetPath, &hkey ))) 688 { 689 WARN("failed to open local assembly key %d\n", res); 690 return ERROR_FUNCTION_FAILED; 691 } 692 } 693 else 694 { 695 if ((res = open_global_assembly_key( package->Context, win32, &hkey ))) 696 { 697 WARN("failed to open global assembly key %d\n", res); 698 return ERROR_FUNCTION_FAILED; 699 } 700 } 701 size = sizeof(buffer); 702 if ((res = RegSetValueExW( hkey, assembly->display_name, 0, REG_MULTI_SZ, (const BYTE *)buffer, size ))) 703 { 704 WARN("failed to set assembly value %d\n", res); 705 } 706 RegCloseKey( hkey ); 707 708 uirow = MSI_CreateRecord( 2 ); 709 MSI_RecordSetStringW( uirow, 2, assembly->display_name ); 710 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow); 711 msiobj_release( &uirow->hdr ); 712 } 713 return ERROR_SUCCESS; 714 } 715 716 UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package ) 717 { 718 MSICOMPONENT *comp; 719 720 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry) 721 { 722 LONG res; 723 MSIRECORD *uirow; 724 MSIASSEMBLY *assembly = comp->assembly; 725 BOOL win32; 726 727 if (!assembly || !comp->ComponentId) continue; 728 729 comp->Action = msi_get_component_action( package, comp ); 730 if (comp->Action != INSTALLSTATE_ABSENT) 731 { 732 TRACE("component not scheduled for removal %s\n", debugstr_w(comp->Component)); 733 continue; 734 } 735 TRACE("unpublishing %s\n", debugstr_w(comp->Component)); 736 737 win32 = assembly->attributes & msidbAssemblyAttributesWin32; 738 if (assembly->application) 739 { 740 MSIFILE *file = msi_get_loaded_file( package, assembly->application ); 741 if ((res = delete_local_assembly_key( package->Context, win32, file->TargetPath ))) 742 WARN("failed to delete local assembly key %d\n", res); 743 } 744 else 745 { 746 HKEY hkey; 747 if ((res = open_global_assembly_key( package->Context, win32, &hkey ))) 748 WARN("failed to delete global assembly key %d\n", res); 749 else 750 { 751 if ((res = RegDeleteValueW( hkey, assembly->display_name ))) 752 WARN("failed to delete global assembly value %d\n", res); 753 RegCloseKey( hkey ); 754 } 755 } 756 757 uirow = MSI_CreateRecord( 2 ); 758 MSI_RecordSetStringW( uirow, 2, assembly->display_name ); 759 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow); 760 msiobj_release( &uirow->hdr ); 761 } 762 return ERROR_SUCCESS; 763 } 764