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