2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2010 Hans Leidekker for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 #include "wine/debug.h"
29 #include "wine/unicode.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(msi);
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 * );
42 static HMODULE hfusion10, hfusion11, hfusion20, hfusion40, hmscoree, hsxs;
44 static BOOL init_function_pointers( void )
46 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
47 static const WCHAR szVersion10[] = {'v','1','.','0','.','3','7','0','5',0};
48 static const WCHAR szVersion11[] = {'v','1','.','1','.','4','3','2','2',0};
49 static const WCHAR szVersion20[] = {'v','2','.','0','.','5','0','7','2','7',0};
50 static const WCHAR szVersion40[] = {'v','4','.','0','.','3','0','3','1','9',0};
52 if (pCreateAssemblyCacheNet10 || pCreateAssemblyCacheNet11 || pCreateAssemblyCacheNet20 ||
53 pCreateAssemblyCacheNet40 ) return TRUE;
55 if (!(hmscoree = LoadLibraryA( "mscoree.dll" ))) return FALSE;
56 pGetFileVersion = (void *)GetProcAddress( hmscoree, "GetFileVersion" ); /* missing from v1.0.3705 */
57 if (!(pLoadLibraryShim = (void *)GetProcAddress( hmscoree, "LoadLibraryShim" ))) goto error;
59 if (!pLoadLibraryShim( szFusion, szVersion10, NULL, &hfusion10 ))
60 pCreateAssemblyCacheNet10 = (void *)GetProcAddress( hfusion10, "CreateAssemblyCache" );
62 if (!pLoadLibraryShim( szFusion, szVersion11, NULL, &hfusion11 ))
63 pCreateAssemblyCacheNet11 = (void *)GetProcAddress( hfusion11, "CreateAssemblyCache" );
65 if (!pLoadLibraryShim( szFusion, szVersion20, NULL, &hfusion20 ))
66 pCreateAssemblyCacheNet20 = (void *)GetProcAddress( hfusion20, "CreateAssemblyCache" );
68 if (!pLoadLibraryShim( szFusion, szVersion40, NULL, &hfusion40 ))
69 pCreateAssemblyCacheNet40 = (void *)GetProcAddress( hfusion40, "CreateAssemblyCache" );
71 if (!pCreateAssemblyCacheNet10 && !pCreateAssemblyCacheNet11 && !pCreateAssemblyCacheNet20
72 && !pCreateAssemblyCacheNet40) goto error;
74 if (!(hsxs = LoadLibraryA( "sxs.dll" ))) goto error;
75 if (!(pCreateAssemblyCacheSxs = (void *)GetProcAddress( hsxs, "CreateAssemblyCache" ))) goto error;
79 pCreateAssemblyCacheNet10 = NULL;
80 pCreateAssemblyCacheNet11 = NULL;
81 pCreateAssemblyCacheNet20 = NULL;
82 pCreateAssemblyCacheNet40 = NULL;
83 FreeLibrary( hfusion10 );
84 FreeLibrary( hfusion11 );
85 FreeLibrary( hfusion20 );
86 FreeLibrary( hfusion40 );
87 FreeLibrary( hmscoree );
91 BOOL msi_init_assembly_caches( MSIPACKAGE *package )
93 if (!init_function_pointers()) return FALSE;
94 if (package->cache_net[CLR_VERSION_V10] ||
95 package->cache_net[CLR_VERSION_V11] ||
96 package->cache_net[CLR_VERSION_V20] ||
97 package->cache_net[CLR_VERSION_V40]) return TRUE;
98 if (pCreateAssemblyCacheSxs( &package->cache_sxs, 0 ) != S_OK) return FALSE;
100 if (pCreateAssemblyCacheNet10) pCreateAssemblyCacheNet10( &package->cache_net[CLR_VERSION_V10], 0 );
101 if (pCreateAssemblyCacheNet11) pCreateAssemblyCacheNet11( &package->cache_net[CLR_VERSION_V11], 0 );
102 if (pCreateAssemblyCacheNet20) pCreateAssemblyCacheNet20( &package->cache_net[CLR_VERSION_V20], 0 );
103 if (pCreateAssemblyCacheNet40) pCreateAssemblyCacheNet40( &package->cache_net[CLR_VERSION_V40], 0 );
105 if (package->cache_net[CLR_VERSION_V10] ||
106 package->cache_net[CLR_VERSION_V11] ||
107 package->cache_net[CLR_VERSION_V20] ||
108 package->cache_net[CLR_VERSION_V40])
112 if (package->cache_net[CLR_VERSION_V10])
114 IAssemblyCache_Release( package->cache_net[CLR_VERSION_V10] );
115 package->cache_net[CLR_VERSION_V10] = NULL;
117 if (package->cache_net[CLR_VERSION_V11])
119 IAssemblyCache_Release( package->cache_net[CLR_VERSION_V11] );
120 package->cache_net[CLR_VERSION_V11] = NULL;
122 if (package->cache_net[CLR_VERSION_V20])
124 IAssemblyCache_Release( package->cache_net[CLR_VERSION_V20] );
125 package->cache_net[CLR_VERSION_V20] = NULL;
127 if (package->cache_net[CLR_VERSION_V40])
129 IAssemblyCache_Release( package->cache_net[CLR_VERSION_V40] );
130 package->cache_net[CLR_VERSION_V40] = NULL;
132 IAssemblyCache_Release( package->cache_sxs );
133 package->cache_sxs = NULL;
137 void msi_destroy_assembly_caches( MSIPACKAGE *package )
141 for (i = 0; i < CLR_VERSION_MAX; i++)
143 if (package->cache_net[i])
145 IAssemblyCache_Release( package->cache_net[i] );
146 package->cache_net[i] = NULL;
149 if (package->cache_sxs)
151 IAssemblyCache_Release( package->cache_sxs );
152 package->cache_sxs = NULL;
154 pCreateAssemblyCacheNet10 = NULL;
155 pCreateAssemblyCacheNet11 = NULL;
156 pCreateAssemblyCacheNet20 = NULL;
157 pCreateAssemblyCacheNet40 = NULL;
158 FreeLibrary( hfusion10 );
159 FreeLibrary( hfusion11 );
160 FreeLibrary( hfusion20 );
161 FreeLibrary( hfusion40 );
162 FreeLibrary( hmscoree );
166 static MSIRECORD *get_assembly_record( MSIPACKAGE *package, const WCHAR *comp )
168 static const WCHAR query[] = {
169 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
170 '`','M','s','i','A','s','s','e','m','b','l','y','`',' ',
171 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
172 ' ','=',' ','\'','%','s','\'',0};
177 r = MSI_OpenQuery( package->db, &view, query, comp );
178 if (r != ERROR_SUCCESS)
181 r = MSI_ViewExecute( view, NULL );
182 if (r != ERROR_SUCCESS)
184 msiobj_release( &view->hdr );
187 r = MSI_ViewFetch( view, &rec );
188 if (r != ERROR_SUCCESS)
190 msiobj_release( &view->hdr );
193 if (!MSI_RecordGetString( rec, 4 ))
194 TRACE("component is a global assembly\n");
196 msiobj_release( &view->hdr );
207 static UINT get_assembly_name_attribute( MSIRECORD *rec, LPVOID param )
209 static const WCHAR fmtW[] = {'%','s','=','"','%','s','"',0};
210 static const WCHAR nameW[] = {'n','a','m','e',0};
211 struct assembly_name *name = param;
212 const WCHAR *attr = MSI_RecordGetString( rec, 2 );
213 const WCHAR *value = MSI_RecordGetString( rec, 3 );
214 int len = strlenW( fmtW ) + strlenW( attr ) + strlenW( value );
216 if (!(name->attrs[name->index] = msi_alloc( len * sizeof(WCHAR) )))
217 return ERROR_OUTOFMEMORY;
219 if (!strcmpiW( attr, nameW )) strcpyW( name->attrs[name->index++], value );
220 else sprintfW( name->attrs[name->index++], fmtW, attr, value );
221 return ERROR_SUCCESS;
224 static WCHAR *get_assembly_display_name( MSIDATABASE *db, const WCHAR *comp, MSIASSEMBLY *assembly )
226 static const WCHAR commaW[] = {',',0};
227 static const WCHAR queryW[] = {
228 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
229 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
230 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
231 ' ','=',' ','\'','%','s','\'',0};
232 struct assembly_name name;
233 WCHAR *display_name = NULL;
238 r = MSI_OpenQuery( db, &view, queryW, comp );
239 if (r != ERROR_SUCCESS)
245 MSI_IterateRecords( view, &name.count, NULL, NULL );
246 if (!name.count) goto done;
248 name.attrs = msi_alloc( name.count * sizeof(WCHAR *) );
249 if (!name.attrs) goto done;
251 MSI_IterateRecords( view, NULL, get_assembly_name_attribute, &name );
254 for (i = 0; i < name.count; i++) len += strlenW( name.attrs[i] ) + 1;
256 display_name = msi_alloc( (len + 1) * sizeof(WCHAR) );
260 for (i = 0; i < name.count; i++)
262 strcatW( display_name, name.attrs[i] );
263 if (i < name.count - 1) strcatW( display_name, commaW );
268 msiobj_release( &view->hdr );
271 for (i = 0; i < name.count; i++) msi_free( name.attrs[i] );
272 msi_free( name.attrs );
277 static BOOL is_assembly_installed( IAssemblyCache *cache, const WCHAR *display_name )
282 memset( &info, 0, sizeof(info) );
283 info.cbAssemblyInfo = sizeof(info);
284 hr = IAssemblyCache_QueryAssemblyInfo( cache, 0, display_name, &info );
285 if (hr == S_OK /* sxs version */ || hr == HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER ))
287 return (info.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
289 TRACE("QueryAssemblyInfo returned 0x%08x\n", hr);
293 static const WCHAR clr_version_v10[] = {'v','1','.','0','.','3','7','0','5',0};
294 static const WCHAR clr_version_v11[] = {'v','1','.','1','.','4','3','2','2',0};
295 static const WCHAR clr_version_v20[] = {'v','2','.','0','.','5','0','7','2','7',0};
296 static const WCHAR clr_version_v40[] = {'v','4','.','0','.','3','0','3','1','9',0};
297 static const WCHAR clr_version_unknown[] = {'u','n','k','n','o','w','n',0};
299 static const WCHAR *clr_version[] =
307 static const WCHAR *get_clr_version_str( enum clr_version version )
309 if (version >= sizeof(clr_version)/sizeof(clr_version[0])) return clr_version_unknown;
310 return clr_version[version];
313 /* assembly caches must be initialized */
314 MSIASSEMBLY *msi_load_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
319 if (!(rec = get_assembly_record( package, comp->Component ))) return NULL;
320 if (!(a = msi_alloc_zero( sizeof(MSIASSEMBLY) )))
322 msiobj_release( &rec->hdr );
325 a->feature = strdupW( MSI_RecordGetString( rec, 2 ) );
326 TRACE("feature %s\n", debugstr_w(a->feature));
328 a->manifest = strdupW( MSI_RecordGetString( rec, 3 ) );
329 TRACE("manifest %s\n", debugstr_w(a->manifest));
331 a->application = strdupW( MSI_RecordGetString( rec, 4 ) );
332 TRACE("application %s\n", debugstr_w(a->application));
334 a->attributes = MSI_RecordGetInteger( rec, 5 );
335 TRACE("attributes %u\n", a->attributes);
337 if (!(a->display_name = get_assembly_display_name( package->db, comp->Component, a )))
339 WARN("can't get display name\n");
340 msiobj_release( &rec->hdr );
341 msi_free( a->feature );
342 msi_free( a->manifest );
343 msi_free( a->application );
347 TRACE("display name %s\n", debugstr_w(a->display_name));
351 /* We can't check the manifest here because the target path may still change.
352 So we assume that the assembly is not installed and lean on the InstallFiles
353 action to determine which files need to be installed.
355 a->installed = FALSE;
359 if (a->attributes == msidbAssemblyAttributesWin32)
360 a->installed = is_assembly_installed( package->cache_sxs, a->display_name );
364 for (i = 0; i < CLR_VERSION_MAX; i++)
366 a->clr_version[i] = is_assembly_installed( package->cache_net[i], a->display_name );
367 if (a->clr_version[i])
369 TRACE("runtime version %s\n", debugstr_w(get_clr_version_str( i )));
376 TRACE("assembly is %s\n", a->installed ? "installed" : "not installed");
377 msiobj_release( &rec->hdr );
381 static enum clr_version get_clr_version( const WCHAR *filename )
385 enum clr_version version = CLR_VERSION_V11;
388 if (!pGetFileVersion) return CLR_VERSION_V10;
390 hr = pGetFileVersion( filename, NULL, 0, &len );
391 if (hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) return CLR_VERSION_V11;
392 if ((strW = msi_alloc( len * sizeof(WCHAR) )))
394 hr = pGetFileVersion( filename, strW, len, &len );
398 for (i = 0; i < CLR_VERSION_MAX; i++)
399 if (!strcmpW( strW, clr_version[i] )) version = i;
406 UINT msi_install_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
409 const WCHAR *manifest;
410 IAssemblyCache *cache;
411 MSIASSEMBLY *assembly = comp->assembly;
412 MSIFEATURE *feature = NULL;
414 if (comp->assembly->feature)
415 feature = msi_get_loaded_feature( package, comp->assembly->feature );
417 if (assembly->application)
419 if (feature) feature->Action = INSTALLSTATE_LOCAL;
420 return ERROR_SUCCESS;
422 if (assembly->attributes == msidbAssemblyAttributesWin32)
424 if (!assembly->manifest)
426 WARN("no manifest\n");
427 return ERROR_FUNCTION_FAILED;
429 manifest = msi_get_loaded_file( package, assembly->manifest )->TargetPath;
430 cache = package->cache_sxs;
434 manifest = msi_get_loaded_file( package, comp->KeyPath )->TargetPath;
435 cache = package->cache_net[get_clr_version( manifest )];
437 TRACE("installing assembly %s\n", debugstr_w(manifest));
439 hr = IAssemblyCache_InstallAssembly( cache, 0, manifest, NULL );
442 ERR("Failed to install assembly %s (0x%08x)\n", debugstr_w(manifest), hr);
443 return ERROR_FUNCTION_FAILED;
445 if (feature) feature->Action = INSTALLSTATE_LOCAL;
446 assembly->installed = TRUE;
447 return ERROR_SUCCESS;
450 UINT msi_uninstall_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
453 IAssemblyCache *cache;
454 MSIASSEMBLY *assembly = comp->assembly;
455 MSIFEATURE *feature = NULL;
457 if (comp->assembly->feature)
458 feature = msi_get_loaded_feature( package, comp->assembly->feature );
460 if (assembly->application)
462 if (feature) feature->Action = INSTALLSTATE_ABSENT;
463 return ERROR_SUCCESS;
465 TRACE("removing %s\n", debugstr_w(assembly->display_name));
467 if (assembly->attributes == msidbAssemblyAttributesWin32)
469 cache = package->cache_sxs;
470 hr = IAssemblyCache_UninstallAssembly( cache, 0, assembly->display_name, NULL, NULL );
471 if (FAILED( hr )) WARN("failed to uninstall assembly 0x%08x\n", hr);
476 for (i = 0; i < CLR_VERSION_MAX; i++)
478 if (!assembly->clr_version[i]) continue;
479 cache = package->cache_net[i];
480 hr = IAssemblyCache_UninstallAssembly( cache, 0, assembly->display_name, NULL, NULL );
481 if (FAILED( hr )) WARN("failed to uninstall assembly 0x%08x\n", hr);
484 if (feature) feature->Action = INSTALLSTATE_ABSENT;
485 assembly->installed = FALSE;
486 return ERROR_SUCCESS;
489 static WCHAR *build_local_assembly_path( const WCHAR *filename )
494 if (!(ret = msi_alloc( (strlenW( filename ) + 1) * sizeof(WCHAR) )))
497 for (i = 0; filename[i]; i++)
499 if (filename[i] == '\\' || filename[i] == '/') ret[i] = '|';
500 else ret[i] = filename[i];
506 static LONG open_assemblies_key( UINT context, BOOL win32, HKEY *hkey )
508 static const WCHAR path_win32[] =
509 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
510 '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};
511 static const WCHAR path_dotnet[] =
512 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
513 'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0};
514 static const WCHAR classes_path_win32[] =
515 {'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};
516 static const WCHAR classes_path_dotnet[] =
517 {'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0};
521 if (context == MSIINSTALLCONTEXT_MACHINE)
523 root = HKEY_CLASSES_ROOT;
524 if (win32) path = classes_path_win32;
525 else path = classes_path_dotnet;
529 root = HKEY_CURRENT_USER;
530 if (win32) path = path_win32;
531 else path = path_dotnet;
533 return RegCreateKeyW( root, path, hkey );
536 static LONG open_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename, HKEY *hkey )
542 if (!(path = build_local_assembly_path( filename )))
543 return ERROR_OUTOFMEMORY;
545 if ((res = open_assemblies_key( context, win32, &root )))
550 res = RegCreateKeyW( root, path, hkey );
556 static LONG delete_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename )
562 if (!(path = build_local_assembly_path( filename )))
563 return ERROR_OUTOFMEMORY;
565 if ((res = open_assemblies_key( context, win32, &root )))
570 res = RegDeleteKeyW( root, path );
576 static LONG open_global_assembly_key( UINT context, BOOL win32, HKEY *hkey )
578 static const WCHAR path_win32[] =
579 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
580 'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',
581 'G','l','o','b','a','l',0};
582 static const WCHAR path_dotnet[] =
583 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
584 'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',
585 'G','l','o','b','a','l',0};
586 static const WCHAR classes_path_win32[] =
587 {'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',
588 'G','l','o','b','a','l',0};
589 static const WCHAR classes_path_dotnet[] =
590 {'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};
594 if (context == MSIINSTALLCONTEXT_MACHINE)
596 root = HKEY_CLASSES_ROOT;
597 if (win32) path = classes_path_win32;
598 else path = classes_path_dotnet;
602 root = HKEY_CURRENT_USER;
603 if (win32) path = path_win32;
604 else path = path_dotnet;
606 return RegCreateKeyW( root, path, hkey );
609 UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
613 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
621 MSIASSEMBLY *assembly = comp->assembly;
624 if (!assembly || !comp->ComponentId) continue;
626 comp->Action = msi_get_component_action( package, comp );
627 if (comp->Action != INSTALLSTATE_LOCAL)
629 TRACE("component not scheduled for installation %s\n", debugstr_w(comp->Component));
632 TRACE("publishing %s\n", debugstr_w(comp->Component));
634 CLSIDFromString( package->ProductCode, &guid );
635 encode_base85_guid( &guid, buffer );
637 CLSIDFromString( comp->ComponentId, &guid );
638 encode_base85_guid( &guid, buffer + 21 );
641 win32 = assembly->attributes & msidbAssemblyAttributesWin32;
642 if (assembly->application)
644 MSIFILE *file = msi_get_loaded_file( package, assembly->application );
645 if ((res = open_local_assembly_key( package->Context, win32, file->TargetPath, &hkey )))
647 WARN("failed to open local assembly key %d\n", res);
648 return ERROR_FUNCTION_FAILED;
653 if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
655 WARN("failed to open global assembly key %d\n", res);
656 return ERROR_FUNCTION_FAILED;
659 size = sizeof(buffer);
660 if ((res = RegSetValueExW( hkey, assembly->display_name, 0, REG_MULTI_SZ, (const BYTE *)buffer, size )))
662 WARN("failed to set assembly value %d\n", res);
666 uirow = MSI_CreateRecord( 2 );
667 MSI_RecordSetStringW( uirow, 2, assembly->display_name );
668 msi_ui_actiondata( package, szMsiPublishAssemblies, uirow );
669 msiobj_release( &uirow->hdr );
671 return ERROR_SUCCESS;
674 UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
678 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
682 MSIASSEMBLY *assembly = comp->assembly;
685 if (!assembly || !comp->ComponentId) continue;
687 comp->Action = msi_get_component_action( package, comp );
688 if (comp->Action != INSTALLSTATE_ABSENT)
690 TRACE("component not scheduled for removal %s\n", debugstr_w(comp->Component));
693 TRACE("unpublishing %s\n", debugstr_w(comp->Component));
695 win32 = assembly->attributes & msidbAssemblyAttributesWin32;
696 if (assembly->application)
698 MSIFILE *file = msi_get_loaded_file( package, assembly->application );
699 if ((res = delete_local_assembly_key( package->Context, win32, file->TargetPath )))
700 WARN("failed to delete local assembly key %d\n", res);
705 if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
706 WARN("failed to delete global assembly key %d\n", res);
709 if ((res = RegDeleteValueW( hkey, assembly->display_name )))
710 WARN("failed to delete global assembly value %d\n", res);
715 uirow = MSI_CreateRecord( 2 );
716 MSI_RecordSetStringW( uirow, 2, assembly->display_name );
717 msi_ui_actiondata( package, szMsiPublishAssemblies, uirow );
718 msiobj_release( &uirow->hdr );
720 return ERROR_SUCCESS;