xref: /reactos/dll/win32/msi/assembly.c (revision 1734f297)
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 "msipriv.h"
30 
31 WINE_DEFAULT_DEBUG_CHANNEL(msi);
32 
33 static BOOL load_fusion_dlls( MSIPACKAGE *package )
34 {
35     HRESULT (WINAPI *pLoadLibraryShim)( const WCHAR *, const WCHAR *, void *, HMODULE * );
36     WCHAR path[MAX_PATH];
37     DWORD len = GetSystemDirectoryW( path, MAX_PATH );
38 
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" )))
42     {
43         FreeLibrary( package->hmscoree );
44         package->hmscoree = NULL;
45         return TRUE;
46     }
47 
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 );
52 
53     return TRUE;
54 }
55 
56 BOOL msi_init_assembly_caches( MSIPACKAGE *package )
57 {
58     HRESULT (WINAPI *pCreateAssemblyCache)( IAssemblyCache **, DWORD );
59 
60     if (package->cache_sxs) return TRUE;
61     if (CreateAssemblyCache( &package->cache_sxs, 0 ) != S_OK) return FALSE;
62 
63     if (!load_fusion_dlls( package )) return FALSE;
64     package->pGetFileVersion = (void *)GetProcAddress( package->hmscoree, "GetFileVersion" ); /* missing from v1.0.3705 */
65 
66     if (package->hfusion10)
67     {
68         pCreateAssemblyCache = (void *)GetProcAddress( package->hfusion10, "CreateAssemblyCache" );
69         pCreateAssemblyCache( &package->cache_net[CLR_VERSION_V10], 0 );
70     }
71     if (package->hfusion11)
72     {
73         pCreateAssemblyCache = (void *)GetProcAddress( package->hfusion11, "CreateAssemblyCache" );
74         pCreateAssemblyCache( &package->cache_net[CLR_VERSION_V11], 0 );
75     }
76     if (package->hfusion20)
77     {
78         pCreateAssemblyCache = (void *)GetProcAddress( package->hfusion20, "CreateAssemblyCache" );
79         pCreateAssemblyCache( &package->cache_net[CLR_VERSION_V20], 0 );
80     }
81     if (package->hfusion40)
82     {
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" );
87     }
88 
89     return TRUE;
90 }
91 
92 void msi_destroy_assembly_caches( MSIPACKAGE *package )
93 {
94     UINT i;
95 
96     if (package->cache_sxs)
97     {
98         IAssemblyCache_Release( package->cache_sxs );
99         package->cache_sxs = NULL;
100     }
101     for (i = 0; i < CLR_VERSION_MAX; i++)
102     {
103         if (package->cache_net[i])
104         {
105             IAssemblyCache_Release( package->cache_net[i] );
106             package->cache_net[i] = NULL;
107         }
108     }
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;
122 }
123 
124 static MSIRECORD *get_assembly_record( MSIPACKAGE *package, const WCHAR *comp )
125 {
126     MSIQUERY *view;
127     MSIRECORD *rec;
128     UINT r;
129 
130     r = MSI_OpenQuery( package->db, &view, L"SELECT * FROM `MsiAssembly` WHERE `Component_` = '%s'", comp );
131     if (r != ERROR_SUCCESS)
132         return NULL;
133 
134     r = MSI_ViewExecute( view, NULL );
135     if (r != ERROR_SUCCESS)
136     {
137         msiobj_release( &view->hdr );
138         return NULL;
139     }
140     r = MSI_ViewFetch( view, &rec );
141     if (r != ERROR_SUCCESS)
142     {
143         msiobj_release( &view->hdr );
144         return NULL;
145     }
146     if (!MSI_RecordGetString( rec, 4 ))
147         TRACE("component is a global assembly\n");
148 
149     msiobj_release( &view->hdr );
150     return rec;
151 }
152 
153 struct assembly_name
154 {
155     DWORD   count;
156     UINT    index;
157     WCHAR **attrs;
158 };
159 
160 static UINT get_assembly_name_attribute( MSIRECORD *rec, LPVOID param )
161 {
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 );
166 
167     if (!(name->attrs[name->index] = msi_alloc( len * sizeof(WCHAR) )))
168         return ERROR_OUTOFMEMORY;
169 
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;
173 }
174 
175 static WCHAR *get_assembly_display_name( MSIDATABASE *db, const WCHAR *comp, MSIASSEMBLY *assembly )
176 {
177     struct assembly_name name;
178     WCHAR *display_name = NULL;
179     MSIQUERY *view;
180     UINT i, r;
181     int len;
182 
183     r = MSI_OpenQuery( db, &view, L"SELECT * FROM `MsiAssemblyName` WHERE `Component_` = '%s'", comp );
184     if (r != ERROR_SUCCESS)
185         return NULL;
186 
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;
192 
193     name.attrs = msi_alloc( name.count * sizeof(WCHAR *) );
194     if (!name.attrs) goto done;
195 
196     MSI_IterateRecords( view, NULL, get_assembly_name_attribute, &name );
197 
198     len = 0;
199     for (i = 0; i < name.count; i++) len += lstrlenW( name.attrs[i] ) + 1;
200 
201     display_name = msi_alloc( (len + 1) * sizeof(WCHAR) );
202     if (display_name)
203     {
204         display_name[0] = 0;
205         for (i = 0; i < name.count; i++)
206         {
207             lstrcatW( display_name, name.attrs[i] );
208             if (i < name.count - 1) lstrcatW( display_name, L"," );
209         }
210     }
211 
212 done:
213     msiobj_release( &view->hdr );
214     if (name.attrs)
215     {
216         for (i = 0; i < name.count; i++) msi_free( name.attrs[i] );
217         msi_free( name.attrs );
218     }
219     return display_name;
220 }
221 
222 static BOOL is_assembly_installed( IAssemblyCache *cache, const WCHAR *display_name )
223 {
224     HRESULT hr;
225     ASSEMBLY_INFO info;
226 
227     if (!cache) return FALSE;
228 
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)
233     {
234         return (info.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
235     }
236     TRACE( "QueryAssemblyInfo returned %#lx\n", hr );
237     return FALSE;
238 }
239 
240 WCHAR *msi_get_assembly_path( MSIPACKAGE *package, const WCHAR *displayname )
241 {
242     HRESULT hr;
243     ASSEMBLY_INFO info;
244     IAssemblyCache *cache = package->cache_net[CLR_VERSION_V40];
245 
246     if (!cache) return NULL;
247 
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;
252 
253     if (!(info.pszCurrentAssemblyPathBuf = msi_alloc( info.cchBuf * sizeof(WCHAR) ))) return NULL;
254 
255     hr = IAssemblyCache_QueryAssemblyInfo( cache, 0, displayname, &info );
256     if (FAILED( hr ))
257     {
258         msi_free( info.pszCurrentAssemblyPathBuf );
259         return NULL;
260     }
261     TRACE("returning %s\n", debugstr_w(info.pszCurrentAssemblyPathBuf));
262     return info.pszCurrentAssemblyPathBuf;
263 }
264 
265 IAssemblyEnum *msi_create_assembly_enum( MSIPACKAGE *package, const WCHAR *displayname )
266 {
267     HRESULT hr;
268     IAssemblyName *name;
269     IAssemblyEnum *ret;
270     WCHAR *str;
271     DWORD len = 0;
272 
273     if (!package->pCreateAssemblyNameObject || !package->pCreateAssemblyEnum) return NULL;
274 
275     hr = package->pCreateAssemblyNameObject( &name, displayname, CANOF_PARSE_DISPLAY_NAME, NULL );
276     if (FAILED( hr )) return NULL;
277 
278     hr = IAssemblyName_GetName( name, &len, NULL );
279     if (hr != E_NOT_SUFFICIENT_BUFFER || !(str = msi_alloc( len * sizeof(WCHAR) )))
280     {
281         IAssemblyName_Release( name );
282         return NULL;
283     }
284 
285     hr = IAssemblyName_GetName( name, &len, str );
286     IAssemblyName_Release( name );
287     if (FAILED( hr ))
288     {
289         msi_free( str );
290         return NULL;
291     }
292 
293     hr = package->pCreateAssemblyNameObject( &name, str, 0, NULL );
294     msi_free( str );
295     if (FAILED( hr )) return NULL;
296 
297     hr = package->pCreateAssemblyEnum( &ret, NULL, name, ASM_CACHE_GAC, NULL );
298     IAssemblyName_Release( name );
299     if (FAILED( hr )) return NULL;
300 
301     return ret;
302 }
303 
304 static const WCHAR *clr_version[] =
305 {
306     L"v1.0.3705",
307     L"v1.2.4322",
308     L"v2.0.50727",
309     L"v4.0.30319"
310 };
311 
312 static const WCHAR *get_clr_version_str( enum clr_version version )
313 {
314     if (version >= ARRAY_SIZE( clr_version )) return L"unknown";
315     return clr_version[version];
316 }
317 
318 /* assembly caches must be initialized */
319 MSIASSEMBLY *msi_load_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
320 {
321     MSIRECORD *rec;
322     MSIASSEMBLY *a;
323 
324     if (!(rec = get_assembly_record( package, comp->Component ))) return NULL;
325     if (!(a = msi_alloc_zero( sizeof(MSIASSEMBLY) )))
326     {
327         msiobj_release( &rec->hdr );
328         return NULL;
329     }
330     a->feature = strdupW( MSI_RecordGetString( rec, 2 ) );
331     TRACE("feature %s\n", debugstr_w(a->feature));
332 
333     a->manifest = strdupW( MSI_RecordGetString( rec, 3 ) );
334     TRACE("manifest %s\n", debugstr_w(a->manifest));
335 
336     a->application = strdupW( MSI_RecordGetString( rec, 4 ) );
337     TRACE("application %s\n", debugstr_w(a->application));
338 
339     a->attributes = MSI_RecordGetInteger( rec, 5 );
340     TRACE( "attributes %lu\n", a->attributes );
341 
342     if (!(a->display_name = get_assembly_display_name( package->db, comp->Component, a )))
343     {
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;
351     }
352     TRACE("display name %s\n", debugstr_w(a->display_name));
353 
354     if (a->application)
355     {
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.
359          */
360         a->installed = FALSE;
361     }
362     else
363     {
364         if (a->attributes == msidbAssemblyAttributesWin32)
365             a->installed = is_assembly_installed( package->cache_sxs, a->display_name );
366         else
367         {
368             UINT i;
369             for (i = 0; i < CLR_VERSION_MAX; i++)
370             {
371                 a->clr_version[i] = is_assembly_installed( package->cache_net[i], a->display_name );
372                 if (a->clr_version[i])
373                 {
374                     TRACE("runtime version %s\n", debugstr_w(get_clr_version_str( i )));
375                     a->installed = TRUE;
376                     break;
377                 }
378             }
379         }
380     }
381     TRACE("assembly is %s\n", a->installed ? "installed" : "not installed");
382     msiobj_release( &rec->hdr );
383     return a;
384 }
385 
386 static enum clr_version get_clr_version( MSIPACKAGE *package, const WCHAR *filename )
387 {
388     DWORD len;
389     HRESULT hr;
390     enum clr_version version = CLR_VERSION_V11;
391     WCHAR *strW;
392 
393     if (!package->pGetFileVersion) return CLR_VERSION_V10;
394 
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) )))
398     {
399         hr = package->pGetFileVersion( filename, strW, len, &len );
400         if (hr == S_OK)
401         {
402             UINT i;
403             for (i = 0; i < CLR_VERSION_MAX; i++)
404                 if (!wcscmp( strW, clr_version[i] )) version = i;
405         }
406         msi_free( strW );
407     }
408     return version;
409 }
410 
411 UINT msi_install_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
412 {
413     HRESULT hr;
414     const WCHAR *manifest;
415     IAssemblyCache *cache;
416     MSIASSEMBLY *assembly = comp->assembly;
417     MSIFEATURE *feature = NULL;
418 
419     if (comp->assembly->feature)
420         feature = msi_get_loaded_feature( package, comp->assembly->feature );
421 
422     if (assembly->application)
423     {
424         if (feature) feature->Action = INSTALLSTATE_LOCAL;
425         return ERROR_SUCCESS;
426     }
427     if (assembly->attributes == msidbAssemblyAttributesWin32)
428     {
429         if (!assembly->manifest)
430         {
431             WARN("no manifest\n");
432             return ERROR_FUNCTION_FAILED;
433         }
434         manifest = msi_get_loaded_file( package, assembly->manifest )->TargetPath;
435         cache = package->cache_sxs;
436     }
437     else
438     {
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;
442     }
443     TRACE("installing assembly %s\n", debugstr_w(manifest));
444 
445     hr = IAssemblyCache_InstallAssembly( cache, 0, manifest, NULL );
446     if (hr != S_OK)
447     {
448         ERR( "failed to install assembly %s (%#lx)\n", debugstr_w(manifest), hr );
449         return ERROR_FUNCTION_FAILED;
450     }
451     if (feature) feature->Action = INSTALLSTATE_LOCAL;
452     assembly->installed = TRUE;
453     return ERROR_SUCCESS;
454 }
455 
456 UINT msi_uninstall_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
457 {
458     HRESULT hr;
459     IAssemblyCache *cache;
460     MSIASSEMBLY *assembly = comp->assembly;
461     MSIFEATURE *feature = NULL;
462 
463     if (comp->assembly->feature)
464         feature = msi_get_loaded_feature( package, comp->assembly->feature );
465 
466     if (assembly->application)
467     {
468         if (feature) feature->Action = INSTALLSTATE_ABSENT;
469         return ERROR_SUCCESS;
470     }
471     TRACE("removing %s\n", debugstr_w(assembly->display_name));
472 
473     if (assembly->attributes == msidbAssemblyAttributesWin32)
474     {
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 %#lx\n", hr );
478     }
479     else
480     {
481         unsigned int i;
482         for (i = 0; i < CLR_VERSION_MAX; i++)
483         {
484             if (!assembly->clr_version[i]) continue;
485             cache = package->cache_net[i];
486             if (cache)
487             {
488                 hr = IAssemblyCache_UninstallAssembly( cache, 0, assembly->display_name, NULL, NULL );
489                 if (FAILED( hr )) WARN( "failed to uninstall assembly %#lx\n", hr );
490             }
491         }
492     }
493     if (feature) feature->Action = INSTALLSTATE_ABSENT;
494     assembly->installed = FALSE;
495     return ERROR_SUCCESS;
496 }
497 
498 static WCHAR *build_local_assembly_path( const WCHAR *filename )
499 {
500     UINT i;
501     WCHAR *ret;
502 
503     if (!(ret = msi_alloc( (lstrlenW( filename ) + 1) * sizeof(WCHAR) )))
504         return NULL;
505 
506     for (i = 0; filename[i]; i++)
507     {
508         if (filename[i] == '\\' || filename[i] == '/') ret[i] = '|';
509         else ret[i] = filename[i];
510     }
511     ret[i] = 0;
512     return ret;
513 }
514 
515 static LONG open_assemblies_key( UINT context, BOOL win32, HKEY *hkey )
516 {
517     HKEY root;
518     const WCHAR *path;
519 
520     if (context == MSIINSTALLCONTEXT_MACHINE)
521     {
522         root = HKEY_CLASSES_ROOT;
523         if (win32) path = L"Installer\\Win32Assemblies\\";
524         else path = L"Installer\\Assemblies\\";
525     }
526     else
527     {
528         root = HKEY_CURRENT_USER;
529         if (win32) path = L"Software\\Microsoft\\Installer\\Win32Assemblies\\";
530         else path = L"Software\\Microsoft\\Installer\\Assemblies\\";
531     }
532     return RegCreateKeyW( root, path, hkey );
533 }
534 
535 static LONG open_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename, HKEY *hkey )
536 {
537     LONG res;
538     HKEY root;
539     WCHAR *path;
540 
541     if (!(path = build_local_assembly_path( filename )))
542         return ERROR_OUTOFMEMORY;
543 
544     if ((res = open_assemblies_key( context, win32, &root )))
545     {
546         msi_free( path );
547         return res;
548     }
549     res = RegCreateKeyW( root, path, hkey );
550     RegCloseKey( root );
551     msi_free( path );
552     return res;
553 }
554 
555 static LONG delete_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename )
556 {
557     LONG res;
558     HKEY root;
559     WCHAR *path;
560 
561     if (!(path = build_local_assembly_path( filename )))
562         return ERROR_OUTOFMEMORY;
563 
564     if ((res = open_assemblies_key( context, win32, &root )))
565     {
566         msi_free( path );
567         return res;
568     }
569     res = RegDeleteKeyW( root, path );
570     RegCloseKey( root );
571     msi_free( path );
572     return res;
573 }
574 
575 static LONG open_global_assembly_key( UINT context, BOOL win32, HKEY *hkey )
576 {
577     HKEY root;
578     const WCHAR *path;
579 
580     if (context == MSIINSTALLCONTEXT_MACHINE)
581     {
582         root = HKEY_CLASSES_ROOT;
583         if (win32) path = L"Installer\\Win32Assemblies\\Global";
584         else path = L"Installer\\Assemblies\\Global";
585     }
586     else
587     {
588         root = HKEY_CURRENT_USER;
589         if (win32) path = L"Software\\Microsoft\\Installer\\Win32Assemblies\\Global";
590         else path = L"Software\\Microsoft\\Installer\\Assemblies\\Global";
591     }
592     return RegCreateKeyW( root, path, hkey );
593 }
594 
595 UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
596 {
597     MSICOMPONENT *comp;
598 
599     if (package->script == SCRIPT_NONE)
600         return msi_schedule_action(package, SCRIPT_INSTALL, L"MsiPublishAssemblies");
601 
602     LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
603     {
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;
612 
613         if (!assembly || !comp->ComponentId) continue;
614 
615         comp->Action = msi_get_component_action( package, comp );
616         if (comp->Action != INSTALLSTATE_LOCAL)
617         {
618             TRACE("component not scheduled for installation %s\n", debugstr_w(comp->Component));
619             continue;
620         }
621         TRACE("publishing %s\n", debugstr_w(comp->Component));
622 
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;
629 
630         win32 = assembly->attributes & msidbAssemblyAttributesWin32;
631         if (assembly->application)
632         {
633             MSIFILE *file = msi_get_loaded_file( package, assembly->application );
634             if (!file)
635             {
636                 WARN("no matching file %s for local assembly\n", debugstr_w(assembly->application));
637                 continue;
638             }
639             if ((res = open_local_assembly_key( package->Context, win32, file->TargetPath, &hkey )))
640             {
641                 WARN( "failed to open local assembly key %ld\n", res );
642                 return ERROR_FUNCTION_FAILED;
643             }
644         }
645         else
646         {
647             if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
648             {
649                 WARN( "failed to open global assembly key %ld\n", res );
650                 return ERROR_FUNCTION_FAILED;
651             }
652         }
653         size = sizeof(buffer);
654         if ((res = RegSetValueExW( hkey, assembly->display_name, 0, REG_MULTI_SZ, (const BYTE *)buffer, size )))
655         {
656             WARN( "failed to set assembly value %ld\n", res );
657         }
658         RegCloseKey( hkey );
659 
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 );
664     }
665     return ERROR_SUCCESS;
666 }
667 
668 UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
669 {
670     MSICOMPONENT *comp;
671 
672     if (package->script == SCRIPT_NONE)
673         return msi_schedule_action(package, SCRIPT_INSTALL, L"MsiUnpublishAssemblies");
674 
675     LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
676     {
677         LONG res;
678         MSIRECORD *uirow;
679         MSIASSEMBLY *assembly = comp->assembly;
680         BOOL win32;
681 
682         if (!assembly || !comp->ComponentId) continue;
683 
684         comp->Action = msi_get_component_action( package, comp );
685         if (comp->Action != INSTALLSTATE_ABSENT)
686         {
687             TRACE("component not scheduled for removal %s\n", debugstr_w(comp->Component));
688             continue;
689         }
690         TRACE("unpublishing %s\n", debugstr_w(comp->Component));
691 
692         win32 = assembly->attributes & msidbAssemblyAttributesWin32;
693         if (assembly->application)
694         {
695             MSIFILE *file = msi_get_loaded_file( package, assembly->application );
696             if (!file)
697             {
698                 WARN("no matching file %s for local assembly\n", debugstr_w(assembly->application));
699                 continue;
700             }
701             if ((res = delete_local_assembly_key( package->Context, win32, file->TargetPath )))
702                 WARN( "failed to delete local assembly key %ld\n", res );
703         }
704         else
705         {
706             HKEY hkey;
707             if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
708                 WARN( "failed to delete global assembly key %ld\n", res );
709             else
710             {
711                 if ((res = RegDeleteValueW( hkey, assembly->display_name )))
712                     WARN( "failed to delete global assembly value %ld\n", res );
713                 RegCloseKey( hkey );
714             }
715         }
716 
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 );
721     }
722     return ERROR_SUCCESS;
723 }
724