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