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