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
;