Merge PR #283 "[USBPORT] Transaction Translator (TT) support bringup"
[reactos.git] / dll / win32 / msi / assembly.c
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
3 *
4 * Copyright 2010 Hans Leidekker for CodeWeavers
5 *
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.
10 *
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.
15 *
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
19 */
20
21 #include <stdarg.h>
22
23 #define COBJMACROS
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winreg.h"
28 #include "wine/debug.h"
29 #include "wine/unicode.h"
30 #include "msipriv.h"
31
32 WINE_DEFAULT_DEBUG_CHANNEL(msi);
33
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 );
43
44 static HMODULE hfusion10, hfusion11, hfusion20, hfusion40, hmscoree, hsxs;
45
46 static BOOL init_function_pointers( void )
47 {
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};
55 WCHAR path[MAX_PATH];
56 DWORD len = GetSystemDirectoryW( path, MAX_PATH );
57
58 if (!hsxs && !(hsxs = LoadLibraryW( szSxs ))) return FALSE;
59 if (!(pCreateAssemblyCacheSxs = (void *)GetProcAddress( hsxs, "CreateAssemblyCache" )))
60 {
61 FreeLibrary( hsxs );
62 hsxs = NULL;
63 return FALSE;
64 }
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" )))
69 {
70 FreeLibrary( hmscoree );
71 hmscoree = NULL;
72 return TRUE;
73 }
74 if (!pLoadLibraryShim( szFusion, szVersion10, NULL, &hfusion10 ))
75 pCreateAssemblyCacheNet10 = (void *)GetProcAddress( hfusion10, "CreateAssemblyCache" );
76
77 if (!pLoadLibraryShim( szFusion, szVersion11, NULL, &hfusion11 ))
78 pCreateAssemblyCacheNet11 = (void *)GetProcAddress( hfusion11, "CreateAssemblyCache" );
79
80 if (!pLoadLibraryShim( szFusion, szVersion20, NULL, &hfusion20 ))
81 pCreateAssemblyCacheNet20 = (void *)GetProcAddress( hfusion20, "CreateAssemblyCache" );
82
83 if (!pLoadLibraryShim( szFusion, szVersion40, NULL, &hfusion40 ))
84 {
85 pCreateAssemblyCacheNet40 = (void *)GetProcAddress( hfusion40, "CreateAssemblyCache" );
86 pCreateAssemblyNameObject = (void *)GetProcAddress( hfusion40, "CreateAssemblyNameObject" );
87 pCreateAssemblyEnum = (void *)GetProcAddress( hfusion40, "CreateAssemblyEnum" );
88 }
89 return TRUE;
90 }
91
92 BOOL msi_init_assembly_caches( MSIPACKAGE *package )
93 {
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 );
100 return TRUE;
101 }
102
103 void msi_destroy_assembly_caches( MSIPACKAGE *package )
104 {
105 UINT i;
106
107 if (package->cache_sxs)
108 {
109 IAssemblyCache_Release( package->cache_sxs );
110 package->cache_sxs = NULL;
111 }
112 for (i = 0; i < CLR_VERSION_MAX; i++)
113 {
114 if (package->cache_net[i])
115 {
116 IAssemblyCache_Release( package->cache_net[i] );
117 package->cache_net[i] = NULL;
118 }
119 }
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 );
129 FreeLibrary( hsxs );
130 hfusion10 = NULL;
131 hfusion11 = NULL;
132 hfusion20 = NULL;
133 hfusion40 = NULL;
134 hmscoree = NULL;
135 hsxs = NULL;
136 }
137
138 static MSIRECORD *get_assembly_record( MSIPACKAGE *package, const WCHAR *comp )
139 {
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};
145 MSIQUERY *view;
146 MSIRECORD *rec;
147 UINT r;
148
149 r = MSI_OpenQuery( package->db, &view, query, comp );
150 if (r != ERROR_SUCCESS)
151 return NULL;
152
153 r = MSI_ViewExecute( view, NULL );
154 if (r != ERROR_SUCCESS)
155 {
156 msiobj_release( &view->hdr );
157 return NULL;
158 }
159 r = MSI_ViewFetch( view, &rec );
160 if (r != ERROR_SUCCESS)
161 {
162 msiobj_release( &view->hdr );
163 return NULL;
164 }
165 if (!MSI_RecordGetString( rec, 4 ))
166 TRACE("component is a global assembly\n");
167
168 msiobj_release( &view->hdr );
169 return rec;
170 }
171
172 struct assembly_name
173 {
174 UINT count;
175 UINT index;
176 WCHAR **attrs;
177 };
178
179 static UINT get_assembly_name_attribute( MSIRECORD *rec, LPVOID param )
180 {
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 );
187
188 if (!(name->attrs[name->index] = msi_alloc( len * sizeof(WCHAR) )))
189 return ERROR_OUTOFMEMORY;
190
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;
194 }
195
196 static WCHAR *get_assembly_display_name( MSIDATABASE *db, const WCHAR *comp, MSIASSEMBLY *assembly )
197 {
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;
206 MSIQUERY *view;
207 UINT i, r;
208 int len;
209
210 r = MSI_OpenQuery( db, &view, queryW, comp );
211 if (r != ERROR_SUCCESS)
212 return NULL;
213
214 name.count = 0;
215 name.index = 0;
216 name.attrs = NULL;
217 MSI_IterateRecords( view, &name.count, NULL, NULL );
218 if (!name.count) goto done;
219
220 name.attrs = msi_alloc( name.count * sizeof(WCHAR *) );
221 if (!name.attrs) goto done;
222
223 MSI_IterateRecords( view, NULL, get_assembly_name_attribute, &name );
224
225 len = 0;
226 for (i = 0; i < name.count; i++) len += strlenW( name.attrs[i] ) + 1;
227
228 display_name = msi_alloc( (len + 1) * sizeof(WCHAR) );
229 if (display_name)
230 {
231 display_name[0] = 0;
232 for (i = 0; i < name.count; i++)
233 {
234 strcatW( display_name, name.attrs[i] );
235 if (i < name.count - 1) strcatW( display_name, commaW );
236 }
237 }
238
239 done:
240 msiobj_release( &view->hdr );
241 if (name.attrs)
242 {
243 for (i = 0; i < name.count; i++) msi_free( name.attrs[i] );
244 msi_free( name.attrs );
245 }
246 return display_name;
247 }
248
249 static BOOL is_assembly_installed( IAssemblyCache *cache, const WCHAR *display_name )
250 {
251 HRESULT hr;
252 ASSEMBLY_INFO info;
253
254 if (!cache) return FALSE;
255
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)
260 {
261 return (info.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
262 }
263 TRACE("QueryAssemblyInfo returned 0x%08x\n", hr);
264 return FALSE;
265 }
266
267 WCHAR *msi_get_assembly_path( MSIPACKAGE *package, const WCHAR *displayname )
268 {
269 HRESULT hr;
270 ASSEMBLY_INFO info;
271 IAssemblyCache *cache = package->cache_net[CLR_VERSION_V40];
272
273 if (!cache) return NULL;
274
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;
279
280 if (!(info.pszCurrentAssemblyPathBuf = msi_alloc( info.cchBuf * sizeof(WCHAR) ))) return NULL;
281
282 hr = IAssemblyCache_QueryAssemblyInfo( cache, 0, displayname, &info );
283 if (FAILED( hr ))
284 {
285 msi_free( info.pszCurrentAssemblyPathBuf );
286 return NULL;
287 }
288 TRACE("returning %s\n", debugstr_w(info.pszCurrentAssemblyPathBuf));
289 return info.pszCurrentAssemblyPathBuf;
290 }
291
292 IAssemblyEnum *msi_create_assembly_enum( MSIPACKAGE *package, const WCHAR *displayname )
293 {
294 HRESULT hr;
295 IAssemblyName *name;
296 IAssemblyEnum *ret;
297 WCHAR *str;
298 UINT len = 0;
299
300 if (!pCreateAssemblyNameObject || !pCreateAssemblyEnum) return NULL;
301
302 hr = pCreateAssemblyNameObject( &name, displayname, CANOF_PARSE_DISPLAY_NAME, NULL );
303 if (FAILED( hr )) return NULL;
304
305 hr = IAssemblyName_GetName( name, &len, NULL );
306 if (hr != E_NOT_SUFFICIENT_BUFFER || !(str = msi_alloc( len * sizeof(WCHAR) )))
307 {
308 IAssemblyName_Release( name );
309 return NULL;
310 }
311
312 hr = IAssemblyName_GetName( name, &len, str );
313 IAssemblyName_Release( name );
314 if (FAILED( hr ))
315 {
316 msi_free( str );
317 return NULL;
318 }
319
320 hr = pCreateAssemblyNameObject( &name, str, 0, NULL );
321 msi_free( str );
322 if (FAILED( hr )) return NULL;
323
324 hr = pCreateAssemblyEnum( &ret, NULL, name, ASM_CACHE_GAC, NULL );
325 IAssemblyName_Release( name );
326 if (FAILED( hr )) return NULL;
327
328 return ret;
329 }
330
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};
336
337 static const WCHAR *clr_version[] =
338 {
339 clr_version_v10,
340 clr_version_v11,
341 clr_version_v20,
342 clr_version_v40
343 };
344
345 static const WCHAR *get_clr_version_str( enum clr_version version )
346 {
347 if (version >= sizeof(clr_version)/sizeof(clr_version[0])) return clr_version_unknown;
348 return clr_version[version];
349 }
350
351 /* assembly caches must be initialized */
352 MSIASSEMBLY *msi_load_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
353 {
354 MSIRECORD *rec;
355 MSIASSEMBLY *a;
356
357 if (!(rec = get_assembly_record( package, comp->Component ))) return NULL;
358 if (!(a = msi_alloc_zero( sizeof(MSIASSEMBLY) )))
359 {
360 msiobj_release( &rec->hdr );
361 return NULL;
362 }
363 a->feature = strdupW( MSI_RecordGetString( rec, 2 ) );
364 TRACE("feature %s\n", debugstr_w(a->feature));
365
366 a->manifest = strdupW( MSI_RecordGetString( rec, 3 ) );
367 TRACE("manifest %s\n", debugstr_w(a->manifest));
368
369 a->application = strdupW( MSI_RecordGetString( rec, 4 ) );
370 TRACE("application %s\n", debugstr_w(a->application));
371
372 a->attributes = MSI_RecordGetInteger( rec, 5 );
373 TRACE("attributes %u\n", a->attributes);
374
375 if (!(a->display_name = get_assembly_display_name( package->db, comp->Component, a )))
376 {
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 );
382 msi_free( a );
383 return NULL;
384 }
385 TRACE("display name %s\n", debugstr_w(a->display_name));
386
387 if (a->application)
388 {
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.
392 */
393 a->installed = FALSE;
394 }
395 else
396 {
397 if (a->attributes == msidbAssemblyAttributesWin32)
398 a->installed = is_assembly_installed( package->cache_sxs, a->display_name );
399 else
400 {
401 UINT i;
402 for (i = 0; i < CLR_VERSION_MAX; i++)
403 {
404 a->clr_version[i] = is_assembly_installed( package->cache_net[i], a->display_name );
405 if (a->clr_version[i])
406 {
407 TRACE("runtime version %s\n", debugstr_w(get_clr_version_str( i )));
408 a->installed = TRUE;
409 break;
410 }
411 }
412 }
413 }
414 TRACE("assembly is %s\n", a->installed ? "installed" : "not installed");
415 msiobj_release( &rec->hdr );
416 return a;
417 }
418
419 static enum clr_version get_clr_version( const WCHAR *filename )
420 {
421 DWORD len;
422 HRESULT hr;
423 enum clr_version version = CLR_VERSION_V11;
424 WCHAR *strW;
425
426 if (!pGetFileVersion) return CLR_VERSION_V10;
427
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) )))
431 {
432 hr = pGetFileVersion( filename, strW, len, &len );
433 if (hr == S_OK)
434 {
435 UINT i;
436 for (i = 0; i < CLR_VERSION_MAX; i++)
437 if (!strcmpW( strW, clr_version[i] )) version = i;
438 }
439 msi_free( strW );
440 }
441 return version;
442 }
443
444 UINT msi_install_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
445 {
446 HRESULT hr;
447 const WCHAR *manifest;
448 IAssemblyCache *cache;
449 MSIASSEMBLY *assembly = comp->assembly;
450 MSIFEATURE *feature = NULL;
451
452 if (comp->assembly->feature)
453 feature = msi_get_loaded_feature( package, comp->assembly->feature );
454
455 if (assembly->application)
456 {
457 if (feature) feature->Action = INSTALLSTATE_LOCAL;
458 return ERROR_SUCCESS;
459 }
460 if (assembly->attributes == msidbAssemblyAttributesWin32)
461 {
462 if (!assembly->manifest)
463 {
464 WARN("no manifest\n");
465 return ERROR_FUNCTION_FAILED;
466 }
467 manifest = msi_get_loaded_file( package, assembly->manifest )->TargetPath;
468 cache = package->cache_sxs;
469 }
470 else
471 {
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;
475 }
476 TRACE("installing assembly %s\n", debugstr_w(manifest));
477
478 hr = IAssemblyCache_InstallAssembly( cache, 0, manifest, NULL );
479 if (hr != S_OK)
480 {
481 ERR("Failed to install assembly %s (0x%08x)\n", debugstr_w(manifest), hr);
482 return ERROR_FUNCTION_FAILED;
483 }
484 if (feature) feature->Action = INSTALLSTATE_LOCAL;
485 assembly->installed = TRUE;
486 return ERROR_SUCCESS;
487 }
488
489 UINT msi_uninstall_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
490 {
491 HRESULT hr;
492 IAssemblyCache *cache;
493 MSIASSEMBLY *assembly = comp->assembly;
494 MSIFEATURE *feature = NULL;
495
496 if (comp->assembly->feature)
497 feature = msi_get_loaded_feature( package, comp->assembly->feature );
498
499 if (assembly->application)
500 {
501 if (feature) feature->Action = INSTALLSTATE_ABSENT;
502 return ERROR_SUCCESS;
503 }
504 TRACE("removing %s\n", debugstr_w(assembly->display_name));
505
506 if (assembly->attributes == msidbAssemblyAttributesWin32)
507 {
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);
511 }
512 else
513 {
514 unsigned int i;
515 for (i = 0; i < CLR_VERSION_MAX; i++)
516 {
517 if (!assembly->clr_version[i]) continue;
518 cache = package->cache_net[i];
519 if (cache)
520 {
521 hr = IAssemblyCache_UninstallAssembly( cache, 0, assembly->display_name, NULL, NULL );
522 if (FAILED( hr )) WARN("failed to uninstall assembly 0x%08x\n", hr);
523 }
524 }
525 }
526 if (feature) feature->Action = INSTALLSTATE_ABSENT;
527 assembly->installed = FALSE;
528 return ERROR_SUCCESS;
529 }
530
531 static WCHAR *build_local_assembly_path( const WCHAR *filename )
532 {
533 UINT i;
534 WCHAR *ret;
535
536 if (!(ret = msi_alloc( (strlenW( filename ) + 1) * sizeof(WCHAR) )))
537 return NULL;
538
539 for (i = 0; filename[i]; i++)
540 {
541 if (filename[i] == '\\' || filename[i] == '/') ret[i] = '|';
542 else ret[i] = filename[i];
543 }
544 ret[i] = 0;
545 return ret;
546 }
547
548 static LONG open_assemblies_key( UINT context, BOOL win32, HKEY *hkey )
549 {
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};
560 HKEY root;
561 const WCHAR *path;
562
563 if (context == MSIINSTALLCONTEXT_MACHINE)
564 {
565 root = HKEY_CLASSES_ROOT;
566 if (win32) path = classes_path_win32;
567 else path = classes_path_dotnet;
568 }
569 else
570 {
571 root = HKEY_CURRENT_USER;
572 if (win32) path = path_win32;
573 else path = path_dotnet;
574 }
575 return RegCreateKeyW( root, path, hkey );
576 }
577
578 static LONG open_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename, HKEY *hkey )
579 {
580 LONG res;
581 HKEY root;
582 WCHAR *path;
583
584 if (!(path = build_local_assembly_path( filename )))
585 return ERROR_OUTOFMEMORY;
586
587 if ((res = open_assemblies_key( context, win32, &root )))
588 {
589 msi_free( path );
590 return res;
591 }
592 res = RegCreateKeyW( root, path, hkey );
593 RegCloseKey( root );
594 msi_free( path );
595 return res;
596 }
597
598 static LONG delete_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename )
599 {
600 LONG res;
601 HKEY root;
602 WCHAR *path;
603
604 if (!(path = build_local_assembly_path( filename )))
605 return ERROR_OUTOFMEMORY;
606
607 if ((res = open_assemblies_key( context, win32, &root )))
608 {
609 msi_free( path );
610 return res;
611 }
612 res = RegDeleteKeyW( root, path );
613 RegCloseKey( root );
614 msi_free( path );
615 return res;
616 }
617
618 static LONG open_global_assembly_key( UINT context, BOOL win32, HKEY *hkey )
619 {
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};
633 HKEY root;
634 const WCHAR *path;
635
636 if (context == MSIINSTALLCONTEXT_MACHINE)
637 {
638 root = HKEY_CLASSES_ROOT;
639 if (win32) path = classes_path_win32;
640 else path = classes_path_dotnet;
641 }
642 else
643 {
644 root = HKEY_CURRENT_USER;
645 if (win32) path = path_win32;
646 else path = path_dotnet;
647 }
648 return RegCreateKeyW( root, path, hkey );
649 }
650
651 UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
652 {
653 MSICOMPONENT *comp;
654
655 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
656 {
657 LONG res;
658 HKEY hkey;
659 GUID guid;
660 DWORD size;
661 WCHAR buffer[43];
662 MSIRECORD *uirow;
663 MSIASSEMBLY *assembly = comp->assembly;
664 BOOL win32;
665
666 if (!assembly || !comp->ComponentId) continue;
667
668 comp->Action = msi_get_component_action( package, comp );
669 if (comp->Action != INSTALLSTATE_LOCAL)
670 {
671 TRACE("component not scheduled for installation %s\n", debugstr_w(comp->Component));
672 continue;
673 }
674 TRACE("publishing %s\n", debugstr_w(comp->Component));
675
676 CLSIDFromString( package->ProductCode, &guid );
677 encode_base85_guid( &guid, buffer );
678 buffer[20] = '>';
679 CLSIDFromString( comp->ComponentId, &guid );
680 encode_base85_guid( &guid, buffer + 21 );
681 buffer[42] = 0;
682
683 win32 = assembly->attributes & msidbAssemblyAttributesWin32;
684 if (assembly->application)
685 {
686 MSIFILE *file = msi_get_loaded_file( package, assembly->application );
687 if ((res = open_local_assembly_key( package->Context, win32, file->TargetPath, &hkey )))
688 {
689 WARN("failed to open local assembly key %d\n", res);
690 return ERROR_FUNCTION_FAILED;
691 }
692 }
693 else
694 {
695 if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
696 {
697 WARN("failed to open global assembly key %d\n", res);
698 return ERROR_FUNCTION_FAILED;
699 }
700 }
701 size = sizeof(buffer);
702 if ((res = RegSetValueExW( hkey, assembly->display_name, 0, REG_MULTI_SZ, (const BYTE *)buffer, size )))
703 {
704 WARN("failed to set assembly value %d\n", res);
705 }
706 RegCloseKey( hkey );
707
708 uirow = MSI_CreateRecord( 2 );
709 MSI_RecordSetStringW( uirow, 2, assembly->display_name );
710 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
711 msiobj_release( &uirow->hdr );
712 }
713 return ERROR_SUCCESS;
714 }
715
716 UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
717 {
718 MSICOMPONENT *comp;
719
720 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
721 {
722 LONG res;
723 MSIRECORD *uirow;
724 MSIASSEMBLY *assembly = comp->assembly;
725 BOOL win32;
726
727 if (!assembly || !comp->ComponentId) continue;
728
729 comp->Action = msi_get_component_action( package, comp );
730 if (comp->Action != INSTALLSTATE_ABSENT)
731 {
732 TRACE("component not scheduled for removal %s\n", debugstr_w(comp->Component));
733 continue;
734 }
735 TRACE("unpublishing %s\n", debugstr_w(comp->Component));
736
737 win32 = assembly->attributes & msidbAssemblyAttributesWin32;
738 if (assembly->application)
739 {
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);
743 }
744 else
745 {
746 HKEY hkey;
747 if ((res = open_global_assembly_key( package->Context, win32, &hkey )))
748 WARN("failed to delete global assembly key %d\n", res);
749 else
750 {
751 if ((res = RegDeleteValueW( hkey, assembly->display_name )))
752 WARN("failed to delete global assembly value %d\n", res);
753 RegCloseKey( hkey );
754 }
755 }
756
757 uirow = MSI_CreateRecord( 2 );
758 MSI_RecordSetStringW( uirow, 2, assembly->display_name );
759 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
760 msiobj_release( &uirow->hdr );
761 }
762 return ERROR_SUCCESS;
763 }