user32/tests: Add tests for calling ShowWindow(SW_HIDE) on a hidden window.
[wine.git] / dlls / msi / assembly.c
bloba25b19db3b79035d08f27e2e093170d093d92b87
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2010 Hans Leidekker for CodeWeavers
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.
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.
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
21 #include <stdarg.h>
23 #define COBJMACROS
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"
32 WINE_DEFAULT_DEBUG_CHANNEL(msi);
34 static HRESULT (WINAPI *pCreateAssemblyCacheNet10)( IAssemblyCache **, DWORD );
35 static HRESULT (WINAPI *pCreateAssemblyCacheNet11)( IAssemblyCache **, DWORD );
36 static HRESULT (WINAPI *pCreateAssemblyCacheNet20)( IAssemblyCache **, DWORD );
37 static HRESULT (WINAPI *pCreateAssemblyCacheNet40)( IAssemblyCache **, DWORD );
38 static HRESULT (WINAPI *pCreateAssemblyCacheSxs)( IAssemblyCache **, DWORD );
39 static HRESULT (WINAPI *pLoadLibraryShim)( LPCWSTR, LPCWSTR, LPVOID, HMODULE * );
40 static HRESULT (WINAPI *pGetFileVersion)( LPCWSTR, LPWSTR, DWORD, DWORD * );
42 static HMODULE hfusion10, hfusion11, hfusion20, hfusion40, hmscoree, hsxs;
44 static BOOL init_function_pointers( void )
46 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
47 static const WCHAR szMscoree[] = {'\\','m','s','c','o','r','e','e','.','d','l','l',0};
48 static const WCHAR szSxs[] = {'s','x','s','.','d','l','l',0};
49 static const WCHAR szVersion10[] = {'v','1','.','0','.','3','7','0','5',0};
50 static const WCHAR szVersion11[] = {'v','1','.','1','.','4','3','2','2',0};
51 static const WCHAR szVersion20[] = {'v','2','.','0','.','5','0','7','2','7',0};
52 static const WCHAR szVersion40[] = {'v','4','.','0','.','3','0','3','1','9',0};
53 WCHAR path[MAX_PATH];
54 DWORD len = GetSystemDirectoryW( path, MAX_PATH );
56 if (!hsxs && !(hsxs = LoadLibraryW( szSxs ))) return FALSE;
57 if (!(pCreateAssemblyCacheSxs = (void *)GetProcAddress( hsxs, "CreateAssemblyCache" )))
59 FreeLibrary( hsxs );
60 hsxs = NULL;
61 return FALSE;
63 strcpyW( path + len, szMscoree );
64 if (hmscoree || !(hmscoree = LoadLibraryW( path ))) return TRUE;
65 pGetFileVersion = (void *)GetProcAddress( hmscoree, "GetFileVersion" ); /* missing from v1.0.3705 */
66 if (!(pLoadLibraryShim = (void *)GetProcAddress( hmscoree, "LoadLibraryShim" )))
68 FreeLibrary( hmscoree );
69 hmscoree = NULL;
70 return TRUE;
72 if (!pLoadLibraryShim( szFusion, szVersion10, NULL, &hfusion10 ))
73 pCreateAssemblyCacheNet10 = (void *)GetProcAddress( hfusion10, "CreateAssemblyCache" );
75 if (!pLoadLibraryShim( szFusion, szVersion11, NULL, &hfusion11 ))
76 pCreateAssemblyCacheNet11 = (void *)GetProcAddress( hfusion11, "CreateAssemblyCache" );
78 if (!pLoadLibraryShim( szFusion, szVersion20, NULL, &hfusion20 ))
79 pCreateAssemblyCacheNet20 = (void *)GetProcAddress( hfusion20, "CreateAssemblyCache" );
81 if (!pLoadLibraryShim( szFusion, szVersion40, NULL, &hfusion40 ))
82 pCreateAssemblyCacheNet40 = (void *)GetProcAddress( hfusion40, "CreateAssemblyCache" );
84 return TRUE;
87 BOOL msi_init_assembly_caches( MSIPACKAGE *package )
89 if (!init_function_pointers()) return FALSE;
90 if (pCreateAssemblyCacheSxs( &package->cache_sxs, 0 ) != S_OK) return FALSE;
91 if (pCreateAssemblyCacheNet10) pCreateAssemblyCacheNet10( &package->cache_net[CLR_VERSION_V10], 0 );
92 if (pCreateAssemblyCacheNet11) pCreateAssemblyCacheNet11( &package->cache_net[CLR_VERSION_V11], 0 );
93 if (pCreateAssemblyCacheNet20) pCreateAssemblyCacheNet20( &package->cache_net[CLR_VERSION_V20], 0 );
94 if (pCreateAssemblyCacheNet40) pCreateAssemblyCacheNet40( &package->cache_net[CLR_VERSION_V40], 0 );
95 return TRUE;
98 void msi_destroy_assembly_caches( MSIPACKAGE *package )
100 UINT i;
102 if (package->cache_sxs)
104 IAssemblyCache_Release( package->cache_sxs );
105 package->cache_sxs = NULL;
107 for (i = 0; i < CLR_VERSION_MAX; i++)
109 if (package->cache_net[i])
111 IAssemblyCache_Release( package->cache_net[i] );
112 package->cache_net[i] = NULL;
115 pCreateAssemblyCacheNet10 = NULL;
116 pCreateAssemblyCacheNet11 = NULL;
117 pCreateAssemblyCacheNet20 = NULL;
118 pCreateAssemblyCacheNet40 = NULL;
119 FreeLibrary( hfusion10 );
120 FreeLibrary( hfusion11 );
121 FreeLibrary( hfusion20 );
122 FreeLibrary( hfusion40 );
123 FreeLibrary( hmscoree );
124 FreeLibrary( hsxs );
125 hfusion10 = NULL;
126 hfusion11 = NULL;
127 hfusion20 = NULL;
128 hfusion40 = NULL;
129 hmscoree = NULL;
130 hsxs = NULL;
133 static MSIRECORD *get_assembly_record( MSIPACKAGE *package, const WCHAR *comp )
135 static const WCHAR query[] = {
136 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
137 '`','M','s','i','A','s','s','e','m','b','l','y','`',' ',
138 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
139 ' ','=',' ','\'','%','s','\'',0};
140 MSIQUERY *view;
141 MSIRECORD *rec;
142 UINT r;
144 r = MSI_OpenQuery( package->db, &view, query, comp );
145 if (r != ERROR_SUCCESS)
146 return NULL;
148 r = MSI_ViewExecute( view, NULL );
149 if (r != ERROR_SUCCESS)
151 msiobj_release( &view->hdr );
152 return NULL;
154 r = MSI_ViewFetch( view, &rec );
155 if (r != ERROR_SUCCESS)
157 msiobj_release( &view->hdr );
158 return NULL;
160 if (!MSI_RecordGetString( rec, 4 ))
161 TRACE("component is a global assembly\n");
163 msiobj_release( &view->hdr );
164 return rec;
167 struct assembly_name
169 UINT count;
170 UINT index;
171 WCHAR **attrs;
174 static UINT get_assembly_name_attribute( MSIRECORD *rec, LPVOID param )
176 static const WCHAR fmtW[] = {'%','s','=','"','%','s','"',0};
177 static const WCHAR nameW[] = {'n','a','m','e',0};
178 struct assembly_name *name = param;
179 const WCHAR *attr = MSI_RecordGetString( rec, 2 );
180 const WCHAR *value = MSI_RecordGetString( rec, 3 );
181 int len = strlenW( fmtW ) + strlenW( attr ) + strlenW( value );
183 if (!(name->attrs[name->index] = msi_alloc( len * sizeof(WCHAR) )))
184 return ERROR_OUTOFMEMORY;
186 if (!strcmpiW( attr, nameW )) strcpyW( name->attrs[name->index++], value );
187 else sprintfW( name->attrs[name->index++], fmtW, attr, value );
188 return ERROR_SUCCESS;
191 static WCHAR *get_assembly_display_name( MSIDATABASE *db, const WCHAR *comp, MSIASSEMBLY *assembly )
193 static const WCHAR commaW[] = {',',0};
194 static const WCHAR queryW[] = {
195 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
196 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
197 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
198 ' ','=',' ','\'','%','s','\'',0};
199 struct assembly_name name;
200 WCHAR *display_name = NULL;
201 MSIQUERY *view;
202 UINT i, r;
203 int len;
205 r = MSI_OpenQuery( db, &view, queryW, comp );
206 if (r != ERROR_SUCCESS)
207 return NULL;
209 name.count = 0;
210 name.index = 0;
211 name.attrs = NULL;
212 MSI_IterateRecords( view, &name.count, NULL, NULL );
213 if (!name.count) goto done;
215 name.attrs = msi_alloc( name.count * sizeof(WCHAR *) );
216 if (!name.attrs) goto done;
218 MSI_IterateRecords( view, NULL, get_assembly_name_attribute, &name );
220 len = 0;
221 for (i = 0; i < name.count; i++) len += strlenW( name.attrs[i] ) + 1;
223 display_name = msi_alloc( (len + 1) * sizeof(WCHAR) );
224 if (display_name)
226 display_name[0] = 0;
227 for (i = 0; i < name.count; i++)
229 strcatW( display_name, name.attrs[i] );
230 if (i < name.count - 1) strcatW( display_name, commaW );
234 done:
235 msiobj_release( &view->hdr );
236 if (name.attrs)
238 for (i = 0; i < name.count; i++) msi_free( name.attrs[i] );
239 msi_free( name.attrs );
241 return display_name;
244 static BOOL is_assembly_installed( IAssemblyCache *cache, const WCHAR *display_name )
246 HRESULT hr;
247 ASSEMBLY_INFO info;
249 if (!cache) return FALSE;
251 memset( &info, 0, sizeof(info) );
252 info.cbAssemblyInfo = sizeof(info);
253 hr = IAssemblyCache_QueryAssemblyInfo( cache, 0, display_name, &info );
254 if (hr == S_OK /* sxs version */ || hr == E_NOT_SUFFICIENT_BUFFER)
256 return (info.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
258 TRACE("QueryAssemblyInfo returned 0x%08x\n", hr);
259 return FALSE;
262 static const WCHAR clr_version_v10[] = {'v','1','.','0','.','3','7','0','5',0};
263 static const WCHAR clr_version_v11[] = {'v','1','.','1','.','4','3','2','2',0};
264 static const WCHAR clr_version_v20[] = {'v','2','.','0','.','5','0','7','2','7',0};
265 static const WCHAR clr_version_v40[] = {'v','4','.','0','.','3','0','3','1','9',0};
266 static const WCHAR clr_version_unknown[] = {'u','n','k','n','o','w','n',0};
268 static const WCHAR *clr_version[] =
270 clr_version_v10,
271 clr_version_v11,
272 clr_version_v20,
273 clr_version_v40
276 static const WCHAR *get_clr_version_str( enum clr_version version )
278 if (version >= sizeof(clr_version)/sizeof(clr_version[0])) return clr_version_unknown;
279 return clr_version[version];
282 /* assembly caches must be initialized */
283 MSIASSEMBLY *msi_load_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
285 MSIRECORD *rec;
286 MSIASSEMBLY *a;
288 if (!(rec = get_assembly_record( package, comp->Component ))) return NULL;
289 if (!(a = msi_alloc_zero( sizeof(MSIASSEMBLY) )))
291 msiobj_release( &rec->hdr );
292 return NULL;
294 a->feature = strdupW( MSI_RecordGetString( rec, 2 ) );
295 TRACE("feature %s\n", debugstr_w(a->feature));
297 a->manifest = strdupW( MSI_RecordGetString( rec, 3 ) );
298 TRACE("manifest %s\n", debugstr_w(a->manifest));
300 a->application = strdupW( MSI_RecordGetString( rec, 4 ) );
301 TRACE("application %s\n", debugstr_w(a->application));
303 a->attributes = MSI_RecordGetInteger( rec, 5 );
304 TRACE("attributes %u\n", a->attributes);
306 if (!(a->display_name = get_assembly_display_name( package->db, comp->Component, a )))
308 WARN("can't get display name\n");
309 msiobj_release( &rec->hdr );
310 msi_free( a->feature );
311 msi_free( a->manifest );
312 msi_free( a->application );
313 msi_free( a );
314 return NULL;
316 TRACE("display name %s\n", debugstr_w(a->display_name));
318 if (a->application)
320 /* We can't check the manifest here because the target path may still change.
321 So we assume that the assembly is not installed and lean on the InstallFiles
322 action to determine which files need to be installed.
324 a->installed = FALSE;
326 else
328 if (a->attributes == msidbAssemblyAttributesWin32)
329 a->installed = is_assembly_installed( package->cache_sxs, a->display_name );
330 else
332 UINT i;
333 for (i = 0; i < CLR_VERSION_MAX; i++)
335 a->clr_version[i] = is_assembly_installed( package->cache_net[i], a->display_name );
336 if (a->clr_version[i])
338 TRACE("runtime version %s\n", debugstr_w(get_clr_version_str( i )));
339 a->installed = TRUE;
340 break;
345 TRACE("assembly is %s\n", a->installed ? "installed" : "not installed");
346 msiobj_release( &rec->hdr );
347 return a;
350 static enum clr_version get_clr_version( const WCHAR *filename )
352 DWORD len;
353 HRESULT hr;
354 enum clr_version version = CLR_VERSION_V11;
355 WCHAR *strW;
357 if (!pGetFileVersion) return CLR_VERSION_V10;
359 hr = pGetFileVersion( filename, NULL, 0, &len );
360 if (hr != E_NOT_SUFFICIENT_BUFFER) return CLR_VERSION_V11;
361 if ((strW = msi_alloc( len * sizeof(WCHAR) )))
363 hr = pGetFileVersion( filename, strW, len, &len );
364 if (hr == S_OK)
366 UINT i;
367 for (i = 0; i < CLR_VERSION_MAX; i++)
368 if (!strcmpW( strW, clr_version[i] )) version = i;
370 msi_free( strW );
372 return version;
375 UINT msi_install_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
377 HRESULT hr;
378 const WCHAR *manifest;
379 IAssemblyCache *cache;
380 MSIASSEMBLY *assembly = comp->assembly;
381 MSIFEATURE *feature = NULL;
383 if (comp->assembly->feature)
384 feature = msi_get_loaded_feature( package, comp->assembly->feature );
386 if (assembly->application)
388 if (feature) feature->Action = INSTALLSTATE_LOCAL;
389 return ERROR_SUCCESS;
391 if (assembly->attributes == msidbAssemblyAttributesWin32)
393 if (!assembly->manifest)
395 WARN("no manifest\n");
396 return ERROR_FUNCTION_FAILED;
398 manifest = msi_get_loaded_file( package, assembly->manifest )->TargetPath;
399 cache = package->cache_sxs;
401 else
403 manifest = msi_get_loaded_file( package, comp->KeyPath )->TargetPath;
404 cache = package->cache_net[get_clr_version( manifest )];
405 if (!cache) return ERROR_SUCCESS;
407 TRACE("installing assembly %s\n", debugstr_w(manifest));
409 hr = IAssemblyCache_InstallAssembly( cache, 0, manifest, NULL );
410 if (hr != S_OK)
412 ERR("Failed to install assembly %s (0x%08x)\n", debugstr_w(manifest), hr);
413 return ERROR_FUNCTION_FAILED;
415 if (feature) feature->Action = INSTALLSTATE_LOCAL;
416 assembly->installed = TRUE;
417 return ERROR_SUCCESS;
420 UINT msi_uninstall_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
422 HRESULT hr;
423 IAssemblyCache *cache;
424 MSIASSEMBLY *assembly = comp->assembly;
425 MSIFEATURE *feature = NULL;
427 if (comp->assembly->feature)
428 feature = msi_get_loaded_feature( package, comp->assembly->feature );
430 if (assembly->application)
432 if (feature) feature->Action = INSTALLSTATE_ABSENT;
433 return ERROR_SUCCESS;
435 TRACE("removing %s\n", debugstr_w(assembly->display_name));
437 if (assembly->attributes == msidbAssemblyAttributesWin32)
439 cache = package->cache_sxs;
440 hr = IAssemblyCache_UninstallAssembly( cache, 0, assembly->display_name, NULL, NULL );
441 if (FAILED( hr )) WARN("failed to uninstall assembly 0x%08x\n", hr);
443 else
445 unsigned int i;
446 for (i = 0; i < CLR_VERSION_MAX; i++)
448 if (!assembly->clr_version[i]) continue;
449 cache = package->cache_net[i];
450 if (cache)
452 hr = IAssemblyCache_UninstallAssembly( cache, 0, assembly->display_name, NULL, NULL );
453 if (FAILED( hr )) WARN("failed to uninstall assembly 0x%08x\n", hr);
457 if (feature) feature->Action = INSTALLSTATE_ABSENT;
458 assembly->installed = FALSE;
459 return ERROR_SUCCESS;
462 static WCHAR *build_local_assembly_path( const WCHAR *filename )
464 UINT i;
465 WCHAR *ret;
467 if (!(ret = msi_alloc( (strlenW( filename ) + 1) * sizeof(WCHAR) )))
468 return NULL;
470 for (i = 0; filename[i]; i++)
472 if (filename[i] == '\\' || filename[i] == '/') ret[i] = '|';
473 else ret[i] = filename[i];
475 ret[i] = 0;
476 return ret;
479 static LONG open_assemblies_key( UINT context, BOOL win32, HKEY *hkey )
481 static const WCHAR path_win32[] =
482 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
483 '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};
484 static const WCHAR path_dotnet[] =
485 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
486 'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0};
487 static const WCHAR classes_path_win32[] =
488 {'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};
489 static const WCHAR classes_path_dotnet[] =
490 {'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0};
491 HKEY root;
492 const WCHAR *path;
494 if (context == MSIINSTALLCONTEXT_MACHINE)
496 root = HKEY_CLASSES_ROOT;
497 if (win32) path = classes_path_win32;
498 else path = classes_path_dotnet;
500 else
502 root = HKEY_CURRENT_USER;
503 if (win32) path = path_win32;
504 else path = path_dotnet;
506 return RegCreateKeyW( root, path, hkey );
509 static LONG open_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename, HKEY *hkey )
511 LONG res;
512 HKEY root;
513 WCHAR *path;
515 if (!(path = build_local_assembly_path( filename )))
516 return ERROR_OUTOFMEMORY;
518 if ((res = open_assemblies_key( context, win32, &root )))
520 msi_free( path );
521 return res;
523 res = RegCreateKeyW( root, path, hkey );
524 RegCloseKey( root );
525 msi_free( path );
526 return res;
529 static LONG delete_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename )
531 LONG res;
532 HKEY root;
533 WCHAR *path;
535 if (!(path = build_local_assembly_path( filename )))
536 return ERROR_OUTOFMEMORY;
538 if ((res = open_assemblies_key( context, win32, &root )))
540 msi_free( path );
541 return res;
543 res = RegDeleteKeyW( root, path );
544 RegCloseKey( root );
545 msi_free( path );
546 return res;
549 static LONG open_global_assembly_key( UINT context, BOOL win32, HKEY *hkey )
551 static const WCHAR path_win32[] =
552 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
553 'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',
554 'G','l','o','b','a','l',0};
555 static const WCHAR path_dotnet[] =
556 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
557 'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',
558 'G','l','o','b','a','l',0};
559 static const WCHAR classes_path_win32[] =
560 {'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',
561 'G','l','o','b','a','l',0};
562 static const WCHAR classes_path_dotnet[] =
563 {'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};
564 HKEY root;
565 const WCHAR *path;
567 if (context == MSIINSTALLCONTEXT_MACHINE)
569 root = HKEY_CLASSES_ROOT;
570 if (win32) path = classes_path_win32;
571 else path = classes_path_dotnet;
573 else
575 root = HKEY_CURRENT_USER;
576 if (win32) path = path_win32;
577 else path = path_dotnet;
579 return RegCreateKeyW( root, path, hkey );
582 UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
584 MSICOMPONENT *comp;
586 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
588 LONG res;
589 HKEY hkey;
590 GUID guid;
591 DWORD size;
592 WCHAR buffer[43];
593 MSIRECORD *uirow;
594 MSIASSEMBLY *assembly = comp->assembly;
595 BOOL win32;
597 if (!assembly || !comp->ComponentId) continue;
599 comp->Action = msi_get_component_action( package, comp );
600 if (comp->Action != INSTALLSTATE_LOCAL)
602 TRACE("component not scheduled for installation %s\n", debugstr_w(comp->Component));
603 continue;
605 TRACE("publishing %s\n", debugstr_w(comp->Component));
607 CLSIDFromString( package->ProductCode, &guid );
608 encode_base85_guid( &guid, buffer );
609 buffer[20] = '>';
610 CLSIDFromString( comp->ComponentId, &guid );
611 encode_base85_guid( &guid, buffer + 21 );
612 buffer[42] = 0;
614 win32 = assembly->attributes & msidbAssemblyAttributesWin32;
615 if (assembly->application)
617 MSIFILE *file = msi_get_loaded_file( package, assembly->application );
618 if ((res = open_local_assembly_key( package->Context, win32, file->TargetPath, &hkey )))
620 WARN("failed to open local assembly key %d\n", res);
621 return ERROR_FUNCTION_FAILED;
624 else
626 if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
628 WARN("failed to open global assembly key %d\n", res);
629 return ERROR_FUNCTION_FAILED;
632 size = sizeof(buffer);
633 if ((res = RegSetValueExW( hkey, assembly->display_name, 0, REG_MULTI_SZ, (const BYTE *)buffer, size )))
635 WARN("failed to set assembly value %d\n", res);
637 RegCloseKey( hkey );
639 uirow = MSI_CreateRecord( 2 );
640 MSI_RecordSetStringW( uirow, 2, assembly->display_name );
641 msi_ui_actiondata( package, szMsiPublishAssemblies, uirow );
642 msiobj_release( &uirow->hdr );
644 return ERROR_SUCCESS;
647 UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
649 MSICOMPONENT *comp;
651 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
653 LONG res;
654 MSIRECORD *uirow;
655 MSIASSEMBLY *assembly = comp->assembly;
656 BOOL win32;
658 if (!assembly || !comp->ComponentId) continue;
660 comp->Action = msi_get_component_action( package, comp );
661 if (comp->Action != INSTALLSTATE_ABSENT)
663 TRACE("component not scheduled for removal %s\n", debugstr_w(comp->Component));
664 continue;
666 TRACE("unpublishing %s\n", debugstr_w(comp->Component));
668 win32 = assembly->attributes & msidbAssemblyAttributesWin32;
669 if (assembly->application)
671 MSIFILE *file = msi_get_loaded_file( package, assembly->application );
672 if ((res = delete_local_assembly_key( package->Context, win32, file->TargetPath )))
673 WARN("failed to delete local assembly key %d\n", res);
675 else
677 HKEY hkey;
678 if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
679 WARN("failed to delete global assembly key %d\n", res);
680 else
682 if ((res = RegDeleteValueW( hkey, assembly->display_name )))
683 WARN("failed to delete global assembly value %d\n", res);
684 RegCloseKey( hkey );
688 uirow = MSI_CreateRecord( 2 );
689 MSI_RecordSetStringW( uirow, 2, assembly->display_name );
690 msi_ui_actiondata( package, szMsiPublishAssemblies, uirow );
691 msiobj_release( &uirow->hdr );
693 return ERROR_SUCCESS;