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 void 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;
41 if (!(pLoadLibraryShim
= (void *)GetProcAddress( package
->hmscoree
, "LoadLibraryShim" )))
43 FreeLibrary( package
->hmscoree
);
44 package
->hmscoree
= NULL
;
48 if (!package
->hfusion10
) pLoadLibraryShim( L
"fusion.dll", L
"v1.0.3705", NULL
, &package
->hfusion10
);
49 if (!package
->hfusion11
) pLoadLibraryShim( L
"fusion.dll", L
"v1.1.4322", NULL
, &package
->hfusion11
);
50 if (!package
->hfusion20
) pLoadLibraryShim( L
"fusion.dll", L
"v2.0.50727", NULL
, &package
->hfusion20
);
51 if (!package
->hfusion40
) pLoadLibraryShim( L
"fusion.dll", L
"v4.0.30319", NULL
, &package
->hfusion40
);
54 static BOOL
init_assembly_caches( MSIPACKAGE
*package
)
56 HRESULT (WINAPI
*pCreateAssemblyCache
)( IAssemblyCache
**, DWORD
);
58 if (!package
->cache_sxs
&& CreateAssemblyCache( &package
->cache_sxs
, 0 ) != S_OK
) return FALSE
;
60 load_fusion_dlls( package
);
61 package
->pGetFileVersion
= (void *)GetProcAddress( package
->hmscoree
, "GetFileVersion" ); /* missing from v1.0.3705 */
63 if (package
->hfusion10
&& !package
->cache_net
[CLR_VERSION_V10
])
65 pCreateAssemblyCache
= (void *)GetProcAddress( package
->hfusion10
, "CreateAssemblyCache" );
66 pCreateAssemblyCache( &package
->cache_net
[CLR_VERSION_V10
], 0 );
68 if (package
->hfusion11
&& !package
->cache_net
[CLR_VERSION_V11
])
70 pCreateAssemblyCache
= (void *)GetProcAddress( package
->hfusion11
, "CreateAssemblyCache" );
71 pCreateAssemblyCache( &package
->cache_net
[CLR_VERSION_V11
], 0 );
73 if (package
->hfusion20
&& !package
->cache_net
[CLR_VERSION_V20
])
75 pCreateAssemblyCache
= (void *)GetProcAddress( package
->hfusion20
, "CreateAssemblyCache" );
76 pCreateAssemblyCache( &package
->cache_net
[CLR_VERSION_V20
], 0 );
78 if (package
->hfusion40
&& !package
->cache_net
[CLR_VERSION_V40
])
80 pCreateAssemblyCache
= (void *)GetProcAddress( package
->hfusion40
, "CreateAssemblyCache" );
81 pCreateAssemblyCache( &package
->cache_net
[CLR_VERSION_V40
], 0 );
82 package
->pCreateAssemblyNameObject
= (void *)GetProcAddress( package
->hfusion40
, "CreateAssemblyNameObject" );
83 package
->pCreateAssemblyEnum
= (void *)GetProcAddress( package
->hfusion40
, "CreateAssemblyEnum" );
89 void msi_destroy_assembly_caches( MSIPACKAGE
*package
)
93 if (package
->cache_sxs
)
95 IAssemblyCache_Release( package
->cache_sxs
);
96 package
->cache_sxs
= NULL
;
98 for (i
= 0; i
< CLR_VERSION_MAX
; i
++)
100 if (package
->cache_net
[i
])
102 IAssemblyCache_Release( package
->cache_net
[i
] );
103 package
->cache_net
[i
] = NULL
;
106 package
->pGetFileVersion
= NULL
;
107 package
->pCreateAssemblyNameObject
= NULL
;
108 package
->pCreateAssemblyEnum
= NULL
;
109 FreeLibrary( package
->hfusion10
);
110 FreeLibrary( package
->hfusion11
);
111 FreeLibrary( package
->hfusion20
);
112 FreeLibrary( package
->hfusion40
);
113 FreeLibrary( package
->hmscoree
);
114 package
->hfusion10
= NULL
;
115 package
->hfusion11
= NULL
;
116 package
->hfusion20
= NULL
;
117 package
->hfusion40
= NULL
;
118 package
->hmscoree
= NULL
;
121 static MSIRECORD
*get_assembly_record( MSIPACKAGE
*package
, const WCHAR
*comp
)
127 r
= MSI_OpenQuery( package
->db
, &view
, L
"SELECT * FROM `MsiAssembly` WHERE `Component_` = '%s'", comp
);
128 if (r
!= ERROR_SUCCESS
)
131 r
= MSI_ViewExecute( view
, NULL
);
132 if (r
!= ERROR_SUCCESS
)
134 msiobj_release( &view
->hdr
);
137 r
= MSI_ViewFetch( view
, &rec
);
138 if (r
!= ERROR_SUCCESS
)
140 msiobj_release( &view
->hdr
);
143 if (!MSI_RecordGetString( rec
, 4 ))
144 TRACE("component is a global assembly\n");
146 msiobj_release( &view
->hdr
);
157 static UINT
get_assembly_name_attribute( MSIRECORD
*rec
, LPVOID param
)
159 struct assembly_name
*name
= param
;
160 const WCHAR
*attr
= MSI_RecordGetString( rec
, 2 );
161 const WCHAR
*value
= MSI_RecordGetString( rec
, 3 );
162 int len
= lstrlenW( L
"%s=\"%s\"" ) + lstrlenW( attr
) + lstrlenW( value
);
164 if (!(name
->attrs
[name
->index
] = malloc( len
* sizeof(WCHAR
) )))
165 return ERROR_OUTOFMEMORY
;
167 if (!wcsicmp( attr
, L
"name" )) lstrcpyW( name
->attrs
[name
->index
++], value
);
168 else swprintf( name
->attrs
[name
->index
++], len
, L
"%s=\"%s\"", attr
, value
);
169 return ERROR_SUCCESS
;
172 static WCHAR
*get_assembly_display_name( MSIDATABASE
*db
, const WCHAR
*comp
, MSIASSEMBLY
*assembly
)
174 struct assembly_name name
;
175 WCHAR
*display_name
= NULL
;
180 r
= MSI_OpenQuery( db
, &view
, L
"SELECT * FROM `MsiAssemblyName` WHERE `Component_` = '%s'", comp
);
181 if (r
!= ERROR_SUCCESS
)
187 MSI_IterateRecords( view
, &name
.count
, NULL
, NULL
);
188 if (!name
.count
) goto done
;
190 name
.attrs
= malloc( name
.count
* sizeof(WCHAR
*) );
191 if (!name
.attrs
) goto done
;
193 MSI_IterateRecords( view
, NULL
, get_assembly_name_attribute
, &name
);
196 for (i
= 0; i
< name
.count
; i
++) len
+= lstrlenW( name
.attrs
[i
] ) + 1;
198 display_name
= malloc( (len
+ 1) * sizeof(WCHAR
) );
202 for (i
= 0; i
< name
.count
; i
++)
204 lstrcatW( display_name
, name
.attrs
[i
] );
205 if (i
< name
.count
- 1) lstrcatW( display_name
, L
"," );
210 msiobj_release( &view
->hdr
);
213 for (i
= 0; i
< name
.count
; i
++) free( name
.attrs
[i
] );
219 WCHAR
*msi_get_assembly_path( MSIPACKAGE
*package
, const WCHAR
*displayname
)
223 IAssemblyCache
*cache
;
225 if (!init_assembly_caches( package
) || !(cache
= package
->cache_net
[CLR_VERSION_V40
])) return NULL
;
227 memset( &info
, 0, sizeof(info
) );
228 info
.cbAssemblyInfo
= sizeof(info
);
229 hr
= IAssemblyCache_QueryAssemblyInfo( cache
, 0, displayname
, &info
);
230 if (hr
!= E_NOT_SUFFICIENT_BUFFER
) return NULL
;
232 if (!(info
.pszCurrentAssemblyPathBuf
= malloc( info
.cchBuf
* sizeof(WCHAR
) ))) return NULL
;
234 hr
= IAssemblyCache_QueryAssemblyInfo( cache
, 0, displayname
, &info
);
237 free( info
.pszCurrentAssemblyPathBuf
);
240 TRACE("returning %s\n", debugstr_w(info
.pszCurrentAssemblyPathBuf
));
241 return info
.pszCurrentAssemblyPathBuf
;
244 IAssemblyEnum
*msi_create_assembly_enum( MSIPACKAGE
*package
, const WCHAR
*displayname
)
252 if (!init_assembly_caches( package
) || !package
->pCreateAssemblyNameObject
|| !package
->pCreateAssemblyEnum
)
255 hr
= package
->pCreateAssemblyNameObject( &name
, displayname
, CANOF_PARSE_DISPLAY_NAME
, NULL
);
256 if (FAILED( hr
)) return NULL
;
258 hr
= IAssemblyName_GetName( name
, &len
, NULL
);
259 if (hr
!= E_NOT_SUFFICIENT_BUFFER
|| !(str
= malloc( len
* sizeof(WCHAR
) )))
261 IAssemblyName_Release( name
);
265 hr
= IAssemblyName_GetName( name
, &len
, str
);
266 IAssemblyName_Release( name
);
273 hr
= package
->pCreateAssemblyNameObject( &name
, str
, 0, NULL
);
275 if (FAILED( hr
)) return NULL
;
277 hr
= package
->pCreateAssemblyEnum( &ret
, NULL
, name
, ASM_CACHE_GAC
, NULL
);
278 IAssemblyName_Release( name
);
279 if (FAILED( hr
)) return NULL
;
284 static const WCHAR
*clr_version
[] =
292 MSIASSEMBLY
*msi_load_assembly( MSIPACKAGE
*package
, MSICOMPONENT
*comp
)
297 if (!(rec
= get_assembly_record( package
, comp
->Component
))) return NULL
;
298 if (!(a
= calloc( 1, sizeof(MSIASSEMBLY
) )))
300 msiobj_release( &rec
->hdr
);
303 a
->feature
= wcsdup( MSI_RecordGetString( rec
, 2 ) );
304 TRACE("feature %s\n", debugstr_w(a
->feature
));
306 a
->manifest
= wcsdup( MSI_RecordGetString( rec
, 3 ) );
307 TRACE("manifest %s\n", debugstr_w(a
->manifest
));
309 a
->application
= wcsdup( MSI_RecordGetString( rec
, 4 ) );
310 TRACE("application %s\n", debugstr_w(a
->application
));
312 a
->attributes
= MSI_RecordGetInteger( rec
, 5 );
313 TRACE( "attributes %lu\n", a
->attributes
);
315 if (!(a
->display_name
= get_assembly_display_name( package
->db
, comp
->Component
, a
)))
317 WARN("can't get display name\n");
318 msiobj_release( &rec
->hdr
);
321 free( a
->application
);
325 TRACE("display name %s\n", debugstr_w(a
->display_name
));
327 msiobj_release( &rec
->hdr
);
331 static enum clr_version
get_clr_version( MSIPACKAGE
*package
, const WCHAR
*filename
)
335 enum clr_version version
= CLR_VERSION_V11
;
338 if (!package
->pGetFileVersion
) return CLR_VERSION_V10
;
340 hr
= package
->pGetFileVersion( filename
, NULL
, 0, &len
);
341 if (hr
!= E_NOT_SUFFICIENT_BUFFER
) return CLR_VERSION_V11
;
342 if ((strW
= malloc( len
* sizeof(WCHAR
) )))
344 hr
= package
->pGetFileVersion( filename
, strW
, len
, &len
);
348 for (i
= 0; i
< CLR_VERSION_MAX
; i
++)
349 if (!wcscmp( strW
, clr_version
[i
] )) version
= i
;
356 UINT
msi_install_assembly( MSIPACKAGE
*package
, MSICOMPONENT
*comp
)
359 const WCHAR
*manifest
;
360 IAssemblyCache
*cache
;
361 MSIASSEMBLY
*assembly
= comp
->assembly
;
362 MSIFEATURE
*feature
= NULL
;
364 if (!init_assembly_caches( package
)) return ERROR_FUNCTION_FAILED
;
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( package
, manifest
)];
388 if (!cache
) return ERROR_SUCCESS
;
390 TRACE("installing assembly %s\n", debugstr_w(manifest
));
392 hr
= IAssemblyCache_InstallAssembly( cache
, 0, manifest
, NULL
);
395 ERR( "failed to install assembly %s (%#lx)\n", debugstr_w(manifest
), hr
);
396 return ERROR_FUNCTION_FAILED
;
398 if (feature
) feature
->Action
= INSTALLSTATE_LOCAL
;
399 return ERROR_SUCCESS
;
402 UINT
msi_uninstall_assembly( MSIPACKAGE
*package
, MSICOMPONENT
*comp
)
405 IAssemblyCache
*cache
;
406 MSIASSEMBLY
*assembly
= comp
->assembly
;
407 MSIFEATURE
*feature
= NULL
;
409 if (!init_assembly_caches( package
)) return ERROR_FUNCTION_FAILED
;
411 if (comp
->assembly
->feature
)
412 feature
= msi_get_loaded_feature( package
, comp
->assembly
->feature
);
414 if (assembly
->application
)
416 if (feature
) feature
->Action
= INSTALLSTATE_ABSENT
;
417 return ERROR_SUCCESS
;
419 TRACE("removing %s\n", debugstr_w(assembly
->display_name
));
421 if (assembly
->attributes
== msidbAssemblyAttributesWin32
)
423 cache
= package
->cache_sxs
;
424 hr
= IAssemblyCache_UninstallAssembly( cache
, 0, assembly
->display_name
, NULL
, NULL
);
425 if (FAILED( hr
)) WARN( "failed to uninstall assembly %#lx\n", hr
);
430 for (i
= 0; i
< CLR_VERSION_MAX
; i
++)
432 if (!assembly
->clr_version
[i
]) continue;
433 cache
= package
->cache_net
[i
];
436 hr
= IAssemblyCache_UninstallAssembly( cache
, 0, assembly
->display_name
, NULL
, NULL
);
437 if (FAILED( hr
)) WARN( "failed to uninstall assembly %#lx\n", hr
);
441 if (feature
) feature
->Action
= INSTALLSTATE_ABSENT
;
442 return ERROR_SUCCESS
;
445 static WCHAR
*build_local_assembly_path( const WCHAR
*filename
)
450 if (!(ret
= malloc( (wcslen( filename
) + 1) * sizeof(WCHAR
) )))
453 for (i
= 0; filename
[i
]; i
++)
455 if (filename
[i
] == '\\' || filename
[i
] == '/') ret
[i
] = '|';
456 else ret
[i
] = filename
[i
];
462 static LONG
open_assemblies_key( UINT context
, BOOL win32
, HKEY
*hkey
)
467 if (context
== MSIINSTALLCONTEXT_MACHINE
)
469 root
= HKEY_CLASSES_ROOT
;
470 if (win32
) path
= L
"Installer\\Win32Assemblies\\";
471 else path
= L
"Installer\\Assemblies\\";
475 root
= HKEY_CURRENT_USER
;
476 if (win32
) path
= L
"Software\\Microsoft\\Installer\\Win32Assemblies\\";
477 else path
= L
"Software\\Microsoft\\Installer\\Assemblies\\";
479 return RegCreateKeyW( root
, path
, hkey
);
482 static LONG
open_local_assembly_key( UINT context
, BOOL win32
, const WCHAR
*filename
, HKEY
*hkey
)
488 if (!(path
= build_local_assembly_path( filename
)))
489 return ERROR_OUTOFMEMORY
;
491 if ((res
= open_assemblies_key( context
, win32
, &root
)))
496 res
= RegCreateKeyW( root
, path
, hkey
);
502 static LONG
delete_local_assembly_key( UINT context
, BOOL win32
, const WCHAR
*filename
)
508 if (!(path
= build_local_assembly_path( filename
)))
509 return ERROR_OUTOFMEMORY
;
511 if ((res
= open_assemblies_key( context
, win32
, &root
)))
516 res
= RegDeleteKeyW( root
, path
);
522 static LONG
open_global_assembly_key( UINT context
, BOOL win32
, HKEY
*hkey
)
527 if (context
== MSIINSTALLCONTEXT_MACHINE
)
529 root
= HKEY_CLASSES_ROOT
;
530 if (win32
) path
= L
"Installer\\Win32Assemblies\\Global";
531 else path
= L
"Installer\\Assemblies\\Global";
535 root
= HKEY_CURRENT_USER
;
536 if (win32
) path
= L
"Software\\Microsoft\\Installer\\Win32Assemblies\\Global";
537 else path
= L
"Software\\Microsoft\\Installer\\Assemblies\\Global";
539 return RegCreateKeyW( root
, path
, hkey
);
542 UINT
ACTION_MsiPublishAssemblies( MSIPACKAGE
*package
)
546 if (package
->script
== SCRIPT_NONE
)
547 return msi_schedule_action(package
, SCRIPT_INSTALL
, L
"MsiPublishAssemblies");
549 LIST_FOR_EACH_ENTRY(comp
, &package
->components
, MSICOMPONENT
, entry
)
557 MSIASSEMBLY
*assembly
= comp
->assembly
;
560 if (!assembly
|| !comp
->ComponentId
) continue;
562 comp
->Action
= msi_get_component_action( package
, comp
);
563 if (comp
->Action
!= INSTALLSTATE_LOCAL
)
565 TRACE("component not scheduled for installation %s\n", debugstr_w(comp
->Component
));
568 TRACE("publishing %s\n", debugstr_w(comp
->Component
));
570 CLSIDFromString( package
->ProductCode
, &guid
);
571 encode_base85_guid( &guid
, buffer
);
573 CLSIDFromString( comp
->ComponentId
, &guid
);
574 encode_base85_guid( &guid
, buffer
+ 21 );
577 win32
= assembly
->attributes
& msidbAssemblyAttributesWin32
;
578 if (assembly
->application
)
580 MSIFILE
*file
= msi_get_loaded_file( package
, assembly
->application
);
583 WARN("no matching file %s for local assembly\n", debugstr_w(assembly
->application
));
586 if ((res
= open_local_assembly_key( package
->Context
, win32
, file
->TargetPath
, &hkey
)))
588 WARN( "failed to open local assembly key %ld\n", res
);
589 return ERROR_FUNCTION_FAILED
;
594 if ((res
= open_global_assembly_key( package
->Context
, win32
, &hkey
)))
596 WARN( "failed to open global assembly key %ld\n", res
);
597 return ERROR_FUNCTION_FAILED
;
600 size
= sizeof(buffer
);
601 if ((res
= RegSetValueExW( hkey
, assembly
->display_name
, 0, REG_MULTI_SZ
, (const BYTE
*)buffer
, size
)))
603 WARN( "failed to set assembly value %ld\n", res
);
607 uirow
= MSI_CreateRecord( 2 );
608 MSI_RecordSetStringW( uirow
, 2, assembly
->display_name
);
609 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONDATA
, uirow
);
610 msiobj_release( &uirow
->hdr
);
612 return ERROR_SUCCESS
;
615 UINT
ACTION_MsiUnpublishAssemblies( MSIPACKAGE
*package
)
619 if (package
->script
== SCRIPT_NONE
)
620 return msi_schedule_action(package
, SCRIPT_INSTALL
, L
"MsiUnpublishAssemblies");
622 LIST_FOR_EACH_ENTRY(comp
, &package
->components
, MSICOMPONENT
, entry
)
626 MSIASSEMBLY
*assembly
= comp
->assembly
;
629 if (!assembly
|| !comp
->ComponentId
) continue;
631 comp
->Action
= msi_get_component_action( package
, comp
);
632 if (comp
->Action
!= INSTALLSTATE_ABSENT
)
634 TRACE("component not scheduled for removal %s\n", debugstr_w(comp
->Component
));
637 TRACE("unpublishing %s\n", debugstr_w(comp
->Component
));
639 win32
= assembly
->attributes
& msidbAssemblyAttributesWin32
;
640 if (assembly
->application
)
642 MSIFILE
*file
= msi_get_loaded_file( package
, assembly
->application
);
645 WARN("no matching file %s for local assembly\n", debugstr_w(assembly
->application
));
648 if ((res
= delete_local_assembly_key( package
->Context
, win32
, file
->TargetPath
)))
649 WARN( "failed to delete local assembly key %ld\n", res
);
654 if ((res
= open_global_assembly_key( package
->Context
, win32
, &hkey
)))
655 WARN( "failed to delete global assembly key %ld\n", res
);
658 if ((res
= RegDeleteValueW( hkey
, assembly
->display_name
)))
659 WARN( "failed to delete global assembly value %ld\n", res
);
664 uirow
= MSI_CreateRecord( 2 );
665 MSI_RecordSetStringW( uirow
, 2, assembly
->display_name
);
666 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONDATA
, uirow
);
667 msiobj_release( &uirow
->hdr
);
669 return ERROR_SUCCESS
;