msi: Add handling for the concurrent install custom action.
[wine] / dlls / msi / custom.c
1 /*
2  * Custom Action processing for the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2005 Aric Stewart for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #define COBJMACROS
22
23 #include <stdarg.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winerror.h"
27 #include "msidefs.h"
28 #include "msipriv.h"
29 #include "winuser.h"
30 #include "wine/debug.h"
31 #include "wine/unicode.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(msi);
34
35 #define CUSTOM_ACTION_TYPE_MASK 0x3F
36 static const WCHAR c_collen[] = {'C',':','\\',0};
37 static const WCHAR cszTempFolder[]= {'T','e','m','p','F','o','l','d','e','r',0};
38
39 typedef struct tagMSIRUNNINGACTION
40 {
41     struct list entry;
42     HANDLE handle;
43     BOOL   process;
44     LPWSTR name;
45 } MSIRUNNINGACTION;
46
47 static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source,
48                                LPCWSTR target, const INT type, LPCWSTR action);
49 static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source,
50                                LPCWSTR target, const INT type, LPCWSTR action);
51 static UINT HANDLE_CustomType17(MSIPACKAGE *package, LPCWSTR source,
52                                 LPCWSTR target, const INT type, LPCWSTR action);
53 static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source,
54                                 LPCWSTR target, const INT type, LPCWSTR action);
55 static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
56                                 LPCWSTR target, const INT type, LPCWSTR action);
57 static UINT HANDLE_CustomType23(MSIPACKAGE *package, LPCWSTR source,
58                                 LPCWSTR target, const INT type, LPCWSTR action);
59 static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source,
60                                 LPCWSTR target, const INT type, LPCWSTR action);
61 static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
62                                 LPCWSTR target, const INT type, LPCWSTR action);
63
64 typedef UINT (WINAPI *MsiCustomActionEntryPoint)( MSIHANDLE );
65
66 static CRITICAL_SECTION msi_custom_action_cs;
67 static CRITICAL_SECTION_DEBUG msi_custom_action_cs_debug =
68 {
69     0, 0, &msi_custom_action_cs,
70     { &msi_custom_action_cs_debug.ProcessLocksList,
71       &msi_custom_action_cs_debug.ProcessLocksList },
72       0, 0, { (DWORD_PTR)(__FILE__ ": msi_custom_action_cs") }
73 };
74 static CRITICAL_SECTION msi_custom_action_cs = { &msi_custom_action_cs_debug, -1, 0, 0, 0, 0 };
75
76 static struct list msi_pending_custom_actions = LIST_INIT( msi_pending_custom_actions );
77
78 static BOOL check_execution_scheduling_options(MSIPACKAGE *package, LPCWSTR action, UINT options)
79 {
80     if (!package->script)
81         return TRUE;
82
83     if ((options & msidbCustomActionTypeClientRepeat) ==
84             msidbCustomActionTypeClientRepeat)
85     {
86         if (!(package->script->InWhatSequence & SEQUENCE_UI &&
87             package->script->InWhatSequence & SEQUENCE_EXEC))
88         {
89             TRACE("Skipping action due to dbCustomActionTypeClientRepeat option.\n");
90             return FALSE;
91         }
92     }
93     else if (options & msidbCustomActionTypeFirstSequence)
94     {
95         if (package->script->InWhatSequence & SEQUENCE_UI &&
96             package->script->InWhatSequence & SEQUENCE_EXEC )
97         {
98             TRACE("Skipping action due to msidbCustomActionTypeFirstSequence option.\n");
99             return FALSE;
100         }
101     }
102     else if (options & msidbCustomActionTypeOncePerProcess)
103     {
104         if (check_unique_action(package,action))
105         {
106             TRACE("Skipping action due to msidbCustomActionTypeOncePerProcess option.\n");
107             return FALSE;
108         }
109         else
110             register_unique_action(package,action);
111     }
112
113     return TRUE;
114 }
115
116 /* stores the CustomActionData before the action:
117  *     [CustomActionData]Action
118  */
119 static LPWSTR msi_get_deferred_action(LPCWSTR action, LPWSTR actiondata)
120 {
121     LPWSTR deferred;
122     DWORD len;
123
124     static const WCHAR begin[] = {'[',0};
125     static const WCHAR end[] = {']',0};
126
127     if (!actiondata)
128         return strdupW(action);
129
130     len = lstrlenW(action) + lstrlenW(actiondata) + 3;
131     deferred = msi_alloc(len * sizeof(WCHAR));
132
133     lstrcpyW(deferred, begin);
134     lstrcatW(deferred, actiondata);
135     lstrcatW(deferred, end);
136     lstrcatW(deferred, action);
137
138     return deferred;
139 }
140
141 UINT ACTION_CustomAction(MSIPACKAGE *package,LPCWSTR action, BOOL execute)
142 {
143     UINT rc = ERROR_SUCCESS;
144     MSIRECORD * row = 0;
145     static const WCHAR ExecSeqQuery[] =
146     {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
147      '`','C','u','s','t','o' ,'m','A','c','t','i','o','n','`',
148      ' ','W','H','E','R','E',' ','`','A','c','t','i' ,'o','n','`',' ',
149      '=',' ','\'','%','s','\'',0};
150     UINT type;
151     LPCWSTR source, target;
152     LPWSTR ptr, deferred_data = NULL;
153     LPWSTR action_copy = strdupW(action);
154     WCHAR *deformated=NULL;
155
156     /* deferred action: [CustomActionData]Action */
157     if ((ptr = strchrW(action_copy, ']')))
158     {
159         deferred_data = action_copy + 1;
160         *ptr = '\0';
161         action = ptr + 1;
162     }
163
164     row = MSI_QueryGetRecord( package->db, ExecSeqQuery, action );
165     if (!row)
166     {
167         msi_free(action_copy);
168         return ERROR_CALL_NOT_IMPLEMENTED;
169     }
170
171     type = MSI_RecordGetInteger(row,2);
172
173     source = MSI_RecordGetString(row,3);
174     target = MSI_RecordGetString(row,4);
175
176     TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type,
177           debugstr_w(source), debugstr_w(target));
178
179     /* handle some of the deferred actions */
180     if (type & msidbCustomActionTypeTSAware)
181         FIXME("msidbCustomActionTypeTSAware not handled\n");
182
183     if (type & msidbCustomActionTypeInScript)
184     {
185         if (type & msidbCustomActionTypeNoImpersonate)
186             FIXME("msidbCustomActionTypeNoImpersonate not handled\n");
187
188         if (type & msidbCustomActionTypeRollback)
189         {
190             FIXME("Rollback only action... rollbacks not supported yet\n");
191             schedule_action(package, ROLLBACK_SCRIPT, action);
192             rc = ERROR_SUCCESS;
193             goto end;
194         }
195         if (!execute)
196         {
197             LPWSTR actiondata = msi_dup_property(package, action);
198             LPWSTR deferred = msi_get_deferred_action(action, actiondata);
199
200             if (type & msidbCustomActionTypeCommit)
201             {
202                 TRACE("Deferring Commit Action!\n");
203                 schedule_action(package, COMMIT_SCRIPT, deferred);
204             }
205             else
206             {
207                 TRACE("Deferring Action!\n");
208                 schedule_action(package, INSTALL_SCRIPT, deferred);
209             }
210
211             rc = ERROR_SUCCESS;
212             msi_free(deferred);
213             goto end;
214         }
215         else
216         {
217             /*Set ActionData*/
218
219             static const WCHAR szActionData[] = {
220             'C','u','s','t','o','m','A','c','t','i','o','n','D','a','t','a',0};
221             static const WCHAR szBlank[] = {0};
222             LPWSTR actiondata = msi_dup_property( package, action );
223             if (deferred_data)
224                 MSI_SetPropertyW(package,szActionData,deferred_data);
225             else if (actiondata)
226                 MSI_SetPropertyW(package,szActionData,actiondata);
227             else
228                 MSI_SetPropertyW(package,szActionData,szBlank);
229             msi_free(actiondata);
230         }
231     }
232     else if (!check_execution_scheduling_options(package,action,type))
233     {
234         rc = ERROR_SUCCESS;
235         goto end;
236     }
237
238     switch (type & CUSTOM_ACTION_TYPE_MASK)
239     {
240         case 1: /* DLL file stored in a Binary table stream */
241             rc = HANDLE_CustomType1(package,source,target,type,action);
242             break;
243         case 2: /* EXE file stored in a Binary table stream */
244             rc = HANDLE_CustomType2(package,source,target,type,action);
245             break;
246         case 18: /*EXE file installed with package */
247             rc = HANDLE_CustomType18(package,source,target,type,action);
248             break;
249         case 19: /* Error that halts install */
250             rc = HANDLE_CustomType19(package,source,target,type,action);
251             break;
252         case 17:
253             rc = HANDLE_CustomType17(package,source,target,type,action);
254             break;
255         case 23: /* installs another package in the source tree */
256             deformat_string(package,target,&deformated);
257             rc = HANDLE_CustomType23(package,source,deformated,type,action);
258             break;
259         case 50: /*EXE file specified by a property value */
260             rc = HANDLE_CustomType50(package,source,target,type,action);
261             break;
262         case 34: /*EXE to be run in specified directory */
263             rc = HANDLE_CustomType34(package,source,target,type,action);
264             break;
265         case 35: /* Directory set with formatted text. */
266             deformat_string(package,target,&deformated);
267             MSI_SetTargetPathW(package, source, deformated);
268             msi_free(deformated);
269             break;
270         case 51: /* Property set with formatted text. */
271             deformat_string(package,target,&deformated);
272             rc = MSI_SetPropertyW(package,source,deformated);
273             msi_free(deformated);
274             break;
275         default:
276             FIXME("UNHANDLED ACTION TYPE %i (%s %s)\n",
277              type & CUSTOM_ACTION_TYPE_MASK, debugstr_w(source),
278              debugstr_w(target));
279     }
280
281 end:
282     msi_free(action_copy);
283     msiobj_release(&row->hdr);
284     return rc;
285 }
286
287
288 static UINT store_binary_to_temp(MSIPACKAGE *package, LPCWSTR source,
289                                 LPWSTR tmp_file)
290 {
291     static const WCHAR query[] = {
292         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
293         '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',' ',
294         '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
295     MSIRECORD *row = 0;
296     HANDLE file;
297     CHAR buffer[1024];
298     static const WCHAR f1[] = {'m','s','i',0};
299     WCHAR fmt[MAX_PATH];
300     DWORD sz = MAX_PATH;
301     UINT r;
302
303     if (MSI_GetPropertyW(package, cszTempFolder, fmt, &sz) != ERROR_SUCCESS)
304         GetTempPathW(MAX_PATH, fmt);
305
306     if (GetTempFileNameW(fmt, f1, 0, tmp_file) == 0)
307     {
308         TRACE("Unable to create file\n");
309         return ERROR_FUNCTION_FAILED;
310     }
311     track_tempfile(package, tmp_file);
312
313     row = MSI_QueryGetRecord(package->db, query, source);
314     if (!row)
315         return ERROR_FUNCTION_FAILED;
316
317     /* write out the file */
318     file = CreateFileW(tmp_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
319                        FILE_ATTRIBUTE_NORMAL, NULL);
320     if (file == INVALID_HANDLE_VALUE)
321         r = ERROR_FUNCTION_FAILED;
322     else
323     {
324         do
325         {
326             DWORD write;
327             sz = sizeof buffer;
328             r = MSI_RecordReadStream(row, 2, buffer, &sz);
329             if (r != ERROR_SUCCESS)
330             {
331                 ERR("Failed to get stream\n");
332                 break;
333             }
334             WriteFile(file, buffer, sz, &write, NULL);
335         } while (sz == sizeof buffer);
336         CloseHandle(file);
337     }
338
339     msiobj_release(&row->hdr);
340
341     return r;
342 }
343
344 static void file_running_action(MSIPACKAGE* package, HANDLE Handle,
345                                 BOOL process, LPCWSTR name)
346 {
347     MSIRUNNINGACTION *action;
348
349     action = msi_alloc( sizeof(MSIRUNNINGACTION) );
350
351     action->handle = Handle;
352     action->process = process;
353     action->name = strdupW(name);
354
355     list_add_tail( &package->RunningActions, &action->entry );
356 }
357
358 static UINT custom_get_process_return( HANDLE process )
359 {
360     DWORD rc = 0;
361
362     GetExitCodeProcess( process, &rc );
363     if (rc != 0)
364         return ERROR_FUNCTION_FAILED;
365     return ERROR_SUCCESS;
366 }
367
368 static UINT custom_get_thread_return( HANDLE thread )
369 {
370     DWORD rc = 0;
371
372     GetExitCodeThread( thread, &rc );
373
374     switch (rc)
375     {
376     case ERROR_FUNCTION_NOT_CALLED:
377     case ERROR_SUCCESS:
378     case ERROR_INSTALL_USEREXIT:
379     case ERROR_INSTALL_FAILURE:
380         return rc;
381     case ERROR_NO_MORE_ITEMS:
382         return ERROR_SUCCESS;
383     default:
384         ERR("Invalid Return Code %d\n",rc);
385         return ERROR_INSTALL_FAILURE;
386     }
387 }
388
389 static UINT wait_process_handle(MSIPACKAGE* package, UINT type,
390                            HANDLE ProcessHandle, LPCWSTR name)
391 {
392     UINT rc = ERROR_SUCCESS;
393
394     if (!(type & msidbCustomActionTypeAsync))
395     {
396         TRACE("waiting for %s\n", debugstr_w(name));
397
398         msi_dialog_check_messages(ProcessHandle);
399
400         if (!(type & msidbCustomActionTypeContinue))
401             rc = custom_get_process_return(ProcessHandle);
402
403         CloseHandle(ProcessHandle);
404     }
405     else
406     {
407         TRACE("%s running in background\n", debugstr_w(name));
408
409         if (!(type & msidbCustomActionTypeContinue))
410             file_running_action(package, ProcessHandle, TRUE, name);
411         else
412             CloseHandle(ProcessHandle);
413     }
414
415     return rc;
416 }
417
418 typedef struct _msi_custom_action_info {
419     struct list entry;
420     MSIPACKAGE *package;
421     LPWSTR source;
422     LPWSTR target;
423     HANDLE handle;
424     LPWSTR action;
425     INT type;
426     GUID guid;
427 } msi_custom_action_info;
428
429 static void free_custom_action_data( msi_custom_action_info *info )
430 {
431     EnterCriticalSection( &msi_custom_action_cs );
432     list_remove( &info->entry );
433     LeaveCriticalSection( &msi_custom_action_cs );
434     if (info->handle)
435         CloseHandle( info->handle );
436     msi_free( info->action );
437     msi_free( info->source );
438     msi_free( info->target );
439     msiobj_release( &info->package->hdr );
440     msi_free( info );
441 }
442
443 static UINT wait_thread_handle( msi_custom_action_info *info )
444 {
445     UINT rc = ERROR_SUCCESS;
446
447     if (!(info->type & msidbCustomActionTypeAsync))
448     {
449         TRACE("waiting for %s\n", debugstr_w( info->action ));
450
451         msi_dialog_check_messages( info->handle );
452
453         if (!(info->type & msidbCustomActionTypeContinue))
454             rc = custom_get_thread_return( info->handle );
455
456         free_custom_action_data( info );
457     }
458     else
459     {
460         TRACE("%s running in background\n", debugstr_w( info->action ));
461     }
462
463     return rc;
464 }
465
466 static msi_custom_action_info *find_action_by_guid( const GUID *guid )
467 {
468     msi_custom_action_info *info;
469     BOOL found = FALSE;
470
471     EnterCriticalSection( &msi_custom_action_cs );
472
473     LIST_FOR_EACH_ENTRY( info, &msi_pending_custom_actions, msi_custom_action_info, entry )
474     {
475         if (IsEqualGUID( &info->guid, guid ))
476         {
477             found = TRUE;
478             break;
479         }
480     }
481
482     LeaveCriticalSection( &msi_custom_action_cs );
483
484     if (!found)
485         return NULL;
486
487     return info;
488 }
489
490 static DWORD WINAPI ACTION_CallDllFunction( const GUID *guid )
491 {
492     msi_custom_action_info *info;
493     MsiCustomActionEntryPoint fn;
494     MSIHANDLE hPackage;
495     HANDLE hModule;
496     LPSTR proc;
497     UINT r = ERROR_FUNCTION_FAILED;
498
499     info = find_action_by_guid( guid );
500     if (!info)
501     {
502         ERR("failed to find action %s\n", debugstr_guid( guid) );
503         return r;
504     }
505
506     TRACE("%s %s\n", debugstr_w( info->source ), debugstr_w( info->target ) );
507
508     hModule = LoadLibraryW( info->source );
509     if (!hModule)
510     {
511         ERR("failed to load dll %s\n", debugstr_w( info->source ) );
512         return r;
513     }
514
515     proc = strdupWtoA( info->target );
516     fn = (MsiCustomActionEntryPoint) GetProcAddress( hModule, proc );
517     msi_free( proc );
518     if (fn)
519     {
520         hPackage = alloc_msihandle( &info->package->hdr );
521         if (hPackage)
522         {
523             TRACE("calling %s\n", debugstr_w( info->target ) );
524             r = fn( hPackage );
525             MsiCloseHandle( hPackage );
526         }
527         else
528             ERR("failed to create handle for %p\n", info->package );
529     }
530     else
531         ERR("GetProcAddress(%s) failed\n", debugstr_w( info->target ) );
532
533     FreeLibrary(hModule);
534
535     if (info->type & msidbCustomActionTypeAsync &&
536         info->type & msidbCustomActionTypeContinue)
537         free_custom_action_data( info );
538
539     return r;
540 }
541
542 static DWORD WINAPI DllThread( LPVOID arg )
543 {
544     LPGUID guid = arg;
545     DWORD rc = 0;
546
547     TRACE("custom action (%x) started\n", GetCurrentThreadId() );
548
549     rc = ACTION_CallDllFunction( guid );
550
551     TRACE("custom action (%x) returned %i\n", GetCurrentThreadId(), rc );
552
553     MsiCloseAllHandles();
554     return rc;
555 }
556
557 static DWORD WINAPI ACTION_CAInstallPackage(const GUID *guid)
558 {
559     msi_custom_action_info *info;
560     UINT r = ERROR_FUNCTION_FAILED;
561     INSTALLUILEVEL old_level;
562
563     info = find_action_by_guid(guid);
564     if (!info)
565     {
566         ERR("failed to find action %s\n", debugstr_guid(guid));
567         return r;
568     }
569
570     old_level = MsiSetInternalUI(INSTALLUILEVEL_BASIC, NULL);
571     r = MsiInstallProductW(info->source, info->target);
572     MsiSetInternalUI(old_level, NULL);
573
574     return r;
575 }
576
577 static DWORD WINAPI ConcurrentInstallThread(LPVOID arg)
578 {
579     LPGUID guid = arg;
580     DWORD rc;
581
582     TRACE("concurrent installation (%x) started\n", GetCurrentThreadId());
583
584     rc = ACTION_CAInstallPackage(guid);
585
586     TRACE("concurrent installation (%x) returned %i\n", GetCurrentThreadId(), rc);
587
588     MsiCloseAllHandles();
589     return rc;
590 }
591
592 static msi_custom_action_info *do_msidbCustomActionTypeDll(
593     MSIPACKAGE *package, INT type, LPCWSTR source, LPCWSTR target, LPCWSTR action )
594 {
595     msi_custom_action_info *info;
596
597     info = msi_alloc( sizeof *info );
598     if (!info)
599         return NULL;
600
601     msiobj_addref( &package->hdr );
602     info->package = package;
603     info->type = type;
604     info->target = strdupW( target );
605     info->source = strdupW( source );
606     info->action = strdupW( action );
607     CoCreateGuid( &info->guid );
608
609     EnterCriticalSection( &msi_custom_action_cs );
610     list_add_tail( &msi_pending_custom_actions, &info->entry );
611     LeaveCriticalSection( &msi_custom_action_cs );
612
613     info->handle = CreateThread( NULL, 0, DllThread, &info->guid, 0, NULL );
614     if (!info->handle)
615     {
616         free_custom_action_data( info );
617         return NULL;
618     }
619
620     return info;
621 }
622
623 static msi_custom_action_info *do_msidbCAConcurrentInstall(
624     MSIPACKAGE *package, INT type, LPCWSTR source, LPCWSTR target, LPCWSTR action)
625 {
626     msi_custom_action_info *info;
627
628     info = msi_alloc( sizeof *info );
629     if (!info)
630         return NULL;
631
632     msiobj_addref( &package->hdr );
633     info->package = package;
634     info->type = type;
635     info->target = strdupW( target );
636     info->source = strdupW( source );
637     info->action = strdupW( action );
638     CoCreateGuid( &info->guid );
639
640     EnterCriticalSection( &msi_custom_action_cs );
641     list_add_tail( &msi_pending_custom_actions, &info->entry );
642     LeaveCriticalSection( &msi_custom_action_cs );
643
644     info->handle = CreateThread( NULL, 0, ConcurrentInstallThread, &info->guid, 0, NULL );
645     if (!info->handle)
646     {
647         free_custom_action_data( info );
648         return NULL;
649     }
650
651     return info;
652 }
653
654 static UINT HANDLE_CustomType23(MSIPACKAGE *package, LPCWSTR source,
655                                 LPCWSTR target, const INT type, LPCWSTR action)
656 {
657     msi_custom_action_info *info;
658     WCHAR package_path[MAX_PATH];
659     DWORD size;
660
661     static const WCHAR backslash[] = {'\\',0};
662
663     MSI_GetPropertyW(package, cszSourceDir, package_path, &size);
664     lstrcatW(package_path, backslash);
665     lstrcatW(package_path, source);
666
667     if (GetFileAttributesW(package_path) == INVALID_FILE_ATTRIBUTES)
668     {
669         ERR("Source package does not exist: %s\n", debugstr_w(package_path));
670         return ERROR_FUNCTION_FAILED;
671     }
672
673     TRACE("Installing package %s concurrently\n", debugstr_w(package_path));
674
675     info = do_msidbCAConcurrentInstall(package, type, package_path, target, action);
676
677     return wait_thread_handle(info);
678 }
679
680 static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source,
681                                LPCWSTR target, const INT type, LPCWSTR action)
682 {
683     msi_custom_action_info *info;
684     WCHAR tmp_file[MAX_PATH];
685     UINT r;
686
687     r = store_binary_to_temp(package, source, tmp_file);
688     if (r != ERROR_SUCCESS)
689         return r;
690
691     TRACE("Calling function %s from %s\n",debugstr_w(target),
692           debugstr_w(tmp_file));
693
694     if (!strchrW(tmp_file,'.'))
695     {
696         static const WCHAR dot[]={'.',0};
697         strcatW(tmp_file,dot);
698     }
699
700     info = do_msidbCustomActionTypeDll( package, type, tmp_file, target, action );
701
702     return wait_thread_handle( info );
703 }
704
705 static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source,
706                                LPCWSTR target, const INT type, LPCWSTR action)
707 {
708     WCHAR tmp_file[MAX_PATH];
709     STARTUPINFOW si;
710     PROCESS_INFORMATION info;
711     BOOL rc;
712     INT len;
713     WCHAR *deformated = NULL;
714     WCHAR *cmd;
715     static const WCHAR spc[] = {' ',0};
716     UINT r;
717
718     memset(&si,0,sizeof(STARTUPINFOW));
719
720     r = store_binary_to_temp(package, source, tmp_file);
721     if (r != ERROR_SUCCESS)
722         return r;
723
724     deformat_string(package,target,&deformated);
725
726     len = strlenW(tmp_file)+2;
727
728     if (deformated)
729         len += strlenW(deformated);
730
731     cmd = msi_alloc(sizeof(WCHAR)*len);
732
733     strcpyW(cmd,tmp_file);
734     if (deformated)
735     {
736         strcatW(cmd,spc);
737         strcatW(cmd,deformated);
738
739         msi_free(deformated);
740     }
741
742     TRACE("executing exe %s\n", debugstr_w(cmd));
743
744     rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
745                   c_collen, &si, &info);
746     msi_free(cmd);
747
748     if ( !rc )
749     {
750         ERR("Unable to execute command %s\n", debugstr_w(cmd));
751         return ERROR_SUCCESS;
752     }
753     CloseHandle( info.hThread );
754
755     r = wait_process_handle(package, type, info.hProcess, action);
756
757     return r;
758 }
759
760 static UINT HANDLE_CustomType17(MSIPACKAGE *package, LPCWSTR source,
761                                 LPCWSTR target, const INT type, LPCWSTR action)
762 {
763     msi_custom_action_info *info;
764     MSIFILE *file;
765
766     TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
767
768     file = get_loaded_file( package, source );
769     if (!file)
770     {
771         ERR("invalid file key %s\n", debugstr_w( source ));
772         return ERROR_FUNCTION_FAILED;
773     }
774
775     info = do_msidbCustomActionTypeDll( package, type, file->TargetPath, target, action );
776
777     return wait_thread_handle( info );
778 }
779
780 static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source,
781                                 LPCWSTR target, const INT type, LPCWSTR action)
782 {
783     STARTUPINFOW si;
784     PROCESS_INFORMATION info;
785     BOOL rc;
786     WCHAR *deformated;
787     WCHAR *cmd;
788     INT len;
789     static const WCHAR spc[] = {' ',0};
790     MSIFILE *file;
791
792     memset(&si,0,sizeof(STARTUPINFOW));
793
794     file = get_loaded_file(package,source);
795     if( !file )
796         return ERROR_FUNCTION_FAILED;
797
798     len = lstrlenW( file->TargetPath );
799
800     deformat_string(package,target,&deformated);
801     if (deformated)
802         len += strlenW(deformated);
803     len += 2;
804
805     cmd = msi_alloc(len * sizeof(WCHAR));
806
807     lstrcpyW( cmd, file->TargetPath);
808     if (deformated)
809     {
810         strcatW(cmd, spc);
811         strcatW(cmd, deformated);
812
813         msi_free(deformated);
814     }
815
816     TRACE("executing exe %s\n", debugstr_w(cmd));
817
818     rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
819                   c_collen, &si, &info);
820
821     if ( !rc )
822     {
823         ERR("Unable to execute command %s\n", debugstr_w(cmd));
824         msi_free(cmd);
825         return ERROR_SUCCESS;
826     }
827     msi_free(cmd);
828     CloseHandle( info.hThread );
829
830     return wait_process_handle(package, type, info.hProcess, action);
831 }
832
833 static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
834                                 LPCWSTR target, const INT type, LPCWSTR action)
835 {
836     static const WCHAR query[] = {
837       'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
838       'F','R','O','M',' ','`','E','r','r','o','r','`',' ',
839       'W','H','E','R','E',' ','`','E','r','r','o','r','`',' ','=',' ',
840       '%','s',0
841     };
842     MSIRECORD *row = 0;
843     LPWSTR deformated = NULL;
844
845     deformat_string( package, target, &deformated );
846
847     /* first try treat the error as a number */
848     row = MSI_QueryGetRecord( package->db, query, deformated );
849     if( row )
850     {
851         LPCWSTR error = MSI_RecordGetString( row, 1 );
852         MessageBoxW( NULL, error, NULL, MB_OK );
853         msiobj_release( &row->hdr );
854     }
855     else
856         MessageBoxW( NULL, deformated, NULL, MB_OK );
857
858     msi_free( deformated );
859
860     return ERROR_FUNCTION_FAILED;
861 }
862
863 static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source,
864                                 LPCWSTR target, const INT type, LPCWSTR action)
865 {
866     STARTUPINFOW si;
867     PROCESS_INFORMATION info;
868     WCHAR *prop;
869     BOOL rc;
870     WCHAR *deformated;
871     WCHAR *cmd;
872     INT len;
873     static const WCHAR spc[] = {' ',0};
874
875     memset(&si,0,sizeof(STARTUPINFOW));
876     memset(&info,0,sizeof(PROCESS_INFORMATION));
877
878     prop = msi_dup_property( package, source );
879     if (!prop)
880         return ERROR_SUCCESS;
881
882     deformat_string(package,target,&deformated);
883     len = strlenW(prop) + 2;
884     if (deformated)
885          len += strlenW(deformated);
886
887     cmd = msi_alloc(sizeof(WCHAR)*len);
888
889     strcpyW(cmd,prop);
890     if (deformated)
891     {
892         strcatW(cmd,spc);
893         strcatW(cmd,deformated);
894
895         msi_free(deformated);
896     }
897     msi_free(prop);
898
899     TRACE("executing exe %s\n", debugstr_w(cmd));
900
901     rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
902                   c_collen, &si, &info);
903
904     if ( !rc )
905     {
906         ERR("Unable to execute command %s\n", debugstr_w(cmd));
907         msi_free(cmd);
908         return ERROR_SUCCESS;
909     }
910     msi_free(cmd);
911
912     CloseHandle( info.hThread );
913
914     return wait_process_handle(package, type, info.hProcess, action);
915 }
916
917 static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
918                                 LPCWSTR target, const INT type, LPCWSTR action)
919 {
920     LPWSTR filename, deformated;
921     STARTUPINFOW si;
922     PROCESS_INFORMATION info;
923     BOOL rc;
924
925     memset(&si,0,sizeof(STARTUPINFOW));
926
927     filename = resolve_folder(package, source, FALSE, FALSE, TRUE, NULL);
928
929     if (!filename)
930         return ERROR_FUNCTION_FAILED;
931
932     SetCurrentDirectoryW(filename);
933     msi_free(filename);
934
935     deformat_string(package,target,&deformated);
936
937     if (!deformated)
938         return ERROR_FUNCTION_FAILED;
939
940     TRACE("executing exe %s\n", debugstr_w(deformated));
941
942     rc = CreateProcessW(NULL, deformated, NULL, NULL, FALSE, 0, NULL,
943                   c_collen, &si, &info);
944
945     if ( !rc )
946     {
947         ERR("Unable to execute command %s\n", debugstr_w(deformated));
948         msi_free(deformated);
949         return ERROR_SUCCESS;
950     }
951     msi_free(deformated);
952     CloseHandle( info.hThread );
953
954     return wait_process_handle(package, type, info.hProcess, action);
955 }
956
957 void ACTION_FinishCustomActions(MSIPACKAGE* package)
958 {
959     struct list *item;
960     HANDLE *wait_handles;
961     unsigned int handle_count, i;
962     msi_custom_action_info *info, *cursor;
963
964     while ((item = list_head( &package->RunningActions )))
965     {
966         MSIRUNNINGACTION *action = LIST_ENTRY( item, MSIRUNNINGACTION, entry );
967
968         list_remove( &action->entry );
969
970         TRACE("waiting for %s\n", debugstr_w( action->name ) );
971         msi_dialog_check_messages( action->handle );
972
973         CloseHandle( action->handle );
974         msi_free( action->name );
975         msi_free( action );
976     }
977
978     EnterCriticalSection( &msi_custom_action_cs );
979
980     handle_count = list_count( &msi_pending_custom_actions );
981     wait_handles = HeapAlloc( GetProcessHeap(), 0, handle_count * sizeof(HANDLE) );
982
983     handle_count = 0;
984     LIST_FOR_EACH_ENTRY_SAFE( info, cursor, &msi_pending_custom_actions, msi_custom_action_info, entry )
985     {
986         if (info->package == package )
987         {
988             if (DuplicateHandle(GetCurrentProcess(), info->handle, GetCurrentProcess(), &wait_handles[handle_count], SYNCHRONIZE, FALSE, 0))
989                 handle_count++;
990             free_custom_action_data( info );
991         }
992     }
993
994     LeaveCriticalSection( &msi_custom_action_cs );
995
996     for (i = 0; i < handle_count; i++)
997     {
998         msi_dialog_check_messages( wait_handles[i] );
999         CloseHandle( wait_handles[i] );
1000     }
1001
1002     HeapFree( GetProcessHeap(), 0, wait_handles );
1003 }