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
*pCreateAssemblyCacheNet40
)( IAssemblyCache
**, DWORD
);
38 static HRESULT (WINAPI
*pCreateAssemblyCacheSxs
)( IAssemblyCache
**, DWORD
);
39 static HRESULT (WINAPI
*pLoadLibraryShim
)( LPCWSTR
, LPCWSTR
, LPVOID
, HMODULE
* );
40 static HRESULT (WINAPI
*pGetFileVersion
)( LPCWSTR
, LPWSTR
, DWORD
, DWORD
* );
41 static HRESULT (WINAPI
*pCreateAssemblyNameObject
)( IAssemblyName
**, LPCWSTR
, DWORD
, LPVOID
);
42 static HRESULT (WINAPI
*pCreateAssemblyEnum
)( IAssemblyEnum
**, IUnknown
*, IAssemblyName
*, DWORD
, LPVOID
);
44 static HMODULE hfusion10
, hfusion11
, hfusion20
, hfusion40
, hmscoree
, hsxs
;
46 static BOOL
init_function_pointers( void )
48 static const WCHAR szFusion
[] = {'f','u','s','i','o','n','.','d','l','l',0};
49 static const WCHAR szMscoree
[] = {'\\','m','s','c','o','r','e','e','.','d','l','l',0};
50 static const WCHAR szSxs
[] = {'s','x','s','.','d','l','l',0};
51 static const WCHAR szVersion10
[] = {'v','1','.','0','.','3','7','0','5',0};
52 static const WCHAR szVersion11
[] = {'v','1','.','1','.','4','3','2','2',0};
53 static const WCHAR szVersion20
[] = {'v','2','.','0','.','5','0','7','2','7',0};
54 static const WCHAR szVersion40
[] = {'v','4','.','0','.','3','0','3','1','9',0};
56 DWORD len
= GetSystemDirectoryW( path
, MAX_PATH
);
58 if (!hsxs
&& !(hsxs
= LoadLibraryW( szSxs
))) return FALSE
;
59 if (!(pCreateAssemblyCacheSxs
= (void *)GetProcAddress( hsxs
, "CreateAssemblyCache" )))
65 strcpyW( path
+ len
, szMscoree
);
66 if (hmscoree
|| !(hmscoree
= LoadLibraryW( path
))) return TRUE
;
67 pGetFileVersion
= (void *)GetProcAddress( hmscoree
, "GetFileVersion" ); /* missing from v1.0.3705 */
68 if (!(pLoadLibraryShim
= (void *)GetProcAddress( hmscoree
, "LoadLibraryShim" )))
70 FreeLibrary( hmscoree
);
74 if (!pLoadLibraryShim( szFusion
, szVersion10
, NULL
, &hfusion10
))
75 pCreateAssemblyCacheNet10
= (void *)GetProcAddress( hfusion10
, "CreateAssemblyCache" );
77 if (!pLoadLibraryShim( szFusion
, szVersion11
, NULL
, &hfusion11
))
78 pCreateAssemblyCacheNet11
= (void *)GetProcAddress( hfusion11
, "CreateAssemblyCache" );
80 if (!pLoadLibraryShim( szFusion
, szVersion20
, NULL
, &hfusion20
))
81 pCreateAssemblyCacheNet20
= (void *)GetProcAddress( hfusion20
, "CreateAssemblyCache" );
83 if (!pLoadLibraryShim( szFusion
, szVersion40
, NULL
, &hfusion40
))
85 pCreateAssemblyCacheNet40
= (void *)GetProcAddress( hfusion40
, "CreateAssemblyCache" );
86 pCreateAssemblyNameObject
= (void *)GetProcAddress( hfusion40
, "CreateAssemblyNameObject" );
87 pCreateAssemblyEnum
= (void *)GetProcAddress( hfusion40
, "CreateAssemblyEnum" );
92 BOOL
msi_init_assembly_caches( MSIPACKAGE
*package
)
94 if (!init_function_pointers()) return FALSE
;
95 if (pCreateAssemblyCacheSxs( &package
->cache_sxs
, 0 ) != S_OK
) return FALSE
;
96 if (pCreateAssemblyCacheNet10
) pCreateAssemblyCacheNet10( &package
->cache_net
[CLR_VERSION_V10
], 0 );
97 if (pCreateAssemblyCacheNet11
) pCreateAssemblyCacheNet11( &package
->cache_net
[CLR_VERSION_V11
], 0 );
98 if (pCreateAssemblyCacheNet20
) pCreateAssemblyCacheNet20( &package
->cache_net
[CLR_VERSION_V20
], 0 );
99 if (pCreateAssemblyCacheNet40
) pCreateAssemblyCacheNet40( &package
->cache_net
[CLR_VERSION_V40
], 0 );
103 void msi_destroy_assembly_caches( MSIPACKAGE
*package
)
107 if (package
->cache_sxs
)
109 IAssemblyCache_Release( package
->cache_sxs
);
110 package
->cache_sxs
= NULL
;
112 for (i
= 0; i
< CLR_VERSION_MAX
; i
++)
114 if (package
->cache_net
[i
])
116 IAssemblyCache_Release( package
->cache_net
[i
] );
117 package
->cache_net
[i
] = NULL
;
120 pCreateAssemblyCacheNet10
= NULL
;
121 pCreateAssemblyCacheNet11
= NULL
;
122 pCreateAssemblyCacheNet20
= NULL
;
123 pCreateAssemblyCacheNet40
= NULL
;
124 FreeLibrary( hfusion10
);
125 FreeLibrary( hfusion11
);
126 FreeLibrary( hfusion20
);
127 FreeLibrary( hfusion40
);
128 FreeLibrary( hmscoree
);
138 static MSIRECORD
*get_assembly_record( MSIPACKAGE
*package
, const WCHAR
*comp
)
140 static const WCHAR query
[] = {
141 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
142 '`','M','s','i','A','s','s','e','m','b','l','y','`',' ',
143 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
144 ' ','=',' ','\'','%','s','\'',0};
149 r
= MSI_OpenQuery( package
->db
, &view
, query
, comp
);
150 if (r
!= ERROR_SUCCESS
)
153 r
= MSI_ViewExecute( view
, NULL
);
154 if (r
!= ERROR_SUCCESS
)
156 msiobj_release( &view
->hdr
);
159 r
= MSI_ViewFetch( view
, &rec
);
160 if (r
!= ERROR_SUCCESS
)
162 msiobj_release( &view
->hdr
);
165 if (!MSI_RecordGetString( rec
, 4 ))
166 TRACE("component is a global assembly\n");
168 msiobj_release( &view
->hdr
);
179 static UINT
get_assembly_name_attribute( MSIRECORD
*rec
, LPVOID param
)
181 static const WCHAR fmtW
[] = {'%','s','=','"','%','s','"',0};
182 static const WCHAR nameW
[] = {'n','a','m','e',0};
183 struct assembly_name
*name
= param
;
184 const WCHAR
*attr
= MSI_RecordGetString( rec
, 2 );
185 const WCHAR
*value
= MSI_RecordGetString( rec
, 3 );
186 int len
= strlenW( fmtW
) + strlenW( attr
) + strlenW( value
);
188 if (!(name
->attrs
[name
->index
] = msi_alloc( len
* sizeof(WCHAR
) )))
189 return ERROR_OUTOFMEMORY
;
191 if (!strcmpiW( attr
, nameW
)) strcpyW( name
->attrs
[name
->index
++], value
);
192 else sprintfW( name
->attrs
[name
->index
++], fmtW
, attr
, value
);
193 return ERROR_SUCCESS
;
196 static WCHAR
*get_assembly_display_name( MSIDATABASE
*db
, const WCHAR
*comp
, MSIASSEMBLY
*assembly
)
198 static const WCHAR commaW
[] = {',',0};
199 static const WCHAR queryW
[] = {
200 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
201 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
202 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
203 ' ','=',' ','\'','%','s','\'',0};
204 struct assembly_name name
;
205 WCHAR
*display_name
= NULL
;
210 r
= MSI_OpenQuery( db
, &view
, queryW
, comp
);
211 if (r
!= ERROR_SUCCESS
)
217 MSI_IterateRecords( view
, &name
.count
, NULL
, NULL
);
218 if (!name
.count
) goto done
;
220 name
.attrs
= msi_alloc( name
.count
* sizeof(WCHAR
*) );
221 if (!name
.attrs
) goto done
;
223 MSI_IterateRecords( view
, NULL
, get_assembly_name_attribute
, &name
);
226 for (i
= 0; i
< name
.count
; i
++) len
+= strlenW( name
.attrs
[i
] ) + 1;
228 display_name
= msi_alloc( (len
+ 1) * sizeof(WCHAR
) );
232 for (i
= 0; i
< name
.count
; i
++)
234 strcatW( display_name
, name
.attrs
[i
] );
235 if (i
< name
.count
- 1) strcatW( display_name
, commaW
);
240 msiobj_release( &view
->hdr
);
243 for (i
= 0; i
< name
.count
; i
++) msi_free( name
.attrs
[i
] );
244 msi_free( name
.attrs
);
249 static BOOL
is_assembly_installed( IAssemblyCache
*cache
, const WCHAR
*display_name
)
254 if (!cache
) return FALSE
;
256 memset( &info
, 0, sizeof(info
) );
257 info
.cbAssemblyInfo
= sizeof(info
);
258 hr
= IAssemblyCache_QueryAssemblyInfo( cache
, 0, display_name
, &info
);
259 if (hr
== S_OK
/* sxs version */ || hr
== E_NOT_SUFFICIENT_BUFFER
)
261 return (info
.dwAssemblyFlags
== ASSEMBLYINFO_FLAG_INSTALLED
);
263 TRACE("QueryAssemblyInfo returned 0x%08x\n", hr
);
267 WCHAR
*msi_get_assembly_path( MSIPACKAGE
*package
, const WCHAR
*displayname
)
271 IAssemblyCache
*cache
= package
->cache_net
[CLR_VERSION_V40
];
273 if (!cache
) return NULL
;
275 memset( &info
, 0, sizeof(info
) );
276 info
.cbAssemblyInfo
= sizeof(info
);
277 hr
= IAssemblyCache_QueryAssemblyInfo( cache
, 0, displayname
, &info
);
278 if (hr
!= E_NOT_SUFFICIENT_BUFFER
) return NULL
;
280 if (!(info
.pszCurrentAssemblyPathBuf
= msi_alloc( info
.cchBuf
* sizeof(WCHAR
) ))) return NULL
;
282 hr
= IAssemblyCache_QueryAssemblyInfo( cache
, 0, displayname
, &info
);
285 msi_free( info
.pszCurrentAssemblyPathBuf
);
288 TRACE("returning %s\n", debugstr_w(info
.pszCurrentAssemblyPathBuf
));
289 return info
.pszCurrentAssemblyPathBuf
;
292 IAssemblyEnum
*msi_create_assembly_enum( MSIPACKAGE
*package
, const WCHAR
*displayname
)
300 if (!pCreateAssemblyNameObject
|| !pCreateAssemblyEnum
) return NULL
;
302 hr
= pCreateAssemblyNameObject( &name
, displayname
, CANOF_PARSE_DISPLAY_NAME
, NULL
);
303 if (FAILED( hr
)) return NULL
;
305 hr
= IAssemblyName_GetName( name
, &len
, NULL
);
306 if (hr
!= E_NOT_SUFFICIENT_BUFFER
|| !(str
= msi_alloc( len
* sizeof(WCHAR
) )))
308 IAssemblyName_Release( name
);
312 hr
= IAssemblyName_GetName( name
, &len
, str
);
313 IAssemblyName_Release( name
);
320 hr
= pCreateAssemblyNameObject( &name
, str
, 0, NULL
);
322 if (FAILED( hr
)) return NULL
;
324 hr
= pCreateAssemblyEnum( &ret
, NULL
, name
, ASM_CACHE_GAC
, NULL
);
325 IAssemblyName_Release( name
);
326 if (FAILED( hr
)) return NULL
;
331 static const WCHAR clr_version_v10
[] = {'v','1','.','0','.','3','7','0','5',0};
332 static const WCHAR clr_version_v11
[] = {'v','1','.','1','.','4','3','2','2',0};
333 static const WCHAR clr_version_v20
[] = {'v','2','.','0','.','5','0','7','2','7',0};
334 static const WCHAR clr_version_v40
[] = {'v','4','.','0','.','3','0','3','1','9',0};
335 static const WCHAR clr_version_unknown
[] = {'u','n','k','n','o','w','n',0};
337 static const WCHAR
*clr_version
[] =
345 static const WCHAR
*get_clr_version_str( enum clr_version version
)
347 if (version
>= sizeof(clr_version
)/sizeof(clr_version
[0])) return clr_version_unknown
;
348 return clr_version
[version
];
351 /* assembly caches must be initialized */
352 MSIASSEMBLY
*msi_load_assembly( MSIPACKAGE
*package
, MSICOMPONENT
*comp
)
357 if (!(rec
= get_assembly_record( package
, comp
->Component
))) return NULL
;
358 if (!(a
= msi_alloc_zero( sizeof(MSIASSEMBLY
) )))
360 msiobj_release( &rec
->hdr
);
363 a
->feature
= strdupW( MSI_RecordGetString( rec
, 2 ) );
364 TRACE("feature %s\n", debugstr_w(a
->feature
));
366 a
->manifest
= strdupW( MSI_RecordGetString( rec
, 3 ) );
367 TRACE("manifest %s\n", debugstr_w(a
->manifest
));
369 a
->application
= strdupW( MSI_RecordGetString( rec
, 4 ) );
370 TRACE("application %s\n", debugstr_w(a
->application
));
372 a
->attributes
= MSI_RecordGetInteger( rec
, 5 );
373 TRACE("attributes %u\n", a
->attributes
);
375 if (!(a
->display_name
= get_assembly_display_name( package
->db
, comp
->Component
, a
)))
377 WARN("can't get display name\n");
378 msiobj_release( &rec
->hdr
);
379 msi_free( a
->feature
);
380 msi_free( a
->manifest
);
381 msi_free( a
->application
);
385 TRACE("display name %s\n", debugstr_w(a
->display_name
));
389 /* We can't check the manifest here because the target path may still change.
390 So we assume that the assembly is not installed and lean on the InstallFiles
391 action to determine which files need to be installed.
393 a
->installed
= FALSE
;
397 if (a
->attributes
== msidbAssemblyAttributesWin32
)
398 a
->installed
= is_assembly_installed( package
->cache_sxs
, a
->display_name
);
402 for (i
= 0; i
< CLR_VERSION_MAX
; i
++)
404 a
->clr_version
[i
] = is_assembly_installed( package
->cache_net
[i
], a
->display_name
);
405 if (a
->clr_version
[i
])
407 TRACE("runtime version %s\n", debugstr_w(get_clr_version_str( i
)));
414 TRACE("assembly is %s\n", a
->installed
? "installed" : "not installed");
415 msiobj_release( &rec
->hdr
);
419 static enum clr_version
get_clr_version( const WCHAR
*filename
)
423 enum clr_version version
= CLR_VERSION_V11
;
426 if (!pGetFileVersion
) return CLR_VERSION_V10
;
428 hr
= pGetFileVersion( filename
, NULL
, 0, &len
);
429 if (hr
!= E_NOT_SUFFICIENT_BUFFER
) return CLR_VERSION_V11
;
430 if ((strW
= msi_alloc( len
* sizeof(WCHAR
) )))
432 hr
= pGetFileVersion( filename
, strW
, len
, &len
);
436 for (i
= 0; i
< CLR_VERSION_MAX
; i
++)
437 if (!strcmpW( strW
, clr_version
[i
] )) version
= i
;
444 UINT
msi_install_assembly( MSIPACKAGE
*package
, MSICOMPONENT
*comp
)
447 const WCHAR
*manifest
;
448 IAssemblyCache
*cache
;
449 MSIASSEMBLY
*assembly
= comp
->assembly
;
450 MSIFEATURE
*feature
= NULL
;
452 if (comp
->assembly
->feature
)
453 feature
= msi_get_loaded_feature( package
, comp
->assembly
->feature
);
455 if (assembly
->application
)
457 if (feature
) feature
->Action
= INSTALLSTATE_LOCAL
;
458 return ERROR_SUCCESS
;
460 if (assembly
->attributes
== msidbAssemblyAttributesWin32
)
462 if (!assembly
->manifest
)
464 WARN("no manifest\n");
465 return ERROR_FUNCTION_FAILED
;
467 manifest
= msi_get_loaded_file( package
, assembly
->manifest
)->TargetPath
;
468 cache
= package
->cache_sxs
;
472 manifest
= msi_get_loaded_file( package
, comp
->KeyPath
)->TargetPath
;
473 cache
= package
->cache_net
[get_clr_version( manifest
)];
474 if (!cache
) return ERROR_SUCCESS
;
476 TRACE("installing assembly %s\n", debugstr_w(manifest
));
478 hr
= IAssemblyCache_InstallAssembly( cache
, 0, manifest
, NULL
);
481 ERR("Failed to install assembly %s (0x%08x)\n", debugstr_w(manifest
), hr
);
482 return ERROR_FUNCTION_FAILED
;
484 if (feature
) feature
->Action
= INSTALLSTATE_LOCAL
;
485 assembly
->installed
= TRUE
;
486 return ERROR_SUCCESS
;
489 UINT
msi_uninstall_assembly( MSIPACKAGE
*package
, MSICOMPONENT
*comp
)
492 IAssemblyCache
*cache
;
493 MSIASSEMBLY
*assembly
= comp
->assembly
;
494 MSIFEATURE
*feature
= NULL
;
496 if (comp
->assembly
->feature
)
497 feature
= msi_get_loaded_feature( package
, comp
->assembly
->feature
);
499 if (assembly
->application
)
501 if (feature
) feature
->Action
= INSTALLSTATE_ABSENT
;
502 return ERROR_SUCCESS
;
504 TRACE("removing %s\n", debugstr_w(assembly
->display_name
));
506 if (assembly
->attributes
== msidbAssemblyAttributesWin32
)
508 cache
= package
->cache_sxs
;
509 hr
= IAssemblyCache_UninstallAssembly( cache
, 0, assembly
->display_name
, NULL
, NULL
);
510 if (FAILED( hr
)) WARN("failed to uninstall assembly 0x%08x\n", hr
);
515 for (i
= 0; i
< CLR_VERSION_MAX
; i
++)
517 if (!assembly
->clr_version
[i
]) continue;
518 cache
= package
->cache_net
[i
];
521 hr
= IAssemblyCache_UninstallAssembly( cache
, 0, assembly
->display_name
, NULL
, NULL
);
522 if (FAILED( hr
)) WARN("failed to uninstall assembly 0x%08x\n", hr
);
526 if (feature
) feature
->Action
= INSTALLSTATE_ABSENT
;
527 assembly
->installed
= FALSE
;
528 return ERROR_SUCCESS
;
531 static WCHAR
*build_local_assembly_path( const WCHAR
*filename
)
536 if (!(ret
= msi_alloc( (strlenW( filename
) + 1) * sizeof(WCHAR
) )))
539 for (i
= 0; filename
[i
]; i
++)
541 if (filename
[i
] == '\\' || filename
[i
] == '/') ret
[i
] = '|';
542 else ret
[i
] = filename
[i
];
548 static LONG
open_assemblies_key( UINT context
, BOOL win32
, HKEY
*hkey
)
550 static const WCHAR path_win32
[] =
551 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
552 '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};
553 static const WCHAR path_dotnet
[] =
554 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
555 'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0};
556 static const WCHAR classes_path_win32
[] =
557 {'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};
558 static const WCHAR classes_path_dotnet
[] =
559 {'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0};
563 if (context
== MSIINSTALLCONTEXT_MACHINE
)
565 root
= HKEY_CLASSES_ROOT
;
566 if (win32
) path
= classes_path_win32
;
567 else path
= classes_path_dotnet
;
571 root
= HKEY_CURRENT_USER
;
572 if (win32
) path
= path_win32
;
573 else path
= path_dotnet
;
575 return RegCreateKeyW( root
, path
, hkey
);
578 static LONG
open_local_assembly_key( UINT context
, BOOL win32
, const WCHAR
*filename
, HKEY
*hkey
)
584 if (!(path
= build_local_assembly_path( filename
)))
585 return ERROR_OUTOFMEMORY
;
587 if ((res
= open_assemblies_key( context
, win32
, &root
)))
592 res
= RegCreateKeyW( root
, path
, hkey
);
598 static LONG
delete_local_assembly_key( UINT context
, BOOL win32
, const WCHAR
*filename
)
604 if (!(path
= build_local_assembly_path( filename
)))
605 return ERROR_OUTOFMEMORY
;
607 if ((res
= open_assemblies_key( context
, win32
, &root
)))
612 res
= RegDeleteKeyW( root
, path
);
618 static LONG
open_global_assembly_key( UINT context
, BOOL win32
, HKEY
*hkey
)
620 static const WCHAR path_win32
[] =
621 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
622 'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',
623 'G','l','o','b','a','l',0};
624 static const WCHAR path_dotnet
[] =
625 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
626 'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',
627 'G','l','o','b','a','l',0};
628 static const WCHAR classes_path_win32
[] =
629 {'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',
630 'G','l','o','b','a','l',0};
631 static const WCHAR classes_path_dotnet
[] =
632 {'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};
636 if (context
== MSIINSTALLCONTEXT_MACHINE
)
638 root
= HKEY_CLASSES_ROOT
;
639 if (win32
) path
= classes_path_win32
;
640 else path
= classes_path_dotnet
;
644 root
= HKEY_CURRENT_USER
;
645 if (win32
) path
= path_win32
;
646 else path
= path_dotnet
;
648 return RegCreateKeyW( root
, path
, hkey
);
651 UINT
ACTION_MsiPublishAssemblies( MSIPACKAGE
*package
)
655 LIST_FOR_EACH_ENTRY(comp
, &package
->components
, MSICOMPONENT
, entry
)
663 MSIASSEMBLY
*assembly
= comp
->assembly
;
666 if (!assembly
|| !comp
->ComponentId
) continue;
668 comp
->Action
= msi_get_component_action( package
, comp
);
669 if (comp
->Action
!= INSTALLSTATE_LOCAL
)
671 TRACE("component not scheduled for installation %s\n", debugstr_w(comp
->Component
));
674 TRACE("publishing %s\n", debugstr_w(comp
->Component
));
676 CLSIDFromString( package
->ProductCode
, &guid
);
677 encode_base85_guid( &guid
, buffer
);
679 CLSIDFromString( comp
->ComponentId
, &guid
);
680 encode_base85_guid( &guid
, buffer
+ 21 );
683 win32
= assembly
->attributes
& msidbAssemblyAttributesWin32
;
684 if (assembly
->application
)
686 MSIFILE
*file
= msi_get_loaded_file( package
, assembly
->application
);
687 if ((res
= open_local_assembly_key( package
->Context
, win32
, file
->TargetPath
, &hkey
)))
689 WARN("failed to open local assembly key %d\n", res
);
690 return ERROR_FUNCTION_FAILED
;
695 if ((res
= open_global_assembly_key( package
->Context
, win32
, &hkey
)))
697 WARN("failed to open global assembly key %d\n", res
);
698 return ERROR_FUNCTION_FAILED
;
701 size
= sizeof(buffer
);
702 if ((res
= RegSetValueExW( hkey
, assembly
->display_name
, 0, REG_MULTI_SZ
, (const BYTE
*)buffer
, size
)))
704 WARN("failed to set assembly value %d\n", res
);
708 uirow
= MSI_CreateRecord( 2 );
709 MSI_RecordSetStringW( uirow
, 2, assembly
->display_name
);
710 msi_ui_actiondata( package
, szMsiPublishAssemblies
, uirow
);
711 msiobj_release( &uirow
->hdr
);
713 return ERROR_SUCCESS
;
716 UINT
ACTION_MsiUnpublishAssemblies( MSIPACKAGE
*package
)
720 LIST_FOR_EACH_ENTRY(comp
, &package
->components
, MSICOMPONENT
, entry
)
724 MSIASSEMBLY
*assembly
= comp
->assembly
;
727 if (!assembly
|| !comp
->ComponentId
) continue;
729 comp
->Action
= msi_get_component_action( package
, comp
);
730 if (comp
->Action
!= INSTALLSTATE_ABSENT
)
732 TRACE("component not scheduled for removal %s\n", debugstr_w(comp
->Component
));
735 TRACE("unpublishing %s\n", debugstr_w(comp
->Component
));
737 win32
= assembly
->attributes
& msidbAssemblyAttributesWin32
;
738 if (assembly
->application
)
740 MSIFILE
*file
= msi_get_loaded_file( package
, assembly
->application
);
741 if ((res
= delete_local_assembly_key( package
->Context
, win32
, file
->TargetPath
)))
742 WARN("failed to delete local assembly key %d\n", res
);
747 if ((res
= open_global_assembly_key( package
->Context
, win32
, &hkey
)))
748 WARN("failed to delete global assembly key %d\n", res
);
751 if ((res
= RegDeleteValueW( hkey
, assembly
->display_name
)))
752 WARN("failed to delete global assembly value %d\n", res
);
757 uirow
= MSI_CreateRecord( 2 );
758 MSI_RecordSetStringW( uirow
, 2, assembly
->display_name
);
759 msi_ui_actiondata( package
, szMsiPublishAssemblies
, uirow
);
760 msiobj_release( &uirow
->hdr
);
762 return ERROR_SUCCESS
;