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