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"
31 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
33 static BOOL
load_fusion_dlls( MSIPACKAGE
*package
)
35 HRESULT (WINAPI
*pLoadLibraryShim
)( const WCHAR
*, const WCHAR
*, void *, HMODULE
* );
37 DWORD len
= GetSystemDirectoryW( path
, MAX_PATH
);
39 lstrcpyW( path
+ len
, L
"\\mscoree.dll" );
40 if (package
->hmscoree
|| !(package
->hmscoree
= LoadLibraryW( path
))) return TRUE
;
41 if (!(pLoadLibraryShim
= (void *)GetProcAddress( package
->hmscoree
, "LoadLibraryShim" )))
43 FreeLibrary( package
->hmscoree
);
44 package
->hmscoree
= NULL
;
48 pLoadLibraryShim( L
"fusion.dll", L
"v1.0.3705", NULL
, &package
->hfusion10
);
49 pLoadLibraryShim( L
"fusion.dll", L
"v1.1.4322", NULL
, &package
->hfusion11
);
50 pLoadLibraryShim( L
"fusion.dll", L
"v2.0.50727", NULL
, &package
->hfusion20
);
51 pLoadLibraryShim( L
"fusion.dll", L
"v4.0.30319", NULL
, &package
->hfusion40
);
56 BOOL
msi_init_assembly_caches( MSIPACKAGE
*package
)
58 HRESULT (WINAPI
*pCreateAssemblyCache
)( IAssemblyCache
**, DWORD
);
60 if (package
->cache_sxs
) return TRUE
;
61 if (CreateAssemblyCache( &package
->cache_sxs
, 0 ) != S_OK
) return FALSE
;
63 if (!load_fusion_dlls( package
)) return FALSE
;
64 package
->pGetFileVersion
= (void *)GetProcAddress( package
->hmscoree
, "GetFileVersion" ); /* missing from v1.0.3705 */
66 if (package
->hfusion10
)
68 pCreateAssemblyCache
= (void *)GetProcAddress( package
->hfusion10
, "CreateAssemblyCache" );
69 pCreateAssemblyCache( &package
->cache_net
[CLR_VERSION_V10
], 0 );
71 if (package
->hfusion11
)
73 pCreateAssemblyCache
= (void *)GetProcAddress( package
->hfusion11
, "CreateAssemblyCache" );
74 pCreateAssemblyCache( &package
->cache_net
[CLR_VERSION_V11
], 0 );
76 if (package
->hfusion20
)
78 pCreateAssemblyCache
= (void *)GetProcAddress( package
->hfusion20
, "CreateAssemblyCache" );
79 pCreateAssemblyCache( &package
->cache_net
[CLR_VERSION_V20
], 0 );
81 if (package
->hfusion40
)
83 pCreateAssemblyCache
= (void *)GetProcAddress( package
->hfusion40
, "CreateAssemblyCache" );
84 pCreateAssemblyCache( &package
->cache_net
[CLR_VERSION_V40
], 0 );
85 package
->pCreateAssemblyNameObject
= (void *)GetProcAddress( package
->hfusion40
, "CreateAssemblyNameObject" );
86 package
->pCreateAssemblyEnum
= (void *)GetProcAddress( package
->hfusion40
, "CreateAssemblyEnum" );
92 void msi_destroy_assembly_caches( MSIPACKAGE
*package
)
96 if (package
->cache_sxs
)
98 IAssemblyCache_Release( package
->cache_sxs
);
99 package
->cache_sxs
= NULL
;
101 for (i
= 0; i
< CLR_VERSION_MAX
; i
++)
103 if (package
->cache_net
[i
])
105 IAssemblyCache_Release( package
->cache_net
[i
] );
106 package
->cache_net
[i
] = NULL
;
109 package
->pGetFileVersion
= NULL
;
110 package
->pCreateAssemblyNameObject
= NULL
;
111 package
->pCreateAssemblyEnum
= NULL
;
112 FreeLibrary( package
->hfusion10
);
113 FreeLibrary( package
->hfusion11
);
114 FreeLibrary( package
->hfusion20
);
115 FreeLibrary( package
->hfusion40
);
116 FreeLibrary( package
->hmscoree
);
117 package
->hfusion10
= NULL
;
118 package
->hfusion11
= NULL
;
119 package
->hfusion20
= NULL
;
120 package
->hfusion40
= NULL
;
121 package
->hmscoree
= NULL
;
124 static MSIRECORD
*get_assembly_record( MSIPACKAGE
*package
, const WCHAR
*comp
)
130 r
= MSI_OpenQuery( package
->db
, &view
, L
"SELECT * FROM `MsiAssembly` WHERE `Component_` = '%s'", comp
);
131 if (r
!= ERROR_SUCCESS
)
134 r
= MSI_ViewExecute( view
, NULL
);
135 if (r
!= ERROR_SUCCESS
)
137 msiobj_release( &view
->hdr
);
140 r
= MSI_ViewFetch( view
, &rec
);
141 if (r
!= ERROR_SUCCESS
)
143 msiobj_release( &view
->hdr
);
146 if (!MSI_RecordGetString( rec
, 4 ))
147 TRACE("component is a global assembly\n");
149 msiobj_release( &view
->hdr
);
160 static UINT
get_assembly_name_attribute( MSIRECORD
*rec
, LPVOID param
)
162 struct assembly_name
*name
= param
;
163 const WCHAR
*attr
= MSI_RecordGetString( rec
, 2 );
164 const WCHAR
*value
= MSI_RecordGetString( rec
, 3 );
165 int len
= lstrlenW( L
"%s=\"%s\"" ) + lstrlenW( attr
) + lstrlenW( value
);
167 if (!(name
->attrs
[name
->index
] = msi_alloc( len
* sizeof(WCHAR
) )))
168 return ERROR_OUTOFMEMORY
;
170 if (!wcsicmp( attr
, L
"name" )) lstrcpyW( name
->attrs
[name
->index
++], value
);
171 else swprintf( name
->attrs
[name
->index
++], len
, L
"%s=\"%s\"", attr
, value
);
172 return ERROR_SUCCESS
;
175 static WCHAR
*get_assembly_display_name( MSIDATABASE
*db
, const WCHAR
*comp
, MSIASSEMBLY
*assembly
)
177 struct assembly_name name
;
178 WCHAR
*display_name
= NULL
;
183 r
= MSI_OpenQuery( db
, &view
, L
"SELECT * FROM `MsiAssemblyName` WHERE `Component_` = '%s'", comp
);
184 if (r
!= ERROR_SUCCESS
)
190 MSI_IterateRecords( view
, &name
.count
, NULL
, NULL
);
191 if (!name
.count
) goto done
;
193 name
.attrs
= msi_alloc( name
.count
* sizeof(WCHAR
*) );
194 if (!name
.attrs
) goto done
;
196 MSI_IterateRecords( view
, NULL
, get_assembly_name_attribute
, &name
);
199 for (i
= 0; i
< name
.count
; i
++) len
+= lstrlenW( name
.attrs
[i
] ) + 1;
201 display_name
= msi_alloc( (len
+ 1) * sizeof(WCHAR
) );
205 for (i
= 0; i
< name
.count
; i
++)
207 lstrcatW( display_name
, name
.attrs
[i
] );
208 if (i
< name
.count
- 1) lstrcatW( display_name
, L
"," );
213 msiobj_release( &view
->hdr
);
216 for (i
= 0; i
< name
.count
; i
++) msi_free( name
.attrs
[i
] );
217 msi_free( name
.attrs
);
222 static BOOL
is_assembly_installed( IAssemblyCache
*cache
, const WCHAR
*display_name
)
227 if (!cache
) return FALSE
;
229 memset( &info
, 0, sizeof(info
) );
230 info
.cbAssemblyInfo
= sizeof(info
);
231 hr
= IAssemblyCache_QueryAssemblyInfo( cache
, 0, display_name
, &info
);
232 if (hr
== S_OK
/* sxs version */ || hr
== E_NOT_SUFFICIENT_BUFFER
)
234 return (info
.dwAssemblyFlags
== ASSEMBLYINFO_FLAG_INSTALLED
);
236 TRACE("QueryAssemblyInfo returned 0x%08x\n", hr
);
240 WCHAR
*msi_get_assembly_path( MSIPACKAGE
*package
, const WCHAR
*displayname
)
244 IAssemblyCache
*cache
= package
->cache_net
[CLR_VERSION_V40
];
246 if (!cache
) return NULL
;
248 memset( &info
, 0, sizeof(info
) );
249 info
.cbAssemblyInfo
= sizeof(info
);
250 hr
= IAssemblyCache_QueryAssemblyInfo( cache
, 0, displayname
, &info
);
251 if (hr
!= E_NOT_SUFFICIENT_BUFFER
) return NULL
;
253 if (!(info
.pszCurrentAssemblyPathBuf
= msi_alloc( info
.cchBuf
* sizeof(WCHAR
) ))) return NULL
;
255 hr
= IAssemblyCache_QueryAssemblyInfo( cache
, 0, displayname
, &info
);
258 msi_free( info
.pszCurrentAssemblyPathBuf
);
261 TRACE("returning %s\n", debugstr_w(info
.pszCurrentAssemblyPathBuf
));
262 return info
.pszCurrentAssemblyPathBuf
;
265 IAssemblyEnum
*msi_create_assembly_enum( MSIPACKAGE
*package
, const WCHAR
*displayname
)
273 if (!package
->pCreateAssemblyNameObject
|| !package
->pCreateAssemblyEnum
) return NULL
;
275 hr
= package
->pCreateAssemblyNameObject( &name
, displayname
, CANOF_PARSE_DISPLAY_NAME
, NULL
);
276 if (FAILED( hr
)) return NULL
;
278 hr
= IAssemblyName_GetName( name
, &len
, NULL
);
279 if (hr
!= E_NOT_SUFFICIENT_BUFFER
|| !(str
= msi_alloc( len
* sizeof(WCHAR
) )))
281 IAssemblyName_Release( name
);
285 hr
= IAssemblyName_GetName( name
, &len
, str
);
286 IAssemblyName_Release( name
);
293 hr
= package
->pCreateAssemblyNameObject( &name
, str
, 0, NULL
);
295 if (FAILED( hr
)) return NULL
;
297 hr
= package
->pCreateAssemblyEnum( &ret
, NULL
, name
, ASM_CACHE_GAC
, NULL
);
298 IAssemblyName_Release( name
);
299 if (FAILED( hr
)) return NULL
;
304 static const WCHAR
*clr_version
[] =
312 static const WCHAR
*get_clr_version_str( enum clr_version version
)
314 if (version
>= ARRAY_SIZE( clr_version
)) return L
"unknown";
315 return clr_version
[version
];
318 /* assembly caches must be initialized */
319 MSIASSEMBLY
*msi_load_assembly( MSIPACKAGE
*package
, MSICOMPONENT
*comp
)
324 if (!(rec
= get_assembly_record( package
, comp
->Component
))) return NULL
;
325 if (!(a
= msi_alloc_zero( sizeof(MSIASSEMBLY
) )))
327 msiobj_release( &rec
->hdr
);
330 a
->feature
= strdupW( MSI_RecordGetString( rec
, 2 ) );
331 TRACE("feature %s\n", debugstr_w(a
->feature
));
333 a
->manifest
= strdupW( MSI_RecordGetString( rec
, 3 ) );
334 TRACE("manifest %s\n", debugstr_w(a
->manifest
));
336 a
->application
= strdupW( MSI_RecordGetString( rec
, 4 ) );
337 TRACE("application %s\n", debugstr_w(a
->application
));
339 a
->attributes
= MSI_RecordGetInteger( rec
, 5 );
340 TRACE("attributes %u\n", a
->attributes
);
342 if (!(a
->display_name
= get_assembly_display_name( package
->db
, comp
->Component
, a
)))
344 WARN("can't get display name\n");
345 msiobj_release( &rec
->hdr
);
346 msi_free( a
->feature
);
347 msi_free( a
->manifest
);
348 msi_free( a
->application
);
352 TRACE("display name %s\n", debugstr_w(a
->display_name
));
356 /* We can't check the manifest here because the target path may still change.
357 So we assume that the assembly is not installed and lean on the InstallFiles
358 action to determine which files need to be installed.
360 a
->installed
= FALSE
;
364 if (a
->attributes
== msidbAssemblyAttributesWin32
)
365 a
->installed
= is_assembly_installed( package
->cache_sxs
, a
->display_name
);
369 for (i
= 0; i
< CLR_VERSION_MAX
; i
++)
371 a
->clr_version
[i
] = is_assembly_installed( package
->cache_net
[i
], a
->display_name
);
372 if (a
->clr_version
[i
])
374 TRACE("runtime version %s\n", debugstr_w(get_clr_version_str( i
)));
381 TRACE("assembly is %s\n", a
->installed
? "installed" : "not installed");
382 msiobj_release( &rec
->hdr
);
386 static enum clr_version
get_clr_version( MSIPACKAGE
*package
, const WCHAR
*filename
)
390 enum clr_version version
= CLR_VERSION_V11
;
393 if (!package
->pGetFileVersion
) return CLR_VERSION_V10
;
395 hr
= package
->pGetFileVersion( filename
, NULL
, 0, &len
);
396 if (hr
!= E_NOT_SUFFICIENT_BUFFER
) return CLR_VERSION_V11
;
397 if ((strW
= msi_alloc( len
* sizeof(WCHAR
) )))
399 hr
= package
->pGetFileVersion( filename
, strW
, len
, &len
);
403 for (i
= 0; i
< CLR_VERSION_MAX
; i
++)
404 if (!wcscmp( strW
, clr_version
[i
] )) version
= i
;
411 UINT
msi_install_assembly( MSIPACKAGE
*package
, MSICOMPONENT
*comp
)
414 const WCHAR
*manifest
;
415 IAssemblyCache
*cache
;
416 MSIASSEMBLY
*assembly
= comp
->assembly
;
417 MSIFEATURE
*feature
= NULL
;
419 if (comp
->assembly
->feature
)
420 feature
= msi_get_loaded_feature( package
, comp
->assembly
->feature
);
422 if (assembly
->application
)
424 if (feature
) feature
->Action
= INSTALLSTATE_LOCAL
;
425 return ERROR_SUCCESS
;
427 if (assembly
->attributes
== msidbAssemblyAttributesWin32
)
429 if (!assembly
->manifest
)
431 WARN("no manifest\n");
432 return ERROR_FUNCTION_FAILED
;
434 manifest
= msi_get_loaded_file( package
, assembly
->manifest
)->TargetPath
;
435 cache
= package
->cache_sxs
;
439 manifest
= msi_get_loaded_file( package
, comp
->KeyPath
)->TargetPath
;
440 cache
= package
->cache_net
[get_clr_version( package
, manifest
)];
441 if (!cache
) return ERROR_SUCCESS
;
443 TRACE("installing assembly %s\n", debugstr_w(manifest
));
445 hr
= IAssemblyCache_InstallAssembly( cache
, 0, manifest
, NULL
);
448 ERR("Failed to install assembly %s (0x%08x)\n", debugstr_w(manifest
), hr
);
449 return ERROR_FUNCTION_FAILED
;
451 if (feature
) feature
->Action
= INSTALLSTATE_LOCAL
;
452 assembly
->installed
= TRUE
;
453 return ERROR_SUCCESS
;
456 UINT
msi_uninstall_assembly( MSIPACKAGE
*package
, MSICOMPONENT
*comp
)
459 IAssemblyCache
*cache
;
460 MSIASSEMBLY
*assembly
= comp
->assembly
;
461 MSIFEATURE
*feature
= NULL
;
463 if (comp
->assembly
->feature
)
464 feature
= msi_get_loaded_feature( package
, comp
->assembly
->feature
);
466 if (assembly
->application
)
468 if (feature
) feature
->Action
= INSTALLSTATE_ABSENT
;
469 return ERROR_SUCCESS
;
471 TRACE("removing %s\n", debugstr_w(assembly
->display_name
));
473 if (assembly
->attributes
== msidbAssemblyAttributesWin32
)
475 cache
= package
->cache_sxs
;
476 hr
= IAssemblyCache_UninstallAssembly( cache
, 0, assembly
->display_name
, NULL
, NULL
);
477 if (FAILED( hr
)) WARN("failed to uninstall assembly 0x%08x\n", hr
);
482 for (i
= 0; i
< CLR_VERSION_MAX
; i
++)
484 if (!assembly
->clr_version
[i
]) continue;
485 cache
= package
->cache_net
[i
];
488 hr
= IAssemblyCache_UninstallAssembly( cache
, 0, assembly
->display_name
, NULL
, NULL
);
489 if (FAILED( hr
)) WARN("failed to uninstall assembly 0x%08x\n", hr
);
493 if (feature
) feature
->Action
= INSTALLSTATE_ABSENT
;
494 assembly
->installed
= FALSE
;
495 return ERROR_SUCCESS
;
498 static WCHAR
*build_local_assembly_path( const WCHAR
*filename
)
503 if (!(ret
= msi_alloc( (lstrlenW( filename
) + 1) * sizeof(WCHAR
) )))
506 for (i
= 0; filename
[i
]; i
++)
508 if (filename
[i
] == '\\' || filename
[i
] == '/') ret
[i
] = '|';
509 else ret
[i
] = filename
[i
];
515 static LONG
open_assemblies_key( UINT context
, BOOL win32
, HKEY
*hkey
)
520 if (context
== MSIINSTALLCONTEXT_MACHINE
)
522 root
= HKEY_CLASSES_ROOT
;
523 if (win32
) path
= L
"Installer\\Win32Assemblies\\";
524 else path
= L
"Installer\\Assemblies\\";
528 root
= HKEY_CURRENT_USER
;
529 if (win32
) path
= L
"Software\\Microsoft\\Installer\\Win32Assemblies\\";
530 else path
= L
"Software\\Microsoft\\Installer\\Assemblies\\";
532 return RegCreateKeyW( root
, path
, hkey
);
535 static LONG
open_local_assembly_key( UINT context
, BOOL win32
, const WCHAR
*filename
, HKEY
*hkey
)
541 if (!(path
= build_local_assembly_path( filename
)))
542 return ERROR_OUTOFMEMORY
;
544 if ((res
= open_assemblies_key( context
, win32
, &root
)))
549 res
= RegCreateKeyW( root
, path
, hkey
);
555 static LONG
delete_local_assembly_key( UINT context
, BOOL win32
, const WCHAR
*filename
)
561 if (!(path
= build_local_assembly_path( filename
)))
562 return ERROR_OUTOFMEMORY
;
564 if ((res
= open_assemblies_key( context
, win32
, &root
)))
569 res
= RegDeleteKeyW( root
, path
);
575 static LONG
open_global_assembly_key( UINT context
, BOOL win32
, HKEY
*hkey
)
580 if (context
== MSIINSTALLCONTEXT_MACHINE
)
582 root
= HKEY_CLASSES_ROOT
;
583 if (win32
) path
= L
"Installer\\Win32Assemblies\\Global";
584 else path
= L
"Installer\\Assemblies\\Global";
588 root
= HKEY_CURRENT_USER
;
589 if (win32
) path
= L
"Software\\Microsoft\\Installer\\Win32Assemblies\\Global";
590 else path
= L
"Software\\Microsoft\\Installer\\Assemblies\\Global";
592 return RegCreateKeyW( root
, path
, hkey
);
595 UINT
ACTION_MsiPublishAssemblies( MSIPACKAGE
*package
)
599 if (package
->script
== SCRIPT_NONE
)
600 return msi_schedule_action(package
, SCRIPT_INSTALL
, L
"MsiPublishAssemblies");
602 LIST_FOR_EACH_ENTRY(comp
, &package
->components
, MSICOMPONENT
, entry
)
610 MSIASSEMBLY
*assembly
= comp
->assembly
;
613 if (!assembly
|| !comp
->ComponentId
) continue;
615 comp
->Action
= msi_get_component_action( package
, comp
);
616 if (comp
->Action
!= INSTALLSTATE_LOCAL
)
618 TRACE("component not scheduled for installation %s\n", debugstr_w(comp
->Component
));
621 TRACE("publishing %s\n", debugstr_w(comp
->Component
));
623 CLSIDFromString( package
->ProductCode
, &guid
);
624 encode_base85_guid( &guid
, buffer
);
626 CLSIDFromString( comp
->ComponentId
, &guid
);
627 encode_base85_guid( &guid
, buffer
+ 21 );
630 win32
= assembly
->attributes
& msidbAssemblyAttributesWin32
;
631 if (assembly
->application
)
633 MSIFILE
*file
= msi_get_loaded_file( package
, assembly
->application
);
636 WARN("no matching file %s for local assembly\n", debugstr_w(assembly
->application
));
639 if ((res
= open_local_assembly_key( package
->Context
, win32
, file
->TargetPath
, &hkey
)))
641 WARN("failed to open local assembly key %d\n", res
);
642 return ERROR_FUNCTION_FAILED
;
647 if ((res
= open_global_assembly_key( package
->Context
, win32
, &hkey
)))
649 WARN("failed to open global assembly key %d\n", res
);
650 return ERROR_FUNCTION_FAILED
;
653 size
= sizeof(buffer
);
654 if ((res
= RegSetValueExW( hkey
, assembly
->display_name
, 0, REG_MULTI_SZ
, (const BYTE
*)buffer
, size
)))
656 WARN("failed to set assembly value %d\n", res
);
660 uirow
= MSI_CreateRecord( 2 );
661 MSI_RecordSetStringW( uirow
, 2, assembly
->display_name
);
662 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONDATA
, uirow
);
663 msiobj_release( &uirow
->hdr
);
665 return ERROR_SUCCESS
;
668 UINT
ACTION_MsiUnpublishAssemblies( MSIPACKAGE
*package
)
672 if (package
->script
== SCRIPT_NONE
)
673 return msi_schedule_action(package
, SCRIPT_INSTALL
, L
"MsiUnpublishAssemblies");
675 LIST_FOR_EACH_ENTRY(comp
, &package
->components
, MSICOMPONENT
, entry
)
679 MSIASSEMBLY
*assembly
= comp
->assembly
;
682 if (!assembly
|| !comp
->ComponentId
) continue;
684 comp
->Action
= msi_get_component_action( package
, comp
);
685 if (comp
->Action
!= INSTALLSTATE_ABSENT
)
687 TRACE("component not scheduled for removal %s\n", debugstr_w(comp
->Component
));
690 TRACE("unpublishing %s\n", debugstr_w(comp
->Component
));
692 win32
= assembly
->attributes
& msidbAssemblyAttributesWin32
;
693 if (assembly
->application
)
695 MSIFILE
*file
= msi_get_loaded_file( package
, assembly
->application
);
698 WARN("no matching file %s for local assembly\n", debugstr_w(assembly
->application
));
701 if ((res
= delete_local_assembly_key( package
->Context
, win32
, file
->TargetPath
)))
702 WARN("failed to delete local assembly key %d\n", res
);
707 if ((res
= open_global_assembly_key( package
->Context
, win32
, &hkey
)))
708 WARN("failed to delete global assembly key %d\n", res
);
711 if ((res
= RegDeleteValueW( hkey
, assembly
->display_name
)))
712 WARN("failed to delete global assembly value %d\n", res
);
717 uirow
= MSI_CreateRecord( 2 );
718 MSI_RecordSetStringW( uirow
, 2, assembly
->display_name
);
719 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONDATA
, uirow
);
720 msiobj_release( &uirow
->hdr
);
722 return ERROR_SUCCESS
;