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
37 #include "wine/debug.h"
46 #include "wine/unicode.h"
48 #define CUSTOM_ACTION_TYPE_MASK 0x3F
51 * These are hacks to get around the inability to write
52 * to the database and the inability to select on string values
53 * once those values are done then it will be possible to do
54 * all this inside the database.
64 static internal_property PropTableHack[MAX_PROP];
65 static INT PropCount = -1;
67 WINE_DEFAULT_DEBUG_CHANNEL(msi);
72 UINT ACTION_PerformAction(MSIHANDLE hPackage, const WCHAR *action);
73 static UINT ACTION_CostInitialize(MSIHANDLE hPackage);
74 static UINT ACTION_CreateFolders(MSIHANDLE hPackage);
75 static UINT ACTION_CostFinalize(MSIHANDLE hPackage);
76 static UINT ACTION_InstallFiles(MSIHANDLE hPackage);
77 static UINT ACTION_DuplicateFiles(MSIHANDLE hPackage);
78 static UINT ACTION_WriteRegistryValues(MSIHANDLE hPackage);
79 static UINT ACTION_CustomAction(MSIHANDLE hPackage,const WCHAR *action);
81 static UINT HANDLE_CustomType1(MSIHANDLE hPackage, const LPWSTR source,
82 const LPWSTR target, const INT type);
83 static UINT HANDLE_CustomType2(MSIHANDLE hPackage, const LPWSTR source,
84 const LPWSTR target, const INT type);
87 static UINT set_property(MSIHANDLE hPackage, const WCHAR* prop,
89 UINT get_property(MSIHANDLE hPackage, const WCHAR* prop, WCHAR* value,
91 static VOID blitz_propertytable();
92 static VOID set_installer_properties(MSIHANDLE hPackage);
93 static DWORD deformat_string(MSIHANDLE hPackage, WCHAR* ptr,WCHAR** data);
96 * consts and values used
98 static const WCHAR cszSourceDir[] = {'S','o','u','r','c','e','D','i','r',0};
99 static const WCHAR cszRootDrive[] = {'R','O','O','T','D','R','I','V','E',0};
100 static const WCHAR cszTargetDir[] = {'T','A','R','G','E','T','D','I','R',0};
102 static const WCHAR cszlsb[]={'[',0};
103 static const WCHAR cszrsb[]={']',0};
104 static const WCHAR cszbs[]={'\\',0};
107 /********************************************************
108 * helper functions to get around current HACKS and such
109 ********************************************************/
110 inline static char *strdupWtoA( const WCHAR *str )
115 DWORD len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL
117 if ((ret = HeapAlloc( GetProcessHeap(), 0, len )))
118 WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
123 static VOID blitz_propertytable()
128 memset(&PropTableHack,0,sizeof(PropTableHack));
130 else if (PropCount > 0)
133 TRACE("Clearing %i properties\n",PropCount);
134 for (i = 0; i < PropCount; i++)
136 HeapFree(GetProcessHeap(), 0, PropTableHack[i].prop_name);
137 HeapFree(GetProcessHeap(), 0, PropTableHack[i].prop_value);
139 memset(&PropTableHack,0,sizeof(PropTableHack));
144 UINT get_property(MSIHANDLE hPackage, const WCHAR* prop, WCHAR* value,
149 WCHAR* pName = PropTableHack[0].prop_name;
151 TRACE("Looking for property %s\n",debugstr_w(prop));
153 /* prop table hacks take presidence */
155 while (pName && strcmpW(pName,prop) && index < PropCount)
158 pName = PropTableHack[index].prop_name;
161 if (pName && index < PropCount)
163 if (*size > strlenW(PropTableHack[index].prop_value))
165 *size = strlenW(PropTableHack[index].prop_value)+1;
166 TRACE(" index %i\n", index);
167 strcpyW(value , PropTableHack[index].prop_value);
168 TRACE(" found value %s\n",debugstr_w(value));
173 *size = strlenW(PropTableHack[index].prop_value);
174 return ERROR_MORE_DATA;
178 rc = MsiGetPropertyW(hPackage,prop,value,size);
180 if (rc == ERROR_SUCCESS)
181 TRACE(" found value %s\n",debugstr_w(value));
183 TRACE(" value not found\n");
188 static UINT set_property(MSIHANDLE hPackage, const WCHAR* prop,
191 /* prop table hacks take precedence */
197 blitz_propertytable();
199 pName = PropTableHack[0].prop_name;
201 TRACE("Setting property %s to %s\n",debugstr_w(prop),debugstr_w(value));
203 while (pName && strcmpW(pName,prop) && index < MAX_PROP)
206 pName = PropTableHack[index].prop_name;
209 if (pName && index < MAX_PROP)
211 TRACE("property index %i\n",index);
212 strcpyW(PropTableHack[index].prop_value,value);
217 if (index >= MAX_PROP)
219 ERR("EXCEEDING MAX PROP!!!!\n");
220 return ERROR_FUNCTION_FAILED;
222 PropTableHack[index].prop_name = HeapAlloc(GetProcessHeap(),0,1024);
223 PropTableHack[index].prop_value= HeapAlloc(GetProcessHeap(),0,1024);
224 strcpyW(PropTableHack[index].prop_name,prop);
225 strcpyW(PropTableHack[index].prop_value,value);
227 TRACE("new property index %i (%i)\n",index,PropCount);
231 /* currently unreachable */
232 rc = MsiSetPropertyW(hPackage,prop,value);
237 * There are a whole slew of these we need to set
240 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/properties.asp
243 static VOID set_installer_properties(MSIHANDLE hPackage)
247 static const WCHAR c_col[] =
249 static const WCHAR CFF[] =
250 {'C','o','m','m','o','n','F','i','l','e','s','F','o','l','d','e','r',0};
251 static const WCHAR PFF[] =
252 {'P','r','o','g','r','a','m','F','i','l','e','s','F','o','l','d','e','r',0};
253 static const WCHAR CADF[] =
254 {'C','o','m','m','o','n','A','p','p','D','a','t','a','F','o','l','d','e','r',0};
255 static const WCHAR ATF[] =
256 {'A','d','m','i','n','T','o','o','l','s','F','o','l','d','e','r',0};
257 static const WCHAR ADF[] =
258 {'A','p','p','D','a','t','a','F','o','l','d','e','r',0};
259 static const WCHAR SF[] =
260 {'S','y','s','t','e','m','F','o','l','d','e','r',0};
261 static const WCHAR LADF[] =
262 {'L','o','c','a','l','A','p','p','D','a','t','a','F','o','l','d','e','r',0};
263 static const WCHAR MPF[] =
264 {'M','y','P','i','c','t','u','r','e','s','F','o','l','d','e','r',0};
265 static const WCHAR PF[] =
266 {'P','e','r','s','o','n','a','l','F','o','l','d','e','r',0};
267 static const WCHAR WF[] =
268 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
269 static const WCHAR TF[]=
270 {'T','e','m','p','F','o','l','d','e','r',0};
272 /* Not yet set ... but needed by iTunes
274 static const WCHAR DF[] =
275 {'D','e','s','k','t','o','p','F','o','l','d','e','r',0};
276 static const WCHAR FF[] =
277 {'F','a','v','o','r','i','t','e','s','F','o','l','d','e','r',0};
278 static const WCHAR FoF[] =
279 {'F','o','n','t','s','F','o','l','d','e','r',0};
291 /* asked for by iTunes ... but are they installer set?
293 * GlobalAssemblyCache
296 set_property(hPackage, cszRootDrive, c_col);
298 SHGetFolderPathW(NULL,CSIDL_PROGRAM_FILES_COMMON,NULL,0,pth);
300 set_property(hPackage, CFF, pth);
302 SHGetFolderPathW(NULL,CSIDL_PROGRAM_FILES,NULL,0,pth);
304 set_property(hPackage, PFF, pth);
306 SHGetFolderPathW(NULL,CSIDL_COMMON_APPDATA,NULL,0,pth);
308 set_property(hPackage, CADF, pth);
310 SHGetFolderPathW(NULL,CSIDL_ADMINTOOLS,NULL,0,pth);
312 set_property(hPackage, ATF, pth);
314 SHGetFolderPathW(NULL,CSIDL_APPDATA,NULL,0,pth);
316 set_property(hPackage, ADF, pth);
318 SHGetFolderPathW(NULL,CSIDL_SYSTEM,NULL,0,pth);
320 set_property(hPackage, SF, pth);
322 SHGetFolderPathW(NULL,CSIDL_LOCAL_APPDATA,NULL,0,pth);
324 set_property(hPackage, LADF, pth);
326 SHGetFolderPathW(NULL,CSIDL_MYPICTURES,NULL,0,pth);
328 set_property(hPackage, MPF, pth);
330 SHGetFolderPathW(NULL,CSIDL_PERSONAL,NULL,0,pth);
332 set_property(hPackage, PF, pth);
334 SHGetFolderPathW(NULL,CSIDL_WINDOWS,NULL,0,pth);
336 set_property(hPackage, WF, pth);
338 GetTempPathW(MAX_PATH,pth);
339 set_property(hPackage, TF, pth);
343 /****************************************************
344 * TOP level entry points
345 *****************************************************/
347 UINT ACTION_DoTopLevelINSTALL(MSIHANDLE hPackage, LPCWSTR szPackagePath,
348 LPCWSTR szCommandLine)
352 static const CHAR *ExecSeqQuery =
353 "select * from InstallExecuteSequence where Sequence > 0 order by Sequence";
355 FIXME("****We do not do any of the UI level stuff yet***\n");
357 /* reset our properties */
358 blitz_propertytable();
362 static const WCHAR OriginalDatabase[] =
363 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
365 WCHAR check[MAX_PATH];
369 set_property(hPackage, OriginalDatabase, szPackagePath);
371 strcpyW(pth,szPackagePath);
372 p = strrchrW(pth,'\\');
380 if (get_property(hPackage,cszSourceDir,check,&size) != ERROR_SUCCESS )
381 set_property(hPackage, cszSourceDir, pth);
384 rc = MsiDatabaseOpenViewA(hPackage, ExecSeqQuery, &view);
386 if (rc == ERROR_SUCCESS)
388 rc = MsiViewExecute(view, 0);
390 if (rc != ERROR_SUCCESS)
393 MsiCloseHandle(view);
397 TRACE("Running the actions \n");
405 rc = MsiViewFetch(view,&row);
406 if (rc != ERROR_SUCCESS)
412 /* check conditions */
413 if (!MsiRecordIsNull(row,2))
416 rc = MsiRecordGetStringW(row,2,buffer,&sz);
417 if (rc != ERROR_SUCCESS)
423 /* this is a hack to skip errors in the condition code */
424 if (MsiEvaluateConditionW(hPackage, buffer) ==
434 rc = MsiRecordGetStringW(row,1,buffer,&sz);
435 if (rc != ERROR_SUCCESS)
437 ERR("Error is %x\n",rc);
442 rc = ACTION_PerformAction(hPackage,buffer);
444 if (rc != ERROR_SUCCESS)
446 ERR("Execution halted due to error (%i)\n",rc);
455 MsiCloseHandle(view);
459 blitz_propertytable();
464 /********************************************************
465 * ACTION helper functions and functions that perform the actions
466 *******************************************************/
469 * Alot of actions are really important even if they don't do anything
470 * explicit.. Lots of properties are set at the beginning of the installation
471 * CostFinalize does a bunch of work to translated the directorys and such
473 * But until I get write access to the database that is hard. so I am going to
474 * hack it to see if I can get something to run.
476 UINT ACTION_PerformAction(MSIHANDLE hPackage, const WCHAR *action)
478 const static WCHAR szCreateFolders[] =
479 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
480 const static WCHAR szCostFinalize[] =
481 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
482 const static WCHAR szInstallFiles[] =
483 {'I','n','s','t','a','l','l','F','i','l','e','s',0};
484 const static WCHAR szDuplicateFiles[] =
485 {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
486 const static WCHAR szWriteRegistryValues[] =
487 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
488 const static WCHAR szCostInitialize[] =
489 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
491 TRACE("Performing action (%s)\n",debugstr_w(action));
493 if (strcmpW(action,szCostInitialize)==0)
494 return ACTION_CostInitialize(hPackage);
495 if (strcmpW(action,szCreateFolders)==0)
496 return ACTION_CreateFolders(hPackage);
497 if (strcmpW(action,szCostFinalize)==0)
498 return ACTION_CostFinalize(hPackage);
499 if (strcmpW(action,szInstallFiles)==0)
500 return ACTION_InstallFiles(hPackage);
501 if (strcmpW(action,szDuplicateFiles)==0)
502 return ACTION_DuplicateFiles(hPackage);
503 if (strcmpW(action,szWriteRegistryValues)==0)
504 return ACTION_WriteRegistryValues(hPackage);
506 Current called during itunes but unimplemented
513 ResolveSource (sets SourceDir)
515 ValidateProductID (sets ProductID)
516 IsolateComponents (Empty)
520 RemoveExistingProducts
522 AllocateRegistrySpace
529 SelfUnregModules (Empty)
530 UnregisterTypeLibraries
535 UnregisterExtensionInfo
540 RemoveEnviromentStrings
544 RemoveRegistryValues (Empty)
545 SelfRegModules (Empty)
549 CreateShortcuts (would be nice to have soon)
551 RegisterExtensionInfo (Empty)
552 RegisterProgIdInfo (Lots to do)
553 RegisterMIMEInfo (Empty)
554 WriteIniValues (Empty)
555 WriteEnvironmentStrings (Empty)
558 RegisterTypeLibraries
569 if (ACTION_CustomAction(hPackage,action) != ERROR_SUCCESS)
570 ERR("UNHANDLED MSI ACTION %s\n",debugstr_w(action));
572 return ERROR_SUCCESS;
576 static UINT ACTION_CustomAction(MSIHANDLE hPackage,const WCHAR *action)
581 WCHAR ExecSeqQuery[1024] =
582 {'s','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','C','u','s','t','o'
583 ,'m','A','c','t','i','o','n',' ','w','h','e','r','e',' ','`','A','c','t','i'
584 ,'o','n','`',' ','=',' ','`',0};
585 static const WCHAR end[]={'`',0};
590 WCHAR *deformated=NULL;
592 strcatW(ExecSeqQuery,action);
593 strcatW(ExecSeqQuery,end);
594 rc = MsiDatabaseOpenViewW(hPackage, ExecSeqQuery, &view);
595 if (rc != ERROR_SUCCESS)
598 rc = MsiViewExecute(view, 0);
599 if (rc != ERROR_SUCCESS)
602 MsiCloseHandle(view);
606 rc = MsiViewFetch(view,&row);
607 if (rc != ERROR_SUCCESS)
610 MsiCloseHandle(view);
614 type = MsiRecordGetInteger(row,2);
617 MsiRecordGetStringW(row,3,source,&sz);
619 MsiRecordGetStringW(row,4,target,&sz);
621 TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type,
622 debugstr_w(source), debugstr_w(target));
624 /* we are ignoring ALOT of flags and important syncornication stuff */
625 switch (type & CUSTOM_ACTION_TYPE_MASK)
627 case 1: /* DLL file stored in a Binary table stream */
628 rc = HANDLE_CustomType1(hPackage,source,target,type);
630 case 2: /* Exe file stored in a Binary table strem */
631 rc = HANDLE_CustomType2(hPackage,source,target,type);
633 case 35: /* Directory set with formatted text. */
634 case 51: /* Property set with formatted text. */
635 deformat_string(hPackage,target,&deformated);
636 set_property(hPackage,source,deformated);
637 HeapFree(GetProcessHeap(),0,deformated);
640 ERR("UNHANDLED ACTION TYPE %i (%s %s)\n",
641 type & CUSTOM_ACTION_TYPE_MASK, debugstr_w(source),
647 MsiCloseHandle(view);
651 static UINT store_binary_to_temp(MSIHANDLE hPackage, const LPWSTR source,
654 static const WCHAR TF[]= {'T','e','m','p','F','o','l','d','e','r',0};
657 if (get_property(hPackage, TF,tmp_file, &sz) != ERROR_SUCCESS)
658 GetTempPathW(MAX_PATH,tmp_file);
660 strcatW(tmp_file,source);
662 if (GetFileAttributesW(tmp_file) != INVALID_FILE_ATTRIBUTES)
664 TRACE("File already exists\n");
665 return ERROR_SUCCESS;
669 /* write out the file */
674 {'s','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','B','i'
675 ,'n','a','r','y',' ','w','h','e','r','e',' ','N','a','m','e','=','`',0};
676 static const WCHAR end[]={'`',0};
680 the_file = CreateFileW(tmp_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
681 FILE_ATTRIBUTE_NORMAL, NULL);
683 if (the_file == INVALID_HANDLE_VALUE)
684 return ERROR_FUNCTION_FAILED;
686 strcatW(Query,source);
688 rc = MsiDatabaseOpenViewW(hPackage, Query, &view);
689 if (rc != ERROR_SUCCESS)
692 rc = MsiViewExecute(view, 0);
693 if (rc != ERROR_SUCCESS)
696 MsiCloseHandle(view);
700 rc = MsiViewFetch(view,&row);
701 if (rc != ERROR_SUCCESS)
704 MsiCloseHandle(view);
712 rc = MsiRecordReadStream(row,2,buffer,&sz);
713 if (rc != ERROR_SUCCESS)
715 ERR("Failed to get stream\n");
716 CloseHandle(the_file);
717 DeleteFileW(tmp_file);
720 WriteFile(the_file,buffer,sz,&write,NULL);
721 } while (sz == 1024);
723 CloseHandle(the_file);
727 MsiCloseHandle(view);
730 return ERROR_SUCCESS;
734 typedef UINT CustomEntry(MSIHANDLE);
736 static UINT HANDLE_CustomType1(MSIHANDLE hPackage, const LPWSTR source,
737 const LPWSTR target, const INT type)
739 WCHAR tmp_file[MAX_PATH];
744 store_binary_to_temp(hPackage, source, tmp_file);
746 TRACE("Calling function %s from %s\n",debugstr_w(target),
747 debugstr_w(tmp_file));
751 ERR("Asyncronious execution.. UNHANDLED\n");
752 return ERROR_SUCCESS;
755 DLL = LoadLibraryW(tmp_file);
758 proc = strdupWtoA( target );
759 fn = (CustomEntry*)GetProcAddress(DLL,proc);
762 TRACE("Calling function\n");
766 ERR("Cannot load functon\n");
768 HeapFree(GetProcessHeap(),0,proc);
772 ERR("Unable to load library\n");
774 return ERROR_SUCCESS;
777 static UINT HANDLE_CustomType2(MSIHANDLE hPackage, const LPWSTR source,
778 const LPWSTR target, const INT type)
780 WCHAR tmp_file[MAX_PATH*2];
782 PROCESS_INFORMATION info;
785 static const WCHAR c_collen[] = {'C',':','\\',0};
786 static const WCHAR spc[] = {' ',0};
788 memset(&si,0,sizeof(STARTUPINFOW));
789 memset(&info,0,sizeof(PROCESS_INFORMATION));
791 store_binary_to_temp(hPackage, source, tmp_file);
793 strcatW(tmp_file,spc);
794 deformat_string(hPackage,target,&deformated);
795 strcatW(tmp_file,deformated);
797 HeapFree(GetProcessHeap(),0,deformated);
799 TRACE("executing exe %s \n",debugstr_w(tmp_file));
801 rc = CreateProcessW(NULL, tmp_file, NULL, NULL, FALSE, 0, NULL,
802 c_collen, &si, &info);
806 ERR("Unable to execute command\n");
807 return ERROR_SUCCESS;
811 WaitForSingleObject(info.hProcess,INFINITE);
813 return ERROR_SUCCESS;
816 /***********************************************************************
819 * Recursively create all directories in the path.
821 * shamelessly stolen from setupapi/queue.c
823 static BOOL create_full_pathW(const WCHAR *path)
829 new_path = HeapAlloc(GetProcessHeap(), 0, (strlenW(path) + 1) *
831 strcpyW(new_path, path);
833 while((len = strlenW(new_path)) && new_path[len - 1] == '\\')
834 new_path[len - 1] = 0;
836 while(!CreateDirectoryW(new_path, NULL))
839 DWORD last_error = GetLastError();
840 if(last_error == ERROR_ALREADY_EXISTS)
843 if(last_error != ERROR_PATH_NOT_FOUND)
849 if(!(slash = strrchrW(new_path, '\\')))
855 len = slash - new_path;
857 if(!create_full_pathW(new_path))
862 new_path[len] = '\\';
865 HeapFree(GetProcessHeap(), 0, new_path);
870 * Also we cannot enable/disable components either, so for now I am just going
871 * to do all the directorys for all the components.
873 static UINT ACTION_CreateFolders(MSIHANDLE hPackage)
875 static const CHAR *ExecSeqQuery = "select * from CreateFolder";
879 rc = MsiDatabaseOpenViewA(hPackage, ExecSeqQuery, &view);
880 if (rc != ERROR_SUCCESS)
883 rc = MsiViewExecute(view, 0);
884 if (rc != ERROR_SUCCESS)
887 MsiCloseHandle(view);
894 WCHAR full_path[MAX_PATH];
898 rc = MsiViewFetch(view,&row);
899 if (rc != ERROR_SUCCESS)
906 rc = MsiRecordGetStringW(row,1,dir,&sz);
908 if (rc!= ERROR_SUCCESS)
910 ERR("Unable to get folder id \n");
916 rc = get_property(hPackage, dir,full_path,&sz);
918 if (rc != ERROR_SUCCESS)
920 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
925 TRACE("Folder is %s\n",debugstr_w(full_path));
926 create_full_pathW(full_path);
931 MsiCloseHandle(view);
937 * Workhorse function for creating the directories
940 static UINT resolve_directory(MSIHANDLE hPackage, const WCHAR* dir,
941 WCHAR* path, BOOL source)
943 static const WCHAR cszsrc[]={'_','S','o','u','r','c','e',0};
944 static const WCHAR cszsrcroot[]=
945 {'[','S','o','u','r','c','e','D','i','r',']',0};
948 {'s','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','D','i','r','e','c',
949 't','o','r','y',' ','w','h','e','r','e',' ','`','D','i','r','e','c','t',
950 'o','r','y','`',' ','=',' ','`',0};
951 static const WCHAR end[]={'`',0};
954 WCHAR targetbuffer[0x100];
955 WCHAR *srcdir = NULL;
956 WCHAR *targetdir = NULL;
959 WCHAR parent_path[MAX_PATH];
962 WCHAR full_path[MAX_PATH];
963 WCHAR name_source[0x100];
966 if (get_property(hPackage,dir,path,&sz)==ERROR_SUCCESS)
967 return ERROR_SUCCESS;
969 TRACE("Working to resolve %s\n",debugstr_w(dir));
971 /* special case... root drive */
972 if (strcmpW(dir,cszTargetDir)==0)
977 if(!get_property(hPackage,cszRootDrive,buffer,&sz))
979 set_property(hPackage,cszTargetDir,buffer);
980 strcpyW(path,buffer);
984 ERR("No RootDrive property defined disaster!\n");
987 MsiCloseHandle(view);
988 return ERROR_FUNCTION_FAILED;
992 strcpyW(path,cszsrcroot);
994 return ERROR_SUCCESS;
999 rc = MsiDatabaseOpenViewW(hPackage, Query, &view);
1000 if (rc != ERROR_SUCCESS)
1003 rc = MsiViewExecute(view, 0);
1004 if (rc != ERROR_SUCCESS)
1007 MsiCloseHandle(view);
1011 rc = MsiViewFetch(view,&row);
1012 if (rc != ERROR_SUCCESS)
1015 MsiCloseHandle(view);
1020 MsiRecordGetStringW(row,3,targetbuffer,&sz);
1021 targetdir=targetbuffer;
1023 /* split src and target dir */
1024 if (strchrW(targetdir,':'))
1026 srcdir=strchrW(targetdir,':');
1033 /* for now only pick long filename versions */
1034 if (strchrW(targetdir,'|'))
1036 targetdir = strchrW(targetdir,'|');
1040 if (srcdir && strchrW(srcdir,'|'))
1042 srcdir= strchrW(srcdir,'|');
1047 /* now check for root dirs */
1048 if (targetdir[0] == '.' && targetdir[1] == 0)
1051 if (srcdir && srcdir[0] == '.' && srcdir[1] == 0)
1054 if (MsiRecordIsNull(row,2))
1059 MsiRecordGetStringW(row,2,parent,&sz);
1064 resolve_directory(hPackage,parent,parent_path,FALSE);
1065 strcpyW(full_path,parent_path);
1068 strcatW(full_path,targetdir);
1069 strcatW(full_path,cszbs);
1071 set_property(hPackage,dir,full_path);
1073 strcpyW(path,full_path);
1075 resolve_directory(hPackage,parent,parent_path,TRUE);
1076 strcpyW(full_path,parent_path);
1079 strcatW(full_path,srcdir);
1080 strcatW(full_path,cszbs);
1084 strcatW(full_path,targetdir);
1085 strcatW(full_path,cszbs);
1088 strcpyW(name_source,dir);
1089 strcatW(name_source,cszsrc);
1090 set_property(hPackage,name_source,full_path);
1092 strcpyW(path,full_path);
1095 MsiCloseHandle(row);
1097 MsiCloseHandle(view);
1102 * This is the action where all the features and components are loaded into
1103 * memory... so when we start doing that that will be important.
1106 static UINT ACTION_CostInitialize(MSIHANDLE hPackage)
1108 return ERROR_SUCCESS;
1112 * Alot is done in this function aside from just the costing.
1113 * The costing needs to be implemented at some point but for now i am going
1114 * to focus on the directory building
1116 * Note about directory names: I know that directory names get processed into
1117 * properties. I am still very unclear where the name_source
1118 * part is used but I am preserving it just as a precaution
1120 static UINT ACTION_CostFinalize(MSIHANDLE hPackage)
1122 static const CHAR *ExecSeqQuery = "select * from Directory";
1126 /* According to MSDN these properties are set when CostFinalize is run
1127 * or MsiSetInstallLevel is called */
1128 TRACE("Setting installer properties\n");
1129 set_installer_properties(hPackage);
1131 TRACE("Building Directory properties\n");
1133 rc = MsiDatabaseOpenViewA(hPackage, ExecSeqQuery, &view);
1134 if (rc != ERROR_SUCCESS)
1137 rc = MsiViewExecute(view, 0);
1138 if (rc != ERROR_SUCCESS)
1141 MsiCloseHandle(view);
1148 WCHAR path[MAX_PATH];
1152 rc = MsiViewFetch(view,&row);
1154 if (rc != ERROR_SUCCESS)
1161 MsiRecordGetStringW(row,1,name,&sz);
1163 /* This helper function now does ALL the work */
1164 TRACE("Dir %s ...\n",debugstr_w(name));
1165 resolve_directory(hPackage,name,path,FALSE);
1166 TRACE("resolves to %s\n",debugstr_w(path));
1168 MsiCloseHandle(row);
1171 MsiCloseHandle(view);
1173 return ERROR_SUCCESS;
1177 * This is a helper function for handling embedded cabinet media
1179 static UINT writeout_cabinet_stream(MSIHANDLE hPackage, WCHAR* stream_name,
1188 rc = read_raw_stream_data(hPackage,stream_name,&data,&size);
1190 if (rc != ERROR_SUCCESS)
1194 if (get_property(hPackage, cszSourceDir, source, &write))
1196 ERR("No Source dir defined \n");
1197 rc = ERROR_FUNCTION_FAILED;
1201 strcatW(source,stream_name);
1202 the_file = CreateFileW(source, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
1203 FILE_ATTRIBUTE_NORMAL, NULL);
1205 if (the_file == INVALID_HANDLE_VALUE)
1207 rc = ERROR_FUNCTION_FAILED;
1211 WriteFile(the_file,data,size,&write,NULL);
1212 CloseHandle(the_file);
1213 TRACE("wrote %li bytes to %s\n",write,debugstr_w(source));
1215 HeapFree(GetProcessHeap(),0,data);
1220 /***********************************************************************
1221 * extract_cabinet_file
1223 * Extract a file from a .cab file.
1225 static BOOL extract_cabinet_file( const WCHAR *cabinet, const WCHAR *root)
1228 static const WCHAR extW[] = {'.','c','a','b',0};
1230 /* from cabinet.h */
1232 long result1; /* 0x000 */
1233 long unknown1[3]; /* 0x004 */
1234 void* filelist; /* 0x010 */
1235 long filecount; /* 0x014 */
1236 long unknown2; /* 0x018 */
1237 char directory[0x104]; /* 0x01c */
1238 char lastfile[0x20c]; /* 0x120 */
1240 extern HRESULT Extract(EXTRACTdest*, LPCSTR);
1242 char *cab_path, *src_path;
1243 int len = strlenW( cabinet );
1246 /* make sure the cabinet file has a .cab extension */
1247 if (len <= 4 || strcmpiW( cabinet + len - 4, extW )) return FALSE;
1249 if (!(cab_path = strdupWtoA( cabinet ))) return FALSE;
1250 if (!(src_path = strdupWtoA( root ))) return FALSE;
1252 memset(&exd,0,sizeof(exd));
1253 strcpy(exd.directory,src_path);
1254 Extract(&exd,cab_path);
1255 HeapFree( GetProcessHeap(), 0, cab_path );
1256 HeapFree( GetProcessHeap(), 0, src_path );
1260 static UINT ready_media_for_file(MSIHANDLE hPackage, UINT sequence,
1266 WCHAR source[MAX_PATH];
1267 static const CHAR *ExecSeqQuery =
1268 "select * from Media where LastSequence > %i order by LastSequence";
1273 static INT last_sequence = 0;
1275 if (sequence <= last_sequence)
1277 TRACE("Media already ready (%i, %i)\n",sequence,last_sequence);
1278 return ERROR_SUCCESS;
1281 sprintf(Query,ExecSeqQuery,sequence);
1283 rc = MsiDatabaseOpenViewA(hPackage, Query, &view);
1284 if (rc != ERROR_SUCCESS)
1287 rc = MsiViewExecute(view, 0);
1288 if (rc != ERROR_SUCCESS)
1291 MsiCloseHandle(view);
1295 rc = MsiViewFetch(view,&row);
1296 if (rc != ERROR_SUCCESS)
1299 MsiCloseHandle(view);
1302 seq = MsiRecordGetInteger(row,2);
1303 last_sequence = seq;
1305 if (!MsiRecordIsNull(row,4))
1308 MsiRecordGetStringW(row,4,cab,&sz);
1309 /* the stream does not contain the # character */
1312 writeout_cabinet_stream(hPackage,&cab[1],source);
1313 strcpyW(path,source);
1314 *(strrchrW(path,'\\')+1)=0;
1319 if (get_property(hPackage, cszSourceDir, source, &sz))
1321 ERR("No Source dir defined \n");
1322 rc = ERROR_FUNCTION_FAILED;
1326 strcpyW(path,source);
1327 strcatW(source,cab);
1330 rc = !extract_cabinet_file(source,path);
1332 MsiCloseHandle(row);
1334 MsiCloseHandle(view);
1338 static void reduce_to_longfilename(WCHAR* filename)
1340 if (strchrW(filename,'|'))
1342 WCHAR newname[MAX_PATH];
1343 strcpyW(newname,strchrW(filename,'|')+1);
1344 strcpyW(filename,newname);
1348 static UINT get_directory_for_component(MSIHANDLE hPackage,
1349 const WCHAR* component, WCHAR* install_path)
1354 WCHAR ExecSeqQuery[1023] =
1355 {'s','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','C','o','m'
1356 ,'p','o','n','e','n','t',' ','w','h','e','r','e',' ','C','o','m'
1357 ,'p','o','n','e','n','t',' ','=',' ','`',0};
1358 static const WCHAR end[]={'`',0};
1362 strcatW(ExecSeqQuery,component);
1363 strcatW(ExecSeqQuery,end);
1365 rc = MsiDatabaseOpenViewW(hPackage, ExecSeqQuery, &view);
1367 if (rc != ERROR_SUCCESS)
1370 rc = MsiViewExecute(view, 0);
1371 if (rc != ERROR_SUCCESS)
1374 MsiCloseHandle(view);
1378 rc = MsiViewFetch(view,&row);
1379 if (rc != ERROR_SUCCESS)
1382 MsiCloseHandle(view);
1383 MsiCloseHandle(row);
1388 MsiRecordGetStringW(row,3,dir,&sz);
1390 rc = get_property(hPackage, dir, install_path, &sz);
1392 MsiCloseHandle(row);
1394 MsiCloseHandle(view);
1398 static UINT ACTION_InstallFiles(MSIHANDLE hPackage)
1403 static const CHAR *ExecSeqQuery =
1404 "select * from File order by Sequence";
1406 /* REALLY what we want to do is go through all the enabled
1407 * features and check all the components of that feature and
1408 * make sure that component is not already install and blah
1409 * blah blah... I will do it that way some day.. really
1410 * but for sheer gratification I am going to just brute force
1411 * install all the files
1414 rc = MsiDatabaseOpenViewA(hPackage, ExecSeqQuery, &view);
1415 if (rc != ERROR_SUCCESS)
1418 rc = MsiViewExecute(view, 0);
1419 if (rc != ERROR_SUCCESS)
1422 MsiCloseHandle(view);
1429 WCHAR component[0x100];
1430 WCHAR install_path[MAX_PATH];
1431 WCHAR path_to_source[MAX_PATH];
1432 WCHAR src_path[MAX_PATH];
1433 WCHAR filename[0x100];
1434 WCHAR sourcename[0x100];
1437 rc = MsiViewFetch(view,&row);
1438 if (rc != ERROR_SUCCESS)
1444 seq = MsiRecordGetInteger(row,8);
1445 rc = ready_media_for_file(hPackage,seq,path_to_source);
1446 if (rc != ERROR_SUCCESS)
1448 ERR("Unable to ready media\n");
1449 MsiCloseHandle(row);
1453 rc = MsiRecordGetStringW(row,2,component,&sz);
1454 if (rc != ERROR_SUCCESS)
1456 ERR("Unable to read component\n");
1457 MsiCloseHandle(row);
1460 rc = get_directory_for_component(hPackage,component,install_path);
1461 if (rc != ERROR_SUCCESS)
1463 ERR("Unable to get directory\n");
1464 MsiCloseHandle(row);
1469 rc = MsiRecordGetStringW(row,1,sourcename,&sz);
1470 if (rc != ERROR_SUCCESS)
1472 ERR("Unable to get sourcename\n");
1473 MsiCloseHandle(row);
1476 strcpyW(src_path,path_to_source);
1477 strcatW(src_path,sourcename);
1480 rc = MsiRecordGetStringW(row,3,filename,&sz);
1481 if (rc != ERROR_SUCCESS)
1483 ERR("Unable to get filename\n");
1484 MsiCloseHandle(row);
1487 reduce_to_longfilename(filename);
1488 strcatW(install_path,filename);
1490 TRACE("Installing file %s to %s\n",debugstr_w(src_path),
1491 debugstr_w(install_path));
1493 rc = !MoveFileW(src_path,install_path);
1496 ERR("Unable to move file\n");
1499 /* for future use lets keep track of this file and where it went */
1500 set_property(hPackage,sourcename,install_path);
1502 MsiCloseHandle(row);
1505 MsiCloseHandle(view);
1510 static UINT ACTION_DuplicateFiles(MSIHANDLE hPackage)
1515 static const CHAR *ExecSeqQuery = "select * from DuplicateFile";
1519 * Yes we should only do this for componenets that are installed
1520 * but again I need to do that went I track components.
1523 rc = MsiDatabaseOpenViewA(hPackage, ExecSeqQuery, &view);
1524 if (rc != ERROR_SUCCESS)
1527 rc = MsiViewExecute(view, 0);
1528 if (rc != ERROR_SUCCESS)
1531 MsiCloseHandle(view);
1537 WCHAR file_key[0x100];
1538 WCHAR file_source[MAX_PATH];
1539 WCHAR dest_name[0x100];
1540 WCHAR dest_path[MAX_PATH];
1544 rc = MsiViewFetch(view,&row);
1545 if (rc != ERROR_SUCCESS)
1552 rc = MsiRecordGetStringW(row,3,file_key,&sz);
1553 if (rc != ERROR_SUCCESS)
1555 ERR("Unable to get file key\n");
1556 MsiCloseHandle(row);
1561 rc = get_property(hPackage,file_key,file_source,&sz);
1562 if (rc != ERROR_SUCCESS)
1564 ERR("Original file unknown %s\n",debugstr_w(file_key));
1565 MsiCloseHandle(row);
1569 if (MsiRecordIsNull(row,4))
1570 strcpyW(dest_name,strrchrW(file_source,'\\')+1);
1574 MsiRecordGetStringW(row,4,dest_name,&sz);
1575 reduce_to_longfilename(dest_name);
1578 if (MsiRecordIsNull(row,5))
1580 strcpyW(dest_path,file_source);
1581 *strrchrW(dest_path,'\\')=0;
1585 WCHAR destkey[0x100];
1587 MsiRecordGetStringW(row,5,destkey,&sz);
1589 rc = get_property(hPackage, destkey, dest_path, &sz);
1590 if (rc != ERROR_SUCCESS)
1592 ERR("Unable to get destination folder\n");
1593 MsiCloseHandle(row);
1598 strcatW(dest_path,dest_name);
1600 TRACE("Duplicating file %s to %s\n",debugstr_w(file_source),
1601 debugstr_w(dest_path));
1603 if (strcmpW(file_source,dest_path))
1604 rc = !CopyFileW(file_source,dest_path,TRUE);
1608 if (rc != ERROR_SUCCESS)
1609 ERR("Failed to copy file\n");
1611 MsiCloseHandle(row);
1614 MsiCloseHandle(view);
1620 static LPSTR parse_value(MSIHANDLE hPackage, WCHAR *value, DWORD *type,
1624 if (value[0]=='#' && value[1]!='#')
1626 ERR("UNHANDLED VALUE TYPE\n");
1637 *size = deformat_string(hPackage, ptr,(LPWSTR*)&data);
1642 static UINT ACTION_WriteRegistryValues(MSIHANDLE hPackage)
1647 static const CHAR *ExecSeqQuery = "select * from Registry";
1649 /* Again here we want to key off of the components being installed...
1653 rc = MsiDatabaseOpenViewA(hPackage, ExecSeqQuery, &view);
1654 if (rc != ERROR_SUCCESS)
1657 rc = MsiViewExecute(view, 0);
1658 if (rc != ERROR_SUCCESS)
1661 MsiCloseHandle(view);
1670 LPSTR value_data = NULL;
1671 HKEY root_key, hkey;
1677 rc = MsiViewFetch(view,&row);
1678 if (rc != ERROR_SUCCESS)
1684 /* null values have special meanings during uninstalls and such */
1686 if(MsiRecordIsNull(row,5))
1688 MsiCloseHandle(row);
1692 root = MsiRecordGetInteger(row,2);
1694 MsiRecordGetStringW(row,3,key,&sz);
1697 if (MsiRecordIsNull(row,4))
1700 MsiRecordGetStringW(row,4,name,&sz);
1703 MsiRecordGetStringW(row,5,value,&sz);
1706 /* get the root key */
1709 case 0: root_key = HKEY_CLASSES_ROOT; break;
1710 case 1: root_key = HKEY_CURRENT_USER; break;
1711 case 2: root_key = HKEY_LOCAL_MACHINE; break;
1712 case 3: root_key = HKEY_USERS; break;
1714 ERR("Unknown root %i\n",root);
1720 MsiCloseHandle(row);
1724 if (RegCreateKeyW( root_key, key, &hkey))
1726 ERR("Could not create key %s\n",debugstr_w(key));
1727 MsiCloseHandle(row);
1731 value_data = parse_value(hPackage, value, &type, &size);
1735 TRACE("Setting value %s\n",debugstr_w(name));
1736 RegSetValueExW(hkey, name, 0, type, value_data, size);
1737 HeapFree(GetProcessHeap(),0,value_data);
1740 MsiCloseHandle(row);
1743 MsiCloseHandle(view);
1748 * This helper function should probably go alot of places
1750 static DWORD deformat_string(MSIHANDLE hPackage, WCHAR* ptr,WCHAR** data)
1759 /* scan for special characters */
1760 if (!strchrW(ptr,'[') || (strchrW(ptr,'[') && !strchrW(ptr,']')))
1763 size = (strlenW(ptr)+1) * sizeof(WCHAR);
1764 *data = HeapAlloc(GetProcessHeap(),0,size);
1769 /* formatted string located */
1770 mark = strchrW(ptr,'[');
1773 INT cnt = (mark - ptr);
1774 TRACE("%i (%i) characters before marker\n",cnt,(mark-ptr));
1775 size = cnt * sizeof(WCHAR);
1776 size += sizeof(WCHAR);
1777 *data = HeapAlloc(GetProcessHeap(),0,size);
1778 strncpyW(*data,ptr,cnt);
1783 size = sizeof(WCHAR);
1784 *data = HeapAlloc(GetProcessHeap(),0,size);
1789 *strchrW(key,']')=0;
1790 mark = strchrW(mark,']');
1792 TRACE("Current %s .. %s\n",debugstr_w(*data),debugstr_w(mark));
1794 if (get_property(hPackage, key, value,&sz) == ERROR_SUCCESS)
1797 chunk = (strlenW(value)+1) * sizeof(WCHAR);
1799 newdata = HeapReAlloc(GetProcessHeap(),0,*data,size);
1801 strcatW(*data,value);
1803 TRACE("Current %s .. %s\n",debugstr_w(*data),debugstr_w(mark));
1807 chunk = (strlenW(mark)+1) * sizeof(WCHAR);
1809 newdata = HeapReAlloc(GetProcessHeap(),0,*data,size);
1811 strcatW(*data,mark);
1813 (*data)[strlenW(*data)]=0;
1814 TRACE("Current %s .. %s\n",debugstr_w(*data),debugstr_w(mark));
1816 /* recursivly do this to clean up */
1817 mark = HeapAlloc(GetProcessHeap(),0,size);
1818 strcpyW(mark,*data);
1819 TRACE("String at this point %s\n",debugstr_w(mark));
1820 size = deformat_string(hPackage,mark,data);
1821 HeapFree(GetProcessHeap(),0,mark);
1825 /* Msi functions that seem approperate here */
1826 UINT WINAPI MsiDoActionA( MSIHANDLE hInstall, LPCSTR szAction )
1831 TRACE(" exteral attempt at action %s\n",szAction);
1834 return ERROR_FUNCTION_FAILED;
1836 return ERROR_FUNCTION_FAILED;
1838 len = MultiByteToWideChar( CP_ACP, 0, szAction, -1, NULL, 0);
1839 szwAction = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
1842 return ERROR_FUNCTION_FAILED;
1844 MultiByteToWideChar( CP_ACP, 0, szAction, -1, szwAction, len);
1846 rc = MsiDoActionW(hInstall, szwAction);
1847 HeapFree(GetProcessHeap(),0,szwAction);
1851 UINT WINAPI MsiDoActionW( MSIHANDLE hInstall, LPCWSTR szAction )
1853 TRACE(" exteral attempt at action %s \n",debugstr_w(szAction));
1854 return ACTION_PerformAction(hInstall,szAction);
1857 UINT WINAPI MsiGetTargetPathA( MSIHANDLE hInstall, LPCSTR szFolder,
1858 LPSTR szPathBuf, DWORD* pcchPathBuf)
1864 TRACE("getting folder %s %p %li\n",szFolder,szPathBuf, *pcchPathBuf);
1867 return ERROR_FUNCTION_FAILED;
1869 return ERROR_FUNCTION_FAILED;
1871 len = MultiByteToWideChar( CP_ACP, 0, szFolder, -1, NULL, 0);
1872 szwFolder= HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
1875 return ERROR_FUNCTION_FAILED;
1877 szwPathBuf = HeapAlloc( GetProcessHeap(), 0 , *pcchPathBuf * sizeof(WCHAR));
1879 MultiByteToWideChar( CP_ACP, 0, szFolder, -1, szwFolder, len);
1881 rc = MsiGetTargetPathW(hInstall, szwFolder, szwPathBuf,pcchPathBuf);
1883 WideCharToMultiByte( CP_ACP, 0, szwPathBuf, *pcchPathBuf, szPathBuf,
1884 *pcchPathBuf, NULL, NULL );
1886 HeapFree(GetProcessHeap(),0,szwFolder);
1887 HeapFree(GetProcessHeap(),0,szwPathBuf);
1892 UINT WINAPI MsiGetTargetPathW( MSIHANDLE hInstall, LPCWSTR szFolder, LPWSTR
1893 szPathBuf, DWORD* pcchPathBuf)
1895 TRACE("(%s %p %li)\n",debugstr_w(szFolder),szPathBuf,*pcchPathBuf);
1896 return get_property(hInstall,szFolder,szPathBuf,pcchPathBuf);
1901 static UINT ACTION_Template(MSIHANDLE hPackage)
1906 static const CHAR *ExecSeqQuery;
1908 rc = MsiDatabaseOpenViewA(hPackage, ExecSeqQuery, &view);
1909 if (rc != ERROR_SUCCESS)
1912 rc = MsiViewExecute(view, 0);
1913 if (rc != ERROR_SUCCESS)
1916 MsiCloseHandle(view);
1922 rc = MsiViewFetch(view,&row);
1923 if (rc != ERROR_SUCCESS)
1929 MsiCloseHandle(row);
1932 MsiCloseHandle(view);