2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004 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
24 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/installexecutesequence_table.asp
26 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/standard_actions_reference.asp
36 #include "wine/debug.h"
45 #include "wine/unicode.h"
48 #define CUSTOM_ACTION_TYPE_MASK 0x3F
50 WINE_DEFAULT_DEBUG_CHANNEL(msi);
52 typedef struct tagMSIFEATURE
55 WCHAR Feature_Parent[96];
57 WCHAR Description[0x100];
65 INT Components[1024]; /* yes hardcoded limit.... I am bad */
69 typedef struct tagMSICOMPONENT
72 WCHAR ComponentId[96];
75 WCHAR Condition[0x100];
84 typedef struct tagMSIFOLDER
87 WCHAR TargetDefault[96];
88 WCHAR SourceDefault[96];
90 WCHAR ResolvedTarget[MAX_PATH];
91 WCHAR ResolvedSource[MAX_PATH];
92 WCHAR Property[MAX_PATH]; /* initialy set property */
95 /* 0 = uninitialized */
97 /* 2 = created remove if empty */
98 /* 3 = created persist if empty */
103 typedef struct tagMSIFILE
107 WCHAR FileName[MAX_PATH];
115 /* 0 = uninitialize */
116 /* 1 = not present */
117 /* 2 = present but replace */
118 /* 3 = present do not replace */
120 WCHAR SourcePath[MAX_PATH];
121 WCHAR TargetPath[MAX_PATH];
128 static UINT ACTION_ProcessExecSequence(MSIHANDLE hPackage, BOOL UIran);
129 static UINT ACTION_ProcessUISequence(MSIHANDLE hPackage);
131 UINT ACTION_PerformAction(MSIHANDLE hPackage, const WCHAR *action);
132 static UINT ACTION_CostInitialize(MSIHANDLE hPackage);
133 static UINT ACTION_CreateFolders(MSIHANDLE hPackage);
134 static UINT ACTION_CostFinalize(MSIHANDLE hPackage);
135 static UINT ACTION_FileCost(MSIHANDLE hPackage);
136 static UINT ACTION_InstallFiles(MSIHANDLE hPackage);
137 static UINT ACTION_DuplicateFiles(MSIHANDLE hPackage);
138 static UINT ACTION_WriteRegistryValues(MSIHANDLE hPackage);
139 static UINT ACTION_CustomAction(MSIHANDLE hPackage,const WCHAR *action);
140 static UINT ACTION_InstallInitialize(MSIHANDLE hPackage);
141 static UINT ACTION_InstallValidate(MSIHANDLE hPackage);
143 static UINT HANDLE_CustomType1(MSIHANDLE hPackage, const LPWSTR source,
144 const LPWSTR target, const INT type);
145 static UINT HANDLE_CustomType2(MSIHANDLE hPackage, const LPWSTR source,
146 const LPWSTR target, const INT type);
148 static DWORD deformat_string(MSIHANDLE hPackage, WCHAR* ptr,WCHAR** data);
149 static UINT resolve_folder(MSIHANDLE hPackage, LPCWSTR name, LPWSTR path,
150 BOOL source, BOOL set_prop, MSIFOLDER **folder);
152 static UINT track_tempfile(MSIHANDLE hPackage, LPCWSTR name, LPCWSTR path);
155 * consts and values used
157 static const WCHAR cszSourceDir[] = {'S','o','u','r','c','e','D','i','r',0};
158 static const WCHAR cszRootDrive[] = {'R','O','O','T','D','R','I','V','E',0};
159 static const WCHAR cszTargetDir[] = {'T','A','R','G','E','T','D','I','R',0};
160 static const WCHAR cszTempFolder[]= {'T','e','m','p','F','o','l','d','e','r',0};
161 static const WCHAR cszDatabase[]={'D','A','T','A','B','A','S','E',0};
162 static const WCHAR c_collen[] = {'C',':','\\',0};
164 static const WCHAR cszlsb[]={'[',0};
165 static const WCHAR cszrsb[]={']',0};
166 static const WCHAR cszbs[]={'\\',0};
169 /********************************************************
170 * helper functions to get around current HACKS and such
171 ********************************************************/
172 inline static void reduce_to_longfilename(WCHAR* filename)
174 if (strchrW(filename,'|'))
176 WCHAR newname[MAX_PATH];
177 strcpyW(newname,strchrW(filename,'|')+1);
178 strcpyW(filename,newname);
182 inline static char *strdupWtoA( const WCHAR *str )
187 DWORD len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL
189 if ((ret = HeapAlloc( GetProcessHeap(), 0, len )))
190 WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
195 inline static int get_loaded_component(MSIPACKAGE* package, LPCWSTR Component )
200 for (i = 0; i < package->loaded_components; i++)
202 if (strcmpW(Component,package->components[i].Component)==0)
211 inline static int get_loaded_feature(MSIPACKAGE* package, LPCWSTR Feature )
216 for (i = 0; i < package->loaded_features; i++)
218 if (strcmpW(Feature,package->features[i].Feature)==0)
227 static UINT track_tempfile(MSIHANDLE hPackage, LPCWSTR name, LPCWSTR path)
233 package = msihandle2msiinfo(hPackage, MSIHANDLETYPE_PACKAGE);
238 for (i=0; i < package->loaded_files; i++)
239 if (strcmpW(package->files[i].File,name)==0)
242 index = package->loaded_files;
243 package->loaded_files++;
244 if (package->loaded_files== 1)
245 package->files = HeapAlloc(GetProcessHeap(),0,sizeof(MSIFILE));
247 package->files = HeapReAlloc(GetProcessHeap(),0,
248 package->files , package->loaded_files * sizeof(MSIFILE));
250 memset(&package->files[index],0,sizeof(MSIFILE));
252 strcpyW(package->files[index].File,name);
253 strcpyW(package->files[index].TargetPath,path);
254 package->files[index].Temporary = TRUE;
256 TRACE("Tracking tempfile (%s)\n",debugstr_w(package->files[index].File));
261 void ACTION_remove_tracked_tempfiles(MSIPACKAGE* package)
268 for (i = 0; i < package->loaded_files; i++)
270 if (package->files[i].Temporary)
271 DeleteFileW(package->files[i].TargetPath);
276 static void progress_message(MSIHANDLE hPackage, int a, int b, int c, int d )
280 row = MsiCreateRecord(4);
281 MsiRecordSetInteger(row,1,a);
282 MsiRecordSetInteger(row,2,b);
283 MsiRecordSetInteger(row,3,c);
284 MsiRecordSetInteger(row,4,d);
285 MsiProcessMessage(hPackage, INSTALLMESSAGE_PROGRESS, row);
289 /****************************************************
290 * TOP level entry points
291 *****************************************************/
293 UINT ACTION_DoTopLevelINSTALL(MSIHANDLE hPackage, LPCWSTR szPackagePath,
294 LPCWSTR szCommandLine)
303 WCHAR check[MAX_PATH];
307 strcpyW(pth,szPackagePath);
308 p = strrchrW(pth,'\\');
316 if (MsiGetPropertyW(hPackage,cszSourceDir,check,&size)
318 MsiSetPropertyW(hPackage, cszSourceDir, pth);
324 ptr = (LPWSTR)szCommandLine;
331 TRACE("Looking at %s\n",debugstr_w(ptr));
333 ptr2 = strchrW(ptr,'=');
338 strncpyW(prop,ptr,ptr2-ptr);
343 while (*ptr && (quote || (!quote && *ptr!=' ')))
356 strncpyW(val,ptr2,len);
362 TRACE("Found commandline property (%s) = (%s)\n", debugstr_w(prop),
364 MsiSetPropertyW(hPackage,prop,val);
369 if (MsiGetPropertyA(hPackage,"UILevel",buffer,&sz) == ERROR_SUCCESS)
371 if (atoi(buffer) >= INSTALLUILEVEL_REDUCED)
373 rc = ACTION_ProcessUISequence(hPackage);
374 if (rc == ERROR_SUCCESS)
375 rc = ACTION_ProcessExecSequence(hPackage,TRUE);
378 rc = ACTION_ProcessExecSequence(hPackage,FALSE);
381 rc = ACTION_ProcessExecSequence(hPackage,FALSE);
387 static UINT ACTION_ProcessExecSequence(MSIHANDLE hPackage, BOOL UIran)
391 static const CHAR *ExecSeqQuery =
392 "select * from InstallExecuteSequence where Sequence > %i order by Sequence";
397 db = MsiGetActiveDatabase(hPackage);
402 static const CHAR *IVQuery =
403 "select Sequence from InstallExecuteSequence where Action = `InstallValidate`" ;
405 MsiDatabaseOpenViewA(db, IVQuery, &view);
406 MsiViewExecute(view, 0);
407 MsiViewFetch(view,&row);
408 seq = MsiRecordGetInteger(row,1);
411 MsiCloseHandle(view);
412 sprintf(Query,ExecSeqQuery,0);
415 sprintf(Query,ExecSeqQuery,0);
417 rc = MsiDatabaseOpenViewA(db, Query, &view);
420 if (rc == ERROR_SUCCESS)
422 rc = MsiViewExecute(view, 0);
424 if (rc != ERROR_SUCCESS)
427 MsiCloseHandle(view);
431 TRACE("Running the actions \n");
438 rc = MsiViewFetch(view,&row);
439 if (rc != ERROR_SUCCESS)
445 /* check conditions */
446 if (!MsiRecordIsNull(row,2))
449 rc = MsiRecordGetStringW(row,2,buffer,&sz);
450 if (rc != ERROR_SUCCESS)
456 /* this is a hack to skip errors in the condition code */
457 if (MsiEvaluateConditionW(hPackage, buffer) ==
467 rc = MsiRecordGetStringW(row,1,buffer,&sz);
468 if (rc != ERROR_SUCCESS)
470 ERR("Error is %x\n",rc);
475 rc = ACTION_PerformAction(hPackage,buffer);
477 if (rc != ERROR_SUCCESS)
479 ERR("Execution halted due to error (%i)\n",rc);
488 MsiCloseHandle(view);
496 static UINT ACTION_ProcessUISequence(MSIHANDLE hPackage)
500 static const CHAR *ExecSeqQuery =
501 "select * from InstallUISequence where Sequence > 0 order by Sequence";
504 db = MsiGetActiveDatabase(hPackage);
505 rc = MsiDatabaseOpenViewA(db, ExecSeqQuery, &view);
508 if (rc == ERROR_SUCCESS)
510 rc = MsiViewExecute(view, 0);
512 if (rc != ERROR_SUCCESS)
515 MsiCloseHandle(view);
519 TRACE("Running the actions \n");
527 rc = MsiViewFetch(view,&row);
528 if (rc != ERROR_SUCCESS)
534 /* check conditions */
535 if (!MsiRecordIsNull(row,2))
538 rc = MsiRecordGetStringW(row,2,buffer,&sz);
539 if (rc != ERROR_SUCCESS)
545 if (MsiEvaluateConditionW(hPackage, buffer) ==
555 rc = MsiRecordGetStringW(row,1,buffer,&sz);
556 if (rc != ERROR_SUCCESS)
558 ERR("Error is %x\n",rc);
563 rc = ACTION_PerformAction(hPackage,buffer);
565 if (rc != ERROR_SUCCESS)
567 ERR("Execution halted due to error (%i)\n",rc);
576 MsiCloseHandle(view);
583 /********************************************************
584 * ACTION helper functions and functions that perform the actions
585 *******************************************************/
588 * Alot of actions are really important even if they don't do anything
589 * explicit.. Lots of properties are set at the beginning of the installation
590 * CostFinalize does a bunch of work to translated the directories and such
592 * But until I get write access to the database that is hard, so I am going to
593 * hack it to see if I can get something to run.
595 UINT ACTION_PerformAction(MSIHANDLE hPackage, const WCHAR *action)
597 const static WCHAR szCreateFolders[] =
598 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
599 const static WCHAR szCostFinalize[] =
600 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
601 const static WCHAR szInstallFiles[] =
602 {'I','n','s','t','a','l','l','F','i','l','e','s',0};
603 const static WCHAR szDuplicateFiles[] =
604 {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
605 const static WCHAR szWriteRegistryValues[] =
606 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
607 const static WCHAR szCostInitialize[] =
608 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
609 const static WCHAR szFileCost[] =
610 {'F','i','l','e','C','o','s','t',0};
611 const static WCHAR szInstallInitialize[] =
612 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
613 const static WCHAR szInstallValidate[] =
614 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
616 TRACE("Performing action (%s)\n",debugstr_w(action));
617 progress_message(hPackage,2,25,0,0);
619 /* pre install, setup and configureation block */
620 if (strcmpW(action,szCostInitialize)==0)
621 return ACTION_CostInitialize(hPackage);
622 if (strcmpW(action,szFileCost)==0)
623 return ACTION_FileCost(hPackage);
624 if (strcmpW(action,szCostFinalize)==0)
625 return ACTION_CostFinalize(hPackage);
626 if (strcmpW(action,szInstallValidate)==0)
627 return ACTION_InstallValidate(hPackage);
630 if (strcmpW(action,szInstallInitialize)==0)
631 return ACTION_InstallInitialize(hPackage);
632 if (strcmpW(action,szCreateFolders)==0)
633 return ACTION_CreateFolders(hPackage);
634 if (strcmpW(action,szInstallFiles)==0)
635 return ACTION_InstallFiles(hPackage);
636 if (strcmpW(action,szDuplicateFiles)==0)
637 return ACTION_DuplicateFiles(hPackage);
638 if (strcmpW(action,szWriteRegistryValues)==0)
639 return ACTION_WriteRegistryValues(hPackage);
642 Current called during itunes but unimplemented
649 ResolveSource (sets SourceDir)
650 ValidateProductID (sets ProductID)
651 IsolateComponents (Empty)
654 RemoveExistingProducts
655 AllocateRegistrySpace
662 SelfUnregModules (Empty)
663 UnregisterTypeLibraries
668 UnregisterExtensionInfo
673 RemoveEnviromentStrings
677 RemoveRegistryValues (Empty)
678 SelfRegModules (Empty)
682 CreateShortcuts (would be nice to have soon)
684 RegisterExtensionInfo (Empty)
685 RegisterProgIdInfo (Lots to do)
686 RegisterMIMEInfo (Empty)
687 WriteIniValues (Empty)
688 WriteEnvironmentStrings (Empty)
691 RegisterTypeLibraries
702 if (ACTION_CustomAction(hPackage,action) != ERROR_SUCCESS)
703 FIXME("UNHANDLED MSI ACTION %s\n",debugstr_w(action));
705 return ERROR_SUCCESS;
709 static UINT ACTION_CustomAction(MSIHANDLE hPackage,const WCHAR *action)
714 WCHAR ExecSeqQuery[1024] =
715 {'s','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','C','u','s','t','o'
716 ,'m','A','c','t','i','o','n',' ','w','h','e','r','e',' ','`','A','c','t','i'
717 ,'o','n','`',' ','=',' ','`',0};
718 static const WCHAR end[]={'`',0};
723 WCHAR *deformated=NULL;
726 strcatW(ExecSeqQuery,action);
727 strcatW(ExecSeqQuery,end);
729 db = MsiGetActiveDatabase(hPackage);
730 rc = MsiDatabaseOpenViewW(db, ExecSeqQuery, &view);
733 if (rc != ERROR_SUCCESS)
736 rc = MsiViewExecute(view, 0);
737 if (rc != ERROR_SUCCESS)
740 MsiCloseHandle(view);
744 rc = MsiViewFetch(view,&row);
745 if (rc != ERROR_SUCCESS)
748 MsiCloseHandle(view);
752 type = MsiRecordGetInteger(row,2);
755 MsiRecordGetStringW(row,3,source,&sz);
757 MsiRecordGetStringW(row,4,target,&sz);
759 TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type,
760 debugstr_w(source), debugstr_w(target));
762 /* we are ignoring ALOT of flags and important synchronization stuff */
763 switch (type & CUSTOM_ACTION_TYPE_MASK)
765 case 1: /* DLL file stored in a Binary table stream */
766 rc = HANDLE_CustomType1(hPackage,source,target,type);
768 case 2: /* Exe file stored in a Binary table strem */
769 rc = HANDLE_CustomType2(hPackage,source,target,type);
771 case 35: /* Directory set with formatted text. */
772 case 51: /* Property set with formatted text. */
773 deformat_string(hPackage,target,&deformated);
774 MsiSetPropertyW(hPackage,source,deformated);
775 HeapFree(GetProcessHeap(),0,deformated);
778 FIXME("UNHANDLED ACTION TYPE %i (%s %s)\n",
779 type & CUSTOM_ACTION_TYPE_MASK, debugstr_w(source),
785 MsiCloseHandle(view);
789 static UINT store_binary_to_temp(MSIHANDLE hPackage, const LPWSTR source,
794 if (MsiGetPropertyW(hPackage, cszTempFolder, tmp_file, &sz)
796 GetTempPathW(MAX_PATH,tmp_file);
798 strcatW(tmp_file,source);
800 if (GetFileAttributesW(tmp_file) != INVALID_FILE_ATTRIBUTES)
802 TRACE("File already exists\n");
803 return ERROR_SUCCESS;
807 /* write out the file */
812 {'s','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','B','i'
813 ,'n','a','r','y',' ','w','h','e','r','e',' ','N','a','m','e','=','`',0};
814 static const WCHAR end[]={'`',0};
819 if (track_tempfile(hPackage, source, tmp_file)!=0)
820 FIXME("File Name in temp tracking collision\n");
822 the_file = CreateFileW(tmp_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
823 FILE_ATTRIBUTE_NORMAL, NULL);
825 if (the_file == INVALID_HANDLE_VALUE)
826 return ERROR_FUNCTION_FAILED;
828 strcatW(Query,source);
831 db = MsiGetActiveDatabase(hPackage);
832 rc = MsiDatabaseOpenViewW(db, Query, &view);
835 if (rc != ERROR_SUCCESS)
838 rc = MsiViewExecute(view, 0);
839 if (rc != ERROR_SUCCESS)
842 MsiCloseHandle(view);
846 rc = MsiViewFetch(view,&row);
847 if (rc != ERROR_SUCCESS)
850 MsiCloseHandle(view);
858 rc = MsiRecordReadStream(row,2,buffer,&sz);
859 if (rc != ERROR_SUCCESS)
861 ERR("Failed to get stream\n");
862 CloseHandle(the_file);
863 DeleteFileW(tmp_file);
866 WriteFile(the_file,buffer,sz,&write,NULL);
867 } while (sz == 1024);
869 CloseHandle(the_file);
873 MsiCloseHandle(view);
876 return ERROR_SUCCESS;
880 typedef UINT CustomEntry(MSIHANDLE);
882 static UINT HANDLE_CustomType1(MSIHANDLE hPackage, const LPWSTR source,
883 const LPWSTR target, const INT type)
885 WCHAR tmp_file[MAX_PATH];
890 store_binary_to_temp(hPackage, source, tmp_file);
892 TRACE("Calling function %s from %s\n",debugstr_w(target),
893 debugstr_w(tmp_file));
897 FIXME("Asynchronous execution.. UNHANDLED\n");
898 return ERROR_SUCCESS;
901 if (!strchrW(tmp_file,'.'))
903 static const WCHAR dot[]={'.',0};
904 strcatW(tmp_file,dot);
907 DLL = LoadLibraryW(tmp_file);
910 proc = strdupWtoA( target );
911 fn = (CustomEntry*)GetProcAddress(DLL,proc);
914 TRACE("Calling function\n");
918 ERR("Cannot load functon\n");
920 HeapFree(GetProcessHeap(),0,proc);
924 ERR("Unable to load library\n");
926 return ERROR_SUCCESS;
929 static UINT HANDLE_CustomType2(MSIHANDLE hPackage, const LPWSTR source,
930 const LPWSTR target, const INT type)
932 WCHAR tmp_file[MAX_PATH*2];
934 PROCESS_INFORMATION info;
937 static const WCHAR spc[] = {' ',0};
939 memset(&si,0,sizeof(STARTUPINFOW));
940 memset(&info,0,sizeof(PROCESS_INFORMATION));
942 store_binary_to_temp(hPackage, source, tmp_file);
944 strcatW(tmp_file,spc);
945 deformat_string(hPackage,target,&deformated);
946 strcatW(tmp_file,deformated);
948 HeapFree(GetProcessHeap(),0,deformated);
950 TRACE("executing exe %s \n",debugstr_w(tmp_file));
952 rc = CreateProcessW(NULL, tmp_file, NULL, NULL, FALSE, 0, NULL,
953 c_collen, &si, &info);
957 ERR("Unable to execute command\n");
958 return ERROR_SUCCESS;
962 WaitForSingleObject(info.hProcess,INFINITE);
964 return ERROR_SUCCESS;
967 /***********************************************************************
970 * Recursively create all directories in the path.
972 * shamelessly stolen from setupapi/queue.c
974 static BOOL create_full_pathW(const WCHAR *path)
980 new_path = HeapAlloc(GetProcessHeap(), 0, (strlenW(path) + 1) *
982 strcpyW(new_path, path);
984 while((len = strlenW(new_path)) && new_path[len - 1] == '\\')
985 new_path[len - 1] = 0;
987 while(!CreateDirectoryW(new_path, NULL))
990 DWORD last_error = GetLastError();
991 if(last_error == ERROR_ALREADY_EXISTS)
994 if(last_error != ERROR_PATH_NOT_FOUND)
1000 if(!(slash = strrchrW(new_path, '\\')))
1006 len = slash - new_path;
1008 if(!create_full_pathW(new_path))
1013 new_path[len] = '\\';
1016 HeapFree(GetProcessHeap(), 0, new_path);
1021 * Also we cannot enable/disable components either, so for now I am just going
1022 * to do all the directories for all the components.
1024 static UINT ACTION_CreateFolders(MSIHANDLE hPackage)
1026 static const CHAR *ExecSeqQuery = "select Directory_ from CreateFolder";
1032 db = MsiGetActiveDatabase(hPackage);
1033 rc = MsiDatabaseOpenViewA(db, ExecSeqQuery, &view);
1036 if (rc != ERROR_SUCCESS)
1039 rc = MsiViewExecute(view, 0);
1040 if (rc != ERROR_SUCCESS)
1043 MsiCloseHandle(view);
1050 WCHAR full_path[MAX_PATH];
1054 rc = MsiViewFetch(view,&row);
1055 if (rc != ERROR_SUCCESS)
1062 rc = MsiRecordGetStringW(row,1,dir,&sz);
1064 if (rc!= ERROR_SUCCESS)
1066 ERR("Unable to get folder id \n");
1067 MsiCloseHandle(row);
1072 rc = resolve_folder(hPackage,dir,full_path,FALSE,FALSE,&folder);
1074 if (rc != ERROR_SUCCESS)
1076 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1077 MsiCloseHandle(row);
1081 TRACE("Folder is %s\n",debugstr_w(full_path));
1083 if (folder->State == 0)
1084 create_full_pathW(full_path);
1088 MsiCloseHandle(row);
1091 MsiCloseHandle(view);
1096 static int load_component(MSIPACKAGE* package, MSIHANDLE row)
1098 int index = package->loaded_components;
1101 /* fill in the data */
1103 package->loaded_components++;
1104 if (package->loaded_components == 1)
1105 package->components = HeapAlloc(GetProcessHeap(),0,
1106 sizeof(MSICOMPONENT));
1108 package->components = HeapReAlloc(GetProcessHeap(),0,
1109 package->components, package->loaded_components *
1110 sizeof(MSICOMPONENT));
1112 memset(&package->components[index],0,sizeof(MSICOMPONENT));
1115 MsiRecordGetStringW(row,1,package->components[index].Component,&sz);
1117 TRACE("Loading Component %s\n",
1118 debugstr_w(package->components[index].Component));
1121 if (!MsiRecordIsNull(row,2))
1122 MsiRecordGetStringW(row,2,package->components[index].ComponentId,&sz);
1125 MsiRecordGetStringW(row,3,package->components[index].Directory,&sz);
1127 package->components[index].Attributes = MsiRecordGetInteger(row,4);
1130 MsiRecordGetStringW(row,5,package->components[index].Condition,&sz);
1133 MsiRecordGetStringW(row,6,package->components[index].KeyPath,&sz);
1135 package->components[index].State = INSTALLSTATE_UNKNOWN;
1136 package->components[index].Enabled = TRUE;
1137 package->components[index].FeatureState= FALSE;
1142 static void load_feature(MSIPACKAGE* package, MSIHANDLE row)
1144 int index = package->loaded_features;
1146 static const WCHAR Query1[] = {'S','E','L','E','C','T',' ','C','o','m','p',
1147 'o','n','e','n','t','_',' ','F','R','O','M',' ','F','e','a','t','u','r','e',
1148 'C','o','m','p','o','n','e','n','t','s',' ','W','H','E','R','E',' ','F','e',
1149 'a','t','u','r','e','_','=','\'','%','s','\'',0};
1150 static const WCHAR Query2[] = {'S','E','L','E','C','T',' ','*',' ','F','R',
1151 'O','M',' ','C','o','m','p','o','n','e','n','t',' ','W','H','E','R','E',' ','C',
1152 'o','m','p','o','n','e','n','t','=','\'','%','s','\'',0};
1159 /* fill in the data */
1161 package->loaded_features ++;
1162 if (package->loaded_features == 1)
1163 package->features = HeapAlloc(GetProcessHeap(),0,sizeof(MSIFEATURE));
1165 package->features = HeapReAlloc(GetProcessHeap(),0,package->features,
1166 package->loaded_features * sizeof(MSIFEATURE));
1168 memset(&package->features[index],0,sizeof(MSIFEATURE));
1171 MsiRecordGetStringW(row,1,package->features[index].Feature,&sz);
1173 TRACE("Loading feature %s\n",debugstr_w(package->features[index].Feature));
1176 if (!MsiRecordIsNull(row,2))
1177 MsiRecordGetStringW(row,2,package->features[index].Feature_Parent,&sz);
1180 if (!MsiRecordIsNull(row,3))
1181 MsiRecordGetStringW(row,3,package->features[index].Title,&sz);
1184 if (!MsiRecordIsNull(row,4))
1185 MsiRecordGetStringW(row,4,package->features[index].Description,&sz);
1187 if (!MsiRecordIsNull(row,5))
1188 package->features[index].Display = MsiRecordGetInteger(row,5);
1190 package->features[index].Level= MsiRecordGetInteger(row,6);
1193 if (!MsiRecordIsNull(row,7))
1194 MsiRecordGetStringW(row,7,package->features[index].Directory,&sz);
1196 package->features[index].Attributes= MsiRecordGetInteger(row,8);
1197 package->features[index].State = INSTALLSTATE_UNKNOWN;
1199 /* load feature components */
1201 sprintfW(Query,Query1,package->features[index].Feature);
1202 MsiDatabaseOpenViewW(package->db,Query,&view);
1203 MsiViewExecute(view,0);
1207 WCHAR buffer[0x100];
1210 INT cnt = package->features[index].ComponentCount;
1212 rc = MsiViewFetch(view,&row2);
1213 if (rc != ERROR_SUCCESS)
1217 MsiRecordGetStringW(row2,1,buffer,&sz);
1219 /* check to see if the component is already loaded */
1220 c_indx = get_loaded_component(package,buffer);
1223 TRACE("Component %s already loaded at %i\n", debugstr_w(buffer),
1225 package->features[index].Components[cnt] = c_indx;
1226 package->features[index].ComponentCount ++;
1229 sprintfW(Query,Query2,buffer);
1231 MsiDatabaseOpenViewW(package->db,Query,&view2);
1232 MsiViewExecute(view2,0);
1237 rc = MsiViewFetch(view2,&row3);
1238 if (rc != ERROR_SUCCESS)
1240 c_indx = load_component(package,row3);
1241 MsiCloseHandle(row3);
1243 package->features[index].Components[cnt] = c_indx;
1244 package->features[index].ComponentCount ++;
1246 MsiViewClose(view2);
1247 MsiCloseHandle(view2);
1248 MsiCloseHandle(row2);
1251 MsiCloseHandle(view);
1255 * I am not doing any of the costing functionality yet.
1256 * Mostly looking at doing the Component and Feature loading
1258 * The native MSI does ALOT of modification to tables here. Mostly adding alot
1259 * of temporary columns to the Feature and Component tables.
1261 * note: native msi also tracks the short filename. but i am only going to
1262 * track the long ones. Also looking at this directory table
1263 * it appears that the directory table does not get the parents
1264 * resolved base on property only based on their entrys in the
1267 static UINT ACTION_CostInitialize(MSIHANDLE hPackage)
1272 MSIPACKAGE *package;
1274 static const CHAR Query_all[] = "SELECT * FROM Feature";
1276 MsiSetPropertyA(hPackage,"CostingComplete","0");
1277 MsiSetPropertyW(hPackage, cszRootDrive , c_collen);
1279 package = msihandle2msiinfo(hPackage, MSIHANDLETYPE_PACKAGE);
1282 MsiDatabaseOpenViewA(package->db,Query_all,&view);
1283 MsiViewExecute(view,0);
1288 rc = MsiViewFetch(view,&row);
1289 if (rc != ERROR_SUCCESS)
1292 load_feature(package,row);
1293 MsiCloseHandle(row);
1296 MsiCloseHandle(view);
1298 return ERROR_SUCCESS;
1301 static int load_file(MSIPACKAGE* package, MSIHANDLE row)
1303 int index = package->loaded_files;
1305 WCHAR buffer[0x100];
1308 /* fill in the data */
1310 package->loaded_files++;
1311 if (package->loaded_files== 1)
1312 package->files = HeapAlloc(GetProcessHeap(),0,sizeof(MSIFILE));
1314 package->files = HeapReAlloc(GetProcessHeap(),0,
1315 package->files , package->loaded_files * sizeof(MSIFILE));
1317 memset(&package->files[index],0,sizeof(MSIFILE));
1320 MsiRecordGetStringW(row,1,package->files[index].File,&sz);
1323 MsiRecordGetStringW(row,2,buffer,&sz);
1325 package->files[index].ComponentIndex = -1;
1326 for (i = 0; i < package->loaded_components; i++)
1327 if (strcmpW(package->components[i].Component,buffer)==0)
1329 package->files[index].ComponentIndex = i;
1332 if (package->files[index].ComponentIndex == -1)
1333 ERR("Unfound Component %s\n",debugstr_w(buffer));
1336 MsiRecordGetStringW(row,3,package->files[index].FileName,&sz);
1338 reduce_to_longfilename(package->files[index].FileName);
1340 package->files[index].FileSize = MsiRecordGetInteger(row,4);
1343 if (!MsiRecordIsNull(row,5))
1344 MsiRecordGetStringW(row,5,package->files[index].Version,&sz);
1347 if (!MsiRecordIsNull(row,6))
1348 MsiRecordGetStringW(row,6,package->files[index].Language,&sz);
1350 if (!MsiRecordIsNull(row,7))
1351 package->files[index].Attributes= MsiRecordGetInteger(row,7);
1353 package->files[index].Sequence= MsiRecordGetInteger(row,8);
1355 package->files[index].Temporary = FALSE;
1356 package->files[index].State = 0;
1358 TRACE("File Loaded (%s)\n",debugstr_w(package->files[index].File));
1360 return ERROR_SUCCESS;
1363 static UINT ACTION_FileCost(MSIHANDLE hPackage)
1367 MSIPACKAGE *package;
1369 static const CHAR Query[] = "SELECT * FROM File Order by Sequence";
1371 package = msihandle2msiinfo(hPackage, MSIHANDLETYPE_PACKAGE);
1373 return ERROR_INVALID_HANDLE;
1375 rc = MsiDatabaseOpenViewA(package->db, Query, &view);
1376 if (rc != ERROR_SUCCESS)
1379 rc = MsiViewExecute(view, 0);
1380 if (rc != ERROR_SUCCESS)
1383 MsiCloseHandle(view);
1389 rc = MsiViewFetch(view,&row);
1390 if (rc != ERROR_SUCCESS)
1395 load_file(package,row);
1396 MsiCloseHandle(row);
1399 MsiCloseHandle(view);
1401 return ERROR_SUCCESS;
1404 static INT load_folder(MSIHANDLE hPackage, const WCHAR* dir)
1408 {'s','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','D','i','r','e','c',
1409 't','o','r','y',' ','w','h','e','r','e',' ','`','D','i','r','e','c','t',
1410 'o','r','y','`',' ','=',' ','`',0};
1411 static const WCHAR end[]={'`',0};
1414 WCHAR targetbuffer[0x100];
1415 WCHAR *srcdir = NULL;
1416 WCHAR *targetdir = NULL;
1417 WCHAR parent[0x100];
1420 MSIPACKAGE *package;
1423 package = msihandle2msiinfo(hPackage, MSIHANDLETYPE_PACKAGE);
1425 TRACE("Looking for dir %s\n",debugstr_w(dir));
1427 for (i = 0; i < package->loaded_folders; i++)
1429 if (strcmpW(package->folders[i].Directory,dir)==0)
1431 TRACE(" %s retuning on index %i\n",debugstr_w(dir),i);
1436 TRACE("Working to load %s\n",debugstr_w(dir));
1438 index = package->loaded_folders;
1440 package->loaded_folders++;
1441 if (package->loaded_folders== 1)
1442 package->folders = HeapAlloc(GetProcessHeap(),0,
1445 package->folders= HeapReAlloc(GetProcessHeap(),0,
1446 package->folders, package->loaded_folders*
1449 memset(&package->folders[index],0,sizeof(MSIFOLDER));
1451 strcpyW(package->folders[index].Directory,dir);
1456 rc = MsiDatabaseOpenViewW(package->db, Query, &view);
1458 if (rc != ERROR_SUCCESS)
1461 rc = MsiViewExecute(view, 0);
1462 if (rc != ERROR_SUCCESS)
1465 MsiCloseHandle(view);
1469 rc = MsiViewFetch(view,&row);
1470 if (rc != ERROR_SUCCESS)
1473 MsiCloseHandle(view);
1478 MsiRecordGetStringW(row,3,targetbuffer,&sz);
1479 targetdir=targetbuffer;
1481 /* split src and target dir */
1482 if (strchrW(targetdir,':'))
1484 srcdir=strchrW(targetdir,':');
1491 /* for now only pick long filename versions */
1492 if (strchrW(targetdir,'|'))
1494 targetdir = strchrW(targetdir,'|');
1498 if (srcdir && strchrW(srcdir,'|'))
1500 srcdir= strchrW(srcdir,'|');
1505 /* now check for root dirs */
1506 if (targetdir[0] == '.' && targetdir[1] == 0)
1509 if (srcdir && srcdir[0] == '.' && srcdir[1] == 0)
1513 strcpyW(package->folders[index].TargetDefault,targetdir);
1516 strcpyW(package->folders[index].SourceDefault,srcdir);
1518 strcpyW(package->folders[index].SourceDefault,targetdir);
1520 if (MsiRecordIsNull(row,2))
1525 MsiRecordGetStringW(row,2,parent,&sz);
1530 i = load_folder(hPackage,parent);
1531 package->folders[index].ParentIndex = i;
1532 TRACE("Parent is index %i... %s %s\n",
1533 package->folders[index].ParentIndex,
1534 debugstr_w(package->folders[package->folders[index].ParentIndex].Directory),
1535 debugstr_w(parent));
1538 package->folders[index].ParentIndex = -2;
1541 rc = MsiGetPropertyW(hPackage, dir, package->folders[index].Property, &sz);
1542 if (rc != ERROR_SUCCESS)
1543 package->folders[index].Property[0]=0;
1545 MsiCloseHandle(row);
1547 MsiCloseHandle(view);
1548 TRACE(" %s retuning on index %i\n",debugstr_w(dir),index);
1552 static UINT resolve_folder(MSIHANDLE hPackage, LPCWSTR name, LPWSTR path,
1553 BOOL source, BOOL set_prop, MSIFOLDER **folder)
1555 MSIPACKAGE *package;
1557 UINT rc = ERROR_SUCCESS;
1560 package = msihandle2msiinfo(hPackage, MSIHANDLETYPE_PACKAGE);
1562 TRACE("Working to resolve %s\n",debugstr_w(name));
1567 /* special resolving for Target and Source root dir */
1568 if (strcmpW(name,cszTargetDir)==0 || strcmpW(name,cszSourceDir)==0)
1573 rc = MsiGetPropertyW(hPackage,cszTargetDir,path,&sz);
1574 if (rc != ERROR_SUCCESS)
1577 rc = MsiGetPropertyW(hPackage,cszRootDrive,path,&sz);
1579 MsiSetPropertyW(hPackage,cszTargetDir,path);
1582 *folder = &(package->folders[0]);
1588 rc = MsiGetPropertyW(hPackage,cszSourceDir,path,&sz);
1589 if (rc != ERROR_SUCCESS)
1592 rc = MsiGetPropertyW(hPackage,cszDatabase,path,&sz);
1593 if (rc == ERROR_SUCCESS)
1595 LPWSTR ptr = strrchrW(path,'\\');
1604 *folder = &(package->folders[0]);
1609 for (i = 0; i < package->loaded_folders; i++)
1611 if (strcmpW(package->folders[i].Directory,name)==0)
1615 if (i >= package->loaded_folders)
1616 return ERROR_FUNCTION_FAILED;
1619 *folder = &(package->folders[i]);
1621 if (!source && package->folders[i].ResolvedTarget[0])
1623 strcpyW(path,package->folders[i].ResolvedTarget);
1624 TRACE(" already resolved to %s\n",debugstr_w(path));
1625 return ERROR_SUCCESS;
1627 else if (source && package->folders[i].ResolvedSource[0])
1629 strcpyW(path,package->folders[i].ResolvedSource);
1630 return ERROR_SUCCESS;
1632 else if (!source && package->folders[i].Property[0])
1634 strcpyW(path,package->folders[i].Property);
1635 TRACE(" internally set to %s\n",debugstr_w(path));
1637 MsiSetPropertyW(hPackage,name,path);
1638 return ERROR_SUCCESS;
1641 if (package->folders[i].ParentIndex >= 0)
1643 TRACE(" ! Parent is %s\n", debugstr_w(package->folders[
1644 package->folders[i].ParentIndex].Directory));
1645 resolve_folder(hPackage, package->folders[
1646 package->folders[i].ParentIndex].Directory, path,source,
1651 if (package->folders[i].TargetDefault[0])
1653 strcatW(path,package->folders[i].TargetDefault);
1654 strcatW(path,cszbs);
1656 strcpyW(package->folders[i].ResolvedTarget,path);
1657 TRACE(" resolved into %s\n",debugstr_w(path));
1659 MsiSetPropertyW(hPackage,name,path);
1663 if (package->folders[i].SourceDefault[0])
1665 strcatW(path,package->folders[i].SourceDefault);
1666 strcatW(path,cszbs);
1668 strcpyW(package->folders[i].ResolvedSource,path);
1675 * Alot is done in this function aside from just the costing.
1676 * The costing needs to be implemented at some point but for now I am going
1677 * to focus on the directory building
1680 static UINT ACTION_CostFinalize(MSIHANDLE hPackage)
1682 static const CHAR *ExecSeqQuery = "select * from Directory";
1683 static const CHAR *ConditionQuery = "select * from Condition";
1686 MSIPACKAGE *package;
1689 TRACE("Building Directory properties\n");
1691 package = msihandle2msiinfo(hPackage, MSIHANDLETYPE_PACKAGE);
1693 rc = MsiDatabaseOpenViewA(package->db, ExecSeqQuery, &view);
1695 if (rc != ERROR_SUCCESS)
1698 rc = MsiViewExecute(view, 0);
1699 if (rc != ERROR_SUCCESS)
1702 MsiCloseHandle(view);
1709 WCHAR path[MAX_PATH];
1713 rc = MsiViewFetch(view,&row);
1715 if (rc != ERROR_SUCCESS)
1722 MsiRecordGetStringW(row,1,name,&sz);
1724 /* This helper function now does ALL the work */
1725 TRACE("Dir %s ...\n",debugstr_w(name));
1726 load_folder(hPackage,name);
1727 resolve_folder(hPackage,name,path,FALSE,TRUE,NULL);
1728 TRACE("resolves to %s\n",debugstr_w(path));
1730 MsiCloseHandle(row);
1733 MsiCloseHandle(view);
1735 TRACE("File calculations %i files\n",package->loaded_files);
1737 for (i = 0; i < package->loaded_files; i++)
1739 MSICOMPONENT* comp = NULL;
1740 MSIFILE* file= NULL;
1742 file = &package->files[i];
1743 if (file->ComponentIndex >= 0)
1744 comp = &package->components[file->ComponentIndex];
1748 /* calculate target */
1749 resolve_folder(hPackage, comp->Directory, file->TargetPath, FALSE,
1751 strcatW(file->TargetPath,file->FileName);
1753 TRACE("file %s resolves to %s\n",
1754 debugstr_w(file->File),debugstr_w(file->TargetPath));
1756 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
1759 comp->Cost += file->FileSize;
1763 if (file->Version[0])
1769 WCHAR filever[0x100];
1770 static const WCHAR name[] =
1771 {'\\','V','a','r','F','i','l','e','I','n','f','o',
1772 '\\','F','i','l','e','V','e','r','s','i','o','n',0};
1774 FIXME("Version comparison.. Untried Untested and most "
1775 "likely very very wrong\n");
1776 versize = GetFileVersionInfoSizeW(file->TargetPath,&handle);
1777 version = HeapAlloc(GetProcessHeap(),0,versize);
1778 GetFileVersionInfoW(file->TargetPath, 0, versize, version);
1780 VerQueryValueW(version,name,(LPVOID)filever,&sz);
1781 HeapFree(GetProcessHeap(),0,version);
1783 if (strcmpW(version,file->Version)<0)
1786 FIXME("cost should be diff in size\n");
1787 comp->Cost += file->FileSize;
1798 TRACE("Evaluating Condition Table\n");
1800 rc = MsiDatabaseOpenViewA(package->db, ConditionQuery, &view);
1802 if (rc != ERROR_SUCCESS)
1805 rc = MsiViewExecute(view, 0);
1806 if (rc != ERROR_SUCCESS)
1809 MsiCloseHandle(view);
1815 WCHAR Feature[0x100];
1816 WCHAR Condition[0x100];
1821 rc = MsiViewFetch(view,&row);
1823 if (rc != ERROR_SUCCESS)
1830 MsiRecordGetStringW(row,1,Feature,&sz);
1832 MsiRecordGetStringW(row,3,Condition,&sz);
1834 feature_index = get_loaded_feature(package,Feature);
1835 if (feature_index < 0)
1836 ERR("FAILED to find loaded feature %s\n",debugstr_w(Feature));
1839 if (MsiEvaluateConditionW(hPackage,Condition) == MSICONDITION_TRUE)
1841 int level = MsiRecordGetInteger(row,2);
1842 TRACE("Reseting feature %s to level %i\n",debugstr_w(Feature),
1844 package->features[feature_index].Level = level;
1848 MsiCloseHandle(row);
1851 MsiCloseHandle(view);
1853 TRACE("Enabling or Disabling Components\n");
1854 for (i = 0; i < package->loaded_components; i++)
1856 if (package->components[i].Condition[0])
1858 if (MsiEvaluateConditionW(hPackage,
1859 package->components[i].Condition) == MSICONDITION_FALSE)
1861 TRACE("Disabling component %s\n",
1862 debugstr_w(package->components[i].Component));
1863 package->components[i].Enabled = FALSE;
1868 MsiSetPropertyA(hPackage,"CostingComplete","1");
1869 return ERROR_SUCCESS;
1873 * This is a helper function for handling embedded cabinet media
1875 static UINT writeout_cabinet_stream(MSIHANDLE hPackage, WCHAR* stream_name,
1884 WCHAR tmp[MAX_PATH];
1886 db = MsiGetActiveDatabase(hPackage);
1887 rc = read_raw_stream_data(db,stream_name,&data,&size);
1890 if (rc != ERROR_SUCCESS)
1894 if (MsiGetPropertyW(hPackage, cszTempFolder, tmp, &write))
1895 GetTempPathW(MAX_PATH,tmp);
1897 GetTempFileNameW(tmp,stream_name,0,source);
1899 track_tempfile(hPackage,strrchrW(source,'\\'), source);
1900 the_file = CreateFileW(source, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
1901 FILE_ATTRIBUTE_NORMAL, NULL);
1903 if (the_file == INVALID_HANDLE_VALUE)
1905 rc = ERROR_FUNCTION_FAILED;
1909 WriteFile(the_file,data,size,&write,NULL);
1910 CloseHandle(the_file);
1911 TRACE("wrote %li bytes to %s\n",write,debugstr_w(source));
1913 HeapFree(GetProcessHeap(),0,data);
1918 /***********************************************************************
1919 * extract_cabinet_file
1921 * Extract files from a cab file.
1923 static void (WINAPI *pExtractFiles)( LPSTR, LPSTR, DWORD, DWORD, DWORD, DWORD );
1925 static BOOL extract_cabinet_file_advpack( const WCHAR *cabinet,
1928 static HMODULE advpack;
1930 char *cab_path, *cab_file;
1934 if (!advpack && !(advpack = LoadLibraryA( "advpack.dll" )))
1936 ERR( "could not load advpack.dll\n" );
1939 if (!(pExtractFiles = (void *)GetProcAddress( advpack, "ExtractFiles"
1942 ERR( "could not find ExtractFiles in advpack.dll\n" );
1947 if (!(cab_file = strdupWtoA( cabinet ))) return FALSE;
1948 if (!(cab_path = strdupWtoA( root ))) return FALSE;
1950 FIXME( "awful hack: extracting cabinet %s\n", debugstr_a(cab_file) );
1951 pExtractFiles( cab_file, cab_path, 0, 0, 0, 0 );
1952 HeapFree( GetProcessHeap(), 0, cab_file );
1953 HeapFree( GetProcessHeap(), 0, cab_path );
1957 static BOOL extract_cabinet_file_cabinet( const WCHAR *cabinet,
1961 /* from cabinet.h */
1963 struct ExtractFileList {
1965 struct ExtractFileList *next;
1966 BOOL unknown; /* always 1L */
1970 long result1; /* 0x000 */
1971 long unknown1[3]; /* 0x004 */
1972 struct ExtractFileList* filelist; /* 0x010 */
1973 long filecount; /* 0x014 */
1974 long unknown2; /* 0x018 */
1975 char directory[0x104]; /* 0x01c */
1976 char lastfile[0x20c]; /* 0x120 */
1979 HRESULT WINAPI Extract(EXTRACTdest *dest, LPCSTR what);
1981 char *cab_path, *src_path;
1983 struct ExtractFileList fl;
1985 if (!(cab_path = strdupWtoA( cabinet ))) return FALSE;
1986 if (!(src_path = strdupWtoA( root ))) return FALSE;
1988 memset(&exd,0,sizeof(exd));
1989 strcpy(exd.directory,src_path);
1991 fl.filename = cab_path;
1995 FIXME( "more aweful hack: extracting cabinet %s\n", debugstr_a(cab_path) );
1996 Extract(&exd,cab_path);
1998 HeapFree( GetProcessHeap(), 0, cab_path );
1999 HeapFree( GetProcessHeap(), 0, src_path );
2003 static BOOL extract_cabinet_file(const WCHAR* source, const WCHAR* path)
2005 TRACE("Extracting %s to %s\n",debugstr_w(source), debugstr_w(path));
2006 if (!extract_cabinet_file_advpack(source,path))
2007 return extract_cabinet_file_cabinet(source,path);
2011 static UINT ready_media_for_file(MSIHANDLE hPackage, UINT sequence,
2017 WCHAR source[MAX_PATH];
2018 static const CHAR *ExecSeqQuery =
2019 "select * from Media where LastSequence >= %i order by LastSequence";
2024 static INT last_sequence = 0;
2027 if (sequence <= last_sequence)
2029 TRACE("Media already ready (%i, %i)\n",sequence,last_sequence);
2030 return ERROR_SUCCESS;
2033 sprintf(Query,ExecSeqQuery,sequence);
2035 db = MsiGetActiveDatabase(hPackage);
2036 rc = MsiDatabaseOpenViewA(db, Query, &view);
2039 if (rc != ERROR_SUCCESS)
2042 rc = MsiViewExecute(view, 0);
2043 if (rc != ERROR_SUCCESS)
2046 MsiCloseHandle(view);
2050 rc = MsiViewFetch(view,&row);
2051 if (rc != ERROR_SUCCESS)
2054 MsiCloseHandle(view);
2057 seq = MsiRecordGetInteger(row,2);
2058 last_sequence = seq;
2060 if (!MsiRecordIsNull(row,4))
2063 MsiRecordGetStringW(row,4,cab,&sz);
2064 TRACE("Source is CAB %s\n",debugstr_w(cab));
2065 /* the stream does not contain the # character */
2068 writeout_cabinet_stream(hPackage,&cab[1],source);
2069 strcpyW(path,source);
2070 *(strrchrW(path,'\\')+1)=0;
2075 if (MsiGetPropertyW(hPackage, cszSourceDir, source, &sz))
2077 ERR("No Source dir defined \n");
2078 rc = ERROR_FUNCTION_FAILED;
2082 strcpyW(path,source);
2083 strcatW(source,cab);
2084 /* extract the cab file into a folder in the temp folder */
2086 if (MsiGetPropertyW(hPackage, cszTempFolder,path, &sz)
2088 GetTempPathW(MAX_PATH,path);
2091 rc = !extract_cabinet_file(source,path);
2093 MsiCloseHandle(row);
2095 MsiCloseHandle(view);
2099 inline static UINT create_component_directory (MSIHANDLE hPackage, MSIPACKAGE*
2100 package, INT component)
2104 WCHAR install_path[MAX_PATH];
2106 rc = resolve_folder(hPackage, package->components[component].Directory,
2107 install_path, FALSE, FALSE, &folder);
2109 if (rc != ERROR_SUCCESS)
2112 /* create the path */
2113 if (folder->State == 0)
2115 create_full_pathW(install_path);
2122 static UINT ACTION_InstallFiles(MSIHANDLE hPackage)
2124 UINT rc = ERROR_SUCCESS;
2126 MSIPACKAGE *package;
2128 package = msihandle2msiinfo(hPackage, MSIHANDLETYPE_PACKAGE);
2131 return ERROR_INVALID_HANDLE;
2133 for (index = 0; index < package->loaded_files; index++)
2135 WCHAR path_to_source[MAX_PATH];
2138 file = &package->files[index];
2140 if (file->Temporary)
2143 if (!package->components[file->ComponentIndex].Enabled ||
2144 !package->components[file->ComponentIndex].FeatureState)
2146 TRACE("File %s is not scheduled for install\n",
2147 debugstr_w(file->File));
2151 if ((file->State == 1) || (file->State == 2))
2153 TRACE("Installing %s\n",debugstr_w(file->File));
2154 rc = ready_media_for_file(hPackage,file->Sequence,path_to_source);
2157 * our file table could change here because a new temp file
2158 * may have been created
2160 file = &package->files[index];
2161 if (rc != ERROR_SUCCESS)
2163 ERR("Unable to ready media\n");
2164 rc = ERROR_FUNCTION_FAILED;
2168 create_component_directory(hPackage, package, file->ComponentIndex);
2170 strcpyW(file->SourcePath, path_to_source);
2171 strcatW(file->SourcePath, file->File);
2173 TRACE("file paths %s to %s\n",debugstr_w(file->SourcePath),
2174 debugstr_w(file->TargetPath));
2176 progress_message(hPackage,2,1,0,0);
2177 rc = !MoveFileW(file->SourcePath,file->TargetPath);
2179 ERR("Unable to move file\n");
2188 inline static UINT get_file_target(MSIHANDLE hPackage, LPCWSTR file_key,
2191 MSIPACKAGE *package;
2194 package = msihandle2msiinfo(hPackage, MSIHANDLETYPE_PACKAGE);
2196 return ERROR_INVALID_HANDLE;
2198 for (index = 0; index < package->loaded_files; index ++)
2200 if (strcmpW(file_key,package->files[index].File)==0)
2202 if (package->files[index].State >= 3)
2204 strcpyW(file_source,package->files[index].TargetPath);
2205 return ERROR_SUCCESS;
2208 return ERROR_FILE_NOT_FOUND;
2212 return ERROR_FUNCTION_FAILED;
2215 static UINT ACTION_DuplicateFiles(MSIHANDLE hPackage)
2220 static const CHAR *ExecSeqQuery = "select * from DuplicateFile";
2221 MSIPACKAGE* package;
2223 package = msihandle2msiinfo(hPackage, MSIHANDLETYPE_PACKAGE);
2225 return ERROR_INVALID_HANDLE;
2227 rc = MsiDatabaseOpenViewA(package->db, ExecSeqQuery, &view);
2229 if (rc != ERROR_SUCCESS)
2232 rc = MsiViewExecute(view, 0);
2233 if (rc != ERROR_SUCCESS)
2236 MsiCloseHandle(view);
2242 WCHAR file_key[0x100];
2243 WCHAR file_source[MAX_PATH];
2244 WCHAR dest_name[0x100];
2245 WCHAR dest_path[MAX_PATH];
2246 WCHAR component[0x100];
2247 INT component_index;
2251 rc = MsiViewFetch(view,&row);
2252 if (rc != ERROR_SUCCESS)
2259 rc = MsiRecordGetStringW(row,2,component,&sz);
2260 if (rc != ERROR_SUCCESS)
2262 ERR("Unable to get component\n");
2263 MsiCloseHandle(row);
2267 component_index = get_loaded_component(package,component);
2268 if (!package->components[component_index].Enabled ||
2269 !package->components[component_index].FeatureState)
2271 TRACE("Skipping copy due to disabled component\n");
2272 MsiCloseHandle(row);
2277 rc = MsiRecordGetStringW(row,3,file_key,&sz);
2278 if (rc != ERROR_SUCCESS)
2280 ERR("Unable to get file key\n");
2281 MsiCloseHandle(row);
2285 rc = get_file_target(hPackage,file_key,file_source);
2287 if (rc != ERROR_SUCCESS)
2289 ERR("Original file unknown %s\n",debugstr_w(file_key));
2290 MsiCloseHandle(row);
2294 if (MsiRecordIsNull(row,4))
2296 strcpyW(dest_name,strrchrW(file_source,'\\')+1);
2301 MsiRecordGetStringW(row,4,dest_name,&sz);
2302 reduce_to_longfilename(dest_name);
2305 if (MsiRecordIsNull(row,5))
2307 strcpyW(dest_path,file_source);
2308 *strrchrW(dest_path,'\\')=0;
2312 WCHAR destkey[0x100];
2314 MsiRecordGetStringW(row,5,destkey,&sz);
2316 rc = resolve_folder(hPackage, destkey, dest_path,FALSE,FALSE,NULL);
2317 if (rc != ERROR_SUCCESS)
2319 ERR("Unable to get destination folder\n");
2320 MsiCloseHandle(row);
2325 strcatW(dest_path,dest_name);
2327 TRACE("Duplicating file %s to %s\n",debugstr_w(file_source),
2328 debugstr_w(dest_path));
2330 if (strcmpW(file_source,dest_path))
2331 rc = !CopyFileW(file_source,dest_path,TRUE);
2335 if (rc != ERROR_SUCCESS)
2336 ERR("Failed to copy file\n");
2338 FIXME("We should track these duplicate files as well\n");
2340 MsiCloseHandle(row);
2343 MsiCloseHandle(view);
2349 /* OK this value is "interpretted" and then formatted based on the
2350 first few characters */
2351 static LPSTR parse_value(MSIHANDLE hPackage, WCHAR *value, DWORD *type,
2355 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2364 deformat_string(hPackage, &value[2], &deformated);
2366 /* binary value type */
2369 *size = strlenW(ptr)/2;
2370 data = HeapAlloc(GetProcessHeap(),0,*size);
2382 data[count] = (BYTE)strtol(byte,NULL,0);
2385 HeapFree(GetProcessHeap(),0,deformated);
2387 TRACE("Data %li bytes(%i)\n",*size,count);
2392 deformat_string(hPackage, &value[1], &deformated);
2395 *size = sizeof(DWORD);
2396 data = HeapAlloc(GetProcessHeap(),0,*size);
2397 *(LPDWORD)data = atoiW(deformated);
2398 TRACE("DWORD %i\n",*data);
2400 HeapFree(GetProcessHeap(),0,deformated);
2413 *type=REG_EXPAND_SZ;
2421 *size = deformat_string(hPackage, ptr,(LPWSTR*)&data);
2426 static UINT ACTION_WriteRegistryValues(MSIHANDLE hPackage)
2431 static const CHAR *ExecSeqQuery = "select * from Registry";
2432 MSIPACKAGE *package;
2434 package = msihandle2msiinfo(hPackage, MSIHANDLETYPE_PACKAGE);
2436 return ERROR_INVALID_HANDLE;
2438 rc = MsiDatabaseOpenViewA(package->db, ExecSeqQuery, &view);
2440 if (rc != ERROR_SUCCESS)
2443 rc = MsiViewExecute(view, 0);
2444 if (rc != ERROR_SUCCESS)
2447 MsiCloseHandle(view);
2456 LPSTR value_data = NULL;
2457 HKEY root_key, hkey;
2459 WCHAR component[0x100];
2460 INT component_index;
2465 rc = MsiViewFetch(view,&row);
2466 if (rc != ERROR_SUCCESS)
2473 MsiRecordGetStringW(row,6,component,&sz);
2474 component_index = get_loaded_component(package,component);
2476 if (!package->components[component_index].Enabled ||
2477 !package->components[component_index].FeatureState)
2479 TRACE("Skipping write due to disabled component\n");
2480 MsiCloseHandle(row);
2484 /* null values have special meanings during uninstalls and such */
2486 if(MsiRecordIsNull(row,5))
2488 MsiCloseHandle(row);
2492 root = MsiRecordGetInteger(row,2);
2494 MsiRecordGetStringW(row,3,key,&sz);
2497 if (MsiRecordIsNull(row,4))
2500 MsiRecordGetStringW(row,4,name,&sz);
2503 /* get the root key */
2506 case 0: root_key = HKEY_CLASSES_ROOT; break;
2507 case 1: root_key = HKEY_CURRENT_USER; break;
2508 case 2: root_key = HKEY_LOCAL_MACHINE; break;
2509 case 3: root_key = HKEY_USERS; break;
2511 ERR("Unknown root %i\n",root);
2517 MsiCloseHandle(row);
2521 if (RegCreateKeyW( root_key, key, &hkey))
2523 ERR("Could not create key %s\n",debugstr_w(key));
2524 MsiCloseHandle(row);
2529 MsiRecordGetStringW(row,5,NULL,&sz);
2531 value = HeapAlloc(GetProcessHeap(),0,sz * sizeof(WCHAR));
2532 MsiRecordGetStringW(row,5,value,&sz);
2533 value_data = parse_value(hPackage, value, &type, &size);
2534 HeapFree(GetProcessHeap(),0,value);
2538 TRACE("Setting value %s\n",debugstr_w(name));
2539 RegSetValueExW(hkey, name, 0, type, value_data, size);
2540 HeapFree(GetProcessHeap(),0,value_data);
2542 progress_message(hPackage,2,1,0,0);
2544 MsiCloseHandle(row);
2547 MsiCloseHandle(view);
2552 * This helper function should probably go alot of places
2554 * Thinking about this, maybe this should become yet another Bison file
2556 static DWORD deformat_string(MSIHANDLE hPackage, WCHAR* ptr,WCHAR** data)
2565 /* scan for special characters */
2566 if (!strchrW(ptr,'[') || (strchrW(ptr,'[') && !strchrW(ptr,']')))
2569 size = (strlenW(ptr)+1) * sizeof(WCHAR);
2570 *data = HeapAlloc(GetProcessHeap(),0,size);
2575 /* formatted string located */
2576 mark = strchrW(ptr,'[');
2579 INT cnt = (mark - ptr);
2580 TRACE("%i (%i) characters before marker\n",cnt,(mark-ptr));
2581 size = cnt * sizeof(WCHAR);
2582 size += sizeof(WCHAR);
2583 *data = HeapAlloc(GetProcessHeap(),0,size);
2584 strncpyW(*data,ptr,cnt);
2589 size = sizeof(WCHAR);
2590 *data = HeapAlloc(GetProcessHeap(),0,size);
2595 *strchrW(key,']')=0;
2596 mark = strchrW(mark,']');
2598 TRACE("Current %s .. %s\n",debugstr_w(*data),debugstr_w(mark));
2600 if (MsiGetPropertyW(hPackage, key, value,&sz) == ERROR_SUCCESS)
2603 chunk = (strlenW(value)+1) * sizeof(WCHAR);
2605 newdata = HeapReAlloc(GetProcessHeap(),0,*data,size);
2607 strcatW(*data,value);
2609 TRACE("Current %s .. %s\n",debugstr_w(*data),debugstr_w(mark));
2613 chunk = (strlenW(mark)+1) * sizeof(WCHAR);
2615 newdata = HeapReAlloc(GetProcessHeap(),0,*data,size);
2617 strcatW(*data,mark);
2619 (*data)[strlenW(*data)]=0;
2620 TRACE("Current %s .. %s\n",debugstr_w(*data),debugstr_w(mark));
2622 /* recursively do this to clean up */
2623 mark = HeapAlloc(GetProcessHeap(),0,size);
2624 strcpyW(mark,*data);
2625 TRACE("String at this point %s\n",debugstr_w(mark));
2626 size = deformat_string(hPackage,mark,data);
2627 HeapFree(GetProcessHeap(),0,mark);
2631 static UINT ACTION_InstallInitialize(MSIHANDLE hPackage)
2636 MSIPACKAGE *package;
2638 /* I do not know if this is where it should happen.. but */
2640 TRACE("Checking Install Level\n");
2641 FIXME("The Attributes of the feature OVERRIDE THIS... unimplemented\n");
2642 FIXME("As does the ALLLOCAL and such propertys\n");
2645 if (MsiGetPropertyA(hPackage,"INSTALLLEVEL",level,&sz)==ERROR_SUCCESS)
2646 install_level = atoi(level);
2650 package = msihandle2msiinfo(hPackage, MSIHANDLETYPE_PACKAGE);
2652 return ERROR_INVALID_HANDLE;
2655 * components FeatureState defaults to FALSE. the idea is we want to
2656 * enable the component is ANY feature that uses it is enabled to install
2658 for(i = 0; i < package->loaded_features; i++)
2660 BOOL feature_state= ((package->features[i].Level > 0) &&
2661 (package->features[i].Level <= install_level));
2662 TRACE("Feature %s has a state of %i\n",
2663 debugstr_w(package->features[i].Feature), feature_state);
2664 for( j = 0; j < package->features[i].ComponentCount; j++)
2666 package->components[package->features[i].Components[j]].FeatureState
2671 * so basically we ONLY want to install a component if its Enabled AND
2672 * FeatureState are both TRUE
2674 return ERROR_SUCCESS;
2677 static UINT ACTION_InstallValidate(MSIHANDLE hPackage)
2680 static const CHAR q1[]="SELECT * FROM Registry";
2681 static const CHAR q2[]=
2682 "select Action from InstallExecuteSequence where Sequence > 0 order by Sequence";
2688 MSIPACKAGE* package;
2690 TRACE(" InstallValidate \n");
2692 db = MsiGetActiveDatabase(hPackage);
2693 rc = MsiDatabaseOpenViewA(db, q2, &view);
2694 rc = MsiViewExecute(view, 0);
2698 rc = MsiViewFetch(view,&row);
2699 if (rc != ERROR_SUCCESS)
2708 MsiRecordGetStringA(row,1,buf,&sz);
2709 if (strcmp(buf,"InstallValidate")==0)
2715 MsiCloseHandle(row);
2718 MsiCloseHandle(view);
2720 rc = MsiDatabaseOpenViewA(db, q1, &view);
2721 rc = MsiViewExecute(view, 0);
2724 rc = MsiViewFetch(view,&row);
2725 if (rc != ERROR_SUCCESS)
2732 MsiCloseHandle(row);
2735 MsiCloseHandle(view);
2738 package = msihandle2msiinfo(hPackage, MSIHANDLETYPE_PACKAGE);
2739 progress_message(hPackage,0,progress+package->loaded_files,0,0);
2741 return ERROR_SUCCESS;
2748 /* Msi functions that seem approperate here */
2749 UINT WINAPI MsiDoActionA( MSIHANDLE hInstall, LPCSTR szAction )
2754 TRACE(" exteral attempt at action %s\n",szAction);
2757 return ERROR_FUNCTION_FAILED;
2759 return ERROR_FUNCTION_FAILED;
2761 len = MultiByteToWideChar( CP_ACP, 0, szAction, -1, NULL, 0);
2762 szwAction = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
2765 return ERROR_FUNCTION_FAILED;
2767 MultiByteToWideChar( CP_ACP, 0, szAction, -1, szwAction, len);
2769 rc = MsiDoActionW(hInstall, szwAction);
2770 HeapFree(GetProcessHeap(),0,szwAction);
2774 UINT WINAPI MsiDoActionW( MSIHANDLE hInstall, LPCWSTR szAction )
2776 TRACE(" exteral attempt at action %s \n",debugstr_w(szAction));
2777 return ACTION_PerformAction(hInstall,szAction);
2780 UINT WINAPI MsiGetTargetPathA( MSIHANDLE hInstall, LPCSTR szFolder,
2781 LPSTR szPathBuf, DWORD* pcchPathBuf)
2787 TRACE("getting folder %s %p %li\n",szFolder,szPathBuf, *pcchPathBuf);
2790 return ERROR_FUNCTION_FAILED;
2792 return ERROR_FUNCTION_FAILED;
2794 len = MultiByteToWideChar( CP_ACP, 0, szFolder, -1, NULL, 0);
2795 szwFolder= HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
2798 return ERROR_FUNCTION_FAILED;
2800 szwPathBuf = HeapAlloc( GetProcessHeap(), 0 , *pcchPathBuf * sizeof(WCHAR));
2802 MultiByteToWideChar( CP_ACP, 0, szFolder, -1, szwFolder, len);
2804 rc = MsiGetTargetPathW(hInstall, szwFolder, szwPathBuf,pcchPathBuf);
2806 WideCharToMultiByte( CP_ACP, 0, szwPathBuf, *pcchPathBuf, szPathBuf,
2807 *pcchPathBuf, NULL, NULL );
2809 HeapFree(GetProcessHeap(),0,szwFolder);
2810 HeapFree(GetProcessHeap(),0,szwPathBuf);
2815 UINT WINAPI MsiGetTargetPathW( MSIHANDLE hInstall, LPCWSTR szFolder, LPWSTR
2816 szPathBuf, DWORD* pcchPathBuf)
2818 WCHAR path[MAX_PATH];
2821 TRACE("(%s %p %li)\n",debugstr_w(szFolder),szPathBuf,*pcchPathBuf);
2823 rc = resolve_folder(hInstall, szFolder, path, FALSE, FALSE, NULL);
2825 if (rc == ERROR_SUCCESS && strlenW(path) > *pcchPathBuf)
2827 *pcchPathBuf = strlenW(path)+1;
2828 return ERROR_MORE_DATA;
2830 else if (rc == ERROR_SUCCESS)
2832 *pcchPathBuf = strlenW(path)+1;
2833 strcpyW(szPathBuf,path);
2834 TRACE("Returning Path %s\n",debugstr_w(path));
2841 UINT WINAPI MsiGetSourcePathA( MSIHANDLE hInstall, LPCSTR szFolder,
2842 LPSTR szPathBuf, DWORD* pcchPathBuf)
2848 TRACE("getting source %s %p %li\n",szFolder,szPathBuf, *pcchPathBuf);
2851 return ERROR_FUNCTION_FAILED;
2853 return ERROR_FUNCTION_FAILED;
2855 len = MultiByteToWideChar( CP_ACP, 0, szFolder, -1, NULL, 0);
2856 szwFolder= HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
2859 return ERROR_FUNCTION_FAILED;
2861 szwPathBuf = HeapAlloc( GetProcessHeap(), 0 , *pcchPathBuf * sizeof(WCHAR));
2863 MultiByteToWideChar( CP_ACP, 0, szFolder, -1, szwFolder, len);
2865 rc = MsiGetSourcePathW(hInstall, szwFolder, szwPathBuf,pcchPathBuf);
2867 WideCharToMultiByte( CP_ACP, 0, szwPathBuf, *pcchPathBuf, szPathBuf,
2868 *pcchPathBuf, NULL, NULL );
2870 HeapFree(GetProcessHeap(),0,szwFolder);
2871 HeapFree(GetProcessHeap(),0,szwPathBuf);
2876 UINT WINAPI MsiGetSourcePathW( MSIHANDLE hInstall, LPCWSTR szFolder, LPWSTR
2877 szPathBuf, DWORD* pcchPathBuf)
2879 WCHAR path[MAX_PATH];
2882 TRACE("(%s %p %li)\n",debugstr_w(szFolder),szPathBuf,*pcchPathBuf);
2883 rc = resolve_folder(hInstall, szFolder, path, TRUE, FALSE, NULL);
2885 if (rc == ERROR_SUCCESS && strlenW(path) > *pcchPathBuf)
2887 *pcchPathBuf = strlenW(path)+1;
2888 return ERROR_MORE_DATA;
2890 else if (rc == ERROR_SUCCESS)
2892 *pcchPathBuf = strlenW(path)+1;
2893 strcpyW(szPathBuf,path);
2894 TRACE("Returning Path %s\n",debugstr_w(path));
2901 UINT WINAPI MsiSetTargetPathA(MSIHANDLE hInstall, LPCSTR szFolder,
2902 LPCSTR szFolderPath)
2905 LPWSTR szwFolderPath;
2909 return ERROR_FUNCTION_FAILED;
2911 return ERROR_FUNCTION_FAILED;
2913 len = MultiByteToWideChar( CP_ACP, 0, szFolder, -1, NULL, 0);
2914 szwFolder= HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
2917 return ERROR_FUNCTION_FAILED;
2919 MultiByteToWideChar( CP_ACP, 0, szFolder, -1, szwFolder, len);
2921 len = MultiByteToWideChar( CP_ACP, 0, szFolderPath, -1, NULL, 0);
2922 szwFolderPath= HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
2926 HeapFree(GetProcessHeap(),0,szwFolder);
2927 return ERROR_FUNCTION_FAILED;
2930 MultiByteToWideChar( CP_ACP, 0, szFolderPath, -1, szwFolderPath, len);
2932 rc = MsiSetTargetPathW(hInstall, szwFolder, szwFolderPath);
2934 HeapFree(GetProcessHeap(),0,szwFolder);
2935 HeapFree(GetProcessHeap(),0,szwFolderPath);
2940 UINT WINAPI MsiSetTargetPathW(MSIHANDLE hInstall, LPCWSTR szFolder,
2941 LPCWSTR szFolderPath)
2943 MSIPACKAGE *package;
2945 WCHAR path[MAX_PATH];
2948 TRACE("(%s %s)\n",debugstr_w(szFolder),debugstr_w(szFolderPath));
2950 if (szFolderPath[0]==0)
2951 return ERROR_FUNCTION_FAILED;
2953 if (GetFileAttributesW(szFolderPath) == INVALID_FILE_ATTRIBUTES)
2954 return ERROR_FUNCTION_FAILED;
2956 package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
2959 return ERROR_INVALID_HANDLE;
2961 resolve_folder(hInstall,szFolder,path,FALSE,FALSE,&folder);
2964 return ERROR_INVALID_PARAMETER;
2966 strcpyW(folder->Property,szFolderPath);
2968 for (i = 0; i < package->loaded_folders; i++)
2969 package->folders[i].ResolvedTarget[0]=0;
2971 for (i = 0; i < package->loaded_folders; i++)
2972 resolve_folder(hInstall, package->folders[i].Directory, path, FALSE,
2975 return ERROR_SUCCESS;
2978 BOOL WINAPI MsiGetMode(MSIHANDLE hInstall, DWORD iRunMode)
2980 FIXME("STUB (%li)\n",iRunMode);
2985 static UINT ACTION_Template(MSIHANDLE hPackage)
2990 static const CHAR *ExecSeqQuery;
2993 db = MsiGetActiveDatabase(hPackage);
2994 rc = MsiDatabaseOpenViewA(db, ExecSeqQuery, &view);
2997 if (rc != ERROR_SUCCESS)
3000 rc = MsiViewExecute(view, 0);
3001 if (rc != ERROR_SUCCESS)
3004 MsiCloseHandle(view);
3010 rc = MsiViewFetch(view,&row);
3011 if (rc != ERROR_SUCCESS)
3017 MsiCloseHandle(row);
3020 MsiCloseHandle(view);