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
28 #include "wine/debug.h"
29 #include "wine/unicode.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(msi);
34 static HRESULT (WINAPI *pCreateAssemblyCacheNet11)( IAssemblyCache **, DWORD );
35 static HRESULT (WINAPI *pCreateAssemblyCacheNet20)( IAssemblyCache **, DWORD );
36 static HRESULT (WINAPI *pCreateAssemblyCacheSxs)( IAssemblyCache **, DWORD );
37 static HRESULT (WINAPI *pLoadLibraryShim)( LPCWSTR, LPCWSTR, LPVOID, HMODULE * );
38 static HRESULT (WINAPI *pGetFileVersion)( LPCWSTR, LPWSTR, DWORD, DWORD * );
40 static HMODULE hfusion11, hfusion20, hmscoree, hsxs;
42 static BOOL init_function_pointers( void )
44 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
45 static const WCHAR szVersion11[] = {'v','1','.','1','.','4','3','2','2',0};
46 static const WCHAR szVersion20[] = {'v','2','.','0','.','5','0','7','2','7',0};
48 if (pCreateAssemblyCacheNet11 || pCreateAssemblyCacheNet20) return TRUE;
50 if (!(hmscoree = LoadLibraryA( "mscoree.dll" ))) return FALSE;
51 if (!(pGetFileVersion = (void *)GetProcAddress( hmscoree, "GetFileVersion" ))) goto error;
52 if (!(pLoadLibraryShim = (void *)GetProcAddress( hmscoree, "LoadLibraryShim" ))) goto error;
54 if (!pLoadLibraryShim( szFusion, szVersion11, NULL, &hfusion11 ))
55 pCreateAssemblyCacheNet11 = (void *)GetProcAddress( hfusion11, "CreateAssemblyCache" );
57 if (!pLoadLibraryShim( szFusion, szVersion20, NULL, &hfusion20 ))
58 pCreateAssemblyCacheNet20 = (void *)GetProcAddress( hfusion20, "CreateAssemblyCache" );
60 if (!pCreateAssemblyCacheNet11 && !pCreateAssemblyCacheNet20) goto error;
62 if (!(hsxs = LoadLibraryA( "sxs.dll" ))) goto error;
63 if (!(pCreateAssemblyCacheSxs = (void *)GetProcAddress( hsxs, "CreateAssemblyCache" ))) goto error;
67 pCreateAssemblyCacheNet11 = NULL;
68 pCreateAssemblyCacheNet20 = NULL;
69 FreeLibrary( hfusion11 );
70 FreeLibrary( hfusion20 );
71 FreeLibrary( hmscoree );
75 BOOL msi_init_assembly_caches( MSIPACKAGE *package )
77 if (!init_function_pointers()) return FALSE;
78 if (package->cache_net[CLR_VERSION_V11] || package->cache_net[CLR_VERSION_V20]) return TRUE;
79 if (pCreateAssemblyCacheSxs( &package->cache_sxs, 0 ) != S_OK) return FALSE;
81 if (pCreateAssemblyCacheNet11) pCreateAssemblyCacheNet11( &package->cache_net[CLR_VERSION_V11], 0 );
82 if (pCreateAssemblyCacheNet20) pCreateAssemblyCacheNet20( &package->cache_net[CLR_VERSION_V20], 0 );
84 if (package->cache_net[CLR_VERSION_V11] || package->cache_net[CLR_VERSION_V20])
88 if (package->cache_net[CLR_VERSION_V11])
90 IAssemblyCache_Release( package->cache_net[CLR_VERSION_V11] );
91 package->cache_net[CLR_VERSION_V11] = NULL;
93 if (package->cache_net[CLR_VERSION_V20])
95 IAssemblyCache_Release( package->cache_net[CLR_VERSION_V20] );
96 package->cache_net[CLR_VERSION_V20] = NULL;
98 IAssemblyCache_Release( package->cache_sxs );
99 package->cache_sxs = NULL;
103 void msi_destroy_assembly_caches( MSIPACKAGE *package )
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 if (package->cache_sxs)
117 IAssemblyCache_Release( package->cache_sxs );
118 package->cache_sxs = NULL;
120 pCreateAssemblyCacheNet11 = NULL;
121 pCreateAssemblyCacheNet20 = NULL;
122 FreeLibrary( hfusion11 );
123 FreeLibrary( hfusion20 );
124 FreeLibrary( hmscoree );
128 static MSIRECORD *get_assembly_record( MSIPACKAGE *package, const WCHAR *comp )
130 static const WCHAR query[] = {
131 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
132 '`','M','s','i','A','s','s','e','m','b','l','y','`',' ',
133 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
134 ' ','=',' ','\'','%','s','\'',0};
139 r = MSI_OpenQuery( package->db, &view, query, comp );
140 if (r != ERROR_SUCCESS)
143 r = MSI_ViewExecute( view, NULL );
144 if (r != ERROR_SUCCESS)
146 msiobj_release( &view->hdr );
149 r = MSI_ViewFetch( view, &rec );
150 if (r != ERROR_SUCCESS)
152 msiobj_release( &view->hdr );
155 if (!MSI_RecordGetString( rec, 4 ))
156 TRACE("component is a global assembly\n");
158 msiobj_release( &view->hdr );
169 static UINT get_assembly_name_attribute( MSIRECORD *rec, LPVOID param )
171 static const WCHAR fmtW[] = {'%','s','=','"','%','s','"',0};
172 static const WCHAR nameW[] = {'n','a','m','e',0};
173 struct assembly_name *name = param;
174 const WCHAR *attr = MSI_RecordGetString( rec, 2 );
175 const WCHAR *value = MSI_RecordGetString( rec, 3 );
176 int len = strlenW( fmtW ) + strlenW( attr ) + strlenW( value );
178 if (!(name->attrs[name->index] = msi_alloc( len * sizeof(WCHAR) )))
179 return ERROR_OUTOFMEMORY;
181 if (!strcmpiW( attr, nameW )) strcpyW( name->attrs[name->index++], value );
182 else sprintfW( name->attrs[name->index++], fmtW, attr, value );
183 return ERROR_SUCCESS;
186 static WCHAR *get_assembly_display_name( MSIDATABASE *db, const WCHAR *comp, MSIASSEMBLY *assembly )
188 static const WCHAR commaW[] = {',',0};
189 static const WCHAR queryW[] = {
190 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
191 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
192 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
193 ' ','=',' ','\'','%','s','\'',0};
194 struct assembly_name name;
195 WCHAR *display_name = NULL;
200 r = MSI_OpenQuery( db, &view, queryW, comp );
201 if (r != ERROR_SUCCESS)
207 MSI_IterateRecords( view, &name.count, NULL, NULL );
208 if (!name.count) goto done;
210 name.attrs = msi_alloc( name.count * sizeof(WCHAR *) );
211 if (!name.attrs) goto done;
213 MSI_IterateRecords( view, NULL, get_assembly_name_attribute, &name );
216 for (i = 0; i < name.count; i++) len += strlenW( name.attrs[i] ) + 1;
218 display_name = msi_alloc( (len + 1) * sizeof(WCHAR) );
222 for (i = 0; i < name.count; i++)
224 strcatW( display_name, name.attrs[i] );
225 if (i < name.count - 1) strcatW( display_name, commaW );
230 msiobj_release( &view->hdr );
231 for (i = 0; i < name.count; i++) msi_free( name.attrs[i] );
232 msi_free( name.attrs );
236 static BOOL is_assembly_installed( IAssemblyCache *cache, const WCHAR *display_name )
241 memset( &info, 0, sizeof(info) );
242 info.cbAssemblyInfo = sizeof(info);
243 hr = IAssemblyCache_QueryAssemblyInfo( cache, QUERYASMINFO_FLAG_GETSIZE, display_name, &info );
246 TRACE("QueryAssemblyInfo returned 0x%08x\n", hr);
249 return (info.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
252 static const WCHAR clr_version_v11[] = {'v','1','.','1','.','4','3','2','2',0};
253 static const WCHAR clr_version_v20[] = {'v','2','.','0','.','5','0','7','2','7',0};
254 static const WCHAR clr_version_unknown[] = {'u','n','k','n','o','w','n',0};
256 static const WCHAR *clr_version[] =
262 static const WCHAR *get_clr_version_str( enum clr_version version )
264 if (version >= sizeof(clr_version)/sizeof(clr_version[0])) return clr_version_unknown;
265 return clr_version[version];
268 /* assembly caches must be initialized */
269 MSIASSEMBLY *msi_load_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
274 if (!(rec = get_assembly_record( package, comp->Component ))) return NULL;
275 if (!(a = msi_alloc_zero( sizeof(MSIASSEMBLY) )))
277 msiobj_release( &rec->hdr );
280 a->feature = strdupW( MSI_RecordGetString( rec, 2 ) );
281 TRACE("feature %s\n", debugstr_w(a->feature));
283 a->manifest = strdupW( MSI_RecordGetString( rec, 3 ) );
284 TRACE("manifest %s\n", debugstr_w(a->manifest));
286 a->application = strdupW( MSI_RecordGetString( rec, 4 ) );
287 TRACE("application %s\n", debugstr_w(a->application));
289 a->attributes = MSI_RecordGetInteger( rec, 5 );
290 TRACE("attributes %u\n", a->attributes);
292 if (!(a->display_name = get_assembly_display_name( package->db, comp->Component, a )))
294 WARN("can't get display name\n");
295 msiobj_release( &rec->hdr );
296 msi_free( a->feature );
297 msi_free( a->manifest );
298 msi_free( a->application );
302 TRACE("display name %s\n", debugstr_w(a->display_name));
306 /* We can't check the manifest here because the target path may still change.
307 So we assume that the assembly is not installed and lean on the InstallFiles
308 action to determine which files need to be installed.
310 a->installed = FALSE;
314 if (a->attributes == msidbAssemblyAttributesWin32)
315 a->installed = is_assembly_installed( package->cache_sxs, a->display_name );
319 for (i = 0; i < CLR_VERSION_MAX; i++)
321 a->clr_version[i] = is_assembly_installed( package->cache_net[i], a->display_name );
322 if (a->clr_version[i])
324 TRACE("runtime version %s\n", debugstr_w(get_clr_version_str( i )));
330 TRACE("assembly is %s\n", a->installed ? "installed" : "not installed");
331 msiobj_release( &rec->hdr );
335 static enum clr_version get_clr_version( const WCHAR *filename )
339 enum clr_version version = CLR_VERSION_V11;
342 hr = pGetFileVersion( filename, NULL, 0, &len );
343 if (hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) return CLR_VERSION_V11;
344 if ((strW = msi_alloc( len * sizeof(WCHAR) )))
346 hr = pGetFileVersion( filename, strW, len, &len );
350 for (i = 0; i < CLR_VERSION_MAX; i++)
351 if (!strcmpW( strW, clr_version[i] )) version = i;
358 UINT msi_install_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
361 const WCHAR *manifest;
362 IAssemblyCache *cache;
363 MSIASSEMBLY *assembly = comp->assembly;
364 MSIFEATURE *feature = NULL;
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;
386 manifest = msi_get_loaded_file( package, comp->KeyPath )->TargetPath;
387 cache = package->cache_net[get_clr_version( manifest )];
389 TRACE("installing assembly %s\n", debugstr_w(manifest));
391 hr = IAssemblyCache_InstallAssembly( cache, 0, manifest, NULL );
394 ERR("Failed to install assembly %s (0x%08x)\n", debugstr_w(manifest), hr);
395 return ERROR_FUNCTION_FAILED;
397 if (feature) feature->Action = INSTALLSTATE_LOCAL;
398 assembly->installed = TRUE;
399 return ERROR_SUCCESS;
402 static WCHAR *build_local_assembly_path( const WCHAR *filename )
407 if (!(ret = msi_alloc( (strlenW( filename ) + 1) * sizeof(WCHAR) )))
410 for (i = 0; filename[i]; i++)
412 if (filename[i] == '\\' || filename[i] == '/') ret[i] = '|';
413 else ret[i] = filename[i];
419 static LONG open_assemblies_key( UINT context, BOOL win32, HKEY *hkey )
421 static const WCHAR path_win32[] =
422 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
423 '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};
424 static const WCHAR path_dotnet[] =
425 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
426 'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0};
427 static const WCHAR classes_path_win32[] =
428 {'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};
429 static const WCHAR classes_path_dotnet[] =
430 {'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0};
434 if (context == MSIINSTALLCONTEXT_MACHINE)
436 root = HKEY_CLASSES_ROOT;
437 if (win32) path = classes_path_win32;
438 else path = classes_path_dotnet;
442 root = HKEY_CURRENT_USER;
443 if (win32) path = path_win32;
444 else path = path_dotnet;
446 return RegCreateKeyW( root, path, hkey );
449 static LONG open_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename, HKEY *hkey )
455 if (!(path = build_local_assembly_path( filename )))
456 return ERROR_OUTOFMEMORY;
458 if ((res = open_assemblies_key( context, win32, &root )))
463 res = RegCreateKeyW( root, path, hkey );
469 static LONG delete_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename )
475 if (!(path = build_local_assembly_path( filename )))
476 return ERROR_OUTOFMEMORY;
478 if ((res = open_assemblies_key( context, win32, &root )))
483 res = RegDeleteKeyW( root, path );
489 static LONG open_global_assembly_key( UINT context, BOOL win32, HKEY *hkey )
491 static const WCHAR path_win32[] =
492 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
493 'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',
494 'G','l','o','b','a','l',0};
495 static const WCHAR path_dotnet[] =
496 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
497 'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',
498 'G','l','o','b','a','l',0};
499 static const WCHAR classes_path_win32[] =
500 {'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',
501 'G','l','o','b','a','l',0};
502 static const WCHAR classes_path_dotnet[] =
503 {'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};
507 if (context == MSIINSTALLCONTEXT_MACHINE)
509 root = HKEY_CLASSES_ROOT;
510 if (win32) path = classes_path_win32;
511 else path = classes_path_dotnet;
515 root = HKEY_CURRENT_USER;
516 if (win32) path = path_win32;
517 else path = path_dotnet;
519 return RegCreateKeyW( root, path, hkey );
522 UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
526 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
534 MSIASSEMBLY *assembly = comp->assembly;
537 if (!assembly || !comp->ComponentId) continue;
541 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
545 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
547 TRACE("Component not scheduled for installation: %s\n", debugstr_w(comp->Component));
548 comp->Action = comp->Installed;
551 comp->Action = INSTALLSTATE_LOCAL;
553 TRACE("publishing %s\n", debugstr_w(comp->Component));
555 CLSIDFromString( package->ProductCode, &guid );
556 encode_base85_guid( &guid, buffer );
558 CLSIDFromString( comp->ComponentId, &guid );
559 encode_base85_guid( &guid, buffer + 21 );
562 win32 = assembly->attributes & msidbAssemblyAttributesWin32;
563 if (assembly->application)
565 MSIFILE *file = msi_get_loaded_file( package, assembly->application );
566 if ((res = open_local_assembly_key( package->Context, win32, file->TargetPath, &hkey )))
568 WARN("failed to open local assembly key %d\n", res);
569 return ERROR_FUNCTION_FAILED;
574 if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
576 WARN("failed to open global assembly key %d\n", res);
577 return ERROR_FUNCTION_FAILED;
580 size = sizeof(buffer);
581 if ((res = RegSetValueExW( hkey, assembly->display_name, 0, REG_MULTI_SZ, (const BYTE *)buffer, size )))
583 WARN("failed to set assembly value %d\n", res);
587 uirow = MSI_CreateRecord( 2 );
588 MSI_RecordSetStringW( uirow, 2, assembly->display_name );
589 msi_ui_actiondata( package, szMsiPublishAssemblies, uirow );
590 msiobj_release( &uirow->hdr );
592 return ERROR_SUCCESS;
595 UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
599 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
603 MSIASSEMBLY *assembly = comp->assembly;
606 if (!assembly || !comp->ComponentId) continue;
610 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
614 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
616 TRACE("Component not scheduled for removal: %s\n", debugstr_w(comp->Component));
617 comp->Action = comp->Installed;
620 comp->Action = INSTALLSTATE_ABSENT;
622 TRACE("unpublishing %s\n", debugstr_w(comp->Component));
624 win32 = assembly->attributes & msidbAssemblyAttributesWin32;
625 if (assembly->application)
627 MSIFILE *file = msi_get_loaded_file( package, assembly->application );
628 if ((res = delete_local_assembly_key( package->Context, win32, file->TargetPath )))
629 WARN("failed to delete local assembly key %d\n", res);
634 if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
635 WARN("failed to delete global assembly key %d\n", res);
638 if ((res = RegDeleteValueW( hkey, assembly->display_name )))
639 WARN("failed to delete global assembly value %d\n", res);
644 uirow = MSI_CreateRecord( 2 );
645 MSI_RecordSetStringW( uirow, 2, assembly->display_name );
646 msi_ui_actiondata( package, szMsiPublishAssemblies, uirow );
647 msiobj_release( &uirow->hdr );
649 return ERROR_SUCCESS;