2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2005 Aric Stewart 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * Here are helper functions formally in action.c that are used by a variaty of
23 * actions and functions.
31 #include "wine/debug.h"
34 #include "wine/unicode.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
39 static const WCHAR cszTargetDir
[] = {'T','A','R','G','E','T','D','I','R',0};
40 static const WCHAR cszDatabase
[]={'D','A','T','A','B','A','S','E',0};
42 const WCHAR cszSourceDir
[] = {'S','o','u','r','c','e','D','i','r',0};
43 const WCHAR szProductCode
[]= {'P','r','o','d','u','c','t','C','o','d','e',0};
44 const WCHAR cszRootDrive
[] = {'R','O','O','T','D','R','I','V','E',0};
45 const WCHAR cszbs
[]={'\\',0};
47 DWORD
build_version_dword(LPCWSTR version_string
)
51 DWORD rc
= 0x00000000;
54 ptr1
= version_string
;
63 ptr1
= strchrW(ptr1
,'.');
73 ptr1
= strchrW(ptr1
,'.');
83 rc
= MAKELONG(build
,MAKEWORD(minor
,major
));
84 TRACE("%s -> 0x%lx\n",debugstr_w(version_string
),rc
);
88 UINT
build_icon_path(MSIPACKAGE
*package
, LPCWSTR icon_name
,
96 static const WCHAR szInstaller
[] =
97 {'M','i','c','r','o','s','o','f','t','\\',
98 'I','n','s','t','a','l','l','e','r','\\',0};
99 static const WCHAR szFolder
[] =
100 {'A','p','p','D','a','t','a','F','o','l','d','e','r',0};
102 ProductCode
= load_dynamic_property(package
,szProductCode
,&rc
);
106 SystemFolder
= load_dynamic_property(package
,szFolder
,NULL
);
108 dest
= build_directory_name(3, SystemFolder
, szInstaller
, ProductCode
);
110 create_full_pathW(dest
);
112 *FilePath
= build_directory_name(2, dest
, icon_name
);
114 HeapFree(GetProcessHeap(),0,SystemFolder
);
115 HeapFree(GetProcessHeap(),0,ProductCode
);
116 HeapFree(GetProcessHeap(),0,dest
);
117 return ERROR_SUCCESS
;
120 WCHAR
*load_dynamic_stringW(MSIRECORD
*row
, INT index
)
127 if (MSI_RecordIsNull(row
,index
))
130 rc
= MSI_RecordGetStringW(row
,index
,NULL
,&sz
);
132 /* having an empty string is different than NULL */
135 ret
= HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR
));
141 ret
= HeapAlloc(GetProcessHeap(),0,sz
* sizeof (WCHAR
));
142 rc
= MSI_RecordGetStringW(row
,index
,ret
,&sz
);
143 if (rc
!=ERROR_SUCCESS
)
145 ERR("Unable to load dynamic string\n");
146 HeapFree(GetProcessHeap(), 0, ret
);
152 LPWSTR
load_dynamic_property(MSIPACKAGE
*package
, LPCWSTR prop
, UINT
* rc
)
158 r
= MSI_GetPropertyW(package
, prop
, NULL
, &sz
);
159 if (r
!= ERROR_SUCCESS
&& r
!= ERROR_MORE_DATA
)
166 str
= HeapAlloc(GetProcessHeap(),0,sz
*sizeof(WCHAR
));
167 r
= MSI_GetPropertyW(package
, prop
, str
, &sz
);
168 if (r
!= ERROR_SUCCESS
)
170 HeapFree(GetProcessHeap(),0,str
);
178 int get_loaded_component(MSIPACKAGE
* package
, LPCWSTR Component
)
183 for (i
= 0; i
< package
->loaded_components
; i
++)
185 if (strcmpW(Component
,package
->components
[i
].Component
)==0)
194 int get_loaded_feature(MSIPACKAGE
* package
, LPCWSTR Feature
)
199 for (i
= 0; i
< package
->loaded_features
; i
++)
201 if (strcmpW(Feature
,package
->features
[i
].Feature
)==0)
210 int get_loaded_file(MSIPACKAGE
* package
, LPCWSTR file
)
215 for (i
= 0; i
< package
->loaded_files
; i
++)
217 if (strcmpW(file
,package
->files
[i
].File
)==0)
226 int track_tempfile(MSIPACKAGE
*package
, LPCWSTR name
, LPCWSTR path
)
234 for (i
=0; i
< package
->loaded_files
; i
++)
235 if (strcmpW(package
->files
[i
].File
,name
)==0)
238 index
= package
->loaded_files
;
239 package
->loaded_files
++;
240 if (package
->loaded_files
== 1)
241 package
->files
= HeapAlloc(GetProcessHeap(),0,sizeof(MSIFILE
));
243 package
->files
= HeapReAlloc(GetProcessHeap(),0,
244 package
->files
, package
->loaded_files
* sizeof(MSIFILE
));
246 memset(&package
->files
[index
],0,sizeof(MSIFILE
));
248 package
->files
[index
].File
= strdupW(name
);
249 package
->files
[index
].TargetPath
= strdupW(path
);
250 package
->files
[index
].Temporary
= TRUE
;
252 TRACE("Tracking tempfile (%s)\n",debugstr_w(package
->files
[index
].File
));
257 LPWSTR
resolve_folder(MSIPACKAGE
*package
, LPCWSTR name
, BOOL source
,
258 BOOL set_prop
, MSIFOLDER
**folder
)
261 LPWSTR p
, path
= NULL
;
263 TRACE("Working to resolve %s\n",debugstr_w(name
));
268 /* special resolving for Target and Source root dir */
269 if (strcmpW(name
,cszTargetDir
)==0 || strcmpW(name
,cszSourceDir
)==0)
274 check_path
= load_dynamic_property(package
,cszTargetDir
,NULL
);
277 check_path
= load_dynamic_property(package
,cszRootDrive
,NULL
);
279 MSI_SetPropertyW(package
,cszTargetDir
,check_path
);
282 /* correct misbuilt target dir */
283 path
= build_directory_name(2, check_path
, NULL
);
284 if (strcmpiW(path
,check_path
)!=0)
285 MSI_SetPropertyW(package
,cszTargetDir
,path
);
289 for (i
= 0; i
< package
->loaded_folders
; i
++)
291 if (strcmpW(package
->folders
[i
].Directory
,name
)==0)
294 *folder
= &(package
->folders
[i
]);
300 path
= load_dynamic_property(package
,cszSourceDir
,NULL
);
303 path
= load_dynamic_property(package
,cszDatabase
,NULL
);
306 p
= strrchrW(path
,'\\');
313 for (i
= 0; i
< package
->loaded_folders
; i
++)
315 if (strcmpW(package
->folders
[i
].Directory
,name
)==0)
318 *folder
= &(package
->folders
[i
]);
324 for (i
= 0; i
< package
->loaded_folders
; i
++)
326 if (strcmpW(package
->folders
[i
].Directory
,name
)==0)
330 if (i
>= package
->loaded_folders
)
334 *folder
= &(package
->folders
[i
]);
336 if (!source
&& package
->folders
[i
].ResolvedTarget
)
338 path
= strdupW(package
->folders
[i
].ResolvedTarget
);
339 TRACE(" already resolved to %s\n",debugstr_w(path
));
342 else if (source
&& package
->folders
[i
].ResolvedSource
)
344 path
= strdupW(package
->folders
[i
].ResolvedSource
);
345 TRACE(" (source)already resolved to %s\n",debugstr_w(path
));
348 else if (!source
&& package
->folders
[i
].Property
)
350 path
= build_directory_name(2, package
->folders
[i
].Property
, NULL
);
352 TRACE(" internally set to %s\n",debugstr_w(path
));
354 MSI_SetPropertyW(package
,name
,path
);
358 if (package
->folders
[i
].ParentIndex
>= 0)
360 LPWSTR parent
= package
->folders
[package
->folders
[i
].ParentIndex
].Directory
;
362 TRACE(" ! Parent is %s\n", debugstr_w(parent
));
364 p
= resolve_folder(package
, parent
, source
, set_prop
, NULL
);
367 TRACE(" TargetDefault = %s\n",
368 debugstr_w(package
->folders
[i
].TargetDefault
));
370 path
= build_directory_name(3, p
,
371 package
->folders
[i
].TargetDefault
, NULL
);
372 package
->folders
[i
].ResolvedTarget
= strdupW(path
);
373 TRACE(" resolved into %s\n",debugstr_w(path
));
375 MSI_SetPropertyW(package
,name
,path
);
379 if (package
->folders
[i
].SourceDefault
&&
380 package
->folders
[i
].SourceDefault
[0]!='.')
381 path
= build_directory_name(3, p
, package
->folders
[i
].SourceDefault
, NULL
);
384 TRACE(" (source)resolved into %s\n",debugstr_w(path
));
385 package
->folders
[i
].ResolvedSource
= strdupW(path
);
387 HeapFree(GetProcessHeap(),0,p
);
392 /* wrapper to resist a need for a full rewrite right now */
393 DWORD
deformat_string(MSIPACKAGE
*package
, LPCWSTR ptr
, WCHAR
** data
)
397 MSIRECORD
*rec
= MSI_CreateRecord(1);
400 MSI_RecordSetStringW(rec
,0,ptr
);
401 MSI_FormatRecordW(package
,rec
,NULL
,&size
);
405 *data
= HeapAlloc(GetProcessHeap(),0,size
*sizeof(WCHAR
));
407 MSI_FormatRecordW(package
,rec
,*data
,&size
);
410 msiobj_release( &rec
->hdr
);
411 return sizeof(WCHAR
)*size
;
413 msiobj_release( &rec
->hdr
);
420 UINT
schedule_action(MSIPACKAGE
*package
, UINT script
, LPCWSTR action
)
423 LPWSTR
*newbuf
= NULL
;
424 if (script
>= TOTAL_SCRIPTS
)
426 FIXME("Unknown script requested %i\n",script
);
427 return ERROR_FUNCTION_FAILED
;
429 TRACE("Scheduling Action %s in script %i\n",debugstr_w(action
), script
);
431 count
= package
->script
->ActionCount
[script
];
432 package
->script
->ActionCount
[script
]++;
434 newbuf
= HeapReAlloc(GetProcessHeap(),0,
435 package
->script
->Actions
[script
],
436 package
->script
->ActionCount
[script
]* sizeof(LPWSTR
));
438 newbuf
= HeapAlloc(GetProcessHeap(),0, sizeof(LPWSTR
));
440 newbuf
[count
] = strdupW(action
);
441 package
->script
->Actions
[script
] = newbuf
;
443 return ERROR_SUCCESS
;
446 static void remove_tracked_tempfiles(MSIPACKAGE
* package
)
453 for (i
= 0; i
< package
->loaded_files
; i
++)
455 if (package
->files
[i
].Temporary
)
457 TRACE("Cleaning up %s\n",debugstr_w(package
->files
[i
].TargetPath
));
458 DeleteFileW(package
->files
[i
].TargetPath
);
464 /* Called when the package is being closed */
465 void ACTION_free_package_structures( MSIPACKAGE
* package
)
469 TRACE("Freeing package action data\n");
471 remove_tracked_tempfiles(package
);
473 /* No dynamic buffers in features */
474 if (package
->features
&& package
->loaded_features
> 0)
475 HeapFree(GetProcessHeap(),0,package
->features
);
477 for (i
= 0; i
< package
->loaded_folders
; i
++)
479 HeapFree(GetProcessHeap(),0,package
->folders
[i
].Directory
);
480 HeapFree(GetProcessHeap(),0,package
->folders
[i
].TargetDefault
);
481 HeapFree(GetProcessHeap(),0,package
->folders
[i
].SourceDefault
);
482 HeapFree(GetProcessHeap(),0,package
->folders
[i
].ResolvedTarget
);
483 HeapFree(GetProcessHeap(),0,package
->folders
[i
].ResolvedSource
);
484 HeapFree(GetProcessHeap(),0,package
->folders
[i
].Property
);
486 if (package
->folders
&& package
->loaded_folders
> 0)
487 HeapFree(GetProcessHeap(),0,package
->folders
);
489 for (i
= 0; i
< package
->loaded_components
; i
++)
490 HeapFree(GetProcessHeap(),0,package
->components
[i
].FullKeypath
);
492 if (package
->components
&& package
->loaded_components
> 0)
493 HeapFree(GetProcessHeap(),0,package
->components
);
495 for (i
= 0; i
< package
->loaded_files
; i
++)
497 HeapFree(GetProcessHeap(),0,package
->files
[i
].File
);
498 HeapFree(GetProcessHeap(),0,package
->files
[i
].FileName
);
499 HeapFree(GetProcessHeap(),0,package
->files
[i
].ShortName
);
500 HeapFree(GetProcessHeap(),0,package
->files
[i
].Version
);
501 HeapFree(GetProcessHeap(),0,package
->files
[i
].Language
);
502 HeapFree(GetProcessHeap(),0,package
->files
[i
].SourcePath
);
503 HeapFree(GetProcessHeap(),0,package
->files
[i
].TargetPath
);
506 if (package
->files
&& package
->loaded_files
> 0)
507 HeapFree(GetProcessHeap(),0,package
->files
);
509 /* clean up extension, progid, class and verb structures */
510 for (i
= 0; i
< package
->loaded_classes
; i
++)
512 HeapFree(GetProcessHeap(),0,package
->classes
[i
].Description
);
513 HeapFree(GetProcessHeap(),0,package
->classes
[i
].FileTypeMask
);
514 HeapFree(GetProcessHeap(),0,package
->classes
[i
].IconPath
);
515 HeapFree(GetProcessHeap(),0,package
->classes
[i
].DefInprocHandler
);
516 HeapFree(GetProcessHeap(),0,package
->classes
[i
].DefInprocHandler32
);
517 HeapFree(GetProcessHeap(),0,package
->classes
[i
].Argument
);
518 HeapFree(GetProcessHeap(),0,package
->classes
[i
].ProgIDText
);
521 if (package
->classes
&& package
->loaded_classes
> 0)
522 HeapFree(GetProcessHeap(),0,package
->classes
);
524 for (i
= 0; i
< package
->loaded_extensions
; i
++)
526 HeapFree(GetProcessHeap(),0,package
->extensions
[i
].ProgIDText
);
529 if (package
->extensions
&& package
->loaded_extensions
> 0)
530 HeapFree(GetProcessHeap(),0,package
->extensions
);
532 for (i
= 0; i
< package
->loaded_progids
; i
++)
534 HeapFree(GetProcessHeap(),0,package
->progids
[i
].ProgID
);
535 HeapFree(GetProcessHeap(),0,package
->progids
[i
].Description
);
536 HeapFree(GetProcessHeap(),0,package
->progids
[i
].IconPath
);
539 if (package
->progids
&& package
->loaded_progids
> 0)
540 HeapFree(GetProcessHeap(),0,package
->progids
);
542 for (i
= 0; i
< package
->loaded_verbs
; i
++)
544 HeapFree(GetProcessHeap(),0,package
->verbs
[i
].Verb
);
545 HeapFree(GetProcessHeap(),0,package
->verbs
[i
].Command
);
546 HeapFree(GetProcessHeap(),0,package
->verbs
[i
].Argument
);
549 if (package
->verbs
&& package
->loaded_verbs
> 0)
550 HeapFree(GetProcessHeap(),0,package
->verbs
);
552 for (i
= 0; i
< package
->loaded_mimes
; i
++)
553 HeapFree(GetProcessHeap(),0,package
->mimes
[i
].ContentType
);
555 if (package
->mimes
&& package
->loaded_mimes
> 0)
556 HeapFree(GetProcessHeap(),0,package
->mimes
);
558 for (i
= 0; i
< package
->loaded_appids
; i
++)
560 HeapFree(GetProcessHeap(),0,package
->appids
[i
].RemoteServerName
);
561 HeapFree(GetProcessHeap(),0,package
->appids
[i
].LocalServer
);
562 HeapFree(GetProcessHeap(),0,package
->appids
[i
].ServiceParameters
);
563 HeapFree(GetProcessHeap(),0,package
->appids
[i
].DllSurrogate
);
566 if (package
->appids
&& package
->loaded_appids
> 0)
567 HeapFree(GetProcessHeap(),0,package
->appids
);
571 for (i
= 0; i
< TOTAL_SCRIPTS
; i
++)
574 for (j
= 0; j
< package
->script
->ActionCount
[i
]; j
++)
575 HeapFree(GetProcessHeap(),0,package
->script
->Actions
[i
][j
]);
577 HeapFree(GetProcessHeap(),0,package
->script
->Actions
[i
]);
579 HeapFree(GetProcessHeap(),0,package
->script
);
582 HeapFree(GetProcessHeap(),0,package
->PackagePath
);
584 /* cleanup control event subscriptions */
585 ControlEvent_CleanupSubscriptions(package
);
589 * build_directory_name()
591 * This function is to save messing round with directory names
592 * It handles adding backslashes between path segments,
593 * and can add \ at the end of the directory name if told to.
595 * It takes a variable number of arguments.
596 * It always allocates a new string for the result, so make sure
597 * to free the return value when finished with it.
599 * The first arg is the number of path segments that follow.
600 * The arguments following count are a list of path segments.
601 * A path segment may be NULL.
603 * Path segments will be added with a \ separating them.
604 * A \ will not be added after the last segment, however if the
605 * last segment is NULL, then the last character will be a \
608 LPWSTR
build_directory_name(DWORD count
, ...)
615 for(i
=0; i
<count
; i
++)
617 LPCWSTR str
= va_arg(va
,LPCWSTR
);
619 sz
+= strlenW(str
) + 1;
623 dir
= HeapAlloc(GetProcessHeap(), 0, sz
*sizeof(WCHAR
));
627 for(i
=0; i
<count
; i
++)
629 LPCWSTR str
= va_arg(va
,LPCWSTR
);
633 if( ((i
+1)!=count
) && dir
[strlenW(dir
)-1]!='\\')
639 /***********************************************************************
642 * Recursively create all directories in the path.
644 * shamelessly stolen from setupapi/queue.c
646 BOOL
create_full_pathW(const WCHAR
*path
)
652 new_path
= HeapAlloc(GetProcessHeap(), 0, (strlenW(path
) + 1) *
655 strcpyW(new_path
, path
);
657 while((len
= strlenW(new_path
)) && new_path
[len
- 1] == '\\')
658 new_path
[len
- 1] = 0;
660 while(!CreateDirectoryW(new_path
, NULL
))
663 DWORD last_error
= GetLastError();
664 if(last_error
== ERROR_ALREADY_EXISTS
)
667 if(last_error
!= ERROR_PATH_NOT_FOUND
)
673 if(!(slash
= strrchrW(new_path
, '\\')))
679 len
= slash
- new_path
;
681 if(!create_full_pathW(new_path
))
686 new_path
[len
] = '\\';
689 HeapFree(GetProcessHeap(), 0, new_path
);
693 void ui_progress(MSIPACKAGE
*package
, int a
, int b
, int c
, int d
)
697 row
= MSI_CreateRecord(4);
698 MSI_RecordSetInteger(row
,1,a
);
699 MSI_RecordSetInteger(row
,2,b
);
700 MSI_RecordSetInteger(row
,3,c
);
701 MSI_RecordSetInteger(row
,4,d
);
702 MSI_ProcessMessage(package
, INSTALLMESSAGE_PROGRESS
, row
);
703 msiobj_release(&row
->hdr
);
705 msi_dialog_check_messages(NULL
);
708 void ui_actiondata(MSIPACKAGE
*package
, LPCWSTR action
, MSIRECORD
* record
)
710 static const WCHAR Query_t
[] =
711 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
712 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
713 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',
714 ' ','\'','%','s','\'',0};
718 static const WCHAR szActionData
[] =
719 {'A','c','t','i','o','n','D','a','t','a',0};
721 if (!package
->LastAction
|| strcmpW(package
->LastAction
,action
))
723 row
= MSI_QueryGetRecord(package
->db
, Query_t
, action
);
727 if (MSI_RecordIsNull(row
,3))
729 msiobj_release(&row
->hdr
);
733 /* update the cached actionformat */
734 HeapFree(GetProcessHeap(),0,package
->ActionFormat
);
735 package
->ActionFormat
= load_dynamic_stringW(row
,3);
737 HeapFree(GetProcessHeap(),0,package
->LastAction
);
738 package
->LastAction
= strdupW(action
);
740 msiobj_release(&row
->hdr
);
743 MSI_RecordSetStringW(record
,0,package
->ActionFormat
);
745 MSI_FormatRecordW(package
,record
,message
,&size
);
747 row
= MSI_CreateRecord(1);
748 MSI_RecordSetStringW(row
,1,message
);
750 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONDATA
, row
);
752 ControlEvent_FireSubscribedEvent(package
,szActionData
, row
);
754 msiobj_release(&row
->hdr
);
757 BOOL
ACTION_VerifyComponentForAction(MSIPACKAGE
* package
, INT index
,
760 if (package
->components
[index
].Installed
== check
)
763 if (package
->components
[index
].ActionRequest
== check
)
769 BOOL
ACTION_VerifyFeatureForAction(MSIPACKAGE
* package
, INT index
,
772 if (package
->features
[index
].Installed
== check
)
775 if (package
->features
[index
].ActionRequest
== check
)
781 void reduce_to_longfilename(WCHAR
* filename
)
783 LPWSTR p
= strchrW(filename
,'|');
785 memmove(filename
, p
+1, (strlenW(p
+1)+1)*sizeof(WCHAR
));
788 void reduce_to_shortfilename(WCHAR
* filename
)
790 LPWSTR p
= strchrW(filename
,'|');
795 LPWSTR
create_component_advertise_string(MSIPACKAGE
* package
,
796 MSICOMPONENT
* component
, LPCWSTR feature
)
798 LPWSTR productid
=NULL
;
800 WCHAR productid_85
[21];
801 WCHAR component_85
[21];
803 * I have a fair bit of confusion as to when a < is used and when a > is
804 * used. I do not think i have it right...
806 * Ok it appears that the > is used if there is a guid for the compoenent
807 * and the < is used if not.
809 static WCHAR fmt1
[] = {'%','s','%','s','<',0,0};
810 static WCHAR fmt2
[] = {'%','s','%','s','>','%','s',0,0};
811 LPWSTR output
= NULL
;
814 memset(productid_85
,0,sizeof(productid_85
));
815 memset(component_85
,0,sizeof(component_85
));
817 productid
= load_dynamic_property(package
,szProductCode
,NULL
);
818 CLSIDFromString(productid
, &clsid
);
820 encode_base85_guid(&clsid
,productid_85
);
822 CLSIDFromString(component
->ComponentId
, &clsid
);
823 encode_base85_guid(&clsid
,component_85
);
825 TRACE("Doing something with this... %s %s %s\n",
826 debugstr_w(productid_85
), debugstr_w(feature
),
827 debugstr_w(component_85
));
829 sz
= lstrlenW(productid_85
) + lstrlenW(feature
);
831 sz
+= lstrlenW(component_85
);
836 output
= HeapAlloc(GetProcessHeap(),0,sz
);
840 sprintfW(output
,fmt2
,productid_85
,feature
,component_85
);
842 sprintfW(output
,fmt1
,productid_85
,feature
);
844 HeapFree(GetProcessHeap(),0,productid
);
849 /* update compoennt state based on a feature change */
850 void ACTION_UpdateComponentStates(MSIPACKAGE
*package
, LPCWSTR szFeature
)
853 INSTALLSTATE newstate
;
856 i
= get_loaded_feature(package
,szFeature
);
860 feature
= &package
->features
[i
];
861 newstate
= feature
->ActionRequest
;
863 for( i
= 0; i
< feature
->ComponentCount
; i
++)
865 MSICOMPONENT
* component
= &package
->components
[feature
->Components
[i
]];
867 TRACE("MODIFYING(%i): Component %s (Installed %i, Action %i, Request %i)\n",
868 newstate
, debugstr_w(component
->Component
), component
->Installed
,
869 component
->Action
, component
->ActionRequest
);
871 if (!component
->Enabled
)
875 if (newstate
== INSTALLSTATE_LOCAL
)
877 component
->ActionRequest
= INSTALLSTATE_LOCAL
;
878 component
->Action
= INSTALLSTATE_LOCAL
;
884 component
->ActionRequest
= newstate
;
885 component
->Action
= newstate
;
887 /*if any other feature wants is local we need to set it local*/
889 j
< package
->loaded_features
&&
890 component
->ActionRequest
!= INSTALLSTATE_LOCAL
;
893 for (k
= 0; k
< package
->features
[j
].ComponentCount
; k
++)
894 if ( package
->features
[j
].Components
[k
] ==
895 feature
->Components
[i
] )
897 if (package
->features
[j
].ActionRequest
==
900 TRACE("Saved by %s\n", debugstr_w(package
->features
[j
].Feature
));
901 component
->ActionRequest
= INSTALLSTATE_LOCAL
;
902 component
->Action
= INSTALLSTATE_LOCAL
;
909 TRACE("Result (%i): Component %s (Installed %i, Action %i, Request %i)\n",
910 newstate
, debugstr_w(component
->Component
), component
->Installed
,
911 component
->Action
, component
->ActionRequest
);