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
* );
42 static HMODULE hfusion10
, hfusion11
, hfusion20
, hfusion40
, hmscoree
, hsxs
;
44 static BOOL
init_function_pointers( void )
46 static const WCHAR szFusion
[] = {'f','u','s','i','o','n','.','d','l','l',0};
47 static const WCHAR szMscoree
[] = {'\\','m','s','c','o','r','e','e','.','d','l','l',0};
48 static const WCHAR szSxs
[] = {'s','x','s','.','d','l','l',0};
49 static const WCHAR szVersion10
[] = {'v','1','.','0','.','3','7','0','5',0};
50 static const WCHAR szVersion11
[] = {'v','1','.','1','.','4','3','2','2',0};
51 static const WCHAR szVersion20
[] = {'v','2','.','0','.','5','0','7','2','7',0};
52 static const WCHAR szVersion40
[] = {'v','4','.','0','.','3','0','3','1','9',0};
54 DWORD len
= GetSystemDirectoryW( path
, MAX_PATH
);
56 if (!hsxs
&& !(hsxs
= LoadLibraryW( szSxs
))) return FALSE
;
57 if (!(pCreateAssemblyCacheSxs
= (void *)GetProcAddress( hsxs
, "CreateAssemblyCache" )))
63 strcpyW( path
+ len
, szMscoree
);
64 if (hmscoree
|| !(hmscoree
= LoadLibraryW( path
))) return TRUE
;
65 pGetFileVersion
= (void *)GetProcAddress( hmscoree
, "GetFileVersion" ); /* missing from v1.0.3705 */
66 if (!(pLoadLibraryShim
= (void *)GetProcAddress( hmscoree
, "LoadLibraryShim" )))
68 FreeLibrary( hmscoree
);
72 if (!pLoadLibraryShim( szFusion
, szVersion10
, NULL
, &hfusion10
))
73 pCreateAssemblyCacheNet10
= (void *)GetProcAddress( hfusion10
, "CreateAssemblyCache" );
75 if (!pLoadLibraryShim( szFusion
, szVersion11
, NULL
, &hfusion11
))
76 pCreateAssemblyCacheNet11
= (void *)GetProcAddress( hfusion11
, "CreateAssemblyCache" );
78 if (!pLoadLibraryShim( szFusion
, szVersion20
, NULL
, &hfusion20
))
79 pCreateAssemblyCacheNet20
= (void *)GetProcAddress( hfusion20
, "CreateAssemblyCache" );
81 if (!pLoadLibraryShim( szFusion
, szVersion40
, NULL
, &hfusion40
))
82 pCreateAssemblyCacheNet40
= (void *)GetProcAddress( hfusion40
, "CreateAssemblyCache" );
87 BOOL
msi_init_assembly_caches( MSIPACKAGE
*package
)
89 if (!init_function_pointers()) return FALSE
;
90 if (pCreateAssemblyCacheSxs( &package
->cache_sxs
, 0 ) != S_OK
) return FALSE
;
91 if (pCreateAssemblyCacheNet10
) pCreateAssemblyCacheNet10( &package
->cache_net
[CLR_VERSION_V10
], 0 );
92 if (pCreateAssemblyCacheNet11
) pCreateAssemblyCacheNet11( &package
->cache_net
[CLR_VERSION_V11
], 0 );
93 if (pCreateAssemblyCacheNet20
) pCreateAssemblyCacheNet20( &package
->cache_net
[CLR_VERSION_V20
], 0 );
94 if (pCreateAssemblyCacheNet40
) pCreateAssemblyCacheNet40( &package
->cache_net
[CLR_VERSION_V40
], 0 );
98 void msi_destroy_assembly_caches( MSIPACKAGE
*package
)
102 if (package
->cache_sxs
)
104 IAssemblyCache_Release( package
->cache_sxs
);
105 package
->cache_sxs
= NULL
;
107 for (i
= 0; i
< CLR_VERSION_MAX
; i
++)
109 if (package
->cache_net
[i
])
111 IAssemblyCache_Release( package
->cache_net
[i
] );
112 package
->cache_net
[i
] = NULL
;
115 pCreateAssemblyCacheNet10
= NULL
;
116 pCreateAssemblyCacheNet11
= NULL
;
117 pCreateAssemblyCacheNet20
= NULL
;
118 pCreateAssemblyCacheNet40
= NULL
;
119 FreeLibrary( hfusion10
);
120 FreeLibrary( hfusion11
);
121 FreeLibrary( hfusion20
);
122 FreeLibrary( hfusion40
);
123 FreeLibrary( hmscoree
);
133 static MSIRECORD
*get_assembly_record( MSIPACKAGE
*package
, const WCHAR
*comp
)
135 static const WCHAR query
[] = {
136 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
137 '`','M','s','i','A','s','s','e','m','b','l','y','`',' ',
138 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
139 ' ','=',' ','\'','%','s','\'',0};
144 r
= MSI_OpenQuery( package
->db
, &view
, query
, comp
);
145 if (r
!= ERROR_SUCCESS
)
148 r
= MSI_ViewExecute( view
, NULL
);
149 if (r
!= ERROR_SUCCESS
)
151 msiobj_release( &view
->hdr
);
154 r
= MSI_ViewFetch( view
, &rec
);
155 if (r
!= ERROR_SUCCESS
)
157 msiobj_release( &view
->hdr
);
160 if (!MSI_RecordGetString( rec
, 4 ))
161 TRACE("component is a global assembly\n");
163 msiobj_release( &view
->hdr
);
174 static UINT
get_assembly_name_attribute( MSIRECORD
*rec
, LPVOID param
)
176 static const WCHAR fmtW
[] = {'%','s','=','"','%','s','"',0};
177 static const WCHAR nameW
[] = {'n','a','m','e',0};
178 struct assembly_name
*name
= param
;
179 const WCHAR
*attr
= MSI_RecordGetString( rec
, 2 );
180 const WCHAR
*value
= MSI_RecordGetString( rec
, 3 );
181 int len
= strlenW( fmtW
) + strlenW( attr
) + strlenW( value
);
183 if (!(name
->attrs
[name
->index
] = msi_alloc( len
* sizeof(WCHAR
) )))
184 return ERROR_OUTOFMEMORY
;
186 if (!strcmpiW( attr
, nameW
)) strcpyW( name
->attrs
[name
->index
++], value
);
187 else sprintfW( name
->attrs
[name
->index
++], fmtW
, attr
, value
);
188 return ERROR_SUCCESS
;
191 static WCHAR
*get_assembly_display_name( MSIDATABASE
*db
, const WCHAR
*comp
, MSIASSEMBLY
*assembly
)
193 static const WCHAR commaW
[] = {',',0};
194 static const WCHAR queryW
[] = {
195 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
196 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
197 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
198 ' ','=',' ','\'','%','s','\'',0};
199 struct assembly_name name
;
200 WCHAR
*display_name
= NULL
;
205 r
= MSI_OpenQuery( db
, &view
, queryW
, comp
);
206 if (r
!= ERROR_SUCCESS
)
212 MSI_IterateRecords( view
, &name
.count
, NULL
, NULL
);
213 if (!name
.count
) goto done
;
215 name
.attrs
= msi_alloc( name
.count
* sizeof(WCHAR
*) );
216 if (!name
.attrs
) goto done
;
218 MSI_IterateRecords( view
, NULL
, get_assembly_name_attribute
, &name
);
221 for (i
= 0; i
< name
.count
; i
++) len
+= strlenW( name
.attrs
[i
] ) + 1;
223 display_name
= msi_alloc( (len
+ 1) * sizeof(WCHAR
) );
227 for (i
= 0; i
< name
.count
; i
++)
229 strcatW( display_name
, name
.attrs
[i
] );
230 if (i
< name
.count
- 1) strcatW( display_name
, commaW
);
235 msiobj_release( &view
->hdr
);
238 for (i
= 0; i
< name
.count
; i
++) msi_free( name
.attrs
[i
] );
239 msi_free( name
.attrs
);
244 static BOOL
is_assembly_installed( IAssemblyCache
*cache
, const WCHAR
*display_name
)
249 if (!cache
) return FALSE
;
251 memset( &info
, 0, sizeof(info
) );
252 info
.cbAssemblyInfo
= sizeof(info
);
253 hr
= IAssemblyCache_QueryAssemblyInfo( cache
, 0, display_name
, &info
);
254 if (hr
== S_OK
/* sxs version */ || hr
== E_NOT_SUFFICIENT_BUFFER
)
256 return (info
.dwAssemblyFlags
== ASSEMBLYINFO_FLAG_INSTALLED
);
258 TRACE("QueryAssemblyInfo returned 0x%08x\n", hr
);
262 static const WCHAR clr_version_v10
[] = {'v','1','.','0','.','3','7','0','5',0};
263 static const WCHAR clr_version_v11
[] = {'v','1','.','1','.','4','3','2','2',0};
264 static const WCHAR clr_version_v20
[] = {'v','2','.','0','.','5','0','7','2','7',0};
265 static const WCHAR clr_version_v40
[] = {'v','4','.','0','.','3','0','3','1','9',0};
266 static const WCHAR clr_version_unknown
[] = {'u','n','k','n','o','w','n',0};
268 static const WCHAR
*clr_version
[] =
276 static const WCHAR
*get_clr_version_str( enum clr_version version
)
278 if (version
>= sizeof(clr_version
)/sizeof(clr_version
[0])) return clr_version_unknown
;
279 return clr_version
[version
];
282 /* assembly caches must be initialized */
283 MSIASSEMBLY
*msi_load_assembly( MSIPACKAGE
*package
, MSICOMPONENT
*comp
)
288 if (!(rec
= get_assembly_record( package
, comp
->Component
))) return NULL
;
289 if (!(a
= msi_alloc_zero( sizeof(MSIASSEMBLY
) )))
291 msiobj_release( &rec
->hdr
);
294 a
->feature
= strdupW( MSI_RecordGetString( rec
, 2 ) );
295 TRACE("feature %s\n", debugstr_w(a
->feature
));
297 a
->manifest
= strdupW( MSI_RecordGetString( rec
, 3 ) );
298 TRACE("manifest %s\n", debugstr_w(a
->manifest
));
300 a
->application
= strdupW( MSI_RecordGetString( rec
, 4 ) );
301 TRACE("application %s\n", debugstr_w(a
->application
));
303 a
->attributes
= MSI_RecordGetInteger( rec
, 5 );
304 TRACE("attributes %u\n", a
->attributes
);
306 if (!(a
->display_name
= get_assembly_display_name( package
->db
, comp
->Component
, a
)))
308 WARN("can't get display name\n");
309 msiobj_release( &rec
->hdr
);
310 msi_free( a
->feature
);
311 msi_free( a
->manifest
);
312 msi_free( a
->application
);
316 TRACE("display name %s\n", debugstr_w(a
->display_name
));
320 /* We can't check the manifest here because the target path may still change.
321 So we assume that the assembly is not installed and lean on the InstallFiles
322 action to determine which files need to be installed.
324 a
->installed
= FALSE
;
328 if (a
->attributes
== msidbAssemblyAttributesWin32
)
329 a
->installed
= is_assembly_installed( package
->cache_sxs
, a
->display_name
);
333 for (i
= 0; i
< CLR_VERSION_MAX
; i
++)
335 a
->clr_version
[i
] = is_assembly_installed( package
->cache_net
[i
], a
->display_name
);
336 if (a
->clr_version
[i
])
338 TRACE("runtime version %s\n", debugstr_w(get_clr_version_str( i
)));
345 TRACE("assembly is %s\n", a
->installed
? "installed" : "not installed");
346 msiobj_release( &rec
->hdr
);
350 static enum clr_version
get_clr_version( const WCHAR
*filename
)
354 enum clr_version version
= CLR_VERSION_V11
;
357 if (!pGetFileVersion
) return CLR_VERSION_V10
;
359 hr
= pGetFileVersion( filename
, NULL
, 0, &len
);
360 if (hr
!= E_NOT_SUFFICIENT_BUFFER
) return CLR_VERSION_V11
;
361 if ((strW
= msi_alloc( len
* sizeof(WCHAR
) )))
363 hr
= pGetFileVersion( filename
, strW
, len
, &len
);
367 for (i
= 0; i
< CLR_VERSION_MAX
; i
++)
368 if (!strcmpW( strW
, clr_version
[i
] )) version
= i
;
375 UINT
msi_install_assembly( MSIPACKAGE
*package
, MSICOMPONENT
*comp
)
378 const WCHAR
*manifest
;
379 IAssemblyCache
*cache
;
380 MSIASSEMBLY
*assembly
= comp
->assembly
;
381 MSIFEATURE
*feature
= NULL
;
383 if (comp
->assembly
->feature
)
384 feature
= msi_get_loaded_feature( package
, comp
->assembly
->feature
);
386 if (assembly
->application
)
388 if (feature
) feature
->Action
= INSTALLSTATE_LOCAL
;
389 return ERROR_SUCCESS
;
391 if (assembly
->attributes
== msidbAssemblyAttributesWin32
)
393 if (!assembly
->manifest
)
395 WARN("no manifest\n");
396 return ERROR_FUNCTION_FAILED
;
398 manifest
= msi_get_loaded_file( package
, assembly
->manifest
)->TargetPath
;
399 cache
= package
->cache_sxs
;
403 manifest
= msi_get_loaded_file( package
, comp
->KeyPath
)->TargetPath
;
404 cache
= package
->cache_net
[get_clr_version( manifest
)];
405 if (!cache
) return ERROR_SUCCESS
;
407 TRACE("installing assembly %s\n", debugstr_w(manifest
));
409 hr
= IAssemblyCache_InstallAssembly( cache
, 0, manifest
, NULL
);
412 ERR("Failed to install assembly %s (0x%08x)\n", debugstr_w(manifest
), hr
);
413 return ERROR_FUNCTION_FAILED
;
415 if (feature
) feature
->Action
= INSTALLSTATE_LOCAL
;
416 assembly
->installed
= TRUE
;
417 return ERROR_SUCCESS
;
420 UINT
msi_uninstall_assembly( MSIPACKAGE
*package
, MSICOMPONENT
*comp
)
423 IAssemblyCache
*cache
;
424 MSIASSEMBLY
*assembly
= comp
->assembly
;
425 MSIFEATURE
*feature
= NULL
;
427 if (comp
->assembly
->feature
)
428 feature
= msi_get_loaded_feature( package
, comp
->assembly
->feature
);
430 if (assembly
->application
)
432 if (feature
) feature
->Action
= INSTALLSTATE_ABSENT
;
433 return ERROR_SUCCESS
;
435 TRACE("removing %s\n", debugstr_w(assembly
->display_name
));
437 if (assembly
->attributes
== msidbAssemblyAttributesWin32
)
439 cache
= package
->cache_sxs
;
440 hr
= IAssemblyCache_UninstallAssembly( cache
, 0, assembly
->display_name
, NULL
, NULL
);
441 if (FAILED( hr
)) WARN("failed to uninstall assembly 0x%08x\n", hr
);
446 for (i
= 0; i
< CLR_VERSION_MAX
; i
++)
448 if (!assembly
->clr_version
[i
]) continue;
449 cache
= package
->cache_net
[i
];
452 hr
= IAssemblyCache_UninstallAssembly( cache
, 0, assembly
->display_name
, NULL
, NULL
);
453 if (FAILED( hr
)) WARN("failed to uninstall assembly 0x%08x\n", hr
);
457 if (feature
) feature
->Action
= INSTALLSTATE_ABSENT
;
458 assembly
->installed
= FALSE
;
459 return ERROR_SUCCESS
;
462 static WCHAR
*build_local_assembly_path( const WCHAR
*filename
)
467 if (!(ret
= msi_alloc( (strlenW( filename
) + 1) * sizeof(WCHAR
) )))
470 for (i
= 0; filename
[i
]; i
++)
472 if (filename
[i
] == '\\' || filename
[i
] == '/') ret
[i
] = '|';
473 else ret
[i
] = filename
[i
];
479 static LONG
open_assemblies_key( UINT context
, BOOL win32
, HKEY
*hkey
)
481 static const WCHAR path_win32
[] =
482 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
483 '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};
484 static const WCHAR path_dotnet
[] =
485 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
486 'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0};
487 static const WCHAR classes_path_win32
[] =
488 {'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};
489 static const WCHAR classes_path_dotnet
[] =
490 {'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0};
494 if (context
== MSIINSTALLCONTEXT_MACHINE
)
496 root
= HKEY_CLASSES_ROOT
;
497 if (win32
) path
= classes_path_win32
;
498 else path
= classes_path_dotnet
;
502 root
= HKEY_CURRENT_USER
;
503 if (win32
) path
= path_win32
;
504 else path
= path_dotnet
;
506 return RegCreateKeyW( root
, path
, hkey
);
509 static LONG
open_local_assembly_key( UINT context
, BOOL win32
, const WCHAR
*filename
, HKEY
*hkey
)
515 if (!(path
= build_local_assembly_path( filename
)))
516 return ERROR_OUTOFMEMORY
;
518 if ((res
= open_assemblies_key( context
, win32
, &root
)))
523 res
= RegCreateKeyW( root
, path
, hkey
);
529 static LONG
delete_local_assembly_key( UINT context
, BOOL win32
, const WCHAR
*filename
)
535 if (!(path
= build_local_assembly_path( filename
)))
536 return ERROR_OUTOFMEMORY
;
538 if ((res
= open_assemblies_key( context
, win32
, &root
)))
543 res
= RegDeleteKeyW( root
, path
);
549 static LONG
open_global_assembly_key( UINT context
, BOOL win32
, HKEY
*hkey
)
551 static const WCHAR path_win32
[] =
552 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
553 'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',
554 'G','l','o','b','a','l',0};
555 static const WCHAR path_dotnet
[] =
556 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
557 'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',
558 'G','l','o','b','a','l',0};
559 static const WCHAR classes_path_win32
[] =
560 {'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',
561 'G','l','o','b','a','l',0};
562 static const WCHAR classes_path_dotnet
[] =
563 {'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};
567 if (context
== MSIINSTALLCONTEXT_MACHINE
)
569 root
= HKEY_CLASSES_ROOT
;
570 if (win32
) path
= classes_path_win32
;
571 else path
= classes_path_dotnet
;
575 root
= HKEY_CURRENT_USER
;
576 if (win32
) path
= path_win32
;
577 else path
= path_dotnet
;
579 return RegCreateKeyW( root
, path
, hkey
);
582 UINT
ACTION_MsiPublishAssemblies( MSIPACKAGE
*package
)
586 LIST_FOR_EACH_ENTRY(comp
, &package
->components
, MSICOMPONENT
, entry
)
594 MSIASSEMBLY
*assembly
= comp
->assembly
;
597 if (!assembly
|| !comp
->ComponentId
) continue;
599 comp
->Action
= msi_get_component_action( package
, comp
);
600 if (comp
->Action
!= INSTALLSTATE_LOCAL
)
602 TRACE("component not scheduled for installation %s\n", debugstr_w(comp
->Component
));
605 TRACE("publishing %s\n", debugstr_w(comp
->Component
));
607 CLSIDFromString( package
->ProductCode
, &guid
);
608 encode_base85_guid( &guid
, buffer
);
610 CLSIDFromString( comp
->ComponentId
, &guid
);
611 encode_base85_guid( &guid
, buffer
+ 21 );
614 win32
= assembly
->attributes
& msidbAssemblyAttributesWin32
;
615 if (assembly
->application
)
617 MSIFILE
*file
= msi_get_loaded_file( package
, assembly
->application
);
618 if ((res
= open_local_assembly_key( package
->Context
, win32
, file
->TargetPath
, &hkey
)))
620 WARN("failed to open local assembly key %d\n", res
);
621 return ERROR_FUNCTION_FAILED
;
626 if ((res
= open_global_assembly_key( package
->Context
, win32
, &hkey
)))
628 WARN("failed to open global assembly key %d\n", res
);
629 return ERROR_FUNCTION_FAILED
;
632 size
= sizeof(buffer
);
633 if ((res
= RegSetValueExW( hkey
, assembly
->display_name
, 0, REG_MULTI_SZ
, (const BYTE
*)buffer
, size
)))
635 WARN("failed to set assembly value %d\n", res
);
639 uirow
= MSI_CreateRecord( 2 );
640 MSI_RecordSetStringW( uirow
, 2, assembly
->display_name
);
641 msi_ui_actiondata( package
, szMsiPublishAssemblies
, uirow
);
642 msiobj_release( &uirow
->hdr
);
644 return ERROR_SUCCESS
;
647 UINT
ACTION_MsiUnpublishAssemblies( MSIPACKAGE
*package
)
651 LIST_FOR_EACH_ENTRY(comp
, &package
->components
, MSICOMPONENT
, entry
)
655 MSIASSEMBLY
*assembly
= comp
->assembly
;
658 if (!assembly
|| !comp
->ComponentId
) continue;
660 comp
->Action
= msi_get_component_action( package
, comp
);
661 if (comp
->Action
!= INSTALLSTATE_ABSENT
)
663 TRACE("component not scheduled for removal %s\n", debugstr_w(comp
->Component
));
666 TRACE("unpublishing %s\n", debugstr_w(comp
->Component
));
668 win32
= assembly
->attributes
& msidbAssemblyAttributesWin32
;
669 if (assembly
->application
)
671 MSIFILE
*file
= msi_get_loaded_file( package
, assembly
->application
);
672 if ((res
= delete_local_assembly_key( package
->Context
, win32
, file
->TargetPath
)))
673 WARN("failed to delete local assembly key %d\n", res
);
678 if ((res
= open_global_assembly_key( package
->Context
, win32
, &hkey
)))
679 WARN("failed to delete global assembly key %d\n", res
);
682 if ((res
= RegDeleteValueW( hkey
, assembly
->display_name
)))
683 WARN("failed to delete global assembly value %d\n", res
);
688 uirow
= MSI_CreateRecord( 2 );
689 MSI_RecordSetStringW( uirow
, 2, assembly
->display_name
);
690 msi_ui_actiondata( package
, szMsiPublishAssemblies
, uirow
);
691 msiobj_release( &uirow
->hdr
);
693 return ERROR_SUCCESS
;