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