hid: Rewrite HidP_MaxUsageListLength using enum_value_caps.
[wine.git] / dlls / msi / assembly.c
blob1007979827a19505ffdf0851fbca864afc859b6a
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 "msipriv.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(msi);
33 static BOOL load_fusion_dlls( MSIPACKAGE *package )
35 HRESULT (WINAPI *pLoadLibraryShim)( const WCHAR *, const WCHAR *, void *, HMODULE * );
36 WCHAR path[MAX_PATH];
37 DWORD len = GetSystemDirectoryW( path, MAX_PATH );
39 lstrcpyW( path + len, L"\\mscoree.dll" );
40 if (package->hmscoree || !(package->hmscoree = LoadLibraryW( path ))) return TRUE;
41 if (!(pLoadLibraryShim = (void *)GetProcAddress( package->hmscoree, "LoadLibraryShim" )))
43 FreeLibrary( package->hmscoree );
44 package->hmscoree = NULL;
45 return TRUE;
48 pLoadLibraryShim( L"fusion.dll", L"v1.0.3705", NULL, &package->hfusion10 );
49 pLoadLibraryShim( L"fusion.dll", L"v1.1.4322", NULL, &package->hfusion11 );
50 pLoadLibraryShim( L"fusion.dll", L"v2.0.50727", NULL, &package->hfusion20 );
51 pLoadLibraryShim( L"fusion.dll", L"v4.0.30319", NULL, &package->hfusion40 );
53 return TRUE;
56 BOOL msi_init_assembly_caches( MSIPACKAGE *package )
58 HRESULT (WINAPI *pCreateAssemblyCache)( IAssemblyCache **, DWORD );
60 if (package->cache_sxs) return TRUE;
61 if (CreateAssemblyCache( &package->cache_sxs, 0 ) != S_OK) return FALSE;
63 if (!load_fusion_dlls( package )) return FALSE;
64 package->pGetFileVersion = (void *)GetProcAddress( package->hmscoree, "GetFileVersion" ); /* missing from v1.0.3705 */
66 if (package->hfusion10)
68 pCreateAssemblyCache = (void *)GetProcAddress( package->hfusion10, "CreateAssemblyCache" );
69 pCreateAssemblyCache( &package->cache_net[CLR_VERSION_V10], 0 );
71 if (package->hfusion11)
73 pCreateAssemblyCache = (void *)GetProcAddress( package->hfusion11, "CreateAssemblyCache" );
74 pCreateAssemblyCache( &package->cache_net[CLR_VERSION_V11], 0 );
76 if (package->hfusion20)
78 pCreateAssemblyCache = (void *)GetProcAddress( package->hfusion20, "CreateAssemblyCache" );
79 pCreateAssemblyCache( &package->cache_net[CLR_VERSION_V20], 0 );
81 if (package->hfusion40)
83 pCreateAssemblyCache = (void *)GetProcAddress( package->hfusion40, "CreateAssemblyCache" );
84 pCreateAssemblyCache( &package->cache_net[CLR_VERSION_V40], 0 );
85 package->pCreateAssemblyNameObject = (void *)GetProcAddress( package->hfusion40, "CreateAssemblyNameObject" );
86 package->pCreateAssemblyEnum = (void *)GetProcAddress( package->hfusion40, "CreateAssemblyEnum" );
89 return TRUE;
92 void msi_destroy_assembly_caches( MSIPACKAGE *package )
94 UINT i;
96 if (package->cache_sxs)
98 IAssemblyCache_Release( package->cache_sxs );
99 package->cache_sxs = NULL;
101 for (i = 0; i < CLR_VERSION_MAX; i++)
103 if (package->cache_net[i])
105 IAssemblyCache_Release( package->cache_net[i] );
106 package->cache_net[i] = NULL;
109 package->pGetFileVersion = NULL;
110 package->pCreateAssemblyNameObject = NULL;
111 package->pCreateAssemblyEnum = NULL;
112 FreeLibrary( package->hfusion10 );
113 FreeLibrary( package->hfusion11 );
114 FreeLibrary( package->hfusion20 );
115 FreeLibrary( package->hfusion40 );
116 FreeLibrary( package->hmscoree );
117 package->hfusion10 = NULL;
118 package->hfusion11 = NULL;
119 package->hfusion20 = NULL;
120 package->hfusion40 = NULL;
121 package->hmscoree = NULL;
124 static MSIRECORD *get_assembly_record( MSIPACKAGE *package, const WCHAR *comp )
126 MSIQUERY *view;
127 MSIRECORD *rec;
128 UINT r;
130 r = MSI_OpenQuery( package->db, &view, L"SELECT * FROM `MsiAssembly` WHERE `Component_` = '%s'", comp );
131 if (r != ERROR_SUCCESS)
132 return NULL;
134 r = MSI_ViewExecute( view, NULL );
135 if (r != ERROR_SUCCESS)
137 msiobj_release( &view->hdr );
138 return NULL;
140 r = MSI_ViewFetch( view, &rec );
141 if (r != ERROR_SUCCESS)
143 msiobj_release( &view->hdr );
144 return NULL;
146 if (!MSI_RecordGetString( rec, 4 ))
147 TRACE("component is a global assembly\n");
149 msiobj_release( &view->hdr );
150 return rec;
153 struct assembly_name
155 UINT count;
156 UINT index;
157 WCHAR **attrs;
160 static UINT get_assembly_name_attribute( MSIRECORD *rec, LPVOID param )
162 struct assembly_name *name = param;
163 const WCHAR *attr = MSI_RecordGetString( rec, 2 );
164 const WCHAR *value = MSI_RecordGetString( rec, 3 );
165 int len = lstrlenW( L"%s=\"%s\"" ) + lstrlenW( attr ) + lstrlenW( value );
167 if (!(name->attrs[name->index] = msi_alloc( len * sizeof(WCHAR) )))
168 return ERROR_OUTOFMEMORY;
170 if (!wcsicmp( attr, L"name" )) lstrcpyW( name->attrs[name->index++], value );
171 else swprintf( name->attrs[name->index++], len, L"%s=\"%s\"", attr, value );
172 return ERROR_SUCCESS;
175 static WCHAR *get_assembly_display_name( MSIDATABASE *db, const WCHAR *comp, MSIASSEMBLY *assembly )
177 struct assembly_name name;
178 WCHAR *display_name = NULL;
179 MSIQUERY *view;
180 UINT i, r;
181 int len;
183 r = MSI_OpenQuery( db, &view, L"SELECT * FROM `MsiAssemblyName` WHERE `Component_` = '%s'", comp );
184 if (r != ERROR_SUCCESS)
185 return NULL;
187 name.count = 0;
188 name.index = 0;
189 name.attrs = NULL;
190 MSI_IterateRecords( view, &name.count, NULL, NULL );
191 if (!name.count) goto done;
193 name.attrs = msi_alloc( name.count * sizeof(WCHAR *) );
194 if (!name.attrs) goto done;
196 MSI_IterateRecords( view, NULL, get_assembly_name_attribute, &name );
198 len = 0;
199 for (i = 0; i < name.count; i++) len += lstrlenW( name.attrs[i] ) + 1;
201 display_name = msi_alloc( (len + 1) * sizeof(WCHAR) );
202 if (display_name)
204 display_name[0] = 0;
205 for (i = 0; i < name.count; i++)
207 lstrcatW( display_name, name.attrs[i] );
208 if (i < name.count - 1) lstrcatW( display_name, L"," );
212 done:
213 msiobj_release( &view->hdr );
214 if (name.attrs)
216 for (i = 0; i < name.count; i++) msi_free( name.attrs[i] );
217 msi_free( name.attrs );
219 return display_name;
222 static BOOL is_assembly_installed( IAssemblyCache *cache, const WCHAR *display_name )
224 HRESULT hr;
225 ASSEMBLY_INFO info;
227 if (!cache) return FALSE;
229 memset( &info, 0, sizeof(info) );
230 info.cbAssemblyInfo = sizeof(info);
231 hr = IAssemblyCache_QueryAssemblyInfo( cache, 0, display_name, &info );
232 if (hr == S_OK /* sxs version */ || hr == E_NOT_SUFFICIENT_BUFFER)
234 return (info.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
236 TRACE("QueryAssemblyInfo returned 0x%08x\n", hr);
237 return FALSE;
240 WCHAR *msi_get_assembly_path( MSIPACKAGE *package, const WCHAR *displayname )
242 HRESULT hr;
243 ASSEMBLY_INFO info;
244 IAssemblyCache *cache = package->cache_net[CLR_VERSION_V40];
246 if (!cache) return NULL;
248 memset( &info, 0, sizeof(info) );
249 info.cbAssemblyInfo = sizeof(info);
250 hr = IAssemblyCache_QueryAssemblyInfo( cache, 0, displayname, &info );
251 if (hr != E_NOT_SUFFICIENT_BUFFER) return NULL;
253 if (!(info.pszCurrentAssemblyPathBuf = msi_alloc( info.cchBuf * sizeof(WCHAR) ))) return NULL;
255 hr = IAssemblyCache_QueryAssemblyInfo( cache, 0, displayname, &info );
256 if (FAILED( hr ))
258 msi_free( info.pszCurrentAssemblyPathBuf );
259 return NULL;
261 TRACE("returning %s\n", debugstr_w(info.pszCurrentAssemblyPathBuf));
262 return info.pszCurrentAssemblyPathBuf;
265 IAssemblyEnum *msi_create_assembly_enum( MSIPACKAGE *package, const WCHAR *displayname )
267 HRESULT hr;
268 IAssemblyName *name;
269 IAssemblyEnum *ret;
270 WCHAR *str;
271 UINT len = 0;
273 if (!package->pCreateAssemblyNameObject || !package->pCreateAssemblyEnum) return NULL;
275 hr = package->pCreateAssemblyNameObject( &name, displayname, CANOF_PARSE_DISPLAY_NAME, NULL );
276 if (FAILED( hr )) return NULL;
278 hr = IAssemblyName_GetName( name, &len, NULL );
279 if (hr != E_NOT_SUFFICIENT_BUFFER || !(str = msi_alloc( len * sizeof(WCHAR) )))
281 IAssemblyName_Release( name );
282 return NULL;
285 hr = IAssemblyName_GetName( name, &len, str );
286 IAssemblyName_Release( name );
287 if (FAILED( hr ))
289 msi_free( str );
290 return NULL;
293 hr = package->pCreateAssemblyNameObject( &name, str, 0, NULL );
294 msi_free( str );
295 if (FAILED( hr )) return NULL;
297 hr = package->pCreateAssemblyEnum( &ret, NULL, name, ASM_CACHE_GAC, NULL );
298 IAssemblyName_Release( name );
299 if (FAILED( hr )) return NULL;
301 return ret;
304 static const WCHAR *clr_version[] =
306 L"v1.0.3705",
307 L"v1.2.4322",
308 L"v2.0.50727",
309 L"v4.0.30319"
312 static const WCHAR *get_clr_version_str( enum clr_version version )
314 if (version >= ARRAY_SIZE( clr_version )) return L"unknown";
315 return clr_version[version];
318 /* assembly caches must be initialized */
319 MSIASSEMBLY *msi_load_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
321 MSIRECORD *rec;
322 MSIASSEMBLY *a;
324 if (!(rec = get_assembly_record( package, comp->Component ))) return NULL;
325 if (!(a = msi_alloc_zero( sizeof(MSIASSEMBLY) )))
327 msiobj_release( &rec->hdr );
328 return NULL;
330 a->feature = strdupW( MSI_RecordGetString( rec, 2 ) );
331 TRACE("feature %s\n", debugstr_w(a->feature));
333 a->manifest = strdupW( MSI_RecordGetString( rec, 3 ) );
334 TRACE("manifest %s\n", debugstr_w(a->manifest));
336 a->application = strdupW( MSI_RecordGetString( rec, 4 ) );
337 TRACE("application %s\n", debugstr_w(a->application));
339 a->attributes = MSI_RecordGetInteger( rec, 5 );
340 TRACE("attributes %u\n", a->attributes);
342 if (!(a->display_name = get_assembly_display_name( package->db, comp->Component, a )))
344 WARN("can't get display name\n");
345 msiobj_release( &rec->hdr );
346 msi_free( a->feature );
347 msi_free( a->manifest );
348 msi_free( a->application );
349 msi_free( a );
350 return NULL;
352 TRACE("display name %s\n", debugstr_w(a->display_name));
354 if (a->application)
356 /* We can't check the manifest here because the target path may still change.
357 So we assume that the assembly is not installed and lean on the InstallFiles
358 action to determine which files need to be installed.
360 a->installed = FALSE;
362 else
364 if (a->attributes == msidbAssemblyAttributesWin32)
365 a->installed = is_assembly_installed( package->cache_sxs, a->display_name );
366 else
368 UINT i;
369 for (i = 0; i < CLR_VERSION_MAX; i++)
371 a->clr_version[i] = is_assembly_installed( package->cache_net[i], a->display_name );
372 if (a->clr_version[i])
374 TRACE("runtime version %s\n", debugstr_w(get_clr_version_str( i )));
375 a->installed = TRUE;
376 break;
381 TRACE("assembly is %s\n", a->installed ? "installed" : "not installed");
382 msiobj_release( &rec->hdr );
383 return a;
386 static enum clr_version get_clr_version( MSIPACKAGE *package, const WCHAR *filename )
388 DWORD len;
389 HRESULT hr;
390 enum clr_version version = CLR_VERSION_V11;
391 WCHAR *strW;
393 if (!package->pGetFileVersion) return CLR_VERSION_V10;
395 hr = package->pGetFileVersion( filename, NULL, 0, &len );
396 if (hr != E_NOT_SUFFICIENT_BUFFER) return CLR_VERSION_V11;
397 if ((strW = msi_alloc( len * sizeof(WCHAR) )))
399 hr = package->pGetFileVersion( filename, strW, len, &len );
400 if (hr == S_OK)
402 UINT i;
403 for (i = 0; i < CLR_VERSION_MAX; i++)
404 if (!wcscmp( strW, clr_version[i] )) version = i;
406 msi_free( strW );
408 return version;
411 UINT msi_install_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
413 HRESULT hr;
414 const WCHAR *manifest;
415 IAssemblyCache *cache;
416 MSIASSEMBLY *assembly = comp->assembly;
417 MSIFEATURE *feature = NULL;
419 if (comp->assembly->feature)
420 feature = msi_get_loaded_feature( package, comp->assembly->feature );
422 if (assembly->application)
424 if (feature) feature->Action = INSTALLSTATE_LOCAL;
425 return ERROR_SUCCESS;
427 if (assembly->attributes == msidbAssemblyAttributesWin32)
429 if (!assembly->manifest)
431 WARN("no manifest\n");
432 return ERROR_FUNCTION_FAILED;
434 manifest = msi_get_loaded_file( package, assembly->manifest )->TargetPath;
435 cache = package->cache_sxs;
437 else
439 manifest = msi_get_loaded_file( package, comp->KeyPath )->TargetPath;
440 cache = package->cache_net[get_clr_version( package, manifest )];
441 if (!cache) return ERROR_SUCCESS;
443 TRACE("installing assembly %s\n", debugstr_w(manifest));
445 hr = IAssemblyCache_InstallAssembly( cache, 0, manifest, NULL );
446 if (hr != S_OK)
448 ERR("Failed to install assembly %s (0x%08x)\n", debugstr_w(manifest), hr);
449 return ERROR_FUNCTION_FAILED;
451 if (feature) feature->Action = INSTALLSTATE_LOCAL;
452 assembly->installed = TRUE;
453 return ERROR_SUCCESS;
456 UINT msi_uninstall_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
458 HRESULT hr;
459 IAssemblyCache *cache;
460 MSIASSEMBLY *assembly = comp->assembly;
461 MSIFEATURE *feature = NULL;
463 if (comp->assembly->feature)
464 feature = msi_get_loaded_feature( package, comp->assembly->feature );
466 if (assembly->application)
468 if (feature) feature->Action = INSTALLSTATE_ABSENT;
469 return ERROR_SUCCESS;
471 TRACE("removing %s\n", debugstr_w(assembly->display_name));
473 if (assembly->attributes == msidbAssemblyAttributesWin32)
475 cache = package->cache_sxs;
476 hr = IAssemblyCache_UninstallAssembly( cache, 0, assembly->display_name, NULL, NULL );
477 if (FAILED( hr )) WARN("failed to uninstall assembly 0x%08x\n", hr);
479 else
481 unsigned int i;
482 for (i = 0; i < CLR_VERSION_MAX; i++)
484 if (!assembly->clr_version[i]) continue;
485 cache = package->cache_net[i];
486 if (cache)
488 hr = IAssemblyCache_UninstallAssembly( cache, 0, assembly->display_name, NULL, NULL );
489 if (FAILED( hr )) WARN("failed to uninstall assembly 0x%08x\n", hr);
493 if (feature) feature->Action = INSTALLSTATE_ABSENT;
494 assembly->installed = FALSE;
495 return ERROR_SUCCESS;
498 static WCHAR *build_local_assembly_path( const WCHAR *filename )
500 UINT i;
501 WCHAR *ret;
503 if (!(ret = msi_alloc( (lstrlenW( filename ) + 1) * sizeof(WCHAR) )))
504 return NULL;
506 for (i = 0; filename[i]; i++)
508 if (filename[i] == '\\' || filename[i] == '/') ret[i] = '|';
509 else ret[i] = filename[i];
511 ret[i] = 0;
512 return ret;
515 static LONG open_assemblies_key( UINT context, BOOL win32, HKEY *hkey )
517 HKEY root;
518 const WCHAR *path;
520 if (context == MSIINSTALLCONTEXT_MACHINE)
522 root = HKEY_CLASSES_ROOT;
523 if (win32) path = L"Installer\\Win32Assemblies\\";
524 else path = L"Installer\\Assemblies\\";
526 else
528 root = HKEY_CURRENT_USER;
529 if (win32) path = L"Software\\Microsoft\\Installer\\Win32Assemblies\\";
530 else path = L"Software\\Microsoft\\Installer\\Assemblies\\";
532 return RegCreateKeyW( root, path, hkey );
535 static LONG open_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename, HKEY *hkey )
537 LONG res;
538 HKEY root;
539 WCHAR *path;
541 if (!(path = build_local_assembly_path( filename )))
542 return ERROR_OUTOFMEMORY;
544 if ((res = open_assemblies_key( context, win32, &root )))
546 msi_free( path );
547 return res;
549 res = RegCreateKeyW( root, path, hkey );
550 RegCloseKey( root );
551 msi_free( path );
552 return res;
555 static LONG delete_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename )
557 LONG res;
558 HKEY root;
559 WCHAR *path;
561 if (!(path = build_local_assembly_path( filename )))
562 return ERROR_OUTOFMEMORY;
564 if ((res = open_assemblies_key( context, win32, &root )))
566 msi_free( path );
567 return res;
569 res = RegDeleteKeyW( root, path );
570 RegCloseKey( root );
571 msi_free( path );
572 return res;
575 static LONG open_global_assembly_key( UINT context, BOOL win32, HKEY *hkey )
577 HKEY root;
578 const WCHAR *path;
580 if (context == MSIINSTALLCONTEXT_MACHINE)
582 root = HKEY_CLASSES_ROOT;
583 if (win32) path = L"Installer\\Win32Assemblies\\Global";
584 else path = L"Installer\\Assemblies\\Global";
586 else
588 root = HKEY_CURRENT_USER;
589 if (win32) path = L"Software\\Microsoft\\Installer\\Win32Assemblies\\Global";
590 else path = L"Software\\Microsoft\\Installer\\Assemblies\\Global";
592 return RegCreateKeyW( root, path, hkey );
595 UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
597 MSICOMPONENT *comp;
599 if (package->script == SCRIPT_NONE)
600 return msi_schedule_action(package, SCRIPT_INSTALL, L"MsiPublishAssemblies");
602 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
604 LONG res;
605 HKEY hkey;
606 GUID guid;
607 DWORD size;
608 WCHAR buffer[43];
609 MSIRECORD *uirow;
610 MSIASSEMBLY *assembly = comp->assembly;
611 BOOL win32;
613 if (!assembly || !comp->ComponentId) continue;
615 comp->Action = msi_get_component_action( package, comp );
616 if (comp->Action != INSTALLSTATE_LOCAL)
618 TRACE("component not scheduled for installation %s\n", debugstr_w(comp->Component));
619 continue;
621 TRACE("publishing %s\n", debugstr_w(comp->Component));
623 CLSIDFromString( package->ProductCode, &guid );
624 encode_base85_guid( &guid, buffer );
625 buffer[20] = '>';
626 CLSIDFromString( comp->ComponentId, &guid );
627 encode_base85_guid( &guid, buffer + 21 );
628 buffer[42] = 0;
630 win32 = assembly->attributes & msidbAssemblyAttributesWin32;
631 if (assembly->application)
633 MSIFILE *file = msi_get_loaded_file( package, assembly->application );
634 if (!file)
636 WARN("no matching file %s for local assembly\n", debugstr_w(assembly->application));
637 continue;
639 if ((res = open_local_assembly_key( package->Context, win32, file->TargetPath, &hkey )))
641 WARN("failed to open local assembly key %d\n", res);
642 return ERROR_FUNCTION_FAILED;
645 else
647 if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
649 WARN("failed to open global assembly key %d\n", res);
650 return ERROR_FUNCTION_FAILED;
653 size = sizeof(buffer);
654 if ((res = RegSetValueExW( hkey, assembly->display_name, 0, REG_MULTI_SZ, (const BYTE *)buffer, size )))
656 WARN("failed to set assembly value %d\n", res);
658 RegCloseKey( hkey );
660 uirow = MSI_CreateRecord( 2 );
661 MSI_RecordSetStringW( uirow, 2, assembly->display_name );
662 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
663 msiobj_release( &uirow->hdr );
665 return ERROR_SUCCESS;
668 UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
670 MSICOMPONENT *comp;
672 if (package->script == SCRIPT_NONE)
673 return msi_schedule_action(package, SCRIPT_INSTALL, L"MsiUnpublishAssemblies");
675 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
677 LONG res;
678 MSIRECORD *uirow;
679 MSIASSEMBLY *assembly = comp->assembly;
680 BOOL win32;
682 if (!assembly || !comp->ComponentId) continue;
684 comp->Action = msi_get_component_action( package, comp );
685 if (comp->Action != INSTALLSTATE_ABSENT)
687 TRACE("component not scheduled for removal %s\n", debugstr_w(comp->Component));
688 continue;
690 TRACE("unpublishing %s\n", debugstr_w(comp->Component));
692 win32 = assembly->attributes & msidbAssemblyAttributesWin32;
693 if (assembly->application)
695 MSIFILE *file = msi_get_loaded_file( package, assembly->application );
696 if (!file)
698 WARN("no matching file %s for local assembly\n", debugstr_w(assembly->application));
699 continue;
701 if ((res = delete_local_assembly_key( package->Context, win32, file->TargetPath )))
702 WARN("failed to delete local assembly key %d\n", res);
704 else
706 HKEY hkey;
707 if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
708 WARN("failed to delete global assembly key %d\n", res);
709 else
711 if ((res = RegDeleteValueW( hkey, assembly->display_name )))
712 WARN("failed to delete global assembly value %d\n", res);
713 RegCloseKey( hkey );
717 uirow = MSI_CreateRecord( 2 );
718 MSI_RecordSetStringW( uirow, 2, assembly->display_name );
719 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
720 msiobj_release( &uirow->hdr );
722 return ERROR_SUCCESS;