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