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