2 * Custom Action processing for the Microsoft Installer (msi.dll)
4 * Copyright 2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "wine/debug.h"
31 #include "wine/unicode.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(msi);
35 #define CUSTOM_ACTION_TYPE_MASK 0x3F
36 static const WCHAR c_collen[] = {'C',':','\\',0};
37 static const WCHAR cszTempFolder[]= {'T','e','m','p','F','o','l','d','e','r',0};
39 typedef struct tagMSIRUNNINGACTION
47 static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source,
48 LPCWSTR target, const INT type, LPCWSTR action);
49 static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source,
50 LPCWSTR target, const INT type, LPCWSTR action);
51 static UINT HANDLE_CustomType17(MSIPACKAGE *package, LPCWSTR source,
52 LPCWSTR target, const INT type, LPCWSTR action);
53 static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source,
54 LPCWSTR target, const INT type, LPCWSTR action);
55 static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
56 LPCWSTR target, const INT type, LPCWSTR action);
57 static UINT HANDLE_CustomType23(MSIPACKAGE *package, LPCWSTR source,
58 LPCWSTR target, const INT type, LPCWSTR action);
59 static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source,
60 LPCWSTR target, const INT type, LPCWSTR action);
61 static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
62 LPCWSTR target, const INT type, LPCWSTR action);
63 static UINT HANDLE_CustomType37_38(MSIPACKAGE *package, LPCWSTR source,
64 LPCWSTR target, const INT type, LPCWSTR action);
65 static UINT HANDLE_CustomType5_6(MSIPACKAGE *package, LPCWSTR source,
66 LPCWSTR target, const INT type, LPCWSTR action);
67 static UINT HANDLE_CustomType21_22(MSIPACKAGE *package, LPCWSTR source,
68 LPCWSTR target, const INT type, LPCWSTR action);
69 static UINT HANDLE_CustomType53_54(MSIPACKAGE *package, LPCWSTR source,
70 LPCWSTR target, const INT type, LPCWSTR action);
72 typedef UINT (WINAPI *MsiCustomActionEntryPoint)( MSIHANDLE );
74 static CRITICAL_SECTION msi_custom_action_cs;
75 static CRITICAL_SECTION_DEBUG msi_custom_action_cs_debug =
77 0, 0, &msi_custom_action_cs,
78 { &msi_custom_action_cs_debug.ProcessLocksList,
79 &msi_custom_action_cs_debug.ProcessLocksList },
80 0, 0, { (DWORD_PTR)(__FILE__ ": msi_custom_action_cs") }
82 static CRITICAL_SECTION msi_custom_action_cs = { &msi_custom_action_cs_debug, -1, 0, 0, 0, 0 };
84 static struct list msi_pending_custom_actions = LIST_INIT( msi_pending_custom_actions );
86 static BOOL check_execution_scheduling_options(MSIPACKAGE *package, LPCWSTR action, UINT options)
91 if ((options & msidbCustomActionTypeClientRepeat) ==
92 msidbCustomActionTypeClientRepeat)
94 if (!(package->script->InWhatSequence & SEQUENCE_UI &&
95 package->script->InWhatSequence & SEQUENCE_EXEC))
97 TRACE("Skipping action due to dbCustomActionTypeClientRepeat option.\n");
101 else if (options & msidbCustomActionTypeFirstSequence)
103 if (package->script->InWhatSequence & SEQUENCE_UI &&
104 package->script->InWhatSequence & SEQUENCE_EXEC )
106 TRACE("Skipping action due to msidbCustomActionTypeFirstSequence option.\n");
110 else if (options & msidbCustomActionTypeOncePerProcess)
112 if (check_unique_action(package,action))
114 TRACE("Skipping action due to msidbCustomActionTypeOncePerProcess option.\n");
118 register_unique_action(package,action);
124 /* stores the CustomActionData before the action:
125 * [CustomActionData]Action
127 static LPWSTR msi_get_deferred_action(LPCWSTR action, LPCWSTR actiondata)
132 static const WCHAR begin[] = {'[',0};
133 static const WCHAR end[] = {']',0};
136 return strdupW(action);
138 len = lstrlenW(action) + lstrlenW(actiondata) + 3;
139 deferred = msi_alloc(len * sizeof(WCHAR));
141 lstrcpyW(deferred, begin);
142 lstrcatW(deferred, actiondata);
143 lstrcatW(deferred, end);
144 lstrcatW(deferred, action);
149 UINT ACTION_CustomAction(MSIPACKAGE *package,LPCWSTR action, BOOL execute)
151 UINT rc = ERROR_SUCCESS;
153 static const WCHAR ExecSeqQuery[] =
154 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
155 '`','C','u','s','t','o' ,'m','A','c','t','i','o','n','`',
156 ' ','W','H','E','R','E',' ','`','A','c','t','i' ,'o','n','`',' ',
157 '=',' ','\'','%','s','\'',0};
159 LPCWSTR source, target;
160 LPWSTR ptr, deferred_data = NULL;
161 LPWSTR action_copy = strdupW(action);
162 WCHAR *deformated=NULL;
164 /* deferred action: [CustomActionData]Action */
165 if ((ptr = strchrW(action_copy, ']')))
167 deferred_data = action_copy + 1;
172 row = MSI_QueryGetRecord( package->db, ExecSeqQuery, action );
175 msi_free(action_copy);
176 return ERROR_CALL_NOT_IMPLEMENTED;
179 type = MSI_RecordGetInteger(row,2);
181 source = MSI_RecordGetString(row,3);
182 target = MSI_RecordGetString(row,4);
184 TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type,
185 debugstr_w(source), debugstr_w(target));
187 /* handle some of the deferred actions */
188 if (type & msidbCustomActionTypeTSAware)
189 FIXME("msidbCustomActionTypeTSAware not handled\n");
191 if (type & msidbCustomActionTypeInScript)
193 if (type & msidbCustomActionTypeNoImpersonate)
194 FIXME("msidbCustomActionTypeNoImpersonate not handled\n");
196 if (type & msidbCustomActionTypeRollback)
198 FIXME("Rollback only action... rollbacks not supported yet\n");
199 schedule_action(package, ROLLBACK_SCRIPT, action);
205 LPWSTR actiondata = msi_dup_property(package, action);
206 LPWSTR deferred = msi_get_deferred_action(action, actiondata);
208 if (type & msidbCustomActionTypeCommit)
210 TRACE("Deferring Commit Action!\n");
211 schedule_action(package, COMMIT_SCRIPT, deferred);
215 TRACE("Deferring Action!\n");
216 schedule_action(package, INSTALL_SCRIPT, deferred);
227 static const WCHAR szActionData[] = {
228 'C','u','s','t','o','m','A','c','t','i','o','n','D','a','t','a',0};
229 static const WCHAR szBlank[] = {0};
230 LPWSTR actiondata = msi_dup_property( package, action );
232 MSI_SetPropertyW(package,szActionData,deferred_data);
234 MSI_SetPropertyW(package,szActionData,actiondata);
236 MSI_SetPropertyW(package,szActionData,szBlank);
237 msi_free(actiondata);
240 else if (!check_execution_scheduling_options(package,action,type))
246 switch (type & CUSTOM_ACTION_TYPE_MASK)
248 case 1: /* DLL file stored in a Binary table stream */
249 rc = HANDLE_CustomType1(package,source,target,type,action);
251 case 2: /* EXE file stored in a Binary table stream */
252 rc = HANDLE_CustomType2(package,source,target,type,action);
254 case 18: /*EXE file installed with package */
255 rc = HANDLE_CustomType18(package,source,target,type,action);
257 case 19: /* Error that halts install */
258 rc = HANDLE_CustomType19(package,source,target,type,action);
261 rc = HANDLE_CustomType17(package,source,target,type,action);
263 case 23: /* installs another package in the source tree */
264 deformat_string(package,target,&deformated);
265 rc = HANDLE_CustomType23(package,source,deformated,type,action);
267 case 50: /*EXE file specified by a property value */
268 rc = HANDLE_CustomType50(package,source,target,type,action);
270 case 34: /*EXE to be run in specified directory */
271 rc = HANDLE_CustomType34(package,source,target,type,action);
273 case 35: /* Directory set with formatted text. */
274 deformat_string(package,target,&deformated);
275 MSI_SetTargetPathW(package, source, deformated);
276 msi_free(deformated);
278 case 51: /* Property set with formatted text. */
279 deformat_string(package,target,&deformated);
280 rc = MSI_SetPropertyW(package,source,deformated);
281 msi_free(deformated);
283 case 37: /* JScript/VBScript text stored in target column. */
285 rc = HANDLE_CustomType37_38(package,source,target,type,action);
288 case 6: /* JScript/VBScript file stored in a Binary table stream. */
289 rc = HANDLE_CustomType5_6(package,source,target,type,action);
291 case 21: /* JScript/VBScript file installed with the product. */
293 rc = HANDLE_CustomType21_22(package,source,target,type,action);
295 case 53: /* JScript/VBScript text specified by a property value. */
297 rc = HANDLE_CustomType53_54(package,source,target,type,action);
300 FIXME("UNHANDLED ACTION TYPE %i (%s %s)\n",
301 type & CUSTOM_ACTION_TYPE_MASK, debugstr_w(source),
306 msi_free(action_copy);
307 msiobj_release(&row->hdr);
312 static UINT store_binary_to_temp(MSIPACKAGE *package, LPCWSTR source,
315 static const WCHAR query[] = {
316 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
317 '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',' ',
318 '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
322 static const WCHAR f1[] = {'m','s','i',0};
327 if (MSI_GetPropertyW(package, cszTempFolder, fmt, &sz) != ERROR_SUCCESS)
328 GetTempPathW(MAX_PATH, fmt);
330 if (GetTempFileNameW(fmt, f1, 0, tmp_file) == 0)
332 TRACE("Unable to create file\n");
333 return ERROR_FUNCTION_FAILED;
335 track_tempfile(package, tmp_file);
337 row = MSI_QueryGetRecord(package->db, query, source);
339 return ERROR_FUNCTION_FAILED;
341 /* write out the file */
342 file = CreateFileW(tmp_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
343 FILE_ATTRIBUTE_NORMAL, NULL);
344 if (file == INVALID_HANDLE_VALUE)
345 r = ERROR_FUNCTION_FAILED;
352 r = MSI_RecordReadStream(row, 2, buffer, &sz);
353 if (r != ERROR_SUCCESS)
355 ERR("Failed to get stream\n");
358 WriteFile(file, buffer, sz, &write, NULL);
359 } while (sz == sizeof buffer);
363 msiobj_release(&row->hdr);
368 static void file_running_action(MSIPACKAGE* package, HANDLE Handle,
369 BOOL process, LPCWSTR name)
371 MSIRUNNINGACTION *action;
373 action = msi_alloc( sizeof(MSIRUNNINGACTION) );
375 action->handle = Handle;
376 action->process = process;
377 action->name = strdupW(name);
379 list_add_tail( &package->RunningActions, &action->entry );
382 static UINT custom_get_process_return( HANDLE process )
386 GetExitCodeProcess( process, &rc );
388 return ERROR_FUNCTION_FAILED;
389 return ERROR_SUCCESS;
392 static UINT custom_get_thread_return( MSIPACKAGE *package, HANDLE thread )
396 GetExitCodeThread( thread, &rc );
400 case ERROR_FUNCTION_NOT_CALLED:
402 case ERROR_INSTALL_USEREXIT:
403 case ERROR_INSTALL_FAILURE:
405 case ERROR_NO_MORE_ITEMS:
406 return ERROR_SUCCESS;
407 case ERROR_INSTALL_SUSPEND:
408 ACTION_ForceReboot( package );
409 return ERROR_SUCCESS;
411 ERR("Invalid Return Code %d\n",rc);
412 return ERROR_INSTALL_FAILURE;
416 static UINT wait_process_handle(MSIPACKAGE* package, UINT type,
417 HANDLE ProcessHandle, LPCWSTR name)
419 UINT rc = ERROR_SUCCESS;
421 if (!(type & msidbCustomActionTypeAsync))
423 TRACE("waiting for %s\n", debugstr_w(name));
425 msi_dialog_check_messages(ProcessHandle);
427 if (!(type & msidbCustomActionTypeContinue))
428 rc = custom_get_process_return(ProcessHandle);
430 CloseHandle(ProcessHandle);
434 TRACE("%s running in background\n", debugstr_w(name));
436 if (!(type & msidbCustomActionTypeContinue))
437 file_running_action(package, ProcessHandle, TRUE, name);
439 CloseHandle(ProcessHandle);
445 typedef struct _msi_custom_action_info {
454 } msi_custom_action_info;
456 static void free_custom_action_data( msi_custom_action_info *info )
458 EnterCriticalSection( &msi_custom_action_cs );
459 list_remove( &info->entry );
460 LeaveCriticalSection( &msi_custom_action_cs );
462 CloseHandle( info->handle );
463 msi_free( info->action );
464 msi_free( info->source );
465 msi_free( info->target );
466 msiobj_release( &info->package->hdr );
470 static UINT wait_thread_handle( msi_custom_action_info *info )
472 UINT rc = ERROR_SUCCESS;
474 if (!(info->type & msidbCustomActionTypeAsync))
476 TRACE("waiting for %s\n", debugstr_w( info->action ));
478 msi_dialog_check_messages( info->handle );
480 if (!(info->type & msidbCustomActionTypeContinue))
481 rc = custom_get_thread_return( info->package, info->handle );
483 free_custom_action_data( info );
487 TRACE("%s running in background\n", debugstr_w( info->action ));
493 static msi_custom_action_info *find_action_by_guid( const GUID *guid )
495 msi_custom_action_info *info;
498 EnterCriticalSection( &msi_custom_action_cs );
500 LIST_FOR_EACH_ENTRY( info, &msi_pending_custom_actions, msi_custom_action_info, entry )
502 if (IsEqualGUID( &info->guid, guid ))
509 LeaveCriticalSection( &msi_custom_action_cs );
517 static void handle_msi_break( LPCWSTR target )
522 static const WCHAR MsiBreak[] = { 'M','s','i','B','r','e','a','k',0 };
523 static const WCHAR WindowsInstaller[] = {
524 'W','i','n','d','o','w','s',' ','I','n','s','t','a','l','l','e','r',0
527 static const WCHAR format[] = {
528 'T','o',' ','d','e','b','u','g',' ','y','o','u','r',' ',
529 'c','u','s','t','o','m',' ','a','c','t','i','o','n',',',' ',
530 'a','t','t','a','c','h',' ','y','o','u','r',' ','d','e','b','u','g','g','e','r',' ',
531 't','o',' ','p','r','o','c','e','s','s',' ','%','i',' ','(','0','x','%','X',')',' ',
532 'a','n','d',' ','p','r','e','s','s',' ','O','K',0
535 if( !GetEnvironmentVariableW( MsiBreak, val, MAX_PATH ))
538 if( lstrcmpiW( val, target ))
541 msg = msi_alloc( (lstrlenW(format) + 10) * sizeof(WCHAR) );
545 wsprintfW( msg, format, GetCurrentProcessId(), GetCurrentProcessId());
546 MessageBoxW( NULL, msg, WindowsInstaller, MB_OK);
551 static DWORD WINAPI ACTION_CallDllFunction( const GUID *guid )
553 msi_custom_action_info *info;
554 MsiCustomActionEntryPoint fn;
558 UINT r = ERROR_FUNCTION_FAILED;
560 info = find_action_by_guid( guid );
563 ERR("failed to find action %s\n", debugstr_guid( guid) );
567 TRACE("%s %s\n", debugstr_w( info->source ), debugstr_w( info->target ) );
569 hModule = LoadLibraryW( info->source );
572 ERR("failed to load dll %s\n", debugstr_w( info->source ) );
576 proc = strdupWtoA( info->target );
577 fn = (MsiCustomActionEntryPoint) GetProcAddress( hModule, proc );
581 hPackage = alloc_msihandle( &info->package->hdr );
584 TRACE("calling %s\n", debugstr_w( info->target ) );
585 handle_msi_break( info->target );
587 MsiCloseHandle( hPackage );
590 ERR("failed to create handle for %p\n", info->package );
593 ERR("GetProcAddress(%s) failed\n", debugstr_w( info->target ) );
595 FreeLibrary(hModule);
597 if (info->type & msidbCustomActionTypeAsync &&
598 info->type & msidbCustomActionTypeContinue)
599 free_custom_action_data( info );
604 static DWORD WINAPI DllThread( LPVOID arg )
609 TRACE("custom action (%x) started\n", GetCurrentThreadId() );
611 rc = ACTION_CallDllFunction( guid );
613 TRACE("custom action (%x) returned %i\n", GetCurrentThreadId(), rc );
615 MsiCloseAllHandles();
619 static DWORD WINAPI ACTION_CAInstallPackage(const GUID *guid)
621 msi_custom_action_info *info;
622 UINT r = ERROR_FUNCTION_FAILED;
623 INSTALLUILEVEL old_level;
625 info = find_action_by_guid(guid);
628 ERR("failed to find action %s\n", debugstr_guid(guid));
632 old_level = MsiSetInternalUI(INSTALLUILEVEL_BASIC, NULL);
633 r = MsiInstallProductW(info->source, info->target);
634 MsiSetInternalUI(old_level, NULL);
639 static DWORD WINAPI ConcurrentInstallThread(LPVOID arg)
644 TRACE("concurrent installation (%x) started\n", GetCurrentThreadId());
646 rc = ACTION_CAInstallPackage(guid);
648 TRACE("concurrent installation (%x) returned %i\n", GetCurrentThreadId(), rc);
650 MsiCloseAllHandles();
654 static msi_custom_action_info *do_msidbCustomActionTypeDll(
655 MSIPACKAGE *package, INT type, LPCWSTR source, LPCWSTR target, LPCWSTR action )
657 msi_custom_action_info *info;
659 info = msi_alloc( sizeof *info );
663 msiobj_addref( &package->hdr );
664 info->package = package;
666 info->target = strdupW( target );
667 info->source = strdupW( source );
668 info->action = strdupW( action );
669 CoCreateGuid( &info->guid );
671 EnterCriticalSection( &msi_custom_action_cs );
672 list_add_tail( &msi_pending_custom_actions, &info->entry );
673 LeaveCriticalSection( &msi_custom_action_cs );
675 info->handle = CreateThread( NULL, 0, DllThread, &info->guid, 0, NULL );
678 free_custom_action_data( info );
685 static msi_custom_action_info *do_msidbCAConcurrentInstall(
686 MSIPACKAGE *package, INT type, LPCWSTR source, LPCWSTR target, LPCWSTR action)
688 msi_custom_action_info *info;
690 info = msi_alloc( sizeof *info );
694 msiobj_addref( &package->hdr );
695 info->package = package;
697 info->target = strdupW( target );
698 info->source = strdupW( source );
699 info->action = strdupW( action );
700 CoCreateGuid( &info->guid );
702 EnterCriticalSection( &msi_custom_action_cs );
703 list_add_tail( &msi_pending_custom_actions, &info->entry );
704 LeaveCriticalSection( &msi_custom_action_cs );
706 info->handle = CreateThread( NULL, 0, ConcurrentInstallThread, &info->guid, 0, NULL );
709 free_custom_action_data( info );
716 static UINT HANDLE_CustomType23(MSIPACKAGE *package, LPCWSTR source,
717 LPCWSTR target, const INT type, LPCWSTR action)
719 msi_custom_action_info *info;
720 WCHAR package_path[MAX_PATH];
723 static const WCHAR backslash[] = {'\\',0};
725 MSI_GetPropertyW(package, cszSourceDir, package_path, &size);
726 lstrcatW(package_path, backslash);
727 lstrcatW(package_path, source);
729 if (GetFileAttributesW(package_path) == INVALID_FILE_ATTRIBUTES)
731 ERR("Source package does not exist: %s\n", debugstr_w(package_path));
732 return ERROR_FUNCTION_FAILED;
735 TRACE("Installing package %s concurrently\n", debugstr_w(package_path));
737 info = do_msidbCAConcurrentInstall(package, type, package_path, target, action);
739 return wait_thread_handle(info);
742 static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source,
743 LPCWSTR target, const INT type, LPCWSTR action)
745 msi_custom_action_info *info;
746 WCHAR tmp_file[MAX_PATH];
749 r = store_binary_to_temp(package, source, tmp_file);
750 if (r != ERROR_SUCCESS)
753 TRACE("Calling function %s from %s\n",debugstr_w(target),
754 debugstr_w(tmp_file));
756 if (!strchrW(tmp_file,'.'))
758 static const WCHAR dot[]={'.',0};
759 strcatW(tmp_file,dot);
762 info = do_msidbCustomActionTypeDll( package, type, tmp_file, target, action );
764 return wait_thread_handle( info );
767 static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source,
768 LPCWSTR target, const INT type, LPCWSTR action)
770 WCHAR tmp_file[MAX_PATH];
772 PROCESS_INFORMATION info;
775 WCHAR *deformated = NULL;
777 static const WCHAR spc[] = {' ',0};
780 memset(&si,0,sizeof(STARTUPINFOW));
782 r = store_binary_to_temp(package, source, tmp_file);
783 if (r != ERROR_SUCCESS)
786 deformat_string(package,target,&deformated);
788 len = strlenW(tmp_file)+2;
791 len += strlenW(deformated);
793 cmd = msi_alloc(sizeof(WCHAR)*len);
795 strcpyW(cmd,tmp_file);
799 strcatW(cmd,deformated);
801 msi_free(deformated);
804 TRACE("executing exe %s\n", debugstr_w(cmd));
806 rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
807 c_collen, &si, &info);
812 ERR("Unable to execute command %s\n", debugstr_w(cmd));
813 return ERROR_SUCCESS;
815 CloseHandle( info.hThread );
817 r = wait_process_handle(package, type, info.hProcess, action);
822 static UINT HANDLE_CustomType17(MSIPACKAGE *package, LPCWSTR source,
823 LPCWSTR target, const INT type, LPCWSTR action)
825 msi_custom_action_info *info;
828 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
830 file = get_loaded_file( package, source );
833 ERR("invalid file key %s\n", debugstr_w( source ));
834 return ERROR_FUNCTION_FAILED;
837 info = do_msidbCustomActionTypeDll( package, type, file->TargetPath, target, action );
839 return wait_thread_handle( info );
842 static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source,
843 LPCWSTR target, const INT type, LPCWSTR action)
846 PROCESS_INFORMATION info;
851 static const WCHAR spc[] = {' ',0};
854 memset(&si,0,sizeof(STARTUPINFOW));
856 file = get_loaded_file(package,source);
858 return ERROR_FUNCTION_FAILED;
860 len = lstrlenW( file->TargetPath );
862 deformat_string(package,target,&deformated);
864 len += strlenW(deformated);
867 cmd = msi_alloc(len * sizeof(WCHAR));
869 lstrcpyW( cmd, file->TargetPath);
873 strcatW(cmd, deformated);
875 msi_free(deformated);
878 TRACE("executing exe %s\n", debugstr_w(cmd));
880 rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
881 c_collen, &si, &info);
885 ERR("Unable to execute command %s\n", debugstr_w(cmd));
887 return ERROR_SUCCESS;
890 CloseHandle( info.hThread );
892 return wait_process_handle(package, type, info.hProcess, action);
895 static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
896 LPCWSTR target, const INT type, LPCWSTR action)
898 static const WCHAR query[] = {
899 'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
900 'F','R','O','M',' ','`','E','r','r','o','r','`',' ',
901 'W','H','E','R','E',' ','`','E','r','r','o','r','`',' ','=',' ',
905 LPWSTR deformated = NULL;
907 deformat_string( package, target, &deformated );
909 /* first try treat the error as a number */
910 row = MSI_QueryGetRecord( package->db, query, deformated );
913 LPCWSTR error = MSI_RecordGetString( row, 1 );
914 MessageBoxW( NULL, error, NULL, MB_OK );
915 msiobj_release( &row->hdr );
918 MessageBoxW( NULL, deformated, NULL, MB_OK );
920 msi_free( deformated );
922 return ERROR_FUNCTION_FAILED;
925 static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source,
926 LPCWSTR target, const INT type, LPCWSTR action)
929 PROCESS_INFORMATION info;
935 static const WCHAR spc[] = {' ',0};
937 memset(&si,0,sizeof(STARTUPINFOW));
938 memset(&info,0,sizeof(PROCESS_INFORMATION));
940 prop = msi_dup_property( package, source );
942 return ERROR_SUCCESS;
944 deformat_string(package,target,&deformated);
945 len = strlenW(prop) + 2;
947 len += strlenW(deformated);
949 cmd = msi_alloc(sizeof(WCHAR)*len);
955 strcatW(cmd,deformated);
957 msi_free(deformated);
961 TRACE("executing exe %s\n", debugstr_w(cmd));
963 rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
964 c_collen, &si, &info);
968 ERR("Unable to execute command %s\n", debugstr_w(cmd));
970 return ERROR_SUCCESS;
974 CloseHandle( info.hThread );
976 return wait_process_handle(package, type, info.hProcess, action);
979 static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
980 LPCWSTR target, const INT type, LPCWSTR action)
982 LPWSTR filename, deformated;
984 PROCESS_INFORMATION info;
987 memset(&si,0,sizeof(STARTUPINFOW));
989 filename = resolve_folder(package, source, FALSE, FALSE, TRUE, NULL);
992 return ERROR_FUNCTION_FAILED;
994 SetCurrentDirectoryW(filename);
997 deformat_string(package,target,&deformated);
1000 return ERROR_FUNCTION_FAILED;
1002 TRACE("executing exe %s\n", debugstr_w(deformated));
1004 rc = CreateProcessW(NULL, deformated, NULL, NULL, FALSE, 0, NULL,
1005 c_collen, &si, &info);
1009 ERR("Unable to execute command %s\n", debugstr_w(deformated));
1010 msi_free(deformated);
1011 return ERROR_SUCCESS;
1013 msi_free(deformated);
1014 CloseHandle( info.hThread );
1016 return wait_process_handle(package, type, info.hProcess, action);
1019 static DWORD WINAPI ACTION_CallScript( const GUID *guid )
1021 msi_custom_action_info *info;
1023 UINT r = ERROR_FUNCTION_FAILED;
1025 info = find_action_by_guid( guid );
1028 ERR("failed to find action %s\n", debugstr_guid( guid) );
1032 TRACE("function %s, script %s\n", debugstr_w( info->target ), debugstr_w( info->source ) );
1034 hPackage = alloc_msihandle( &info->package->hdr );
1037 r = call_script( hPackage, info->type, info->source, info->target, info->action );
1038 MsiCloseHandle( hPackage );
1041 ERR("failed to create handle for %p\n", info->package );
1043 if (info->type & msidbCustomActionTypeAsync &&
1044 info->type & msidbCustomActionTypeContinue)
1045 free_custom_action_data( info );
1050 static DWORD WINAPI ScriptThread( LPVOID arg )
1055 TRACE("custom action (%x) started\n", GetCurrentThreadId() );
1057 rc = ACTION_CallScript( guid );
1059 TRACE("custom action (%x) returned %i\n", GetCurrentThreadId(), rc );
1061 MsiCloseAllHandles();
1065 static msi_custom_action_info *do_msidbCustomActionTypeScript(
1066 MSIPACKAGE *package, INT type, LPCWSTR script, LPCWSTR function, LPCWSTR action )
1068 msi_custom_action_info *info;
1070 info = msi_alloc( sizeof *info );
1074 msiobj_addref( &package->hdr );
1075 info->package = package;
1077 info->target = strdupW( function );
1078 info->source = strdupW( script );
1079 info->action = strdupW( action );
1080 CoCreateGuid( &info->guid );
1082 EnterCriticalSection( &msi_custom_action_cs );
1083 list_add_tail( &msi_pending_custom_actions, &info->entry );
1084 LeaveCriticalSection( &msi_custom_action_cs );
1086 info->handle = CreateThread( NULL, 0, ScriptThread, &info->guid, 0, NULL );
1089 free_custom_action_data( info );
1096 static UINT HANDLE_CustomType37_38(MSIPACKAGE *package, LPCWSTR source,
1097 LPCWSTR target, const INT type, LPCWSTR action)
1099 msi_custom_action_info *info;
1101 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1103 info = do_msidbCustomActionTypeScript( package, type, target, NULL, action );
1105 return wait_thread_handle( info );
1108 static UINT HANDLE_CustomType5_6(MSIPACKAGE *package, LPCWSTR source,
1109 LPCWSTR target, const INT type, LPCWSTR action)
1111 static const WCHAR query[] = {
1112 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1113 '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',' ',
1114 '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
1116 msi_custom_action_info *info;
1117 CHAR *buffer = NULL;
1118 WCHAR *bufferw = NULL;
1122 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1124 row = MSI_QueryGetRecord(package->db, query, source);
1126 return ERROR_FUNCTION_FAILED;
1128 r = MSI_RecordReadStream(row, 2, NULL, &sz);
1129 if (r != ERROR_SUCCESS)
1132 buffer = msi_alloc(sizeof(CHAR)*(sz+1));
1134 return ERROR_FUNCTION_FAILED;
1136 r = MSI_RecordReadStream(row, 2, buffer, &sz);
1137 if (r != ERROR_SUCCESS)
1141 bufferw = strdupAtoW(buffer);
1144 r = ERROR_FUNCTION_FAILED;
1148 info = do_msidbCustomActionTypeScript( package, type, bufferw, target, action );
1149 r = wait_thread_handle( info );
1157 static UINT HANDLE_CustomType21_22(MSIPACKAGE *package, LPCWSTR source,
1158 LPCWSTR target, const INT type, LPCWSTR action)
1160 msi_custom_action_info *info;
1163 DWORD sz, szHighWord = 0, read;
1165 WCHAR *bufferw=NULL;
1169 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1171 file = get_loaded_file(package,source);
1174 ERR("invalid file key %s\n", debugstr_w(source));
1175 return ERROR_FUNCTION_FAILED;
1178 hFile = CreateFileW(file->TargetPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
1179 if (hFile == INVALID_HANDLE_VALUE)
1180 return ERROR_FUNCTION_FAILED;
1182 sz = GetFileSize(hFile, &szHighWord);
1183 if (sz == INVALID_FILE_SIZE || szHighWord != 0)
1186 return ERROR_FUNCTION_FAILED;
1189 buffer = msi_alloc(sizeof(CHAR)*(sz+1));
1193 return ERROR_FUNCTION_FAILED;
1196 bRet = ReadFile(hFile, buffer, sz, &read, NULL);
1200 r = ERROR_FUNCTION_FAILED;
1205 bufferw = strdupAtoW(buffer);
1208 r = ERROR_FUNCTION_FAILED;
1212 info = do_msidbCustomActionTypeScript( package, type, bufferw, target, action );
1213 r = wait_thread_handle( info );
1221 static UINT HANDLE_CustomType53_54(MSIPACKAGE *package, LPCWSTR source,
1222 LPCWSTR target, const INT type, LPCWSTR action)
1224 msi_custom_action_info *info;
1227 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1229 prop = msi_dup_property(package,source);
1231 return ERROR_SUCCESS;
1233 info = do_msidbCustomActionTypeScript( package, type, prop, NULL, action );
1235 return wait_thread_handle( info );
1238 void ACTION_FinishCustomActions(const MSIPACKAGE* package)
1241 HANDLE *wait_handles;
1242 unsigned int handle_count, i;
1243 msi_custom_action_info *info, *cursor;
1245 while ((item = list_head( &package->RunningActions )))
1247 MSIRUNNINGACTION *action = LIST_ENTRY( item, MSIRUNNINGACTION, entry );
1249 list_remove( &action->entry );
1251 TRACE("waiting for %s\n", debugstr_w( action->name ) );
1252 msi_dialog_check_messages( action->handle );
1254 CloseHandle( action->handle );
1255 msi_free( action->name );
1259 EnterCriticalSection( &msi_custom_action_cs );
1261 handle_count = list_count( &msi_pending_custom_actions );
1262 wait_handles = HeapAlloc( GetProcessHeap(), 0, handle_count * sizeof(HANDLE) );
1265 LIST_FOR_EACH_ENTRY_SAFE( info, cursor, &msi_pending_custom_actions, msi_custom_action_info, entry )
1267 if (info->package == package )
1269 if (DuplicateHandle(GetCurrentProcess(), info->handle, GetCurrentProcess(), &wait_handles[handle_count], SYNCHRONIZE, FALSE, 0))
1271 free_custom_action_data( info );
1275 LeaveCriticalSection( &msi_custom_action_cs );
1277 for (i = 0; i < handle_count; i++)
1279 msi_dialog_check_messages( wait_handles[i] );
1280 CloseHandle( wait_handles[i] );
1283 HeapFree( GetProcessHeap(), 0, wait_handles );