netstat: Initial implementation.
[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 *pCreateAssemblyCacheNet40)( IAssemblyCache **, DWORD );
38 static HRESULT (WINAPI *pCreateAssemblyCacheSxs)( IAssemblyCache **, DWORD );
39 static HRESULT (WINAPI *pLoadLibraryShim)( LPCWSTR, LPCWSTR, LPVOID, HMODULE * );
40 static HRESULT (WINAPI *pGetFileVersion)( LPCWSTR, LPWSTR, DWORD, DWORD * );
41
42 static HMODULE hfusion10, hfusion11, hfusion20, hfusion40, hmscoree, hsxs;
43
44 static BOOL init_function_pointers( void )
45 {
46     static const WCHAR szFusion[]    = {'f','u','s','i','o','n','.','d','l','l',0};
47     static const WCHAR szMscoree[]   = {'\\','m','s','c','o','r','e','e','.','d','l','l',0};
48     static const WCHAR szSxs[]       = {'s','x','s','.','d','l','l',0};
49     static const WCHAR szVersion10[] = {'v','1','.','0','.','3','7','0','5',0};
50     static const WCHAR szVersion11[] = {'v','1','.','1','.','4','3','2','2',0};
51     static const WCHAR szVersion20[] = {'v','2','.','0','.','5','0','7','2','7',0};
52     static const WCHAR szVersion40[] = {'v','4','.','0','.','3','0','3','1','9',0};
53     WCHAR path[MAX_PATH];
54     DWORD len = GetSystemDirectoryW( path, MAX_PATH );
55
56     if (!hsxs && !(hsxs = LoadLibraryW( szSxs ))) return FALSE;
57     if (!(pCreateAssemblyCacheSxs = (void *)GetProcAddress( hsxs, "CreateAssemblyCache" )))
58     {
59         FreeLibrary( hsxs );
60         hsxs = NULL;
61         return FALSE;
62     }
63     strcpyW( path + len, szMscoree );
64     if (hmscoree || !(hmscoree = LoadLibraryW( path ))) return TRUE;
65     pGetFileVersion = (void *)GetProcAddress( hmscoree, "GetFileVersion" ); /* missing from v1.0.3705 */
66     if (!(pLoadLibraryShim = (void *)GetProcAddress( hmscoree, "LoadLibraryShim" )))
67     {
68         FreeLibrary( hmscoree );
69         hmscoree = NULL;
70         return TRUE;
71     }
72     if (!pLoadLibraryShim( szFusion, szVersion10, NULL, &hfusion10 ))
73         pCreateAssemblyCacheNet10 = (void *)GetProcAddress( hfusion10, "CreateAssemblyCache" );
74
75     if (!pLoadLibraryShim( szFusion, szVersion11, NULL, &hfusion11 ))
76         pCreateAssemblyCacheNet11 = (void *)GetProcAddress( hfusion11, "CreateAssemblyCache" );
77
78     if (!pLoadLibraryShim( szFusion, szVersion20, NULL, &hfusion20 ))
79         pCreateAssemblyCacheNet20 = (void *)GetProcAddress( hfusion20, "CreateAssemblyCache" );
80
81     if (!pLoadLibraryShim( szFusion, szVersion40, NULL, &hfusion40 ))
82         pCreateAssemblyCacheNet40 = (void *)GetProcAddress( hfusion40, "CreateAssemblyCache" );
83
84     return TRUE;
85 }
86
87 BOOL msi_init_assembly_caches( MSIPACKAGE *package )
88 {
89     if (!init_function_pointers()) return FALSE;
90     if (pCreateAssemblyCacheSxs( &package->cache_sxs, 0 ) != S_OK) return FALSE;
91     if (pCreateAssemblyCacheNet10) pCreateAssemblyCacheNet10( &package->cache_net[CLR_VERSION_V10], 0 );
92     if (pCreateAssemblyCacheNet11) pCreateAssemblyCacheNet11( &package->cache_net[CLR_VERSION_V11], 0 );
93     if (pCreateAssemblyCacheNet20) pCreateAssemblyCacheNet20( &package->cache_net[CLR_VERSION_V20], 0 );
94     if (pCreateAssemblyCacheNet40) pCreateAssemblyCacheNet40( &package->cache_net[CLR_VERSION_V40], 0 );
95     return TRUE;
96 }
97
98 void msi_destroy_assembly_caches( MSIPACKAGE *package )
99 {
100     UINT i;
101
102     if (package->cache_sxs)
103     {
104         IAssemblyCache_Release( package->cache_sxs );
105         package->cache_sxs = NULL;
106     }
107     for (i = 0; i < CLR_VERSION_MAX; i++)
108     {
109         if (package->cache_net[i])
110         {
111             IAssemblyCache_Release( package->cache_net[i] );
112             package->cache_net[i] = NULL;
113         }
114     }
115     pCreateAssemblyCacheNet10 = NULL;
116     pCreateAssemblyCacheNet11 = NULL;
117     pCreateAssemblyCacheNet20 = NULL;
118     pCreateAssemblyCacheNet40 = NULL;
119     FreeLibrary( hfusion10 );
120     FreeLibrary( hfusion11 );
121     FreeLibrary( hfusion20 );
122     FreeLibrary( hfusion40 );
123     FreeLibrary( hmscoree );
124     FreeLibrary( hsxs );
125     hfusion10 = NULL;
126     hfusion11 = NULL;
127     hfusion20 = NULL;
128     hfusion40 = NULL;
129     hmscoree = NULL;
130     hsxs = NULL;
131 }
132
133 static MSIRECORD *get_assembly_record( MSIPACKAGE *package, const WCHAR *comp )
134 {
135     static const WCHAR query[] = {
136         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
137          '`','M','s','i','A','s','s','e','m','b','l','y','`',' ',
138          'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
139          ' ','=',' ','\'','%','s','\'',0};
140     MSIQUERY *view;
141     MSIRECORD *rec;
142     UINT r;
143
144     r = MSI_OpenQuery( package->db, &view, query, comp );
145     if (r != ERROR_SUCCESS)
146         return NULL;
147
148     r = MSI_ViewExecute( view, NULL );
149     if (r != ERROR_SUCCESS)
150     {
151         msiobj_release( &view->hdr );
152         return NULL;
153     }
154     r = MSI_ViewFetch( view, &rec );
155     if (r != ERROR_SUCCESS)
156     {
157         msiobj_release( &view->hdr );
158         return NULL;
159     }
160     if (!MSI_RecordGetString( rec, 4 ))
161         TRACE("component is a global assembly\n");
162
163     msiobj_release( &view->hdr );
164     return rec;
165 }
166
167 struct assembly_name
168 {
169     UINT    count;
170     UINT    index;
171     WCHAR **attrs;
172 };
173
174 static UINT get_assembly_name_attribute( MSIRECORD *rec, LPVOID param )
175 {
176     static const WCHAR fmtW[] = {'%','s','=','"','%','s','"',0};
177     static const WCHAR nameW[] = {'n','a','m','e',0};
178     struct assembly_name *name = param;
179     const WCHAR *attr = MSI_RecordGetString( rec, 2 );
180     const WCHAR *value = MSI_RecordGetString( rec, 3 );
181     int len = strlenW( fmtW ) + strlenW( attr ) + strlenW( value );
182
183     if (!(name->attrs[name->index] = msi_alloc( len * sizeof(WCHAR) )))
184         return ERROR_OUTOFMEMORY;
185
186     if (!strcmpiW( attr, nameW )) strcpyW( name->attrs[name->index++], value );
187     else sprintfW( name->attrs[name->index++], fmtW, attr, value );
188     return ERROR_SUCCESS;
189 }
190
191 static WCHAR *get_assembly_display_name( MSIDATABASE *db, const WCHAR *comp, MSIASSEMBLY *assembly )
192 {
193     static const WCHAR commaW[] = {',',0};
194     static const WCHAR queryW[] = {
195         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
196         '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
197         'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
198         ' ','=',' ','\'','%','s','\'',0};
199     struct assembly_name name;
200     WCHAR *display_name = NULL;
201     MSIQUERY *view;
202     UINT i, r;
203     int len;
204
205     r = MSI_OpenQuery( db, &view, queryW, comp );
206     if (r != ERROR_SUCCESS)
207         return NULL;
208
209     name.count = 0;
210     name.index = 0;
211     name.attrs = NULL;
212     MSI_IterateRecords( view, &name.count, NULL, NULL );
213     if (!name.count) goto done;
214
215     name.attrs = msi_alloc( name.count * sizeof(WCHAR *) );
216     if (!name.attrs) goto done;
217
218     MSI_IterateRecords( view, NULL, get_assembly_name_attribute, &name );
219
220     len = 0;
221     for (i = 0; i < name.count; i++) len += strlenW( name.attrs[i] ) + 1;
222
223     display_name = msi_alloc( (len + 1) * sizeof(WCHAR) );
224     if (display_name)
225     {
226         display_name[0] = 0;
227         for (i = 0; i < name.count; i++)
228         {
229             strcatW( display_name, name.attrs[i] );
230             if (i < name.count - 1) strcatW( display_name, commaW );
231         }
232     }
233
234 done:
235     msiobj_release( &view->hdr );
236     if (name.attrs)
237     {
238         for (i = 0; i < name.count; i++) msi_free( name.attrs[i] );
239         msi_free( name.attrs );
240     }
241     return display_name;
242 }
243
244 static BOOL is_assembly_installed( IAssemblyCache *cache, const WCHAR *display_name )
245 {
246     HRESULT hr;
247     ASSEMBLY_INFO info;
248
249     if (!cache) return FALSE;
250
251     memset( &info, 0, sizeof(info) );
252     info.cbAssemblyInfo = sizeof(info);
253     hr = IAssemblyCache_QueryAssemblyInfo( cache, 0, display_name, &info );
254     if (hr == S_OK /* sxs version */ || hr == HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER ))
255     {
256         return (info.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
257     }
258     TRACE("QueryAssemblyInfo returned 0x%08x\n", hr);
259     return FALSE;
260 }
261
262 static const WCHAR clr_version_v10[] = {'v','1','.','0','.','3','7','0','5',0};
263 static const WCHAR clr_version_v11[] = {'v','1','.','1','.','4','3','2','2',0};
264 static const WCHAR clr_version_v20[] = {'v','2','.','0','.','5','0','7','2','7',0};
265 static const WCHAR clr_version_v40[] = {'v','4','.','0','.','3','0','3','1','9',0};
266 static const WCHAR clr_version_unknown[] = {'u','n','k','n','o','w','n',0};
267
268 static const WCHAR *clr_version[] =
269 {
270     clr_version_v10,
271     clr_version_v11,
272     clr_version_v20,
273     clr_version_v40
274 };
275
276 static const WCHAR *get_clr_version_str( enum clr_version version )
277 {
278     if (version >= sizeof(clr_version)/sizeof(clr_version[0])) return clr_version_unknown;
279     return clr_version[version];
280 }
281
282 /* assembly caches must be initialized */
283 MSIASSEMBLY *msi_load_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
284 {
285     MSIRECORD *rec;
286     MSIASSEMBLY *a;
287
288     if (!(rec = get_assembly_record( package, comp->Component ))) return NULL;
289     if (!(a = msi_alloc_zero( sizeof(MSIASSEMBLY) )))
290     {
291         msiobj_release( &rec->hdr );
292         return NULL;
293     }
294     a->feature = strdupW( MSI_RecordGetString( rec, 2 ) );
295     TRACE("feature %s\n", debugstr_w(a->feature));
296
297     a->manifest = strdupW( MSI_RecordGetString( rec, 3 ) );
298     TRACE("manifest %s\n", debugstr_w(a->manifest));
299
300     a->application = strdupW( MSI_RecordGetString( rec, 4 ) );
301     TRACE("application %s\n", debugstr_w(a->application));
302
303     a->attributes = MSI_RecordGetInteger( rec, 5 );
304     TRACE("attributes %u\n", a->attributes);
305
306     if (!(a->display_name = get_assembly_display_name( package->db, comp->Component, a )))
307     {
308         WARN("can't get display name\n");
309         msiobj_release( &rec->hdr );
310         msi_free( a->feature );
311         msi_free( a->manifest );
312         msi_free( a->application );
313         msi_free( a );
314         return NULL;
315     }
316     TRACE("display name %s\n", debugstr_w(a->display_name));
317
318     if (a->application)
319     {
320         /* We can't check the manifest here because the target path may still change.
321            So we assume that the assembly is not installed and lean on the InstallFiles
322            action to determine which files need to be installed.
323          */
324         a->installed = FALSE;
325     }
326     else
327     {
328         if (a->attributes == msidbAssemblyAttributesWin32)
329             a->installed = is_assembly_installed( package->cache_sxs, a->display_name );
330         else
331         {
332             UINT i;
333             for (i = 0; i < CLR_VERSION_MAX; i++)
334             {
335                 a->clr_version[i] = is_assembly_installed( package->cache_net[i], a->display_name );
336                 if (a->clr_version[i])
337                 {
338                     TRACE("runtime version %s\n", debugstr_w(get_clr_version_str( i )));
339                     a->installed = TRUE;
340                     break;
341                 }
342             }
343         }
344     }
345     TRACE("assembly is %s\n", a->installed ? "installed" : "not installed");
346     msiobj_release( &rec->hdr );
347     return a;
348 }
349
350 static enum clr_version get_clr_version( const WCHAR *filename )
351 {
352     DWORD len;
353     HRESULT hr;
354     enum clr_version version = CLR_VERSION_V11;
355     WCHAR *strW;
356
357     if (!pGetFileVersion) return CLR_VERSION_V10;
358
359     hr = pGetFileVersion( filename, NULL, 0, &len );
360     if (hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) return CLR_VERSION_V11;
361     if ((strW = msi_alloc( len * sizeof(WCHAR) )))
362     {
363         hr = pGetFileVersion( filename, strW, len, &len );
364         if (hr == S_OK)
365         {
366             UINT i;
367             for (i = 0; i < CLR_VERSION_MAX; i++)
368                 if (!strcmpW( strW, clr_version[i] )) version = i;
369         }
370         msi_free( strW );
371     }
372     return version;
373 }
374
375 UINT msi_install_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
376 {
377     HRESULT hr;
378     const WCHAR *manifest;
379     IAssemblyCache *cache;
380     MSIASSEMBLY *assembly = comp->assembly;
381     MSIFEATURE *feature = NULL;
382
383     if (comp->assembly->feature)
384         feature = msi_get_loaded_feature( package, comp->assembly->feature );
385
386     if (assembly->application)
387     {
388         if (feature) feature->Action = INSTALLSTATE_LOCAL;
389         return ERROR_SUCCESS;
390     }
391     if (assembly->attributes == msidbAssemblyAttributesWin32)
392     {
393         if (!assembly->manifest)
394         {
395             WARN("no manifest\n");
396             return ERROR_FUNCTION_FAILED;
397         }
398         manifest = msi_get_loaded_file( package, assembly->manifest )->TargetPath;
399         cache = package->cache_sxs;
400     }
401     else
402     {
403         manifest = msi_get_loaded_file( package, comp->KeyPath )->TargetPath;
404         cache = package->cache_net[get_clr_version( manifest )];
405         if (!cache) return ERROR_SUCCESS;
406     }
407     TRACE("installing assembly %s\n", debugstr_w(manifest));
408
409     hr = IAssemblyCache_InstallAssembly( cache, 0, manifest, NULL );
410     if (hr != S_OK)
411     {
412         ERR("Failed to install assembly %s (0x%08x)\n", debugstr_w(manifest), hr);
413         return ERROR_FUNCTION_FAILED;
414     }
415     if (feature) feature->Action = INSTALLSTATE_LOCAL;
416     assembly->installed = TRUE;
417     return ERROR_SUCCESS;
418 }
419
420 UINT msi_uninstall_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
421 {
422     HRESULT hr;
423     IAssemblyCache *cache;
424     MSIASSEMBLY *assembly = comp->assembly;
425     MSIFEATURE *feature = NULL;
426
427     if (comp->assembly->feature)
428         feature = msi_get_loaded_feature( package, comp->assembly->feature );
429
430     if (assembly->application)
431     {
432         if (feature) feature->Action = INSTALLSTATE_ABSENT;
433         return ERROR_SUCCESS;
434     }
435     TRACE("removing %s\n", debugstr_w(assembly->display_name));
436
437     if (assembly->attributes == msidbAssemblyAttributesWin32)
438     {
439         cache = package->cache_sxs;
440         hr = IAssemblyCache_UninstallAssembly( cache, 0, assembly->display_name, NULL, NULL );
441         if (FAILED( hr )) WARN("failed to uninstall assembly 0x%08x\n", hr);
442     }
443     else
444     {
445         unsigned int i;
446         for (i = 0; i < CLR_VERSION_MAX; i++)
447         {
448             if (!assembly->clr_version[i]) continue;
449             cache = package->cache_net[i];
450             if (cache)
451             {
452                 hr = IAssemblyCache_UninstallAssembly( cache, 0, assembly->display_name, NULL, NULL );
453                 if (FAILED( hr )) WARN("failed to uninstall assembly 0x%08x\n", hr);
454             }
455         }
456     }
457     if (feature) feature->Action = INSTALLSTATE_ABSENT;
458     assembly->installed = FALSE;
459     return ERROR_SUCCESS;
460 }
461
462 static WCHAR *build_local_assembly_path( const WCHAR *filename )
463 {
464     UINT i;
465     WCHAR *ret;
466
467     if (!(ret = msi_alloc( (strlenW( filename ) + 1) * sizeof(WCHAR) )))
468         return NULL;
469
470     for (i = 0; filename[i]; i++)
471     {
472         if (filename[i] == '\\' || filename[i] == '/') ret[i] = '|';
473         else ret[i] = filename[i];
474     }
475     ret[i] = 0;
476     return ret;
477 }
478
479 static LONG open_assemblies_key( UINT context, BOOL win32, HKEY *hkey )
480 {
481     static const WCHAR path_win32[] =
482         {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
483           '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};
484     static const WCHAR path_dotnet[] =
485         {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
486          'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0};
487     static const WCHAR classes_path_win32[] =
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 classes_path_dotnet[] =
490         {'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0};
491     HKEY root;
492     const WCHAR *path;
493
494     if (context == MSIINSTALLCONTEXT_MACHINE)
495     {
496         root = HKEY_CLASSES_ROOT;
497         if (win32) path = classes_path_win32;
498         else path = classes_path_dotnet;
499     }
500     else
501     {
502         root = HKEY_CURRENT_USER;
503         if (win32) path = path_win32;
504         else path = path_dotnet;
505     }
506     return RegCreateKeyW( root, path, hkey );
507 }
508
509 static LONG open_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename, HKEY *hkey )
510 {
511     LONG res;
512     HKEY root;
513     WCHAR *path;
514
515     if (!(path = build_local_assembly_path( filename )))
516         return ERROR_OUTOFMEMORY;
517
518     if ((res = open_assemblies_key( context, win32, &root )))
519     {
520         msi_free( path );
521         return res;
522     }
523     res = RegCreateKeyW( root, path, hkey );
524     RegCloseKey( root );
525     msi_free( path );
526     return res;
527 }
528
529 static LONG delete_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename )
530 {
531     LONG res;
532     HKEY root;
533     WCHAR *path;
534
535     if (!(path = build_local_assembly_path( filename )))
536         return ERROR_OUTOFMEMORY;
537
538     if ((res = open_assemblies_key( context, win32, &root )))
539     {
540         msi_free( path );
541         return res;
542     }
543     res = RegDeleteKeyW( root, path );
544     RegCloseKey( root );
545     msi_free( path );
546     return res;
547 }
548
549 static LONG open_global_assembly_key( UINT context, BOOL win32, HKEY *hkey )
550 {
551     static const WCHAR path_win32[] =
552         {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
553          'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',
554          'G','l','o','b','a','l',0};
555     static const WCHAR path_dotnet[] =
556         {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
557          'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',
558          'G','l','o','b','a','l',0};
559     static const WCHAR classes_path_win32[] =
560         {'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',
561          'G','l','o','b','a','l',0};
562     static const WCHAR classes_path_dotnet[] =
563         {'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};
564     HKEY root;
565     const WCHAR *path;
566
567     if (context == MSIINSTALLCONTEXT_MACHINE)
568     {
569         root = HKEY_CLASSES_ROOT;
570         if (win32) path = classes_path_win32;
571         else path = classes_path_dotnet;
572     }
573     else
574     {
575         root = HKEY_CURRENT_USER;
576         if (win32) path = path_win32;
577         else path = path_dotnet;
578     }
579     return RegCreateKeyW( root, path, hkey );
580 }
581
582 UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
583 {
584     MSICOMPONENT *comp;
585
586     LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
587     {
588         LONG res;
589         HKEY hkey;
590         GUID guid;
591         DWORD size;
592         WCHAR buffer[43];
593         MSIRECORD *uirow;
594         MSIASSEMBLY *assembly = comp->assembly;
595         BOOL win32;
596
597         if (!assembly || !comp->ComponentId) continue;
598
599         comp->Action = msi_get_component_action( package, comp );
600         if (comp->Action != INSTALLSTATE_LOCAL)
601         {
602             TRACE("component not scheduled for installation %s\n", debugstr_w(comp->Component));
603             continue;
604         }
605         TRACE("publishing %s\n", debugstr_w(comp->Component));
606
607         CLSIDFromString( package->ProductCode, &guid );
608         encode_base85_guid( &guid, buffer );
609         buffer[20] = '>';
610         CLSIDFromString( comp->ComponentId, &guid );
611         encode_base85_guid( &guid, buffer + 21 );
612         buffer[42] = 0;
613
614         win32 = assembly->attributes & msidbAssemblyAttributesWin32;
615         if (assembly->application)
616         {
617             MSIFILE *file = msi_get_loaded_file( package, assembly->application );
618             if ((res = open_local_assembly_key( package->Context, win32, file->TargetPath, &hkey )))
619             {
620                 WARN("failed to open local assembly key %d\n", res);
621                 return ERROR_FUNCTION_FAILED;
622             }
623         }
624         else
625         {
626             if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
627             {
628                 WARN("failed to open global assembly key %d\n", res);
629                 return ERROR_FUNCTION_FAILED;
630             }
631         }
632         size = sizeof(buffer);
633         if ((res = RegSetValueExW( hkey, assembly->display_name, 0, REG_MULTI_SZ, (const BYTE *)buffer, size )))
634         {
635             WARN("failed to set assembly value %d\n", res);
636         }
637         RegCloseKey( hkey );
638
639         uirow = MSI_CreateRecord( 2 );
640         MSI_RecordSetStringW( uirow, 2, assembly->display_name );
641         msi_ui_actiondata( package, szMsiPublishAssemblies, uirow );
642         msiobj_release( &uirow->hdr );
643     }
644     return ERROR_SUCCESS;
645 }
646
647 UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
648 {
649     MSICOMPONENT *comp;
650
651     LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
652     {
653         LONG res;
654         MSIRECORD *uirow;
655         MSIASSEMBLY *assembly = comp->assembly;
656         BOOL win32;
657
658         if (!assembly || !comp->ComponentId) continue;
659
660         comp->Action = msi_get_component_action( package, comp );
661         if (comp->Action != INSTALLSTATE_ABSENT)
662         {
663             TRACE("component not scheduled for removal %s\n", debugstr_w(comp->Component));
664             continue;
665         }
666         TRACE("unpublishing %s\n", debugstr_w(comp->Component));
667
668         win32 = assembly->attributes & msidbAssemblyAttributesWin32;
669         if (assembly->application)
670         {
671             MSIFILE *file = msi_get_loaded_file( package, assembly->application );
672             if ((res = delete_local_assembly_key( package->Context, win32, file->TargetPath )))
673                 WARN("failed to delete local assembly key %d\n", res);
674         }
675         else
676         {
677             HKEY hkey;
678             if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
679                 WARN("failed to delete global assembly key %d\n", res);
680             else
681             {
682                 if ((res = RegDeleteValueW( hkey, assembly->display_name )))
683                     WARN("failed to delete global assembly value %d\n", res);
684                 RegCloseKey( hkey );
685             }
686         }
687
688         uirow = MSI_CreateRecord( 2 );
689         MSI_RecordSetStringW( uirow, 2, assembly->display_name );
690         msi_ui_actiondata( package, szMsiPublishAssemblies, uirow );
691         msiobj_release( &uirow->hdr );
692     }
693     return ERROR_SUCCESS;
694 }