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