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