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