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
31 #include "wine/debug.h"
32 #include "wine/unicode.h"
33 #include "wine/exception.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(msi);
37 #define CUSTOM_ACTION_TYPE_MASK 0x3F
38 static const WCHAR c_collen[] = {'C',':','\\',0};
39 static const WCHAR cszTempFolder[]= {'T','e','m','p','F','o','l','d','e','r',0};
42 static const WCHAR szActionData[] = {
43 'C','u','s','t','o','m','A','c','t','i','o','n','D','a','t','a',0
45 static const WCHAR ProdCode[] = {
46 'P','r','o','d','u','c','t','C','o','d','e',0
48 static const WCHAR UserSID[] = {'U','s','e','r','S','I','D',0};
50 typedef struct tagMSIRUNNINGACTION
58 static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source,
59 LPCWSTR target, const INT type, LPCWSTR action);
60 static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source,
61 LPCWSTR target, const INT type, LPCWSTR action);
62 static UINT HANDLE_CustomType17(MSIPACKAGE *package, LPCWSTR source,
63 LPCWSTR target, const INT type, LPCWSTR action);
64 static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source,
65 LPCWSTR target, const INT type, LPCWSTR action);
66 static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
67 LPCWSTR target, const INT type, LPCWSTR action);
68 static UINT HANDLE_CustomType23(MSIPACKAGE *package, LPCWSTR source,
69 LPCWSTR target, const INT type, LPCWSTR action);
70 static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source,
71 LPCWSTR target, const INT type, LPCWSTR action);
72 static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
73 LPCWSTR target, const INT type, LPCWSTR action);
74 static UINT HANDLE_CustomType37_38(MSIPACKAGE *package, LPCWSTR source,
75 LPCWSTR target, const INT type, LPCWSTR action);
76 static UINT HANDLE_CustomType5_6(MSIPACKAGE *package, LPCWSTR source,
77 LPCWSTR target, const INT type, LPCWSTR action);
78 static UINT HANDLE_CustomType21_22(MSIPACKAGE *package, LPCWSTR source,
79 LPCWSTR target, const INT type, LPCWSTR action);
80 static UINT HANDLE_CustomType53_54(MSIPACKAGE *package, LPCWSTR source,
81 LPCWSTR target, const INT type, LPCWSTR action);
83 typedef UINT (WINAPI *MsiCustomActionEntryPoint)( MSIHANDLE );
85 static CRITICAL_SECTION msi_custom_action_cs;
86 static CRITICAL_SECTION_DEBUG msi_custom_action_cs_debug =
88 0, 0, &msi_custom_action_cs,
89 { &msi_custom_action_cs_debug.ProcessLocksList,
90 &msi_custom_action_cs_debug.ProcessLocksList },
91 0, 0, { (DWORD_PTR)(__FILE__ ": msi_custom_action_cs") }
93 static CRITICAL_SECTION msi_custom_action_cs = { &msi_custom_action_cs_debug, -1, 0, 0, 0, 0 };
95 static struct list msi_pending_custom_actions = LIST_INIT( msi_pending_custom_actions );
97 static BOOL check_execution_scheduling_options(MSIPACKAGE *package, LPCWSTR action, UINT options)
102 if ((options & msidbCustomActionTypeClientRepeat) ==
103 msidbCustomActionTypeClientRepeat)
105 if (!(package->script->InWhatSequence & SEQUENCE_UI &&
106 package->script->InWhatSequence & SEQUENCE_EXEC))
108 TRACE("Skipping action due to dbCustomActionTypeClientRepeat option.\n");
112 else if (options & msidbCustomActionTypeFirstSequence)
114 if (package->script->InWhatSequence & SEQUENCE_UI &&
115 package->script->InWhatSequence & SEQUENCE_EXEC )
117 TRACE("Skipping action due to msidbCustomActionTypeFirstSequence option.\n");
121 else if (options & msidbCustomActionTypeOncePerProcess)
123 if (check_unique_action(package,action))
125 TRACE("Skipping action due to msidbCustomActionTypeOncePerProcess option.\n");
129 register_unique_action(package,action);
135 /* stores the following properties before the action:
137 * [CustomActionData;UserSID;ProductCode]Action
139 static LPWSTR msi_get_deferred_action(LPCWSTR action, LPCWSTR actiondata,
140 LPCWSTR usersid, LPCWSTR prodcode)
145 static const WCHAR format[] = {'[','%','s',';','%','s',';','%','s',']','%','s',0};
148 return strdupW(action);
150 len = lstrlenW(action) + lstrlenW(actiondata) +
151 lstrlenW(usersid) + lstrlenW(prodcode) + 5;
152 deferred = msi_alloc(len * sizeof(WCHAR));
154 sprintfW(deferred, format, actiondata, usersid, prodcode, action);
158 static void set_deferred_action_props(MSIPACKAGE *package, LPWSTR deferred_data)
160 LPWSTR end, beg = deferred_data;
162 end = strchrW(beg, ';');
164 MSI_SetPropertyW(package, szActionData, beg);
167 end = strchrW(beg, ';');
169 MSI_SetPropertyW(package, UserSID, beg);
172 end = strchrW(beg, ']');
174 MSI_SetPropertyW(package, ProdCode, beg);
177 UINT ACTION_CustomAction(MSIPACKAGE *package,LPCWSTR action, BOOL execute)
179 UINT rc = ERROR_SUCCESS;
181 static const WCHAR ExecSeqQuery[] =
182 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
183 '`','C','u','s','t','o' ,'m','A','c','t','i','o','n','`',
184 ' ','W','H','E','R','E',' ','`','A','c','t','i' ,'o','n','`',' ',
185 '=',' ','\'','%','s','\'',0};
187 LPCWSTR source, target;
188 LPWSTR ptr, deferred_data = NULL;
189 LPWSTR action_copy = strdupW(action);
190 WCHAR *deformated=NULL;
192 /* deferred action: [CustomActionData]Action */
193 if ((ptr = strchrW(action_copy, ']')))
195 deferred_data = action_copy + 1;
200 row = MSI_QueryGetRecord( package->db, ExecSeqQuery, action );
203 msi_free(action_copy);
204 return ERROR_CALL_NOT_IMPLEMENTED;
207 type = MSI_RecordGetInteger(row,2);
209 source = MSI_RecordGetString(row,3);
210 target = MSI_RecordGetString(row,4);
212 TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type,
213 debugstr_w(source), debugstr_w(target));
215 /* handle some of the deferred actions */
216 if (type & msidbCustomActionTypeTSAware)
217 FIXME("msidbCustomActionTypeTSAware not handled\n");
219 if (type & msidbCustomActionTypeInScript)
221 if (type & msidbCustomActionTypeNoImpersonate)
222 FIXME("msidbCustomActionTypeNoImpersonate not handled\n");
224 if (type & msidbCustomActionTypeRollback)
226 FIXME("Rollback only action... rollbacks not supported yet\n");
227 schedule_action(package, ROLLBACK_SCRIPT, action);
233 LPWSTR actiondata = msi_dup_property(package, action);
234 LPWSTR usersid = msi_dup_property(package, UserSID);
235 LPWSTR prodcode = msi_dup_property(package, ProdCode);
236 LPWSTR deferred = msi_get_deferred_action(action, actiondata, usersid, prodcode);
238 if (type & msidbCustomActionTypeCommit)
240 TRACE("Deferring Commit Action!\n");
241 schedule_action(package, COMMIT_SCRIPT, deferred);
245 TRACE("Deferring Action!\n");
246 schedule_action(package, INSTALL_SCRIPT, deferred);
250 msi_free(actiondata);
258 static const WCHAR szBlank[] = {0};
260 LPWSTR actiondata = msi_dup_property( package, action );
263 set_deferred_action_props(package, deferred_data);
265 MSI_SetPropertyW(package,szActionData,actiondata);
267 MSI_SetPropertyW(package,szActionData,szBlank);
269 msi_free(actiondata);
272 else if (!check_execution_scheduling_options(package,action,type))
278 switch (type & CUSTOM_ACTION_TYPE_MASK)
280 case 1: /* DLL file stored in a Binary table stream */
281 rc = HANDLE_CustomType1(package,source,target,type,action);
283 case 2: /* EXE file stored in a Binary table stream */
284 rc = HANDLE_CustomType2(package,source,target,type,action);
286 case 18: /*EXE file installed with package */
287 rc = HANDLE_CustomType18(package,source,target,type,action);
289 case 19: /* Error that halts install */
290 rc = HANDLE_CustomType19(package,source,target,type,action);
293 rc = HANDLE_CustomType17(package,source,target,type,action);
295 case 23: /* installs another package in the source tree */
296 deformat_string(package,target,&deformated);
297 rc = HANDLE_CustomType23(package,source,deformated,type,action);
299 case 50: /*EXE file specified by a property value */
300 rc = HANDLE_CustomType50(package,source,target,type,action);
302 case 34: /*EXE to be run in specified directory */
303 rc = HANDLE_CustomType34(package,source,target,type,action);
305 case 35: /* Directory set with formatted text. */
306 deformat_string(package,target,&deformated);
307 MSI_SetTargetPathW(package, source, deformated);
308 msi_free(deformated);
310 case 51: /* Property set with formatted text. */
311 deformat_string(package,target,&deformated);
312 rc = MSI_SetPropertyW(package,source,deformated);
313 msi_free(deformated);
315 case 37: /* JScript/VBScript text stored in target column. */
317 rc = HANDLE_CustomType37_38(package,source,target,type,action);
320 case 6: /* JScript/VBScript file stored in a Binary table stream. */
321 rc = HANDLE_CustomType5_6(package,source,target,type,action);
323 case 21: /* JScript/VBScript file installed with the product. */
325 rc = HANDLE_CustomType21_22(package,source,target,type,action);
327 case 53: /* JScript/VBScript text specified by a property value. */
329 rc = HANDLE_CustomType53_54(package,source,target,type,action);
332 FIXME("UNHANDLED ACTION TYPE %i (%s %s)\n",
333 type & CUSTOM_ACTION_TYPE_MASK, debugstr_w(source),
338 msi_free(action_copy);
339 msiobj_release(&row->hdr);
344 static UINT store_binary_to_temp(MSIPACKAGE *package, LPCWSTR source,
347 static const WCHAR query[] = {
348 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
349 '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',' ',
350 '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
354 static const WCHAR f1[] = {'m','s','i',0};
359 if (MSI_GetPropertyW(package, cszTempFolder, fmt, &sz) != ERROR_SUCCESS)
360 GetTempPathW(MAX_PATH, fmt);
362 if (GetTempFileNameW(fmt, f1, 0, tmp_file) == 0)
364 TRACE("Unable to create file\n");
365 return ERROR_FUNCTION_FAILED;
367 track_tempfile(package, tmp_file);
369 row = MSI_QueryGetRecord(package->db, query, source);
371 return ERROR_FUNCTION_FAILED;
373 /* write out the file */
374 file = CreateFileW(tmp_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
375 FILE_ATTRIBUTE_NORMAL, NULL);
376 if (file == INVALID_HANDLE_VALUE)
377 r = ERROR_FUNCTION_FAILED;
384 r = MSI_RecordReadStream(row, 2, buffer, &sz);
385 if (r != ERROR_SUCCESS)
387 ERR("Failed to get stream\n");
390 WriteFile(file, buffer, sz, &write, NULL);
391 } while (sz == sizeof buffer);
395 msiobj_release(&row->hdr);
400 static void file_running_action(MSIPACKAGE* package, HANDLE Handle,
401 BOOL process, LPCWSTR name)
403 MSIRUNNINGACTION *action;
405 action = msi_alloc( sizeof(MSIRUNNINGACTION) );
407 action->handle = Handle;
408 action->process = process;
409 action->name = strdupW(name);
411 list_add_tail( &package->RunningActions, &action->entry );
414 static UINT custom_get_process_return( HANDLE process )
418 GetExitCodeProcess( process, &rc );
420 return ERROR_FUNCTION_FAILED;
421 return ERROR_SUCCESS;
424 static UINT custom_get_thread_return( MSIPACKAGE *package, HANDLE thread )
428 GetExitCodeThread( thread, &rc );
432 case ERROR_FUNCTION_NOT_CALLED:
434 case ERROR_INSTALL_USEREXIT:
435 case ERROR_INSTALL_FAILURE:
437 case ERROR_NO_MORE_ITEMS:
438 return ERROR_SUCCESS;
439 case ERROR_INSTALL_SUSPEND:
440 ACTION_ForceReboot( package );
441 return ERROR_SUCCESS;
443 ERR("Invalid Return Code %d\n",rc);
444 return ERROR_INSTALL_FAILURE;
448 static UINT wait_process_handle(MSIPACKAGE* package, UINT type,
449 HANDLE ProcessHandle, LPCWSTR name)
451 UINT rc = ERROR_SUCCESS;
453 if (!(type & msidbCustomActionTypeAsync))
455 TRACE("waiting for %s\n", debugstr_w(name));
457 msi_dialog_check_messages(ProcessHandle);
459 if (!(type & msidbCustomActionTypeContinue))
460 rc = custom_get_process_return(ProcessHandle);
462 CloseHandle(ProcessHandle);
466 TRACE("%s running in background\n", debugstr_w(name));
468 if (!(type & msidbCustomActionTypeContinue))
469 file_running_action(package, ProcessHandle, TRUE, name);
471 CloseHandle(ProcessHandle);
477 typedef struct _msi_custom_action_info {
486 } msi_custom_action_info;
488 static void free_custom_action_data( msi_custom_action_info *info )
490 EnterCriticalSection( &msi_custom_action_cs );
491 list_remove( &info->entry );
492 LeaveCriticalSection( &msi_custom_action_cs );
494 CloseHandle( info->handle );
495 msi_free( info->action );
496 msi_free( info->source );
497 msi_free( info->target );
498 msiobj_release( &info->package->hdr );
502 static UINT wait_thread_handle( msi_custom_action_info *info )
504 UINT rc = ERROR_SUCCESS;
506 if (!(info->type & msidbCustomActionTypeAsync))
508 TRACE("waiting for %s\n", debugstr_w( info->action ));
510 msi_dialog_check_messages( info->handle );
512 if (!(info->type & msidbCustomActionTypeContinue))
513 rc = custom_get_thread_return( info->package, info->handle );
515 free_custom_action_data( info );
519 TRACE("%s running in background\n", debugstr_w( info->action ));
525 static msi_custom_action_info *find_action_by_guid( const GUID *guid )
527 msi_custom_action_info *info;
530 EnterCriticalSection( &msi_custom_action_cs );
532 LIST_FOR_EACH_ENTRY( info, &msi_pending_custom_actions, msi_custom_action_info, entry )
534 if (IsEqualGUID( &info->guid, guid ))
541 LeaveCriticalSection( &msi_custom_action_cs );
549 static void handle_msi_break( LPCWSTR target )
554 static const WCHAR MsiBreak[] = { 'M','s','i','B','r','e','a','k',0 };
555 static const WCHAR WindowsInstaller[] = {
556 'W','i','n','d','o','w','s',' ','I','n','s','t','a','l','l','e','r',0
559 static const WCHAR format[] = {
560 'T','o',' ','d','e','b','u','g',' ','y','o','u','r',' ',
561 'c','u','s','t','o','m',' ','a','c','t','i','o','n',',',' ',
562 'a','t','t','a','c','h',' ','y','o','u','r',' ','d','e','b','u','g','g','e','r',' ',
563 't','o',' ','p','r','o','c','e','s','s',' ','%','i',' ','(','0','x','%','X',')',' ',
564 'a','n','d',' ','p','r','e','s','s',' ','O','K',0
567 if( !GetEnvironmentVariableW( MsiBreak, val, MAX_PATH ))
570 if( lstrcmpiW( val, target ))
573 msg = msi_alloc( (lstrlenW(format) + 10) * sizeof(WCHAR) );
577 wsprintfW( msg, format, GetCurrentProcessId(), GetCurrentProcessId());
578 MessageBoxW( NULL, msg, WindowsInstaller, MB_OK);
583 static DWORD WINAPI ACTION_CallDllFunction( const GUID *guid )
585 msi_custom_action_info *info;
586 MsiCustomActionEntryPoint fn;
590 UINT r = ERROR_FUNCTION_FAILED;
592 info = find_action_by_guid( guid );
595 ERR("failed to find action %s\n", debugstr_guid( guid) );
599 TRACE("%s %s\n", debugstr_w( info->source ), debugstr_w( info->target ) );
601 hModule = LoadLibraryW( info->source );
604 ERR("failed to load dll %s\n", debugstr_w( info->source ) );
608 proc = strdupWtoA( info->target );
609 fn = (MsiCustomActionEntryPoint) GetProcAddress( hModule, proc );
613 hPackage = alloc_msihandle( &info->package->hdr );
616 TRACE("calling %s\n", debugstr_w( info->target ) );
617 handle_msi_break( info->target );
625 ERR("Custom action (%s:%s) caused a page fault: %08x\n",
626 debugstr_w(info->source), debugstr_w(info->target), GetExceptionCode());
631 MsiCloseHandle( hPackage );
634 ERR("failed to create handle for %p\n", info->package );
637 ERR("GetProcAddress(%s) failed\n", debugstr_w( info->target ) );
639 FreeLibrary(hModule);
641 if (info->type & msidbCustomActionTypeAsync &&
642 info->type & msidbCustomActionTypeContinue)
643 free_custom_action_data( info );
648 static DWORD WINAPI DllThread( LPVOID arg )
653 TRACE("custom action (%x) started\n", GetCurrentThreadId() );
655 rc = ACTION_CallDllFunction( guid );
657 TRACE("custom action (%x) returned %i\n", GetCurrentThreadId(), rc );
659 MsiCloseAllHandles();
663 static DWORD WINAPI ACTION_CAInstallPackage(const GUID *guid)
665 msi_custom_action_info *info;
666 UINT r = ERROR_FUNCTION_FAILED;
667 INSTALLUILEVEL old_level;
669 info = find_action_by_guid(guid);
672 ERR("failed to find action %s\n", debugstr_guid(guid));
676 old_level = MsiSetInternalUI(INSTALLUILEVEL_BASIC, NULL);
677 r = MsiInstallProductW(info->source, info->target);
678 MsiSetInternalUI(old_level, NULL);
683 static DWORD WINAPI ConcurrentInstallThread(LPVOID arg)
688 TRACE("concurrent installation (%x) started\n", GetCurrentThreadId());
690 rc = ACTION_CAInstallPackage(guid);
692 TRACE("concurrent installation (%x) returned %i\n", GetCurrentThreadId(), rc);
694 MsiCloseAllHandles();
698 static msi_custom_action_info *do_msidbCustomActionTypeDll(
699 MSIPACKAGE *package, INT type, LPCWSTR source, LPCWSTR target, LPCWSTR action )
701 msi_custom_action_info *info;
703 info = msi_alloc( sizeof *info );
707 msiobj_addref( &package->hdr );
708 info->package = package;
710 info->target = strdupW( target );
711 info->source = strdupW( source );
712 info->action = strdupW( action );
713 CoCreateGuid( &info->guid );
715 EnterCriticalSection( &msi_custom_action_cs );
716 list_add_tail( &msi_pending_custom_actions, &info->entry );
717 LeaveCriticalSection( &msi_custom_action_cs );
719 info->handle = CreateThread( NULL, 0, DllThread, &info->guid, 0, NULL );
722 free_custom_action_data( info );
729 static msi_custom_action_info *do_msidbCAConcurrentInstall(
730 MSIPACKAGE *package, INT type, LPCWSTR source, LPCWSTR target, LPCWSTR action)
732 msi_custom_action_info *info;
734 info = msi_alloc( sizeof *info );
738 msiobj_addref( &package->hdr );
739 info->package = package;
741 info->target = strdupW( target );
742 info->source = strdupW( source );
743 info->action = strdupW( action );
744 CoCreateGuid( &info->guid );
746 EnterCriticalSection( &msi_custom_action_cs );
747 list_add_tail( &msi_pending_custom_actions, &info->entry );
748 LeaveCriticalSection( &msi_custom_action_cs );
750 info->handle = CreateThread( NULL, 0, ConcurrentInstallThread, &info->guid, 0, NULL );
753 free_custom_action_data( info );
760 static UINT HANDLE_CustomType23(MSIPACKAGE *package, LPCWSTR source,
761 LPCWSTR target, const INT type, LPCWSTR action)
763 msi_custom_action_info *info;
764 WCHAR package_path[MAX_PATH];
767 static const WCHAR backslash[] = {'\\',0};
769 MSI_GetPropertyW(package, cszSourceDir, package_path, &size);
770 lstrcatW(package_path, backslash);
771 lstrcatW(package_path, source);
773 if (GetFileAttributesW(package_path) == INVALID_FILE_ATTRIBUTES)
775 ERR("Source package does not exist: %s\n", debugstr_w(package_path));
776 return ERROR_FUNCTION_FAILED;
779 TRACE("Installing package %s concurrently\n", debugstr_w(package_path));
781 info = do_msidbCAConcurrentInstall(package, type, package_path, target, action);
783 return wait_thread_handle(info);
786 static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source,
787 LPCWSTR target, const INT type, LPCWSTR action)
789 msi_custom_action_info *info;
790 WCHAR tmp_file[MAX_PATH];
793 r = store_binary_to_temp(package, source, tmp_file);
794 if (r != ERROR_SUCCESS)
797 TRACE("Calling function %s from %s\n",debugstr_w(target),
798 debugstr_w(tmp_file));
800 if (!strchrW(tmp_file,'.'))
802 static const WCHAR dot[]={'.',0};
803 strcatW(tmp_file,dot);
806 info = do_msidbCustomActionTypeDll( package, type, tmp_file, target, action );
808 return wait_thread_handle( info );
811 static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source,
812 LPCWSTR target, const INT type, LPCWSTR action)
814 WCHAR tmp_file[MAX_PATH];
816 PROCESS_INFORMATION info;
819 WCHAR *deformated = NULL;
821 static const WCHAR spc[] = {' ',0};
824 memset(&si,0,sizeof(STARTUPINFOW));
826 r = store_binary_to_temp(package, source, tmp_file);
827 if (r != ERROR_SUCCESS)
830 deformat_string(package,target,&deformated);
832 len = strlenW(tmp_file)+2;
835 len += strlenW(deformated);
837 cmd = msi_alloc(sizeof(WCHAR)*len);
839 strcpyW(cmd,tmp_file);
843 strcatW(cmd,deformated);
845 msi_free(deformated);
848 TRACE("executing exe %s\n", debugstr_w(cmd));
850 rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
851 c_collen, &si, &info);
856 ERR("Unable to execute command %s\n", debugstr_w(cmd));
857 return ERROR_SUCCESS;
859 CloseHandle( info.hThread );
861 r = wait_process_handle(package, type, info.hProcess, action);
866 static UINT HANDLE_CustomType17(MSIPACKAGE *package, LPCWSTR source,
867 LPCWSTR target, const INT type, LPCWSTR action)
869 msi_custom_action_info *info;
872 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
874 file = get_loaded_file( package, source );
877 ERR("invalid file key %s\n", debugstr_w( source ));
878 return ERROR_FUNCTION_FAILED;
881 info = do_msidbCustomActionTypeDll( package, type, file->TargetPath, target, action );
883 return wait_thread_handle( info );
886 static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source,
887 LPCWSTR target, const INT type, LPCWSTR action)
890 PROCESS_INFORMATION info;
895 static const WCHAR spc[] = {' ',0};
898 memset(&si,0,sizeof(STARTUPINFOW));
900 file = get_loaded_file(package,source);
902 return ERROR_FUNCTION_FAILED;
904 len = lstrlenW( file->TargetPath );
906 deformat_string(package,target,&deformated);
908 len += strlenW(deformated);
911 cmd = msi_alloc(len * sizeof(WCHAR));
913 lstrcpyW( cmd, file->TargetPath);
917 strcatW(cmd, deformated);
919 msi_free(deformated);
922 TRACE("executing exe %s\n", debugstr_w(cmd));
924 rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
925 c_collen, &si, &info);
929 ERR("Unable to execute command %s\n", debugstr_w(cmd));
931 return ERROR_SUCCESS;
934 CloseHandle( info.hThread );
936 return wait_process_handle(package, type, info.hProcess, action);
939 static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
940 LPCWSTR target, const INT type, LPCWSTR action)
942 static const WCHAR query[] = {
943 'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
944 'F','R','O','M',' ','`','E','r','r','o','r','`',' ',
945 'W','H','E','R','E',' ','`','E','r','r','o','r','`',' ','=',' ',
949 LPWSTR deformated = NULL;
951 deformat_string( package, target, &deformated );
953 /* first try treat the error as a number */
954 row = MSI_QueryGetRecord( package->db, query, deformated );
957 LPCWSTR error = MSI_RecordGetString( row, 1 );
958 MessageBoxW( NULL, error, NULL, MB_OK );
959 msiobj_release( &row->hdr );
962 MessageBoxW( NULL, deformated, NULL, MB_OK );
964 msi_free( deformated );
966 return ERROR_FUNCTION_FAILED;
969 static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source,
970 LPCWSTR target, const INT type, LPCWSTR action)
973 PROCESS_INFORMATION info;
979 static const WCHAR spc[] = {' ',0};
981 memset(&si,0,sizeof(STARTUPINFOW));
982 memset(&info,0,sizeof(PROCESS_INFORMATION));
984 prop = msi_dup_property( package, source );
986 return ERROR_SUCCESS;
988 deformat_string(package,target,&deformated);
989 len = strlenW(prop) + 2;
991 len += strlenW(deformated);
993 cmd = msi_alloc(sizeof(WCHAR)*len);
999 strcatW(cmd,deformated);
1001 msi_free(deformated);
1005 TRACE("executing exe %s\n", debugstr_w(cmd));
1007 rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
1008 c_collen, &si, &info);
1012 ERR("Unable to execute command %s\n", debugstr_w(cmd));
1014 return ERROR_SUCCESS;
1018 CloseHandle( info.hThread );
1020 return wait_process_handle(package, type, info.hProcess, action);
1023 static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
1024 LPCWSTR target, const INT type, LPCWSTR action)
1026 LPWSTR filename, deformated;
1028 PROCESS_INFORMATION info;
1031 memset(&si,0,sizeof(STARTUPINFOW));
1033 filename = resolve_folder(package, source, FALSE, FALSE, TRUE, NULL);
1036 return ERROR_FUNCTION_FAILED;
1038 SetCurrentDirectoryW(filename);
1041 deformat_string(package,target,&deformated);
1044 return ERROR_FUNCTION_FAILED;
1046 TRACE("executing exe %s\n", debugstr_w(deformated));
1048 rc = CreateProcessW(NULL, deformated, NULL, NULL, FALSE, 0, NULL,
1049 c_collen, &si, &info);
1053 ERR("Unable to execute command %s\n", debugstr_w(deformated));
1054 msi_free(deformated);
1055 return ERROR_SUCCESS;
1057 msi_free(deformated);
1058 CloseHandle( info.hThread );
1060 return wait_process_handle(package, type, info.hProcess, action);
1063 static DWORD WINAPI ACTION_CallScript( const GUID *guid )
1065 msi_custom_action_info *info;
1067 UINT r = ERROR_FUNCTION_FAILED;
1069 info = find_action_by_guid( guid );
1072 ERR("failed to find action %s\n", debugstr_guid( guid) );
1076 TRACE("function %s, script %s\n", debugstr_w( info->target ), debugstr_w( info->source ) );
1078 hPackage = alloc_msihandle( &info->package->hdr );
1081 r = call_script( hPackage, info->type, info->source, info->target, info->action );
1082 MsiCloseHandle( hPackage );
1085 ERR("failed to create handle for %p\n", info->package );
1087 if (info->type & msidbCustomActionTypeAsync &&
1088 info->type & msidbCustomActionTypeContinue)
1089 free_custom_action_data( info );
1094 static DWORD WINAPI ScriptThread( LPVOID arg )
1099 TRACE("custom action (%x) started\n", GetCurrentThreadId() );
1101 rc = ACTION_CallScript( guid );
1103 TRACE("custom action (%x) returned %i\n", GetCurrentThreadId(), rc );
1105 MsiCloseAllHandles();
1109 static msi_custom_action_info *do_msidbCustomActionTypeScript(
1110 MSIPACKAGE *package, INT type, LPCWSTR script, LPCWSTR function, LPCWSTR action )
1112 msi_custom_action_info *info;
1114 info = msi_alloc( sizeof *info );
1118 msiobj_addref( &package->hdr );
1119 info->package = package;
1121 info->target = strdupW( function );
1122 info->source = strdupW( script );
1123 info->action = strdupW( action );
1124 CoCreateGuid( &info->guid );
1126 EnterCriticalSection( &msi_custom_action_cs );
1127 list_add_tail( &msi_pending_custom_actions, &info->entry );
1128 LeaveCriticalSection( &msi_custom_action_cs );
1130 info->handle = CreateThread( NULL, 0, ScriptThread, &info->guid, 0, NULL );
1133 free_custom_action_data( info );
1140 static UINT HANDLE_CustomType37_38(MSIPACKAGE *package, LPCWSTR source,
1141 LPCWSTR target, const INT type, LPCWSTR action)
1143 msi_custom_action_info *info;
1145 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1147 info = do_msidbCustomActionTypeScript( package, type, target, NULL, action );
1149 return wait_thread_handle( info );
1152 static UINT HANDLE_CustomType5_6(MSIPACKAGE *package, LPCWSTR source,
1153 LPCWSTR target, const INT type, LPCWSTR action)
1155 static const WCHAR query[] = {
1156 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1157 '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',' ',
1158 '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
1160 msi_custom_action_info *info;
1161 CHAR *buffer = NULL;
1162 WCHAR *bufferw = NULL;
1166 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1168 row = MSI_QueryGetRecord(package->db, query, source);
1170 return ERROR_FUNCTION_FAILED;
1172 r = MSI_RecordReadStream(row, 2, NULL, &sz);
1173 if (r != ERROR_SUCCESS)
1176 buffer = msi_alloc(sizeof(CHAR)*(sz+1));
1178 return ERROR_FUNCTION_FAILED;
1180 r = MSI_RecordReadStream(row, 2, buffer, &sz);
1181 if (r != ERROR_SUCCESS)
1185 bufferw = strdupAtoW(buffer);
1188 r = ERROR_FUNCTION_FAILED;
1192 info = do_msidbCustomActionTypeScript( package, type, bufferw, target, action );
1193 r = wait_thread_handle( info );
1201 static UINT HANDLE_CustomType21_22(MSIPACKAGE *package, LPCWSTR source,
1202 LPCWSTR target, const INT type, LPCWSTR action)
1204 msi_custom_action_info *info;
1207 DWORD sz, szHighWord = 0, read;
1209 WCHAR *bufferw=NULL;
1213 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1215 file = get_loaded_file(package,source);
1218 ERR("invalid file key %s\n", debugstr_w(source));
1219 return ERROR_FUNCTION_FAILED;
1222 hFile = CreateFileW(file->TargetPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
1223 if (hFile == INVALID_HANDLE_VALUE)
1224 return ERROR_FUNCTION_FAILED;
1226 sz = GetFileSize(hFile, &szHighWord);
1227 if (sz == INVALID_FILE_SIZE || szHighWord != 0)
1230 return ERROR_FUNCTION_FAILED;
1233 buffer = msi_alloc(sizeof(CHAR)*(sz+1));
1237 return ERROR_FUNCTION_FAILED;
1240 bRet = ReadFile(hFile, buffer, sz, &read, NULL);
1244 r = ERROR_FUNCTION_FAILED;
1249 bufferw = strdupAtoW(buffer);
1252 r = ERROR_FUNCTION_FAILED;
1256 info = do_msidbCustomActionTypeScript( package, type, bufferw, target, action );
1257 r = wait_thread_handle( info );
1265 static UINT HANDLE_CustomType53_54(MSIPACKAGE *package, LPCWSTR source,
1266 LPCWSTR target, const INT type, LPCWSTR action)
1268 msi_custom_action_info *info;
1271 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1273 prop = msi_dup_property(package,source);
1275 return ERROR_SUCCESS;
1277 info = do_msidbCustomActionTypeScript( package, type, prop, NULL, action );
1279 return wait_thread_handle( info );
1282 void ACTION_FinishCustomActions(const MSIPACKAGE* package)
1285 HANDLE *wait_handles;
1286 unsigned int handle_count, i;
1287 msi_custom_action_info *info, *cursor;
1289 while ((item = list_head( &package->RunningActions )))
1291 MSIRUNNINGACTION *action = LIST_ENTRY( item, MSIRUNNINGACTION, entry );
1293 list_remove( &action->entry );
1295 TRACE("waiting for %s\n", debugstr_w( action->name ) );
1296 msi_dialog_check_messages( action->handle );
1298 CloseHandle( action->handle );
1299 msi_free( action->name );
1303 EnterCriticalSection( &msi_custom_action_cs );
1305 handle_count = list_count( &msi_pending_custom_actions );
1306 wait_handles = HeapAlloc( GetProcessHeap(), 0, handle_count * sizeof(HANDLE) );
1309 LIST_FOR_EACH_ENTRY_SAFE( info, cursor, &msi_pending_custom_actions, msi_custom_action_info, entry )
1311 if (info->package == package )
1313 if (DuplicateHandle(GetCurrentProcess(), info->handle, GetCurrentProcess(), &wait_handles[handle_count], SYNCHRONIZE, FALSE, 0))
1315 free_custom_action_data( info );
1319 LeaveCriticalSection( &msi_custom_action_cs );
1321 for (i = 0; i < handle_count; i++)
1323 msi_dialog_check_messages( wait_handles[i] );
1324 CloseHandle( wait_handles[i] );
1327 HeapFree( GetProcessHeap(), 0, wait_handles );