msi: Add handling for the concurrent install custom action.
[wine/wine-kai.git] / dlls / msi / custom.c
blobc38ea9888d3979fc758f9d344dbd46af05f2c69b
1 /*
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
21 #define COBJMACROS
23 #include <stdarg.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winerror.h"
27 #include "msidefs.h"
28 #include "msipriv.h"
29 #include "winuser.h"
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
41 struct list entry;
42 HANDLE handle;
43 BOOL process;
44 LPWSTR name;
45 } MSIRUNNINGACTION;
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);
64 typedef UINT (WINAPI *MsiCustomActionEntryPoint)( MSIHANDLE );
66 static CRITICAL_SECTION msi_custom_action_cs;
67 static CRITICAL_SECTION_DEBUG msi_custom_action_cs_debug =
69 0, 0, &msi_custom_action_cs,
70 { &msi_custom_action_cs_debug.ProcessLocksList,
71 &msi_custom_action_cs_debug.ProcessLocksList },
72 0, 0, { (DWORD_PTR)(__FILE__ ": msi_custom_action_cs") }
74 static CRITICAL_SECTION msi_custom_action_cs = { &msi_custom_action_cs_debug, -1, 0, 0, 0, 0 };
76 static struct list msi_pending_custom_actions = LIST_INIT( msi_pending_custom_actions );
78 static BOOL check_execution_scheduling_options(MSIPACKAGE *package, LPCWSTR action, UINT options)
80 if (!package->script)
81 return TRUE;
83 if ((options & msidbCustomActionTypeClientRepeat) ==
84 msidbCustomActionTypeClientRepeat)
86 if (!(package->script->InWhatSequence & SEQUENCE_UI &&
87 package->script->InWhatSequence & SEQUENCE_EXEC))
89 TRACE("Skipping action due to dbCustomActionTypeClientRepeat option.\n");
90 return FALSE;
93 else if (options & msidbCustomActionTypeFirstSequence)
95 if (package->script->InWhatSequence & SEQUENCE_UI &&
96 package->script->InWhatSequence & SEQUENCE_EXEC )
98 TRACE("Skipping action due to msidbCustomActionTypeFirstSequence option.\n");
99 return FALSE;
102 else if (options & msidbCustomActionTypeOncePerProcess)
104 if (check_unique_action(package,action))
106 TRACE("Skipping action due to msidbCustomActionTypeOncePerProcess option.\n");
107 return FALSE;
109 else
110 register_unique_action(package,action);
113 return TRUE;
116 /* stores the CustomActionData before the action:
117 * [CustomActionData]Action
119 static LPWSTR msi_get_deferred_action(LPCWSTR action, LPWSTR actiondata)
121 LPWSTR deferred;
122 DWORD len;
124 static const WCHAR begin[] = {'[',0};
125 static const WCHAR end[] = {']',0};
127 if (!actiondata)
128 return strdupW(action);
130 len = lstrlenW(action) + lstrlenW(actiondata) + 3;
131 deferred = msi_alloc(len * sizeof(WCHAR));
133 lstrcpyW(deferred, begin);
134 lstrcatW(deferred, actiondata);
135 lstrcatW(deferred, end);
136 lstrcatW(deferred, action);
138 return deferred;
141 UINT ACTION_CustomAction(MSIPACKAGE *package,LPCWSTR action, BOOL execute)
143 UINT rc = ERROR_SUCCESS;
144 MSIRECORD * row = 0;
145 static const WCHAR ExecSeqQuery[] =
146 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
147 '`','C','u','s','t','o' ,'m','A','c','t','i','o','n','`',
148 ' ','W','H','E','R','E',' ','`','A','c','t','i' ,'o','n','`',' ',
149 '=',' ','\'','%','s','\'',0};
150 UINT type;
151 LPCWSTR source, target;
152 LPWSTR ptr, deferred_data = NULL;
153 LPWSTR action_copy = strdupW(action);
154 WCHAR *deformated=NULL;
156 /* deferred action: [CustomActionData]Action */
157 if ((ptr = strchrW(action_copy, ']')))
159 deferred_data = action_copy + 1;
160 *ptr = '\0';
161 action = ptr + 1;
164 row = MSI_QueryGetRecord( package->db, ExecSeqQuery, action );
165 if (!row)
167 msi_free(action_copy);
168 return ERROR_CALL_NOT_IMPLEMENTED;
171 type = MSI_RecordGetInteger(row,2);
173 source = MSI_RecordGetString(row,3);
174 target = MSI_RecordGetString(row,4);
176 TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type,
177 debugstr_w(source), debugstr_w(target));
179 /* handle some of the deferred actions */
180 if (type & msidbCustomActionTypeTSAware)
181 FIXME("msidbCustomActionTypeTSAware not handled\n");
183 if (type & msidbCustomActionTypeInScript)
185 if (type & msidbCustomActionTypeNoImpersonate)
186 FIXME("msidbCustomActionTypeNoImpersonate not handled\n");
188 if (type & msidbCustomActionTypeRollback)
190 FIXME("Rollback only action... rollbacks not supported yet\n");
191 schedule_action(package, ROLLBACK_SCRIPT, action);
192 rc = ERROR_SUCCESS;
193 goto end;
195 if (!execute)
197 LPWSTR actiondata = msi_dup_property(package, action);
198 LPWSTR deferred = msi_get_deferred_action(action, actiondata);
200 if (type & msidbCustomActionTypeCommit)
202 TRACE("Deferring Commit Action!\n");
203 schedule_action(package, COMMIT_SCRIPT, deferred);
205 else
207 TRACE("Deferring Action!\n");
208 schedule_action(package, INSTALL_SCRIPT, deferred);
211 rc = ERROR_SUCCESS;
212 msi_free(deferred);
213 goto end;
215 else
217 /*Set ActionData*/
219 static const WCHAR szActionData[] = {
220 'C','u','s','t','o','m','A','c','t','i','o','n','D','a','t','a',0};
221 static const WCHAR szBlank[] = {0};
222 LPWSTR actiondata = msi_dup_property( package, action );
223 if (deferred_data)
224 MSI_SetPropertyW(package,szActionData,deferred_data);
225 else if (actiondata)
226 MSI_SetPropertyW(package,szActionData,actiondata);
227 else
228 MSI_SetPropertyW(package,szActionData,szBlank);
229 msi_free(actiondata);
232 else if (!check_execution_scheduling_options(package,action,type))
234 rc = ERROR_SUCCESS;
235 goto end;
238 switch (type & CUSTOM_ACTION_TYPE_MASK)
240 case 1: /* DLL file stored in a Binary table stream */
241 rc = HANDLE_CustomType1(package,source,target,type,action);
242 break;
243 case 2: /* EXE file stored in a Binary table stream */
244 rc = HANDLE_CustomType2(package,source,target,type,action);
245 break;
246 case 18: /*EXE file installed with package */
247 rc = HANDLE_CustomType18(package,source,target,type,action);
248 break;
249 case 19: /* Error that halts install */
250 rc = HANDLE_CustomType19(package,source,target,type,action);
251 break;
252 case 17:
253 rc = HANDLE_CustomType17(package,source,target,type,action);
254 break;
255 case 23: /* installs another package in the source tree */
256 deformat_string(package,target,&deformated);
257 rc = HANDLE_CustomType23(package,source,deformated,type,action);
258 break;
259 case 50: /*EXE file specified by a property value */
260 rc = HANDLE_CustomType50(package,source,target,type,action);
261 break;
262 case 34: /*EXE to be run in specified directory */
263 rc = HANDLE_CustomType34(package,source,target,type,action);
264 break;
265 case 35: /* Directory set with formatted text. */
266 deformat_string(package,target,&deformated);
267 MSI_SetTargetPathW(package, source, deformated);
268 msi_free(deformated);
269 break;
270 case 51: /* Property set with formatted text. */
271 deformat_string(package,target,&deformated);
272 rc = MSI_SetPropertyW(package,source,deformated);
273 msi_free(deformated);
274 break;
275 default:
276 FIXME("UNHANDLED ACTION TYPE %i (%s %s)\n",
277 type & CUSTOM_ACTION_TYPE_MASK, debugstr_w(source),
278 debugstr_w(target));
281 end:
282 msi_free(action_copy);
283 msiobj_release(&row->hdr);
284 return rc;
288 static UINT store_binary_to_temp(MSIPACKAGE *package, LPCWSTR source,
289 LPWSTR tmp_file)
291 static const WCHAR query[] = {
292 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
293 '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',' ',
294 '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
295 MSIRECORD *row = 0;
296 HANDLE file;
297 CHAR buffer[1024];
298 static const WCHAR f1[] = {'m','s','i',0};
299 WCHAR fmt[MAX_PATH];
300 DWORD sz = MAX_PATH;
301 UINT r;
303 if (MSI_GetPropertyW(package, cszTempFolder, fmt, &sz) != ERROR_SUCCESS)
304 GetTempPathW(MAX_PATH, fmt);
306 if (GetTempFileNameW(fmt, f1, 0, tmp_file) == 0)
308 TRACE("Unable to create file\n");
309 return ERROR_FUNCTION_FAILED;
311 track_tempfile(package, tmp_file);
313 row = MSI_QueryGetRecord(package->db, query, source);
314 if (!row)
315 return ERROR_FUNCTION_FAILED;
317 /* write out the file */
318 file = CreateFileW(tmp_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
319 FILE_ATTRIBUTE_NORMAL, NULL);
320 if (file == INVALID_HANDLE_VALUE)
321 r = ERROR_FUNCTION_FAILED;
322 else
326 DWORD write;
327 sz = sizeof buffer;
328 r = MSI_RecordReadStream(row, 2, buffer, &sz);
329 if (r != ERROR_SUCCESS)
331 ERR("Failed to get stream\n");
332 break;
334 WriteFile(file, buffer, sz, &write, NULL);
335 } while (sz == sizeof buffer);
336 CloseHandle(file);
339 msiobj_release(&row->hdr);
341 return r;
344 static void file_running_action(MSIPACKAGE* package, HANDLE Handle,
345 BOOL process, LPCWSTR name)
347 MSIRUNNINGACTION *action;
349 action = msi_alloc( sizeof(MSIRUNNINGACTION) );
351 action->handle = Handle;
352 action->process = process;
353 action->name = strdupW(name);
355 list_add_tail( &package->RunningActions, &action->entry );
358 static UINT custom_get_process_return( HANDLE process )
360 DWORD rc = 0;
362 GetExitCodeProcess( process, &rc );
363 if (rc != 0)
364 return ERROR_FUNCTION_FAILED;
365 return ERROR_SUCCESS;
368 static UINT custom_get_thread_return( HANDLE thread )
370 DWORD rc = 0;
372 GetExitCodeThread( thread, &rc );
374 switch (rc)
376 case ERROR_FUNCTION_NOT_CALLED:
377 case ERROR_SUCCESS:
378 case ERROR_INSTALL_USEREXIT:
379 case ERROR_INSTALL_FAILURE:
380 return rc;
381 case ERROR_NO_MORE_ITEMS:
382 return ERROR_SUCCESS;
383 default:
384 ERR("Invalid Return Code %d\n",rc);
385 return ERROR_INSTALL_FAILURE;
389 static UINT wait_process_handle(MSIPACKAGE* package, UINT type,
390 HANDLE ProcessHandle, LPCWSTR name)
392 UINT rc = ERROR_SUCCESS;
394 if (!(type & msidbCustomActionTypeAsync))
396 TRACE("waiting for %s\n", debugstr_w(name));
398 msi_dialog_check_messages(ProcessHandle);
400 if (!(type & msidbCustomActionTypeContinue))
401 rc = custom_get_process_return(ProcessHandle);
403 CloseHandle(ProcessHandle);
405 else
407 TRACE("%s running in background\n", debugstr_w(name));
409 if (!(type & msidbCustomActionTypeContinue))
410 file_running_action(package, ProcessHandle, TRUE, name);
411 else
412 CloseHandle(ProcessHandle);
415 return rc;
418 typedef struct _msi_custom_action_info {
419 struct list entry;
420 MSIPACKAGE *package;
421 LPWSTR source;
422 LPWSTR target;
423 HANDLE handle;
424 LPWSTR action;
425 INT type;
426 GUID guid;
427 } msi_custom_action_info;
429 static void free_custom_action_data( msi_custom_action_info *info )
431 EnterCriticalSection( &msi_custom_action_cs );
432 list_remove( &info->entry );
433 LeaveCriticalSection( &msi_custom_action_cs );
434 if (info->handle)
435 CloseHandle( info->handle );
436 msi_free( info->action );
437 msi_free( info->source );
438 msi_free( info->target );
439 msiobj_release( &info->package->hdr );
440 msi_free( info );
443 static UINT wait_thread_handle( msi_custom_action_info *info )
445 UINT rc = ERROR_SUCCESS;
447 if (!(info->type & msidbCustomActionTypeAsync))
449 TRACE("waiting for %s\n", debugstr_w( info->action ));
451 msi_dialog_check_messages( info->handle );
453 if (!(info->type & msidbCustomActionTypeContinue))
454 rc = custom_get_thread_return( info->handle );
456 free_custom_action_data( info );
458 else
460 TRACE("%s running in background\n", debugstr_w( info->action ));
463 return rc;
466 static msi_custom_action_info *find_action_by_guid( const GUID *guid )
468 msi_custom_action_info *info;
469 BOOL found = FALSE;
471 EnterCriticalSection( &msi_custom_action_cs );
473 LIST_FOR_EACH_ENTRY( info, &msi_pending_custom_actions, msi_custom_action_info, entry )
475 if (IsEqualGUID( &info->guid, guid ))
477 found = TRUE;
478 break;
482 LeaveCriticalSection( &msi_custom_action_cs );
484 if (!found)
485 return NULL;
487 return info;
490 static DWORD WINAPI ACTION_CallDllFunction( const GUID *guid )
492 msi_custom_action_info *info;
493 MsiCustomActionEntryPoint fn;
494 MSIHANDLE hPackage;
495 HANDLE hModule;
496 LPSTR proc;
497 UINT r = ERROR_FUNCTION_FAILED;
499 info = find_action_by_guid( guid );
500 if (!info)
502 ERR("failed to find action %s\n", debugstr_guid( guid) );
503 return r;
506 TRACE("%s %s\n", debugstr_w( info->source ), debugstr_w( info->target ) );
508 hModule = LoadLibraryW( info->source );
509 if (!hModule)
511 ERR("failed to load dll %s\n", debugstr_w( info->source ) );
512 return r;
515 proc = strdupWtoA( info->target );
516 fn = (MsiCustomActionEntryPoint) GetProcAddress( hModule, proc );
517 msi_free( proc );
518 if (fn)
520 hPackage = alloc_msihandle( &info->package->hdr );
521 if (hPackage)
523 TRACE("calling %s\n", debugstr_w( info->target ) );
524 r = fn( hPackage );
525 MsiCloseHandle( hPackage );
527 else
528 ERR("failed to create handle for %p\n", info->package );
530 else
531 ERR("GetProcAddress(%s) failed\n", debugstr_w( info->target ) );
533 FreeLibrary(hModule);
535 if (info->type & msidbCustomActionTypeAsync &&
536 info->type & msidbCustomActionTypeContinue)
537 free_custom_action_data( info );
539 return r;
542 static DWORD WINAPI DllThread( LPVOID arg )
544 LPGUID guid = arg;
545 DWORD rc = 0;
547 TRACE("custom action (%x) started\n", GetCurrentThreadId() );
549 rc = ACTION_CallDllFunction( guid );
551 TRACE("custom action (%x) returned %i\n", GetCurrentThreadId(), rc );
553 MsiCloseAllHandles();
554 return rc;
557 static DWORD WINAPI ACTION_CAInstallPackage(const GUID *guid)
559 msi_custom_action_info *info;
560 UINT r = ERROR_FUNCTION_FAILED;
561 INSTALLUILEVEL old_level;
563 info = find_action_by_guid(guid);
564 if (!info)
566 ERR("failed to find action %s\n", debugstr_guid(guid));
567 return r;
570 old_level = MsiSetInternalUI(INSTALLUILEVEL_BASIC, NULL);
571 r = MsiInstallProductW(info->source, info->target);
572 MsiSetInternalUI(old_level, NULL);
574 return r;
577 static DWORD WINAPI ConcurrentInstallThread(LPVOID arg)
579 LPGUID guid = arg;
580 DWORD rc;
582 TRACE("concurrent installation (%x) started\n", GetCurrentThreadId());
584 rc = ACTION_CAInstallPackage(guid);
586 TRACE("concurrent installation (%x) returned %i\n", GetCurrentThreadId(), rc);
588 MsiCloseAllHandles();
589 return rc;
592 static msi_custom_action_info *do_msidbCustomActionTypeDll(
593 MSIPACKAGE *package, INT type, LPCWSTR source, LPCWSTR target, LPCWSTR action )
595 msi_custom_action_info *info;
597 info = msi_alloc( sizeof *info );
598 if (!info)
599 return NULL;
601 msiobj_addref( &package->hdr );
602 info->package = package;
603 info->type = type;
604 info->target = strdupW( target );
605 info->source = strdupW( source );
606 info->action = strdupW( action );
607 CoCreateGuid( &info->guid );
609 EnterCriticalSection( &msi_custom_action_cs );
610 list_add_tail( &msi_pending_custom_actions, &info->entry );
611 LeaveCriticalSection( &msi_custom_action_cs );
613 info->handle = CreateThread( NULL, 0, DllThread, &info->guid, 0, NULL );
614 if (!info->handle)
616 free_custom_action_data( info );
617 return NULL;
620 return info;
623 static msi_custom_action_info *do_msidbCAConcurrentInstall(
624 MSIPACKAGE *package, INT type, LPCWSTR source, LPCWSTR target, LPCWSTR action)
626 msi_custom_action_info *info;
628 info = msi_alloc( sizeof *info );
629 if (!info)
630 return NULL;
632 msiobj_addref( &package->hdr );
633 info->package = package;
634 info->type = type;
635 info->target = strdupW( target );
636 info->source = strdupW( source );
637 info->action = strdupW( action );
638 CoCreateGuid( &info->guid );
640 EnterCriticalSection( &msi_custom_action_cs );
641 list_add_tail( &msi_pending_custom_actions, &info->entry );
642 LeaveCriticalSection( &msi_custom_action_cs );
644 info->handle = CreateThread( NULL, 0, ConcurrentInstallThread, &info->guid, 0, NULL );
645 if (!info->handle)
647 free_custom_action_data( info );
648 return NULL;
651 return info;
654 static UINT HANDLE_CustomType23(MSIPACKAGE *package, LPCWSTR source,
655 LPCWSTR target, const INT type, LPCWSTR action)
657 msi_custom_action_info *info;
658 WCHAR package_path[MAX_PATH];
659 DWORD size;
661 static const WCHAR backslash[] = {'\\',0};
663 MSI_GetPropertyW(package, cszSourceDir, package_path, &size);
664 lstrcatW(package_path, backslash);
665 lstrcatW(package_path, source);
667 if (GetFileAttributesW(package_path) == INVALID_FILE_ATTRIBUTES)
669 ERR("Source package does not exist: %s\n", debugstr_w(package_path));
670 return ERROR_FUNCTION_FAILED;
673 TRACE("Installing package %s concurrently\n", debugstr_w(package_path));
675 info = do_msidbCAConcurrentInstall(package, type, package_path, target, action);
677 return wait_thread_handle(info);
680 static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source,
681 LPCWSTR target, const INT type, LPCWSTR action)
683 msi_custom_action_info *info;
684 WCHAR tmp_file[MAX_PATH];
685 UINT r;
687 r = store_binary_to_temp(package, source, tmp_file);
688 if (r != ERROR_SUCCESS)
689 return r;
691 TRACE("Calling function %s from %s\n",debugstr_w(target),
692 debugstr_w(tmp_file));
694 if (!strchrW(tmp_file,'.'))
696 static const WCHAR dot[]={'.',0};
697 strcatW(tmp_file,dot);
700 info = do_msidbCustomActionTypeDll( package, type, tmp_file, target, action );
702 return wait_thread_handle( info );
705 static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source,
706 LPCWSTR target, const INT type, LPCWSTR action)
708 WCHAR tmp_file[MAX_PATH];
709 STARTUPINFOW si;
710 PROCESS_INFORMATION info;
711 BOOL rc;
712 INT len;
713 WCHAR *deformated = NULL;
714 WCHAR *cmd;
715 static const WCHAR spc[] = {' ',0};
716 UINT r;
718 memset(&si,0,sizeof(STARTUPINFOW));
720 r = store_binary_to_temp(package, source, tmp_file);
721 if (r != ERROR_SUCCESS)
722 return r;
724 deformat_string(package,target,&deformated);
726 len = strlenW(tmp_file)+2;
728 if (deformated)
729 len += strlenW(deformated);
731 cmd = msi_alloc(sizeof(WCHAR)*len);
733 strcpyW(cmd,tmp_file);
734 if (deformated)
736 strcatW(cmd,spc);
737 strcatW(cmd,deformated);
739 msi_free(deformated);
742 TRACE("executing exe %s\n", debugstr_w(cmd));
744 rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
745 c_collen, &si, &info);
746 msi_free(cmd);
748 if ( !rc )
750 ERR("Unable to execute command %s\n", debugstr_w(cmd));
751 return ERROR_SUCCESS;
753 CloseHandle( info.hThread );
755 r = wait_process_handle(package, type, info.hProcess, action);
757 return r;
760 static UINT HANDLE_CustomType17(MSIPACKAGE *package, LPCWSTR source,
761 LPCWSTR target, const INT type, LPCWSTR action)
763 msi_custom_action_info *info;
764 MSIFILE *file;
766 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
768 file = get_loaded_file( package, source );
769 if (!file)
771 ERR("invalid file key %s\n", debugstr_w( source ));
772 return ERROR_FUNCTION_FAILED;
775 info = do_msidbCustomActionTypeDll( package, type, file->TargetPath, target, action );
777 return wait_thread_handle( info );
780 static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source,
781 LPCWSTR target, const INT type, LPCWSTR action)
783 STARTUPINFOW si;
784 PROCESS_INFORMATION info;
785 BOOL rc;
786 WCHAR *deformated;
787 WCHAR *cmd;
788 INT len;
789 static const WCHAR spc[] = {' ',0};
790 MSIFILE *file;
792 memset(&si,0,sizeof(STARTUPINFOW));
794 file = get_loaded_file(package,source);
795 if( !file )
796 return ERROR_FUNCTION_FAILED;
798 len = lstrlenW( file->TargetPath );
800 deformat_string(package,target,&deformated);
801 if (deformated)
802 len += strlenW(deformated);
803 len += 2;
805 cmd = msi_alloc(len * sizeof(WCHAR));
807 lstrcpyW( cmd, file->TargetPath);
808 if (deformated)
810 strcatW(cmd, spc);
811 strcatW(cmd, deformated);
813 msi_free(deformated);
816 TRACE("executing exe %s\n", debugstr_w(cmd));
818 rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
819 c_collen, &si, &info);
821 if ( !rc )
823 ERR("Unable to execute command %s\n", debugstr_w(cmd));
824 msi_free(cmd);
825 return ERROR_SUCCESS;
827 msi_free(cmd);
828 CloseHandle( info.hThread );
830 return wait_process_handle(package, type, info.hProcess, action);
833 static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
834 LPCWSTR target, const INT type, LPCWSTR action)
836 static const WCHAR query[] = {
837 'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
838 'F','R','O','M',' ','`','E','r','r','o','r','`',' ',
839 'W','H','E','R','E',' ','`','E','r','r','o','r','`',' ','=',' ',
840 '%','s',0
842 MSIRECORD *row = 0;
843 LPWSTR deformated = NULL;
845 deformat_string( package, target, &deformated );
847 /* first try treat the error as a number */
848 row = MSI_QueryGetRecord( package->db, query, deformated );
849 if( row )
851 LPCWSTR error = MSI_RecordGetString( row, 1 );
852 MessageBoxW( NULL, error, NULL, MB_OK );
853 msiobj_release( &row->hdr );
855 else
856 MessageBoxW( NULL, deformated, NULL, MB_OK );
858 msi_free( deformated );
860 return ERROR_FUNCTION_FAILED;
863 static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source,
864 LPCWSTR target, const INT type, LPCWSTR action)
866 STARTUPINFOW si;
867 PROCESS_INFORMATION info;
868 WCHAR *prop;
869 BOOL rc;
870 WCHAR *deformated;
871 WCHAR *cmd;
872 INT len;
873 static const WCHAR spc[] = {' ',0};
875 memset(&si,0,sizeof(STARTUPINFOW));
876 memset(&info,0,sizeof(PROCESS_INFORMATION));
878 prop = msi_dup_property( package, source );
879 if (!prop)
880 return ERROR_SUCCESS;
882 deformat_string(package,target,&deformated);
883 len = strlenW(prop) + 2;
884 if (deformated)
885 len += strlenW(deformated);
887 cmd = msi_alloc(sizeof(WCHAR)*len);
889 strcpyW(cmd,prop);
890 if (deformated)
892 strcatW(cmd,spc);
893 strcatW(cmd,deformated);
895 msi_free(deformated);
897 msi_free(prop);
899 TRACE("executing exe %s\n", debugstr_w(cmd));
901 rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
902 c_collen, &si, &info);
904 if ( !rc )
906 ERR("Unable to execute command %s\n", debugstr_w(cmd));
907 msi_free(cmd);
908 return ERROR_SUCCESS;
910 msi_free(cmd);
912 CloseHandle( info.hThread );
914 return wait_process_handle(package, type, info.hProcess, action);
917 static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
918 LPCWSTR target, const INT type, LPCWSTR action)
920 LPWSTR filename, deformated;
921 STARTUPINFOW si;
922 PROCESS_INFORMATION info;
923 BOOL rc;
925 memset(&si,0,sizeof(STARTUPINFOW));
927 filename = resolve_folder(package, source, FALSE, FALSE, TRUE, NULL);
929 if (!filename)
930 return ERROR_FUNCTION_FAILED;
932 SetCurrentDirectoryW(filename);
933 msi_free(filename);
935 deformat_string(package,target,&deformated);
937 if (!deformated)
938 return ERROR_FUNCTION_FAILED;
940 TRACE("executing exe %s\n", debugstr_w(deformated));
942 rc = CreateProcessW(NULL, deformated, NULL, NULL, FALSE, 0, NULL,
943 c_collen, &si, &info);
945 if ( !rc )
947 ERR("Unable to execute command %s\n", debugstr_w(deformated));
948 msi_free(deformated);
949 return ERROR_SUCCESS;
951 msi_free(deformated);
952 CloseHandle( info.hThread );
954 return wait_process_handle(package, type, info.hProcess, action);
957 void ACTION_FinishCustomActions(MSIPACKAGE* package)
959 struct list *item;
960 HANDLE *wait_handles;
961 unsigned int handle_count, i;
962 msi_custom_action_info *info, *cursor;
964 while ((item = list_head( &package->RunningActions )))
966 MSIRUNNINGACTION *action = LIST_ENTRY( item, MSIRUNNINGACTION, entry );
968 list_remove( &action->entry );
970 TRACE("waiting for %s\n", debugstr_w( action->name ) );
971 msi_dialog_check_messages( action->handle );
973 CloseHandle( action->handle );
974 msi_free( action->name );
975 msi_free( action );
978 EnterCriticalSection( &msi_custom_action_cs );
980 handle_count = list_count( &msi_pending_custom_actions );
981 wait_handles = HeapAlloc( GetProcessHeap(), 0, handle_count * sizeof(HANDLE) );
983 handle_count = 0;
984 LIST_FOR_EACH_ENTRY_SAFE( info, cursor, &msi_pending_custom_actions, msi_custom_action_info, entry )
986 if (info->package == package )
988 if (DuplicateHandle(GetCurrentProcess(), info->handle, GetCurrentProcess(), &wait_handles[handle_count], SYNCHRONIZE, FALSE, 0))
989 handle_count++;
990 free_custom_action_data( info );
994 LeaveCriticalSection( &msi_custom_action_cs );
996 for (i = 0; i < handle_count; i++)
998 msi_dialog_check_messages( wait_handles[i] );
999 CloseHandle( wait_handles[i] );
1002 HeapFree( GetProcessHeap(), 0, wait_handles );