2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 #include "wine/debug.h"
40 #include "wine/unicode.h"
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
48 static const WCHAR szCreateFolders[] =
49 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
50 static const WCHAR szCostFinalize[] =
51 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
52 static const WCHAR szWriteRegistryValues[] =
53 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
54 static const WCHAR szFileCost[] =
55 {'F','i','l','e','C','o','s','t',0};
56 static const WCHAR szInstallInitialize[] =
57 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
58 static const WCHAR szInstallValidate[] =
59 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
60 static const WCHAR szLaunchConditions[] =
61 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
62 static const WCHAR szProcessComponents[] =
63 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
64 static const WCHAR szRegisterTypeLibraries[] =
65 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
66 static const WCHAR szCreateShortcuts[] =
67 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
68 static const WCHAR szPublishProduct[] =
69 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
70 static const WCHAR szWriteIniValues[] =
71 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
72 static const WCHAR szSelfRegModules[] =
73 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
74 static const WCHAR szPublishFeatures[] =
75 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
76 static const WCHAR szRegisterProduct[] =
77 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
78 static const WCHAR szInstallExecute[] =
79 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
80 static const WCHAR szInstallExecuteAgain[] =
81 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
82 static const WCHAR szInstallFinalize[] =
83 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
84 static const WCHAR szForceReboot[] =
85 {'F','o','r','c','e','R','e','b','o','o','t',0};
86 static const WCHAR szResolveSource[] =
87 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
88 static const WCHAR szAllocateRegistrySpace[] =
89 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
90 static const WCHAR szBindImage[] =
91 {'B','i','n','d','I','m','a','g','e',0};
92 static const WCHAR szDeleteServices[] =
93 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
94 static const WCHAR szDisableRollback[] =
95 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
96 static const WCHAR szExecuteAction[] =
97 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
98 static const WCHAR szInstallAdminPackage[] =
99 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
100 static const WCHAR szInstallSFPCatalogFile[] =
101 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
102 static const WCHAR szIsolateComponents[] =
103 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
104 static const WCHAR szMigrateFeatureStates[] =
105 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
106 static const WCHAR szMsiUnpublishAssemblies[] =
107 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
108 static const WCHAR szInstallODBC[] =
109 {'I','n','s','t','a','l','l','O','D','B','C',0};
110 static const WCHAR szInstallServices[] =
111 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
112 static const WCHAR szPublishComponents[] =
113 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
114 static const WCHAR szRegisterComPlus[] =
115 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
116 static const WCHAR szRegisterUser[] =
117 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
118 static const WCHAR szRemoveEnvironmentStrings[] =
119 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
120 static const WCHAR szRemoveExistingProducts[] =
121 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
122 static const WCHAR szRemoveFolders[] =
123 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
124 static const WCHAR szRemoveIniValues[] =
125 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
126 static const WCHAR szRemoveODBC[] =
127 {'R','e','m','o','v','e','O','D','B','C',0};
128 static const WCHAR szRemoveRegistryValues[] =
129 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
130 static const WCHAR szRemoveShortcuts[] =
131 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
132 static const WCHAR szRMCCPSearch[] =
133 {'R','M','C','C','P','S','e','a','r','c','h',0};
134 static const WCHAR szScheduleReboot[] =
135 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
136 static const WCHAR szSelfUnregModules[] =
137 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
138 static const WCHAR szSetODBCFolders[] =
139 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
140 static const WCHAR szStartServices[] =
141 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
142 static const WCHAR szStopServices[] =
143 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
144 static const WCHAR szUnpublishComponents[] =
145 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
146 static const WCHAR szUnpublishFeatures[] =
147 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
148 static const WCHAR szUnregisterComPlus[] =
149 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
150 static const WCHAR szUnregisterTypeLibraries[] =
151 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
152 static const WCHAR szValidateProductID[] =
153 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
154 static const WCHAR szWriteEnvironmentStrings[] =
155 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
157 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
159 static const WCHAR Query_t[] =
160 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
161 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
162 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
163 ' ','\'','%','s','\'',0};
166 row = MSI_QueryGetRecord( package->db, Query_t, action );
169 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
170 msiobj_release(&row->hdr);
173 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
177 static const WCHAR template_s[]=
178 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
180 static const WCHAR template_e[]=
181 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
182 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
184 static const WCHAR format[] =
185 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
189 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
191 sprintfW(message,template_s,timet,action);
193 sprintfW(message,template_e,timet,action,rc);
195 row = MSI_CreateRecord(1);
196 MSI_RecordSetStringW(row,1,message);
198 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
199 msiobj_release(&row->hdr);
209 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
211 enum parse_state state = state_quote;
214 int ignore, in_quotes = 0, count = 0, len = 0;
216 for (p = str; *p; p++)
221 case state_whitespace:
231 if (in_quotes && p[1] != '\"') count--;
236 if (!count) in_quotes = 0;
248 if (in_quotes) count--;
252 state = state_whitespace;
253 if (!count) goto done;
258 if (!count) in_quotes = 0;
269 if (in_quotes && p[1] != '\"') count--;
273 state = state_whitespace;
274 if (!count || (count > 1 && !len)) goto done;
280 if (!count) in_quotes = 0;
289 if (!ignore) *out++ = *p;
293 if (!len) *value = 0;
300 static void remove_quotes( WCHAR *str )
303 int len = strlenW( str );
305 while ((p = strchrW( p, '"' )))
307 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
312 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
322 return ERROR_SUCCESS;
327 while (*ptr == ' ') ptr++;
330 ptr2 = strchrW( ptr, '=' );
331 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
334 if (!len) return ERROR_INVALID_COMMAND_LINE;
336 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
337 memcpy( prop, ptr, len * sizeof(WCHAR) );
339 if (!preserve_case) struprW( prop );
342 while (*ptr2 == ' ') ptr2++;
345 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
346 len = parse_prop( ptr2, val, &num_quotes );
349 WARN("unbalanced quotes\n");
352 return ERROR_INVALID_COMMAND_LINE;
354 remove_quotes( val );
355 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
357 r = msi_set_property( package->db, prop, val );
358 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
359 msi_reset_folders( package, TRUE );
367 return ERROR_SUCCESS;
370 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
373 LPWSTR p, *ret = NULL;
379 /* count the number of substrings */
380 for ( pc = str, count = 0; pc; count++ )
382 pc = strchrW( pc, sep );
387 /* allocate space for an array of substring pointers and the substrings */
388 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
389 (lstrlenW(str)+1) * sizeof(WCHAR) );
393 /* copy the string and set the pointers */
394 p = (LPWSTR) &ret[count+1];
396 for( count = 0; (ret[count] = p); count++ )
398 p = strchrW( p, sep );
406 static BOOL ui_sequence_exists( MSIPACKAGE *package )
408 static const WCHAR query [] = {
409 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
410 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
411 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
412 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
416 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
417 if (rc == ERROR_SUCCESS)
419 msiobj_release(&view->hdr);
425 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
427 LPWSTR source, check;
429 if (msi_get_property_int( package->db, szInstalled, 0 ))
433 MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE );
434 source = msi_reg_get_val_str( hkey, INSTALLPROPERTY_INSTALLSOURCEW );
442 db = msi_dup_property( package->db, szOriginalDatabase );
444 return ERROR_OUTOFMEMORY;
446 p = strrchrW( db, '\\' );
449 p = strrchrW( db, '/' );
453 return ERROR_SUCCESS;
458 source = msi_alloc( len * sizeof(WCHAR) );
459 lstrcpynW( source, db, len );
463 check = msi_dup_property( package->db, szSourceDir );
464 if (!check || replace)
466 UINT r = msi_set_property( package->db, szSourceDir, source );
467 if (r == ERROR_SUCCESS)
468 msi_reset_folders( package, TRUE );
472 check = msi_dup_property( package->db, szSOURCEDIR );
473 if (!check || replace)
474 msi_set_property( package->db, szSOURCEDIR, source );
479 return ERROR_SUCCESS;
482 static BOOL needs_ui_sequence(MSIPACKAGE *package)
484 INT level = msi_get_property_int(package->db, szUILevel, 0);
485 return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
488 UINT msi_set_context(MSIPACKAGE *package)
490 UINT r = msi_locate_product( package->ProductCode, &package->Context );
491 if (r != ERROR_SUCCESS)
493 int num = msi_get_property_int( package->db, szAllUsers, 0 );
494 if (num == 1 || num == 2)
495 package->Context = MSIINSTALLCONTEXT_MACHINE;
497 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
499 return ERROR_SUCCESS;
502 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
505 LPCWSTR cond, action;
506 MSIPACKAGE *package = param;
508 action = MSI_RecordGetString(row,1);
511 ERR("Error is retrieving action name\n");
512 return ERROR_FUNCTION_FAILED;
515 /* check conditions */
516 cond = MSI_RecordGetString(row,2);
518 /* this is a hack to skip errors in the condition code */
519 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
521 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
522 return ERROR_SUCCESS;
525 if (needs_ui_sequence(package))
526 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
528 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
530 msi_dialog_check_messages( NULL );
532 if (package->CurrentInstallState != ERROR_SUCCESS)
533 rc = package->CurrentInstallState;
535 if (rc == ERROR_FUNCTION_NOT_CALLED)
538 if (rc != ERROR_SUCCESS)
539 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
544 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
546 static const WCHAR query[] = {
547 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
548 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
549 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
550 '`','S','e','q','u','e','n','c','e','`',0};
554 TRACE("%p %s\n", package, debugstr_w(table));
556 r = MSI_OpenQuery( package->db, &view, query, table );
557 if (r == ERROR_SUCCESS)
559 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
560 msiobj_release(&view->hdr);
565 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
567 static const WCHAR query[] = {
568 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
569 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
570 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
571 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
572 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
573 static const WCHAR query_validate[] = {
574 'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
575 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
576 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
577 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
578 ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0};
583 if (package->script->ExecuteSequenceRun)
585 TRACE("Execute Sequence already Run\n");
586 return ERROR_SUCCESS;
589 package->script->ExecuteSequenceRun = TRUE;
591 /* get the sequence number */
594 MSIRECORD *row = MSI_QueryGetRecord(package->db, query_validate);
595 if (!row) return ERROR_FUNCTION_FAILED;
596 seq = MSI_RecordGetInteger(row,1);
597 msiobj_release(&row->hdr);
599 rc = MSI_OpenQuery(package->db, &view, query, seq);
600 if (rc == ERROR_SUCCESS)
602 TRACE("Running the actions\n");
604 msi_set_property(package->db, szSourceDir, NULL);
605 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
606 msiobj_release(&view->hdr);
611 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
613 static const WCHAR query[] = {
614 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
615 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
616 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
617 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
621 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
622 if (rc == ERROR_SUCCESS)
624 TRACE("Running the actions\n");
625 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
626 msiobj_release(&view->hdr);
631 /********************************************************
632 * ACTION helper functions and functions that perform the actions
633 *******************************************************/
634 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
635 UINT* rc, UINT script, BOOL force )
640 arc = ACTION_CustomAction(package, action, script, force);
642 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
650 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
654 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
656 if (!strcmpW( Component, comp->Component )) return comp;
661 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
665 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
667 if (!strcmpW( Feature, feature->Feature )) return feature;
672 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
676 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
678 if (!strcmpW( key, file->File )) return file;
683 MSIFILEPATCH *msi_get_loaded_filepatch( MSIPACKAGE *package, const WCHAR *key )
687 /* FIXME: There might be more than one patch */
688 LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry )
690 if (!strcmpW( key, patch->File->File )) return patch;
695 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
699 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
701 if (!strcmpW( dir, folder->Directory )) return folder;
707 * Recursively create all directories in the path.
708 * shamelessly stolen from setupapi/queue.c
710 BOOL msi_create_full_path( const WCHAR *path )
716 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
717 strcpyW( new_path, path );
719 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
720 new_path[len - 1] = 0;
722 while (!CreateDirectoryW( new_path, NULL ))
725 DWORD last_error = GetLastError();
726 if (last_error == ERROR_ALREADY_EXISTS) break;
727 if (last_error != ERROR_PATH_NOT_FOUND)
732 if (!(slash = strrchrW( new_path, '\\' )))
737 len = slash - new_path;
739 if (!msi_create_full_path( new_path ))
744 new_path[len] = '\\';
746 msi_free( new_path );
750 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
754 row = MSI_CreateRecord( 4 );
755 MSI_RecordSetInteger( row, 1, a );
756 MSI_RecordSetInteger( row, 2, b );
757 MSI_RecordSetInteger( row, 3, c );
758 MSI_RecordSetInteger( row, 4, d );
759 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
760 msiobj_release( &row->hdr );
762 msi_dialog_check_messages( NULL );
765 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
767 static const WCHAR query[] =
768 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
769 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
770 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
775 if (!package->LastAction || strcmpW( package->LastAction, action ))
777 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
779 if (MSI_RecordIsNull( row, 3 ))
781 msiobj_release( &row->hdr );
784 /* update the cached action format */
785 msi_free( package->ActionFormat );
786 package->ActionFormat = msi_dup_record_field( row, 3 );
787 msi_free( package->LastAction );
788 package->LastAction = strdupW( action );
789 msiobj_release( &row->hdr );
792 MSI_RecordSetStringW( record, 0, package->ActionFormat );
793 MSI_FormatRecordW( package, record, message, &size );
794 row = MSI_CreateRecord( 1 );
795 MSI_RecordSetStringW( row, 1, message );
796 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
797 msiobj_release( &row->hdr );
800 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
804 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
805 return INSTALLSTATE_UNKNOWN;
807 if (package->need_rollback) return comp->Installed;
808 return comp->ActionRequest;
811 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
813 if (package->need_rollback) return feature->Installed;
814 return feature->ActionRequest;
817 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
819 MSIPACKAGE *package = param;
820 LPCWSTR dir, component, full_path;
825 component = MSI_RecordGetString(row, 2);
827 return ERROR_SUCCESS;
829 comp = msi_get_loaded_component(package, component);
831 return ERROR_SUCCESS;
833 comp->Action = msi_get_component_action( package, comp );
834 if (comp->Action != INSTALLSTATE_LOCAL)
836 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
837 return ERROR_SUCCESS;
840 dir = MSI_RecordGetString(row,1);
843 ERR("Unable to get folder id\n");
844 return ERROR_SUCCESS;
847 uirow = MSI_CreateRecord(1);
848 MSI_RecordSetStringW(uirow, 1, dir);
849 msi_ui_actiondata(package, szCreateFolders, uirow);
850 msiobj_release(&uirow->hdr);
852 full_path = msi_get_target_folder( package, dir );
855 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
856 return ERROR_SUCCESS;
858 TRACE("folder is %s\n", debugstr_w(full_path));
860 folder = msi_get_loaded_folder( package, dir );
861 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
862 folder->State = FOLDER_STATE_CREATED;
863 return ERROR_SUCCESS;
866 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
868 static const WCHAR query[] = {
869 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
870 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
874 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
875 if (rc != ERROR_SUCCESS)
876 return ERROR_SUCCESS;
878 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
879 msiobj_release(&view->hdr);
883 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
885 MSIPACKAGE *package = param;
886 LPCWSTR dir, component, full_path;
891 component = MSI_RecordGetString(row, 2);
893 return ERROR_SUCCESS;
895 comp = msi_get_loaded_component(package, component);
897 return ERROR_SUCCESS;
899 comp->Action = msi_get_component_action( package, comp );
900 if (comp->Action != INSTALLSTATE_ABSENT)
902 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
903 return ERROR_SUCCESS;
906 dir = MSI_RecordGetString( row, 1 );
909 ERR("Unable to get folder id\n");
910 return ERROR_SUCCESS;
913 full_path = msi_get_target_folder( package, dir );
916 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
917 return ERROR_SUCCESS;
919 TRACE("folder is %s\n", debugstr_w(full_path));
921 uirow = MSI_CreateRecord( 1 );
922 MSI_RecordSetStringW( uirow, 1, dir );
923 msi_ui_actiondata( package, szRemoveFolders, uirow );
924 msiobj_release( &uirow->hdr );
926 RemoveDirectoryW( full_path );
927 folder = msi_get_loaded_folder( package, dir );
928 folder->State = FOLDER_STATE_REMOVED;
929 return ERROR_SUCCESS;
932 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
934 static const WCHAR query[] = {
935 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
936 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
940 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
941 if (rc != ERROR_SUCCESS)
942 return ERROR_SUCCESS;
944 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
945 msiobj_release( &view->hdr );
949 static UINT load_component( MSIRECORD *row, LPVOID param )
951 MSIPACKAGE *package = param;
954 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
956 return ERROR_FUNCTION_FAILED;
958 list_add_tail( &package->components, &comp->entry );
960 /* fill in the data */
961 comp->Component = msi_dup_record_field( row, 1 );
963 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
965 comp->ComponentId = msi_dup_record_field( row, 2 );
966 comp->Directory = msi_dup_record_field( row, 3 );
967 comp->Attributes = MSI_RecordGetInteger(row,4);
968 comp->Condition = msi_dup_record_field( row, 5 );
969 comp->KeyPath = msi_dup_record_field( row, 6 );
971 comp->Installed = INSTALLSTATE_UNKNOWN;
972 comp->Action = INSTALLSTATE_UNKNOWN;
973 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
975 comp->assembly = msi_load_assembly( package, comp );
976 return ERROR_SUCCESS;
979 UINT msi_load_all_components( MSIPACKAGE *package )
981 static const WCHAR query[] = {
982 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
983 '`','C','o','m','p','o','n','e','n','t','`',0};
987 if (!list_empty(&package->components))
988 return ERROR_SUCCESS;
990 r = MSI_DatabaseOpenViewW( package->db, query, &view );
991 if (r != ERROR_SUCCESS)
994 if (!msi_init_assembly_caches( package ))
996 ERR("can't initialize assembly caches\n");
997 msiobj_release( &view->hdr );
998 return ERROR_FUNCTION_FAILED;
1001 r = MSI_IterateRecords(view, NULL, load_component, package);
1002 msiobj_release(&view->hdr);
1003 msi_destroy_assembly_caches( package );
1008 MSIPACKAGE *package;
1009 MSIFEATURE *feature;
1012 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1016 cl = msi_alloc( sizeof (*cl) );
1018 return ERROR_NOT_ENOUGH_MEMORY;
1019 cl->component = comp;
1020 list_add_tail( &feature->Components, &cl->entry );
1022 return ERROR_SUCCESS;
1025 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1029 fl = msi_alloc( sizeof(*fl) );
1031 return ERROR_NOT_ENOUGH_MEMORY;
1032 fl->feature = child;
1033 list_add_tail( &parent->Children, &fl->entry );
1035 return ERROR_SUCCESS;
1038 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1040 _ilfs* ilfs = param;
1044 component = MSI_RecordGetString(row,1);
1046 /* check to see if the component is already loaded */
1047 comp = msi_get_loaded_component( ilfs->package, component );
1050 WARN("ignoring unknown component %s\n", debugstr_w(component));
1051 return ERROR_SUCCESS;
1053 add_feature_component( ilfs->feature, comp );
1054 comp->Enabled = TRUE;
1056 return ERROR_SUCCESS;
1059 static UINT load_feature(MSIRECORD * row, LPVOID param)
1061 static const WCHAR query[] = {
1062 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1063 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1064 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1065 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1066 MSIPACKAGE *package = param;
1067 MSIFEATURE *feature;
1072 /* fill in the data */
1074 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1076 return ERROR_NOT_ENOUGH_MEMORY;
1078 list_init( &feature->Children );
1079 list_init( &feature->Components );
1081 feature->Feature = msi_dup_record_field( row, 1 );
1083 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1085 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1086 feature->Title = msi_dup_record_field( row, 3 );
1087 feature->Description = msi_dup_record_field( row, 4 );
1089 if (!MSI_RecordIsNull(row,5))
1090 feature->Display = MSI_RecordGetInteger(row,5);
1092 feature->Level= MSI_RecordGetInteger(row,6);
1093 feature->Directory = msi_dup_record_field( row, 7 );
1094 feature->Attributes = MSI_RecordGetInteger(row,8);
1096 feature->Installed = INSTALLSTATE_UNKNOWN;
1097 feature->Action = INSTALLSTATE_UNKNOWN;
1098 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1100 list_add_tail( &package->features, &feature->entry );
1102 /* load feature components */
1104 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1105 if (rc != ERROR_SUCCESS)
1106 return ERROR_SUCCESS;
1108 ilfs.package = package;
1109 ilfs.feature = feature;
1111 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1112 msiobj_release(&view->hdr);
1116 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1118 MSIPACKAGE *package = param;
1119 MSIFEATURE *parent, *child;
1121 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1123 return ERROR_FUNCTION_FAILED;
1125 if (!child->Feature_Parent)
1126 return ERROR_SUCCESS;
1128 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1130 return ERROR_FUNCTION_FAILED;
1132 add_feature_child( parent, child );
1133 return ERROR_SUCCESS;
1136 UINT msi_load_all_features( MSIPACKAGE *package )
1138 static const WCHAR query[] = {
1139 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1140 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1141 '`','D','i','s','p','l','a','y','`',0};
1145 if (!list_empty(&package->features))
1146 return ERROR_SUCCESS;
1148 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1149 if (r != ERROR_SUCCESS)
1152 r = MSI_IterateRecords( view, NULL, load_feature, package );
1153 if (r != ERROR_SUCCESS)
1155 msiobj_release( &view->hdr );
1158 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1159 msiobj_release( &view->hdr );
1163 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1174 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1176 static const WCHAR query[] = {
1177 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1178 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1179 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1180 MSIQUERY *view = NULL;
1181 MSIRECORD *row = NULL;
1184 TRACE("%s\n", debugstr_w(file->File));
1186 r = MSI_OpenQuery(package->db, &view, query, file->File);
1187 if (r != ERROR_SUCCESS)
1190 r = MSI_ViewExecute(view, NULL);
1191 if (r != ERROR_SUCCESS)
1194 r = MSI_ViewFetch(view, &row);
1195 if (r != ERROR_SUCCESS)
1198 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1199 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1200 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1201 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1202 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1205 if (view) msiobj_release(&view->hdr);
1206 if (row) msiobj_release(&row->hdr);
1210 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1213 static const WCHAR query[] = {
1214 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1215 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1216 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1218 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1221 WARN("query failed\n");
1222 return ERROR_FUNCTION_FAILED;
1225 file->disk_id = MSI_RecordGetInteger( row, 1 );
1226 msiobj_release( &row->hdr );
1227 return ERROR_SUCCESS;
1230 static UINT load_file(MSIRECORD *row, LPVOID param)
1232 MSIPACKAGE* package = param;
1236 /* fill in the data */
1238 file = msi_alloc_zero( sizeof (MSIFILE) );
1240 return ERROR_NOT_ENOUGH_MEMORY;
1242 file->File = msi_dup_record_field( row, 1 );
1244 component = MSI_RecordGetString( row, 2 );
1245 file->Component = msi_get_loaded_component( package, component );
1247 if (!file->Component)
1249 WARN("Component not found: %s\n", debugstr_w(component));
1250 msi_free(file->File);
1252 return ERROR_SUCCESS;
1255 file->FileName = msi_dup_record_field( row, 3 );
1256 msi_reduce_to_long_filename( file->FileName );
1258 file->ShortName = msi_dup_record_field( row, 3 );
1259 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1261 file->FileSize = MSI_RecordGetInteger( row, 4 );
1262 file->Version = msi_dup_record_field( row, 5 );
1263 file->Language = msi_dup_record_field( row, 6 );
1264 file->Attributes = MSI_RecordGetInteger( row, 7 );
1265 file->Sequence = MSI_RecordGetInteger( row, 8 );
1267 file->state = msifs_invalid;
1269 /* if the compressed bits are not set in the file attributes,
1270 * then read the information from the package word count property
1272 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1274 file->IsCompressed = FALSE;
1276 else if (file->Attributes &
1277 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1279 file->IsCompressed = TRUE;
1281 else if (file->Attributes & msidbFileAttributesNoncompressed)
1283 file->IsCompressed = FALSE;
1287 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1290 load_file_hash(package, file);
1291 load_file_disk_id(package, file);
1293 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1295 list_add_tail( &package->files, &file->entry );
1297 return ERROR_SUCCESS;
1300 static UINT load_all_files(MSIPACKAGE *package)
1302 static const WCHAR query[] = {
1303 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1304 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1305 '`','S','e','q','u','e','n','c','e','`', 0};
1309 if (!list_empty(&package->files))
1310 return ERROR_SUCCESS;
1312 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1313 if (rc != ERROR_SUCCESS)
1314 return ERROR_SUCCESS;
1316 rc = MSI_IterateRecords(view, NULL, load_file, package);
1317 msiobj_release(&view->hdr);
1321 static UINT load_media( MSIRECORD *row, LPVOID param )
1323 MSIPACKAGE *package = param;
1324 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1325 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1327 /* FIXME: load external cabinets and directory sources too */
1328 if (!cabinet || cabinet[0] != '#') return ERROR_SUCCESS;
1329 msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1330 return ERROR_SUCCESS;
1333 static UINT load_all_media( MSIPACKAGE *package )
1335 static const WCHAR query[] = {
1336 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1337 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1338 '`','D','i','s','k','I','d','`',0};
1342 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1343 if (r != ERROR_SUCCESS)
1344 return ERROR_SUCCESS;
1346 r = MSI_IterateRecords( view, NULL, load_media, package );
1347 msiobj_release( &view->hdr );
1351 static UINT load_patch(MSIRECORD *row, LPVOID param)
1353 MSIPACKAGE *package = param;
1354 MSIFILEPATCH *patch;
1357 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1359 return ERROR_NOT_ENOUGH_MEMORY;
1361 file_key = msi_dup_record_field( row, 1 );
1362 patch->File = msi_get_loaded_file( package, file_key );
1367 ERR("Failed to find target for patch in File table\n");
1369 return ERROR_FUNCTION_FAILED;
1372 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1374 /* FIXME: The database should be properly transformed */
1375 patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
1377 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1378 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1379 patch->IsApplied = FALSE;
1382 * Header field - for patch validation.
1383 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1386 TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File));
1388 list_add_tail( &package->filepatches, &patch->entry );
1390 return ERROR_SUCCESS;
1393 static UINT load_all_patches(MSIPACKAGE *package)
1395 static const WCHAR query[] = {
1396 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1397 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1398 '`','S','e','q','u','e','n','c','e','`',0};
1402 if (!list_empty(&package->filepatches))
1403 return ERROR_SUCCESS;
1405 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1406 if (rc != ERROR_SUCCESS)
1407 return ERROR_SUCCESS;
1409 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1410 msiobj_release(&view->hdr);
1414 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1416 static const WCHAR query[] = {
1417 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1418 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1419 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1422 folder->persistent = FALSE;
1423 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1425 if (!MSI_ViewExecute( view, NULL ))
1428 if (!MSI_ViewFetch( view, &rec ))
1430 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1431 folder->persistent = TRUE;
1432 msiobj_release( &rec->hdr );
1435 msiobj_release( &view->hdr );
1437 return ERROR_SUCCESS;
1440 static UINT load_folder( MSIRECORD *row, LPVOID param )
1442 MSIPACKAGE *package = param;
1443 static WCHAR szEmpty[] = { 0 };
1444 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1447 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1448 list_init( &folder->children );
1449 folder->Directory = msi_dup_record_field( row, 1 );
1450 folder->Parent = msi_dup_record_field( row, 2 );
1451 p = msi_dup_record_field(row, 3);
1453 TRACE("%s\n", debugstr_w(folder->Directory));
1455 /* split src and target dir */
1457 src_short = folder_split_path( p, ':' );
1459 /* split the long and short paths */
1460 tgt_long = folder_split_path( tgt_short, '|' );
1461 src_long = folder_split_path( src_short, '|' );
1463 /* check for no-op dirs */
1464 if (tgt_short && !strcmpW( szDot, tgt_short ))
1465 tgt_short = szEmpty;
1466 if (src_short && !strcmpW( szDot, src_short ))
1467 src_short = szEmpty;
1470 tgt_long = tgt_short;
1473 src_short = tgt_short;
1474 src_long = tgt_long;
1478 src_long = src_short;
1480 /* FIXME: use the target short path too */
1481 folder->TargetDefault = strdupW(tgt_long);
1482 folder->SourceShortPath = strdupW(src_short);
1483 folder->SourceLongPath = strdupW(src_long);
1486 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1487 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1488 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1490 load_folder_persistence( package, folder );
1492 list_add_tail( &package->folders, &folder->entry );
1493 return ERROR_SUCCESS;
1496 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1500 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1502 list_add_tail( &parent->children, &fl->entry );
1503 return ERROR_SUCCESS;
1506 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1508 MSIPACKAGE *package = param;
1509 MSIFOLDER *parent, *child;
1511 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1512 return ERROR_FUNCTION_FAILED;
1514 if (!child->Parent) return ERROR_SUCCESS;
1516 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1517 return ERROR_FUNCTION_FAILED;
1519 return add_folder_child( parent, child );
1522 static UINT load_all_folders( MSIPACKAGE *package )
1524 static const WCHAR query[] = {
1525 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1526 '`','D','i','r','e','c','t','o','r','y','`',0};
1530 if (!list_empty(&package->folders))
1531 return ERROR_SUCCESS;
1533 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1534 if (r != ERROR_SUCCESS)
1537 r = MSI_IterateRecords( view, NULL, load_folder, package );
1538 if (r != ERROR_SUCCESS)
1540 msiobj_release( &view->hdr );
1543 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1544 msiobj_release( &view->hdr );
1548 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1550 msi_set_property( package->db, szCostingComplete, szZero );
1551 msi_set_property( package->db, szRootDrive, szCRoot );
1553 load_all_folders( package );
1554 msi_load_all_components( package );
1555 msi_load_all_features( package );
1556 load_all_files( package );
1557 load_all_patches( package );
1558 load_all_media( package );
1560 return ERROR_SUCCESS;
1563 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1565 const WCHAR *action = package->script->Actions[script][index];
1566 ui_actionstart( package, action );
1567 TRACE("executing %s\n", debugstr_w(action));
1568 return ACTION_PerformAction( package, action, script );
1571 static UINT execute_script( MSIPACKAGE *package, UINT script )
1573 UINT i, rc = ERROR_SUCCESS;
1575 TRACE("executing script %u\n", script);
1577 if (!package->script)
1579 ERR("no script!\n");
1580 return ERROR_FUNCTION_FAILED;
1582 if (script == SCRIPT_ROLLBACK)
1584 for (i = package->script->ActionCount[script]; i > 0; i--)
1586 rc = execute_script_action( package, script, i - 1 );
1587 if (rc != ERROR_SUCCESS) break;
1592 for (i = 0; i < package->script->ActionCount[script]; i++)
1594 rc = execute_script_action( package, script, i );
1595 if (rc != ERROR_SUCCESS) break;
1598 msi_free_action_script(package, script);
1602 static UINT ACTION_FileCost(MSIPACKAGE *package)
1604 return ERROR_SUCCESS;
1607 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1612 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1614 if (!comp->ComponentId) continue;
1616 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1617 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1619 if (r != ERROR_SUCCESS)
1620 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1621 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1623 if (r != ERROR_SUCCESS)
1624 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1625 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1627 if (r != ERROR_SUCCESS)
1628 comp->Installed = INSTALLSTATE_ABSENT;
1632 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1634 MSIFEATURE *feature;
1636 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1638 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1640 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1641 feature->Installed = INSTALLSTATE_ABSENT;
1643 feature->Installed = state;
1647 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1649 return (feature->Level > 0 && feature->Level <= level);
1652 static BOOL process_state_property(MSIPACKAGE* package, int level,
1653 LPCWSTR property, INSTALLSTATE state)
1656 MSIFEATURE *feature;
1658 override = msi_dup_property( package->db, property );
1662 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1664 if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level ))
1667 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1669 if (!strcmpiW( override, szAll ))
1671 if (feature->Installed != state)
1673 feature->Action = state;
1674 feature->ActionRequest = state;
1679 LPWSTR ptr = override;
1680 LPWSTR ptr2 = strchrW(override,',');
1684 int len = ptr2 - ptr;
1686 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1687 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1689 if (feature->Installed != state)
1691 feature->Action = state;
1692 feature->ActionRequest = state;
1699 ptr2 = strchrW(ptr,',');
1710 static BOOL process_overrides( MSIPACKAGE *package, int level )
1712 static const WCHAR szAddLocal[] =
1713 {'A','D','D','L','O','C','A','L',0};
1714 static const WCHAR szAddSource[] =
1715 {'A','D','D','S','O','U','R','C','E',0};
1716 static const WCHAR szAdvertise[] =
1717 {'A','D','V','E','R','T','I','S','E',0};
1720 /* all these activation/deactivation things happen in order and things
1721 * later on the list override things earlier on the list.
1723 * 0 INSTALLLEVEL processing
1736 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1737 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1738 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1739 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1740 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1743 msi_set_property( package->db, szPreselected, szOne );
1748 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1751 MSICOMPONENT* component;
1752 MSIFEATURE *feature;
1754 TRACE("Checking Install Level\n");
1756 level = msi_get_property_int(package->db, szInstallLevel, 1);
1758 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1760 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1762 if (!is_feature_selected( feature, level )) continue;
1764 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1766 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1768 feature->Action = INSTALLSTATE_SOURCE;
1769 feature->ActionRequest = INSTALLSTATE_SOURCE;
1771 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1773 feature->Action = INSTALLSTATE_ADVERTISED;
1774 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1778 feature->Action = INSTALLSTATE_LOCAL;
1779 feature->ActionRequest = INSTALLSTATE_LOCAL;
1783 /* disable child features of unselected parent or follow parent */
1784 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1788 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1790 if (!is_feature_selected( feature, level ))
1792 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1793 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1795 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1797 fl->feature->Action = feature->Action;
1798 fl->feature->ActionRequest = feature->ActionRequest;
1803 else /* preselected */
1805 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1807 if (!is_feature_selected( feature, level )) continue;
1809 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1811 if (feature->Installed == INSTALLSTATE_ABSENT)
1813 feature->Action = INSTALLSTATE_UNKNOWN;
1814 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1818 feature->Action = feature->Installed;
1819 feature->ActionRequest = feature->Installed;
1825 /* now we want to set component state based based on feature state */
1826 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1830 TRACE("Examining Feature %s (Level %d Installed %d Request %d Action %d)\n",
1831 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1832 feature->ActionRequest, feature->Action);
1834 if (!is_feature_selected( feature, level )) continue;
1836 /* features with components that have compressed files are made local */
1837 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1839 if (cl->component->ForceLocalState &&
1840 feature->ActionRequest == INSTALLSTATE_SOURCE)
1842 feature->Action = INSTALLSTATE_LOCAL;
1843 feature->ActionRequest = INSTALLSTATE_LOCAL;
1848 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1850 component = cl->component;
1852 switch (feature->ActionRequest)
1854 case INSTALLSTATE_ABSENT:
1855 component->anyAbsent = 1;
1857 case INSTALLSTATE_ADVERTISED:
1858 component->hasAdvertiseFeature = 1;
1860 case INSTALLSTATE_SOURCE:
1861 component->hasSourceFeature = 1;
1863 case INSTALLSTATE_LOCAL:
1864 component->hasLocalFeature = 1;
1866 case INSTALLSTATE_DEFAULT:
1867 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1868 component->hasAdvertiseFeature = 1;
1869 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1870 component->hasSourceFeature = 1;
1872 component->hasLocalFeature = 1;
1880 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1882 /* check if it's local or source */
1883 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1884 (component->hasLocalFeature || component->hasSourceFeature))
1886 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1887 !component->ForceLocalState)
1889 component->Action = INSTALLSTATE_SOURCE;
1890 component->ActionRequest = INSTALLSTATE_SOURCE;
1894 component->Action = INSTALLSTATE_LOCAL;
1895 component->ActionRequest = INSTALLSTATE_LOCAL;
1900 /* if any feature is local, the component must be local too */
1901 if (component->hasLocalFeature)
1903 component->Action = INSTALLSTATE_LOCAL;
1904 component->ActionRequest = INSTALLSTATE_LOCAL;
1907 if (component->hasSourceFeature)
1909 component->Action = INSTALLSTATE_SOURCE;
1910 component->ActionRequest = INSTALLSTATE_SOURCE;
1913 if (component->hasAdvertiseFeature)
1915 component->Action = INSTALLSTATE_ADVERTISED;
1916 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1919 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1920 if (component->anyAbsent &&
1921 (component->Installed == INSTALLSTATE_LOCAL || component->Installed == INSTALLSTATE_SOURCE))
1923 component->Action = INSTALLSTATE_ABSENT;
1924 component->ActionRequest = INSTALLSTATE_ABSENT;
1928 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1930 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1932 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1933 component->Action = INSTALLSTATE_LOCAL;
1934 component->ActionRequest = INSTALLSTATE_LOCAL;
1937 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1938 component->Installed == INSTALLSTATE_SOURCE &&
1939 component->hasSourceFeature)
1941 component->Action = INSTALLSTATE_UNKNOWN;
1942 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1945 TRACE("Result: Component %s (Installed %d Request %d Action %d)\n",
1946 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1949 return ERROR_SUCCESS;
1952 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1954 MSIPACKAGE *package = param;
1956 MSIFEATURE *feature;
1958 name = MSI_RecordGetString( row, 1 );
1960 feature = msi_get_loaded_feature( package, name );
1962 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1966 Condition = MSI_RecordGetString(row,3);
1968 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1970 int level = MSI_RecordGetInteger(row,2);
1971 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1972 feature->Level = level;
1975 return ERROR_SUCCESS;
1978 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
1980 static const WCHAR name[] = {'\\',0};
1981 VS_FIXEDFILEINFO *ptr, *ret;
1983 DWORD versize, handle;
1986 versize = GetFileVersionInfoSizeW( filename, &handle );
1990 version = msi_alloc( versize );
1994 GetFileVersionInfoW( filename, 0, versize, version );
1996 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
1998 msi_free( version );
2002 ret = msi_alloc( sz );
2003 memcpy( ret, ptr, sz );
2005 msi_free( version );
2009 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2013 msi_parse_version_string( version, &ms, &ls );
2015 if (fi->dwFileVersionMS > ms) return 1;
2016 else if (fi->dwFileVersionMS < ms) return -1;
2017 else if (fi->dwFileVersionLS > ls) return 1;
2018 else if (fi->dwFileVersionLS < ls) return -1;
2022 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2026 msi_parse_version_string( ver1, &ms1, NULL );
2027 msi_parse_version_string( ver2, &ms2, NULL );
2029 if (ms1 > ms2) return 1;
2030 else if (ms1 < ms2) return -1;
2034 DWORD msi_get_disk_file_size( LPCWSTR filename )
2039 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2040 if (file == INVALID_HANDLE_VALUE)
2041 return INVALID_FILE_SIZE;
2043 size = GetFileSize( file, NULL );
2044 TRACE("size is %u\n", size);
2045 CloseHandle( file );
2049 BOOL msi_file_hash_matches( MSIFILE *file )
2052 MSIFILEHASHINFO hash;
2054 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2055 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2056 if (r != ERROR_SUCCESS)
2059 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2062 static WCHAR *get_temp_dir( void )
2065 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2067 GetTempPathW( MAX_PATH, tmp );
2070 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2071 if (CreateDirectoryW( dir, NULL )) break;
2073 return strdupW( dir );
2077 * msi_build_directory_name()
2079 * This function is to save messing round with directory names
2080 * It handles adding backslashes between path segments,
2081 * and can add \ at the end of the directory name if told to.
2083 * It takes a variable number of arguments.
2084 * It always allocates a new string for the result, so make sure
2085 * to free the return value when finished with it.
2087 * The first arg is the number of path segments that follow.
2088 * The arguments following count are a list of path segments.
2089 * A path segment may be NULL.
2091 * Path segments will be added with a \ separating them.
2092 * A \ will not be added after the last segment, however if the
2093 * last segment is NULL, then the last character will be a \
2095 WCHAR *msi_build_directory_name( DWORD count, ... )
2101 va_start( va, count );
2102 for (i = 0; i < count; i++)
2104 const WCHAR *str = va_arg( va, const WCHAR * );
2105 if (str) sz += strlenW( str ) + 1;
2109 dir = msi_alloc( sz * sizeof(WCHAR) );
2112 va_start( va, count );
2113 for (i = 0; i < count; i++)
2115 const WCHAR *str = va_arg( va, const WCHAR * );
2117 strcatW( dir, str );
2118 if ( i + 1 != count && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2124 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2126 MSIASSEMBLY *assembly = file->Component->assembly;
2128 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2130 msi_free( file->TargetPath );
2131 if (assembly && !assembly->application)
2133 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2134 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2135 msi_track_tempfile( package, file->TargetPath );
2139 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2140 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2143 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2146 static UINT calculate_file_cost( MSIPACKAGE *package )
2148 VS_FIXEDFILEINFO *file_version;
2149 WCHAR *font_version;
2152 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2154 MSICOMPONENT *comp = file->Component;
2157 if (!comp->Enabled) continue;
2159 if (file->IsCompressed)
2160 comp->ForceLocalState = TRUE;
2162 set_target_path( package, file );
2164 if ((comp->assembly && !comp->assembly->installed) ||
2165 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2167 comp->Cost += file->FileSize;
2170 file_size = msi_get_disk_file_size( file->TargetPath );
2174 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2176 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2178 comp->Cost += file->FileSize - file_size;
2180 msi_free( file_version );
2183 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2185 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2187 comp->Cost += file->FileSize - file_size;
2189 msi_free( font_version );
2193 if (file_size != file->FileSize)
2195 comp->Cost += file->FileSize - file_size;
2198 return ERROR_SUCCESS;
2201 WCHAR *msi_normalize_path( const WCHAR *in )
2203 const WCHAR *p = in;
2205 int n, len = strlenW( in ) + 2;
2207 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2212 /* copy until the end of the string or a space */
2213 while (*p != ' ' && (*q = *p))
2216 /* reduce many backslashes to one */
2217 if (*p != '\\' || *q != '\\')
2221 /* quit at the end of the string */
2225 /* count the number of spaces */
2230 /* if it's leading or trailing space, skip it */
2231 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2233 else /* copy n spaces */
2234 while (n && (*q++ = *p++)) n--;
2236 while (q - ret > 0 && q[-1] == ' ') q--;
2237 if (q - ret > 0 && q[-1] != '\\')
2245 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2248 MSIFOLDER *folder, *parent, *child;
2249 WCHAR *path, *normalized_path;
2251 TRACE("resolving %s\n", debugstr_w(name));
2253 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2255 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2257 if (!load_prop || !(path = msi_dup_property( package->db, szTargetDir )))
2259 path = msi_dup_property( package->db, szRootDrive );
2262 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2264 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2266 parent = msi_get_loaded_folder( package, folder->Parent );
2267 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2270 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2272 normalized_path = msi_normalize_path( path );
2274 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2276 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2277 msi_free( normalized_path );
2280 msi_set_property( package->db, folder->Directory, normalized_path );
2281 msi_free( folder->ResolvedTarget );
2282 folder->ResolvedTarget = normalized_path;
2284 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2287 msi_resolve_target_folder( package, child->Directory, load_prop );
2289 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2292 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2294 static const WCHAR query[] = {
2295 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2296 '`','C','o','n','d','i','t','i','o','n','`',0};
2297 static const WCHAR szOutOfDiskSpace[] = {
2298 'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2304 TRACE("Building directory properties\n");
2305 msi_resolve_target_folder( package, szTargetDir, TRUE );
2307 TRACE("Evaluating component conditions\n");
2308 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2310 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2312 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2313 comp->Enabled = FALSE;
2316 comp->Enabled = TRUE;
2319 /* read components states from the registry */
2320 ACTION_GetComponentInstallStates(package);
2321 ACTION_GetFeatureInstallStates(package);
2323 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2325 TRACE("Evaluating feature conditions\n");
2327 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2328 if (rc == ERROR_SUCCESS)
2330 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2331 msiobj_release( &view->hdr );
2332 if (rc != ERROR_SUCCESS)
2337 TRACE("Calculating file cost\n");
2338 calculate_file_cost( package );
2340 msi_set_property( package->db, szCostingComplete, szOne );
2341 /* set default run level if not set */
2342 level = msi_dup_property( package->db, szInstallLevel );
2344 msi_set_property( package->db, szInstallLevel, szOne );
2347 /* FIXME: check volume disk space */
2348 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2350 return MSI_SetFeatureStates(package);
2353 /* OK this value is "interpreted" and then formatted based on the
2354 first few characters */
2355 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2360 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2366 LPWSTR deformated = NULL;
2369 deformat_string(package, &value[2], &deformated);
2371 /* binary value type */
2375 *size = (strlenW(ptr)/2)+1;
2377 *size = strlenW(ptr)/2;
2379 data = msi_alloc(*size);
2385 /* if uneven pad with a zero in front */
2391 data[count] = (BYTE)strtol(byte,NULL,0);
2393 TRACE("Uneven byte count\n");
2401 data[count] = (BYTE)strtol(byte,NULL,0);
2404 msi_free(deformated);
2406 TRACE("Data %i bytes(%i)\n",*size,count);
2413 deformat_string(package, &value[1], &deformated);
2416 *size = sizeof(DWORD);
2417 data = msi_alloc(*size);
2423 if ( (*p < '0') || (*p > '9') )
2429 if (deformated[0] == '-')
2432 TRACE("DWORD %i\n",*(LPDWORD)data);
2434 msi_free(deformated);
2439 static const WCHAR szMulti[] = {'[','~',']',0};
2448 *type=REG_EXPAND_SZ;
2456 if (strstrW(value, szMulti))
2457 *type = REG_MULTI_SZ;
2459 /* remove initial delimiter */
2460 if (!strncmpW(value, szMulti, 3))
2463 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2465 /* add double NULL terminator */
2466 if (*type == REG_MULTI_SZ)
2468 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2469 data = msi_realloc_zero(data, *size);
2475 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2482 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2484 *root_key = HKEY_LOCAL_MACHINE;
2489 *root_key = HKEY_CURRENT_USER;
2494 *root_key = HKEY_CLASSES_ROOT;
2498 *root_key = HKEY_CURRENT_USER;
2502 *root_key = HKEY_LOCAL_MACHINE;
2506 *root_key = HKEY_USERS;
2510 ERR("Unknown root %i\n", root);
2517 static WCHAR *get_keypath( MSIPACKAGE *package, HKEY root, const WCHAR *path )
2519 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2520 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2522 if (is_64bit && package->platform == PLATFORM_INTEL &&
2523 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2528 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2529 if (!(path_32node = msi_alloc( size ))) return NULL;
2531 memcpy( path_32node, path, len * sizeof(WCHAR) );
2532 strcpyW( path_32node + len, szWow6432Node );
2533 strcatW( path_32node, szBackSlash );
2534 strcatW( path_32node, path + len );
2538 return strdupW( path );
2541 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2543 MSIPACKAGE *package = param;
2544 LPSTR value_data = NULL;
2545 HKEY root_key, hkey;
2547 LPWSTR deformated, uikey, keypath;
2548 LPCWSTR szRoot, component, name, key, value;
2552 BOOL check_first = FALSE;
2555 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2557 component = MSI_RecordGetString(row, 6);
2558 comp = msi_get_loaded_component(package,component);
2560 return ERROR_SUCCESS;
2562 comp->Action = msi_get_component_action( package, comp );
2563 if (comp->Action != INSTALLSTATE_LOCAL)
2565 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2566 return ERROR_SUCCESS;
2569 name = MSI_RecordGetString(row, 4);
2570 if( MSI_RecordIsNull(row,5) && name )
2572 /* null values can have special meanings */
2573 if (name[0]=='-' && name[1] == 0)
2574 return ERROR_SUCCESS;
2575 else if ((name[0]=='+' && name[1] == 0) ||
2576 (name[0] == '*' && name[1] == 0))
2581 root = MSI_RecordGetInteger(row,2);
2582 key = MSI_RecordGetString(row, 3);
2584 szRoot = get_root_key( package, root, &root_key );
2586 return ERROR_SUCCESS;
2588 deformat_string(package, key , &deformated);
2589 size = strlenW(deformated) + strlenW(szRoot) + 1;
2590 uikey = msi_alloc(size*sizeof(WCHAR));
2591 strcpyW(uikey,szRoot);
2592 strcatW(uikey,deformated);
2594 keypath = get_keypath( package, root_key, deformated );
2595 msi_free( deformated );
2596 if (RegCreateKeyW( root_key, keypath, &hkey ))
2598 ERR("Could not create key %s\n", debugstr_w(keypath));
2601 return ERROR_SUCCESS;
2604 value = MSI_RecordGetString(row,5);
2606 value_data = parse_value(package, value, &type, &size);
2609 value_data = (LPSTR)strdupW(szEmpty);
2610 size = sizeof(szEmpty);
2614 deformat_string(package, name, &deformated);
2618 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2620 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2625 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2626 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2628 TRACE("value %s of %s checked already exists\n",
2629 debugstr_w(deformated), debugstr_w(uikey));
2633 TRACE("Checked and setting value %s of %s\n",
2634 debugstr_w(deformated), debugstr_w(uikey));
2635 if (deformated || size)
2636 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2641 uirow = MSI_CreateRecord(3);
2642 MSI_RecordSetStringW(uirow,2,deformated);
2643 MSI_RecordSetStringW(uirow,1,uikey);
2644 if (type == REG_SZ || type == REG_EXPAND_SZ)
2645 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2646 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2647 msiobj_release( &uirow->hdr );
2649 msi_free(value_data);
2650 msi_free(deformated);
2654 return ERROR_SUCCESS;
2657 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2659 static const WCHAR query[] = {
2660 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2661 '`','R','e','g','i','s','t','r','y','`',0};
2665 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2666 if (rc != ERROR_SUCCESS)
2667 return ERROR_SUCCESS;
2669 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2670 msiobj_release(&view->hdr);
2674 static void delete_reg_value( HKEY root, const WCHAR *keypath, const WCHAR *value )
2678 DWORD num_subkeys, num_values;
2680 if (!(res = RegOpenKeyW( root, keypath, &hkey )))
2682 if ((res = RegDeleteValueW( hkey, value )))
2684 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
2686 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2687 NULL, NULL, NULL, NULL );
2688 RegCloseKey( hkey );
2689 if (!res && !num_subkeys && !num_values)
2691 TRACE("removing empty key %s\n", debugstr_w(keypath));
2692 RegDeleteKeyW( root, keypath );
2696 TRACE("failed to open key %s (%d)\n", debugstr_w(keypath), res);
2699 static void delete_reg_key( HKEY root, const WCHAR *keypath )
2701 LONG res = RegDeleteTreeW( root, keypath );
2702 if (res) TRACE("failed to delete key %s (%d)\n", debugstr_w(keypath), res);
2705 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2707 MSIPACKAGE *package = param;
2708 LPCWSTR component, name, key_str, root_key_str;
2709 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2712 BOOL delete_key = FALSE;
2717 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2719 component = MSI_RecordGetString( row, 6 );
2720 comp = msi_get_loaded_component( package, component );
2722 return ERROR_SUCCESS;
2724 comp->Action = msi_get_component_action( package, comp );
2725 if (comp->Action != INSTALLSTATE_ABSENT)
2727 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
2728 return ERROR_SUCCESS;
2731 name = MSI_RecordGetString( row, 4 );
2732 if (MSI_RecordIsNull( row, 5 ) && name )
2734 if (name[0] == '+' && !name[1])
2735 return ERROR_SUCCESS;
2736 else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
2743 root = MSI_RecordGetInteger( row, 2 );
2744 key_str = MSI_RecordGetString( row, 3 );
2746 root_key_str = get_root_key( package, root, &hkey_root );
2748 return ERROR_SUCCESS;
2750 deformat_string( package, key_str, &deformated_key );
2751 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2752 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2753 strcpyW( ui_key_str, root_key_str );
2754 strcatW( ui_key_str, deformated_key );
2756 deformat_string( package, name, &deformated_name );
2758 keypath = get_keypath( package, hkey_root, deformated_key );
2759 msi_free( deformated_key );
2760 if (delete_key) delete_reg_key( hkey_root, keypath );
2761 else delete_reg_value( hkey_root, keypath, deformated_name );
2762 msi_free( keypath );
2764 uirow = MSI_CreateRecord( 2 );
2765 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2766 MSI_RecordSetStringW( uirow, 2, deformated_name );
2767 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2768 msiobj_release( &uirow->hdr );
2770 msi_free( ui_key_str );
2771 msi_free( deformated_name );
2772 return ERROR_SUCCESS;
2775 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2777 MSIPACKAGE *package = param;
2778 LPCWSTR component, name, key_str, root_key_str;
2779 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2782 BOOL delete_key = FALSE;
2787 component = MSI_RecordGetString( row, 5 );
2788 comp = msi_get_loaded_component( package, component );
2790 return ERROR_SUCCESS;
2792 comp->Action = msi_get_component_action( package, comp );
2793 if (comp->Action != INSTALLSTATE_LOCAL)
2795 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2796 return ERROR_SUCCESS;
2799 if ((name = MSI_RecordGetString( row, 4 )))
2801 if (name[0] == '-' && !name[1])
2808 root = MSI_RecordGetInteger( row, 2 );
2809 key_str = MSI_RecordGetString( row, 3 );
2811 root_key_str = get_root_key( package, root, &hkey_root );
2813 return ERROR_SUCCESS;
2815 deformat_string( package, key_str, &deformated_key );
2816 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2817 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2818 strcpyW( ui_key_str, root_key_str );
2819 strcatW( ui_key_str, deformated_key );
2821 deformat_string( package, name, &deformated_name );
2823 keypath = get_keypath( package, hkey_root, deformated_key );
2824 msi_free( deformated_key );
2825 if (delete_key) delete_reg_key( hkey_root, keypath );
2826 else delete_reg_value( hkey_root, keypath, deformated_name );
2827 msi_free( keypath );
2829 uirow = MSI_CreateRecord( 2 );
2830 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2831 MSI_RecordSetStringW( uirow, 2, deformated_name );
2832 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2833 msiobj_release( &uirow->hdr );
2835 msi_free( ui_key_str );
2836 msi_free( deformated_name );
2837 return ERROR_SUCCESS;
2840 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2842 static const WCHAR registry_query[] = {
2843 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2844 '`','R','e','g','i','s','t','r','y','`',0};
2845 static const WCHAR remove_registry_query[] = {
2846 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2847 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
2851 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2852 if (rc == ERROR_SUCCESS)
2854 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2855 msiobj_release( &view->hdr );
2856 if (rc != ERROR_SUCCESS)
2859 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2860 if (rc == ERROR_SUCCESS)
2862 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2863 msiobj_release( &view->hdr );
2864 if (rc != ERROR_SUCCESS)
2867 return ERROR_SUCCESS;
2870 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2872 package->script->CurrentlyScripting = TRUE;
2874 return ERROR_SUCCESS;
2878 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2880 static const WCHAR query[]= {
2881 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2882 '`','R','e','g','i','s','t','r','y','`',0};
2884 DWORD total = 0, count = 0;
2886 MSIFEATURE *feature;
2890 TRACE("InstallValidate\n");
2892 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2893 if (rc == ERROR_SUCCESS)
2895 rc = MSI_IterateRecords( view, &count, NULL, package );
2896 msiobj_release( &view->hdr );
2897 if (rc != ERROR_SUCCESS)
2899 total += count * REG_PROGRESS_VALUE;
2901 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2902 total += COMPONENT_PROGRESS_VALUE;
2904 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2905 total += file->FileSize;
2907 msi_ui_progress( package, 0, total, 0, 0 );
2909 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2911 TRACE("Feature: %s Installed %d Request %d Action %d\n",
2912 debugstr_w(feature->Feature), feature->Installed,
2913 feature->ActionRequest, feature->Action);
2915 return ERROR_SUCCESS;
2918 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2920 MSIPACKAGE* package = param;
2921 LPCWSTR cond = NULL;
2922 LPCWSTR message = NULL;
2925 static const WCHAR title[]=
2926 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2928 cond = MSI_RecordGetString(row,1);
2930 r = MSI_EvaluateConditionW(package,cond);
2931 if (r == MSICONDITION_FALSE)
2933 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2936 message = MSI_RecordGetString(row,2);
2937 deformat_string(package,message,&deformated);
2938 MessageBoxW(NULL,deformated,title,MB_OK);
2939 msi_free(deformated);
2942 return ERROR_INSTALL_FAILURE;
2945 return ERROR_SUCCESS;
2948 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2950 static const WCHAR query[] = {
2951 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2952 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2956 TRACE("Checking launch conditions\n");
2958 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2959 if (rc != ERROR_SUCCESS)
2960 return ERROR_SUCCESS;
2962 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2963 msiobj_release(&view->hdr);
2967 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2971 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
2973 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2975 static const WCHAR query[] = {
2976 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2977 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
2978 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
2979 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
2980 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2983 LPWSTR deformated, buffer, deformated_name;
2986 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
2990 root = MSI_RecordGetInteger(row,2);
2991 key = MSI_RecordGetString(row, 3);
2992 name = MSI_RecordGetString(row, 4);
2993 deformat_string(package, key , &deformated);
2994 deformat_string(package, name, &deformated_name);
2996 len = strlenW(deformated) + 6;
2997 if (deformated_name)
2998 len+=strlenW(deformated_name);
3000 buffer = msi_alloc( len *sizeof(WCHAR));
3002 if (deformated_name)
3003 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3005 sprintfW(buffer,fmt,root,deformated);
3007 msi_free(deformated);
3008 msi_free(deformated_name);
3009 msiobj_release(&row->hdr);
3013 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3015 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3020 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3023 return strdupW( file->TargetPath );
3028 static HKEY openSharedDLLsKey(void)
3031 static const WCHAR path[] =
3032 {'S','o','f','t','w','a','r','e','\\',
3033 'M','i','c','r','o','s','o','f','t','\\',
3034 'W','i','n','d','o','w','s','\\',
3035 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3036 'S','h','a','r','e','d','D','L','L','s',0};
3038 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3042 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3047 DWORD sz = sizeof(count);
3050 hkey = openSharedDLLsKey();
3051 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3052 if (rc != ERROR_SUCCESS)
3058 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3062 hkey = openSharedDLLsKey();
3064 msi_reg_set_val_dword( hkey, path, count );
3066 RegDeleteValueW(hkey,path);
3071 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3073 MSIFEATURE *feature;
3077 /* only refcount DLLs */
3078 if (comp->KeyPath == NULL ||
3080 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3081 comp->Attributes & msidbComponentAttributesODBCDataSource)
3085 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3086 write = (count > 0);
3088 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3092 /* increment counts */
3093 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3097 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3100 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3102 if ( cl->component == comp )
3107 /* decrement counts */
3108 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3112 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3115 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3117 if ( cl->component == comp )
3122 /* ref count all the files in the component */
3127 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3129 if (file->Component == comp)
3130 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3134 /* add a count for permanent */
3135 if (comp->Attributes & msidbComponentAttributesPermanent)
3138 comp->RefCount = count;
3141 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3144 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3148 const WCHAR prefixW[] = {'<','\\',0};
3149 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3150 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3154 strcpyW( keypath, prefixW );
3155 strcatW( keypath, comp->assembly->display_name );
3159 return resolve_keypath( package, comp );
3162 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3164 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3171 squash_guid(package->ProductCode,squished_pc);
3172 msi_set_sourcedir_props(package, FALSE);
3174 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3177 INSTALLSTATE action;
3179 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3180 if (!comp->ComponentId)
3183 squash_guid( comp->ComponentId, squished_cc );
3184 msi_free( comp->FullKeypath );
3185 comp->FullKeypath = build_full_keypath( package, comp );
3187 ACTION_RefCountComponent( package, comp );
3189 if (package->need_rollback) action = comp->Installed;
3190 else action = comp->ActionRequest;
3192 TRACE("Component %s (%s), Keypath=%s, RefCount=%u Action=%u\n",
3193 debugstr_w(comp->Component), debugstr_w(squished_cc),
3194 debugstr_w(comp->FullKeypath), comp->RefCount, action);
3196 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3198 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3199 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3201 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3203 if (rc != ERROR_SUCCESS)
3206 if (comp->Attributes & msidbComponentAttributesPermanent)
3208 static const WCHAR szPermKey[] =
3209 { '0','0','0','0','0','0','0','0','0','0','0','0',
3210 '0','0','0','0','0','0','0','0','0','0','0','0',
3211 '0','0','0','0','0','0','0','0',0 };
3213 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3215 if (action == INSTALLSTATE_LOCAL)
3216 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3222 WCHAR source[MAX_PATH];
3223 WCHAR base[MAX_PATH];
3226 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3227 static const WCHAR query[] = {
3228 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3229 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3230 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3231 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3232 '`','D','i','s','k','I','d','`',0};
3234 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3237 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3238 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3239 ptr2 = strrchrW(source, '\\') + 1;
3240 msiobj_release(&row->hdr);
3242 lstrcpyW(base, package->PackagePath);
3243 ptr = strrchrW(base, '\\');
3246 sourcepath = msi_resolve_file_source(package, file);
3247 ptr = sourcepath + lstrlenW(base);
3248 lstrcpyW(ptr2, ptr);
3249 msi_free(sourcepath);
3251 msi_reg_set_val_str(hkey, squished_pc, source);
3255 else if (action == INSTALLSTATE_ABSENT)
3257 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3258 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3260 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3264 uirow = MSI_CreateRecord(3);
3265 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3266 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3267 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3268 msi_ui_actiondata( package, szProcessComponents, uirow );
3269 msiobj_release( &uirow->hdr );
3271 return ERROR_SUCCESS;
3282 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3283 LPWSTR lpszName, LONG_PTR lParam)
3286 typelib_struct *tl_struct = (typelib_struct*) lParam;
3287 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3291 if (!IS_INTRESOURCE(lpszName))
3293 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3297 sz = strlenW(tl_struct->source)+4;
3298 sz *= sizeof(WCHAR);
3300 if ((INT_PTR)lpszName == 1)
3301 tl_struct->path = strdupW(tl_struct->source);
3304 tl_struct->path = msi_alloc(sz);
3305 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3308 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3309 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3312 msi_free(tl_struct->path);
3313 tl_struct->path = NULL;
3318 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3319 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3321 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3325 msi_free(tl_struct->path);
3326 tl_struct->path = NULL;
3328 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3329 ITypeLib_Release(tl_struct->ptLib);
3334 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3336 MSIPACKAGE* package = param;
3340 typelib_struct tl_struct;
3345 component = MSI_RecordGetString(row,3);
3346 comp = msi_get_loaded_component(package,component);
3348 return ERROR_SUCCESS;
3350 comp->Action = msi_get_component_action( package, comp );
3351 if (comp->Action != INSTALLSTATE_LOCAL)
3353 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3354 return ERROR_SUCCESS;
3357 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3359 TRACE("component has no key path\n");
3360 return ERROR_SUCCESS;
3362 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3364 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3368 guid = MSI_RecordGetString(row,1);
3369 CLSIDFromString( guid, &tl_struct.clsid);
3370 tl_struct.source = strdupW( file->TargetPath );
3371 tl_struct.path = NULL;
3373 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3374 (LONG_PTR)&tl_struct);
3378 LPCWSTR helpid, help_path = NULL;
3381 helpid = MSI_RecordGetString(row,6);
3383 if (helpid) help_path = msi_get_target_folder( package, helpid );
3384 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3387 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3389 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3391 ITypeLib_Release(tl_struct.ptLib);
3392 msi_free(tl_struct.path);
3394 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3396 FreeLibrary(module);
3397 msi_free(tl_struct.source);
3401 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3404 ERR("Failed to load type library: %08x\n", hr);
3405 return ERROR_INSTALL_FAILURE;
3408 ITypeLib_Release(tlib);
3411 return ERROR_SUCCESS;
3414 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3416 static const WCHAR query[] = {
3417 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3418 '`','T','y','p','e','L','i','b','`',0};
3422 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3423 if (rc != ERROR_SUCCESS)
3424 return ERROR_SUCCESS;
3426 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3427 msiobj_release(&view->hdr);
3431 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3433 MSIPACKAGE *package = param;
3434 LPCWSTR component, guid;
3442 component = MSI_RecordGetString( row, 3 );
3443 comp = msi_get_loaded_component( package, component );
3445 return ERROR_SUCCESS;
3447 comp->Action = msi_get_component_action( package, comp );
3448 if (comp->Action != INSTALLSTATE_ABSENT)
3450 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3451 return ERROR_SUCCESS;
3453 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3455 guid = MSI_RecordGetString( row, 1 );
3456 CLSIDFromString( guid, &libid );
3457 version = MSI_RecordGetInteger( row, 4 );
3458 language = MSI_RecordGetInteger( row, 2 );
3461 syskind = SYS_WIN64;
3463 syskind = SYS_WIN32;
3466 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3469 WARN("Failed to unregister typelib: %08x\n", hr);
3472 return ERROR_SUCCESS;
3475 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3477 static const WCHAR query[] = {
3478 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3479 '`','T','y','p','e','L','i','b','`',0};
3483 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3484 if (rc != ERROR_SUCCESS)
3485 return ERROR_SUCCESS;
3487 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3488 msiobj_release( &view->hdr );
3492 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3494 static const WCHAR szlnk[] = {'.','l','n','k',0};
3495 LPCWSTR directory, extension, link_folder;
3496 LPWSTR link_file, filename;
3498 directory = MSI_RecordGetString( row, 2 );
3499 link_folder = msi_get_target_folder( package, directory );
3502 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3505 /* may be needed because of a bug somewhere else */
3506 msi_create_full_path( link_folder );
3508 filename = msi_dup_record_field( row, 3 );
3509 msi_reduce_to_long_filename( filename );
3511 extension = strchrW( filename, '.' );
3512 if (!extension || strcmpiW( extension, szlnk ))
3514 int len = strlenW( filename );
3515 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3516 memcpy( filename + len, szlnk, sizeof(szlnk) );
3518 link_file = msi_build_directory_name( 2, link_folder, filename );
3519 msi_free( filename );
3524 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3526 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3527 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3528 WCHAR *folder, *dest, *path;
3530 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3531 folder = msi_dup_property( package->db, szWindowsFolder );
3534 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3535 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3536 msi_free( appdata );
3538 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3539 msi_create_full_path( dest );
3540 path = msi_build_directory_name( 2, dest, icon_name );
3546 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3548 MSIPACKAGE *package = param;
3549 LPWSTR link_file, deformated, path;
3550 LPCWSTR component, target;
3552 IShellLinkW *sl = NULL;
3553 IPersistFile *pf = NULL;
3556 component = MSI_RecordGetString(row, 4);
3557 comp = msi_get_loaded_component(package, component);
3559 return ERROR_SUCCESS;
3561 comp->Action = msi_get_component_action( package, comp );
3562 if (comp->Action != INSTALLSTATE_LOCAL)
3564 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3565 return ERROR_SUCCESS;
3567 msi_ui_actiondata( package, szCreateShortcuts, row );
3569 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3570 &IID_IShellLinkW, (LPVOID *) &sl );
3574 ERR("CLSID_ShellLink not available\n");
3578 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3581 ERR("QueryInterface(IID_IPersistFile) failed\n");
3585 target = MSI_RecordGetString(row, 5);
3586 if (strchrW(target, '['))
3588 deformat_string( package, target, &path );
3589 TRACE("target path is %s\n", debugstr_w(path));
3590 IShellLinkW_SetPath( sl, path );
3595 FIXME("poorly handled shortcut format, advertised shortcut\n");
3596 IShellLinkW_SetPath(sl,comp->FullKeypath);
3599 if (!MSI_RecordIsNull(row,6))
3601 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3602 deformat_string(package, arguments, &deformated);
3603 IShellLinkW_SetArguments(sl,deformated);
3604 msi_free(deformated);
3607 if (!MSI_RecordIsNull(row,7))
3609 LPCWSTR description = MSI_RecordGetString(row, 7);
3610 IShellLinkW_SetDescription(sl, description);
3613 if (!MSI_RecordIsNull(row,8))
3614 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3616 if (!MSI_RecordIsNull(row,9))
3619 LPCWSTR icon = MSI_RecordGetString(row, 9);
3621 path = msi_build_icon_path(package, icon);
3622 index = MSI_RecordGetInteger(row,10);
3624 /* no value means 0 */
3625 if (index == MSI_NULL_INTEGER)
3628 IShellLinkW_SetIconLocation(sl, path, index);
3632 if (!MSI_RecordIsNull(row,11))
3633 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3635 if (!MSI_RecordIsNull(row,12))
3637 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3638 full_path = msi_get_target_folder( package, wkdir );
3639 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3641 link_file = get_link_file(package, row);
3643 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3644 IPersistFile_Save(pf, link_file, FALSE);
3645 msi_free(link_file);
3649 IPersistFile_Release( pf );
3651 IShellLinkW_Release( sl );
3653 return ERROR_SUCCESS;
3656 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3658 static const WCHAR query[] = {
3659 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3660 '`','S','h','o','r','t','c','u','t','`',0};
3665 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3666 if (rc != ERROR_SUCCESS)
3667 return ERROR_SUCCESS;
3669 res = CoInitialize( NULL );
3671 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3672 msiobj_release(&view->hdr);
3674 if (SUCCEEDED(res)) CoUninitialize();
3678 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3680 MSIPACKAGE *package = param;
3685 component = MSI_RecordGetString( row, 4 );
3686 comp = msi_get_loaded_component( package, component );
3688 return ERROR_SUCCESS;
3690 comp->Action = msi_get_component_action( package, comp );
3691 if (comp->Action != INSTALLSTATE_ABSENT)
3693 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3694 return ERROR_SUCCESS;
3696 msi_ui_actiondata( package, szRemoveShortcuts, row );
3698 link_file = get_link_file( package, row );
3700 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3701 if (!DeleteFileW( link_file ))
3703 WARN("Failed to remove shortcut file %u\n", GetLastError());
3705 msi_free( link_file );
3707 return ERROR_SUCCESS;
3710 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3712 static const WCHAR query[] = {
3713 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3714 '`','S','h','o','r','t','c','u','t','`',0};
3718 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3719 if (rc != ERROR_SUCCESS)
3720 return ERROR_SUCCESS;
3722 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3723 msiobj_release( &view->hdr );
3727 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3729 MSIPACKAGE* package = param;
3737 FileName = MSI_RecordGetString(row,1);
3740 ERR("Unable to get FileName\n");
3741 return ERROR_SUCCESS;
3744 FilePath = msi_build_icon_path(package, FileName);
3746 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3748 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3749 FILE_ATTRIBUTE_NORMAL, NULL);
3751 if (the_file == INVALID_HANDLE_VALUE)
3753 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3755 return ERROR_SUCCESS;
3762 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3763 if (rc != ERROR_SUCCESS)
3765 ERR("Failed to get stream\n");
3766 CloseHandle(the_file);
3767 DeleteFileW(FilePath);
3770 WriteFile(the_file,buffer,sz,&write,NULL);
3771 } while (sz == 1024);
3774 CloseHandle(the_file);
3776 return ERROR_SUCCESS;
3779 static UINT msi_publish_icons(MSIPACKAGE *package)
3781 static const WCHAR query[]= {
3782 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3783 '`','I','c','o','n','`',0};
3787 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3788 if (r == ERROR_SUCCESS)
3790 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3791 msiobj_release(&view->hdr);
3792 if (r != ERROR_SUCCESS)
3795 return ERROR_SUCCESS;
3798 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3804 MSISOURCELISTINFO *info;
3806 r = RegCreateKeyW(hkey, szSourceList, &source);
3807 if (r != ERROR_SUCCESS)
3810 RegCloseKey(source);
3812 buffer = strrchrW(package->PackagePath, '\\') + 1;
3813 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3814 package->Context, MSICODE_PRODUCT,
3815 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3816 if (r != ERROR_SUCCESS)
3819 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3820 package->Context, MSICODE_PRODUCT,
3821 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3822 if (r != ERROR_SUCCESS)
3825 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3826 package->Context, MSICODE_PRODUCT,
3827 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3828 if (r != ERROR_SUCCESS)
3831 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3833 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
3834 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3835 info->options, info->value);
3837 MsiSourceListSetInfoW(package->ProductCode, NULL,
3838 info->context, info->options,
3839 info->property, info->value);
3842 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3844 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3845 disk->context, disk->options,
3846 disk->disk_id, disk->volume_label, disk->disk_prompt);
3849 return ERROR_SUCCESS;
3852 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3854 MSIHANDLE hdb, suminfo;
3855 WCHAR guids[MAX_PATH];
3856 WCHAR packcode[SQUISH_GUID_SIZE];
3863 static const WCHAR szARPProductIcon[] =
3864 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3865 static const WCHAR szAssignment[] =
3866 {'A','s','s','i','g','n','m','e','n','t',0};
3867 static const WCHAR szAdvertiseFlags[] =
3868 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3869 static const WCHAR szClients[] =
3870 {'C','l','i','e','n','t','s',0};
3871 static const WCHAR szColon[] = {':',0};
3873 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3874 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3877 langid = msi_get_property_int(package->db, szProductLanguage, 0);
3878 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3881 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3883 buffer = msi_dup_property(package->db, szARPProductIcon);
3886 LPWSTR path = msi_build_icon_path(package, buffer);
3887 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3892 buffer = msi_dup_property(package->db, szProductVersion);
3895 DWORD verdword = msi_version_str_to_dword(buffer);
3896 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3900 msi_reg_set_val_dword(hkey, szAssignment, 0);
3901 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3902 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3903 msi_reg_set_val_str(hkey, szClients, szColon);
3905 hdb = alloc_msihandle(&package->db->hdr);
3907 return ERROR_NOT_ENOUGH_MEMORY;
3909 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3910 MsiCloseHandle(hdb);
3911 if (r != ERROR_SUCCESS)
3915 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3916 NULL, guids, &size);
3917 if (r != ERROR_SUCCESS)
3920 ptr = strchrW(guids, ';');
3922 squash_guid(guids, packcode);
3923 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3926 MsiCloseHandle(suminfo);
3927 return ERROR_SUCCESS;
3930 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3935 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3937 upgrade = msi_dup_property(package->db, szUpgradeCode);
3939 return ERROR_SUCCESS;
3941 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3942 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3944 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3946 if (r != ERROR_SUCCESS)
3948 WARN("failed to open upgrade code key\n");
3950 return ERROR_SUCCESS;
3952 squash_guid(package->ProductCode, squashed_pc);
3953 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3956 return ERROR_SUCCESS;
3959 static BOOL msi_check_publish(MSIPACKAGE *package)
3961 MSIFEATURE *feature;
3963 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3965 feature->Action = msi_get_feature_action( package, feature );
3966 if (feature->Action == INSTALLSTATE_LOCAL)
3973 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3975 MSIFEATURE *feature;
3977 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3979 feature->Action = msi_get_feature_action( package, feature );
3980 if (feature->Action != INSTALLSTATE_ABSENT)
3987 static UINT msi_publish_patches( MSIPACKAGE *package )
3989 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
3990 WCHAR patch_squashed[GUID_SIZE];
3991 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
3993 MSIPATCHINFO *patch;
3995 WCHAR *p, *all_patches = NULL;
3998 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
3999 if (r != ERROR_SUCCESS)
4000 return ERROR_FUNCTION_FAILED;
4002 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4003 if (res != ERROR_SUCCESS)
4005 r = ERROR_FUNCTION_FAILED;
4009 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4010 if (r != ERROR_SUCCESS)
4013 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4015 squash_guid( patch->patchcode, patch_squashed );
4016 len += strlenW( patch_squashed ) + 1;
4019 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4023 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4027 squash_guid( patch->patchcode, p );
4028 p += strlenW( p ) + 1;
4030 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4031 (const BYTE *)patch->transforms,
4032 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4033 if (res != ERROR_SUCCESS)
4036 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4037 if (r != ERROR_SUCCESS)
4040 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4041 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4042 RegCloseKey( patch_key );
4043 if (res != ERROR_SUCCESS)
4046 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4048 res = GetLastError();
4049 ERR("Unable to copy patch package %d\n", res);
4052 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4053 if (res != ERROR_SUCCESS)
4056 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4057 RegCloseKey( patch_key );
4058 if (res != ERROR_SUCCESS)
4062 all_patches[len] = 0;
4063 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4064 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4065 if (res != ERROR_SUCCESS)
4068 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4069 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4070 if (res != ERROR_SUCCESS)
4071 r = ERROR_FUNCTION_FAILED;
4074 RegCloseKey( product_patches_key );
4075 RegCloseKey( patches_key );
4076 RegCloseKey( product_key );
4077 msi_free( all_patches );
4081 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4084 HKEY hukey = NULL, hudkey = NULL;
4087 if (!list_empty(&package->patches))
4089 rc = msi_publish_patches(package);
4090 if (rc != ERROR_SUCCESS)
4094 /* FIXME: also need to publish if the product is in advertise mode */
4095 if (!msi_check_publish(package))
4096 return ERROR_SUCCESS;
4098 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4100 if (rc != ERROR_SUCCESS)
4103 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4104 NULL, &hudkey, TRUE);
4105 if (rc != ERROR_SUCCESS)
4108 rc = msi_publish_upgrade_code(package);
4109 if (rc != ERROR_SUCCESS)
4112 rc = msi_publish_product_properties(package, hukey);
4113 if (rc != ERROR_SUCCESS)
4116 rc = msi_publish_sourcelist(package, hukey);
4117 if (rc != ERROR_SUCCESS)
4120 rc = msi_publish_icons(package);
4123 uirow = MSI_CreateRecord( 1 );
4124 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4125 msi_ui_actiondata( package, szPublishProduct, uirow );
4126 msiobj_release( &uirow->hdr );
4129 RegCloseKey(hudkey);
4133 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4135 WCHAR *filename, *ptr, *folder, *ret;
4136 const WCHAR *dirprop;
4138 filename = msi_dup_record_field( row, 2 );
4139 if (filename && (ptr = strchrW( filename, '|' )))
4144 dirprop = MSI_RecordGetString( row, 3 );
4147 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4148 if (!folder) folder = msi_dup_property( package->db, dirprop );
4151 folder = msi_dup_property( package->db, szWindowsFolder );
4155 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4156 msi_free( filename );
4160 ret = msi_build_directory_name( 2, folder, ptr );
4162 msi_free( filename );
4167 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4169 MSIPACKAGE *package = param;
4170 LPCWSTR component, section, key, value, identifier;
4171 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4176 component = MSI_RecordGetString(row, 8);
4177 comp = msi_get_loaded_component(package,component);
4179 return ERROR_SUCCESS;
4181 comp->Action = msi_get_component_action( package, comp );
4182 if (comp->Action != INSTALLSTATE_LOCAL)
4184 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4185 return ERROR_SUCCESS;
4188 identifier = MSI_RecordGetString(row,1);
4189 section = MSI_RecordGetString(row,4);
4190 key = MSI_RecordGetString(row,5);
4191 value = MSI_RecordGetString(row,6);
4192 action = MSI_RecordGetInteger(row,7);
4194 deformat_string(package,section,&deformated_section);
4195 deformat_string(package,key,&deformated_key);
4196 deformat_string(package,value,&deformated_value);
4198 fullname = get_ini_file_name(package, row);
4202 TRACE("Adding value %s to section %s in %s\n",
4203 debugstr_w(deformated_key), debugstr_w(deformated_section),
4204 debugstr_w(fullname));
4205 WritePrivateProfileStringW(deformated_section, deformated_key,
4206 deformated_value, fullname);
4208 else if (action == 1)
4211 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4212 returned, 10, fullname);
4213 if (returned[0] == 0)
4215 TRACE("Adding value %s to section %s in %s\n",
4216 debugstr_w(deformated_key), debugstr_w(deformated_section),
4217 debugstr_w(fullname));
4219 WritePrivateProfileStringW(deformated_section, deformated_key,
4220 deformated_value, fullname);
4223 else if (action == 3)
4224 FIXME("Append to existing section not yet implemented\n");
4226 uirow = MSI_CreateRecord(4);
4227 MSI_RecordSetStringW(uirow,1,identifier);
4228 MSI_RecordSetStringW(uirow,2,deformated_section);
4229 MSI_RecordSetStringW(uirow,3,deformated_key);
4230 MSI_RecordSetStringW(uirow,4,deformated_value);
4231 msi_ui_actiondata( package, szWriteIniValues, uirow );
4232 msiobj_release( &uirow->hdr );
4235 msi_free(deformated_key);
4236 msi_free(deformated_value);
4237 msi_free(deformated_section);
4238 return ERROR_SUCCESS;
4241 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4243 static const WCHAR query[] = {
4244 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4245 '`','I','n','i','F','i','l','e','`',0};
4249 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4250 if (rc != ERROR_SUCCESS)
4251 return ERROR_SUCCESS;
4253 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4254 msiobj_release(&view->hdr);
4258 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4260 MSIPACKAGE *package = param;
4261 LPCWSTR component, section, key, value, identifier;
4262 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4267 component = MSI_RecordGetString( row, 8 );
4268 comp = msi_get_loaded_component( package, component );
4270 return ERROR_SUCCESS;
4272 comp->Action = msi_get_component_action( package, comp );
4273 if (comp->Action != INSTALLSTATE_ABSENT)
4275 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4276 return ERROR_SUCCESS;
4279 identifier = MSI_RecordGetString( row, 1 );
4280 section = MSI_RecordGetString( row, 4 );
4281 key = MSI_RecordGetString( row, 5 );
4282 value = MSI_RecordGetString( row, 6 );
4283 action = MSI_RecordGetInteger( row, 7 );
4285 deformat_string( package, section, &deformated_section );
4286 deformat_string( package, key, &deformated_key );
4287 deformat_string( package, value, &deformated_value );
4289 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4291 filename = get_ini_file_name( package, row );
4293 TRACE("Removing key %s from section %s in %s\n",
4294 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4296 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4298 WARN("Unable to remove key %u\n", GetLastError());
4300 msi_free( filename );
4303 FIXME("Unsupported action %d\n", action);
4306 uirow = MSI_CreateRecord( 4 );
4307 MSI_RecordSetStringW( uirow, 1, identifier );
4308 MSI_RecordSetStringW( uirow, 2, deformated_section );
4309 MSI_RecordSetStringW( uirow, 3, deformated_key );
4310 MSI_RecordSetStringW( uirow, 4, deformated_value );
4311 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4312 msiobj_release( &uirow->hdr );
4314 msi_free( deformated_key );
4315 msi_free( deformated_value );
4316 msi_free( deformated_section );
4317 return ERROR_SUCCESS;
4320 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4322 MSIPACKAGE *package = param;
4323 LPCWSTR component, section, key, value, identifier;
4324 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4329 component = MSI_RecordGetString( row, 8 );
4330 comp = msi_get_loaded_component( package, component );
4332 return ERROR_SUCCESS;
4334 comp->Action = msi_get_component_action( package, comp );
4335 if (comp->Action != INSTALLSTATE_LOCAL)
4337 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4338 return ERROR_SUCCESS;
4341 identifier = MSI_RecordGetString( row, 1 );
4342 section = MSI_RecordGetString( row, 4 );
4343 key = MSI_RecordGetString( row, 5 );
4344 value = MSI_RecordGetString( row, 6 );
4345 action = MSI_RecordGetInteger( row, 7 );
4347 deformat_string( package, section, &deformated_section );
4348 deformat_string( package, key, &deformated_key );
4349 deformat_string( package, value, &deformated_value );
4351 if (action == msidbIniFileActionRemoveLine)
4353 filename = get_ini_file_name( package, row );
4355 TRACE("Removing key %s from section %s in %s\n",
4356 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4358 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4360 WARN("Unable to remove key %u\n", GetLastError());
4362 msi_free( filename );
4365 FIXME("Unsupported action %d\n", action);
4367 uirow = MSI_CreateRecord( 4 );
4368 MSI_RecordSetStringW( uirow, 1, identifier );
4369 MSI_RecordSetStringW( uirow, 2, deformated_section );
4370 MSI_RecordSetStringW( uirow, 3, deformated_key );
4371 MSI_RecordSetStringW( uirow, 4, deformated_value );
4372 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4373 msiobj_release( &uirow->hdr );
4375 msi_free( deformated_key );
4376 msi_free( deformated_value );
4377 msi_free( deformated_section );
4378 return ERROR_SUCCESS;
4381 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4383 static const WCHAR query[] = {
4384 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4385 '`','I','n','i','F','i','l','e','`',0};
4386 static const WCHAR remove_query[] = {
4387 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4388 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4392 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4393 if (rc == ERROR_SUCCESS)
4395 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4396 msiobj_release( &view->hdr );
4397 if (rc != ERROR_SUCCESS)
4400 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4401 if (rc == ERROR_SUCCESS)
4403 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4404 msiobj_release( &view->hdr );
4405 if (rc != ERROR_SUCCESS)
4408 return ERROR_SUCCESS;
4411 static void register_dll( const WCHAR *dll, BOOL unregister )
4415 hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4418 HRESULT (WINAPI *func_ptr)( void );
4419 const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4421 func_ptr = (void *)GetProcAddress( hmod, func );
4424 HRESULT hr = func_ptr();
4426 WARN("failed to register dll 0x%08x\n", hr);
4429 WARN("entry point %s not found\n", func);
4430 FreeLibrary( hmod );
4433 WARN("failed to load library %u\n", GetLastError());
4436 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4438 MSIPACKAGE *package = param;
4443 filename = MSI_RecordGetString( row, 1 );
4444 file = msi_get_loaded_file( package, filename );
4447 WARN("unable to find file %s\n", debugstr_w(filename));
4448 return ERROR_SUCCESS;
4450 file->Component->Action = msi_get_component_action( package, file->Component );
4451 if (file->Component->Action != INSTALLSTATE_LOCAL)
4453 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4454 return ERROR_SUCCESS;
4457 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4458 register_dll( file->TargetPath, FALSE );
4460 uirow = MSI_CreateRecord( 2 );
4461 MSI_RecordSetStringW( uirow, 1, file->File );
4462 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4463 msi_ui_actiondata( package, szSelfRegModules, uirow );
4464 msiobj_release( &uirow->hdr );
4466 return ERROR_SUCCESS;
4469 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4471 static const WCHAR query[] = {
4472 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4473 '`','S','e','l','f','R','e','g','`',0};
4477 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4478 if (rc != ERROR_SUCCESS)
4479 return ERROR_SUCCESS;
4481 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4482 msiobj_release(&view->hdr);
4486 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4488 MSIPACKAGE *package = param;
4493 filename = MSI_RecordGetString( row, 1 );
4494 file = msi_get_loaded_file( package, filename );
4497 WARN("unable to find file %s\n", debugstr_w(filename));
4498 return ERROR_SUCCESS;
4500 file->Component->Action = msi_get_component_action( package, file->Component );
4501 if (file->Component->Action != INSTALLSTATE_ABSENT)
4503 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4504 return ERROR_SUCCESS;
4507 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4508 register_dll( file->TargetPath, TRUE );
4510 uirow = MSI_CreateRecord( 2 );
4511 MSI_RecordSetStringW( uirow, 1, file->File );
4512 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4513 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4514 msiobj_release( &uirow->hdr );
4516 return ERROR_SUCCESS;
4519 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4521 static const WCHAR query[] = {
4522 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4523 '`','S','e','l','f','R','e','g','`',0};
4527 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4528 if (rc != ERROR_SUCCESS)
4529 return ERROR_SUCCESS;
4531 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4532 msiobj_release( &view->hdr );
4536 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4538 MSIFEATURE *feature;
4540 HKEY hkey = NULL, userdata = NULL;
4542 if (!msi_check_publish(package))
4543 return ERROR_SUCCESS;
4545 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4547 if (rc != ERROR_SUCCESS)
4550 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4552 if (rc != ERROR_SUCCESS)
4555 /* here the guids are base 85 encoded */
4556 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4562 BOOL absent = FALSE;
4565 if (feature->Action != INSTALLSTATE_LOCAL &&
4566 feature->Action != INSTALLSTATE_SOURCE &&
4567 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4570 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4574 if (feature->Feature_Parent)
4575 size += strlenW( feature->Feature_Parent )+2;
4577 data = msi_alloc(size * sizeof(WCHAR));
4580 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4582 MSICOMPONENT* component = cl->component;
4586 if (component->ComponentId)
4588 TRACE("From %s\n",debugstr_w(component->ComponentId));
4589 CLSIDFromString(component->ComponentId, &clsid);
4590 encode_base85_guid(&clsid,buf);
4591 TRACE("to %s\n",debugstr_w(buf));
4596 if (feature->Feature_Parent)
4598 static const WCHAR sep[] = {'\2',0};
4600 strcatW(data,feature->Feature_Parent);
4603 msi_reg_set_val_str( userdata, feature->Feature, data );
4607 if (feature->Feature_Parent)
4608 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4611 size += sizeof(WCHAR);
4612 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4613 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4617 size += 2*sizeof(WCHAR);
4618 data = msi_alloc(size);
4621 if (feature->Feature_Parent)
4622 strcpyW( &data[1], feature->Feature_Parent );
4623 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4629 uirow = MSI_CreateRecord( 1 );
4630 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4631 msi_ui_actiondata( package, szPublishFeatures, uirow );
4632 msiobj_release( &uirow->hdr );
4633 /* FIXME: call msi_ui_progress? */
4638 RegCloseKey(userdata);
4642 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4648 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4650 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4652 if (r == ERROR_SUCCESS)
4654 RegDeleteValueW(hkey, feature->Feature);
4658 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4660 if (r == ERROR_SUCCESS)
4662 RegDeleteValueW(hkey, feature->Feature);
4666 uirow = MSI_CreateRecord( 1 );
4667 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4668 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
4669 msiobj_release( &uirow->hdr );
4671 return ERROR_SUCCESS;
4674 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4676 MSIFEATURE *feature;
4678 if (!msi_check_unpublish(package))
4679 return ERROR_SUCCESS;
4681 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4683 msi_unpublish_feature(package, feature);
4686 return ERROR_SUCCESS;
4689 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4693 WCHAR date[9], *val, *buffer;
4694 const WCHAR *prop, *key;
4696 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4697 static const WCHAR modpath_fmt[] =
4698 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4699 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4700 static const WCHAR szModifyPath[] =
4701 {'M','o','d','i','f','y','P','a','t','h',0};
4702 static const WCHAR szUninstallString[] =
4703 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4704 static const WCHAR szEstimatedSize[] =
4705 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4706 static const WCHAR szDisplayVersion[] =
4707 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4708 static const WCHAR szInstallSource[] =
4709 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4710 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4711 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4712 static const WCHAR szAuthorizedCDFPrefix[] =
4713 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4714 static const WCHAR szARPCONTACT[] =
4715 {'A','R','P','C','O','N','T','A','C','T',0};
4716 static const WCHAR szContact[] =
4717 {'C','o','n','t','a','c','t',0};
4718 static const WCHAR szARPCOMMENTS[] =
4719 {'A','R','P','C','O','M','M','E','N','T','S',0};
4720 static const WCHAR szComments[] =
4721 {'C','o','m','m','e','n','t','s',0};
4722 static const WCHAR szProductName[] =
4723 {'P','r','o','d','u','c','t','N','a','m','e',0};
4724 static const WCHAR szDisplayName[] =
4725 {'D','i','s','p','l','a','y','N','a','m','e',0};
4726 static const WCHAR szARPHELPLINK[] =
4727 {'A','R','P','H','E','L','P','L','I','N','K',0};
4728 static const WCHAR szHelpLink[] =
4729 {'H','e','l','p','L','i','n','k',0};
4730 static const WCHAR szARPHELPTELEPHONE[] =
4731 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4732 static const WCHAR szHelpTelephone[] =
4733 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4734 static const WCHAR szARPINSTALLLOCATION[] =
4735 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4736 static const WCHAR szInstallLocation[] =
4737 {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
4738 static const WCHAR szManufacturer[] =
4739 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4740 static const WCHAR szPublisher[] =
4741 {'P','u','b','l','i','s','h','e','r',0};
4742 static const WCHAR szARPREADME[] =
4743 {'A','R','P','R','E','A','D','M','E',0};
4744 static const WCHAR szReadme[] =
4745 {'R','e','a','d','M','e',0};
4746 static const WCHAR szARPSIZE[] =
4747 {'A','R','P','S','I','Z','E',0};
4748 static const WCHAR szSize[] =
4749 {'S','i','z','e',0};
4750 static const WCHAR szARPURLINFOABOUT[] =
4751 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4752 static const WCHAR szURLInfoAbout[] =
4753 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4754 static const WCHAR szARPURLUPDATEINFO[] =
4755 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4756 static const WCHAR szURLUpdateInfo[] =
4757 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4758 static const WCHAR szARPSYSTEMCOMPONENT[] =
4759 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
4760 static const WCHAR szSystemComponent[] =
4761 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
4763 static const WCHAR *propval[] = {
4764 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4765 szARPCONTACT, szContact,
4766 szARPCOMMENTS, szComments,
4767 szProductName, szDisplayName,
4768 szARPHELPLINK, szHelpLink,
4769 szARPHELPTELEPHONE, szHelpTelephone,
4770 szARPINSTALLLOCATION, szInstallLocation,
4771 szSourceDir, szInstallSource,
4772 szManufacturer, szPublisher,
4773 szARPREADME, szReadme,
4775 szARPURLINFOABOUT, szURLInfoAbout,
4776 szARPURLUPDATEINFO, szURLUpdateInfo,
4779 const WCHAR **p = propval;
4785 val = msi_dup_property(package->db, prop);
4786 msi_reg_set_val_str(hkey, key, val);
4790 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4791 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
4793 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
4795 size = deformat_string(package, modpath_fmt, &buffer);
4796 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4797 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4800 /* FIXME: Write real Estimated Size when we have it */
4801 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4803 GetLocalTime(&systime);
4804 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4805 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4807 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4808 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4810 buffer = msi_dup_property(package->db, szProductVersion);
4811 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4814 DWORD verdword = msi_version_str_to_dword(buffer);
4816 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4817 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4818 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4822 return ERROR_SUCCESS;
4825 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4827 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4829 LPWSTR upgrade_code;
4830 HKEY hkey, props, upgrade_key;
4833 /* FIXME: also need to publish if the product is in advertise mode */
4834 if (!msi_check_publish(package))
4835 return ERROR_SUCCESS;
4837 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
4838 if (rc != ERROR_SUCCESS)
4841 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
4842 if (rc != ERROR_SUCCESS)
4845 rc = msi_publish_install_properties(package, hkey);
4846 if (rc != ERROR_SUCCESS)
4849 rc = msi_publish_install_properties(package, props);
4850 if (rc != ERROR_SUCCESS)
4853 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4856 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
4857 if (rc == ERROR_SUCCESS)
4859 squash_guid( package->ProductCode, squashed_pc );
4860 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
4861 RegCloseKey( upgrade_key );
4863 msi_free( upgrade_code );
4865 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
4866 package->delete_on_close = FALSE;
4869 uirow = MSI_CreateRecord( 1 );
4870 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4871 msi_ui_actiondata( package, szRegisterProduct, uirow );
4872 msiobj_release( &uirow->hdr );
4875 return ERROR_SUCCESS;
4878 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4880 return execute_script(package, SCRIPT_INSTALL);
4883 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
4885 MSIPACKAGE *package = param;
4886 const WCHAR *icon = MSI_RecordGetString( row, 1 );
4887 WCHAR *p, *icon_path;
4889 if (!icon) return ERROR_SUCCESS;
4890 if ((icon_path = msi_build_icon_path( package, icon )))
4892 TRACE("removing icon file %s\n", debugstr_w(icon_path));
4893 DeleteFileW( icon_path );
4894 if ((p = strrchrW( icon_path, '\\' )))
4897 RemoveDirectoryW( icon_path );
4899 msi_free( icon_path );
4901 return ERROR_SUCCESS;
4904 static UINT msi_unpublish_icons( MSIPACKAGE *package )
4906 static const WCHAR query[]= {
4907 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
4911 r = MSI_DatabaseOpenViewW( package->db, query, &view );
4912 if (r == ERROR_SUCCESS)
4914 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
4915 msiobj_release( &view->hdr );
4916 if (r != ERROR_SUCCESS)
4919 return ERROR_SUCCESS;
4922 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
4924 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
4925 WCHAR *upgrade, **features;
4926 BOOL full_uninstall = TRUE;
4927 MSIFEATURE *feature;
4928 MSIPATCHINFO *patch;
4931 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4933 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
4935 features = msi_split_string( remove, ',' );
4936 for (i = 0; features && features[i]; i++)
4938 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
4942 if (!full_uninstall)
4943 return ERROR_SUCCESS;
4945 MSIREG_DeleteProductKey(package->ProductCode);
4946 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4947 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
4949 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4950 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4951 MSIREG_DeleteUserProductKey(package->ProductCode);
4952 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4954 upgrade = msi_dup_property(package->db, szUpgradeCode);
4957 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4958 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
4962 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
4964 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
4965 if (!strcmpW( package->ProductCode, patch->products ))
4967 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
4968 patch->delete_on_close = TRUE;
4970 /* FIXME: remove local patch package if this is the last product */
4972 TRACE("removing local package %s\n", debugstr_w(package->localfile));
4973 package->delete_on_close = TRUE;
4975 msi_unpublish_icons( package );
4976 return ERROR_SUCCESS;
4979 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4984 /* turn off scheduling */
4985 package->script->CurrentlyScripting= FALSE;
4987 /* first do the same as an InstallExecute */
4988 rc = ACTION_InstallExecute(package);
4989 if (rc != ERROR_SUCCESS)
4992 /* then handle commit actions */
4993 rc = execute_script(package, SCRIPT_COMMIT);
4994 if (rc != ERROR_SUCCESS)
4997 remove = msi_dup_property(package->db, szRemove);
4998 rc = msi_unpublish_product(package, remove);
5003 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5005 static const WCHAR RunOnce[] = {
5006 'S','o','f','t','w','a','r','e','\\',
5007 'M','i','c','r','o','s','o','f','t','\\',
5008 'W','i','n','d','o','w','s','\\',
5009 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5010 'R','u','n','O','n','c','e',0};
5011 static const WCHAR InstallRunOnce[] = {
5012 'S','o','f','t','w','a','r','e','\\',
5013 'M','i','c','r','o','s','o','f','t','\\',
5014 'W','i','n','d','o','w','s','\\',
5015 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5016 'I','n','s','t','a','l','l','e','r','\\',
5017 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5019 static const WCHAR msiexec_fmt[] = {
5021 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5022 '\"','%','s','\"',0};
5023 static const WCHAR install_fmt[] = {
5024 '/','I',' ','\"','%','s','\"',' ',
5025 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5026 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5027 WCHAR buffer[256], sysdir[MAX_PATH];
5029 WCHAR squished_pc[100];
5031 squash_guid(package->ProductCode,squished_pc);
5033 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5034 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5035 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5038 msi_reg_set_val_str( hkey, squished_pc, buffer );
5041 TRACE("Reboot command %s\n",debugstr_w(buffer));
5043 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5044 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5046 msi_reg_set_val_str( hkey, squished_pc, buffer );
5049 return ERROR_INSTALL_SUSPEND;
5052 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5054 static const WCHAR query[] =
5055 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5056 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5057 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5058 MSIRECORD *rec, *row;
5064 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5066 rec = MSI_CreateRecord( count + 2 );
5067 str = MSI_RecordGetString( row, 1 );
5068 MSI_RecordSetStringW( rec, 0, str );
5069 msiobj_release( &row->hdr );
5070 MSI_RecordSetInteger( rec, 1, error );
5072 va_start( va, count );
5073 for (i = 0; i < count; i++)
5075 str = va_arg( va, const WCHAR *);
5076 MSI_RecordSetStringW( rec, i + 2, str );
5080 MSI_FormatRecordW( package, rec, NULL, &size );
5082 data = msi_alloc( size * sizeof(WCHAR) );
5083 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5085 msiobj_release( &rec->hdr );
5089 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5095 * We are currently doing what should be done here in the top level Install
5096 * however for Administrative and uninstalls this step will be needed
5098 if (!package->PackagePath)
5099 return ERROR_SUCCESS;
5101 msi_set_sourcedir_props(package, TRUE);
5103 attrib = GetFileAttributesW(package->db->path);
5104 if (attrib == INVALID_FILE_ATTRIBUTES)
5109 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5110 package->Context, MSICODE_PRODUCT,
5111 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5112 if (rc == ERROR_MORE_DATA)
5114 prompt = msi_alloc(size * sizeof(WCHAR));
5115 MsiSourceListGetInfoW(package->ProductCode, NULL,
5116 package->Context, MSICODE_PRODUCT,
5117 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5120 prompt = strdupW(package->db->path);
5122 msg = msi_build_error_string(package, 1302, 1, prompt);
5124 while(attrib == INVALID_FILE_ATTRIBUTES)
5126 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5130 return ERROR_INSTALL_USEREXIT;
5132 attrib = GetFileAttributesW(package->db->path);
5138 return ERROR_SUCCESS;
5143 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5146 LPWSTR buffer, productid = NULL;
5147 UINT i, rc = ERROR_SUCCESS;
5150 static const WCHAR szPropKeys[][80] =
5152 {'P','r','o','d','u','c','t','I','D',0},
5153 {'U','S','E','R','N','A','M','E',0},
5154 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5158 static const WCHAR szRegKeys[][80] =
5160 {'P','r','o','d','u','c','t','I','D',0},
5161 {'R','e','g','O','w','n','e','r',0},
5162 {'R','e','g','C','o','m','p','a','n','y',0},
5166 if (msi_check_unpublish(package))
5168 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5172 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5176 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5178 if (rc != ERROR_SUCCESS)
5181 for( i = 0; szPropKeys[i][0]; i++ )
5183 buffer = msi_dup_property( package->db, szPropKeys[i] );
5184 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5189 uirow = MSI_CreateRecord( 1 );
5190 MSI_RecordSetStringW( uirow, 1, productid );
5191 msi_ui_actiondata( package, szRegisterUser, uirow );
5192 msiobj_release( &uirow->hdr );
5194 msi_free(productid);
5200 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5204 package->script->InWhatSequence |= SEQUENCE_EXEC;
5205 rc = ACTION_ProcessExecSequence(package,FALSE);
5209 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5211 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5212 WCHAR productid_85[21], component_85[21], *ret;
5216 /* > is used if there is a component GUID and < if not. */
5218 productid_85[0] = 0;
5219 component_85[0] = 0;
5220 CLSIDFromString( package->ProductCode, &clsid );
5222 encode_base85_guid( &clsid, productid_85 );
5225 CLSIDFromString( component->ComponentId, &clsid );
5226 encode_base85_guid( &clsid, component_85 );
5229 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5230 debugstr_w(component_85));
5232 sz = 20 + strlenW( feature ) + 20 + 3;
5233 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5234 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5238 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5240 MSIPACKAGE *package = param;
5241 LPCWSTR compgroupid, component, feature, qualifier, text;
5242 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5251 feature = MSI_RecordGetString(rec, 5);
5252 feat = msi_get_loaded_feature(package, feature);
5254 return ERROR_SUCCESS;
5256 feat->Action = msi_get_feature_action( package, feat );
5257 if (feat->Action != INSTALLSTATE_LOCAL &&
5258 feat->Action != INSTALLSTATE_SOURCE &&
5259 feat->Action != INSTALLSTATE_ADVERTISED)
5261 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5262 return ERROR_SUCCESS;
5265 component = MSI_RecordGetString(rec, 3);
5266 comp = msi_get_loaded_component(package, component);
5268 return ERROR_SUCCESS;
5270 compgroupid = MSI_RecordGetString(rec,1);
5271 qualifier = MSI_RecordGetString(rec,2);
5273 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5274 if (rc != ERROR_SUCCESS)
5277 advertise = msi_create_component_advertise_string( package, comp, feature );
5278 text = MSI_RecordGetString( rec, 4 );
5281 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5282 strcpyW( p, advertise );
5284 msi_free( advertise );
5287 existing = msi_reg_get_val_str( hkey, qualifier );
5289 sz = strlenW( advertise ) + 1;
5292 for (p = existing; *p; p += len)
5294 len = strlenW( p ) + 1;
5295 if (strcmpW( advertise, p )) sz += len;
5298 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5300 rc = ERROR_OUTOFMEMORY;
5306 for (p = existing; *p; p += len)
5308 len = strlenW( p ) + 1;
5309 if (strcmpW( advertise, p ))
5311 memcpy( q, p, len * sizeof(WCHAR) );
5316 strcpyW( q, advertise );
5317 q[strlenW( q ) + 1] = 0;
5319 msi_reg_set_val_multi_str( hkey, qualifier, output );
5324 msi_free( advertise );
5325 msi_free( existing );
5328 uirow = MSI_CreateRecord( 2 );
5329 MSI_RecordSetStringW( uirow, 1, compgroupid );
5330 MSI_RecordSetStringW( uirow, 2, qualifier);
5331 msi_ui_actiondata( package, szPublishComponents, uirow );
5332 msiobj_release( &uirow->hdr );
5333 /* FIXME: call ui_progress? */
5339 * At present I am ignorning the advertised components part of this and only
5340 * focusing on the qualified component sets
5342 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5344 static const WCHAR query[] = {
5345 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5346 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5350 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5351 if (rc != ERROR_SUCCESS)
5352 return ERROR_SUCCESS;
5354 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5355 msiobj_release(&view->hdr);
5359 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5361 static const WCHAR szInstallerComponents[] = {
5362 'S','o','f','t','w','a','r','e','\\',
5363 'M','i','c','r','o','s','o','f','t','\\',
5364 'I','n','s','t','a','l','l','e','r','\\',
5365 'C','o','m','p','o','n','e','n','t','s','\\',0};
5367 MSIPACKAGE *package = param;
5368 LPCWSTR compgroupid, component, feature, qualifier;
5372 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5375 feature = MSI_RecordGetString( rec, 5 );
5376 feat = msi_get_loaded_feature( package, feature );
5378 return ERROR_SUCCESS;
5380 feat->Action = msi_get_feature_action( package, feat );
5381 if (feat->Action != INSTALLSTATE_ABSENT)
5383 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5384 return ERROR_SUCCESS;
5387 component = MSI_RecordGetString( rec, 3 );
5388 comp = msi_get_loaded_component( package, component );
5390 return ERROR_SUCCESS;
5392 compgroupid = MSI_RecordGetString( rec, 1 );
5393 qualifier = MSI_RecordGetString( rec, 2 );
5395 squash_guid( compgroupid, squashed );
5396 strcpyW( keypath, szInstallerComponents );
5397 strcatW( keypath, squashed );
5399 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5400 if (res != ERROR_SUCCESS)
5402 WARN("Unable to delete component key %d\n", res);
5405 uirow = MSI_CreateRecord( 2 );
5406 MSI_RecordSetStringW( uirow, 1, compgroupid );
5407 MSI_RecordSetStringW( uirow, 2, qualifier );
5408 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5409 msiobj_release( &uirow->hdr );
5411 return ERROR_SUCCESS;
5414 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5416 static const WCHAR query[] = {
5417 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5418 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5422 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5423 if (rc != ERROR_SUCCESS)
5424 return ERROR_SUCCESS;
5426 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5427 msiobj_release( &view->hdr );
5431 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5433 static const WCHAR query[] =
5434 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5435 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5436 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5437 MSIPACKAGE *package = param;
5438 MSICOMPONENT *component;
5441 SC_HANDLE hscm = NULL, service = NULL;
5443 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5444 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5445 DWORD serv_type, start_type, err_control;
5446 SERVICE_DESCRIPTIONW sd = {NULL};
5448 comp = MSI_RecordGetString( rec, 12 );
5449 component = msi_get_loaded_component( package, comp );
5452 WARN("service component not found\n");
5455 component->Action = msi_get_component_action( package, component );
5456 if (component->Action != INSTALLSTATE_LOCAL)
5458 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5461 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5464 ERR("Failed to open the SC Manager!\n");
5468 start_type = MSI_RecordGetInteger(rec, 5);
5469 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5472 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5473 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5474 serv_type = MSI_RecordGetInteger(rec, 4);
5475 err_control = MSI_RecordGetInteger(rec, 6);
5476 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5477 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5478 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5479 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5480 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5481 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5483 /* fetch the service path */
5484 row = MSI_QueryGetRecord(package->db, query, comp);
5487 ERR("Query failed\n");
5490 key = MSI_RecordGetString(row, 6);
5491 file = msi_get_loaded_file(package, key);
5492 msiobj_release(&row->hdr);
5495 ERR("Failed to load the service file\n");
5499 if (!args || !args[0]) image_path = file->TargetPath;
5502 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5503 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5504 return ERROR_OUTOFMEMORY;
5506 strcpyW(image_path, file->TargetPath);
5507 strcatW(image_path, szSpace);
5508 strcatW(image_path, args);
5510 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5511 start_type, err_control, image_path, load_order,
5512 NULL, depends, serv_name, pass);
5516 if (GetLastError() != ERROR_SERVICE_EXISTS)
5517 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5519 else if (sd.lpDescription)
5521 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5522 WARN("failed to set service description %u\n", GetLastError());
5525 if (image_path != file->TargetPath) msi_free(image_path);
5527 CloseServiceHandle(service);
5528 CloseServiceHandle(hscm);
5531 msi_free(sd.lpDescription);
5532 msi_free(load_order);
5533 msi_free(serv_name);
5538 return ERROR_SUCCESS;
5541 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5543 static const WCHAR query[] = {
5544 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5545 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5549 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5550 if (rc != ERROR_SUCCESS)
5551 return ERROR_SUCCESS;
5553 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5554 msiobj_release(&view->hdr);
5558 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5559 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5561 LPCWSTR *vector, *temp_vector;
5565 static const WCHAR separator[] = {'[','~',']',0};
5568 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5573 vector = msi_alloc(sizeof(LPWSTR));
5581 vector[*numargs - 1] = p;
5583 if ((q = strstrW(p, separator)))
5587 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5593 vector = temp_vector;
5602 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5604 MSIPACKAGE *package = param;
5607 SC_HANDLE scm = NULL, service = NULL;
5608 LPCWSTR component, *vector = NULL;
5609 LPWSTR name, args, display_name = NULL;
5610 DWORD event, numargs, len, wait, dummy;
5611 UINT r = ERROR_FUNCTION_FAILED;
5612 SERVICE_STATUS_PROCESS status;
5613 ULONGLONG start_time;
5615 component = MSI_RecordGetString(rec, 6);
5616 comp = msi_get_loaded_component(package, component);
5618 return ERROR_SUCCESS;
5620 comp->Action = msi_get_component_action( package, comp );
5621 if (comp->Action != INSTALLSTATE_LOCAL)
5623 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5624 return ERROR_SUCCESS;
5627 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5628 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5629 event = MSI_RecordGetInteger(rec, 3);
5630 wait = MSI_RecordGetInteger(rec, 5);
5632 if (!(event & msidbServiceControlEventStart))
5638 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5641 ERR("Failed to open the service control manager\n");
5646 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5647 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5649 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5650 GetServiceDisplayNameW( scm, name, display_name, &len );
5653 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5656 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5660 vector = msi_service_args_to_vector(args, &numargs);
5662 if (!StartServiceW(service, numargs, vector) &&
5663 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5665 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5672 /* wait for at most 30 seconds for the service to be up and running */
5673 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5674 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5676 TRACE("failed to query service status (%u)\n", GetLastError());
5679 start_time = GetTickCount64();
5680 while (status.dwCurrentState == SERVICE_START_PENDING)
5682 if (GetTickCount64() - start_time > 30000) break;
5684 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5685 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5687 TRACE("failed to query service status (%u)\n", GetLastError());
5691 if (status.dwCurrentState != SERVICE_RUNNING)
5693 WARN("service failed to start %u\n", status.dwCurrentState);
5694 r = ERROR_FUNCTION_FAILED;
5699 uirow = MSI_CreateRecord( 2 );
5700 MSI_RecordSetStringW( uirow, 1, display_name );
5701 MSI_RecordSetStringW( uirow, 2, name );
5702 msi_ui_actiondata( package, szStartServices, uirow );
5703 msiobj_release( &uirow->hdr );
5705 CloseServiceHandle(service);
5706 CloseServiceHandle(scm);
5711 msi_free(display_name);
5715 static UINT ACTION_StartServices( MSIPACKAGE *package )
5717 static const WCHAR query[] = {
5718 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5719 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5723 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5724 if (rc != ERROR_SUCCESS)
5725 return ERROR_SUCCESS;
5727 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5728 msiobj_release(&view->hdr);
5732 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5734 DWORD i, needed, count;
5735 ENUM_SERVICE_STATUSW *dependencies;
5739 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5740 0, &needed, &count))
5743 if (GetLastError() != ERROR_MORE_DATA)
5746 dependencies = msi_alloc(needed);
5750 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5751 needed, &needed, &count))
5754 for (i = 0; i < count; i++)
5756 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5757 SERVICE_STOP | SERVICE_QUERY_STATUS);
5761 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5768 msi_free(dependencies);
5772 static UINT stop_service( LPCWSTR name )
5774 SC_HANDLE scm = NULL, service = NULL;
5775 SERVICE_STATUS status;
5776 SERVICE_STATUS_PROCESS ssp;
5779 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5782 WARN("Failed to open the SCM: %d\n", GetLastError());
5786 service = OpenServiceW(scm, name,
5788 SERVICE_QUERY_STATUS |
5789 SERVICE_ENUMERATE_DEPENDENTS);
5792 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5796 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5797 sizeof(SERVICE_STATUS_PROCESS), &needed))
5799 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5803 if (ssp.dwCurrentState == SERVICE_STOPPED)
5806 stop_service_dependents(scm, service);
5808 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5809 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5812 CloseServiceHandle(service);
5813 CloseServiceHandle(scm);
5815 return ERROR_SUCCESS;
5818 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5820 MSIPACKAGE *package = param;
5824 LPWSTR name = NULL, display_name = NULL;
5828 event = MSI_RecordGetInteger( rec, 3 );
5829 if (!(event & msidbServiceControlEventStop))
5830 return ERROR_SUCCESS;
5832 component = MSI_RecordGetString( rec, 6 );
5833 comp = msi_get_loaded_component( package, component );
5835 return ERROR_SUCCESS;
5837 comp->Action = msi_get_component_action( package, comp );
5838 if (comp->Action != INSTALLSTATE_ABSENT)
5840 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5841 return ERROR_SUCCESS;
5844 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5847 ERR("Failed to open the service control manager\n");
5852 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5853 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5855 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5856 GetServiceDisplayNameW( scm, name, display_name, &len );
5858 CloseServiceHandle( scm );
5860 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5861 stop_service( name );
5864 uirow = MSI_CreateRecord( 2 );
5865 MSI_RecordSetStringW( uirow, 1, display_name );
5866 MSI_RecordSetStringW( uirow, 2, name );
5867 msi_ui_actiondata( package, szStopServices, uirow );
5868 msiobj_release( &uirow->hdr );
5871 msi_free( display_name );
5872 return ERROR_SUCCESS;
5875 static UINT ACTION_StopServices( MSIPACKAGE *package )
5877 static const WCHAR query[] = {
5878 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5879 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5883 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5884 if (rc != ERROR_SUCCESS)
5885 return ERROR_SUCCESS;
5887 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5888 msiobj_release(&view->hdr);
5892 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5894 MSIPACKAGE *package = param;
5898 LPWSTR name = NULL, display_name = NULL;
5900 SC_HANDLE scm = NULL, service = NULL;
5902 event = MSI_RecordGetInteger( rec, 3 );
5903 if (!(event & msidbServiceControlEventDelete))
5904 return ERROR_SUCCESS;
5906 component = MSI_RecordGetString(rec, 6);
5907 comp = msi_get_loaded_component(package, component);
5909 return ERROR_SUCCESS;
5911 comp->Action = msi_get_component_action( package, comp );
5912 if (comp->Action != INSTALLSTATE_ABSENT)
5914 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5915 return ERROR_SUCCESS;
5918 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5919 stop_service( name );
5921 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5924 WARN("Failed to open the SCM: %d\n", GetLastError());
5929 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5930 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5932 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5933 GetServiceDisplayNameW( scm, name, display_name, &len );
5936 service = OpenServiceW( scm, name, DELETE );
5939 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5943 if (!DeleteService( service ))
5944 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5947 uirow = MSI_CreateRecord( 2 );
5948 MSI_RecordSetStringW( uirow, 1, display_name );
5949 MSI_RecordSetStringW( uirow, 2, name );
5950 msi_ui_actiondata( package, szDeleteServices, uirow );
5951 msiobj_release( &uirow->hdr );
5953 CloseServiceHandle( service );
5954 CloseServiceHandle( scm );
5956 msi_free( display_name );
5958 return ERROR_SUCCESS;
5961 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5963 static const WCHAR query[] = {
5964 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5965 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5969 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5970 if (rc != ERROR_SUCCESS)
5971 return ERROR_SUCCESS;
5973 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
5974 msiobj_release( &view->hdr );
5978 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
5980 MSIPACKAGE *package = param;
5981 LPWSTR driver, driver_path, ptr;
5982 WCHAR outpath[MAX_PATH];
5983 MSIFILE *driver_file = NULL, *setup_file = NULL;
5986 LPCWSTR desc, file_key, component;
5988 UINT r = ERROR_SUCCESS;
5990 static const WCHAR driver_fmt[] = {
5991 'D','r','i','v','e','r','=','%','s',0};
5992 static const WCHAR setup_fmt[] = {
5993 'S','e','t','u','p','=','%','s',0};
5994 static const WCHAR usage_fmt[] = {
5995 'F','i','l','e','U','s','a','g','e','=','1',0};
5997 component = MSI_RecordGetString( rec, 2 );
5998 comp = msi_get_loaded_component( package, component );
6000 return ERROR_SUCCESS;
6002 comp->Action = msi_get_component_action( package, comp );
6003 if (comp->Action != INSTALLSTATE_LOCAL)
6005 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6006 return ERROR_SUCCESS;
6008 desc = MSI_RecordGetString(rec, 3);
6010 file_key = MSI_RecordGetString( rec, 4 );
6011 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6013 file_key = MSI_RecordGetString( rec, 5 );
6014 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6018 ERR("ODBC Driver entry not found!\n");
6019 return ERROR_FUNCTION_FAILED;
6022 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6024 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6025 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6027 driver = msi_alloc(len * sizeof(WCHAR));
6029 return ERROR_OUTOFMEMORY;
6032 lstrcpyW(ptr, desc);
6033 ptr += lstrlenW(ptr) + 1;
6035 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6040 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6044 lstrcpyW(ptr, usage_fmt);
6045 ptr += lstrlenW(ptr) + 1;
6048 driver_path = strdupW(driver_file->TargetPath);
6049 ptr = strrchrW(driver_path, '\\');
6050 if (ptr) *ptr = '\0';
6052 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6053 NULL, ODBC_INSTALL_COMPLETE, &usage))
6055 ERR("Failed to install SQL driver!\n");
6056 r = ERROR_FUNCTION_FAILED;
6059 uirow = MSI_CreateRecord( 5 );
6060 MSI_RecordSetStringW( uirow, 1, desc );
6061 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6062 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6063 msi_ui_actiondata( package, szInstallODBC, uirow );
6064 msiobj_release( &uirow->hdr );
6067 msi_free(driver_path);
6072 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6074 MSIPACKAGE *package = param;
6075 LPWSTR translator, translator_path, ptr;
6076 WCHAR outpath[MAX_PATH];
6077 MSIFILE *translator_file = NULL, *setup_file = NULL;
6080 LPCWSTR desc, file_key, component;
6082 UINT r = ERROR_SUCCESS;
6084 static const WCHAR translator_fmt[] = {
6085 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6086 static const WCHAR setup_fmt[] = {
6087 'S','e','t','u','p','=','%','s',0};
6089 component = MSI_RecordGetString( rec, 2 );
6090 comp = msi_get_loaded_component( package, component );
6092 return ERROR_SUCCESS;
6094 comp->Action = msi_get_component_action( package, comp );
6095 if (comp->Action != INSTALLSTATE_LOCAL)
6097 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6098 return ERROR_SUCCESS;
6100 desc = MSI_RecordGetString(rec, 3);
6102 file_key = MSI_RecordGetString( rec, 4 );
6103 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6105 file_key = MSI_RecordGetString( rec, 5 );
6106 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6108 if (!translator_file)
6110 ERR("ODBC Translator entry not found!\n");
6111 return ERROR_FUNCTION_FAILED;
6114 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6116 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6118 translator = msi_alloc(len * sizeof(WCHAR));
6120 return ERROR_OUTOFMEMORY;
6123 lstrcpyW(ptr, desc);
6124 ptr += lstrlenW(ptr) + 1;
6126 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6131 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6136 translator_path = strdupW(translator_file->TargetPath);
6137 ptr = strrchrW(translator_path, '\\');
6138 if (ptr) *ptr = '\0';
6140 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6141 NULL, ODBC_INSTALL_COMPLETE, &usage))
6143 ERR("Failed to install SQL translator!\n");
6144 r = ERROR_FUNCTION_FAILED;
6147 uirow = MSI_CreateRecord( 5 );
6148 MSI_RecordSetStringW( uirow, 1, desc );
6149 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6150 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6151 msi_ui_actiondata( package, szInstallODBC, uirow );
6152 msiobj_release( &uirow->hdr );
6154 msi_free(translator);
6155 msi_free(translator_path);
6160 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6162 MSIPACKAGE *package = param;
6165 LPCWSTR desc, driver, component;
6166 WORD request = ODBC_ADD_SYS_DSN;
6169 UINT r = ERROR_SUCCESS;
6172 static const WCHAR attrs_fmt[] = {
6173 'D','S','N','=','%','s',0 };
6175 component = MSI_RecordGetString( rec, 2 );
6176 comp = msi_get_loaded_component( package, component );
6178 return ERROR_SUCCESS;
6180 comp->Action = msi_get_component_action( package, comp );
6181 if (comp->Action != INSTALLSTATE_LOCAL)
6183 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6184 return ERROR_SUCCESS;
6187 desc = MSI_RecordGetString(rec, 3);
6188 driver = MSI_RecordGetString(rec, 4);
6189 registration = MSI_RecordGetInteger(rec, 5);
6191 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6192 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6194 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6195 attrs = msi_alloc(len * sizeof(WCHAR));
6197 return ERROR_OUTOFMEMORY;
6199 len = sprintfW(attrs, attrs_fmt, desc);
6202 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6204 ERR("Failed to install SQL data source!\n");
6205 r = ERROR_FUNCTION_FAILED;
6208 uirow = MSI_CreateRecord( 5 );
6209 MSI_RecordSetStringW( uirow, 1, desc );
6210 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6211 MSI_RecordSetInteger( uirow, 3, request );
6212 msi_ui_actiondata( package, szInstallODBC, uirow );
6213 msiobj_release( &uirow->hdr );
6220 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6222 static const WCHAR driver_query[] = {
6223 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6224 'O','D','B','C','D','r','i','v','e','r',0};
6225 static const WCHAR translator_query[] = {
6226 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6227 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6228 static const WCHAR source_query[] = {
6229 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6230 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6234 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6235 if (rc == ERROR_SUCCESS)
6237 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6238 msiobj_release(&view->hdr);
6239 if (rc != ERROR_SUCCESS)
6242 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6243 if (rc == ERROR_SUCCESS)
6245 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6246 msiobj_release(&view->hdr);
6247 if (rc != ERROR_SUCCESS)
6250 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6251 if (rc == ERROR_SUCCESS)
6253 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6254 msiobj_release(&view->hdr);
6255 if (rc != ERROR_SUCCESS)
6258 return ERROR_SUCCESS;
6261 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6263 MSIPACKAGE *package = param;
6267 LPCWSTR desc, component;
6269 component = MSI_RecordGetString( rec, 2 );
6270 comp = msi_get_loaded_component( package, component );
6272 return ERROR_SUCCESS;
6274 comp->Action = msi_get_component_action( package, comp );
6275 if (comp->Action != INSTALLSTATE_ABSENT)
6277 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6278 return ERROR_SUCCESS;
6281 desc = MSI_RecordGetString( rec, 3 );
6282 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6284 WARN("Failed to remove ODBC driver\n");
6288 FIXME("Usage count reached 0\n");
6291 uirow = MSI_CreateRecord( 2 );
6292 MSI_RecordSetStringW( uirow, 1, desc );
6293 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6294 msi_ui_actiondata( package, szRemoveODBC, uirow );
6295 msiobj_release( &uirow->hdr );
6297 return ERROR_SUCCESS;
6300 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6302 MSIPACKAGE *package = param;
6306 LPCWSTR desc, component;
6308 component = MSI_RecordGetString( rec, 2 );
6309 comp = msi_get_loaded_component( package, component );
6311 return ERROR_SUCCESS;
6313 comp->Action = msi_get_component_action( package, comp );
6314 if (comp->Action != INSTALLSTATE_ABSENT)
6316 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6317 return ERROR_SUCCESS;
6320 desc = MSI_RecordGetString( rec, 3 );
6321 if (!SQLRemoveTranslatorW( desc, &usage ))
6323 WARN("Failed to remove ODBC translator\n");
6327 FIXME("Usage count reached 0\n");
6330 uirow = MSI_CreateRecord( 2 );
6331 MSI_RecordSetStringW( uirow, 1, desc );
6332 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6333 msi_ui_actiondata( package, szRemoveODBC, uirow );
6334 msiobj_release( &uirow->hdr );
6336 return ERROR_SUCCESS;
6339 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6341 MSIPACKAGE *package = param;
6345 LPCWSTR desc, driver, component;
6346 WORD request = ODBC_REMOVE_SYS_DSN;
6350 static const WCHAR attrs_fmt[] = {
6351 'D','S','N','=','%','s',0 };
6353 component = MSI_RecordGetString( rec, 2 );
6354 comp = msi_get_loaded_component( package, component );
6356 return ERROR_SUCCESS;
6358 comp->Action = msi_get_component_action( package, comp );
6359 if (comp->Action != INSTALLSTATE_ABSENT)
6361 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6362 return ERROR_SUCCESS;
6365 desc = MSI_RecordGetString( rec, 3 );
6366 driver = MSI_RecordGetString( rec, 4 );
6367 registration = MSI_RecordGetInteger( rec, 5 );
6369 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6370 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6372 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6373 attrs = msi_alloc( len * sizeof(WCHAR) );
6375 return ERROR_OUTOFMEMORY;
6377 FIXME("Use ODBCSourceAttribute table\n");
6379 len = sprintfW( attrs, attrs_fmt, desc );
6382 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6384 WARN("Failed to remove ODBC data source\n");
6388 uirow = MSI_CreateRecord( 3 );
6389 MSI_RecordSetStringW( uirow, 1, desc );
6390 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6391 MSI_RecordSetInteger( uirow, 3, request );
6392 msi_ui_actiondata( package, szRemoveODBC, uirow );
6393 msiobj_release( &uirow->hdr );
6395 return ERROR_SUCCESS;
6398 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6400 static const WCHAR driver_query[] = {
6401 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6402 'O','D','B','C','D','r','i','v','e','r',0};
6403 static const WCHAR translator_query[] = {
6404 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6405 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6406 static const WCHAR source_query[] = {
6407 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6408 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6412 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6413 if (rc == ERROR_SUCCESS)
6415 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6416 msiobj_release( &view->hdr );
6417 if (rc != ERROR_SUCCESS)
6420 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6421 if (rc == ERROR_SUCCESS)
6423 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6424 msiobj_release( &view->hdr );
6425 if (rc != ERROR_SUCCESS)
6428 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6429 if (rc == ERROR_SUCCESS)
6431 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6432 msiobj_release( &view->hdr );
6433 if (rc != ERROR_SUCCESS)
6436 return ERROR_SUCCESS;
6439 #define ENV_ACT_SETALWAYS 0x1
6440 #define ENV_ACT_SETABSENT 0x2
6441 #define ENV_ACT_REMOVE 0x4
6442 #define ENV_ACT_REMOVEMATCH 0x8
6444 #define ENV_MOD_MACHINE 0x20000000
6445 #define ENV_MOD_APPEND 0x40000000
6446 #define ENV_MOD_PREFIX 0x80000000
6447 #define ENV_MOD_MASK 0xC0000000
6449 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6451 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6453 LPCWSTR cptr = *name;
6455 static const WCHAR prefix[] = {'[','~',']',0};
6456 static const int prefix_len = 3;
6462 *flags |= ENV_ACT_SETALWAYS;
6463 else if (*cptr == '+')
6464 *flags |= ENV_ACT_SETABSENT;
6465 else if (*cptr == '-')
6466 *flags |= ENV_ACT_REMOVE;
6467 else if (*cptr == '!')
6468 *flags |= ENV_ACT_REMOVEMATCH;
6469 else if (*cptr == '*')
6470 *flags |= ENV_MOD_MACHINE;
6480 ERR("Missing environment variable\n");
6481 return ERROR_FUNCTION_FAILED;
6486 LPCWSTR ptr = *value;
6487 if (!strncmpW(ptr, prefix, prefix_len))
6489 if (ptr[prefix_len] == szSemiColon[0])
6491 *flags |= ENV_MOD_APPEND;
6492 *value += lstrlenW(prefix);
6499 else if (lstrlenW(*value) >= prefix_len)
6501 ptr += lstrlenW(ptr) - prefix_len;
6502 if (!strcmpW( ptr, prefix ))
6504 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6506 *flags |= ENV_MOD_PREFIX;
6507 /* the "[~]" will be removed by deformat_string */;
6517 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6518 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6519 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6520 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6522 ERR("Invalid flags: %08x\n", *flags);
6523 return ERROR_FUNCTION_FAILED;
6527 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6529 return ERROR_SUCCESS;
6532 static UINT open_env_key( DWORD flags, HKEY *key )
6534 static const WCHAR user_env[] =
6535 {'E','n','v','i','r','o','n','m','e','n','t',0};
6536 static const WCHAR machine_env[] =
6537 {'S','y','s','t','e','m','\\',
6538 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6539 'C','o','n','t','r','o','l','\\',
6540 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6541 'E','n','v','i','r','o','n','m','e','n','t',0};
6546 if (flags & ENV_MOD_MACHINE)
6549 root = HKEY_LOCAL_MACHINE;
6554 root = HKEY_CURRENT_USER;
6557 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6558 if (res != ERROR_SUCCESS)
6560 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6561 return ERROR_FUNCTION_FAILED;
6564 return ERROR_SUCCESS;
6567 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6569 MSIPACKAGE *package = param;
6570 LPCWSTR name, value, component;
6571 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6572 DWORD flags, type, size;
6579 component = MSI_RecordGetString(rec, 4);
6580 comp = msi_get_loaded_component(package, component);
6582 return ERROR_SUCCESS;
6584 comp->Action = msi_get_component_action( package, comp );
6585 if (comp->Action != INSTALLSTATE_LOCAL)
6587 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6588 return ERROR_SUCCESS;
6590 name = MSI_RecordGetString(rec, 2);
6591 value = MSI_RecordGetString(rec, 3);
6593 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6595 res = env_parse_flags(&name, &value, &flags);
6596 if (res != ERROR_SUCCESS || !value)
6599 if (value && !deformat_string(package, value, &deformatted))
6601 res = ERROR_OUTOFMEMORY;
6605 value = deformatted;
6607 res = open_env_key( flags, &env );
6608 if (res != ERROR_SUCCESS)
6611 if (flags & ENV_MOD_MACHINE)
6612 action |= 0x20000000;
6616 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6617 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6618 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6621 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6625 /* Nothing to do. */
6628 res = ERROR_SUCCESS;
6632 /* If we are appending but the string was empty, strip ; */
6633 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6635 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6636 newval = strdupW(value);
6639 res = ERROR_OUTOFMEMORY;
6647 /* Contrary to MSDN, +-variable to [~];path works */
6648 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6650 res = ERROR_SUCCESS;
6654 data = msi_alloc(size);
6658 return ERROR_OUTOFMEMORY;
6661 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6662 if (res != ERROR_SUCCESS)
6665 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6668 res = RegDeleteValueW(env, name);
6669 if (res != ERROR_SUCCESS)
6670 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6674 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6675 if (flags & ENV_MOD_MASK)
6679 if (flags & ENV_MOD_APPEND) multiplier++;
6680 if (flags & ENV_MOD_PREFIX) multiplier++;
6681 mod_size = lstrlenW(value) * multiplier;
6682 size += mod_size * sizeof(WCHAR);
6685 newval = msi_alloc(size);
6689 res = ERROR_OUTOFMEMORY;
6693 if (flags & ENV_MOD_PREFIX)
6695 lstrcpyW(newval, value);
6696 ptr = newval + lstrlenW(value);
6697 action |= 0x80000000;
6700 lstrcpyW(ptr, data);
6702 if (flags & ENV_MOD_APPEND)
6704 lstrcatW(newval, value);
6705 action |= 0x40000000;
6708 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6709 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6712 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6716 uirow = MSI_CreateRecord( 3 );
6717 MSI_RecordSetStringW( uirow, 1, name );
6718 MSI_RecordSetStringW( uirow, 2, newval );
6719 MSI_RecordSetInteger( uirow, 3, action );
6720 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6721 msiobj_release( &uirow->hdr );
6723 if (env) RegCloseKey(env);
6724 msi_free(deformatted);
6730 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6732 static const WCHAR query[] = {
6733 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6734 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6738 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6739 if (rc != ERROR_SUCCESS)
6740 return ERROR_SUCCESS;
6742 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6743 msiobj_release(&view->hdr);
6747 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6749 MSIPACKAGE *package = param;
6750 LPCWSTR name, value, component;
6751 LPWSTR deformatted = NULL;
6760 component = MSI_RecordGetString( rec, 4 );
6761 comp = msi_get_loaded_component( package, component );
6763 return ERROR_SUCCESS;
6765 comp->Action = msi_get_component_action( package, comp );
6766 if (comp->Action != INSTALLSTATE_ABSENT)
6768 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6769 return ERROR_SUCCESS;
6771 name = MSI_RecordGetString( rec, 2 );
6772 value = MSI_RecordGetString( rec, 3 );
6774 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6776 r = env_parse_flags( &name, &value, &flags );
6777 if (r != ERROR_SUCCESS)
6780 if (!(flags & ENV_ACT_REMOVE))
6782 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6783 return ERROR_SUCCESS;
6786 if (value && !deformat_string( package, value, &deformatted ))
6787 return ERROR_OUTOFMEMORY;
6789 value = deformatted;
6791 r = open_env_key( flags, &env );
6792 if (r != ERROR_SUCCESS)
6798 if (flags & ENV_MOD_MACHINE)
6799 action |= 0x20000000;
6801 TRACE("Removing %s\n", debugstr_w(name));
6803 res = RegDeleteValueW( env, name );
6804 if (res != ERROR_SUCCESS)
6806 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6811 uirow = MSI_CreateRecord( 3 );
6812 MSI_RecordSetStringW( uirow, 1, name );
6813 MSI_RecordSetStringW( uirow, 2, value );
6814 MSI_RecordSetInteger( uirow, 3, action );
6815 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6816 msiobj_release( &uirow->hdr );
6818 if (env) RegCloseKey( env );
6819 msi_free( deformatted );
6823 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6825 static const WCHAR query[] = {
6826 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6827 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6831 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6832 if (rc != ERROR_SUCCESS)
6833 return ERROR_SUCCESS;
6835 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6836 msiobj_release( &view->hdr );
6840 UINT msi_validate_product_id( MSIPACKAGE *package )
6842 LPWSTR key, template, id;
6843 UINT r = ERROR_SUCCESS;
6845 id = msi_dup_property( package->db, szProductID );
6849 return ERROR_SUCCESS;
6851 template = msi_dup_property( package->db, szPIDTemplate );
6852 key = msi_dup_property( package->db, szPIDKEY );
6853 if (key && template)
6855 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6856 r = msi_set_property( package->db, szProductID, key );
6858 msi_free( template );
6863 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6865 return msi_validate_product_id( package );
6868 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6871 package->need_reboot = 1;
6872 return ERROR_SUCCESS;
6875 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6877 static const WCHAR szAvailableFreeReg[] =
6878 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6880 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
6882 TRACE("%p %d kilobytes\n", package, space);
6884 uirow = MSI_CreateRecord( 1 );
6885 MSI_RecordSetInteger( uirow, 1, space );
6886 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
6887 msiobj_release( &uirow->hdr );
6889 return ERROR_SUCCESS;
6892 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6894 TRACE("%p\n", package);
6896 msi_set_property( package->db, szRollbackDisabled, szOne );
6897 return ERROR_SUCCESS;
6900 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6902 FIXME("%p\n", package);
6903 return ERROR_SUCCESS;
6906 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6908 static const WCHAR driver_query[] = {
6909 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6910 'O','D','B','C','D','r','i','v','e','r',0};
6911 static const WCHAR translator_query[] = {
6912 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6913 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6917 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6918 if (r == ERROR_SUCCESS)
6921 r = MSI_IterateRecords( view, &count, NULL, package );
6922 msiobj_release( &view->hdr );
6923 if (r != ERROR_SUCCESS)
6925 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
6927 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6928 if (r == ERROR_SUCCESS)
6931 r = MSI_IterateRecords( view, &count, NULL, package );
6932 msiobj_release( &view->hdr );
6933 if (r != ERROR_SUCCESS)
6935 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
6937 return ERROR_SUCCESS;
6940 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
6942 MSIPACKAGE *package = param;
6943 const WCHAR *property = MSI_RecordGetString( rec, 1 );
6946 if ((value = msi_dup_property( package->db, property )))
6948 FIXME("remove %s\n", debugstr_w(value));
6951 return ERROR_SUCCESS;
6954 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6956 static const WCHAR query[] = {
6957 'S','E','L','E','C','T',' ','A','c','t','i','o','n','P','r','o','p','e','r','t','y',' ',
6958 'F','R','O','M',' ','U','p','g','r','a','d','e',0};
6962 r = MSI_DatabaseOpenViewW( package->db, query, &view );
6963 if (r == ERROR_SUCCESS)
6965 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
6966 msiobj_release( &view->hdr );
6967 if (r != ERROR_SUCCESS)
6970 return ERROR_SUCCESS;
6973 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
6975 MSIPACKAGE *package = param;
6976 int attributes = MSI_RecordGetInteger( rec, 5 );
6978 if (attributes & msidbUpgradeAttributesMigrateFeatures)
6980 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
6981 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
6982 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
6983 const WCHAR *language = MSI_RecordGetString( rec, 4 );
6987 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
6989 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
6990 if (r != ERROR_SUCCESS)
6991 return ERROR_SUCCESS;
6995 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
6996 if (r != ERROR_SUCCESS)
6997 return ERROR_SUCCESS;
6999 RegCloseKey( hkey );
7001 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7002 debugstr_w(upgrade_code), debugstr_w(version_min),
7003 debugstr_w(version_max), debugstr_w(language));
7005 return ERROR_SUCCESS;
7008 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7010 static const WCHAR query[] = {
7011 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7012 'U','p','g','r','a','d','e',0};
7016 if (msi_get_property_int( package->db, szInstalled, 0 ))
7018 TRACE("product is installed, skipping action\n");
7019 return ERROR_SUCCESS;
7021 if (msi_get_property_int( package->db, szPreselected, 0 ))
7023 TRACE("Preselected property is set, not migrating feature states\n");
7024 return ERROR_SUCCESS;
7026 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7027 if (r == ERROR_SUCCESS)
7029 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7030 msiobj_release( &view->hdr );
7031 if (r != ERROR_SUCCESS)
7034 return ERROR_SUCCESS;
7037 static void bind_image( const char *filename, const char *path )
7039 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7041 WARN("failed to bind image %u\n", GetLastError());
7045 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7049 MSIPACKAGE *package = param;
7050 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7051 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7052 char *filenameA, *pathA;
7053 WCHAR *pathW, **path_list;
7055 if (!(file = msi_get_loaded_file( package, key )))
7057 WARN("file %s not found\n", debugstr_w(key));
7058 return ERROR_SUCCESS;
7060 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7061 path_list = msi_split_string( paths, ';' );
7062 if (!path_list) bind_image( filenameA, NULL );
7065 for (i = 0; path_list[i] && path_list[i][0]; i++)
7067 deformat_string( package, path_list[i], &pathW );
7068 if ((pathA = strdupWtoA( pathW )))
7070 bind_image( filenameA, pathA );
7076 msi_free( path_list );
7077 msi_free( filenameA );
7078 return ERROR_SUCCESS;
7081 static UINT ACTION_BindImage( MSIPACKAGE *package )
7083 static const WCHAR query[] = {
7084 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7085 'B','i','n','d','I','m','a','g','e',0};
7089 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7090 if (r == ERROR_SUCCESS)
7092 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7093 msiobj_release( &view->hdr );
7094 if (r != ERROR_SUCCESS)
7097 return ERROR_SUCCESS;
7100 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7102 static const WCHAR query[] = {
7103 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7108 r = MSI_OpenQuery( package->db, &view, query, table );
7109 if (r == ERROR_SUCCESS)
7111 r = MSI_IterateRecords(view, &count, NULL, package);
7112 msiobj_release(&view->hdr);
7113 if (r != ERROR_SUCCESS)
7116 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7117 return ERROR_SUCCESS;
7120 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7122 static const WCHAR table[] = {
7123 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7124 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7127 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7129 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7130 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7133 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7135 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7136 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7139 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7141 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7142 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7145 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7147 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7148 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7153 const WCHAR *action;
7154 UINT (*handler)(MSIPACKAGE *);
7155 const WCHAR *action_rollback;
7159 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7160 { szAppSearch, ACTION_AppSearch, NULL },
7161 { szBindImage, ACTION_BindImage, NULL },
7162 { szCCPSearch, ACTION_CCPSearch, NULL },
7163 { szCostFinalize, ACTION_CostFinalize, NULL },
7164 { szCostInitialize, ACTION_CostInitialize, NULL },
7165 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7166 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7167 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7168 { szDisableRollback, ACTION_DisableRollback, NULL },
7169 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7170 { szExecuteAction, ACTION_ExecuteAction, NULL },
7171 { szFileCost, ACTION_FileCost, NULL },
7172 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7173 { szForceReboot, ACTION_ForceReboot, NULL },
7174 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7175 { szInstallExecute, ACTION_InstallExecute, NULL },
7176 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7177 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7178 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7179 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7180 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7181 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7182 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7183 { szInstallValidate, ACTION_InstallValidate, NULL },
7184 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7185 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7186 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7187 { szMoveFiles, ACTION_MoveFiles, NULL },
7188 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7189 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7190 { szPatchFiles, ACTION_PatchFiles, NULL },
7191 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7192 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7193 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7194 { szPublishProduct, ACTION_PublishProduct, NULL },
7195 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7196 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7197 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7198 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7199 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7200 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7201 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7202 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7203 { szRegisterUser, ACTION_RegisterUser, NULL },
7204 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7205 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7206 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7207 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7208 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7209 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7210 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7211 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7212 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7213 { szResolveSource, ACTION_ResolveSource, NULL },
7214 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7215 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7216 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7217 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7218 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7219 { szStartServices, ACTION_StartServices, szStopServices },
7220 { szStopServices, ACTION_StopServices, szStartServices },
7221 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7222 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7223 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7224 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7225 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7226 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7227 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7228 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7229 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7230 { szValidateProductID, ACTION_ValidateProductID, NULL },
7231 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7232 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7233 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7234 { NULL, NULL, NULL }
7237 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7243 while (StandardActions[i].action != NULL)
7245 if (!strcmpW( StandardActions[i].action, action ))
7247 ui_actionstart( package, action );
7248 if (StandardActions[i].handler)
7250 ui_actioninfo( package, action, TRUE, 0 );
7251 *rc = StandardActions[i].handler( package );
7252 ui_actioninfo( package, action, FALSE, *rc );
7254 if (StandardActions[i].action_rollback && !package->need_rollback)
7256 TRACE("scheduling rollback action\n");
7257 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7262 FIXME("unhandled standard action %s\n", debugstr_w(action));
7263 *rc = ERROR_SUCCESS;
7273 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7275 UINT rc = ERROR_SUCCESS;
7278 TRACE("Performing action (%s)\n", debugstr_w(action));
7280 handled = ACTION_HandleStandardAction(package, action, &rc);
7283 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7287 WARN("unhandled msi action %s\n", debugstr_w(action));
7288 rc = ERROR_FUNCTION_NOT_CALLED;
7294 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7296 UINT rc = ERROR_SUCCESS;
7297 BOOL handled = FALSE;
7299 TRACE("Performing action (%s)\n", debugstr_w(action));
7301 handled = ACTION_HandleStandardAction(package, action, &rc);
7304 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7306 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7311 WARN("unhandled msi action %s\n", debugstr_w(action));
7312 rc = ERROR_FUNCTION_NOT_CALLED;
7318 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7320 UINT rc = ERROR_SUCCESS;
7323 static const WCHAR query[] =
7324 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7325 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7326 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7327 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7328 static const WCHAR ui_query[] =
7329 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7330 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7331 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7332 ' ', '=',' ','%','i',0};
7334 if (needs_ui_sequence(package))
7335 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7337 row = MSI_QueryGetRecord(package->db, query, seq);
7341 LPCWSTR action, cond;
7343 TRACE("Running the actions\n");
7345 /* check conditions */
7346 cond = MSI_RecordGetString(row, 2);
7348 /* this is a hack to skip errors in the condition code */
7349 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7351 msiobj_release(&row->hdr);
7352 return ERROR_SUCCESS;
7355 action = MSI_RecordGetString(row, 1);
7358 ERR("failed to fetch action\n");
7359 msiobj_release(&row->hdr);
7360 return ERROR_FUNCTION_FAILED;
7363 if (needs_ui_sequence(package))
7364 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7366 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7368 msiobj_release(&row->hdr);
7374 /****************************************************
7375 * TOP level entry points
7376 *****************************************************/
7378 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7379 LPCWSTR szCommandLine )
7381 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7382 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7383 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7384 WCHAR *reinstall = NULL;
7388 msi_set_property( package->db, szAction, szInstall );
7390 package->script->InWhatSequence = SEQUENCE_INSTALL;
7397 dir = strdupW(szPackagePath);
7398 p = strrchrW(dir, '\\');
7402 file = szPackagePath + (p - dir);
7407 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7408 GetCurrentDirectoryW(MAX_PATH, dir);
7409 lstrcatW(dir, szBackSlash);
7410 file = szPackagePath;
7413 msi_free( package->PackagePath );
7414 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7415 if (!package->PackagePath)
7418 return ERROR_OUTOFMEMORY;
7421 lstrcpyW(package->PackagePath, dir);
7422 lstrcatW(package->PackagePath, file);
7425 msi_set_sourcedir_props(package, FALSE);
7428 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7429 if (rc != ERROR_SUCCESS)
7432 msi_apply_transforms( package );
7433 msi_apply_patches( package );
7435 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7437 TRACE("setting reinstall property\n");
7438 msi_set_property( package->db, szReinstall, szAll );
7441 /* properties may have been added by a transform */
7442 msi_clone_properties( package );
7444 msi_parse_command_line( package, szCommandLine, FALSE );
7445 msi_adjust_privilege_properties( package );
7446 msi_set_context( package );
7448 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7450 TRACE("disabling rollback\n");
7451 msi_set_property( package->db, szRollbackDisabled, szOne );
7454 if (needs_ui_sequence( package))
7456 package->script->InWhatSequence |= SEQUENCE_UI;
7457 rc = ACTION_ProcessUISequence(package);
7458 ui_exists = ui_sequence_exists(package);
7459 if (rc == ERROR_SUCCESS || !ui_exists)
7461 package->script->InWhatSequence |= SEQUENCE_EXEC;
7462 rc = ACTION_ProcessExecSequence(package, ui_exists);
7466 rc = ACTION_ProcessExecSequence(package, FALSE);
7468 package->script->CurrentlyScripting = FALSE;
7470 /* process the ending type action */
7471 if (rc == ERROR_SUCCESS)
7472 ACTION_PerformActionSequence(package, -1);
7473 else if (rc == ERROR_INSTALL_USEREXIT)
7474 ACTION_PerformActionSequence(package, -2);
7475 else if (rc == ERROR_INSTALL_SUSPEND)
7476 ACTION_PerformActionSequence(package, -4);
7479 ACTION_PerformActionSequence(package, -3);
7480 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7482 package->need_rollback = TRUE;
7486 /* finish up running custom actions */
7487 ACTION_FinishCustomActions(package);
7489 if (package->need_rollback && !(reinstall = msi_dup_property( package->db, szReinstall )))
7491 WARN("installation failed, running rollback script\n");
7492 execute_script( package, SCRIPT_ROLLBACK );
7494 msi_free( reinstall );
7496 if (rc == ERROR_SUCCESS && package->need_reboot)
7497 return ERROR_SUCCESS_REBOOT_REQUIRED;