hhctrl.ocx: Open a specific topic when requested.
[wine/multimedia.git] / dlls / msi / assembly.c
blobfd1f952abf67dc962ecd7c5319933b9ef974d777
1 /*
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
21 #include <stdarg.h>
23 #define COBJMACROS
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"
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 *pCreateAssemblyCacheSxs)( IAssemblyCache **, DWORD );
38 static HRESULT (WINAPI *pLoadLibraryShim)( LPCWSTR, LPCWSTR, LPVOID, HMODULE * );
39 static HRESULT (WINAPI *pGetFileVersion)( LPCWSTR, LPWSTR, DWORD, DWORD * );
41 static HMODULE hfusion10, hfusion11, hfusion20, hmscoree, hsxs;
43 static BOOL init_function_pointers( void )
45 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
46 static const WCHAR szVersion10[] = {'v','1','.','0','.','3','7','0','5',0};
47 static const WCHAR szVersion11[] = {'v','1','.','1','.','4','3','2','2',0};
48 static const WCHAR szVersion20[] = {'v','2','.','0','.','5','0','7','2','7',0};
50 if (pCreateAssemblyCacheNet10 || pCreateAssemblyCacheNet11 || pCreateAssemblyCacheNet20) return TRUE;
52 if (!(hmscoree = LoadLibraryA( "mscoree.dll" ))) return FALSE;
53 pGetFileVersion = (void *)GetProcAddress( hmscoree, "GetFileVersion" ); /* missing from v1.0.3705 */
54 if (!(pLoadLibraryShim = (void *)GetProcAddress( hmscoree, "LoadLibraryShim" ))) goto error;
56 if (!pLoadLibraryShim( szFusion, szVersion10, NULL, &hfusion10 ))
57 pCreateAssemblyCacheNet10 = (void *)GetProcAddress( hfusion10, "CreateAssemblyCache" );
59 if (!pLoadLibraryShim( szFusion, szVersion11, NULL, &hfusion11 ))
60 pCreateAssemblyCacheNet11 = (void *)GetProcAddress( hfusion11, "CreateAssemblyCache" );
62 if (!pLoadLibraryShim( szFusion, szVersion20, NULL, &hfusion20 ))
63 pCreateAssemblyCacheNet20 = (void *)GetProcAddress( hfusion20, "CreateAssemblyCache" );
65 if (!pCreateAssemblyCacheNet10 && !pCreateAssemblyCacheNet11 && !pCreateAssemblyCacheNet20) goto error;
67 if (!(hsxs = LoadLibraryA( "sxs.dll" ))) goto error;
68 if (!(pCreateAssemblyCacheSxs = (void *)GetProcAddress( hsxs, "CreateAssemblyCache" ))) goto error;
69 return TRUE;
71 error:
72 pCreateAssemblyCacheNet10 = NULL;
73 pCreateAssemblyCacheNet11 = NULL;
74 pCreateAssemblyCacheNet20 = NULL;
75 FreeLibrary( hfusion10 );
76 FreeLibrary( hfusion11 );
77 FreeLibrary( hfusion20 );
78 FreeLibrary( hmscoree );
79 return FALSE;
82 BOOL msi_init_assembly_caches( MSIPACKAGE *package )
84 if (!init_function_pointers()) return FALSE;
85 if (package->cache_net[CLR_VERSION_V10] ||
86 package->cache_net[CLR_VERSION_V11] ||
87 package->cache_net[CLR_VERSION_V20]) return TRUE;
88 if (pCreateAssemblyCacheSxs( &package->cache_sxs, 0 ) != S_OK) return FALSE;
90 if (pCreateAssemblyCacheNet10) pCreateAssemblyCacheNet11( &package->cache_net[CLR_VERSION_V10], 0 );
91 if (pCreateAssemblyCacheNet11) pCreateAssemblyCacheNet11( &package->cache_net[CLR_VERSION_V11], 0 );
92 if (pCreateAssemblyCacheNet20) pCreateAssemblyCacheNet20( &package->cache_net[CLR_VERSION_V20], 0 );
94 if (package->cache_net[CLR_VERSION_V10] ||
95 package->cache_net[CLR_VERSION_V11] ||
96 package->cache_net[CLR_VERSION_V20])
98 return TRUE;
100 if (package->cache_net[CLR_VERSION_V10])
102 IAssemblyCache_Release( package->cache_net[CLR_VERSION_V10] );
103 package->cache_net[CLR_VERSION_V10] = NULL;
105 if (package->cache_net[CLR_VERSION_V11])
107 IAssemblyCache_Release( package->cache_net[CLR_VERSION_V11] );
108 package->cache_net[CLR_VERSION_V11] = NULL;
110 if (package->cache_net[CLR_VERSION_V20])
112 IAssemblyCache_Release( package->cache_net[CLR_VERSION_V20] );
113 package->cache_net[CLR_VERSION_V20] = NULL;
115 IAssemblyCache_Release( package->cache_sxs );
116 package->cache_sxs = NULL;
117 return FALSE;
120 void msi_destroy_assembly_caches( MSIPACKAGE *package )
122 UINT i;
124 for (i = 0; i < CLR_VERSION_MAX; i++)
126 if (package->cache_net[i])
128 IAssemblyCache_Release( package->cache_net[i] );
129 package->cache_net[i] = NULL;
132 if (package->cache_sxs)
134 IAssemblyCache_Release( package->cache_sxs );
135 package->cache_sxs = NULL;
137 pCreateAssemblyCacheNet10 = NULL;
138 pCreateAssemblyCacheNet11 = NULL;
139 pCreateAssemblyCacheNet20 = NULL;
140 FreeLibrary( hfusion10 );
141 FreeLibrary( hfusion11 );
142 FreeLibrary( hfusion20 );
143 FreeLibrary( hmscoree );
144 FreeLibrary( hsxs );
147 static MSIRECORD *get_assembly_record( MSIPACKAGE *package, const WCHAR *comp )
149 static const WCHAR query[] = {
150 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
151 '`','M','s','i','A','s','s','e','m','b','l','y','`',' ',
152 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
153 ' ','=',' ','\'','%','s','\'',0};
154 MSIQUERY *view;
155 MSIRECORD *rec;
156 UINT r;
158 r = MSI_OpenQuery( package->db, &view, query, comp );
159 if (r != ERROR_SUCCESS)
160 return NULL;
162 r = MSI_ViewExecute( view, NULL );
163 if (r != ERROR_SUCCESS)
165 msiobj_release( &view->hdr );
166 return NULL;
168 r = MSI_ViewFetch( view, &rec );
169 if (r != ERROR_SUCCESS)
171 msiobj_release( &view->hdr );
172 return NULL;
174 if (!MSI_RecordGetString( rec, 4 ))
175 TRACE("component is a global assembly\n");
177 msiobj_release( &view->hdr );
178 return rec;
181 struct assembly_name
183 UINT count;
184 UINT index;
185 WCHAR **attrs;
188 static UINT get_assembly_name_attribute( MSIRECORD *rec, LPVOID param )
190 static const WCHAR fmtW[] = {'%','s','=','"','%','s','"',0};
191 static const WCHAR nameW[] = {'n','a','m','e',0};
192 struct assembly_name *name = param;
193 const WCHAR *attr = MSI_RecordGetString( rec, 2 );
194 const WCHAR *value = MSI_RecordGetString( rec, 3 );
195 int len = strlenW( fmtW ) + strlenW( attr ) + strlenW( value );
197 if (!(name->attrs[name->index] = msi_alloc( len * sizeof(WCHAR) )))
198 return ERROR_OUTOFMEMORY;
200 if (!strcmpiW( attr, nameW )) strcpyW( name->attrs[name->index++], value );
201 else sprintfW( name->attrs[name->index++], fmtW, attr, value );
202 return ERROR_SUCCESS;
205 static WCHAR *get_assembly_display_name( MSIDATABASE *db, const WCHAR *comp, MSIASSEMBLY *assembly )
207 static const WCHAR commaW[] = {',',0};
208 static const WCHAR queryW[] = {
209 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
210 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
211 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
212 ' ','=',' ','\'','%','s','\'',0};
213 struct assembly_name name;
214 WCHAR *display_name = NULL;
215 MSIQUERY *view;
216 UINT i, r;
217 int len;
219 r = MSI_OpenQuery( db, &view, queryW, comp );
220 if (r != ERROR_SUCCESS)
221 return NULL;
223 name.count = 0;
224 name.index = 0;
225 name.attrs = NULL;
226 MSI_IterateRecords( view, &name.count, NULL, NULL );
227 if (!name.count) goto done;
229 name.attrs = msi_alloc( name.count * sizeof(WCHAR *) );
230 if (!name.attrs) goto done;
232 MSI_IterateRecords( view, NULL, get_assembly_name_attribute, &name );
234 len = 0;
235 for (i = 0; i < name.count; i++) len += strlenW( name.attrs[i] ) + 1;
237 display_name = msi_alloc( (len + 1) * sizeof(WCHAR) );
238 if (display_name)
240 display_name[0] = 0;
241 for (i = 0; i < name.count; i++)
243 strcatW( display_name, name.attrs[i] );
244 if (i < name.count - 1) strcatW( display_name, commaW );
248 done:
249 msiobj_release( &view->hdr );
250 if (name.attrs)
252 for (i = 0; i < name.count; i++) msi_free( name.attrs[i] );
253 msi_free( name.attrs );
255 return display_name;
258 static BOOL is_assembly_installed( IAssemblyCache *cache, const WCHAR *display_name )
260 HRESULT hr;
261 ASSEMBLY_INFO info;
263 memset( &info, 0, sizeof(info) );
264 info.cbAssemblyInfo = sizeof(info);
265 hr = IAssemblyCache_QueryAssemblyInfo( cache, QUERYASMINFO_FLAG_GETSIZE, display_name, &info );
266 if (FAILED( hr ))
268 TRACE("QueryAssemblyInfo returned 0x%08x\n", hr);
269 return FALSE;
271 return (info.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
274 static const WCHAR clr_version_v10[] = {'v','1','.','0','.','3','7','0','5',0};
275 static const WCHAR clr_version_v11[] = {'v','1','.','1','.','4','3','2','2',0};
276 static const WCHAR clr_version_v20[] = {'v','2','.','0','.','5','0','7','2','7',0};
277 static const WCHAR clr_version_unknown[] = {'u','n','k','n','o','w','n',0};
279 static const WCHAR *clr_version[] =
281 clr_version_v10,
282 clr_version_v11,
283 clr_version_v20
286 static const WCHAR *get_clr_version_str( enum clr_version version )
288 if (version >= sizeof(clr_version)/sizeof(clr_version[0])) return clr_version_unknown;
289 return clr_version[version];
292 /* assembly caches must be initialized */
293 MSIASSEMBLY *msi_load_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
295 MSIRECORD *rec;
296 MSIASSEMBLY *a;
298 if (!(rec = get_assembly_record( package, comp->Component ))) return NULL;
299 if (!(a = msi_alloc_zero( sizeof(MSIASSEMBLY) )))
301 msiobj_release( &rec->hdr );
302 return NULL;
304 a->feature = strdupW( MSI_RecordGetString( rec, 2 ) );
305 TRACE("feature %s\n", debugstr_w(a->feature));
307 a->manifest = strdupW( MSI_RecordGetString( rec, 3 ) );
308 TRACE("manifest %s\n", debugstr_w(a->manifest));
310 a->application = strdupW( MSI_RecordGetString( rec, 4 ) );
311 TRACE("application %s\n", debugstr_w(a->application));
313 a->attributes = MSI_RecordGetInteger( rec, 5 );
314 TRACE("attributes %u\n", a->attributes);
316 if (!(a->display_name = get_assembly_display_name( package->db, comp->Component, a )))
318 WARN("can't get display name\n");
319 msiobj_release( &rec->hdr );
320 msi_free( a->feature );
321 msi_free( a->manifest );
322 msi_free( a->application );
323 msi_free( a );
324 return NULL;
326 TRACE("display name %s\n", debugstr_w(a->display_name));
328 if (a->application)
330 /* We can't check the manifest here because the target path may still change.
331 So we assume that the assembly is not installed and lean on the InstallFiles
332 action to determine which files need to be installed.
334 a->installed = FALSE;
336 else
338 if (a->attributes == msidbAssemblyAttributesWin32)
339 a->installed = is_assembly_installed( package->cache_sxs, a->display_name );
340 else
342 UINT i;
343 for (i = 0; i < CLR_VERSION_MAX; i++)
345 a->clr_version[i] = is_assembly_installed( package->cache_net[i], a->display_name );
346 if (a->clr_version[i])
348 TRACE("runtime version %s\n", debugstr_w(get_clr_version_str( i )));
349 a->installed = TRUE;
354 TRACE("assembly is %s\n", a->installed ? "installed" : "not installed");
355 msiobj_release( &rec->hdr );
356 return a;
359 static enum clr_version get_clr_version( const WCHAR *filename )
361 DWORD len;
362 HRESULT hr;
363 enum clr_version version = CLR_VERSION_V11;
364 WCHAR *strW;
366 if (!pGetFileVersion) return CLR_VERSION_V10;
368 hr = pGetFileVersion( filename, NULL, 0, &len );
369 if (hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) return CLR_VERSION_V11;
370 if ((strW = msi_alloc( len * sizeof(WCHAR) )))
372 hr = pGetFileVersion( filename, strW, len, &len );
373 if (hr == S_OK)
375 UINT i;
376 for (i = 0; i < CLR_VERSION_MAX; i++)
377 if (!strcmpW( strW, clr_version[i] )) version = i;
379 msi_free( strW );
381 return version;
384 UINT msi_install_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
386 HRESULT hr;
387 const WCHAR *manifest;
388 IAssemblyCache *cache;
389 MSIASSEMBLY *assembly = comp->assembly;
390 MSIFEATURE *feature = NULL;
392 if (comp->assembly->feature)
393 feature = msi_get_loaded_feature( package, comp->assembly->feature );
395 if (assembly->application)
397 if (feature) feature->Action = INSTALLSTATE_LOCAL;
398 return ERROR_SUCCESS;
400 if (assembly->attributes == msidbAssemblyAttributesWin32)
402 if (!assembly->manifest)
404 WARN("no manifest\n");
405 return ERROR_FUNCTION_FAILED;
407 manifest = msi_get_loaded_file( package, assembly->manifest )->TargetPath;
408 cache = package->cache_sxs;
410 else
412 manifest = msi_get_loaded_file( package, comp->KeyPath )->TargetPath;
413 cache = package->cache_net[get_clr_version( manifest )];
415 TRACE("installing assembly %s\n", debugstr_w(manifest));
417 hr = IAssemblyCache_InstallAssembly( cache, 0, manifest, NULL );
418 if (hr != S_OK)
420 ERR("Failed to install assembly %s (0x%08x)\n", debugstr_w(manifest), hr);
421 return ERROR_FUNCTION_FAILED;
423 if (feature) feature->Action = INSTALLSTATE_LOCAL;
424 assembly->installed = TRUE;
425 return ERROR_SUCCESS;
428 static WCHAR *build_local_assembly_path( const WCHAR *filename )
430 UINT i;
431 WCHAR *ret;
433 if (!(ret = msi_alloc( (strlenW( filename ) + 1) * sizeof(WCHAR) )))
434 return NULL;
436 for (i = 0; filename[i]; i++)
438 if (filename[i] == '\\' || filename[i] == '/') ret[i] = '|';
439 else ret[i] = filename[i];
441 ret[i] = 0;
442 return ret;
445 static LONG open_assemblies_key( UINT context, BOOL win32, HKEY *hkey )
447 static const WCHAR path_win32[] =
448 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
449 '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};
450 static const WCHAR path_dotnet[] =
451 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
452 'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0};
453 static const WCHAR classes_path_win32[] =
454 {'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};
455 static const WCHAR classes_path_dotnet[] =
456 {'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0};
457 HKEY root;
458 const WCHAR *path;
460 if (context == MSIINSTALLCONTEXT_MACHINE)
462 root = HKEY_CLASSES_ROOT;
463 if (win32) path = classes_path_win32;
464 else path = classes_path_dotnet;
466 else
468 root = HKEY_CURRENT_USER;
469 if (win32) path = path_win32;
470 else path = path_dotnet;
472 return RegCreateKeyW( root, path, hkey );
475 static LONG open_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename, HKEY *hkey )
477 LONG res;
478 HKEY root;
479 WCHAR *path;
481 if (!(path = build_local_assembly_path( filename )))
482 return ERROR_OUTOFMEMORY;
484 if ((res = open_assemblies_key( context, win32, &root )))
486 msi_free( path );
487 return res;
489 res = RegCreateKeyW( root, path, hkey );
490 RegCloseKey( root );
491 msi_free( path );
492 return res;
495 static LONG delete_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename )
497 LONG res;
498 HKEY root;
499 WCHAR *path;
501 if (!(path = build_local_assembly_path( filename )))
502 return ERROR_OUTOFMEMORY;
504 if ((res = open_assemblies_key( context, win32, &root )))
506 msi_free( path );
507 return res;
509 res = RegDeleteKeyW( root, path );
510 RegCloseKey( root );
511 msi_free( path );
512 return res;
515 static LONG open_global_assembly_key( UINT context, BOOL win32, HKEY *hkey )
517 static const WCHAR path_win32[] =
518 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
519 'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',
520 'G','l','o','b','a','l',0};
521 static const WCHAR path_dotnet[] =
522 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
523 'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',
524 'G','l','o','b','a','l',0};
525 static const WCHAR classes_path_win32[] =
526 {'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',
527 'G','l','o','b','a','l',0};
528 static const WCHAR classes_path_dotnet[] =
529 {'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};
530 HKEY root;
531 const WCHAR *path;
533 if (context == MSIINSTALLCONTEXT_MACHINE)
535 root = HKEY_CLASSES_ROOT;
536 if (win32) path = classes_path_win32;
537 else path = classes_path_dotnet;
539 else
541 root = HKEY_CURRENT_USER;
542 if (win32) path = path_win32;
543 else path = path_dotnet;
545 return RegCreateKeyW( root, path, hkey );
548 UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
550 MSICOMPONENT *comp;
552 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
554 LONG res;
555 HKEY hkey;
556 GUID guid;
557 DWORD size;
558 WCHAR buffer[43];
559 MSIRECORD *uirow;
560 MSIASSEMBLY *assembly = comp->assembly;
561 BOOL win32;
563 if (!assembly || !comp->ComponentId) continue;
565 comp->Action = msi_get_component_action( package, comp );
566 if (comp->Action != INSTALLSTATE_LOCAL)
568 TRACE("component not scheduled for installation %s\n", debugstr_w(comp->Component));
569 continue;
571 TRACE("publishing %s\n", debugstr_w(comp->Component));
573 CLSIDFromString( package->ProductCode, &guid );
574 encode_base85_guid( &guid, buffer );
575 buffer[20] = '>';
576 CLSIDFromString( comp->ComponentId, &guid );
577 encode_base85_guid( &guid, buffer + 21 );
578 buffer[42] = 0;
580 win32 = assembly->attributes & msidbAssemblyAttributesWin32;
581 if (assembly->application)
583 MSIFILE *file = msi_get_loaded_file( package, assembly->application );
584 if ((res = open_local_assembly_key( package->Context, win32, file->TargetPath, &hkey )))
586 WARN("failed to open local assembly key %d\n", res);
587 return ERROR_FUNCTION_FAILED;
590 else
592 if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
594 WARN("failed to open global assembly key %d\n", res);
595 return ERROR_FUNCTION_FAILED;
598 size = sizeof(buffer);
599 if ((res = RegSetValueExW( hkey, assembly->display_name, 0, REG_MULTI_SZ, (const BYTE *)buffer, size )))
601 WARN("failed to set assembly value %d\n", res);
603 RegCloseKey( hkey );
605 uirow = MSI_CreateRecord( 2 );
606 MSI_RecordSetStringW( uirow, 2, assembly->display_name );
607 msi_ui_actiondata( package, szMsiPublishAssemblies, uirow );
608 msiobj_release( &uirow->hdr );
610 return ERROR_SUCCESS;
613 UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
615 MSICOMPONENT *comp;
617 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
619 LONG res;
620 MSIRECORD *uirow;
621 MSIASSEMBLY *assembly = comp->assembly;
622 BOOL win32;
624 if (!assembly || !comp->ComponentId) continue;
626 comp->Action = msi_get_component_action( package, comp );
627 if (comp->Action != INSTALLSTATE_ABSENT)
629 TRACE("component not scheduled for removal %s\n", debugstr_w(comp->Component));
630 continue;
632 TRACE("unpublishing %s\n", debugstr_w(comp->Component));
634 win32 = assembly->attributes & msidbAssemblyAttributesWin32;
635 if (assembly->application)
637 MSIFILE *file = msi_get_loaded_file( package, assembly->application );
638 if ((res = delete_local_assembly_key( package->Context, win32, file->TargetPath )))
639 WARN("failed to delete local assembly key %d\n", res);
641 else
643 HKEY hkey;
644 if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
645 WARN("failed to delete global assembly key %d\n", res);
646 else
648 if ((res = RegDeleteValueW( hkey, assembly->display_name )))
649 WARN("failed to delete global assembly value %d\n", res);
650 RegCloseKey( hkey );
654 uirow = MSI_CreateRecord( 2 );
655 MSI_RecordSetStringW( uirow, 2, assembly->display_name );
656 msi_ui_actiondata( package, szMsiPublishAssemblies, uirow );
657 msiobj_release( &uirow->hdr );
659 return ERROR_SUCCESS;