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
27 #include "wine/debug.h"
28 #include "wine/unicode.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
33 static HRESULT (WINAPI
*pCreateAssemblyCacheNet
)( IAssemblyCache
**, DWORD
);
34 static HRESULT (WINAPI
*pCreateAssemblyCacheSxs
)( IAssemblyCache
**, DWORD
);
35 static HRESULT (WINAPI
*pLoadLibraryShim
)( LPCWSTR
, LPCWSTR
, LPVOID
, HMODULE
* );
37 static BOOL
init_function_pointers( void )
39 static const WCHAR szFusion
[] = {'f','u','s','i','o','n','.','d','l','l',0};
40 HMODULE hfusion
, hmscoree
, hsxs
;
42 if (pCreateAssemblyCacheNet
) return TRUE
;
44 if (!(hmscoree
= LoadLibraryA( "mscoree.dll" )))
46 WARN("mscoree.dll not available\n");
49 if (!(pLoadLibraryShim
= (void *)GetProcAddress( hmscoree
, "LoadLibraryShim" )))
51 WARN("LoadLibraryShim not available\n");
52 FreeLibrary( hmscoree
);
55 if (FAILED( pLoadLibraryShim( szFusion
, NULL
, NULL
, &hfusion
)))
57 WARN("fusion.dll not available\n");
58 FreeLibrary( hmscoree
);
61 pCreateAssemblyCacheNet
= (void *)GetProcAddress( hfusion
, "CreateAssemblyCache" );
62 FreeLibrary( hmscoree
);
63 if (!(hsxs
= LoadLibraryA( "sxs.dll" )))
65 WARN("sxs.dll not available\n");
66 FreeLibrary( hfusion
);
69 pCreateAssemblyCacheSxs
= (void *)GetProcAddress( hsxs
, "CreateAssemblyCache" );
73 static BOOL
init_assembly_caches( MSIPACKAGE
*package
)
77 if (!init_function_pointers()) return FALSE
;
78 if (package
->cache_net
) return TRUE
;
80 hr
= pCreateAssemblyCacheNet( &package
->cache_net
, 0 );
81 if (hr
!= S_OK
) return FALSE
;
83 hr
= pCreateAssemblyCacheSxs( &package
->cache_sxs
, 0 );
86 IAssemblyCache_Release( package
->cache_net
);
87 package
->cache_net
= NULL
;
93 MSIRECORD
*get_assembly_record( MSIPACKAGE
*package
, const WCHAR
*comp
)
95 static const WCHAR query
[] = {
96 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
97 '`','M','s','i','A','s','s','e','m','b','l','y','`',' ',
98 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
99 ' ','=',' ','\'','%','s','\'',0};
104 r
= MSI_OpenQuery( package
->db
, &view
, query
, comp
);
105 if (r
!= ERROR_SUCCESS
)
108 r
= MSI_ViewExecute( view
, NULL
);
109 if (r
!= ERROR_SUCCESS
)
111 msiobj_release( &view
->hdr
);
114 r
= MSI_ViewFetch( view
, &rec
);
115 if (r
!= ERROR_SUCCESS
)
117 msiobj_release( &view
->hdr
);
120 if (!MSI_RecordGetString( rec
, 4 ))
121 TRACE("component is a global assembly\n");
123 msiobj_release( &view
->hdr
);
137 static UINT
get_assembly_name_attribute( MSIRECORD
*rec
, LPVOID param
)
139 static const WCHAR typeW
[] = {'t','y','p','e',0};
140 static const WCHAR nameW
[] = {'n','a','m','e',0};
141 static const WCHAR versionW
[] = {'v','e','r','s','i','o','n',0};
142 static const WCHAR cultureW
[] = {'c','u','l','t','u','r','e',0};
143 static const WCHAR tokenW
[] = {'p','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
144 static const WCHAR archW
[] = {'p','r','o','c','e','s','s','o','r','A','r','c','h','i','t','e','c','t','u','r','e',0};
145 struct assembly_name
*name
= param
;
146 const WCHAR
*attr
= MSI_RecordGetString( rec
, 2 );
147 WCHAR
*value
= msi_dup_record_field( rec
, 3 );
149 if (!strcmpiW( attr
, typeW
))
151 else if (!strcmpiW( attr
, nameW
))
153 else if (!strcmpiW( attr
, versionW
))
154 name
->version
= value
;
155 else if (!strcmpiW( attr
, cultureW
))
156 name
->culture
= value
;
157 else if (!strcmpiW( attr
, tokenW
))
159 else if (!strcmpiW( attr
, archW
))
164 return ERROR_SUCCESS
;
167 static WCHAR
*get_assembly_display_name( MSIDATABASE
*db
, const WCHAR
*comp
, MSIASSEMBLY
*assembly
)
169 static const WCHAR fmt_netW
[] = {
170 '%','s',',',' ','v','e','r','s','i','o','n','=','%','s',',',' ',
171 'c','u','l','t','u','r','e','=','%','s',',',' ',
172 'p','u','b','l','i','c','K','e','y','T','o','k','e','n','=','%','s',0};
173 static const WCHAR fmt_sxsW
[] = {
174 '%','s',',',' ','v','e','r','s','i','o','n','=','%','s',',',' ',
175 'p','u','b','l','i','c','K','e','y','T','o','k','e','n','=','%','s',',',' ',
176 'p','r','o','c','e','s','s','o','r','A','r','c','h','i','t','e','c','t','u','r','e','=','%','s',0};
177 static const WCHAR queryW
[] = {
178 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
179 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
180 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
181 ' ','=',' ','\'','%','s','\'',0};
182 struct assembly_name name
;
183 WCHAR
*display_name
= NULL
;
188 memset( &name
, 0, sizeof(name
) );
190 r
= MSI_OpenQuery( db
, &view
, queryW
, comp
);
191 if (r
!= ERROR_SUCCESS
)
194 MSI_IterateRecords( view
, NULL
, get_assembly_name_attribute
, &name
);
195 msiobj_release( &view
->hdr
);
197 if (assembly
->attributes
== msidbAssemblyAttributesWin32
)
199 if (!name
.type
|| !name
.name
|| !name
.version
|| !name
.token
|| !name
.arch
)
201 WARN("invalid win32 assembly name\n");
204 len
= strlenW( fmt_sxsW
);
205 len
+= strlenW( name
.name
);
206 len
+= strlenW( name
.version
);
207 len
+= strlenW( name
.token
);
208 len
+= strlenW( name
.arch
);
209 if (!(display_name
= msi_alloc( len
* sizeof(WCHAR
) ))) goto done
;
210 sprintfW( display_name
, fmt_sxsW
, name
.name
, name
.version
, name
.token
, name
.arch
);
214 if (!name
.name
|| !name
.version
|| !name
.culture
|| !name
.token
)
216 WARN("invalid assembly name\n");
219 len
= strlenW( fmt_netW
);
220 len
+= strlenW( name
.name
);
221 len
+= strlenW( name
.version
);
222 len
+= strlenW( name
.culture
);
223 len
+= strlenW( name
.token
);
224 if (!(display_name
= msi_alloc( len
* sizeof(WCHAR
) ))) goto done
;
225 sprintfW( display_name
, fmt_netW
, name
.name
, name
.version
, name
.culture
, name
.token
);
229 msi_free( name
.type
);
230 msi_free( name
.name
);
231 msi_free( name
.version
);
232 msi_free( name
.culture
);
233 msi_free( name
.token
);
234 msi_free( name
.arch
);
239 static BOOL
check_assembly_installed( MSIPACKAGE
*package
, MSIASSEMBLY
*assembly
)
241 IAssemblyCache
*cache
;
245 if (assembly
->application
)
247 /* FIXME: we should probably check the manifest file here */
251 if (!init_assembly_caches( package
))
254 if (assembly
->attributes
== msidbAssemblyAttributesWin32
)
255 cache
= package
->cache_sxs
;
257 cache
= package
->cache_net
;
259 memset( &info
, 0, sizeof(info
) );
260 info
.cbAssemblyInfo
= sizeof(info
);
261 hr
= IAssemblyCache_QueryAssemblyInfo( cache
, QUERYASMINFO_FLAG_VALIDATE
, assembly
->display_name
, &info
);
265 return (info
.dwAssemblyFlags
== ASSEMBLYINFO_FLAG_INSTALLED
);
268 MSIASSEMBLY
*load_assembly( MSIPACKAGE
*package
, MSICOMPONENT
*comp
)
273 if (!(rec
= get_assembly_record( package
, comp
->Component
)))
276 if (!(a
= msi_alloc_zero( sizeof(MSIASSEMBLY
) )))
278 msiobj_release( &rec
->hdr
);
281 a
->feature
= strdupW( MSI_RecordGetString( rec
, 2 ) );
282 TRACE("feature %s\n", debugstr_w(a
->feature
));
284 a
->manifest
= strdupW( MSI_RecordGetString( rec
, 3 ) );
285 TRACE("manifest %s\n", debugstr_w(a
->manifest
));
287 a
->application
= strdupW( MSI_RecordGetString( rec
, 4 ) );
288 TRACE("application %s\n", debugstr_w(a
->application
));
290 a
->attributes
= MSI_RecordGetInteger( rec
, 5 );
291 TRACE("attributes %u\n", a
->attributes
);
293 if (!(a
->display_name
= get_assembly_display_name( package
->db
, comp
->Component
, a
)))
295 WARN("can't get display name\n");
296 msiobj_release( &rec
->hdr
);
300 TRACE("display name %s\n", debugstr_w(a
->display_name
));
302 a
->installed
= check_assembly_installed( package
, a
);
303 TRACE("assembly is %s\n", a
->installed
? "installed" : "not installed");
305 msiobj_release( &rec
->hdr
);
309 UINT
install_assembly( MSIPACKAGE
*package
, MSICOMPONENT
*comp
)
312 const WCHAR
*manifest
;
313 IAssemblyCache
*cache
;
314 MSIASSEMBLY
*assembly
= comp
->assembly
;
315 MSIFEATURE
*feature
= NULL
;
317 if (comp
->assembly
->feature
)
318 feature
= get_loaded_feature( package
, comp
->assembly
->feature
);
320 if (assembly
->application
)
322 if (feature
) feature
->Action
= INSTALLSTATE_LOCAL
;
323 return ERROR_SUCCESS
;
325 if (assembly
->attributes
== msidbAssemblyAttributesWin32
)
327 if (!assembly
->manifest
)
329 WARN("no manifest\n");
330 return ERROR_FUNCTION_FAILED
;
332 manifest
= get_loaded_file( package
, assembly
->manifest
)->TargetPath
;
333 cache
= package
->cache_sxs
;
337 manifest
= get_loaded_file( package
, comp
->KeyPath
)->TargetPath
;
338 cache
= package
->cache_net
;
340 TRACE("installing assembly %s\n", debugstr_w(manifest
));
342 hr
= IAssemblyCache_InstallAssembly( cache
, 0, manifest
, NULL
);
345 ERR("Failed to install assembly %s (0x%08x)\n", debugstr_w(manifest
), hr
);
346 return ERROR_FUNCTION_FAILED
;
348 if (feature
) feature
->Action
= INSTALLSTATE_LOCAL
;
349 assembly
->installed
= TRUE
;
350 return ERROR_SUCCESS
;
353 UINT
ACTION_MsiPublishAssemblies( MSIPACKAGE
*package
)
358 LIST_FOR_EACH_ENTRY(comp
, &package
->components
, MSICOMPONENT
, entry
)
360 if (!comp
->assembly
|| !comp
->Enabled
)
363 /* FIXME: write assembly registry values */
365 uirow
= MSI_CreateRecord( 2 );
366 MSI_RecordSetStringW( uirow
, 2, comp
->assembly
->display_name
);
367 ui_actiondata( package
, szMsiPublishAssemblies
, uirow
);
368 msiobj_release( &uirow
->hdr
);
370 return ERROR_SUCCESS
;