[MSI] Sync with Wine Staging 2.16. CORE-13762
[reactos.git] / dll / win32 / msi / assembly.c
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
3 *
4 * Copyright 2010 Hans Leidekker for CodeWeavers
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #include "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 }