ntdll: Use a separate memory allocation for the kernel stack.
[wine.git] / dlls / msi / assembly.c
blob47e8071502c8314769400574006fdfd3de71a551
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 "msipriv.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(msi);
33 static void load_fusion_dlls( MSIPACKAGE *package )
35 HRESULT (WINAPI *pLoadLibraryShim)( const WCHAR *, const WCHAR *, void *, HMODULE * );
36 WCHAR path[MAX_PATH];
37 DWORD len = GetSystemDirectoryW( path, MAX_PATH );
39 lstrcpyW( path + len, L"\\mscoree.dll" );
40 if (!package->hmscoree && !(package->hmscoree = LoadLibraryW( path ))) return;
41 if (!(pLoadLibraryShim = (void *)GetProcAddress( package->hmscoree, "LoadLibraryShim" )))
43 FreeLibrary( package->hmscoree );
44 package->hmscoree = NULL;
45 return;
48 if (!package->hfusion10) pLoadLibraryShim( L"fusion.dll", L"v1.0.3705", NULL, &package->hfusion10 );
49 if (!package->hfusion11) pLoadLibraryShim( L"fusion.dll", L"v1.1.4322", NULL, &package->hfusion11 );
50 if (!package->hfusion20) pLoadLibraryShim( L"fusion.dll", L"v2.0.50727", NULL, &package->hfusion20 );
51 if (!package->hfusion40) pLoadLibraryShim( L"fusion.dll", L"v4.0.30319", NULL, &package->hfusion40 );
54 static BOOL init_assembly_caches( MSIPACKAGE *package )
56 HRESULT (WINAPI *pCreateAssemblyCache)( IAssemblyCache **, DWORD );
58 if (!package->cache_sxs && CreateAssemblyCache( &package->cache_sxs, 0 ) != S_OK) return FALSE;
60 load_fusion_dlls( package );
61 package->pGetFileVersion = (void *)GetProcAddress( package->hmscoree, "GetFileVersion" ); /* missing from v1.0.3705 */
63 if (package->hfusion10 && !package->cache_net[CLR_VERSION_V10])
65 pCreateAssemblyCache = (void *)GetProcAddress( package->hfusion10, "CreateAssemblyCache" );
66 pCreateAssemblyCache( &package->cache_net[CLR_VERSION_V10], 0 );
68 if (package->hfusion11 && !package->cache_net[CLR_VERSION_V11])
70 pCreateAssemblyCache = (void *)GetProcAddress( package->hfusion11, "CreateAssemblyCache" );
71 pCreateAssemblyCache( &package->cache_net[CLR_VERSION_V11], 0 );
73 if (package->hfusion20 && !package->cache_net[CLR_VERSION_V20])
75 pCreateAssemblyCache = (void *)GetProcAddress( package->hfusion20, "CreateAssemblyCache" );
76 pCreateAssemblyCache( &package->cache_net[CLR_VERSION_V20], 0 );
78 if (package->hfusion40 && !package->cache_net[CLR_VERSION_V40])
80 pCreateAssemblyCache = (void *)GetProcAddress( package->hfusion40, "CreateAssemblyCache" );
81 pCreateAssemblyCache( &package->cache_net[CLR_VERSION_V40], 0 );
82 package->pCreateAssemblyNameObject = (void *)GetProcAddress( package->hfusion40, "CreateAssemblyNameObject" );
83 package->pCreateAssemblyEnum = (void *)GetProcAddress( package->hfusion40, "CreateAssemblyEnum" );
86 return TRUE;
89 void msi_destroy_assembly_caches( MSIPACKAGE *package )
91 UINT i;
93 if (package->cache_sxs)
95 IAssemblyCache_Release( package->cache_sxs );
96 package->cache_sxs = NULL;
98 for (i = 0; i < CLR_VERSION_MAX; i++)
100 if (package->cache_net[i])
102 IAssemblyCache_Release( package->cache_net[i] );
103 package->cache_net[i] = NULL;
106 package->pGetFileVersion = NULL;
107 package->pCreateAssemblyNameObject = NULL;
108 package->pCreateAssemblyEnum = NULL;
109 FreeLibrary( package->hfusion10 );
110 FreeLibrary( package->hfusion11 );
111 FreeLibrary( package->hfusion20 );
112 FreeLibrary( package->hfusion40 );
113 FreeLibrary( package->hmscoree );
114 package->hfusion10 = NULL;
115 package->hfusion11 = NULL;
116 package->hfusion20 = NULL;
117 package->hfusion40 = NULL;
118 package->hmscoree = NULL;
121 static MSIRECORD *get_assembly_record( MSIPACKAGE *package, const WCHAR *comp )
123 MSIQUERY *view;
124 MSIRECORD *rec;
125 UINT r;
127 r = MSI_OpenQuery( package->db, &view, L"SELECT * FROM `MsiAssembly` WHERE `Component_` = '%s'", comp );
128 if (r != ERROR_SUCCESS)
129 return NULL;
131 r = MSI_ViewExecute( view, NULL );
132 if (r != ERROR_SUCCESS)
134 msiobj_release( &view->hdr );
135 return NULL;
137 r = MSI_ViewFetch( view, &rec );
138 if (r != ERROR_SUCCESS)
140 msiobj_release( &view->hdr );
141 return NULL;
143 if (!MSI_RecordGetString( rec, 4 ))
144 TRACE("component is a global assembly\n");
146 msiobj_release( &view->hdr );
147 return rec;
150 struct assembly_name
152 DWORD count;
153 UINT index;
154 WCHAR **attrs;
157 static UINT get_assembly_name_attribute( MSIRECORD *rec, LPVOID param )
159 struct assembly_name *name = param;
160 const WCHAR *attr = MSI_RecordGetString( rec, 2 );
161 const WCHAR *value = MSI_RecordGetString( rec, 3 );
162 int len = lstrlenW( L"%s=\"%s\"" ) + lstrlenW( attr ) + lstrlenW( value );
164 if (!(name->attrs[name->index] = malloc( len * sizeof(WCHAR) )))
165 return ERROR_OUTOFMEMORY;
167 if (!wcsicmp( attr, L"name" )) lstrcpyW( name->attrs[name->index++], value );
168 else swprintf( name->attrs[name->index++], len, L"%s=\"%s\"", attr, value );
169 return ERROR_SUCCESS;
172 static WCHAR *get_assembly_display_name( MSIDATABASE *db, const WCHAR *comp, MSIASSEMBLY *assembly )
174 struct assembly_name name;
175 WCHAR *display_name = NULL;
176 MSIQUERY *view;
177 UINT i, r;
178 int len;
180 r = MSI_OpenQuery( db, &view, L"SELECT * FROM `MsiAssemblyName` WHERE `Component_` = '%s'", comp );
181 if (r != ERROR_SUCCESS)
182 return NULL;
184 name.count = 0;
185 name.index = 0;
186 name.attrs = NULL;
187 MSI_IterateRecords( view, &name.count, NULL, NULL );
188 if (!name.count) goto done;
190 name.attrs = malloc( name.count * sizeof(WCHAR *) );
191 if (!name.attrs) goto done;
193 MSI_IterateRecords( view, NULL, get_assembly_name_attribute, &name );
195 len = 0;
196 for (i = 0; i < name.count; i++) len += lstrlenW( name.attrs[i] ) + 1;
198 display_name = malloc( (len + 1) * sizeof(WCHAR) );
199 if (display_name)
201 display_name[0] = 0;
202 for (i = 0; i < name.count; i++)
204 lstrcatW( display_name, name.attrs[i] );
205 if (i < name.count - 1) lstrcatW( display_name, L"," );
209 done:
210 msiobj_release( &view->hdr );
211 if (name.attrs)
213 for (i = 0; i < name.count; i++) free( name.attrs[i] );
214 free( name.attrs );
216 return display_name;
219 WCHAR *msi_get_assembly_path( MSIPACKAGE *package, const WCHAR *displayname )
221 HRESULT hr;
222 ASSEMBLY_INFO info;
223 IAssemblyCache *cache;
225 if (!init_assembly_caches( package ) || !(cache = package->cache_net[CLR_VERSION_V40])) return NULL;
227 memset( &info, 0, sizeof(info) );
228 info.cbAssemblyInfo = sizeof(info);
229 hr = IAssemblyCache_QueryAssemblyInfo( cache, 0, displayname, &info );
230 if (hr != E_NOT_SUFFICIENT_BUFFER) return NULL;
232 if (!(info.pszCurrentAssemblyPathBuf = malloc( info.cchBuf * sizeof(WCHAR) ))) return NULL;
234 hr = IAssemblyCache_QueryAssemblyInfo( cache, 0, displayname, &info );
235 if (FAILED( hr ))
237 free( info.pszCurrentAssemblyPathBuf );
238 return NULL;
240 TRACE("returning %s\n", debugstr_w(info.pszCurrentAssemblyPathBuf));
241 return info.pszCurrentAssemblyPathBuf;
244 IAssemblyEnum *msi_create_assembly_enum( MSIPACKAGE *package, const WCHAR *displayname )
246 HRESULT hr;
247 IAssemblyName *name;
248 IAssemblyEnum *ret;
249 WCHAR *str;
250 DWORD len = 0;
252 if (!init_assembly_caches( package ) || !package->pCreateAssemblyNameObject || !package->pCreateAssemblyEnum)
253 return NULL;
255 hr = package->pCreateAssemblyNameObject( &name, displayname, CANOF_PARSE_DISPLAY_NAME, NULL );
256 if (FAILED( hr )) return NULL;
258 hr = IAssemblyName_GetName( name, &len, NULL );
259 if (hr != E_NOT_SUFFICIENT_BUFFER || !(str = malloc( len * sizeof(WCHAR) )))
261 IAssemblyName_Release( name );
262 return NULL;
265 hr = IAssemblyName_GetName( name, &len, str );
266 IAssemblyName_Release( name );
267 if (FAILED( hr ))
269 free( str );
270 return NULL;
273 hr = package->pCreateAssemblyNameObject( &name, str, 0, NULL );
274 free( str );
275 if (FAILED( hr )) return NULL;
277 hr = package->pCreateAssemblyEnum( &ret, NULL, name, ASM_CACHE_GAC, NULL );
278 IAssemblyName_Release( name );
279 if (FAILED( hr )) return NULL;
281 return ret;
284 static const WCHAR *clr_version[] =
286 L"v1.0.3705",
287 L"v1.2.4322",
288 L"v2.0.50727",
289 L"v4.0.30319"
292 MSIASSEMBLY *msi_load_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
294 MSIRECORD *rec;
295 MSIASSEMBLY *a;
297 if (!(rec = get_assembly_record( package, comp->Component ))) return NULL;
298 if (!(a = calloc( 1, sizeof(MSIASSEMBLY) )))
300 msiobj_release( &rec->hdr );
301 return NULL;
303 a->feature = wcsdup( MSI_RecordGetString( rec, 2 ) );
304 TRACE("feature %s\n", debugstr_w(a->feature));
306 a->manifest = wcsdup( MSI_RecordGetString( rec, 3 ) );
307 TRACE("manifest %s\n", debugstr_w(a->manifest));
309 a->application = wcsdup( MSI_RecordGetString( rec, 4 ) );
310 TRACE("application %s\n", debugstr_w(a->application));
312 a->attributes = MSI_RecordGetInteger( rec, 5 );
313 TRACE( "attributes %lu\n", a->attributes );
315 if (!(a->display_name = get_assembly_display_name( package->db, comp->Component, a )))
317 WARN("can't get display name\n");
318 msiobj_release( &rec->hdr );
319 free( a->feature );
320 free( a->manifest );
321 free( a->application );
322 free( a );
323 return NULL;
325 TRACE("display name %s\n", debugstr_w(a->display_name));
327 msiobj_release( &rec->hdr );
328 return a;
331 static enum clr_version get_clr_version( MSIPACKAGE *package, const WCHAR *filename )
333 DWORD len;
334 HRESULT hr;
335 enum clr_version version = CLR_VERSION_V11;
336 WCHAR *strW;
338 if (!package->pGetFileVersion) return CLR_VERSION_V10;
340 hr = package->pGetFileVersion( filename, NULL, 0, &len );
341 if (hr != E_NOT_SUFFICIENT_BUFFER) return CLR_VERSION_V11;
342 if ((strW = malloc( len * sizeof(WCHAR) )))
344 hr = package->pGetFileVersion( filename, strW, len, &len );
345 if (hr == S_OK)
347 UINT i;
348 for (i = 0; i < CLR_VERSION_MAX; i++)
349 if (!wcscmp( strW, clr_version[i] )) version = i;
351 free( strW );
353 return version;
356 UINT msi_install_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
358 HRESULT hr;
359 const WCHAR *manifest;
360 IAssemblyCache *cache;
361 MSIASSEMBLY *assembly = comp->assembly;
362 MSIFEATURE *feature = NULL;
364 if (!init_assembly_caches( package )) return ERROR_FUNCTION_FAILED;
366 if (comp->assembly->feature)
367 feature = msi_get_loaded_feature( package, comp->assembly->feature );
369 if (assembly->application)
371 if (feature) feature->Action = INSTALLSTATE_LOCAL;
372 return ERROR_SUCCESS;
374 if (assembly->attributes == msidbAssemblyAttributesWin32)
376 if (!assembly->manifest)
378 WARN("no manifest\n");
379 return ERROR_FUNCTION_FAILED;
381 manifest = msi_get_loaded_file( package, assembly->manifest )->TargetPath;
382 cache = package->cache_sxs;
384 else
386 manifest = msi_get_loaded_file( package, comp->KeyPath )->TargetPath;
387 cache = package->cache_net[get_clr_version( package, manifest )];
388 if (!cache) return ERROR_SUCCESS;
390 TRACE("installing assembly %s\n", debugstr_w(manifest));
392 hr = IAssemblyCache_InstallAssembly( cache, 0, manifest, NULL );
393 if (hr != S_OK)
395 ERR( "failed to install assembly %s (%#lx)\n", debugstr_w(manifest), hr );
396 return ERROR_FUNCTION_FAILED;
398 if (feature) feature->Action = INSTALLSTATE_LOCAL;
399 return ERROR_SUCCESS;
402 UINT msi_uninstall_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
404 HRESULT hr;
405 IAssemblyCache *cache;
406 MSIASSEMBLY *assembly = comp->assembly;
407 MSIFEATURE *feature = NULL;
409 if (!init_assembly_caches( package )) return ERROR_FUNCTION_FAILED;
411 if (comp->assembly->feature)
412 feature = msi_get_loaded_feature( package, comp->assembly->feature );
414 if (assembly->application)
416 if (feature) feature->Action = INSTALLSTATE_ABSENT;
417 return ERROR_SUCCESS;
419 TRACE("removing %s\n", debugstr_w(assembly->display_name));
421 if (assembly->attributes == msidbAssemblyAttributesWin32)
423 cache = package->cache_sxs;
424 hr = IAssemblyCache_UninstallAssembly( cache, 0, assembly->display_name, NULL, NULL );
425 if (FAILED( hr )) WARN( "failed to uninstall assembly %#lx\n", hr );
427 else
429 unsigned int i;
430 for (i = 0; i < CLR_VERSION_MAX; i++)
432 if (!assembly->clr_version[i]) continue;
433 cache = package->cache_net[i];
434 if (cache)
436 hr = IAssemblyCache_UninstallAssembly( cache, 0, assembly->display_name, NULL, NULL );
437 if (FAILED( hr )) WARN( "failed to uninstall assembly %#lx\n", hr );
441 if (feature) feature->Action = INSTALLSTATE_ABSENT;
442 return ERROR_SUCCESS;
445 static WCHAR *build_local_assembly_path( const WCHAR *filename )
447 UINT i;
448 WCHAR *ret;
450 if (!(ret = malloc( (wcslen( filename ) + 1) * sizeof(WCHAR) )))
451 return NULL;
453 for (i = 0; filename[i]; i++)
455 if (filename[i] == '\\' || filename[i] == '/') ret[i] = '|';
456 else ret[i] = filename[i];
458 ret[i] = 0;
459 return ret;
462 static LONG open_assemblies_key( UINT context, BOOL win32, HKEY *hkey )
464 HKEY root;
465 const WCHAR *path;
467 if (context == MSIINSTALLCONTEXT_MACHINE)
469 root = HKEY_CLASSES_ROOT;
470 if (win32) path = L"Installer\\Win32Assemblies\\";
471 else path = L"Installer\\Assemblies\\";
473 else
475 root = HKEY_CURRENT_USER;
476 if (win32) path = L"Software\\Microsoft\\Installer\\Win32Assemblies\\";
477 else path = L"Software\\Microsoft\\Installer\\Assemblies\\";
479 return RegCreateKeyW( root, path, hkey );
482 static LONG open_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename, HKEY *hkey )
484 LONG res;
485 HKEY root;
486 WCHAR *path;
488 if (!(path = build_local_assembly_path( filename )))
489 return ERROR_OUTOFMEMORY;
491 if ((res = open_assemblies_key( context, win32, &root )))
493 free( path );
494 return res;
496 res = RegCreateKeyW( root, path, hkey );
497 RegCloseKey( root );
498 free( path );
499 return res;
502 static LONG delete_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename )
504 LONG res;
505 HKEY root;
506 WCHAR *path;
508 if (!(path = build_local_assembly_path( filename )))
509 return ERROR_OUTOFMEMORY;
511 if ((res = open_assemblies_key( context, win32, &root )))
513 free( path );
514 return res;
516 res = RegDeleteKeyW( root, path );
517 RegCloseKey( root );
518 free( path );
519 return res;
522 static LONG open_global_assembly_key( UINT context, BOOL win32, HKEY *hkey )
524 HKEY root;
525 const WCHAR *path;
527 if (context == MSIINSTALLCONTEXT_MACHINE)
529 root = HKEY_CLASSES_ROOT;
530 if (win32) path = L"Installer\\Win32Assemblies\\Global";
531 else path = L"Installer\\Assemblies\\Global";
533 else
535 root = HKEY_CURRENT_USER;
536 if (win32) path = L"Software\\Microsoft\\Installer\\Win32Assemblies\\Global";
537 else path = L"Software\\Microsoft\\Installer\\Assemblies\\Global";
539 return RegCreateKeyW( root, path, hkey );
542 UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
544 MSICOMPONENT *comp;
546 if (package->script == SCRIPT_NONE)
547 return msi_schedule_action(package, SCRIPT_INSTALL, L"MsiPublishAssemblies");
549 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
551 LONG res;
552 HKEY hkey;
553 GUID guid;
554 DWORD size;
555 WCHAR buffer[43];
556 MSIRECORD *uirow;
557 MSIASSEMBLY *assembly = comp->assembly;
558 BOOL win32;
560 if (!assembly || !comp->ComponentId) continue;
562 comp->Action = msi_get_component_action( package, comp );
563 if (comp->Action != INSTALLSTATE_LOCAL)
565 TRACE("component not scheduled for installation %s\n", debugstr_w(comp->Component));
566 continue;
568 TRACE("publishing %s\n", debugstr_w(comp->Component));
570 CLSIDFromString( package->ProductCode, &guid );
571 encode_base85_guid( &guid, buffer );
572 buffer[20] = '>';
573 CLSIDFromString( comp->ComponentId, &guid );
574 encode_base85_guid( &guid, buffer + 21 );
575 buffer[42] = 0;
577 win32 = assembly->attributes & msidbAssemblyAttributesWin32;
578 if (assembly->application)
580 MSIFILE *file = msi_get_loaded_file( package, assembly->application );
581 if (!file)
583 WARN("no matching file %s for local assembly\n", debugstr_w(assembly->application));
584 continue;
586 if ((res = open_local_assembly_key( package->Context, win32, file->TargetPath, &hkey )))
588 WARN( "failed to open local assembly key %ld\n", res );
589 return ERROR_FUNCTION_FAILED;
592 else
594 if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
596 WARN( "failed to open global assembly key %ld\n", res );
597 return ERROR_FUNCTION_FAILED;
600 size = sizeof(buffer);
601 if ((res = RegSetValueExW( hkey, assembly->display_name, 0, REG_MULTI_SZ, (const BYTE *)buffer, size )))
603 WARN( "failed to set assembly value %ld\n", res );
605 RegCloseKey( hkey );
607 uirow = MSI_CreateRecord( 2 );
608 MSI_RecordSetStringW( uirow, 2, assembly->display_name );
609 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
610 msiobj_release( &uirow->hdr );
612 return ERROR_SUCCESS;
615 UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
617 MSICOMPONENT *comp;
619 if (package->script == SCRIPT_NONE)
620 return msi_schedule_action(package, SCRIPT_INSTALL, L"MsiUnpublishAssemblies");
622 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
624 LONG res;
625 MSIRECORD *uirow;
626 MSIASSEMBLY *assembly = comp->assembly;
627 BOOL win32;
629 if (!assembly || !comp->ComponentId) continue;
631 comp->Action = msi_get_component_action( package, comp );
632 if (comp->Action != INSTALLSTATE_ABSENT)
634 TRACE("component not scheduled for removal %s\n", debugstr_w(comp->Component));
635 continue;
637 TRACE("unpublishing %s\n", debugstr_w(comp->Component));
639 win32 = assembly->attributes & msidbAssemblyAttributesWin32;
640 if (assembly->application)
642 MSIFILE *file = msi_get_loaded_file( package, assembly->application );
643 if (!file)
645 WARN("no matching file %s for local assembly\n", debugstr_w(assembly->application));
646 continue;
648 if ((res = delete_local_assembly_key( package->Context, win32, file->TargetPath )))
649 WARN( "failed to delete local assembly key %ld\n", res );
651 else
653 HKEY hkey;
654 if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
655 WARN( "failed to delete global assembly key %ld\n", res );
656 else
658 if ((res = RegDeleteValueW( hkey, assembly->display_name )))
659 WARN( "failed to delete global assembly value %ld\n", res );
660 RegCloseKey( hkey );
664 uirow = MSI_CreateRecord( 2 );
665 MSI_RecordSetStringW( uirow, 2, assembly->display_name );
666 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
667 msiobj_release( &uirow->hdr );
669 return ERROR_SUCCESS;