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