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 + 1;
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: [properties]Action */
193 if ((ptr = strchrW(action_copy, ']')))
195 deferred_data = action_copy;
199 row = MSI_QueryGetRecord( package->db, ExecSeqQuery, action );
202 msi_free(action_copy);
203 return ERROR_CALL_NOT_IMPLEMENTED;
206 type = MSI_RecordGetInteger(row,2);
208 source = MSI_RecordGetString(row,3);
209 target = MSI_RecordGetString(row,4);
211 TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type,
212 debugstr_w(source), debugstr_w(target));
214 /* handle some of the deferred actions */
215 if (type & msidbCustomActionTypeTSAware)
216 FIXME("msidbCustomActionTypeTSAware not handled\n");
218 if (type & msidbCustomActionTypeInScript)
220 if (type & msidbCustomActionTypeNoImpersonate)
221 FIXME("msidbCustomActionTypeNoImpersonate not handled\n");
223 if (type & msidbCustomActionTypeRollback)
225 FIXME("Rollback only action... rollbacks not supported yet\n");
226 schedule_action(package, ROLLBACK_SCRIPT, action);
232 LPWSTR actiondata = msi_dup_property(package, action);
233 LPWSTR usersid = msi_dup_property(package, UserSID);
234 LPWSTR prodcode = msi_dup_property(package, ProdCode);
235 LPWSTR deferred = msi_get_deferred_action(action, actiondata, usersid, prodcode);
237 if (type & msidbCustomActionTypeCommit)
239 TRACE("Deferring Commit Action!\n");
240 schedule_action(package, COMMIT_SCRIPT, deferred);
244 TRACE("Deferring Action!\n");
245 schedule_action(package, INSTALL_SCRIPT, deferred);
249 msi_free(actiondata);
257 static const WCHAR szBlank[] = {0};
259 LPWSTR actiondata = msi_dup_property( package, action );
262 set_deferred_action_props(package, deferred_data);
264 MSI_SetPropertyW(package,szActionData,actiondata);
266 MSI_SetPropertyW(package,szActionData,szBlank);
268 msi_free(actiondata);
271 else if (!check_execution_scheduling_options(package,action,type))
277 switch (type & CUSTOM_ACTION_TYPE_MASK)
279 case 1: /* DLL file stored in a Binary table stream */
280 rc = HANDLE_CustomType1(package,source,target,type,action);
282 case 2: /* EXE file stored in a Binary table stream */
283 rc = HANDLE_CustomType2(package,source,target,type,action);
285 case 18: /*EXE file installed with package */
286 rc = HANDLE_CustomType18(package,source,target,type,action);
288 case 19: /* Error that halts install */
289 rc = HANDLE_CustomType19(package,source,target,type,action);
292 rc = HANDLE_CustomType17(package,source,target,type,action);
294 case 23: /* installs another package in the source tree */
295 deformat_string(package,target,&deformated);
296 rc = HANDLE_CustomType23(package,source,deformated,type,action);
298 case 50: /*EXE file specified by a property value */
299 rc = HANDLE_CustomType50(package,source,target,type,action);
301 case 34: /*EXE to be run in specified directory */
302 rc = HANDLE_CustomType34(package,source,target,type,action);
304 case 35: /* Directory set with formatted text. */
305 deformat_string(package,target,&deformated);
306 MSI_SetTargetPathW(package, source, deformated);
307 msi_free(deformated);
309 case 51: /* Property set with formatted text. */
310 deformat_string(package,target,&deformated);
311 rc = MSI_SetPropertyW(package,source,deformated);
312 msi_free(deformated);
314 case 37: /* JScript/VBScript text stored in target column. */
316 rc = HANDLE_CustomType37_38(package,source,target,type,action);
319 case 6: /* JScript/VBScript file stored in a Binary table stream. */
320 rc = HANDLE_CustomType5_6(package,source,target,type,action);
322 case 21: /* JScript/VBScript file installed with the product. */
324 rc = HANDLE_CustomType21_22(package,source,target,type,action);
326 case 53: /* JScript/VBScript text specified by a property value. */
328 rc = HANDLE_CustomType53_54(package,source,target,type,action);
331 FIXME("UNHANDLED ACTION TYPE %i (%s %s)\n",
332 type & CUSTOM_ACTION_TYPE_MASK, debugstr_w(source),
337 msi_free(action_copy);
338 msiobj_release(&row->hdr);
343 static UINT store_binary_to_temp(MSIPACKAGE *package, LPCWSTR source,
346 static const WCHAR query[] = {
347 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
348 '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',' ',
349 '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
353 static const WCHAR f1[] = {'m','s','i',0};
358 if (MSI_GetPropertyW(package, cszTempFolder, fmt, &sz) != ERROR_SUCCESS)
359 GetTempPathW(MAX_PATH, fmt);
361 if (GetTempFileNameW(fmt, f1, 0, tmp_file) == 0)
363 TRACE("Unable to create file\n");
364 return ERROR_FUNCTION_FAILED;
366 track_tempfile(package, tmp_file);
368 row = MSI_QueryGetRecord(package->db, query, source);
370 return ERROR_FUNCTION_FAILED;
372 /* write out the file */
373 file = CreateFileW(tmp_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
374 FILE_ATTRIBUTE_NORMAL, NULL);
375 if (file == INVALID_HANDLE_VALUE)
376 r = ERROR_FUNCTION_FAILED;
383 r = MSI_RecordReadStream(row, 2, buffer, &sz);
384 if (r != ERROR_SUCCESS)
386 ERR("Failed to get stream\n");
389 WriteFile(file, buffer, sz, &write, NULL);
390 } while (sz == sizeof buffer);
394 msiobj_release(&row->hdr);
399 static void file_running_action(MSIPACKAGE* package, HANDLE Handle,
400 BOOL process, LPCWSTR name)
402 MSIRUNNINGACTION *action;
404 action = msi_alloc( sizeof(MSIRUNNINGACTION) );
406 action->handle = Handle;
407 action->process = process;
408 action->name = strdupW(name);
410 list_add_tail( &package->RunningActions, &action->entry );
413 static UINT custom_get_process_return( HANDLE process )
417 GetExitCodeProcess( process, &rc );
419 return ERROR_FUNCTION_FAILED;
420 return ERROR_SUCCESS;
423 static UINT custom_get_thread_return( MSIPACKAGE *package, HANDLE thread )
427 GetExitCodeThread( thread, &rc );
431 case ERROR_FUNCTION_NOT_CALLED:
433 case ERROR_INSTALL_USEREXIT:
434 case ERROR_INSTALL_FAILURE:
436 case ERROR_NO_MORE_ITEMS:
437 return ERROR_SUCCESS;
438 case ERROR_INSTALL_SUSPEND:
439 ACTION_ForceReboot( package );
440 return ERROR_SUCCESS;
442 ERR("Invalid Return Code %d\n",rc);
443 return ERROR_INSTALL_FAILURE;
447 static UINT wait_process_handle(MSIPACKAGE* package, UINT type,
448 HANDLE ProcessHandle, LPCWSTR name)
450 UINT rc = ERROR_SUCCESS;
452 if (!(type & msidbCustomActionTypeAsync))
454 TRACE("waiting for %s\n", debugstr_w(name));
456 msi_dialog_check_messages(ProcessHandle);
458 if (!(type & msidbCustomActionTypeContinue))
459 rc = custom_get_process_return(ProcessHandle);
461 CloseHandle(ProcessHandle);
465 TRACE("%s running in background\n", debugstr_w(name));
467 if (!(type & msidbCustomActionTypeContinue))
468 file_running_action(package, ProcessHandle, TRUE, name);
470 CloseHandle(ProcessHandle);
476 typedef struct _msi_custom_action_info {
485 } msi_custom_action_info;
487 static void free_custom_action_data( msi_custom_action_info *info )
489 EnterCriticalSection( &msi_custom_action_cs );
490 list_remove( &info->entry );
491 LeaveCriticalSection( &msi_custom_action_cs );
493 CloseHandle( info->handle );
494 msi_free( info->action );
495 msi_free( info->source );
496 msi_free( info->target );
497 msiobj_release( &info->package->hdr );
501 static UINT wait_thread_handle( msi_custom_action_info *info )
503 UINT rc = ERROR_SUCCESS;
505 if (!(info->type & msidbCustomActionTypeAsync))
507 TRACE("waiting for %s\n", debugstr_w( info->action ));
509 msi_dialog_check_messages( info->handle );
511 if (!(info->type & msidbCustomActionTypeContinue))
512 rc = custom_get_thread_return( info->package, info->handle );
514 free_custom_action_data( info );
518 TRACE("%s running in background\n", debugstr_w( info->action ));
524 static msi_custom_action_info *find_action_by_guid( const GUID *guid )
526 msi_custom_action_info *info;
529 EnterCriticalSection( &msi_custom_action_cs );
531 LIST_FOR_EACH_ENTRY( info, &msi_pending_custom_actions, msi_custom_action_info, entry )
533 if (IsEqualGUID( &info->guid, guid ))
540 LeaveCriticalSection( &msi_custom_action_cs );
548 static void handle_msi_break( LPCWSTR target )
553 static const WCHAR MsiBreak[] = { 'M','s','i','B','r','e','a','k',0 };
554 static const WCHAR WindowsInstaller[] = {
555 'W','i','n','d','o','w','s',' ','I','n','s','t','a','l','l','e','r',0
558 static const WCHAR format[] = {
559 'T','o',' ','d','e','b','u','g',' ','y','o','u','r',' ',
560 'c','u','s','t','o','m',' ','a','c','t','i','o','n',',',' ',
561 'a','t','t','a','c','h',' ','y','o','u','r',' ','d','e','b','u','g','g','e','r',' ',
562 't','o',' ','p','r','o','c','e','s','s',' ','%','i',' ','(','0','x','%','X',')',' ',
563 'a','n','d',' ','p','r','e','s','s',' ','O','K',0
566 if( !GetEnvironmentVariableW( MsiBreak, val, MAX_PATH ))
569 if( lstrcmpiW( val, target ))
572 msg = msi_alloc( (lstrlenW(format) + 10) * sizeof(WCHAR) );
576 wsprintfW( msg, format, GetCurrentProcessId(), GetCurrentProcessId());
577 MessageBoxW( NULL, msg, WindowsInstaller, MB_OK);
582 static DWORD WINAPI ACTION_CallDllFunction( const GUID *guid )
584 msi_custom_action_info *info;
585 MsiCustomActionEntryPoint fn;
589 UINT r = ERROR_FUNCTION_FAILED;
591 info = find_action_by_guid( guid );
594 ERR("failed to find action %s\n", debugstr_guid( guid) );
598 TRACE("%s %s\n", debugstr_w( info->source ), debugstr_w( info->target ) );
600 hModule = LoadLibraryW( info->source );
603 ERR("failed to load dll %s\n", debugstr_w( info->source ) );
607 proc = strdupWtoA( info->target );
608 fn = (MsiCustomActionEntryPoint) GetProcAddress( hModule, proc );
612 hPackage = alloc_msihandle( &info->package->hdr );
615 TRACE("calling %s\n", debugstr_w( info->target ) );
616 handle_msi_break( info->target );
624 ERR("Custom action (%s:%s) caused a page fault: %08x\n",
625 debugstr_w(info->source), debugstr_w(info->target), GetExceptionCode());
630 MsiCloseHandle( hPackage );
633 ERR("failed to create handle for %p\n", info->package );
636 ERR("GetProcAddress(%s) failed\n", debugstr_w( info->target ) );
638 FreeLibrary(hModule);
640 if (info->type & msidbCustomActionTypeAsync &&
641 info->type & msidbCustomActionTypeContinue)
642 free_custom_action_data( info );
647 static DWORD WINAPI DllThread( LPVOID arg )
652 TRACE("custom action (%x) started\n", GetCurrentThreadId() );
654 rc = ACTION_CallDllFunction( guid );
656 TRACE("custom action (%x) returned %i\n", GetCurrentThreadId(), rc );
658 MsiCloseAllHandles();
662 static DWORD WINAPI ACTION_CAInstallPackage(const GUID *guid)
664 msi_custom_action_info *info;
665 UINT r = ERROR_FUNCTION_FAILED;
666 INSTALLUILEVEL old_level;
668 info = find_action_by_guid(guid);
671 ERR("failed to find action %s\n", debugstr_guid(guid));
675 old_level = MsiSetInternalUI(INSTALLUILEVEL_BASIC, NULL);
676 r = MsiInstallProductW(info->source, info->target);
677 MsiSetInternalUI(old_level, NULL);
682 static DWORD WINAPI ConcurrentInstallThread(LPVOID arg)
687 TRACE("concurrent installation (%x) started\n", GetCurrentThreadId());
689 rc = ACTION_CAInstallPackage(guid);
691 TRACE("concurrent installation (%x) returned %i\n", GetCurrentThreadId(), rc);
693 MsiCloseAllHandles();
697 static msi_custom_action_info *do_msidbCustomActionTypeDll(
698 MSIPACKAGE *package, INT type, LPCWSTR source, LPCWSTR target, LPCWSTR action )
700 msi_custom_action_info *info;
702 info = msi_alloc( sizeof *info );
706 msiobj_addref( &package->hdr );
707 info->package = package;
709 info->target = strdupW( target );
710 info->source = strdupW( source );
711 info->action = strdupW( action );
712 CoCreateGuid( &info->guid );
714 EnterCriticalSection( &msi_custom_action_cs );
715 list_add_tail( &msi_pending_custom_actions, &info->entry );
716 LeaveCriticalSection( &msi_custom_action_cs );
718 info->handle = CreateThread( NULL, 0, DllThread, &info->guid, 0, NULL );
721 free_custom_action_data( info );
728 static msi_custom_action_info *do_msidbCAConcurrentInstall(
729 MSIPACKAGE *package, INT type, LPCWSTR source, LPCWSTR target, LPCWSTR action)
731 msi_custom_action_info *info;
733 info = msi_alloc( sizeof *info );
737 msiobj_addref( &package->hdr );
738 info->package = package;
740 info->target = strdupW( target );
741 info->source = strdupW( source );
742 info->action = strdupW( action );
743 CoCreateGuid( &info->guid );
745 EnterCriticalSection( &msi_custom_action_cs );
746 list_add_tail( &msi_pending_custom_actions, &info->entry );
747 LeaveCriticalSection( &msi_custom_action_cs );
749 info->handle = CreateThread( NULL, 0, ConcurrentInstallThread, &info->guid, 0, NULL );
752 free_custom_action_data( info );
759 static UINT HANDLE_CustomType23(MSIPACKAGE *package, LPCWSTR source,
760 LPCWSTR target, const INT type, LPCWSTR action)
762 msi_custom_action_info *info;
763 WCHAR package_path[MAX_PATH];
766 static const WCHAR backslash[] = {'\\',0};
768 MSI_GetPropertyW(package, cszSourceDir, package_path, &size);
769 lstrcatW(package_path, backslash);
770 lstrcatW(package_path, source);
772 if (GetFileAttributesW(package_path) == INVALID_FILE_ATTRIBUTES)
774 ERR("Source package does not exist: %s\n", debugstr_w(package_path));
775 return ERROR_FUNCTION_FAILED;
778 TRACE("Installing package %s concurrently\n", debugstr_w(package_path));
780 info = do_msidbCAConcurrentInstall(package, type, package_path, target, action);
782 return wait_thread_handle(info);
785 static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source,
786 LPCWSTR target, const INT type, LPCWSTR action)
788 msi_custom_action_info *info;
789 WCHAR tmp_file[MAX_PATH];
792 r = store_binary_to_temp(package, source, tmp_file);
793 if (r != ERROR_SUCCESS)
796 TRACE("Calling function %s from %s\n",debugstr_w(target),
797 debugstr_w(tmp_file));
799 if (!strchrW(tmp_file,'.'))
801 static const WCHAR dot[]={'.',0};
802 strcatW(tmp_file,dot);
805 info = do_msidbCustomActionTypeDll( package, type, tmp_file, target, action );
807 return wait_thread_handle( info );
810 static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source,
811 LPCWSTR target, const INT type, LPCWSTR action)
813 WCHAR tmp_file[MAX_PATH];
815 PROCESS_INFORMATION info;
818 WCHAR *deformated = NULL;
820 static const WCHAR spc[] = {' ',0};
823 memset(&si,0,sizeof(STARTUPINFOW));
825 r = store_binary_to_temp(package, source, tmp_file);
826 if (r != ERROR_SUCCESS)
829 deformat_string(package,target,&deformated);
831 len = strlenW(tmp_file)+2;
834 len += strlenW(deformated);
836 cmd = msi_alloc(sizeof(WCHAR)*len);
838 strcpyW(cmd,tmp_file);
842 strcatW(cmd,deformated);
844 msi_free(deformated);
847 TRACE("executing exe %s\n", debugstr_w(cmd));
849 rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
850 c_collen, &si, &info);
855 ERR("Unable to execute command %s\n", debugstr_w(cmd));
856 return ERROR_SUCCESS;
858 CloseHandle( info.hThread );
860 r = wait_process_handle(package, type, info.hProcess, action);
865 static UINT HANDLE_CustomType17(MSIPACKAGE *package, LPCWSTR source,
866 LPCWSTR target, const INT type, LPCWSTR action)
868 msi_custom_action_info *info;
871 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
873 file = get_loaded_file( package, source );
876 ERR("invalid file key %s\n", debugstr_w( source ));
877 return ERROR_FUNCTION_FAILED;
880 info = do_msidbCustomActionTypeDll( package, type, file->TargetPath, target, action );
882 return wait_thread_handle( info );
885 static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source,
886 LPCWSTR target, const INT type, LPCWSTR action)
889 PROCESS_INFORMATION info;
894 static const WCHAR spc[] = {' ',0};
897 memset(&si,0,sizeof(STARTUPINFOW));
899 file = get_loaded_file(package,source);
901 return ERROR_FUNCTION_FAILED;
903 len = lstrlenW( file->TargetPath );
905 deformat_string(package,target,&deformated);
907 len += strlenW(deformated);
910 cmd = msi_alloc(len * sizeof(WCHAR));
912 lstrcpyW( cmd, file->TargetPath);
916 strcatW(cmd, deformated);
918 msi_free(deformated);
921 TRACE("executing exe %s\n", debugstr_w(cmd));
923 rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
924 c_collen, &si, &info);
928 ERR("Unable to execute command %s\n", debugstr_w(cmd));
930 return ERROR_SUCCESS;
933 CloseHandle( info.hThread );
935 return wait_process_handle(package, type, info.hProcess, action);
938 static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
939 LPCWSTR target, const INT type, LPCWSTR action)
941 static const WCHAR query[] = {
942 'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
943 'F','R','O','M',' ','`','E','r','r','o','r','`',' ',
944 'W','H','E','R','E',' ','`','E','r','r','o','r','`',' ','=',' ',
948 LPWSTR deformated = NULL;
950 deformat_string( package, target, &deformated );
952 /* first try treat the error as a number */
953 row = MSI_QueryGetRecord( package->db, query, deformated );
956 LPCWSTR error = MSI_RecordGetString( row, 1 );
957 MessageBoxW( NULL, error, NULL, MB_OK );
958 msiobj_release( &row->hdr );
961 MessageBoxW( NULL, deformated, NULL, MB_OK );
963 msi_free( deformated );
965 return ERROR_FUNCTION_FAILED;
968 static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source,
969 LPCWSTR target, const INT type, LPCWSTR action)
972 PROCESS_INFORMATION info;
978 static const WCHAR spc[] = {' ',0};
980 memset(&si,0,sizeof(STARTUPINFOW));
981 memset(&info,0,sizeof(PROCESS_INFORMATION));
983 prop = msi_dup_property( package, source );
985 return ERROR_SUCCESS;
987 deformat_string(package,target,&deformated);
988 len = strlenW(prop) + 2;
990 len += strlenW(deformated);
992 cmd = msi_alloc(sizeof(WCHAR)*len);
998 strcatW(cmd,deformated);
1000 msi_free(deformated);
1004 TRACE("executing exe %s\n", debugstr_w(cmd));
1006 rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
1007 c_collen, &si, &info);
1011 ERR("Unable to execute command %s\n", debugstr_w(cmd));
1013 return ERROR_SUCCESS;
1017 CloseHandle( info.hThread );
1019 return wait_process_handle(package, type, info.hProcess, action);
1022 static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
1023 LPCWSTR target, const INT type, LPCWSTR action)
1025 LPWSTR filename, deformated;
1027 PROCESS_INFORMATION info;
1030 memset(&si,0,sizeof(STARTUPINFOW));
1032 filename = resolve_folder(package, source, FALSE, FALSE, TRUE, NULL);
1035 return ERROR_FUNCTION_FAILED;
1037 SetCurrentDirectoryW(filename);
1040 deformat_string(package,target,&deformated);
1043 return ERROR_FUNCTION_FAILED;
1045 TRACE("executing exe %s\n", debugstr_w(deformated));
1047 rc = CreateProcessW(NULL, deformated, NULL, NULL, FALSE, 0, NULL,
1048 c_collen, &si, &info);
1052 ERR("Unable to execute command %s\n", debugstr_w(deformated));
1053 msi_free(deformated);
1054 return ERROR_SUCCESS;
1056 msi_free(deformated);
1057 CloseHandle( info.hThread );
1059 return wait_process_handle(package, type, info.hProcess, action);
1062 static DWORD WINAPI ACTION_CallScript( const GUID *guid )
1064 msi_custom_action_info *info;
1066 UINT r = ERROR_FUNCTION_FAILED;
1068 info = find_action_by_guid( guid );
1071 ERR("failed to find action %s\n", debugstr_guid( guid) );
1075 TRACE("function %s, script %s\n", debugstr_w( info->target ), debugstr_w( info->source ) );
1077 hPackage = alloc_msihandle( &info->package->hdr );
1080 r = call_script( hPackage, info->type, info->source, info->target, info->action );
1081 MsiCloseHandle( hPackage );
1084 ERR("failed to create handle for %p\n", info->package );
1086 if (info->type & msidbCustomActionTypeAsync &&
1087 info->type & msidbCustomActionTypeContinue)
1088 free_custom_action_data( info );
1093 static DWORD WINAPI ScriptThread( LPVOID arg )
1098 TRACE("custom action (%x) started\n", GetCurrentThreadId() );
1100 rc = ACTION_CallScript( guid );
1102 TRACE("custom action (%x) returned %i\n", GetCurrentThreadId(), rc );
1104 MsiCloseAllHandles();
1108 static msi_custom_action_info *do_msidbCustomActionTypeScript(
1109 MSIPACKAGE *package, INT type, LPCWSTR script, LPCWSTR function, LPCWSTR action )
1111 msi_custom_action_info *info;
1113 info = msi_alloc( sizeof *info );
1117 msiobj_addref( &package->hdr );
1118 info->package = package;
1120 info->target = strdupW( function );
1121 info->source = strdupW( script );
1122 info->action = strdupW( action );
1123 CoCreateGuid( &info->guid );
1125 EnterCriticalSection( &msi_custom_action_cs );
1126 list_add_tail( &msi_pending_custom_actions, &info->entry );
1127 LeaveCriticalSection( &msi_custom_action_cs );
1129 info->handle = CreateThread( NULL, 0, ScriptThread, &info->guid, 0, NULL );
1132 free_custom_action_data( info );
1139 static UINT HANDLE_CustomType37_38(MSIPACKAGE *package, LPCWSTR source,
1140 LPCWSTR target, const INT type, LPCWSTR action)
1142 msi_custom_action_info *info;
1144 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1146 info = do_msidbCustomActionTypeScript( package, type, target, NULL, action );
1148 return wait_thread_handle( info );
1151 static UINT HANDLE_CustomType5_6(MSIPACKAGE *package, LPCWSTR source,
1152 LPCWSTR target, const INT type, LPCWSTR action)
1154 static const WCHAR query[] = {
1155 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1156 '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',' ',
1157 '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
1159 msi_custom_action_info *info;
1160 CHAR *buffer = NULL;
1161 WCHAR *bufferw = NULL;
1165 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1167 row = MSI_QueryGetRecord(package->db, query, source);
1169 return ERROR_FUNCTION_FAILED;
1171 r = MSI_RecordReadStream(row, 2, NULL, &sz);
1172 if (r != ERROR_SUCCESS)
1175 buffer = msi_alloc(sizeof(CHAR)*(sz+1));
1177 return ERROR_FUNCTION_FAILED;
1179 r = MSI_RecordReadStream(row, 2, buffer, &sz);
1180 if (r != ERROR_SUCCESS)
1184 bufferw = strdupAtoW(buffer);
1187 r = ERROR_FUNCTION_FAILED;
1191 info = do_msidbCustomActionTypeScript( package, type, bufferw, target, action );
1192 r = wait_thread_handle( info );
1200 static UINT HANDLE_CustomType21_22(MSIPACKAGE *package, LPCWSTR source,
1201 LPCWSTR target, const INT type, LPCWSTR action)
1203 msi_custom_action_info *info;
1206 DWORD sz, szHighWord = 0, read;
1208 WCHAR *bufferw=NULL;
1212 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1214 file = get_loaded_file(package,source);
1217 ERR("invalid file key %s\n", debugstr_w(source));
1218 return ERROR_FUNCTION_FAILED;
1221 hFile = CreateFileW(file->TargetPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
1222 if (hFile == INVALID_HANDLE_VALUE)
1223 return ERROR_FUNCTION_FAILED;
1225 sz = GetFileSize(hFile, &szHighWord);
1226 if (sz == INVALID_FILE_SIZE || szHighWord != 0)
1229 return ERROR_FUNCTION_FAILED;
1232 buffer = msi_alloc(sizeof(CHAR)*(sz+1));
1236 return ERROR_FUNCTION_FAILED;
1239 bRet = ReadFile(hFile, buffer, sz, &read, NULL);
1243 r = ERROR_FUNCTION_FAILED;
1248 bufferw = strdupAtoW(buffer);
1251 r = ERROR_FUNCTION_FAILED;
1255 info = do_msidbCustomActionTypeScript( package, type, bufferw, target, action );
1256 r = wait_thread_handle( info );
1264 static UINT HANDLE_CustomType53_54(MSIPACKAGE *package, LPCWSTR source,
1265 LPCWSTR target, const INT type, LPCWSTR action)
1267 msi_custom_action_info *info;
1270 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1272 prop = msi_dup_property(package,source);
1274 return ERROR_SUCCESS;
1276 info = do_msidbCustomActionTypeScript( package, type, prop, NULL, action );
1278 return wait_thread_handle( info );
1281 void ACTION_FinishCustomActions(const MSIPACKAGE* package)
1284 HANDLE *wait_handles;
1285 unsigned int handle_count, i;
1286 msi_custom_action_info *info, *cursor;
1288 while ((item = list_head( &package->RunningActions )))
1290 MSIRUNNINGACTION *action = LIST_ENTRY( item, MSIRUNNINGACTION, entry );
1292 list_remove( &action->entry );
1294 TRACE("waiting for %s\n", debugstr_w( action->name ) );
1295 msi_dialog_check_messages( action->handle );
1297 CloseHandle( action->handle );
1298 msi_free( action->name );
1302 EnterCriticalSection( &msi_custom_action_cs );
1304 handle_count = list_count( &msi_pending_custom_actions );
1305 wait_handles = HeapAlloc( GetProcessHeap(), 0, handle_count * sizeof(HANDLE) );
1308 LIST_FOR_EACH_ENTRY_SAFE( info, cursor, &msi_pending_custom_actions, msi_custom_action_info, entry )
1310 if (info->package == package )
1312 if (DuplicateHandle(GetCurrentProcess(), info->handle, GetCurrentProcess(), &wait_handles[handle_count], SYNCHRONIZE, FALSE, 0))
1314 free_custom_action_data( info );
1318 LeaveCriticalSection( &msi_custom_action_cs );
1320 for (i = 0; i < handle_count; i++)
1322 msi_dialog_check_messages( wait_handles[i] );
1323 CloseHandle( wait_handles[i] );
1326 HeapFree( GetProcessHeap(), 0, wait_handles );