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