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 BOOL init_function_pointers( void )
42 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
43 static const WCHAR szVersion11[] = {'v','1','.','1','.','4','3','2','2',0};
44 static const WCHAR szVersion20[] = {'v','2','.','0','.','5','0','7','2','7',0};
45 HMODULE hfusion11 = NULL, hfusion20 = NULL, hmscoree, hsxs;
47 if (pCreateAssemblyCacheNet11 || pCreateAssemblyCacheNet20) return TRUE;
49 if (!(hmscoree = LoadLibraryA( "mscoree.dll" ))) return FALSE;
50 if (!(pGetFileVersion = (void *)GetProcAddress( hmscoree, "GetFileVersion" ))) goto error;
51 if (!(pLoadLibraryShim = (void *)GetProcAddress( hmscoree, "LoadLibraryShim" ))) goto error;
53 if (!pLoadLibraryShim( szFusion, szVersion11, NULL, &hfusion11 ))
54 pCreateAssemblyCacheNet11 = (void *)GetProcAddress( hfusion11, "CreateAssemblyCache" );
56 if (!pLoadLibraryShim( szFusion, szVersion20, NULL, &hfusion20 ))
57 pCreateAssemblyCacheNet20 = (void *)GetProcAddress( hfusion20, "CreateAssemblyCache" );
59 if (!pCreateAssemblyCacheNet11 && !pCreateAssemblyCacheNet20) goto error;
61 if (!(hsxs = LoadLibraryA( "sxs.dll" ))) goto error;
62 if (!(pCreateAssemblyCacheSxs = (void *)GetProcAddress( hsxs, "CreateAssemblyCache" ))) goto error;
66 pCreateAssemblyCacheNet11 = NULL;
67 pCreateAssemblyCacheNet20 = NULL;
68 FreeLibrary( hfusion11 );
69 FreeLibrary( hfusion20 );
70 FreeLibrary( hmscoree );
74 static BOOL init_assembly_caches( MSIPACKAGE *package )
76 if (!init_function_pointers()) return FALSE;
77 if (package->cache_net[CLR_VERSION_V11] || package->cache_net[CLR_VERSION_V20]) return TRUE;
78 if (pCreateAssemblyCacheSxs( &package->cache_sxs, 0 ) != S_OK) return FALSE;
80 if (pCreateAssemblyCacheNet11) pCreateAssemblyCacheNet11( &package->cache_net[CLR_VERSION_V11], 0 );
81 if (pCreateAssemblyCacheNet20) pCreateAssemblyCacheNet20( &package->cache_net[CLR_VERSION_V20], 0 );
83 if (package->cache_net[CLR_VERSION_V11] || package->cache_net[CLR_VERSION_V20])
87 if (package->cache_net[CLR_VERSION_V11])
89 IAssemblyCache_Release( package->cache_net[CLR_VERSION_V11] );
90 package->cache_net[CLR_VERSION_V11] = NULL;
92 if (package->cache_net[CLR_VERSION_V20])
94 IAssemblyCache_Release( package->cache_net[CLR_VERSION_V20] );
95 package->cache_net[CLR_VERSION_V20] = NULL;
97 IAssemblyCache_Release( package->cache_sxs );
98 package->cache_sxs = NULL;
102 static MSIRECORD *get_assembly_record( MSIPACKAGE *package, const WCHAR *comp )
104 static const WCHAR query[] = {
105 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
106 '`','M','s','i','A','s','s','e','m','b','l','y','`',' ',
107 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
108 ' ','=',' ','\'','%','s','\'',0};
113 r = MSI_OpenQuery( package->db, &view, query, comp );
114 if (r != ERROR_SUCCESS)
117 r = MSI_ViewExecute( view, NULL );
118 if (r != ERROR_SUCCESS)
120 msiobj_release( &view->hdr );
123 r = MSI_ViewFetch( view, &rec );
124 if (r != ERROR_SUCCESS)
126 msiobj_release( &view->hdr );
129 if (!MSI_RecordGetString( rec, 4 ))
130 TRACE("component is a global assembly\n");
132 msiobj_release( &view->hdr );
143 static UINT get_assembly_name_attribute( MSIRECORD *rec, LPVOID param )
145 static const WCHAR fmtW[] = {'%','s','=','"','%','s','"',0};
146 static const WCHAR nameW[] = {'n','a','m','e',0};
147 struct assembly_name *name = param;
148 const WCHAR *attr = MSI_RecordGetString( rec, 2 );
149 const WCHAR *value = MSI_RecordGetString( rec, 3 );
150 int len = strlenW( fmtW ) + strlenW( attr ) + strlenW( value );
152 if (!(name->attrs[name->index] = msi_alloc( len * sizeof(WCHAR) )))
153 return ERROR_OUTOFMEMORY;
155 if (!strcmpiW( attr, nameW )) strcpyW( name->attrs[name->index++], value );
156 else sprintfW( name->attrs[name->index++], fmtW, attr, value );
157 return ERROR_SUCCESS;
160 static WCHAR *get_assembly_display_name( MSIDATABASE *db, const WCHAR *comp, MSIASSEMBLY *assembly )
162 static const WCHAR commaW[] = {',',0};
163 static const WCHAR queryW[] = {
164 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
165 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
166 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
167 ' ','=',' ','\'','%','s','\'',0};
168 struct assembly_name name;
169 WCHAR *display_name = NULL;
174 r = MSI_OpenQuery( db, &view, queryW, comp );
175 if (r != ERROR_SUCCESS)
181 MSI_IterateRecords( view, &name.count, NULL, NULL );
182 if (!name.count) goto done;
184 name.attrs = msi_alloc( name.count * sizeof(WCHAR *) );
185 if (!name.attrs) goto done;
187 MSI_IterateRecords( view, NULL, get_assembly_name_attribute, &name );
190 for (i = 0; i < name.count; i++) len += strlenW( name.attrs[i] ) + 1;
192 display_name = msi_alloc( (len + 1) * sizeof(WCHAR) );
196 for (i = 0; i < name.count; i++)
198 strcatW( display_name, name.attrs[i] );
199 if (i < name.count - 1) strcatW( display_name, commaW );
204 msiobj_release( &view->hdr );
205 for (i = 0; i < name.count; i++) msi_free( name.attrs[i] );
206 msi_free( name.attrs );
210 static BOOL is_assembly_installed( IAssemblyCache *cache, const WCHAR *display_name )
215 memset( &info, 0, sizeof(info) );
216 info.cbAssemblyInfo = sizeof(info);
217 hr = IAssemblyCache_QueryAssemblyInfo( cache, QUERYASMINFO_FLAG_GETSIZE, display_name, &info );
220 TRACE("QueryAssemblyInfo returned 0x%08x\n", hr);
223 return (info.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
226 static const WCHAR clr_version_v11[] = {'v','1','.','1','.','4','3','2','2',0};
227 static const WCHAR clr_version_v20[] = {'v','2','.','0','.','5','0','7','2','7',0};
228 static const WCHAR clr_version_unknown[] = {'u','n','k','n','o','w','n',0};
230 static const WCHAR *clr_version[] =
236 static const WCHAR *get_clr_version_str( enum clr_version version )
238 if (version >= sizeof(clr_version)/sizeof(clr_version[0])) return clr_version_unknown;
239 return clr_version[version];
242 MSIASSEMBLY *load_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
247 if (!(rec = get_assembly_record( package, comp->Component ))) return NULL;
248 if (!init_assembly_caches( package ))
250 ERR("can't initialize assembly caches\n");
251 msiobj_release( &rec->hdr );
254 if (!(a = msi_alloc_zero( sizeof(MSIASSEMBLY) )))
256 msiobj_release( &rec->hdr );
259 a->feature = strdupW( MSI_RecordGetString( rec, 2 ) );
260 TRACE("feature %s\n", debugstr_w(a->feature));
262 a->manifest = strdupW( MSI_RecordGetString( rec, 3 ) );
263 TRACE("manifest %s\n", debugstr_w(a->manifest));
265 a->application = strdupW( MSI_RecordGetString( rec, 4 ) );
266 TRACE("application %s\n", debugstr_w(a->application));
268 a->attributes = MSI_RecordGetInteger( rec, 5 );
269 TRACE("attributes %u\n", a->attributes);
271 if (!(a->display_name = get_assembly_display_name( package->db, comp->Component, a )))
273 WARN("can't get display name\n");
274 msiobj_release( &rec->hdr );
275 msi_free( a->feature );
276 msi_free( a->manifest );
277 msi_free( a->application );
281 TRACE("display name %s\n", debugstr_w(a->display_name));
285 /* We can't check the manifest here because the target path may still change.
286 So we assume that the assembly is not installed and lean on the InstallFiles
287 action to determine which files need to be installed.
289 a->installed = FALSE;
293 if (a->attributes == msidbAssemblyAttributesWin32)
294 a->installed = is_assembly_installed( package->cache_sxs, a->display_name );
298 for (i = 0; i < CLR_VERSION_MAX; i++)
300 a->clr_version[i] = is_assembly_installed( package->cache_net[i], a->display_name );
301 if (a->clr_version[i])
303 TRACE("runtime version %s\n", debugstr_w(get_clr_version_str( i )));
309 TRACE("assembly is %s\n", a->installed ? "installed" : "not installed");
310 msiobj_release( &rec->hdr );
314 static enum clr_version get_clr_version( const WCHAR *filename )
318 enum clr_version version = CLR_VERSION_V11;
321 hr = pGetFileVersion( filename, NULL, 0, &len );
322 if (hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) return CLR_VERSION_V11;
323 if ((strW = msi_alloc( len * sizeof(WCHAR) )))
325 hr = pGetFileVersion( filename, strW, len, &len );
329 for (i = 0; i < CLR_VERSION_MAX; i++)
330 if (!strcmpW( strW, clr_version[i] )) version = i;
337 UINT install_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
340 const WCHAR *manifest;
341 IAssemblyCache *cache;
342 MSIASSEMBLY *assembly = comp->assembly;
343 MSIFEATURE *feature = NULL;
345 if (comp->assembly->feature)
346 feature = get_loaded_feature( package, comp->assembly->feature );
348 if (assembly->application)
350 if (feature) feature->Action = INSTALLSTATE_LOCAL;
351 return ERROR_SUCCESS;
353 if (assembly->attributes == msidbAssemblyAttributesWin32)
355 if (!assembly->manifest)
357 WARN("no manifest\n");
358 return ERROR_FUNCTION_FAILED;
360 manifest = get_loaded_file( package, assembly->manifest )->TargetPath;
361 cache = package->cache_sxs;
365 manifest = get_loaded_file( package, comp->KeyPath )->TargetPath;
366 cache = package->cache_net[get_clr_version( manifest )];
368 TRACE("installing assembly %s\n", debugstr_w(manifest));
370 hr = IAssemblyCache_InstallAssembly( cache, 0, manifest, NULL );
373 ERR("Failed to install assembly %s (0x%08x)\n", debugstr_w(manifest), hr);
374 return ERROR_FUNCTION_FAILED;
376 if (feature) feature->Action = INSTALLSTATE_LOCAL;
377 assembly->installed = TRUE;
378 return ERROR_SUCCESS;
381 static WCHAR *build_local_assembly_path( const WCHAR *filename )
386 if (!(ret = msi_alloc( (strlenW( filename ) + 1) * sizeof(WCHAR) )))
389 for (i = 0; filename[i]; i++)
391 if (filename[i] == '\\' || filename[i] == '/') ret[i] = '|';
392 else ret[i] = filename[i];
398 static LONG open_assemblies_key( UINT context, BOOL win32, HKEY *hkey )
400 static const WCHAR path_win32[] =
401 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
402 '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};
403 static const WCHAR path_dotnet[] =
404 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
405 'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0};
406 static const WCHAR classes_path_win32[] =
407 {'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};
408 static const WCHAR classes_path_dotnet[] =
409 {'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0};
413 if (context == MSIINSTALLCONTEXT_MACHINE)
415 root = HKEY_CLASSES_ROOT;
416 if (win32) path = classes_path_win32;
417 else path = classes_path_dotnet;
421 root = HKEY_CURRENT_USER;
422 if (win32) path = path_win32;
423 else path = path_dotnet;
425 return RegCreateKeyW( root, path, hkey );
428 static LONG open_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename, HKEY *hkey )
434 if (!(path = build_local_assembly_path( filename )))
435 return ERROR_OUTOFMEMORY;
437 if ((res = open_assemblies_key( context, win32, &root )))
442 res = RegCreateKeyW( root, path, hkey );
448 static LONG delete_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename )
454 if (!(path = build_local_assembly_path( filename )))
455 return ERROR_OUTOFMEMORY;
457 if ((res = open_assemblies_key( context, win32, &root )))
462 res = RegDeleteKeyW( root, path );
468 static LONG open_global_assembly_key( UINT context, BOOL win32, HKEY *hkey )
470 static const WCHAR path_win32[] =
471 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
472 'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',
473 'G','l','o','b','a','l',0};
474 static const WCHAR path_dotnet[] =
475 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
476 'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',
477 'G','l','o','b','a','l',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','\\',
480 'G','l','o','b','a','l',0};
481 static const WCHAR classes_path_dotnet[] =
482 {'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};
486 if (context == MSIINSTALLCONTEXT_MACHINE)
488 root = HKEY_CLASSES_ROOT;
489 if (win32) path = classes_path_win32;
490 else path = classes_path_dotnet;
494 root = HKEY_CURRENT_USER;
495 if (win32) path = path_win32;
496 else path = path_dotnet;
498 return RegCreateKeyW( root, path, hkey );
501 UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
505 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
513 MSIASSEMBLY *assembly = comp->assembly;
516 if (!assembly || !comp->ComponentId) continue;
520 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
524 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
526 TRACE("Component not scheduled for installation: %s\n", debugstr_w(comp->Component));
527 comp->Action = comp->Installed;
530 comp->Action = INSTALLSTATE_LOCAL;
532 TRACE("publishing %s\n", debugstr_w(comp->Component));
534 CLSIDFromString( package->ProductCode, &guid );
535 encode_base85_guid( &guid, buffer );
537 CLSIDFromString( comp->ComponentId, &guid );
538 encode_base85_guid( &guid, buffer + 21 );
541 win32 = assembly->attributes & msidbAssemblyAttributesWin32;
542 if (assembly->application)
544 MSIFILE *file = get_loaded_file( package, assembly->application );
545 if ((res = open_local_assembly_key( package->Context, win32, file->TargetPath, &hkey )))
547 WARN("failed to open local assembly key %d\n", res);
548 return ERROR_FUNCTION_FAILED;
553 if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
555 WARN("failed to open global assembly key %d\n", res);
556 return ERROR_FUNCTION_FAILED;
559 size = sizeof(buffer);
560 if ((res = RegSetValueExW( hkey, assembly->display_name, 0, REG_MULTI_SZ, (const BYTE *)buffer, size )))
562 WARN("failed to set assembly value %d\n", res);
566 uirow = MSI_CreateRecord( 2 );
567 MSI_RecordSetStringW( uirow, 2, assembly->display_name );
568 ui_actiondata( package, szMsiPublishAssemblies, uirow );
569 msiobj_release( &uirow->hdr );
571 return ERROR_SUCCESS;
574 UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
578 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
582 MSIASSEMBLY *assembly = comp->assembly;
585 if (!assembly || !comp->ComponentId) continue;
589 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
593 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
595 TRACE("Component not scheduled for removal: %s\n", debugstr_w(comp->Component));
596 comp->Action = comp->Installed;
599 comp->Action = INSTALLSTATE_ABSENT;
601 TRACE("unpublishing %s\n", debugstr_w(comp->Component));
603 win32 = assembly->attributes & msidbAssemblyAttributesWin32;
604 if (assembly->application)
606 MSIFILE *file = get_loaded_file( package, assembly->application );
607 if ((res = delete_local_assembly_key( package->Context, win32, file->TargetPath )))
608 WARN("failed to delete local assembly key %d\n", res);
613 if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
614 WARN("failed to delete global assembly key %d\n", res);
617 if ((res = RegDeleteValueW( hkey, assembly->display_name )))
618 WARN("failed to delete global assembly value %d\n", res);
623 uirow = MSI_CreateRecord( 2 );
624 MSI_RecordSetStringW( uirow, 2, assembly->display_name );
625 ui_actiondata( package, szMsiPublishAssemblies, uirow );
626 msiobj_release( &uirow->hdr );
628 return ERROR_SUCCESS;