d3d10.idl: Added missing D3D10_RESOURCE_MISC_FLAG values.
[wine] / dlls / msi / assembly.c
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 "wine/unicode.h"
30 #include "msipriv.h"
31
32 WINE_DEFAULT_DEBUG_CHANNEL(msi);
33
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 * );
40
41 static HMODULE hfusion10, hfusion11, hfusion20, hmscoree, hsxs;
42
43 static BOOL init_function_pointers( void )
44 {
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};
49
50     if (pCreateAssemblyCacheNet10 || pCreateAssemblyCacheNet11 || pCreateAssemblyCacheNet20) return TRUE;
51
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;
55
56     if (!pLoadLibraryShim( szFusion, szVersion10, NULL, &hfusion10 ))
57         pCreateAssemblyCacheNet10 = (void *)GetProcAddress( hfusion10, "CreateAssemblyCache" );
58
59     if (!pLoadLibraryShim( szFusion, szVersion11, NULL, &hfusion11 ))
60         pCreateAssemblyCacheNet11 = (void *)GetProcAddress( hfusion11, "CreateAssemblyCache" );
61
62     if (!pLoadLibraryShim( szFusion, szVersion20, NULL, &hfusion20 ))
63         pCreateAssemblyCacheNet20 = (void *)GetProcAddress( hfusion20, "CreateAssemblyCache" );
64
65     if (!pCreateAssemblyCacheNet10 && !pCreateAssemblyCacheNet11 && !pCreateAssemblyCacheNet20) goto error;
66
67     if (!(hsxs = LoadLibraryA( "sxs.dll" ))) goto error;
68     if (!(pCreateAssemblyCacheSxs = (void *)GetProcAddress( hsxs, "CreateAssemblyCache" ))) goto error;
69     return TRUE;
70
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;
80 }
81
82 BOOL msi_init_assembly_caches( MSIPACKAGE *package )
83 {
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;
89
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 );
93
94     if (package->cache_net[CLR_VERSION_V10] ||
95         package->cache_net[CLR_VERSION_V11] ||
96         package->cache_net[CLR_VERSION_V20])
97     {
98         return TRUE;
99     }
100     if (package->cache_net[CLR_VERSION_V10])
101     {
102         IAssemblyCache_Release( package->cache_net[CLR_VERSION_V10] );
103         package->cache_net[CLR_VERSION_V10] = NULL;
104     }
105     if (package->cache_net[CLR_VERSION_V11])
106     {
107         IAssemblyCache_Release( package->cache_net[CLR_VERSION_V11] );
108         package->cache_net[CLR_VERSION_V11] = NULL;
109     }
110     if (package->cache_net[CLR_VERSION_V20])
111     {
112         IAssemblyCache_Release( package->cache_net[CLR_VERSION_V20] );
113         package->cache_net[CLR_VERSION_V20] = NULL;
114     }
115     IAssemblyCache_Release( package->cache_sxs );
116     package->cache_sxs = NULL;
117     return FALSE;
118 }
119
120 void msi_destroy_assembly_caches( MSIPACKAGE *package )
121 {
122     UINT i;
123
124     for (i = 0; i < CLR_VERSION_MAX; i++)
125     {
126         if (package->cache_net[i])
127         {
128             IAssemblyCache_Release( package->cache_net[i] );
129             package->cache_net[i] = NULL;
130         }
131     }
132     if (package->cache_sxs)
133     {
134         IAssemblyCache_Release( package->cache_sxs );
135         package->cache_sxs = NULL;
136     }
137     pCreateAssemblyCacheNet10 = NULL;
138     pCreateAssemblyCacheNet11 = NULL;
139     pCreateAssemblyCacheNet20 = NULL;
140     FreeLibrary( hfusion10 );
141     FreeLibrary( hfusion11 );
142     FreeLibrary( hfusion20 );
143     FreeLibrary( hmscoree );
144     FreeLibrary( hsxs );
145 }
146
147 static MSIRECORD *get_assembly_record( MSIPACKAGE *package, const WCHAR *comp )
148 {
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;
157
158     r = MSI_OpenQuery( package->db, &view, query, comp );
159     if (r != ERROR_SUCCESS)
160         return NULL;
161
162     r = MSI_ViewExecute( view, NULL );
163     if (r != ERROR_SUCCESS)
164     {
165         msiobj_release( &view->hdr );
166         return NULL;
167     }
168     r = MSI_ViewFetch( view, &rec );
169     if (r != ERROR_SUCCESS)
170     {
171         msiobj_release( &view->hdr );
172         return NULL;
173     }
174     if (!MSI_RecordGetString( rec, 4 ))
175         TRACE("component is a global assembly\n");
176
177     msiobj_release( &view->hdr );
178     return rec;
179 }
180
181 struct assembly_name
182 {
183     UINT    count;
184     UINT    index;
185     WCHAR **attrs;
186 };
187
188 static UINT get_assembly_name_attribute( MSIRECORD *rec, LPVOID param )
189 {
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 );
196
197     if (!(name->attrs[name->index] = msi_alloc( len * sizeof(WCHAR) )))
198         return ERROR_OUTOFMEMORY;
199
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;
203 }
204
205 static WCHAR *get_assembly_display_name( MSIDATABASE *db, const WCHAR *comp, MSIASSEMBLY *assembly )
206 {
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;
218
219     r = MSI_OpenQuery( db, &view, queryW, comp );
220     if (r != ERROR_SUCCESS)
221         return NULL;
222
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;
228
229     name.attrs = msi_alloc( name.count * sizeof(WCHAR *) );
230     if (!name.attrs) goto done;
231
232     MSI_IterateRecords( view, NULL, get_assembly_name_attribute, &name );
233
234     len = 0;
235     for (i = 0; i < name.count; i++) len += strlenW( name.attrs[i] ) + 1;
236
237     display_name = msi_alloc( (len + 1) * sizeof(WCHAR) );
238     if (display_name)
239     {
240         display_name[0] = 0;
241         for (i = 0; i < name.count; i++)
242         {
243             strcatW( display_name, name.attrs[i] );
244             if (i < name.count - 1) strcatW( display_name, commaW );
245         }
246     }
247
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;
253 }
254
255 static BOOL is_assembly_installed( IAssemblyCache *cache, const WCHAR *display_name )
256 {
257     HRESULT hr;
258     ASSEMBLY_INFO info;
259
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 ))
264     {
265         TRACE("QueryAssemblyInfo returned 0x%08x\n", hr);
266         return FALSE;
267     }
268     return (info.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
269 }
270
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};
275
276 static const WCHAR *clr_version[] =
277 {
278     clr_version_v10,
279     clr_version_v11,
280     clr_version_v20
281 };
282
283 static const WCHAR *get_clr_version_str( enum clr_version version )
284 {
285     if (version >= sizeof(clr_version)/sizeof(clr_version[0])) return clr_version_unknown;
286     return clr_version[version];
287 }
288
289 /* assembly caches must be initialized */
290 MSIASSEMBLY *msi_load_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
291 {
292     MSIRECORD *rec;
293     MSIASSEMBLY *a;
294
295     if (!(rec = get_assembly_record( package, comp->Component ))) return NULL;
296     if (!(a = msi_alloc_zero( sizeof(MSIASSEMBLY) )))
297     {
298         msiobj_release( &rec->hdr );
299         return NULL;
300     }
301     a->feature = strdupW( MSI_RecordGetString( rec, 2 ) );
302     TRACE("feature %s\n", debugstr_w(a->feature));
303
304     a->manifest = strdupW( MSI_RecordGetString( rec, 3 ) );
305     TRACE("manifest %s\n", debugstr_w(a->manifest));
306
307     a->application = strdupW( MSI_RecordGetString( rec, 4 ) );
308     TRACE("application %s\n", debugstr_w(a->application));
309
310     a->attributes = MSI_RecordGetInteger( rec, 5 );
311     TRACE("attributes %u\n", a->attributes);
312
313     if (!(a->display_name = get_assembly_display_name( package->db, comp->Component, a )))
314     {
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;
322     }
323     TRACE("display name %s\n", debugstr_w(a->display_name));
324
325     if (a->application)
326     {
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.
330          */
331         a->installed = FALSE;
332     }
333     else
334     {
335         if (a->attributes == msidbAssemblyAttributesWin32)
336             a->installed = is_assembly_installed( package->cache_sxs, a->display_name );
337         else
338         {
339             UINT i;
340             for (i = 0; i < CLR_VERSION_MAX; i++)
341             {
342                 a->clr_version[i] = is_assembly_installed( package->cache_net[i], a->display_name );
343                 if (a->clr_version[i])
344                 {
345                     TRACE("runtime version %s\n", debugstr_w(get_clr_version_str( i )));
346                     a->installed = TRUE;
347                 }
348             }
349         }
350     }
351     TRACE("assembly is %s\n", a->installed ? "installed" : "not installed");
352     msiobj_release( &rec->hdr );
353     return a;
354 }
355
356 static enum clr_version get_clr_version( const WCHAR *filename )
357 {
358     DWORD len;
359     HRESULT hr;
360     enum clr_version version = CLR_VERSION_V11;
361     WCHAR *strW;
362
363     if (!pGetFileVersion) return CLR_VERSION_V10;
364
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) )))
368     {
369         hr = pGetFileVersion( filename, strW, len, &len );
370         if (hr == S_OK)
371         {
372             UINT i;
373             for (i = 0; i < CLR_VERSION_MAX; i++)
374                 if (!strcmpW( strW, clr_version[i] )) version = i;
375         }
376         msi_free( strW );
377     }
378     return version;
379 }
380
381 UINT msi_install_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
382 {
383     HRESULT hr;
384     const WCHAR *manifest;
385     IAssemblyCache *cache;
386     MSIASSEMBLY *assembly = comp->assembly;
387     MSIFEATURE *feature = NULL;
388
389     if (comp->assembly->feature)
390         feature = msi_get_loaded_feature( package, comp->assembly->feature );
391
392     if (assembly->application)
393     {
394         if (feature) feature->Action = INSTALLSTATE_LOCAL;
395         return ERROR_SUCCESS;
396     }
397     if (assembly->attributes == msidbAssemblyAttributesWin32)
398     {
399         if (!assembly->manifest)
400         {
401             WARN("no manifest\n");
402             return ERROR_FUNCTION_FAILED;
403         }
404         manifest = msi_get_loaded_file( package, assembly->manifest )->TargetPath;
405         cache = package->cache_sxs;
406     }
407     else
408     {
409         manifest = msi_get_loaded_file( package, comp->KeyPath )->TargetPath;
410         cache = package->cache_net[get_clr_version( manifest )];
411     }
412     TRACE("installing assembly %s\n", debugstr_w(manifest));
413
414     hr = IAssemblyCache_InstallAssembly( cache, 0, manifest, NULL );
415     if (hr != S_OK)
416     {
417         ERR("Failed to install assembly %s (0x%08x)\n", debugstr_w(manifest), hr);
418         return ERROR_FUNCTION_FAILED;
419     }
420     if (feature) feature->Action = INSTALLSTATE_LOCAL;
421     assembly->installed = TRUE;
422     return ERROR_SUCCESS;
423 }
424
425 static WCHAR *build_local_assembly_path( const WCHAR *filename )
426 {
427     UINT i;
428     WCHAR *ret;
429
430     if (!(ret = msi_alloc( (strlenW( filename ) + 1) * sizeof(WCHAR) )))
431         return NULL;
432
433     for (i = 0; filename[i]; i++)
434     {
435         if (filename[i] == '\\' || filename[i] == '/') ret[i] = '|';
436         else ret[i] = filename[i];
437     }
438     ret[i] = 0;
439     return ret;
440 }
441
442 static LONG open_assemblies_key( UINT context, BOOL win32, HKEY *hkey )
443 {
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;
456
457     if (context == MSIINSTALLCONTEXT_MACHINE)
458     {
459         root = HKEY_CLASSES_ROOT;
460         if (win32) path = classes_path_win32;
461         else path = classes_path_dotnet;
462     }
463     else
464     {
465         root = HKEY_CURRENT_USER;
466         if (win32) path = path_win32;
467         else path = path_dotnet;
468     }
469     return RegCreateKeyW( root, path, hkey );
470 }
471
472 static LONG open_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename, HKEY *hkey )
473 {
474     LONG res;
475     HKEY root;
476     WCHAR *path;
477
478     if (!(path = build_local_assembly_path( filename )))
479         return ERROR_OUTOFMEMORY;
480
481     if ((res = open_assemblies_key( context, win32, &root )))
482     {
483         msi_free( path );
484         return res;
485     }
486     res = RegCreateKeyW( root, path, hkey );
487     RegCloseKey( root );
488     msi_free( path );
489     return res;
490 }
491
492 static LONG delete_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename )
493 {
494     LONG res;
495     HKEY root;
496     WCHAR *path;
497
498     if (!(path = build_local_assembly_path( filename )))
499         return ERROR_OUTOFMEMORY;
500
501     if ((res = open_assemblies_key( context, win32, &root )))
502     {
503         msi_free( path );
504         return res;
505     }
506     res = RegDeleteKeyW( root, path );
507     RegCloseKey( root );
508     msi_free( path );
509     return res;
510 }
511
512 static LONG open_global_assembly_key( UINT context, BOOL win32, HKEY *hkey )
513 {
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;
529
530     if (context == MSIINSTALLCONTEXT_MACHINE)
531     {
532         root = HKEY_CLASSES_ROOT;
533         if (win32) path = classes_path_win32;
534         else path = classes_path_dotnet;
535     }
536     else
537     {
538         root = HKEY_CURRENT_USER;
539         if (win32) path = path_win32;
540         else path = path_dotnet;
541     }
542     return RegCreateKeyW( root, path, hkey );
543 }
544
545 UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
546 {
547     MSICOMPONENT *comp;
548
549     LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
550     {
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;
559
560         if (!assembly || !comp->ComponentId) continue;
561
562         comp->Action = msi_get_component_action( package, comp );
563         if (comp->Action != INSTALLSTATE_LOCAL)
564         {
565             TRACE("component not scheduled for installation %s\n", debugstr_w(comp->Component));
566             continue;
567         }
568         TRACE("publishing %s\n", debugstr_w(comp->Component));
569
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;
576
577         win32 = assembly->attributes & msidbAssemblyAttributesWin32;
578         if (assembly->application)
579         {
580             MSIFILE *file = msi_get_loaded_file( package, assembly->application );
581             if ((res = open_local_assembly_key( package->Context, win32, file->TargetPath, &hkey )))
582             {
583                 WARN("failed to open local assembly key %d\n", res);
584                 return ERROR_FUNCTION_FAILED;
585             }
586         }
587         else
588         {
589             if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
590             {
591                 WARN("failed to open global assembly key %d\n", res);
592                 return ERROR_FUNCTION_FAILED;
593             }
594         }
595         size = sizeof(buffer);
596         if ((res = RegSetValueExW( hkey, assembly->display_name, 0, REG_MULTI_SZ, (const BYTE *)buffer, size )))
597         {
598             WARN("failed to set assembly value %d\n", res);
599         }
600         RegCloseKey( hkey );
601
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 );
606     }
607     return ERROR_SUCCESS;
608 }
609
610 UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
611 {
612     MSICOMPONENT *comp;
613
614     LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
615     {
616         LONG res;
617         MSIRECORD *uirow;
618         MSIASSEMBLY *assembly = comp->assembly;
619         BOOL win32;
620
621         if (!assembly || !comp->ComponentId) continue;
622
623         comp->Action = msi_get_component_action( package, comp );
624         if (comp->Action != INSTALLSTATE_ABSENT)
625         {
626             TRACE("component not scheduled for removal %s\n", debugstr_w(comp->Component));
627             continue;
628         }
629         TRACE("unpublishing %s\n", debugstr_w(comp->Component));
630
631         win32 = assembly->attributes & msidbAssemblyAttributesWin32;
632         if (assembly->application)
633         {
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);
637         }
638         else
639         {
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
644             {
645                 if ((res = RegDeleteValueW( hkey, assembly->display_name )))
646                     WARN("failed to delete global assembly value %d\n", res);
647                 RegCloseKey( hkey );
648             }
649         }
650
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 );
655     }
656     return ERROR_SUCCESS;
657 }