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