strmbase: do not lock in BaseOutputPinImpl_GetDeliveryBuffer the MemInputPin will...
[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     if (name.attrs)
251     {
252         for (i = 0; i < name.count; i++) msi_free( name.attrs[i] );
253         msi_free( name.attrs );
254     }
255     return display_name;
256 }
257
258 static BOOL is_assembly_installed( IAssemblyCache *cache, const WCHAR *display_name )
259 {
260     HRESULT hr;
261     ASSEMBLY_INFO info;
262
263     memset( &info, 0, sizeof(info) );
264     info.cbAssemblyInfo = sizeof(info);
265     hr = IAssemblyCache_QueryAssemblyInfo( cache, 0, display_name, &info );
266     if (hr == S_OK /* sxs version */ || hr == HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER ))
267     {
268         return (info.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
269     }
270     TRACE("QueryAssemblyInfo returned 0x%08x\n", hr);
271     return FALSE;
272 }
273
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};
278
279 static const WCHAR *clr_version[] =
280 {
281     clr_version_v10,
282     clr_version_v11,
283     clr_version_v20
284 };
285
286 static const WCHAR *get_clr_version_str( enum clr_version version )
287 {
288     if (version >= sizeof(clr_version)/sizeof(clr_version[0])) return clr_version_unknown;
289     return clr_version[version];
290 }
291
292 /* assembly caches must be initialized */
293 MSIASSEMBLY *msi_load_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
294 {
295     MSIRECORD *rec;
296     MSIASSEMBLY *a;
297
298     if (!(rec = get_assembly_record( package, comp->Component ))) return NULL;
299     if (!(a = msi_alloc_zero( sizeof(MSIASSEMBLY) )))
300     {
301         msiobj_release( &rec->hdr );
302         return NULL;
303     }
304     a->feature = strdupW( MSI_RecordGetString( rec, 2 ) );
305     TRACE("feature %s\n", debugstr_w(a->feature));
306
307     a->manifest = strdupW( MSI_RecordGetString( rec, 3 ) );
308     TRACE("manifest %s\n", debugstr_w(a->manifest));
309
310     a->application = strdupW( MSI_RecordGetString( rec, 4 ) );
311     TRACE("application %s\n", debugstr_w(a->application));
312
313     a->attributes = MSI_RecordGetInteger( rec, 5 );
314     TRACE("attributes %u\n", a->attributes);
315
316     if (!(a->display_name = get_assembly_display_name( package->db, comp->Component, a )))
317     {
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;
325     }
326     TRACE("display name %s\n", debugstr_w(a->display_name));
327
328     if (a->application)
329     {
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.
333          */
334         a->installed = FALSE;
335     }
336     else
337     {
338         if (a->attributes == msidbAssemblyAttributesWin32)
339             a->installed = is_assembly_installed( package->cache_sxs, a->display_name );
340         else
341         {
342             UINT i;
343             for (i = 0; i < CLR_VERSION_MAX; i++)
344             {
345                 a->clr_version[i] = is_assembly_installed( package->cache_net[i], a->display_name );
346                 if (a->clr_version[i])
347                 {
348                     TRACE("runtime version %s\n", debugstr_w(get_clr_version_str( i )));
349                     a->installed = TRUE;
350                 }
351             }
352         }
353     }
354     TRACE("assembly is %s\n", a->installed ? "installed" : "not installed");
355     msiobj_release( &rec->hdr );
356     return a;
357 }
358
359 static enum clr_version get_clr_version( const WCHAR *filename )
360 {
361     DWORD len;
362     HRESULT hr;
363     enum clr_version version = CLR_VERSION_V11;
364     WCHAR *strW;
365
366     if (!pGetFileVersion) return CLR_VERSION_V10;
367
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) )))
371     {
372         hr = pGetFileVersion( filename, strW, len, &len );
373         if (hr == S_OK)
374         {
375             UINT i;
376             for (i = 0; i < CLR_VERSION_MAX; i++)
377                 if (!strcmpW( strW, clr_version[i] )) version = i;
378         }
379         msi_free( strW );
380     }
381     return version;
382 }
383
384 UINT msi_install_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
385 {
386     HRESULT hr;
387     const WCHAR *manifest;
388     IAssemblyCache *cache;
389     MSIASSEMBLY *assembly = comp->assembly;
390     MSIFEATURE *feature = NULL;
391
392     if (comp->assembly->feature)
393         feature = msi_get_loaded_feature( package, comp->assembly->feature );
394
395     if (assembly->application)
396     {
397         if (feature) feature->Action = INSTALLSTATE_LOCAL;
398         return ERROR_SUCCESS;
399     }
400     if (assembly->attributes == msidbAssemblyAttributesWin32)
401     {
402         if (!assembly->manifest)
403         {
404             WARN("no manifest\n");
405             return ERROR_FUNCTION_FAILED;
406         }
407         manifest = msi_get_loaded_file( package, assembly->manifest )->TargetPath;
408         cache = package->cache_sxs;
409     }
410     else
411     {
412         manifest = msi_get_loaded_file( package, comp->KeyPath )->TargetPath;
413         cache = package->cache_net[get_clr_version( manifest )];
414     }
415     TRACE("installing assembly %s\n", debugstr_w(manifest));
416
417     hr = IAssemblyCache_InstallAssembly( cache, 0, manifest, NULL );
418     if (hr != S_OK)
419     {
420         ERR("Failed to install assembly %s (0x%08x)\n", debugstr_w(manifest), hr);
421         return ERROR_FUNCTION_FAILED;
422     }
423     if (feature) feature->Action = INSTALLSTATE_LOCAL;
424     assembly->installed = TRUE;
425     return ERROR_SUCCESS;
426 }
427
428 UINT msi_uninstall_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
429 {
430     HRESULT hr;
431     IAssemblyCache *cache;
432     MSIASSEMBLY *assembly = comp->assembly;
433     MSIFEATURE *feature = NULL;
434
435     if (comp->assembly->feature)
436         feature = msi_get_loaded_feature( package, comp->assembly->feature );
437
438     if (assembly->application)
439     {
440         if (feature) feature->Action = INSTALLSTATE_ABSENT;
441         return ERROR_SUCCESS;
442     }
443     TRACE("removing %s\n", debugstr_w(assembly->display_name));
444
445     if (assembly->attributes == msidbAssemblyAttributesWin32)
446     {
447         cache = package->cache_sxs;
448         hr = IAssemblyCache_UninstallAssembly( cache, 0, assembly->display_name, NULL, NULL );
449         if (FAILED( hr )) WARN("failed to uninstall assembly 0x%08x\n", hr);
450     }
451     else
452     {
453         unsigned int i;
454         for (i = 0; i < CLR_VERSION_MAX; i++)
455         {
456             if (!assembly->clr_version[i]) continue;
457             cache = package->cache_net[i];
458             hr = IAssemblyCache_UninstallAssembly( cache, 0, assembly->display_name, NULL, NULL );
459             if (FAILED( hr )) WARN("failed to uninstall assembly 0x%08x\n", hr);
460         }
461     }
462     if (feature) feature->Action = INSTALLSTATE_ABSENT;
463     assembly->installed = FALSE;
464     return ERROR_SUCCESS;
465 }
466
467 static WCHAR *build_local_assembly_path( const WCHAR *filename )
468 {
469     UINT i;
470     WCHAR *ret;
471
472     if (!(ret = msi_alloc( (strlenW( filename ) + 1) * sizeof(WCHAR) )))
473         return NULL;
474
475     for (i = 0; filename[i]; i++)
476     {
477         if (filename[i] == '\\' || filename[i] == '/') ret[i] = '|';
478         else ret[i] = filename[i];
479     }
480     ret[i] = 0;
481     return ret;
482 }
483
484 static LONG open_assemblies_key( UINT context, BOOL win32, HKEY *hkey )
485 {
486     static const WCHAR path_win32[] =
487         {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
488           '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};
489     static const WCHAR path_dotnet[] =
490         {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
491          'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0};
492     static const WCHAR classes_path_win32[] =
493         {'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};
494     static const WCHAR classes_path_dotnet[] =
495         {'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0};
496     HKEY root;
497     const WCHAR *path;
498
499     if (context == MSIINSTALLCONTEXT_MACHINE)
500     {
501         root = HKEY_CLASSES_ROOT;
502         if (win32) path = classes_path_win32;
503         else path = classes_path_dotnet;
504     }
505     else
506     {
507         root = HKEY_CURRENT_USER;
508         if (win32) path = path_win32;
509         else path = path_dotnet;
510     }
511     return RegCreateKeyW( root, path, hkey );
512 }
513
514 static LONG open_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename, HKEY *hkey )
515 {
516     LONG res;
517     HKEY root;
518     WCHAR *path;
519
520     if (!(path = build_local_assembly_path( filename )))
521         return ERROR_OUTOFMEMORY;
522
523     if ((res = open_assemblies_key( context, win32, &root )))
524     {
525         msi_free( path );
526         return res;
527     }
528     res = RegCreateKeyW( root, path, hkey );
529     RegCloseKey( root );
530     msi_free( path );
531     return res;
532 }
533
534 static LONG delete_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename )
535 {
536     LONG res;
537     HKEY root;
538     WCHAR *path;
539
540     if (!(path = build_local_assembly_path( filename )))
541         return ERROR_OUTOFMEMORY;
542
543     if ((res = open_assemblies_key( context, win32, &root )))
544     {
545         msi_free( path );
546         return res;
547     }
548     res = RegDeleteKeyW( root, path );
549     RegCloseKey( root );
550     msi_free( path );
551     return res;
552 }
553
554 static LONG open_global_assembly_key( UINT context, BOOL win32, HKEY *hkey )
555 {
556     static const WCHAR path_win32[] =
557         {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
558          'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',
559          'G','l','o','b','a','l',0};
560     static const WCHAR path_dotnet[] =
561         {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
562          'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',
563          'G','l','o','b','a','l',0};
564     static const WCHAR classes_path_win32[] =
565         {'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',
566          'G','l','o','b','a','l',0};
567     static const WCHAR classes_path_dotnet[] =
568         {'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};
569     HKEY root;
570     const WCHAR *path;
571
572     if (context == MSIINSTALLCONTEXT_MACHINE)
573     {
574         root = HKEY_CLASSES_ROOT;
575         if (win32) path = classes_path_win32;
576         else path = classes_path_dotnet;
577     }
578     else
579     {
580         root = HKEY_CURRENT_USER;
581         if (win32) path = path_win32;
582         else path = path_dotnet;
583     }
584     return RegCreateKeyW( root, path, hkey );
585 }
586
587 UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
588 {
589     MSICOMPONENT *comp;
590
591     LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
592     {
593         LONG res;
594         HKEY hkey;
595         GUID guid;
596         DWORD size;
597         WCHAR buffer[43];
598         MSIRECORD *uirow;
599         MSIASSEMBLY *assembly = comp->assembly;
600         BOOL win32;
601
602         if (!assembly || !comp->ComponentId) continue;
603
604         comp->Action = msi_get_component_action( package, comp );
605         if (comp->Action != INSTALLSTATE_LOCAL)
606         {
607             TRACE("component not scheduled for installation %s\n", debugstr_w(comp->Component));
608             continue;
609         }
610         TRACE("publishing %s\n", debugstr_w(comp->Component));
611
612         CLSIDFromString( package->ProductCode, &guid );
613         encode_base85_guid( &guid, buffer );
614         buffer[20] = '>';
615         CLSIDFromString( comp->ComponentId, &guid );
616         encode_base85_guid( &guid, buffer + 21 );
617         buffer[42] = 0;
618
619         win32 = assembly->attributes & msidbAssemblyAttributesWin32;
620         if (assembly->application)
621         {
622             MSIFILE *file = msi_get_loaded_file( package, assembly->application );
623             if ((res = open_local_assembly_key( package->Context, win32, file->TargetPath, &hkey )))
624             {
625                 WARN("failed to open local assembly key %d\n", res);
626                 return ERROR_FUNCTION_FAILED;
627             }
628         }
629         else
630         {
631             if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
632             {
633                 WARN("failed to open global assembly key %d\n", res);
634                 return ERROR_FUNCTION_FAILED;
635             }
636         }
637         size = sizeof(buffer);
638         if ((res = RegSetValueExW( hkey, assembly->display_name, 0, REG_MULTI_SZ, (const BYTE *)buffer, size )))
639         {
640             WARN("failed to set assembly value %d\n", res);
641         }
642         RegCloseKey( hkey );
643
644         uirow = MSI_CreateRecord( 2 );
645         MSI_RecordSetStringW( uirow, 2, assembly->display_name );
646         msi_ui_actiondata( package, szMsiPublishAssemblies, uirow );
647         msiobj_release( &uirow->hdr );
648     }
649     return ERROR_SUCCESS;
650 }
651
652 UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
653 {
654     MSICOMPONENT *comp;
655
656     LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
657     {
658         LONG res;
659         MSIRECORD *uirow;
660         MSIASSEMBLY *assembly = comp->assembly;
661         BOOL win32;
662
663         if (!assembly || !comp->ComponentId) continue;
664
665         comp->Action = msi_get_component_action( package, comp );
666         if (comp->Action != INSTALLSTATE_ABSENT)
667         {
668             TRACE("component not scheduled for removal %s\n", debugstr_w(comp->Component));
669             continue;
670         }
671         TRACE("unpublishing %s\n", debugstr_w(comp->Component));
672
673         win32 = assembly->attributes & msidbAssemblyAttributesWin32;
674         if (assembly->application)
675         {
676             MSIFILE *file = msi_get_loaded_file( package, assembly->application );
677             if ((res = delete_local_assembly_key( package->Context, win32, file->TargetPath )))
678                 WARN("failed to delete local assembly key %d\n", res);
679         }
680         else
681         {
682             HKEY hkey;
683             if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
684                 WARN("failed to delete global assembly key %d\n", res);
685             else
686             {
687                 if ((res = RegDeleteValueW( hkey, assembly->display_name )))
688                     WARN("failed to delete global assembly value %d\n", res);
689                 RegCloseKey( hkey );
690             }
691         }
692
693         uirow = MSI_CreateRecord( 2 );
694         MSI_RecordSetStringW( uirow, 2, assembly->display_name );
695         msi_ui_actiondata( package, szMsiPublishAssemblies, uirow );
696         msiobj_release( &uirow->hdr );
697     }
698     return ERROR_SUCCESS;
699 }