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