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