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