msi: Rewrite the second loop in ACTION_FinishCustomActions so that it always terminates.
[wine] / dlls / msi / custom.c
1 /*
2  * Custom Action processing for the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2005 Aric Stewart for CodeWeavers
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #define COBJMACROS
22
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"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(msi);
34
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};
38
39 typedef struct tagMSIRUNNINGACTION
40 {
41     struct list entry;
42     HANDLE handle;
43     BOOL   process;
44     LPWSTR name;
45 } MSIRUNNINGACTION;
46
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_CustomType50(MSIPACKAGE *package, LPCWSTR source,
58                                 LPCWSTR target, const INT type, LPCWSTR action);
59 static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
60                                 LPCWSTR target, const INT type, LPCWSTR action);
61
62 typedef UINT (WINAPI *MsiCustomActionEntryPoint)( MSIHANDLE );
63
64 static CRITICAL_SECTION msi_custom_action_cs;
65 static CRITICAL_SECTION_DEBUG msi_custom_action_cs_debug =
66 {
67     0, 0, &msi_custom_action_cs,
68     { &msi_custom_action_cs_debug.ProcessLocksList,
69       &msi_custom_action_cs_debug.ProcessLocksList },
70       0, 0, { (DWORD_PTR)(__FILE__ ": msi_custom_action_cs") }
71 };
72 static CRITICAL_SECTION msi_custom_action_cs = { &msi_custom_action_cs_debug, -1, 0, 0, 0, 0 };
73
74 static struct list msi_pending_custom_actions = LIST_INIT( msi_pending_custom_actions );
75
76 static BOOL check_execution_scheduling_options(MSIPACKAGE *package, LPCWSTR action, UINT options)
77 {
78     if (!package->script)
79         return TRUE;
80
81     if ((options & msidbCustomActionTypeClientRepeat) ==
82             msidbCustomActionTypeClientRepeat)
83     {
84         if (!(package->script->InWhatSequence & SEQUENCE_UI &&
85             package->script->InWhatSequence & SEQUENCE_EXEC))
86         {
87             TRACE("Skipping action due to dbCustomActionTypeClientRepeat option.\n");
88             return FALSE;
89         }
90     }
91     else if (options & msidbCustomActionTypeFirstSequence)
92     {
93         if (package->script->InWhatSequence & SEQUENCE_UI &&
94             package->script->InWhatSequence & SEQUENCE_EXEC )
95         {
96             TRACE("Skipping action due to msidbCustomActionTypeFirstSequence option.\n");
97             return FALSE;
98         }
99     }
100     else if (options & msidbCustomActionTypeOncePerProcess)
101     {
102         if (check_unique_action(package,action))
103         {
104             TRACE("Skipping action due to msidbCustomActionTypeOncePerProcess option.\n");
105             return FALSE;
106         }
107         else
108             register_unique_action(package,action);
109     }
110
111     return TRUE;
112 }
113
114 /* stores the CustomActionData before the action:
115  *     [CustomActionData]Action
116  */
117 static LPWSTR msi_get_deferred_action(LPCWSTR action, LPWSTR actiondata)
118 {
119     LPWSTR deferred;
120     DWORD len;
121
122     static const WCHAR begin[] = {'[',0};
123     static const WCHAR end[] = {']',0};
124
125     if (!actiondata)
126         return strdupW(action);
127
128     len = lstrlenW(action) + lstrlenW(actiondata) + 3;
129     deferred = msi_alloc(len * sizeof(WCHAR));
130
131     lstrcpyW(deferred, begin);
132     lstrcatW(deferred, actiondata);
133     lstrcatW(deferred, end);
134     lstrcatW(deferred, action);
135
136     return deferred;
137 }
138
139 UINT ACTION_CustomAction(MSIPACKAGE *package,LPCWSTR action, BOOL execute)
140 {
141     UINT rc = ERROR_SUCCESS;
142     MSIRECORD * row = 0;
143     static const WCHAR ExecSeqQuery[] =
144     {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
145      '`','C','u','s','t','o' ,'m','A','c','t','i','o','n','`',
146      ' ','W','H','E','R','E',' ','`','A','c','t','i' ,'o','n','`',' ',
147      '=',' ','\'','%','s','\'',0};
148     UINT type;
149     LPCWSTR source, target;
150     LPWSTR ptr, deferred_data = NULL;
151     LPWSTR action_copy = strdupW(action);
152     WCHAR *deformated=NULL;
153
154     /* deferred action: [CustomActionData]Action */
155     if ((ptr = strchrW(action_copy, ']')))
156     {
157         deferred_data = action_copy + 1;
158         *ptr = '\0';
159         action = ptr + 1;
160     }
161
162     row = MSI_QueryGetRecord( package->db, ExecSeqQuery, action );
163     if (!row)
164     {
165         msi_free(action_copy);
166         return ERROR_CALL_NOT_IMPLEMENTED;
167     }
168
169     type = MSI_RecordGetInteger(row,2);
170
171     source = MSI_RecordGetString(row,3);
172     target = MSI_RecordGetString(row,4);
173
174     TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type,
175           debugstr_w(source), debugstr_w(target));
176
177     /* handle some of the deferred actions */
178     if (type & msidbCustomActionTypeTSAware)
179         FIXME("msidbCustomActionTypeTSAware not handled\n");
180
181     if (type & msidbCustomActionTypeInScript)
182     {
183         if (type & msidbCustomActionTypeNoImpersonate)
184             FIXME("msidbCustomActionTypeNoImpersonate not handled\n");
185
186         if (type & msidbCustomActionTypeRollback)
187         {
188             FIXME("Rollback only action... rollbacks not supported yet\n");
189             schedule_action(package, ROLLBACK_SCRIPT, action);
190             rc = ERROR_SUCCESS;
191             goto end;
192         }
193         if (!execute)
194         {
195             LPWSTR actiondata = msi_dup_property(package, action);
196             LPWSTR deferred = msi_get_deferred_action(action, actiondata);
197
198             if (type & msidbCustomActionTypeCommit)
199             {
200                 TRACE("Deferring Commit Action!\n");
201                 schedule_action(package, COMMIT_SCRIPT, deferred);
202             }
203             else
204             {
205                 TRACE("Deferring Action!\n");
206                 schedule_action(package, INSTALL_SCRIPT, deferred);
207             }
208
209             rc = ERROR_SUCCESS;
210             msi_free(deferred);
211             goto end;
212         }
213         else
214         {
215             /*Set ActionData*/
216
217             static const WCHAR szActionData[] = {
218             'C','u','s','t','o','m','A','c','t','i','o','n','D','a','t','a',0};
219             static const WCHAR szBlank[] = {0};
220             LPWSTR actiondata = msi_dup_property( package, action );
221             if (deferred_data)
222                 MSI_SetPropertyW(package,szActionData,deferred_data);
223             else if (actiondata)
224                 MSI_SetPropertyW(package,szActionData,actiondata);
225             else
226                 MSI_SetPropertyW(package,szActionData,szBlank);
227             msi_free(actiondata);
228         }
229     }
230     else if (!check_execution_scheduling_options(package,action,type))
231     {
232         rc = ERROR_SUCCESS;
233         goto end;
234     }
235
236     switch (type & CUSTOM_ACTION_TYPE_MASK)
237     {
238         case 1: /* DLL file stored in a Binary table stream */
239             rc = HANDLE_CustomType1(package,source,target,type,action);
240             break;
241         case 2: /* EXE file stored in a Binary table stream */
242             rc = HANDLE_CustomType2(package,source,target,type,action);
243             break;
244         case 18: /*EXE file installed with package */
245             rc = HANDLE_CustomType18(package,source,target,type,action);
246             break;
247         case 19: /* Error that halts install */
248             rc = HANDLE_CustomType19(package,source,target,type,action);
249             break;
250         case 17:
251             rc = HANDLE_CustomType17(package,source,target,type,action);
252             break;
253         case 50: /*EXE file specified by a property value */
254             rc = HANDLE_CustomType50(package,source,target,type,action);
255             break;
256         case 34: /*EXE to be run in specified directory */
257             rc = HANDLE_CustomType34(package,source,target,type,action);
258             break;
259         case 35: /* Directory set with formatted text. */
260             deformat_string(package,target,&deformated);
261             MSI_SetTargetPathW(package, source, deformated);
262             msi_free(deformated);
263             break;
264         case 51: /* Property set with formatted text. */
265             deformat_string(package,target,&deformated);
266             rc = MSI_SetPropertyW(package,source,deformated);
267             msi_free(deformated);
268             break;
269         default:
270             FIXME("UNHANDLED ACTION TYPE %i (%s %s)\n",
271              type & CUSTOM_ACTION_TYPE_MASK, debugstr_w(source),
272              debugstr_w(target));
273     }
274
275 end:
276     msi_free(action_copy);
277     msiobj_release(&row->hdr);
278     return rc;
279 }
280
281
282 static UINT store_binary_to_temp(MSIPACKAGE *package, LPCWSTR source,
283                                 LPWSTR tmp_file)
284 {
285     static const WCHAR query[] = {
286         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
287         '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',' ',
288         '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
289     MSIRECORD *row = 0;
290     HANDLE file;
291     CHAR buffer[1024];
292     static const WCHAR f1[] = {'m','s','i',0};
293     WCHAR fmt[MAX_PATH];
294     DWORD sz = MAX_PATH;
295     UINT r;
296
297     if (MSI_GetPropertyW(package, cszTempFolder, fmt, &sz) != ERROR_SUCCESS)
298         GetTempPathW(MAX_PATH, fmt);
299
300     if (GetTempFileNameW(fmt, f1, 0, tmp_file) == 0)
301     {
302         TRACE("Unable to create file\n");
303         return ERROR_FUNCTION_FAILED;
304     }
305     track_tempfile(package, tmp_file);
306
307     row = MSI_QueryGetRecord(package->db, query, source);
308     if (!row)
309         return ERROR_FUNCTION_FAILED;
310
311     /* write out the file */
312     file = CreateFileW(tmp_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
313                        FILE_ATTRIBUTE_NORMAL, NULL);
314     if (file == INVALID_HANDLE_VALUE)
315         r = ERROR_FUNCTION_FAILED;
316     else
317     {
318         do
319         {
320             DWORD write;
321             sz = sizeof buffer;
322             r = MSI_RecordReadStream(row, 2, buffer, &sz);
323             if (r != ERROR_SUCCESS)
324             {
325                 ERR("Failed to get stream\n");
326                 break;
327             }
328             WriteFile(file, buffer, sz, &write, NULL);
329         } while (sz == sizeof buffer);
330         CloseHandle(file);
331     }
332
333     msiobj_release(&row->hdr);
334
335     return r;
336 }
337
338 static void file_running_action(MSIPACKAGE* package, HANDLE Handle,
339                                 BOOL process, LPCWSTR name)
340 {
341     MSIRUNNINGACTION *action;
342
343     action = msi_alloc( sizeof(MSIRUNNINGACTION) );
344
345     action->handle = Handle;
346     action->process = process;
347     action->name = strdupW(name);
348
349     list_add_tail( &package->RunningActions, &action->entry );
350 }
351
352 static UINT custom_get_process_return( HANDLE process )
353 {
354     DWORD rc = 0;
355
356     GetExitCodeProcess( process, &rc );
357     if (rc != 0)
358         return ERROR_FUNCTION_FAILED;
359     return ERROR_SUCCESS;
360 }
361
362 static UINT custom_get_thread_return( HANDLE thread )
363 {
364     DWORD rc = 0;
365
366     GetExitCodeThread( thread, &rc );
367
368     switch (rc)
369     {
370     case ERROR_FUNCTION_NOT_CALLED:
371     case ERROR_SUCCESS:
372     case ERROR_INSTALL_USEREXIT:
373     case ERROR_INSTALL_FAILURE:
374         return rc;
375     case ERROR_NO_MORE_ITEMS:
376         return ERROR_SUCCESS;
377     default:
378         ERR("Invalid Return Code %d\n",rc);
379         return ERROR_INSTALL_FAILURE;
380     }
381 }
382
383 static UINT wait_process_handle(MSIPACKAGE* package, UINT type,
384                            HANDLE ProcessHandle, LPCWSTR name)
385 {
386     UINT rc = ERROR_SUCCESS;
387
388     if (!(type & msidbCustomActionTypeAsync))
389     {
390         TRACE("waiting for %s\n", debugstr_w(name));
391
392         msi_dialog_check_messages(ProcessHandle);
393
394         if (!(type & msidbCustomActionTypeContinue))
395             rc = custom_get_process_return(ProcessHandle);
396
397         CloseHandle(ProcessHandle);
398     }
399     else
400     {
401         TRACE("%s running in background\n", debugstr_w(name));
402
403         if (!(type & msidbCustomActionTypeContinue))
404             file_running_action(package, ProcessHandle, TRUE, name);
405         else
406             CloseHandle(ProcessHandle);
407     }
408
409     return rc;
410 }
411
412 typedef struct _msi_custom_action_info {
413     struct list entry;
414     MSIPACKAGE *package;
415     LPWSTR dllname;
416     LPWSTR function;
417     HANDLE handle;
418     LPWSTR action;
419     INT type;
420     GUID guid;
421 } msi_custom_action_info;
422
423 static void free_custom_action_data( msi_custom_action_info *info )
424 {
425     EnterCriticalSection( &msi_custom_action_cs );
426     list_remove( &info->entry );
427     LeaveCriticalSection( &msi_custom_action_cs );
428     if (info->handle)
429         CloseHandle( info->handle );
430     msi_free( info->action );
431     msi_free( info->dllname );
432     msi_free( info->function );
433     msiobj_release( &info->package->hdr );
434     msi_free( info );
435 }
436
437 static UINT wait_thread_handle( msi_custom_action_info *info )
438 {
439     UINT rc = ERROR_SUCCESS;
440
441     if (!(info->type & msidbCustomActionTypeAsync))
442     {
443         TRACE("waiting for %s\n", debugstr_w( info->action ));
444
445         msi_dialog_check_messages( info->handle );
446
447         if (!(info->type & msidbCustomActionTypeContinue))
448             rc = custom_get_thread_return( info->handle );
449
450         free_custom_action_data( info );
451     }
452     else
453     {
454         TRACE("%s running in background\n", debugstr_w( info->action ));
455     }
456
457     return rc;
458 }
459
460 static msi_custom_action_info *find_action_by_guid( const LPGUID guid )
461 {
462     msi_custom_action_info *info;
463     BOOL found = FALSE;
464
465     EnterCriticalSection( &msi_custom_action_cs );
466
467     LIST_FOR_EACH_ENTRY( info, &msi_pending_custom_actions, msi_custom_action_info, entry )
468     {
469         if (IsEqualGUID( &info->guid, guid ))
470         {
471             found = TRUE;
472             break;
473         }
474     }
475
476     LeaveCriticalSection( &msi_custom_action_cs );
477
478     if (!found)
479         return NULL;
480
481     return info;
482 }
483
484 static DWORD WINAPI ACTION_CallDllFunction( const LPGUID guid )
485 {
486     msi_custom_action_info *info;
487     MsiCustomActionEntryPoint fn;
488     MSIHANDLE hPackage;
489     HANDLE hModule;
490     LPSTR proc;
491     UINT r = ERROR_FUNCTION_FAILED;
492
493     info = find_action_by_guid( guid );
494     if (!info)
495     {
496         ERR("failed to find action %s\n", debugstr_guid( guid) );
497         return r;
498     }
499
500     TRACE("%s %s\n", debugstr_w( info->dllname ), debugstr_w( info->function ) );
501
502     hModule = LoadLibraryW( info->dllname );
503     if (!hModule)
504     {
505         ERR("failed to load dll %s\n", debugstr_w( info->dllname ) );
506         return r;
507     }
508
509     proc = strdupWtoA( info->function );
510     fn = (MsiCustomActionEntryPoint) GetProcAddress( hModule, proc );
511     msi_free( proc );
512     if (fn)
513     {
514         hPackage = alloc_msihandle( &info->package->hdr );
515         if (hPackage)
516         {
517             TRACE("calling %s\n", debugstr_w( info->function ) );
518             r = fn( hPackage );
519             MsiCloseHandle( hPackage );
520         }
521         else
522             ERR("failed to create handle for %p\n", info->package );
523     }
524     else
525         ERR("GetProcAddress(%s) failed\n", debugstr_w( info->function ) );
526
527     FreeLibrary(hModule);
528
529     if (info->type & msidbCustomActionTypeAsync &&
530         info->type & msidbCustomActionTypeContinue)
531         free_custom_action_data( info );
532
533     return r;
534 }
535
536 static DWORD WINAPI DllThread( LPVOID arg )
537 {
538     LPGUID guid = arg;
539     DWORD rc = 0;
540
541     TRACE("custom action (%x) started\n", GetCurrentThreadId() );
542
543     rc = ACTION_CallDllFunction( guid );
544
545     TRACE("custom action (%x) returned %i\n", GetCurrentThreadId(), rc );
546
547     MsiCloseAllHandles();
548     return rc;
549 }
550
551 static msi_custom_action_info *do_msidbCustomActionTypeDll(
552     MSIPACKAGE *package, INT type, LPCWSTR dllname, LPCWSTR function, LPCWSTR action )
553 {
554     msi_custom_action_info *info;
555
556     info = msi_alloc( sizeof *info );
557     if (!info)
558         return NULL;
559
560     msiobj_addref( &package->hdr );
561     info->package = package;
562     info->type = type;
563     info->function = strdupW( function );
564     info->dllname = strdupW( dllname );
565     info->action = strdupW( action );
566     CoCreateGuid( &info->guid );
567
568     EnterCriticalSection( &msi_custom_action_cs );
569     list_add_tail( &msi_pending_custom_actions, &info->entry );
570     LeaveCriticalSection( &msi_custom_action_cs );
571
572     info->handle = CreateThread( NULL, 0, DllThread, &info->guid, 0, NULL );
573     if (!info->handle)
574     {
575         free_custom_action_data( info );
576         return NULL;
577     }
578
579     return info;
580 }
581
582 static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source,
583                                LPCWSTR target, const INT type, LPCWSTR action)
584 {
585     msi_custom_action_info *info;
586     WCHAR tmp_file[MAX_PATH];
587     UINT r;
588
589     r = store_binary_to_temp(package, source, tmp_file);
590     if (r != ERROR_SUCCESS)
591         return r;
592
593     TRACE("Calling function %s from %s\n",debugstr_w(target),
594           debugstr_w(tmp_file));
595
596     if (!strchrW(tmp_file,'.'))
597     {
598         static const WCHAR dot[]={'.',0};
599         strcatW(tmp_file,dot);
600     }
601
602     info = do_msidbCustomActionTypeDll( package, type, tmp_file, target, action );
603
604     return wait_thread_handle( info );
605 }
606
607 static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source,
608                                LPCWSTR target, const INT type, LPCWSTR action)
609 {
610     WCHAR tmp_file[MAX_PATH];
611     STARTUPINFOW si;
612     PROCESS_INFORMATION info;
613     BOOL rc;
614     INT len;
615     WCHAR *deformated = NULL;
616     WCHAR *cmd;
617     static const WCHAR spc[] = {' ',0};
618     UINT r;
619
620     memset(&si,0,sizeof(STARTUPINFOW));
621
622     r = store_binary_to_temp(package, source, tmp_file);
623     if (r != ERROR_SUCCESS)
624         return r;
625
626     deformat_string(package,target,&deformated);
627
628     len = strlenW(tmp_file)+2;
629
630     if (deformated)
631         len += strlenW(deformated);
632
633     cmd = msi_alloc(sizeof(WCHAR)*len);
634
635     strcpyW(cmd,tmp_file);
636     if (deformated)
637     {
638         strcatW(cmd,spc);
639         strcatW(cmd,deformated);
640
641         msi_free(deformated);
642     }
643
644     TRACE("executing exe %s\n", debugstr_w(cmd));
645
646     rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
647                   c_collen, &si, &info);
648     msi_free(cmd);
649
650     if ( !rc )
651     {
652         ERR("Unable to execute command %s\n", debugstr_w(cmd));
653         return ERROR_SUCCESS;
654     }
655     CloseHandle( info.hThread );
656
657     r = wait_process_handle(package, type, info.hProcess, action);
658
659     return r;
660 }
661
662 static UINT HANDLE_CustomType17(MSIPACKAGE *package, LPCWSTR source,
663                                 LPCWSTR target, const INT type, LPCWSTR action)
664 {
665     msi_custom_action_info *info;
666     MSIFILE *file;
667
668     TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
669
670     file = get_loaded_file( package, source );
671     if (!file)
672     {
673         ERR("invalid file key %s\n", debugstr_w( source ));
674         return ERROR_FUNCTION_FAILED;
675     }
676
677     info = do_msidbCustomActionTypeDll( package, type, file->TargetPath, target, action );
678
679     return wait_thread_handle( info );
680 }
681
682 static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source,
683                                 LPCWSTR target, const INT type, LPCWSTR action)
684 {
685     STARTUPINFOW si;
686     PROCESS_INFORMATION info;
687     BOOL rc;
688     WCHAR *deformated;
689     WCHAR *cmd;
690     INT len;
691     static const WCHAR spc[] = {' ',0};
692     MSIFILE *file;
693
694     memset(&si,0,sizeof(STARTUPINFOW));
695
696     file = get_loaded_file(package,source);
697     if( !file )
698         return ERROR_FUNCTION_FAILED;
699
700     len = lstrlenW( file->TargetPath );
701
702     deformat_string(package,target,&deformated);
703     if (deformated)
704         len += strlenW(deformated);
705     len += 2;
706
707     cmd = msi_alloc(len * sizeof(WCHAR));
708
709     lstrcpyW( cmd, file->TargetPath);
710     if (deformated)
711     {
712         strcatW(cmd, spc);
713         strcatW(cmd, deformated);
714
715         msi_free(deformated);
716     }
717
718     TRACE("executing exe %s\n", debugstr_w(cmd));
719
720     rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
721                   c_collen, &si, &info);
722
723     if ( !rc )
724     {
725         ERR("Unable to execute command %s\n", debugstr_w(cmd));
726         msi_free(cmd);
727         return ERROR_SUCCESS;
728     }
729     msi_free(cmd);
730     CloseHandle( info.hThread );
731
732     return wait_process_handle(package, type, info.hProcess, action);
733 }
734
735 static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
736                                 LPCWSTR target, const INT type, LPCWSTR action)
737 {
738     static const WCHAR query[] = {
739       'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
740       'F','R','O','M',' ','`','E','r','r','o','r','`',' ',
741       'W','H','E','R','E',' ','`','E','r','r','o','r','`',' ','=',' ',
742       '%','s',0
743     };
744     MSIRECORD *row = 0;
745     LPWSTR deformated = NULL;
746
747     deformat_string( package, target, &deformated );
748
749     /* first try treat the error as a number */
750     row = MSI_QueryGetRecord( package->db, query, deformated );
751     if( row )
752     {
753         LPCWSTR error = MSI_RecordGetString( row, 1 );
754         MessageBoxW( NULL, error, NULL, MB_OK );
755         msiobj_release( &row->hdr );
756     }
757     else
758         MessageBoxW( NULL, deformated, NULL, MB_OK );
759
760     msi_free( deformated );
761
762     return ERROR_FUNCTION_FAILED;
763 }
764
765 static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source,
766                                 LPCWSTR target, const INT type, LPCWSTR action)
767 {
768     STARTUPINFOW si;
769     PROCESS_INFORMATION info;
770     WCHAR *prop;
771     BOOL rc;
772     WCHAR *deformated;
773     WCHAR *cmd;
774     INT len;
775     static const WCHAR spc[] = {' ',0};
776
777     memset(&si,0,sizeof(STARTUPINFOW));
778     memset(&info,0,sizeof(PROCESS_INFORMATION));
779
780     prop = msi_dup_property( package, source );
781     if (!prop)
782         return ERROR_SUCCESS;
783
784     deformat_string(package,target,&deformated);
785     len = strlenW(prop) + 2;
786     if (deformated)
787          len += strlenW(deformated);
788
789     cmd = msi_alloc(sizeof(WCHAR)*len);
790
791     strcpyW(cmd,prop);
792     if (deformated)
793     {
794         strcatW(cmd,spc);
795         strcatW(cmd,deformated);
796
797         msi_free(deformated);
798     }
799     msi_free(prop);
800
801     TRACE("executing exe %s\n", debugstr_w(cmd));
802
803     rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
804                   c_collen, &si, &info);
805
806     if ( !rc )
807     {
808         ERR("Unable to execute command %s\n", debugstr_w(cmd));
809         msi_free(cmd);
810         return ERROR_SUCCESS;
811     }
812     msi_free(cmd);
813
814     CloseHandle( info.hThread );
815
816     return wait_process_handle(package, type, info.hProcess, action);
817 }
818
819 static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
820                                 LPCWSTR target, const INT type, LPCWSTR action)
821 {
822     LPWSTR filename, deformated;
823     STARTUPINFOW si;
824     PROCESS_INFORMATION info;
825     BOOL rc;
826
827     memset(&si,0,sizeof(STARTUPINFOW));
828
829     filename = resolve_folder(package, source, FALSE, FALSE, NULL);
830
831     if (!filename)
832         return ERROR_FUNCTION_FAILED;
833
834     SetCurrentDirectoryW(filename);
835     msi_free(filename);
836
837     deformat_string(package,target,&deformated);
838
839     if (!deformated)
840         return ERROR_FUNCTION_FAILED;
841
842     TRACE("executing exe %s\n", debugstr_w(deformated));
843
844     rc = CreateProcessW(NULL, deformated, NULL, NULL, FALSE, 0, NULL,
845                   c_collen, &si, &info);
846
847     if ( !rc )
848     {
849         ERR("Unable to execute command %s\n", debugstr_w(deformated));
850         msi_free(deformated);
851         return ERROR_SUCCESS;
852     }
853     msi_free(deformated);
854     CloseHandle( info.hThread );
855
856     return wait_process_handle(package, type, info.hProcess, action);
857 }
858
859 void ACTION_FinishCustomActions(MSIPACKAGE* package)
860 {
861     struct list *item;
862     HANDLE *wait_handles;
863     unsigned int handle_count, i;
864     msi_custom_action_info *info, *cursor;
865
866     while ((item = list_head( &package->RunningActions )))
867     {
868         MSIRUNNINGACTION *action = LIST_ENTRY( item, MSIRUNNINGACTION, entry );
869
870         list_remove( &action->entry );
871
872         TRACE("waiting for %s\n", debugstr_w( action->name ) );
873         msi_dialog_check_messages( action->handle );
874
875         CloseHandle( action->handle );
876         msi_free( action->name );
877         msi_free( action );
878     }
879
880     EnterCriticalSection( &msi_custom_action_cs );
881
882     handle_count = list_count( &msi_pending_custom_actions );
883     wait_handles = HeapAlloc( GetProcessHeap(), 0, handle_count * sizeof(HANDLE) );
884
885     handle_count = 0;
886     LIST_FOR_EACH_ENTRY_SAFE( info, cursor, &msi_pending_custom_actions, msi_custom_action_info, entry )
887     {
888         if (info->package == package )
889         {
890             wait_handles[handle_count++] = info->handle;
891             free_custom_action_data( info );
892         }
893     }
894
895     LeaveCriticalSection( &msi_custom_action_cs );
896
897     for (i = 0; i < handle_count; i++)
898         msi_dialog_check_messages( wait_handles[i] );
899
900     HeapFree( GetProcessHeap(), 0, wait_handles );
901 }