xref: /reactos/dll/win32/msi/assembly.c (revision fd8b07bd)
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     if (package->script == SCRIPT_NONE)
656         return msi_schedule_action(package, SCRIPT_INSTALL, szMsiPublishAssemblies);
657 
658     LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
659     {
660         LONG res;
661         HKEY hkey;
662         GUID guid;
663         DWORD size;
664         WCHAR buffer[43];
665         MSIRECORD *uirow;
666         MSIASSEMBLY *assembly = comp->assembly;
667         BOOL win32;
668 
669         if (!assembly || !comp->ComponentId) continue;
670 
671         comp->Action = msi_get_component_action( package, comp );
672         if (comp->Action != INSTALLSTATE_LOCAL)
673         {
674             TRACE("component not scheduled for installation %s\n", debugstr_w(comp->Component));
675             continue;
676         }
677         TRACE("publishing %s\n", debugstr_w(comp->Component));
678 
679         CLSIDFromString( package->ProductCode, &guid );
680         encode_base85_guid( &guid, buffer );
681         buffer[20] = '>';
682         CLSIDFromString( comp->ComponentId, &guid );
683         encode_base85_guid( &guid, buffer + 21 );
684         buffer[42] = 0;
685 
686         win32 = assembly->attributes & msidbAssemblyAttributesWin32;
687         if (assembly->application)
688         {
689             MSIFILE *file = msi_get_loaded_file( package, assembly->application );
690             if ((res = open_local_assembly_key( package->Context, win32, file->TargetPath, &hkey )))
691             {
692                 WARN("failed to open local assembly key %d\n", res);
693                 return ERROR_FUNCTION_FAILED;
694             }
695         }
696         else
697         {
698             if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
699             {
700                 WARN("failed to open global assembly key %d\n", res);
701                 return ERROR_FUNCTION_FAILED;
702             }
703         }
704         size = sizeof(buffer);
705         if ((res = RegSetValueExW( hkey, assembly->display_name, 0, REG_MULTI_SZ, (const BYTE *)buffer, size )))
706         {
707             WARN("failed to set assembly value %d\n", res);
708         }
709         RegCloseKey( hkey );
710 
711         uirow = MSI_CreateRecord( 2 );
712         MSI_RecordSetStringW( uirow, 2, assembly->display_name );
713         MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
714         msiobj_release( &uirow->hdr );
715     }
716     return ERROR_SUCCESS;
717 }
718 
719 UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
720 {
721     MSICOMPONENT *comp;
722 
723     if (package->script == SCRIPT_NONE)
724         return msi_schedule_action(package, SCRIPT_INSTALL, szMsiUnpublishAssemblies);
725 
726     LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
727     {
728         LONG res;
729         MSIRECORD *uirow;
730         MSIASSEMBLY *assembly = comp->assembly;
731         BOOL win32;
732 
733         if (!assembly || !comp->ComponentId) continue;
734 
735         comp->Action = msi_get_component_action( package, comp );
736         if (comp->Action != INSTALLSTATE_ABSENT)
737         {
738             TRACE("component not scheduled for removal %s\n", debugstr_w(comp->Component));
739             continue;
740         }
741         TRACE("unpublishing %s\n", debugstr_w(comp->Component));
742 
743         win32 = assembly->attributes & msidbAssemblyAttributesWin32;
744         if (assembly->application)
745         {
746             MSIFILE *file = msi_get_loaded_file( package, assembly->application );
747             if ((res = delete_local_assembly_key( package->Context, win32, file->TargetPath )))
748                 WARN("failed to delete local assembly key %d\n", res);
749         }
750         else
751         {
752             HKEY hkey;
753             if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
754                 WARN("failed to delete global assembly key %d\n", res);
755             else
756             {
757                 if ((res = RegDeleteValueW( hkey, assembly->display_name )))
758                     WARN("failed to delete global assembly value %d\n", res);
759                 RegCloseKey( hkey );
760             }
761         }
762 
763         uirow = MSI_CreateRecord( 2 );
764         MSI_RecordSetStringW( uirow, 2, assembly->display_name );
765         MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
766         msiobj_release( &uirow->hdr );
767     }
768     return ERROR_SUCCESS;
769 }
770