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
*pCreateAssemblyCacheNet10
)( IAssemblyCache
**, DWORD
);
35 static HRESULT (WINAPI
*pCreateAssemblyCacheNet11
)( IAssemblyCache
**, DWORD
);
36 static HRESULT (WINAPI
*pCreateAssemblyCacheNet20
)( IAssemblyCache
**, DWORD
);
37 static HRESULT (WINAPI
*pCreateAssemblyCacheSxs
)( IAssemblyCache
**, DWORD
);
38 static HRESULT (WINAPI
*pLoadLibraryShim
)( LPCWSTR
, LPCWSTR
, LPVOID
, HMODULE
* );
39 static HRESULT (WINAPI
*pGetFileVersion
)( LPCWSTR
, LPWSTR
, DWORD
, DWORD
* );
41 static HMODULE hfusion10
, hfusion11
, hfusion20
, hmscoree
, hsxs
;
43 static BOOL
init_function_pointers( void )
45 static const WCHAR szFusion
[] = {'f','u','s','i','o','n','.','d','l','l',0};
46 static const WCHAR szVersion10
[] = {'v','1','.','0','.','3','7','0','5',0};
47 static const WCHAR szVersion11
[] = {'v','1','.','1','.','4','3','2','2',0};
48 static const WCHAR szVersion20
[] = {'v','2','.','0','.','5','0','7','2','7',0};
50 if (pCreateAssemblyCacheNet10
|| pCreateAssemblyCacheNet11
|| pCreateAssemblyCacheNet20
) return TRUE
;
52 if (!(hmscoree
= LoadLibraryA( "mscoree.dll" ))) return FALSE
;
53 pGetFileVersion
= (void *)GetProcAddress( hmscoree
, "GetFileVersion" ); /* missing from v1.0.3705 */
54 if (!(pLoadLibraryShim
= (void *)GetProcAddress( hmscoree
, "LoadLibraryShim" ))) goto error
;
56 if (!pLoadLibraryShim( szFusion
, szVersion10
, NULL
, &hfusion10
))
57 pCreateAssemblyCacheNet10
= (void *)GetProcAddress( hfusion10
, "CreateAssemblyCache" );
59 if (!pLoadLibraryShim( szFusion
, szVersion11
, NULL
, &hfusion11
))
60 pCreateAssemblyCacheNet11
= (void *)GetProcAddress( hfusion11
, "CreateAssemblyCache" );
62 if (!pLoadLibraryShim( szFusion
, szVersion20
, NULL
, &hfusion20
))
63 pCreateAssemblyCacheNet20
= (void *)GetProcAddress( hfusion20
, "CreateAssemblyCache" );
65 if (!pCreateAssemblyCacheNet10
&& !pCreateAssemblyCacheNet11
&& !pCreateAssemblyCacheNet20
) goto error
;
67 if (!(hsxs
= LoadLibraryA( "sxs.dll" ))) goto error
;
68 if (!(pCreateAssemblyCacheSxs
= (void *)GetProcAddress( hsxs
, "CreateAssemblyCache" ))) goto error
;
72 pCreateAssemblyCacheNet10
= NULL
;
73 pCreateAssemblyCacheNet11
= NULL
;
74 pCreateAssemblyCacheNet20
= NULL
;
75 FreeLibrary( hfusion10
);
76 FreeLibrary( hfusion11
);
77 FreeLibrary( hfusion20
);
78 FreeLibrary( hmscoree
);
82 BOOL
msi_init_assembly_caches( MSIPACKAGE
*package
)
84 if (!init_function_pointers()) return FALSE
;
85 if (package
->cache_net
[CLR_VERSION_V10
] ||
86 package
->cache_net
[CLR_VERSION_V11
] ||
87 package
->cache_net
[CLR_VERSION_V20
]) return TRUE
;
88 if (pCreateAssemblyCacheSxs( &package
->cache_sxs
, 0 ) != S_OK
) return FALSE
;
90 if (pCreateAssemblyCacheNet10
) pCreateAssemblyCacheNet11( &package
->cache_net
[CLR_VERSION_V10
], 0 );
91 if (pCreateAssemblyCacheNet11
) pCreateAssemblyCacheNet11( &package
->cache_net
[CLR_VERSION_V11
], 0 );
92 if (pCreateAssemblyCacheNet20
) pCreateAssemblyCacheNet20( &package
->cache_net
[CLR_VERSION_V20
], 0 );
94 if (package
->cache_net
[CLR_VERSION_V10
] ||
95 package
->cache_net
[CLR_VERSION_V11
] ||
96 package
->cache_net
[CLR_VERSION_V20
])
100 if (package
->cache_net
[CLR_VERSION_V10
])
102 IAssemblyCache_Release( package
->cache_net
[CLR_VERSION_V10
] );
103 package
->cache_net
[CLR_VERSION_V10
] = NULL
;
105 if (package
->cache_net
[CLR_VERSION_V11
])
107 IAssemblyCache_Release( package
->cache_net
[CLR_VERSION_V11
] );
108 package
->cache_net
[CLR_VERSION_V11
] = NULL
;
110 if (package
->cache_net
[CLR_VERSION_V20
])
112 IAssemblyCache_Release( package
->cache_net
[CLR_VERSION_V20
] );
113 package
->cache_net
[CLR_VERSION_V20
] = NULL
;
115 IAssemblyCache_Release( package
->cache_sxs
);
116 package
->cache_sxs
= NULL
;
120 void msi_destroy_assembly_caches( MSIPACKAGE
*package
)
124 for (i
= 0; i
< CLR_VERSION_MAX
; i
++)
126 if (package
->cache_net
[i
])
128 IAssemblyCache_Release( package
->cache_net
[i
] );
129 package
->cache_net
[i
] = NULL
;
132 if (package
->cache_sxs
)
134 IAssemblyCache_Release( package
->cache_sxs
);
135 package
->cache_sxs
= NULL
;
137 pCreateAssemblyCacheNet10
= NULL
;
138 pCreateAssemblyCacheNet11
= NULL
;
139 pCreateAssemblyCacheNet20
= NULL
;
140 FreeLibrary( hfusion10
);
141 FreeLibrary( hfusion11
);
142 FreeLibrary( hfusion20
);
143 FreeLibrary( hmscoree
);
147 static MSIRECORD
*get_assembly_record( MSIPACKAGE
*package
, const WCHAR
*comp
)
149 static const WCHAR query
[] = {
150 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
151 '`','M','s','i','A','s','s','e','m','b','l','y','`',' ',
152 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
153 ' ','=',' ','\'','%','s','\'',0};
158 r
= MSI_OpenQuery( package
->db
, &view
, query
, comp
);
159 if (r
!= ERROR_SUCCESS
)
162 r
= MSI_ViewExecute( view
, NULL
);
163 if (r
!= ERROR_SUCCESS
)
165 msiobj_release( &view
->hdr
);
168 r
= MSI_ViewFetch( view
, &rec
);
169 if (r
!= ERROR_SUCCESS
)
171 msiobj_release( &view
->hdr
);
174 if (!MSI_RecordGetString( rec
, 4 ))
175 TRACE("component is a global assembly\n");
177 msiobj_release( &view
->hdr
);
188 static UINT
get_assembly_name_attribute( MSIRECORD
*rec
, LPVOID param
)
190 static const WCHAR fmtW
[] = {'%','s','=','"','%','s','"',0};
191 static const WCHAR nameW
[] = {'n','a','m','e',0};
192 struct assembly_name
*name
= param
;
193 const WCHAR
*attr
= MSI_RecordGetString( rec
, 2 );
194 const WCHAR
*value
= MSI_RecordGetString( rec
, 3 );
195 int len
= strlenW( fmtW
) + strlenW( attr
) + strlenW( value
);
197 if (!(name
->attrs
[name
->index
] = msi_alloc( len
* sizeof(WCHAR
) )))
198 return ERROR_OUTOFMEMORY
;
200 if (!strcmpiW( attr
, nameW
)) strcpyW( name
->attrs
[name
->index
++], value
);
201 else sprintfW( name
->attrs
[name
->index
++], fmtW
, attr
, value
);
202 return ERROR_SUCCESS
;
205 static WCHAR
*get_assembly_display_name( MSIDATABASE
*db
, const WCHAR
*comp
, MSIASSEMBLY
*assembly
)
207 static const WCHAR commaW
[] = {',',0};
208 static const WCHAR queryW
[] = {
209 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
210 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
211 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
212 ' ','=',' ','\'','%','s','\'',0};
213 struct assembly_name name
;
214 WCHAR
*display_name
= NULL
;
219 r
= MSI_OpenQuery( db
, &view
, queryW
, comp
);
220 if (r
!= ERROR_SUCCESS
)
226 MSI_IterateRecords( view
, &name
.count
, NULL
, NULL
);
227 if (!name
.count
) goto done
;
229 name
.attrs
= msi_alloc( name
.count
* sizeof(WCHAR
*) );
230 if (!name
.attrs
) goto done
;
232 MSI_IterateRecords( view
, NULL
, get_assembly_name_attribute
, &name
);
235 for (i
= 0; i
< name
.count
; i
++) len
+= strlenW( name
.attrs
[i
] ) + 1;
237 display_name
= msi_alloc( (len
+ 1) * sizeof(WCHAR
) );
241 for (i
= 0; i
< name
.count
; i
++)
243 strcatW( display_name
, name
.attrs
[i
] );
244 if (i
< name
.count
- 1) strcatW( display_name
, commaW
);
249 msiobj_release( &view
->hdr
);
252 for (i
= 0; i
< name
.count
; i
++) msi_free( name
.attrs
[i
] );
253 msi_free( name
.attrs
);
258 static BOOL
is_assembly_installed( IAssemblyCache
*cache
, const WCHAR
*display_name
)
263 memset( &info
, 0, sizeof(info
) );
264 info
.cbAssemblyInfo
= sizeof(info
);
265 hr
= IAssemblyCache_QueryAssemblyInfo( cache
, QUERYASMINFO_FLAG_GETSIZE
, display_name
, &info
);
268 TRACE("QueryAssemblyInfo returned 0x%08x\n", hr
);
271 return (info
.dwAssemblyFlags
== ASSEMBLYINFO_FLAG_INSTALLED
);
274 static const WCHAR clr_version_v10
[] = {'v','1','.','0','.','3','7','0','5',0};
275 static const WCHAR clr_version_v11
[] = {'v','1','.','1','.','4','3','2','2',0};
276 static const WCHAR clr_version_v20
[] = {'v','2','.','0','.','5','0','7','2','7',0};
277 static const WCHAR clr_version_unknown
[] = {'u','n','k','n','o','w','n',0};
279 static const WCHAR
*clr_version
[] =
286 static const WCHAR
*get_clr_version_str( enum clr_version version
)
288 if (version
>= sizeof(clr_version
)/sizeof(clr_version
[0])) return clr_version_unknown
;
289 return clr_version
[version
];
292 /* assembly caches must be initialized */
293 MSIASSEMBLY
*msi_load_assembly( MSIPACKAGE
*package
, MSICOMPONENT
*comp
)
298 if (!(rec
= get_assembly_record( package
, comp
->Component
))) return NULL
;
299 if (!(a
= msi_alloc_zero( sizeof(MSIASSEMBLY
) )))
301 msiobj_release( &rec
->hdr
);
304 a
->feature
= strdupW( MSI_RecordGetString( rec
, 2 ) );
305 TRACE("feature %s\n", debugstr_w(a
->feature
));
307 a
->manifest
= strdupW( MSI_RecordGetString( rec
, 3 ) );
308 TRACE("manifest %s\n", debugstr_w(a
->manifest
));
310 a
->application
= strdupW( MSI_RecordGetString( rec
, 4 ) );
311 TRACE("application %s\n", debugstr_w(a
->application
));
313 a
->attributes
= MSI_RecordGetInteger( rec
, 5 );
314 TRACE("attributes %u\n", a
->attributes
);
316 if (!(a
->display_name
= get_assembly_display_name( package
->db
, comp
->Component
, a
)))
318 WARN("can't get display name\n");
319 msiobj_release( &rec
->hdr
);
320 msi_free( a
->feature
);
321 msi_free( a
->manifest
);
322 msi_free( a
->application
);
326 TRACE("display name %s\n", debugstr_w(a
->display_name
));
330 /* We can't check the manifest here because the target path may still change.
331 So we assume that the assembly is not installed and lean on the InstallFiles
332 action to determine which files need to be installed.
334 a
->installed
= FALSE
;
338 if (a
->attributes
== msidbAssemblyAttributesWin32
)
339 a
->installed
= is_assembly_installed( package
->cache_sxs
, a
->display_name
);
343 for (i
= 0; i
< CLR_VERSION_MAX
; i
++)
345 a
->clr_version
[i
] = is_assembly_installed( package
->cache_net
[i
], a
->display_name
);
346 if (a
->clr_version
[i
])
348 TRACE("runtime version %s\n", debugstr_w(get_clr_version_str( i
)));
354 TRACE("assembly is %s\n", a
->installed
? "installed" : "not installed");
355 msiobj_release( &rec
->hdr
);
359 static enum clr_version
get_clr_version( const WCHAR
*filename
)
363 enum clr_version version
= CLR_VERSION_V11
;
366 if (!pGetFileVersion
) return CLR_VERSION_V10
;
368 hr
= pGetFileVersion( filename
, NULL
, 0, &len
);
369 if (hr
!= HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER
)) return CLR_VERSION_V11
;
370 if ((strW
= msi_alloc( len
* sizeof(WCHAR
) )))
372 hr
= pGetFileVersion( filename
, strW
, len
, &len
);
376 for (i
= 0; i
< CLR_VERSION_MAX
; i
++)
377 if (!strcmpW( strW
, clr_version
[i
] )) version
= i
;
384 UINT
msi_install_assembly( MSIPACKAGE
*package
, MSICOMPONENT
*comp
)
387 const WCHAR
*manifest
;
388 IAssemblyCache
*cache
;
389 MSIASSEMBLY
*assembly
= comp
->assembly
;
390 MSIFEATURE
*feature
= NULL
;
392 if (comp
->assembly
->feature
)
393 feature
= msi_get_loaded_feature( package
, comp
->assembly
->feature
);
395 if (assembly
->application
)
397 if (feature
) feature
->Action
= INSTALLSTATE_LOCAL
;
398 return ERROR_SUCCESS
;
400 if (assembly
->attributes
== msidbAssemblyAttributesWin32
)
402 if (!assembly
->manifest
)
404 WARN("no manifest\n");
405 return ERROR_FUNCTION_FAILED
;
407 manifest
= msi_get_loaded_file( package
, assembly
->manifest
)->TargetPath
;
408 cache
= package
->cache_sxs
;
412 manifest
= msi_get_loaded_file( package
, comp
->KeyPath
)->TargetPath
;
413 cache
= package
->cache_net
[get_clr_version( manifest
)];
415 TRACE("installing assembly %s\n", debugstr_w(manifest
));
417 hr
= IAssemblyCache_InstallAssembly( cache
, 0, manifest
, NULL
);
420 ERR("Failed to install assembly %s (0x%08x)\n", debugstr_w(manifest
), hr
);
421 return ERROR_FUNCTION_FAILED
;
423 if (feature
) feature
->Action
= INSTALLSTATE_LOCAL
;
424 assembly
->installed
= TRUE
;
425 return ERROR_SUCCESS
;
428 static WCHAR
*build_local_assembly_path( const WCHAR
*filename
)
433 if (!(ret
= msi_alloc( (strlenW( filename
) + 1) * sizeof(WCHAR
) )))
436 for (i
= 0; filename
[i
]; i
++)
438 if (filename
[i
] == '\\' || filename
[i
] == '/') ret
[i
] = '|';
439 else ret
[i
] = filename
[i
];
445 static LONG
open_assemblies_key( UINT context
, BOOL win32
, HKEY
*hkey
)
447 static const WCHAR path_win32
[] =
448 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
449 '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};
450 static const WCHAR path_dotnet
[] =
451 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
452 'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0};
453 static const WCHAR classes_path_win32
[] =
454 {'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};
455 static const WCHAR classes_path_dotnet
[] =
456 {'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0};
460 if (context
== MSIINSTALLCONTEXT_MACHINE
)
462 root
= HKEY_CLASSES_ROOT
;
463 if (win32
) path
= classes_path_win32
;
464 else path
= classes_path_dotnet
;
468 root
= HKEY_CURRENT_USER
;
469 if (win32
) path
= path_win32
;
470 else path
= path_dotnet
;
472 return RegCreateKeyW( root
, path
, hkey
);
475 static LONG
open_local_assembly_key( UINT context
, BOOL win32
, const WCHAR
*filename
, HKEY
*hkey
)
481 if (!(path
= build_local_assembly_path( filename
)))
482 return ERROR_OUTOFMEMORY
;
484 if ((res
= open_assemblies_key( context
, win32
, &root
)))
489 res
= RegCreateKeyW( root
, path
, hkey
);
495 static LONG
delete_local_assembly_key( UINT context
, BOOL win32
, const WCHAR
*filename
)
501 if (!(path
= build_local_assembly_path( filename
)))
502 return ERROR_OUTOFMEMORY
;
504 if ((res
= open_assemblies_key( context
, win32
, &root
)))
509 res
= RegDeleteKeyW( root
, path
);
515 static LONG
open_global_assembly_key( UINT context
, BOOL win32
, HKEY
*hkey
)
517 static const WCHAR path_win32
[] =
518 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
519 'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',
520 'G','l','o','b','a','l',0};
521 static const WCHAR path_dotnet
[] =
522 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
523 'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',
524 'G','l','o','b','a','l',0};
525 static const WCHAR classes_path_win32
[] =
526 {'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',
527 'G','l','o','b','a','l',0};
528 static const WCHAR classes_path_dotnet
[] =
529 {'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};
533 if (context
== MSIINSTALLCONTEXT_MACHINE
)
535 root
= HKEY_CLASSES_ROOT
;
536 if (win32
) path
= classes_path_win32
;
537 else path
= classes_path_dotnet
;
541 root
= HKEY_CURRENT_USER
;
542 if (win32
) path
= path_win32
;
543 else path
= path_dotnet
;
545 return RegCreateKeyW( root
, path
, hkey
);
548 UINT
ACTION_MsiPublishAssemblies( MSIPACKAGE
*package
)
552 LIST_FOR_EACH_ENTRY(comp
, &package
->components
, MSICOMPONENT
, entry
)
560 MSIASSEMBLY
*assembly
= comp
->assembly
;
563 if (!assembly
|| !comp
->ComponentId
) continue;
565 comp
->Action
= msi_get_component_action( package
, comp
);
566 if (comp
->Action
!= INSTALLSTATE_LOCAL
)
568 TRACE("component not scheduled for installation %s\n", debugstr_w(comp
->Component
));
571 TRACE("publishing %s\n", debugstr_w(comp
->Component
));
573 CLSIDFromString( package
->ProductCode
, &guid
);
574 encode_base85_guid( &guid
, buffer
);
576 CLSIDFromString( comp
->ComponentId
, &guid
);
577 encode_base85_guid( &guid
, buffer
+ 21 );
580 win32
= assembly
->attributes
& msidbAssemblyAttributesWin32
;
581 if (assembly
->application
)
583 MSIFILE
*file
= msi_get_loaded_file( package
, assembly
->application
);
584 if ((res
= open_local_assembly_key( package
->Context
, win32
, file
->TargetPath
, &hkey
)))
586 WARN("failed to open local assembly key %d\n", res
);
587 return ERROR_FUNCTION_FAILED
;
592 if ((res
= open_global_assembly_key( package
->Context
, win32
, &hkey
)))
594 WARN("failed to open global assembly key %d\n", res
);
595 return ERROR_FUNCTION_FAILED
;
598 size
= sizeof(buffer
);
599 if ((res
= RegSetValueExW( hkey
, assembly
->display_name
, 0, REG_MULTI_SZ
, (const BYTE
*)buffer
, size
)))
601 WARN("failed to set assembly value %d\n", res
);
605 uirow
= MSI_CreateRecord( 2 );
606 MSI_RecordSetStringW( uirow
, 2, assembly
->display_name
);
607 msi_ui_actiondata( package
, szMsiPublishAssemblies
, uirow
);
608 msiobj_release( &uirow
->hdr
);
610 return ERROR_SUCCESS
;
613 UINT
ACTION_MsiUnpublishAssemblies( MSIPACKAGE
*package
)
617 LIST_FOR_EACH_ENTRY(comp
, &package
->components
, MSICOMPONENT
, entry
)
621 MSIASSEMBLY
*assembly
= comp
->assembly
;
624 if (!assembly
|| !comp
->ComponentId
) continue;
626 comp
->Action
= msi_get_component_action( package
, comp
);
627 if (comp
->Action
!= INSTALLSTATE_ABSENT
)
629 TRACE("component not scheduled for removal %s\n", debugstr_w(comp
->Component
));
632 TRACE("unpublishing %s\n", debugstr_w(comp
->Component
));
634 win32
= assembly
->attributes
& msidbAssemblyAttributesWin32
;
635 if (assembly
->application
)
637 MSIFILE
*file
= msi_get_loaded_file( package
, assembly
->application
);
638 if ((res
= delete_local_assembly_key( package
->Context
, win32
, file
->TargetPath
)))
639 WARN("failed to delete local assembly key %d\n", res
);
644 if ((res
= open_global_assembly_key( package
->Context
, win32
, &hkey
)))
645 WARN("failed to delete global assembly key %d\n", res
);
648 if ((res
= RegDeleteValueW( hkey
, assembly
->display_name
)))
649 WARN("failed to delete global assembly value %d\n", res
);
654 uirow
= MSI_CreateRecord( 2 );
655 MSI_RecordSetStringW( uirow
, 2, assembly
->display_name
);
656 msi_ui_actiondata( package
, szMsiPublishAssemblies
, uirow
);
657 msiobj_release( &uirow
->hdr
);
659 return ERROR_SUCCESS
;