advapi32: Remove wrong "is NULL and error out" code (coccicheck).
[wine/wine-gecko.git] / dlls / msi / assembly.c
blob806f89e1e7e361055bb15a216fc78d71910c5e99
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 for (i = 0; i < name.count; i++) msi_free( name.attrs[i] );
251 msi_free( name.attrs );
252 return display_name;
255 static BOOL is_assembly_installed( IAssemblyCache *cache, const WCHAR *display_name )
257 HRESULT hr;
258 ASSEMBLY_INFO info;
260 memset( &info, 0, sizeof(info) );
261 info.cbAssemblyInfo = sizeof(info);
262 hr = IAssemblyCache_QueryAssemblyInfo( cache, QUERYASMINFO_FLAG_GETSIZE, display_name, &info );
263 if (FAILED( hr ))
265 TRACE("QueryAssemblyInfo returned 0x%08x\n", hr);
266 return FALSE;
268 return (info.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
271 static const WCHAR clr_version_v10[] = {'v','1','.','0','.','3','7','0','5',0};
272 static const WCHAR clr_version_v11[] = {'v','1','.','1','.','4','3','2','2',0};
273 static const WCHAR clr_version_v20[] = {'v','2','.','0','.','5','0','7','2','7',0};
274 static const WCHAR clr_version_unknown[] = {'u','n','k','n','o','w','n',0};
276 static const WCHAR *clr_version[] =
278 clr_version_v10,
279 clr_version_v11,
280 clr_version_v20
283 static const WCHAR *get_clr_version_str( enum clr_version version )
285 if (version >= sizeof(clr_version)/sizeof(clr_version[0])) return clr_version_unknown;
286 return clr_version[version];
289 /* assembly caches must be initialized */
290 MSIASSEMBLY *msi_load_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
292 MSIRECORD *rec;
293 MSIASSEMBLY *a;
295 if (!(rec = get_assembly_record( package, comp->Component ))) return NULL;
296 if (!(a = msi_alloc_zero( sizeof(MSIASSEMBLY) )))
298 msiobj_release( &rec->hdr );
299 return NULL;
301 a->feature = strdupW( MSI_RecordGetString( rec, 2 ) );
302 TRACE("feature %s\n", debugstr_w(a->feature));
304 a->manifest = strdupW( MSI_RecordGetString( rec, 3 ) );
305 TRACE("manifest %s\n", debugstr_w(a->manifest));
307 a->application = strdupW( MSI_RecordGetString( rec, 4 ) );
308 TRACE("application %s\n", debugstr_w(a->application));
310 a->attributes = MSI_RecordGetInteger( rec, 5 );
311 TRACE("attributes %u\n", a->attributes);
313 if (!(a->display_name = get_assembly_display_name( package->db, comp->Component, a )))
315 WARN("can't get display name\n");
316 msiobj_release( &rec->hdr );
317 msi_free( a->feature );
318 msi_free( a->manifest );
319 msi_free( a->application );
320 msi_free( a );
321 return NULL;
323 TRACE("display name %s\n", debugstr_w(a->display_name));
325 if (a->application)
327 /* We can't check the manifest here because the target path may still change.
328 So we assume that the assembly is not installed and lean on the InstallFiles
329 action to determine which files need to be installed.
331 a->installed = FALSE;
333 else
335 if (a->attributes == msidbAssemblyAttributesWin32)
336 a->installed = is_assembly_installed( package->cache_sxs, a->display_name );
337 else
339 UINT i;
340 for (i = 0; i < CLR_VERSION_MAX; i++)
342 a->clr_version[i] = is_assembly_installed( package->cache_net[i], a->display_name );
343 if (a->clr_version[i])
345 TRACE("runtime version %s\n", debugstr_w(get_clr_version_str( i )));
346 a->installed = TRUE;
351 TRACE("assembly is %s\n", a->installed ? "installed" : "not installed");
352 msiobj_release( &rec->hdr );
353 return a;
356 static enum clr_version get_clr_version( const WCHAR *filename )
358 DWORD len;
359 HRESULT hr;
360 enum clr_version version = CLR_VERSION_V11;
361 WCHAR *strW;
363 if (!pGetFileVersion) return CLR_VERSION_V10;
365 hr = pGetFileVersion( filename, NULL, 0, &len );
366 if (hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) return CLR_VERSION_V11;
367 if ((strW = msi_alloc( len * sizeof(WCHAR) )))
369 hr = pGetFileVersion( filename, strW, len, &len );
370 if (hr == S_OK)
372 UINT i;
373 for (i = 0; i < CLR_VERSION_MAX; i++)
374 if (!strcmpW( strW, clr_version[i] )) version = i;
376 msi_free( strW );
378 return version;
381 UINT msi_install_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
383 HRESULT hr;
384 const WCHAR *manifest;
385 IAssemblyCache *cache;
386 MSIASSEMBLY *assembly = comp->assembly;
387 MSIFEATURE *feature = NULL;
389 if (comp->assembly->feature)
390 feature = msi_get_loaded_feature( package, comp->assembly->feature );
392 if (assembly->application)
394 if (feature) feature->Action = INSTALLSTATE_LOCAL;
395 return ERROR_SUCCESS;
397 if (assembly->attributes == msidbAssemblyAttributesWin32)
399 if (!assembly->manifest)
401 WARN("no manifest\n");
402 return ERROR_FUNCTION_FAILED;
404 manifest = msi_get_loaded_file( package, assembly->manifest )->TargetPath;
405 cache = package->cache_sxs;
407 else
409 manifest = msi_get_loaded_file( package, comp->KeyPath )->TargetPath;
410 cache = package->cache_net[get_clr_version( manifest )];
412 TRACE("installing assembly %s\n", debugstr_w(manifest));
414 hr = IAssemblyCache_InstallAssembly( cache, 0, manifest, NULL );
415 if (hr != S_OK)
417 ERR("Failed to install assembly %s (0x%08x)\n", debugstr_w(manifest), hr);
418 return ERROR_FUNCTION_FAILED;
420 if (feature) feature->Action = INSTALLSTATE_LOCAL;
421 assembly->installed = TRUE;
422 return ERROR_SUCCESS;
425 static WCHAR *build_local_assembly_path( const WCHAR *filename )
427 UINT i;
428 WCHAR *ret;
430 if (!(ret = msi_alloc( (strlenW( filename ) + 1) * sizeof(WCHAR) )))
431 return NULL;
433 for (i = 0; filename[i]; i++)
435 if (filename[i] == '\\' || filename[i] == '/') ret[i] = '|';
436 else ret[i] = filename[i];
438 ret[i] = 0;
439 return ret;
442 static LONG open_assemblies_key( UINT context, BOOL win32, HKEY *hkey )
444 static const WCHAR path_win32[] =
445 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
446 '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};
447 static const WCHAR path_dotnet[] =
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','\\','A','s','s','e','m','b','l','i','e','s','\\',0};
450 static const WCHAR classes_path_win32[] =
451 {'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};
452 static const WCHAR classes_path_dotnet[] =
453 {'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0};
454 HKEY root;
455 const WCHAR *path;
457 if (context == MSIINSTALLCONTEXT_MACHINE)
459 root = HKEY_CLASSES_ROOT;
460 if (win32) path = classes_path_win32;
461 else path = classes_path_dotnet;
463 else
465 root = HKEY_CURRENT_USER;
466 if (win32) path = path_win32;
467 else path = path_dotnet;
469 return RegCreateKeyW( root, path, hkey );
472 static LONG open_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename, HKEY *hkey )
474 LONG res;
475 HKEY root;
476 WCHAR *path;
478 if (!(path = build_local_assembly_path( filename )))
479 return ERROR_OUTOFMEMORY;
481 if ((res = open_assemblies_key( context, win32, &root )))
483 msi_free( path );
484 return res;
486 res = RegCreateKeyW( root, path, hkey );
487 RegCloseKey( root );
488 msi_free( path );
489 return res;
492 static LONG delete_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename )
494 LONG res;
495 HKEY root;
496 WCHAR *path;
498 if (!(path = build_local_assembly_path( filename )))
499 return ERROR_OUTOFMEMORY;
501 if ((res = open_assemblies_key( context, win32, &root )))
503 msi_free( path );
504 return res;
506 res = RegDeleteKeyW( root, path );
507 RegCloseKey( root );
508 msi_free( path );
509 return res;
512 static LONG open_global_assembly_key( UINT context, BOOL win32, HKEY *hkey )
514 static const WCHAR path_win32[] =
515 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
516 'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',
517 'G','l','o','b','a','l',0};
518 static const WCHAR path_dotnet[] =
519 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
520 'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',
521 'G','l','o','b','a','l',0};
522 static const WCHAR classes_path_win32[] =
523 {'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',
524 'G','l','o','b','a','l',0};
525 static const WCHAR classes_path_dotnet[] =
526 {'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};
527 HKEY root;
528 const WCHAR *path;
530 if (context == MSIINSTALLCONTEXT_MACHINE)
532 root = HKEY_CLASSES_ROOT;
533 if (win32) path = classes_path_win32;
534 else path = classes_path_dotnet;
536 else
538 root = HKEY_CURRENT_USER;
539 if (win32) path = path_win32;
540 else path = path_dotnet;
542 return RegCreateKeyW( root, path, hkey );
545 UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
547 MSICOMPONENT *comp;
549 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
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;
560 if (!assembly || !comp->ComponentId) continue;
562 comp->Action = msi_get_component_action( package, comp );
563 if (comp->Action != INSTALLSTATE_LOCAL)
565 TRACE("component not scheduled for installation %s\n", debugstr_w(comp->Component));
566 continue;
568 TRACE("publishing %s\n", debugstr_w(comp->Component));
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;
577 win32 = assembly->attributes & msidbAssemblyAttributesWin32;
578 if (assembly->application)
580 MSIFILE *file = msi_get_loaded_file( package, assembly->application );
581 if ((res = open_local_assembly_key( package->Context, win32, file->TargetPath, &hkey )))
583 WARN("failed to open local assembly key %d\n", res);
584 return ERROR_FUNCTION_FAILED;
587 else
589 if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
591 WARN("failed to open global assembly key %d\n", res);
592 return ERROR_FUNCTION_FAILED;
595 size = sizeof(buffer);
596 if ((res = RegSetValueExW( hkey, assembly->display_name, 0, REG_MULTI_SZ, (const BYTE *)buffer, size )))
598 WARN("failed to set assembly value %d\n", res);
600 RegCloseKey( hkey );
602 uirow = MSI_CreateRecord( 2 );
603 MSI_RecordSetStringW( uirow, 2, assembly->display_name );
604 msi_ui_actiondata( package, szMsiPublishAssemblies, uirow );
605 msiobj_release( &uirow->hdr );
607 return ERROR_SUCCESS;
610 UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
612 MSICOMPONENT *comp;
614 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
616 LONG res;
617 MSIRECORD *uirow;
618 MSIASSEMBLY *assembly = comp->assembly;
619 BOOL win32;
621 if (!assembly || !comp->ComponentId) continue;
623 comp->Action = msi_get_component_action( package, comp );
624 if (comp->Action != INSTALLSTATE_ABSENT)
626 TRACE("component not scheduled for removal %s\n", debugstr_w(comp->Component));
627 continue;
629 TRACE("unpublishing %s\n", debugstr_w(comp->Component));
631 win32 = assembly->attributes & msidbAssemblyAttributesWin32;
632 if (assembly->application)
634 MSIFILE *file = msi_get_loaded_file( package, assembly->application );
635 if ((res = delete_local_assembly_key( package->Context, win32, file->TargetPath )))
636 WARN("failed to delete local assembly key %d\n", res);
638 else
640 HKEY hkey;
641 if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
642 WARN("failed to delete global assembly key %d\n", res);
643 else
645 if ((res = RegDeleteValueW( hkey, assembly->display_name )))
646 WARN("failed to delete global assembly value %d\n", res);
647 RegCloseKey( hkey );
651 uirow = MSI_CreateRecord( 2 );
652 MSI_RecordSetStringW( uirow, 2, assembly->display_name );
653 msi_ui_actiondata( package, szMsiPublishAssemblies, uirow );
654 msiobj_release( &uirow->hdr );
656 return ERROR_SUCCESS;