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 *pCreateAssemblyCacheNet)( IAssemblyCache **, DWORD );
35 static HRESULT (WINAPI *pCreateAssemblyCacheSxs)( IAssemblyCache **, DWORD );
36 static HRESULT (WINAPI *pLoadLibraryShim)( LPCWSTR, LPCWSTR, LPVOID, HMODULE * );
38 static BOOL init_function_pointers( void )
40 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
41 HMODULE hfusion, hmscoree, hsxs;
44 if (pCreateAssemblyCacheNet) return TRUE;
46 if (!(hmscoree = LoadLibraryA( "mscoree.dll" )))
48 WARN("mscoree.dll not available\n");
51 if (!(pLoadLibraryShim = (void *)GetProcAddress( hmscoree, "LoadLibraryShim" )))
53 WARN("LoadLibraryShim not available\n");
54 FreeLibrary( hmscoree );
57 hr = pLoadLibraryShim( szFusion, NULL, NULL, &hfusion );
60 WARN("fusion.dll not available 0x%08x\n", hr);
61 FreeLibrary( hmscoree );
64 pCreateAssemblyCacheNet = (void *)GetProcAddress( hfusion, "CreateAssemblyCache" );
65 FreeLibrary( hmscoree );
66 if (!(hsxs = LoadLibraryA( "sxs.dll" )))
68 WARN("sxs.dll not available\n");
69 FreeLibrary( hfusion );
72 pCreateAssemblyCacheSxs = (void *)GetProcAddress( hsxs, "CreateAssemblyCache" );
76 static BOOL init_assembly_caches( MSIPACKAGE *package )
80 if (!init_function_pointers()) return FALSE;
81 if (package->cache_net) return TRUE;
83 hr = pCreateAssemblyCacheNet( &package->cache_net, 0 );
84 if (hr != S_OK) return FALSE;
86 hr = pCreateAssemblyCacheSxs( &package->cache_sxs, 0 );
89 IAssemblyCache_Release( package->cache_net );
90 package->cache_net = NULL;
96 MSIRECORD *get_assembly_record( MSIPACKAGE *package, const WCHAR *comp )
98 static const WCHAR query[] = {
99 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
100 '`','M','s','i','A','s','s','e','m','b','l','y','`',' ',
101 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
102 ' ','=',' ','\'','%','s','\'',0};
107 r = MSI_OpenQuery( package->db, &view, query, comp );
108 if (r != ERROR_SUCCESS)
111 r = MSI_ViewExecute( view, NULL );
112 if (r != ERROR_SUCCESS)
114 msiobj_release( &view->hdr );
117 r = MSI_ViewFetch( view, &rec );
118 if (r != ERROR_SUCCESS)
120 msiobj_release( &view->hdr );
123 if (!MSI_RecordGetString( rec, 4 ))
124 TRACE("component is a global assembly\n");
126 msiobj_release( &view->hdr );
137 static UINT get_assembly_name_attribute( MSIRECORD *rec, LPVOID param )
139 static const WCHAR fmtW[] = {'%','s','=','"','%','s','"',0};
140 static const WCHAR nameW[] = {'n','a','m','e',0};
141 struct assembly_name *name = param;
142 const WCHAR *attr = MSI_RecordGetString( rec, 2 );
143 const WCHAR *value = MSI_RecordGetString( rec, 3 );
144 int len = strlenW( fmtW ) + strlenW( attr ) + strlenW( value );
146 if (!(name->attrs[name->index] = msi_alloc( len * sizeof(WCHAR) )))
147 return ERROR_OUTOFMEMORY;
149 if (!strcmpiW( attr, nameW )) strcpyW( name->attrs[name->index++], value );
150 else sprintfW( name->attrs[name->index++], fmtW, attr, value );
151 return ERROR_SUCCESS;
154 static WCHAR *get_assembly_display_name( MSIDATABASE *db, const WCHAR *comp, MSIASSEMBLY *assembly )
156 static const WCHAR commaW[] = {',',0};
157 static const WCHAR queryW[] = {
158 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
159 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
160 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
161 ' ','=',' ','\'','%','s','\'',0};
162 struct assembly_name name;
163 WCHAR *display_name = NULL;
168 r = MSI_OpenQuery( db, &view, queryW, comp );
169 if (r != ERROR_SUCCESS)
175 MSI_IterateRecords( view, &name.count, NULL, NULL );
176 if (!name.count) goto done;
178 name.attrs = msi_alloc( name.count * sizeof(WCHAR *) );
179 if (!name.attrs) goto done;
181 MSI_IterateRecords( view, NULL, get_assembly_name_attribute, &name );
184 for (i = 0; i < name.count; i++) len += strlenW( name.attrs[i] ) + 1;
186 display_name = msi_alloc( (len + 1) * sizeof(WCHAR) );
190 for (i = 0; i < name.count; i++)
192 strcatW( display_name, name.attrs[i] );
193 if (i < name.count - 1) strcatW( display_name, commaW );
198 msiobj_release( &view->hdr );
199 for (i = 0; i < name.count; i++) msi_free( name.attrs[i] );
200 msi_free( name.attrs );
204 static BOOL check_assembly_installed( MSIPACKAGE *package, MSIASSEMBLY *assembly )
206 IAssemblyCache *cache;
210 if (assembly->application)
212 FIXME("we should probably check the manifest file here\n");
213 if (msi_get_property_int( package->db, szInstalled, 0 )) return TRUE;
217 if (!init_assembly_caches( package ))
220 if (assembly->attributes == msidbAssemblyAttributesWin32)
221 cache = package->cache_sxs;
223 cache = package->cache_net;
225 memset( &info, 0, sizeof(info) );
226 info.cbAssemblyInfo = sizeof(info);
227 hr = IAssemblyCache_QueryAssemblyInfo( cache, QUERYASMINFO_FLAG_VALIDATE, assembly->display_name, &info );
228 if (hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
231 return (info.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
234 MSIASSEMBLY *load_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
239 if (!(rec = get_assembly_record( package, comp->Component )))
242 if (!(a = msi_alloc_zero( sizeof(MSIASSEMBLY) )))
244 msiobj_release( &rec->hdr );
247 a->feature = strdupW( MSI_RecordGetString( rec, 2 ) );
248 TRACE("feature %s\n", debugstr_w(a->feature));
250 a->manifest = strdupW( MSI_RecordGetString( rec, 3 ) );
251 TRACE("manifest %s\n", debugstr_w(a->manifest));
253 a->application = strdupW( MSI_RecordGetString( rec, 4 ) );
254 TRACE("application %s\n", debugstr_w(a->application));
256 a->attributes = MSI_RecordGetInteger( rec, 5 );
257 TRACE("attributes %u\n", a->attributes);
259 if (!(a->display_name = get_assembly_display_name( package->db, comp->Component, a )))
261 WARN("can't get display name\n");
262 msiobj_release( &rec->hdr );
263 msi_free( a->feature );
264 msi_free( a->manifest );
265 msi_free( a->application );
269 TRACE("display name %s\n", debugstr_w(a->display_name));
271 a->installed = check_assembly_installed( package, a );
272 TRACE("assembly is %s\n", a->installed ? "installed" : "not installed");
274 msiobj_release( &rec->hdr );
278 UINT install_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
281 const WCHAR *manifest;
282 IAssemblyCache *cache;
283 MSIASSEMBLY *assembly = comp->assembly;
284 MSIFEATURE *feature = NULL;
286 if (comp->assembly->feature)
287 feature = get_loaded_feature( package, comp->assembly->feature );
289 if (assembly->application)
291 if (feature) feature->Action = INSTALLSTATE_LOCAL;
292 return ERROR_SUCCESS;
294 if (assembly->attributes == msidbAssemblyAttributesWin32)
296 if (!assembly->manifest)
298 WARN("no manifest\n");
299 return ERROR_FUNCTION_FAILED;
301 manifest = get_loaded_file( package, assembly->manifest )->TargetPath;
302 cache = package->cache_sxs;
306 manifest = get_loaded_file( package, comp->KeyPath )->TargetPath;
307 cache = package->cache_net;
309 TRACE("installing assembly %s\n", debugstr_w(manifest));
311 hr = IAssemblyCache_InstallAssembly( cache, 0, manifest, NULL );
314 ERR("Failed to install assembly %s (0x%08x)\n", debugstr_w(manifest), hr);
315 return ERROR_FUNCTION_FAILED;
317 if (feature) feature->Action = INSTALLSTATE_LOCAL;
318 assembly->installed = TRUE;
319 return ERROR_SUCCESS;
322 static WCHAR *build_local_assembly_path( const WCHAR *filename )
327 if (!(ret = msi_alloc( (strlenW( filename ) + 1) * sizeof(WCHAR) )))
330 for (i = 0; filename[i]; i++)
332 if (filename[i] == '\\' || filename[i] == '/') ret[i] = '|';
333 else ret[i] = filename[i];
339 static LONG open_assemblies_key( UINT context, BOOL win32, HKEY *hkey )
341 static const WCHAR path_win32[] =
342 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
343 '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};
344 static const WCHAR path_dotnet[] =
345 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
346 'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0};
347 static const WCHAR classes_path_win32[] =
348 {'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};
349 static const WCHAR classes_path_dotnet[] =
350 {'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0};
354 if (context == MSIINSTALLCONTEXT_MACHINE)
356 root = HKEY_CLASSES_ROOT;
357 if (win32) path = classes_path_win32;
358 else path = classes_path_dotnet;
362 root = HKEY_CURRENT_USER;
363 if (win32) path = path_win32;
364 else path = path_dotnet;
366 return RegCreateKeyW( root, path, hkey );
369 static LONG open_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename, HKEY *hkey )
375 if (!(path = build_local_assembly_path( filename )))
376 return ERROR_OUTOFMEMORY;
378 if ((res = open_assemblies_key( context, win32, &root )))
383 res = RegCreateKeyW( root, path, hkey );
389 static LONG delete_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename )
395 if (!(path = build_local_assembly_path( filename )))
396 return ERROR_OUTOFMEMORY;
398 if ((res = open_assemblies_key( context, win32, &root )))
403 res = RegDeleteKeyW( root, path );
409 static LONG open_global_assembly_key( UINT context, BOOL win32, HKEY *hkey )
411 static const WCHAR path_win32[] =
412 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
413 'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',
414 'G','l','o','b','a','l',0};
415 static const WCHAR path_dotnet[] =
416 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
417 'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',
418 'G','l','o','b','a','l',0};
419 static const WCHAR classes_path_win32[] =
420 {'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',
421 'G','l','o','b','a','l',0};
422 static const WCHAR classes_path_dotnet[] =
423 {'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};
427 if (context == MSIINSTALLCONTEXT_MACHINE)
429 root = HKEY_CLASSES_ROOT;
430 if (win32) path = classes_path_win32;
431 else path = classes_path_dotnet;
435 root = HKEY_CURRENT_USER;
436 if (win32) path = path_win32;
437 else path = path_dotnet;
439 return RegCreateKeyW( root, path, hkey );
442 UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
446 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
454 MSIASSEMBLY *assembly = comp->assembly;
457 if (!assembly || !comp->ComponentId) continue;
461 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
465 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
467 TRACE("Component not scheduled for installation: %s\n", debugstr_w(comp->Component));
468 comp->Action = comp->Installed;
471 comp->Action = INSTALLSTATE_LOCAL;
473 TRACE("publishing %s\n", debugstr_w(comp->Component));
475 CLSIDFromString( package->ProductCode, &guid );
476 encode_base85_guid( &guid, buffer );
478 CLSIDFromString( comp->ComponentId, &guid );
479 encode_base85_guid( &guid, buffer + 21 );
482 win32 = assembly->attributes & msidbAssemblyAttributesWin32;
483 if (assembly->application)
485 MSIFILE *file = get_loaded_file( package, assembly->application );
486 if ((res = open_local_assembly_key( package->Context, win32, file->TargetPath, &hkey )))
488 WARN("failed to open local assembly key %d\n", res);
489 return ERROR_FUNCTION_FAILED;
494 if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
496 WARN("failed to open global assembly key %d\n", res);
497 return ERROR_FUNCTION_FAILED;
500 size = sizeof(buffer);
501 if ((res = RegSetValueExW( hkey, assembly->display_name, 0, REG_MULTI_SZ, (const BYTE *)buffer, size )))
503 WARN("failed to set assembly value %d\n", res);
507 uirow = MSI_CreateRecord( 2 );
508 MSI_RecordSetStringW( uirow, 2, assembly->display_name );
509 ui_actiondata( package, szMsiPublishAssemblies, uirow );
510 msiobj_release( &uirow->hdr );
512 return ERROR_SUCCESS;
515 UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
519 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
523 MSIASSEMBLY *assembly = comp->assembly;
526 if (!assembly || !comp->ComponentId) continue;
530 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
534 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
536 TRACE("Component not scheduled for removal: %s\n", debugstr_w(comp->Component));
537 comp->Action = comp->Installed;
540 comp->Action = INSTALLSTATE_ABSENT;
542 TRACE("unpublishing %s\n", debugstr_w(comp->Component));
544 win32 = assembly->attributes & msidbAssemblyAttributesWin32;
545 if (assembly->application)
547 MSIFILE *file = get_loaded_file( package, assembly->application );
548 if ((res = delete_local_assembly_key( package->Context, win32, file->TargetPath )))
549 WARN("failed to delete local assembly key %d\n", res);
554 if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
555 WARN("failed to delete global assembly key %d\n", res);
558 if ((res = RegDeleteValueW( hkey, assembly->display_name )))
559 WARN("failed to delete global assembly value %d\n", res);
564 uirow = MSI_CreateRecord( 2 );
565 MSI_RecordSetStringW( uirow, 2, assembly->display_name );
566 ui_actiondata( package, szMsiPublishAssemblies, uirow );
567 msiobj_release( &uirow->hdr );
569 return ERROR_SUCCESS;