ddraw: Protect the ddraw list.
[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 static 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 %d\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 (%x) started for custom action\n", GetCurrentThreadId());
507
508     stuff = (thread_struct*)info;
509     rc = ACTION_CallDllFunction(stuff);
510
511     TRACE("MSI Thread (%x) finished (rc %i)\n",GetCurrentThreadId(), rc);
512     /* clse all handles for this thread */
513     MsiCloseAllHandles();
514     return rc;
515 }
516
517 static HANDLE do_msidbCustomActionTypeDll(MSIPACKAGE *package, LPCWSTR dll, LPCWSTR target)
518 {
519     thread_struct *info;
520
521     info = msi_alloc( sizeof(*info) );
522     msiobj_addref( &package->hdr );
523     info->package = package;
524     info->target = strdupW(target);
525     info->source = strdupW(dll);
526
527     return CreateThread(NULL, 0, DllThread, info, 0, NULL);
528 }
529
530 static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source, 
531                                LPCWSTR target, const INT type, LPCWSTR action)
532 {
533     WCHAR tmp_file[MAX_PATH];
534     UINT rc = ERROR_SUCCESS;
535     BOOL finished = FALSE;
536     HANDLE ThreadHandle;
537
538     store_binary_to_temp(package, source, tmp_file);
539
540     TRACE("Calling function %s from %s\n",debugstr_w(target),
541           debugstr_w(tmp_file));
542
543     if (!strchrW(tmp_file,'.'))
544     {
545         static const WCHAR dot[]={'.',0};
546         strcatW(tmp_file,dot);
547     } 
548
549     ThreadHandle = do_msidbCustomActionTypeDll( package, tmp_file, target );
550
551     rc = process_handle(package, type, ThreadHandle, NULL, action, &finished );
552
553     if (!finished)
554         track_tempfile(package, tmp_file, tmp_file);
555     else
556         DeleteFileW(tmp_file);
557
558     return rc;
559 }
560
561 static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source, 
562                                LPCWSTR target, const INT type, LPCWSTR action)
563 {
564     WCHAR tmp_file[MAX_PATH];
565     STARTUPINFOW si;
566     PROCESS_INFORMATION info;
567     BOOL rc;
568     INT len;
569     WCHAR *deformated;
570     WCHAR *cmd;
571     static const WCHAR spc[] = {' ',0};
572     UINT prc = ERROR_SUCCESS;
573     BOOL finished = FALSE;
574
575     memset(&si,0,sizeof(STARTUPINFOW));
576
577     store_binary_to_temp(package, source, tmp_file);
578
579     deformat_string(package,target,&deformated);
580
581     len = strlenW(tmp_file)+2;
582
583     if (deformated)
584         len += strlenW(deformated);
585    
586     cmd = msi_alloc(sizeof(WCHAR)*len);
587
588     strcpyW(cmd,tmp_file);
589     if (deformated)
590     {
591         strcatW(cmd,spc);
592         strcatW(cmd,deformated);
593
594         msi_free(deformated);
595     }
596
597     TRACE("executing exe %s\n", debugstr_w(cmd));
598
599     rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
600                   c_collen, &si, &info);
601
602
603     if ( !rc )
604     {
605         ERR("Unable to execute command %s\n", debugstr_w(cmd));
606         msi_free(cmd);
607         return ERROR_SUCCESS;
608     }
609     msi_free(cmd);
610
611     prc = process_handle(package, type, info.hThread, info.hProcess, action, 
612                           &finished);
613
614     if (!finished)
615         track_tempfile(package, tmp_file, tmp_file);
616     else
617         DeleteFileW(tmp_file);
618     
619     return prc;
620 }
621
622 static UINT HANDLE_CustomType17(MSIPACKAGE *package, LPCWSTR source,
623                                 LPCWSTR target, const INT type, LPCWSTR action)
624 {
625     HANDLE hThread;
626     MSIFILE *file;
627
628     TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
629
630     file = get_loaded_file( package, source );
631     if (!file)
632     {
633         ERR("invalid file key %s\n", debugstr_w( source ));
634         return ERROR_FUNCTION_FAILED;
635     }
636
637     hThread = do_msidbCustomActionTypeDll( package, file->TargetPath, target );
638
639     return process_handle(package, type, hThread, NULL, action, NULL );
640 }
641
642 static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source,
643                                 LPCWSTR target, const INT type, LPCWSTR action)
644 {
645     STARTUPINFOW si;
646     PROCESS_INFORMATION info;
647     BOOL rc;
648     WCHAR *deformated;
649     WCHAR *cmd;
650     INT len;
651     static const WCHAR spc[] = {' ',0};
652     MSIFILE *file;
653     UINT prc;
654
655     memset(&si,0,sizeof(STARTUPINFOW));
656
657     file = get_loaded_file(package,source);
658     if( !file )
659         return ERROR_FUNCTION_FAILED;
660
661     len = lstrlenW( file->TargetPath );
662
663     deformat_string(package,target,&deformated);
664     if (deformated)
665         len += strlenW(deformated);
666     len += 2;
667
668     cmd = msi_alloc(len * sizeof(WCHAR));
669
670     lstrcpyW( cmd, file->TargetPath);
671     if (deformated)
672     {
673         strcatW(cmd, spc);
674         strcatW(cmd, deformated);
675
676         msi_free(deformated);
677     }
678
679     TRACE("executing exe %s\n", debugstr_w(cmd));
680
681     rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
682                   c_collen, &si, &info);
683
684     
685     if ( !rc )
686     {
687         ERR("Unable to execute command %s\n", debugstr_w(cmd));
688         msi_free(cmd);
689         return ERROR_SUCCESS;
690     }
691     msi_free(cmd);
692
693     prc = process_handle(package, type, info.hThread, info.hProcess, action, 
694                          NULL);
695
696     return prc;
697 }
698
699 static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
700                                 LPCWSTR target, const INT type, LPCWSTR action)
701 {
702     static const WCHAR query[] = {
703       'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
704       'F','R','O','M',' ','`','E','r','r','o','r','`',' ',
705       'W','H','E','R','E',' ','`','E','r','r','o','r','`',' ','=',' ',
706       '%','s',0
707     };
708     MSIRECORD *row = 0;
709     LPWSTR deformated = NULL;
710
711     deformat_string( package, target, &deformated );
712
713     /* first try treat the error as a number */
714     row = MSI_QueryGetRecord( package->db, query, deformated );
715     if( row )
716     {
717         LPCWSTR error = MSI_RecordGetString( row, 1 );
718         MessageBoxW( NULL, error, NULL, MB_OK );
719         msiobj_release( &row->hdr );
720     }
721     else
722         MessageBoxW( NULL, deformated, NULL, MB_OK );
723
724     msi_free( deformated );
725
726     return ERROR_FUNCTION_FAILED;
727 }
728
729 static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source,
730                                 LPCWSTR target, const INT type, LPCWSTR action)
731 {
732     STARTUPINFOW si;
733     PROCESS_INFORMATION info;
734     WCHAR *prop;
735     BOOL rc;
736     WCHAR *deformated;
737     WCHAR *cmd;
738     INT len;
739     static const WCHAR spc[] = {' ',0};
740
741     memset(&si,0,sizeof(STARTUPINFOW));
742     memset(&info,0,sizeof(PROCESS_INFORMATION));
743
744     prop = msi_dup_property( package, source );
745     if (!prop)
746         return ERROR_SUCCESS;
747
748     deformat_string(package,target,&deformated);
749     len = strlenW(prop) + 2;
750     if (deformated)
751          len += strlenW(deformated);
752
753     cmd = msi_alloc(sizeof(WCHAR)*len);
754
755     strcpyW(cmd,prop);
756     if (deformated)
757     {
758         strcatW(cmd,spc);
759         strcatW(cmd,deformated);
760
761         msi_free(deformated);
762     }
763     msi_free(prop);
764
765     TRACE("executing exe %s\n", debugstr_w(cmd));
766
767     rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
768                   c_collen, &si, &info);
769
770     
771     if ( !rc )
772     {
773         ERR("Unable to execute command %s\n", debugstr_w(cmd));
774         msi_free(cmd);
775         return ERROR_SUCCESS;
776     }
777     msi_free(cmd);
778
779     return process_handle(package, type, info.hThread, info.hProcess, action, NULL);
780 }
781
782 static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
783                                 LPCWSTR target, const INT type, LPCWSTR action)
784 {
785     LPWSTR filename, deformated;
786     STARTUPINFOW si;
787     PROCESS_INFORMATION info;
788     BOOL rc;
789     UINT prc;
790
791     memset(&si,0,sizeof(STARTUPINFOW));
792
793     filename = resolve_folder(package, source, FALSE, FALSE, NULL);
794
795     if (!filename)
796         return ERROR_FUNCTION_FAILED;
797
798     SetCurrentDirectoryW(filename);
799     msi_free(filename);
800
801     deformat_string(package,target,&deformated);
802
803     if (!deformated)
804         return ERROR_FUNCTION_FAILED;
805
806     TRACE("executing exe %s\n", debugstr_w(deformated));
807
808     rc = CreateProcessW(NULL, deformated, NULL, NULL, FALSE, 0, NULL,
809                   c_collen, &si, &info);
810
811     if ( !rc )
812     {
813         ERR("Unable to execute command %s\n", debugstr_w(deformated));
814         msi_free(deformated);
815         return ERROR_SUCCESS;
816     }
817     msi_free(deformated);
818
819     prc = process_handle(package, type, info.hThread, info.hProcess, action,
820                          NULL);
821
822     return prc;
823 }
824
825
826 void ACTION_FinishCustomActions(MSIPACKAGE* package)
827 {
828     struct list *item, *cursor;
829     DWORD rc;
830
831     LIST_FOR_EACH_SAFE( item, cursor, &package->RunningActions )
832     {
833         MSIRUNNINGACTION *action = LIST_ENTRY( item, MSIRUNNINGACTION, entry );
834
835         TRACE("Checking on action %s\n", debugstr_w(action->name));
836
837         list_remove( &action->entry );
838
839         if (action->process)
840             GetExitCodeProcess( action->handle, &rc );
841         else
842             GetExitCodeThread( action->handle, &rc );
843
844         if (rc == STILL_ACTIVE)
845         {
846             TRACE("Waiting on action %s\n", debugstr_w( action->name) );
847             msi_dialog_check_messages( action->handle );
848         }
849
850         CloseHandle( action->handle );
851         msi_free( action->name );
852         msi_free( action );
853     }
854 }