xref: /reactos/dll/win32/msi/custom.c (revision 8e659192)
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 #include "msipriv.h"
22 
23 #include <wine/exception.h>
24 
25 #ifdef _MSC_VER
26 #include "msvchelper.h"
27 #endif
28 
29 WINE_DEFAULT_DEBUG_CHANNEL(msi);
30 
31 #define CUSTOM_ACTION_TYPE_MASK 0x3F
32 
33 typedef struct tagMSIRUNNINGACTION
34 {
35     struct list entry;
36     HANDLE handle;
37     BOOL   process;
38     LPWSTR name;
39 } MSIRUNNINGACTION;
40 
41 typedef UINT (WINAPI *MsiCustomActionEntryPoint)( MSIHANDLE );
42 
43 static CRITICAL_SECTION msi_custom_action_cs;
44 static CRITICAL_SECTION_DEBUG msi_custom_action_cs_debug =
45 {
46     0, 0, &msi_custom_action_cs,
47     { &msi_custom_action_cs_debug.ProcessLocksList,
48       &msi_custom_action_cs_debug.ProcessLocksList },
49       0, 0, { (DWORD_PTR)(__FILE__ ": msi_custom_action_cs") }
50 };
51 static CRITICAL_SECTION msi_custom_action_cs = { &msi_custom_action_cs_debug, -1, 0, 0, 0, 0 };
52 
53 static struct list msi_pending_custom_actions = LIST_INIT( msi_pending_custom_actions );
54 
55 UINT msi_schedule_action( MSIPACKAGE *package, UINT script, const WCHAR *action )
56 {
57     UINT count;
58     WCHAR **newbuf = NULL;
59 
60     if (script >= SCRIPT_MAX)
61     {
62         FIXME("Unknown script requested %u\n", script);
63         return ERROR_FUNCTION_FAILED;
64     }
65     TRACE("Scheduling action %s in script %u\n", debugstr_w(action), script);
66 
67     count = package->script_actions_count[script];
68     package->script_actions_count[script]++;
69     if (count != 0) newbuf = msi_realloc( package->script_actions[script],
70                                           package->script_actions_count[script] * sizeof(WCHAR *) );
71     else newbuf = msi_alloc( sizeof(WCHAR *) );
72 
73     newbuf[count] = strdupW( action );
74     package->script_actions[script] = newbuf;
75     return ERROR_SUCCESS;
76 }
77 
78 UINT msi_register_unique_action( MSIPACKAGE *package, const WCHAR *action )
79 {
80     UINT count;
81     WCHAR **newbuf = NULL;
82 
83     TRACE("Registering %s as unique action\n", debugstr_w(action));
84 
85     count = package->unique_actions_count;
86     package->unique_actions_count++;
87     if (count != 0) newbuf = msi_realloc( package->unique_actions,
88                                           package->unique_actions_count * sizeof(WCHAR *) );
89     else newbuf = msi_alloc( sizeof(WCHAR *) );
90 
91     newbuf[count] = strdupW( action );
92     package->unique_actions = newbuf;
93     return ERROR_SUCCESS;
94 }
95 
96 BOOL msi_action_is_unique( const MSIPACKAGE *package, const WCHAR *action )
97 {
98     UINT i;
99 
100     for (i = 0; i < package->unique_actions_count; i++)
101     {
102         if (!strcmpW( package->unique_actions[i], action )) return TRUE;
103     }
104     return FALSE;
105 }
106 
107 static BOOL check_execution_scheduling_options(MSIPACKAGE *package, LPCWSTR action, UINT options)
108 {
109     if ((options & msidbCustomActionTypeClientRepeat) ==
110             msidbCustomActionTypeClientRepeat)
111     {
112         if (!(package->InWhatSequence & SEQUENCE_UI &&
113             package->InWhatSequence & SEQUENCE_EXEC))
114         {
115             TRACE("Skipping action due to dbCustomActionTypeClientRepeat option.\n");
116             return FALSE;
117         }
118     }
119     else if (options & msidbCustomActionTypeFirstSequence)
120     {
121         if (package->InWhatSequence & SEQUENCE_UI &&
122             package->InWhatSequence & SEQUENCE_EXEC )
123         {
124             TRACE("Skipping action due to msidbCustomActionTypeFirstSequence option.\n");
125             return FALSE;
126         }
127     }
128     else if (options & msidbCustomActionTypeOncePerProcess)
129     {
130         if (msi_action_is_unique(package, action))
131         {
132             TRACE("Skipping action due to msidbCustomActionTypeOncePerProcess option.\n");
133             return FALSE;
134         }
135         else
136             msi_register_unique_action(package, action);
137     }
138 
139     return TRUE;
140 }
141 
142 /* stores the following properties before the action:
143  *
144  *    [CustomActionData<=>UserSID<=>ProductCode]Action
145  */
146 static LPWSTR msi_get_deferred_action(LPCWSTR action, LPCWSTR actiondata,
147                                       LPCWSTR usersid, LPCWSTR prodcode)
148 {
149     LPWSTR deferred;
150     DWORD len;
151 
152     static const WCHAR format[] = {
153             '[','%','s','<','=','>','%','s','<','=','>','%','s',']','%','s',0
154     };
155 
156     if (!actiondata)
157         return strdupW(action);
158 
159     len = lstrlenW(action) + lstrlenW(actiondata) +
160           lstrlenW(usersid) + lstrlenW(prodcode) +
161           lstrlenW(format) - 7;
162     deferred = msi_alloc(len * sizeof(WCHAR));
163 
164     sprintfW(deferred, format, actiondata, usersid, prodcode, action);
165     return deferred;
166 }
167 
168 static void set_deferred_action_props( MSIPACKAGE *package, const WCHAR *deferred_data )
169 {
170     static const WCHAR sep[] = {'<','=','>',0};
171     const WCHAR *end, *beg = deferred_data + 1;
172 
173     end = strstrW(beg, sep);
174     msi_set_property( package->db, szCustomActionData, beg, end - beg );
175     beg = end + 3;
176 
177     end = strstrW(beg, sep);
178     msi_set_property( package->db, szUserSID, beg, end - beg );
179     beg = end + 3;
180 
181     end = strchrW(beg, ']');
182     msi_set_property( package->db, szProductCode, beg, end - beg );
183 }
184 
185 WCHAR *msi_create_temp_file( MSIDATABASE *db )
186 {
187     WCHAR *ret;
188 
189     if (!db->tempfolder)
190     {
191         WCHAR tmp[MAX_PATH];
192         UINT len = sizeof(tmp)/sizeof(tmp[0]);
193 
194         if (msi_get_property( db, szTempFolder, tmp, &len ) ||
195             GetFileAttributesW( tmp ) != FILE_ATTRIBUTE_DIRECTORY)
196         {
197             GetTempPathW( MAX_PATH, tmp );
198         }
199         if (!(db->tempfolder = strdupW( tmp ))) return NULL;
200     }
201 
202     if ((ret = msi_alloc( (strlenW( db->tempfolder ) + 20) * sizeof(WCHAR) )))
203     {
204         if (!GetTempFileNameW( db->tempfolder, szMsi, 0, ret ))
205         {
206             msi_free( ret );
207             return NULL;
208         }
209     }
210 
211     return ret;
212 }
213 
214 static MSIBINARY *create_temp_binary( MSIPACKAGE *package, LPCWSTR source, BOOL dll )
215 {
216     static const WCHAR query[] = {
217         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
218         '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',' ',
219         '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
220     MSIRECORD *row;
221     MSIBINARY *binary = NULL;
222     HANDLE file;
223     CHAR buffer[1024];
224     WCHAR *tmpfile;
225     DWORD sz, write;
226     UINT r;
227 
228     if (!(tmpfile = msi_create_temp_file( package->db ))) return NULL;
229 
230     if (!(row = MSI_QueryGetRecord( package->db, query, source ))) goto error;
231     if (!(binary = msi_alloc_zero( sizeof(MSIBINARY) ))) goto error;
232 
233     file = CreateFileW( tmpfile, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
234     if (file == INVALID_HANDLE_VALUE) goto error;
235 
236     do
237     {
238         sz = sizeof(buffer);
239         r = MSI_RecordReadStream( row, 2, buffer, &sz );
240         if (r != ERROR_SUCCESS)
241         {
242             ERR("Failed to get stream\n");
243             break;
244         }
245         WriteFile( file, buffer, sz, &write, NULL );
246     } while (sz == sizeof buffer);
247 
248     CloseHandle( file );
249     if (r != ERROR_SUCCESS) goto error;
250 
251     /* keep a reference to prevent the dll from being unloaded */
252     if (dll && !(binary->module = LoadLibraryW( tmpfile )))
253     {
254         ERR( "failed to load dll %s (%u)\n", debugstr_w( tmpfile ), GetLastError() );
255     }
256     binary->source = strdupW( source );
257     binary->tmpfile = tmpfile;
258     list_add_tail( &package->binaries, &binary->entry );
259 
260     msiobj_release( &row->hdr );
261     return binary;
262 
263 error:
264     if (row) msiobj_release( &row->hdr );
265     DeleteFileW( tmpfile );
266     msi_free( tmpfile );
267     msi_free( binary );
268     return NULL;
269 }
270 
271 static MSIBINARY *get_temp_binary( MSIPACKAGE *package, LPCWSTR source, BOOL dll )
272 {
273     MSIBINARY *binary;
274 
275     LIST_FOR_EACH_ENTRY( binary, &package->binaries, MSIBINARY, entry )
276     {
277         if (!strcmpW( binary->source, source ))
278             return binary;
279     }
280 
281     return create_temp_binary( package, source, dll );
282 }
283 
284 static void file_running_action(MSIPACKAGE* package, HANDLE Handle,
285                                 BOOL process, LPCWSTR name)
286 {
287     MSIRUNNINGACTION *action;
288 
289     action = msi_alloc( sizeof(MSIRUNNINGACTION) );
290 
291     action->handle = Handle;
292     action->process = process;
293     action->name = strdupW(name);
294 
295     list_add_tail( &package->RunningActions, &action->entry );
296 }
297 
298 static UINT custom_get_process_return( HANDLE process )
299 {
300     DWORD rc = 0;
301 
302     GetExitCodeProcess( process, &rc );
303     TRACE("exit code is %u\n", rc);
304     if (rc != 0)
305         return ERROR_FUNCTION_FAILED;
306     return ERROR_SUCCESS;
307 }
308 
309 static UINT custom_get_thread_return( MSIPACKAGE *package, HANDLE thread )
310 {
311     DWORD rc = 0;
312 
313     GetExitCodeThread( thread, &rc );
314 
315     switch (rc)
316     {
317     case ERROR_FUNCTION_NOT_CALLED:
318     case ERROR_SUCCESS:
319     case ERROR_INSTALL_USEREXIT:
320     case ERROR_INSTALL_FAILURE:
321         return rc;
322     case ERROR_NO_MORE_ITEMS:
323         return ERROR_SUCCESS;
324     case ERROR_INSTALL_SUSPEND:
325         ACTION_ForceReboot( package );
326         return ERROR_SUCCESS;
327     default:
328         ERR("Invalid Return Code %d\n",rc);
329         return ERROR_INSTALL_FAILURE;
330     }
331 }
332 
333 static UINT wait_process_handle(MSIPACKAGE* package, UINT type,
334                            HANDLE ProcessHandle, LPCWSTR name)
335 {
336     UINT rc = ERROR_SUCCESS;
337 
338     if (!(type & msidbCustomActionTypeAsync))
339     {
340         TRACE("waiting for %s\n", debugstr_w(name));
341 
342         msi_dialog_check_messages(ProcessHandle);
343 
344         if (!(type & msidbCustomActionTypeContinue))
345             rc = custom_get_process_return(ProcessHandle);
346 
347         CloseHandle(ProcessHandle);
348     }
349     else
350     {
351         TRACE("%s running in background\n", debugstr_w(name));
352 
353         if (!(type & msidbCustomActionTypeContinue))
354             file_running_action(package, ProcessHandle, TRUE, name);
355         else
356             CloseHandle(ProcessHandle);
357     }
358 
359     return rc;
360 }
361 
362 typedef struct _msi_custom_action_info {
363     struct list entry;
364     LONG refs;
365     MSIPACKAGE *package;
366     LPWSTR source;
367     LPWSTR target;
368     HANDLE handle;
369     LPWSTR action;
370     INT type;
371     GUID guid;
372 } msi_custom_action_info;
373 
374 static void release_custom_action_data( msi_custom_action_info *info )
375 {
376     EnterCriticalSection( &msi_custom_action_cs );
377 
378     if (!--info->refs)
379     {
380         list_remove( &info->entry );
381         if (info->handle)
382             CloseHandle( info->handle );
383         msi_free( info->action );
384         msi_free( info->source );
385         msi_free( info->target );
386         msiobj_release( &info->package->hdr );
387         msi_free( info );
388     }
389 
390     LeaveCriticalSection( &msi_custom_action_cs );
391 }
392 
393 /* must be called inside msi_custom_action_cs if info is in the pending custom actions list */
394 static void addref_custom_action_data( msi_custom_action_info *info )
395 {
396     info->refs++;
397  }
398 
399 static UINT wait_thread_handle( msi_custom_action_info *info )
400 {
401     UINT rc = ERROR_SUCCESS;
402 
403     if (!(info->type & msidbCustomActionTypeAsync))
404     {
405         TRACE("waiting for %s\n", debugstr_w( info->action ));
406 
407         msi_dialog_check_messages( info->handle );
408 
409         if (!(info->type & msidbCustomActionTypeContinue))
410             rc = custom_get_thread_return( info->package, info->handle );
411 
412         release_custom_action_data( info );
413     }
414     else
415     {
416         TRACE("%s running in background\n", debugstr_w( info->action ));
417     }
418 
419     return rc;
420 }
421 
422 static msi_custom_action_info *find_action_by_guid( const GUID *guid )
423 {
424     msi_custom_action_info *info;
425     BOOL found = FALSE;
426 
427     EnterCriticalSection( &msi_custom_action_cs );
428 
429     LIST_FOR_EACH_ENTRY( info, &msi_pending_custom_actions, msi_custom_action_info, entry )
430     {
431         if (IsEqualGUID( &info->guid, guid ))
432         {
433             addref_custom_action_data( info );
434             found = TRUE;
435             break;
436         }
437     }
438 
439     LeaveCriticalSection( &msi_custom_action_cs );
440 
441     if (!found)
442         return NULL;
443 
444     return info;
445 }
446 
447 static void handle_msi_break( LPCWSTR target )
448 {
449     LPWSTR msg;
450     WCHAR val[MAX_PATH];
451 
452     static const WCHAR MsiBreak[] = { 'M','s','i','B','r','e','a','k',0 };
453     static const WCHAR WindowsInstaller[] = {
454         'W','i','n','d','o','w','s',' ','I','n','s','t','a','l','l','e','r',0
455     };
456 
457     static const WCHAR format[] = {
458         'T','o',' ','d','e','b','u','g',' ','y','o','u','r',' ',
459         'c','u','s','t','o','m',' ','a','c','t','i','o','n',',',' ',
460         'a','t','t','a','c','h',' ','y','o','u','r',' ','d','e','b','u','g','g','e','r',' ',
461         't','o',' ','p','r','o','c','e','s','s',' ','%','i',' ','(','0','x','%','X',')',' ',
462         'a','n','d',' ','p','r','e','s','s',' ','O','K',0
463     };
464 
465     if( !GetEnvironmentVariableW( MsiBreak, val, MAX_PATH ))
466         return;
467 
468     if( strcmpiW( val, target ))
469         return;
470 
471     msg = msi_alloc( (lstrlenW(format) + 10) * sizeof(WCHAR) );
472     if (!msg)
473         return;
474 
475     wsprintfW( msg, format, GetCurrentProcessId(), GetCurrentProcessId());
476     MessageBoxW( NULL, msg, WindowsInstaller, MB_OK);
477     msi_free(msg);
478     DebugBreak();
479 }
480 
481 static UINT get_action_info( const GUID *guid, INT *type, MSIHANDLE *handle,
482                              BSTR *dll, BSTR *funcname,
483                              IWineMsiRemotePackage **package )
484 {
485     IClassFactory *cf = NULL;
486     IWineMsiRemoteCustomAction *rca = NULL;
487     HRESULT r;
488 
489     r = DllGetClassObject( &CLSID_WineMsiRemoteCustomAction,
490                            &IID_IClassFactory, (LPVOID *)&cf );
491     if (FAILED(r))
492     {
493         ERR("failed to get IClassFactory interface\n");
494         return ERROR_FUNCTION_FAILED;
495     }
496 
497     r = IClassFactory_CreateInstance( cf, NULL, &IID_IWineMsiRemoteCustomAction, (LPVOID *)&rca );
498     if (FAILED(r))
499     {
500         ERR("failed to get IWineMsiRemoteCustomAction interface\n");
501         return ERROR_FUNCTION_FAILED;
502     }
503 
504     r = IWineMsiRemoteCustomAction_GetActionInfo( rca, guid, type, handle, dll, funcname, package );
505     IWineMsiRemoteCustomAction_Release( rca );
506     if (FAILED(r))
507     {
508         ERR("GetActionInfo failed\n");
509         return ERROR_FUNCTION_FAILED;
510     }
511 
512     return ERROR_SUCCESS;
513 }
514 
515 #ifdef __i386__
516 extern UINT CUSTOMPROC_wrapper( MsiCustomActionEntryPoint proc, MSIHANDLE handle );
517 __ASM_GLOBAL_FUNC( CUSTOMPROC_wrapper,
518     "pushl %ebp\n\t"
519     __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
520     __ASM_CFI(".cfi_rel_offset %ebp,0\n\t")
521     "movl %esp,%ebp\n\t"
522     __ASM_CFI(".cfi_def_cfa_register %ebp\n\t")
523     "subl $4,%esp\n\t"
524     "pushl 12(%ebp)\n\t"
525     "movl 8(%ebp),%eax\n\t"
526     "call *%eax\n\t"
527     "leave\n\t"
528     __ASM_CFI(".cfi_def_cfa %esp,4\n\t")
529     __ASM_CFI(".cfi_same_value %ebp\n\t")
530     "ret" )
531 #else
532 static inline UINT CUSTOMPROC_wrapper( MsiCustomActionEntryPoint proc, MSIHANDLE handle )
533 {
534     return proc(handle);
535 }
536 #endif
537 
538 static DWORD ACTION_CallDllFunction( const GUID *guid )
539 {
540     MsiCustomActionEntryPoint fn;
541     MSIHANDLE hPackage, handle;
542     HANDLE hModule;
543     LPSTR proc;
544     UINT r = ERROR_FUNCTION_FAILED;
545     BSTR dll = NULL, function = NULL;
546     INT type;
547     IWineMsiRemotePackage *remote_package = NULL;
548 
549     TRACE("%s\n", debugstr_guid( guid ));
550 
551     r = get_action_info( guid, &type, &handle, &dll, &function, &remote_package );
552     if (r != ERROR_SUCCESS)
553         return r;
554 
555     hModule = LoadLibraryW( dll );
556     if (!hModule)
557     {
558         ERR( "failed to load dll %s (%u)\n", debugstr_w( dll ), GetLastError() );
559         return ERROR_SUCCESS;
560     }
561 
562     proc = strdupWtoA( function );
563     fn = (MsiCustomActionEntryPoint) GetProcAddress( hModule, proc );
564     msi_free( proc );
565     if (fn)
566     {
567         hPackage = alloc_msi_remote_handle( (IUnknown *)remote_package );
568         if (hPackage)
569         {
570             IWineMsiRemotePackage_SetMsiHandle( remote_package, handle );
571             TRACE("calling %s\n", debugstr_w( function ) );
572             handle_msi_break( function );
573 
574             __TRY
575             {
576                 r = CUSTOMPROC_wrapper( fn, hPackage );
577             }
578             __EXCEPT_PAGE_FAULT
579             {
580                 ERR("Custom action (%s:%s) caused a page fault: %08x\n",
581                     debugstr_w(dll), debugstr_w(function), GetExceptionCode());
582                 r = ERROR_SUCCESS;
583             }
584             __ENDTRY;
585 
586             MsiCloseHandle( hPackage );
587         }
588         else
589             ERR("failed to create handle for %p\n", remote_package );
590     }
591     else
592         ERR("GetProcAddress(%s) failed\n", debugstr_w( function ) );
593 
594     FreeLibrary(hModule);
595 
596     IWineMsiRemotePackage_Release( remote_package );
597     SysFreeString( dll );
598     SysFreeString( function );
599     MsiCloseHandle( handle );
600 
601     return r;
602 }
603 
604 static DWORD WINAPI DllThread( LPVOID arg )
605 {
606     LPGUID guid = arg;
607     DWORD rc = 0;
608 
609     TRACE("custom action (%x) started\n", GetCurrentThreadId() );
610 
611     rc = ACTION_CallDllFunction( guid );
612 
613     TRACE("custom action (%x) returned %i\n", GetCurrentThreadId(), rc );
614 
615     MsiCloseAllHandles();
616     return rc;
617 }
618 
619 static msi_custom_action_info *do_msidbCustomActionTypeDll(
620     MSIPACKAGE *package, INT type, LPCWSTR source, LPCWSTR target, LPCWSTR action )
621 {
622     msi_custom_action_info *info;
623 
624     info = msi_alloc( sizeof *info );
625     if (!info)
626         return NULL;
627 
628     msiobj_addref( &package->hdr );
629     info->refs = 2; /* 1 for our caller and 1 for thread we created */
630     info->package = package;
631     info->type = type;
632     info->target = strdupW( target );
633     info->source = strdupW( source );
634     info->action = strdupW( action );
635     CoCreateGuid( &info->guid );
636 
637     EnterCriticalSection( &msi_custom_action_cs );
638     list_add_tail( &msi_pending_custom_actions, &info->entry );
639     LeaveCriticalSection( &msi_custom_action_cs );
640 
641     info->handle = CreateThread( NULL, 0, DllThread, &info->guid, 0, NULL );
642     if (!info->handle)
643     {
644         /* release both references */
645         release_custom_action_data( info );
646         release_custom_action_data( info );
647         return NULL;
648     }
649 
650     return info;
651 }
652 
653 static UINT HANDLE_CustomType1( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target,
654                                 INT type, const WCHAR *action )
655 {
656     msi_custom_action_info *info;
657     MSIBINARY *binary;
658 
659     if (!(binary = get_temp_binary( package, source, TRUE )))
660         return ERROR_FUNCTION_FAILED;
661 
662     TRACE("Calling function %s from %s\n", debugstr_w(target), debugstr_w(binary->tmpfile));
663 
664     info = do_msidbCustomActionTypeDll( package, type, binary->tmpfile, target, action );
665     return wait_thread_handle( info );
666 }
667 
668 static HANDLE execute_command( const WCHAR *app, WCHAR *arg, const WCHAR *dir )
669 {
670     static const WCHAR dotexeW[] = {'.','e','x','e',0};
671     STARTUPINFOW si;
672     PROCESS_INFORMATION info;
673     WCHAR *exe = NULL, *cmd = NULL, *p;
674     BOOL ret;
675 
676     if (app)
677     {
678         int len_arg = 0;
679         DWORD len_exe;
680 
681         if (!(exe = msi_alloc( MAX_PATH * sizeof(WCHAR) ))) return INVALID_HANDLE_VALUE;
682         len_exe = SearchPathW( NULL, app, dotexeW, MAX_PATH, exe, NULL );
683         if (len_exe >= MAX_PATH)
684         {
685             msi_free( exe );
686             if (!(exe = msi_alloc( len_exe * sizeof(WCHAR) ))) return INVALID_HANDLE_VALUE;
687             len_exe = SearchPathW( NULL, app, dotexeW, len_exe, exe, NULL );
688         }
689         if (!len_exe)
690         {
691             ERR("can't find executable %u\n", GetLastError());
692             msi_free( exe );
693             return INVALID_HANDLE_VALUE;
694         }
695 
696         if (arg) len_arg = strlenW( arg );
697         if (!(cmd = msi_alloc( (len_exe + len_arg + 4) * sizeof(WCHAR) )))
698         {
699             msi_free( exe );
700             return INVALID_HANDLE_VALUE;
701         }
702         p = cmd;
703         if (strchrW( exe, ' ' ))
704         {
705             *p++ = '\"';
706             memcpy( p, exe, len_exe * sizeof(WCHAR) );
707             p += len_exe;
708             *p++ = '\"';
709             *p = 0;
710         }
711         else
712         {
713             strcpyW( p, exe );
714             p += len_exe;
715         }
716         if (arg)
717         {
718             *p++ = ' ';
719             memcpy( p, arg, len_arg * sizeof(WCHAR) );
720             p[len_arg] = 0;
721         }
722     }
723     memset( &si, 0, sizeof(STARTUPINFOW) );
724     ret = CreateProcessW( exe, exe ? cmd : arg, NULL, NULL, FALSE, 0, NULL, dir, &si, &info );
725     msi_free( cmd );
726     msi_free( exe );
727     if (!ret)
728     {
729         ERR("unable to execute command %u\n", GetLastError());
730         return INVALID_HANDLE_VALUE;
731     }
732     CloseHandle( info.hThread );
733     return info.hProcess;
734 }
735 
736 static UINT HANDLE_CustomType2( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target,
737                                 INT type, const WCHAR *action )
738 {
739     MSIBINARY *binary;
740     HANDLE handle;
741     WCHAR *arg;
742 
743     if (!(binary = get_temp_binary( package, source, FALSE ))) return ERROR_FUNCTION_FAILED;
744 
745     deformat_string( package, target, &arg );
746     TRACE("exe %s arg %s\n", debugstr_w(binary->tmpfile), debugstr_w(arg));
747 
748     handle = execute_command( binary->tmpfile, arg, szCRoot );
749     msi_free( arg );
750     if (handle == INVALID_HANDLE_VALUE) return ERROR_SUCCESS;
751     return wait_process_handle( package, type, handle, action );
752 }
753 
754 static UINT HANDLE_CustomType17( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target,
755                                  INT type, const WCHAR *action )
756 {
757     msi_custom_action_info *info;
758     MSIFILE *file;
759 
760     TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
761 
762     file = msi_get_loaded_file( package, source );
763     if (!file)
764     {
765         ERR("invalid file key %s\n", debugstr_w( source ));
766         return ERROR_FUNCTION_FAILED;
767     }
768 
769     info = do_msidbCustomActionTypeDll( package, type, file->TargetPath, target, action );
770     return wait_thread_handle( info );
771 }
772 
773 static UINT HANDLE_CustomType18( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target,
774                                  INT type, const WCHAR *action )
775 {
776     MSIFILE *file;
777     HANDLE handle;
778     WCHAR *arg;
779 
780     if (!(file = msi_get_loaded_file( package, source ))) return ERROR_FUNCTION_FAILED;
781 
782     deformat_string( package, target, &arg );
783     TRACE("exe %s arg %s\n", debugstr_w(file->TargetPath), debugstr_w(arg));
784 
785     handle = execute_command( file->TargetPath, arg, szCRoot );
786     msi_free( arg );
787     if (handle == INVALID_HANDLE_VALUE) return ERROR_SUCCESS;
788     return wait_process_handle( package, type, handle, action );
789 }
790 
791 static UINT HANDLE_CustomType19( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target,
792                                  INT type, const WCHAR *action )
793 {
794     static const WCHAR query[] = {
795       'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
796       'F','R','O','M',' ','`','E','r','r','o','r','`',' ',
797       'W','H','E','R','E',' ','`','E','r','r','o','r','`',' ','=',' ',
798       '%','s',0
799     };
800     MSIRECORD *row = 0;
801     LPWSTR deformated = NULL;
802 
803     deformat_string( package, target, &deformated );
804 
805     /* first try treat the error as a number */
806     row = MSI_QueryGetRecord( package->db, query, deformated );
807     if( row )
808     {
809         LPCWSTR error = MSI_RecordGetString( row, 1 );
810         if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
811             MessageBoxW( NULL, error, NULL, MB_OK );
812         msiobj_release( &row->hdr );
813     }
814     else if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
815         MessageBoxW( NULL, deformated, NULL, MB_OK );
816 
817     msi_free( deformated );
818 
819     return ERROR_INSTALL_FAILURE;
820 }
821 
822 static UINT HANDLE_CustomType23( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target,
823                                  INT type, const WCHAR *action )
824 {
825     static const WCHAR msiexecW[] = {'m','s','i','e','x','e','c',0};
826     static const WCHAR paramsW[] = {'/','q','b',' ','/','i',' '};
827     WCHAR *dir, *arg, *p;
828     UINT len_src, len_dir, len_tgt, len = sizeof(paramsW)/sizeof(paramsW[0]);
829     HANDLE handle;
830 
831     if (!(dir = msi_dup_property( package->db, szOriginalDatabase ))) return ERROR_OUTOFMEMORY;
832     if (!(p = strrchrW( dir, '\\' )) && !(p = strrchrW( dir, '/' )))
833     {
834         msi_free( dir );
835         return ERROR_FUNCTION_FAILED;
836     }
837     *p = 0;
838     len_dir = p - dir;
839     len_src = strlenW( source );
840     len_tgt = strlenW( target );
841     if (!(arg = msi_alloc( (len + len_dir + len_src + len_tgt + 5) * sizeof(WCHAR) )))
842     {
843         msi_free( dir );
844         return ERROR_OUTOFMEMORY;
845     }
846     memcpy( arg, paramsW, sizeof(paramsW) );
847     arg[len++] = '"';
848     memcpy( arg + len, dir, len_dir * sizeof(WCHAR) );
849     len += len_dir;
850     arg[len++] = '\\';
851     memcpy( arg + len, source, len_src * sizeof(WCHAR) );
852     len += len_src;
853     arg[len++] = '"';
854     arg[len++] = ' ';
855     strcpyW( arg + len, target );
856 
857     TRACE("installing %s concurrently\n", debugstr_w(source));
858 
859     handle = execute_command( msiexecW, arg, dir );
860     msi_free( dir );
861     msi_free( arg );
862     if (handle == INVALID_HANDLE_VALUE) return ERROR_SUCCESS;
863     return wait_process_handle( package, type, handle, action );
864 }
865 
866 static UINT HANDLE_CustomType50( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target,
867                                  INT type, const WCHAR *action )
868 {
869     WCHAR *exe, *arg;
870     HANDLE handle;
871 
872     if (!(exe = msi_dup_property( package->db, source ))) return ERROR_SUCCESS;
873 
874     deformat_string( package, target, &arg );
875     TRACE("exe %s arg %s\n", debugstr_w(exe), debugstr_w(arg));
876 
877     handle = execute_command( exe, arg, szCRoot );
878     msi_free( exe );
879     msi_free( arg );
880     if (handle == INVALID_HANDLE_VALUE) return ERROR_SUCCESS;
881     return wait_process_handle( package, type, handle, action );
882 }
883 
884 static UINT HANDLE_CustomType34( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target,
885                                  INT type, const WCHAR *action )
886 {
887     const WCHAR *workingdir = NULL;
888     HANDLE handle;
889     WCHAR *cmd;
890 
891     if (source)
892     {
893         workingdir = msi_get_target_folder( package, source );
894         if (!workingdir) return ERROR_FUNCTION_FAILED;
895     }
896     deformat_string( package, target, &cmd );
897     if (!cmd) return ERROR_FUNCTION_FAILED;
898 
899     TRACE("cmd %s dir %s\n", debugstr_w(cmd), debugstr_w(workingdir));
900 
901     handle = execute_command( NULL, cmd, workingdir );
902     msi_free( cmd );
903     if (handle == INVALID_HANDLE_VALUE) return ERROR_SUCCESS;
904     return wait_process_handle( package, type, handle, action );
905 }
906 
907 static DWORD ACTION_CallScript( const GUID *guid )
908 {
909     msi_custom_action_info *info;
910     MSIHANDLE hPackage;
911     UINT r = ERROR_FUNCTION_FAILED;
912 
913     info = find_action_by_guid( guid );
914     if (!info)
915     {
916         ERR("failed to find action %s\n", debugstr_guid( guid) );
917         return ERROR_FUNCTION_FAILED;
918     }
919 
920     TRACE("function %s, script %s\n", debugstr_w( info->target ), debugstr_w( info->source ) );
921 
922     hPackage = alloc_msihandle( &info->package->hdr );
923     if (hPackage)
924     {
925         r = call_script( hPackage, info->type, info->source, info->target, info->action );
926         TRACE("script returned %u\n", r);
927         MsiCloseHandle( hPackage );
928     }
929     else
930         ERR("failed to create handle for %p\n", info->package );
931 
932     release_custom_action_data( info );
933     return r;
934 }
935 
936 static DWORD WINAPI ScriptThread( LPVOID arg )
937 {
938     LPGUID guid = arg;
939     DWORD rc;
940 
941     TRACE("custom action (%x) started\n", GetCurrentThreadId() );
942 
943     rc = ACTION_CallScript( guid );
944 
945     TRACE("custom action (%x) returned %i\n", GetCurrentThreadId(), rc );
946 
947     MsiCloseAllHandles();
948     return rc;
949 }
950 
951 static msi_custom_action_info *do_msidbCustomActionTypeScript(
952     MSIPACKAGE *package, INT type, LPCWSTR script, LPCWSTR function, LPCWSTR action )
953 {
954     msi_custom_action_info *info;
955 
956     info = msi_alloc( sizeof *info );
957     if (!info)
958         return NULL;
959 
960     msiobj_addref( &package->hdr );
961     info->refs = 2; /* 1 for our caller and 1 for thread we created */
962     info->package = package;
963     info->type = type;
964     info->target = strdupW( function );
965     info->source = strdupW( script );
966     info->action = strdupW( action );
967     CoCreateGuid( &info->guid );
968 
969     EnterCriticalSection( &msi_custom_action_cs );
970     list_add_tail( &msi_pending_custom_actions, &info->entry );
971     LeaveCriticalSection( &msi_custom_action_cs );
972 
973     info->handle = CreateThread( NULL, 0, ScriptThread, &info->guid, 0, NULL );
974     if (!info->handle)
975     {
976         /* release both references */
977         release_custom_action_data( info );
978         release_custom_action_data( info );
979         return NULL;
980     }
981 
982     return info;
983 }
984 
985 static UINT HANDLE_CustomType37_38( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target,
986                                     INT type, const WCHAR *action )
987 {
988     msi_custom_action_info *info;
989 
990     TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
991 
992     info = do_msidbCustomActionTypeScript( package, type, target, NULL, action );
993     return wait_thread_handle( info );
994 }
995 
996 static UINT HANDLE_CustomType5_6( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target,
997                                   INT type, const WCHAR *action )
998 {
999     static const WCHAR query[] = {
1000         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1001         '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',' ',
1002         '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
1003     MSIRECORD *row = NULL;
1004     msi_custom_action_info *info;
1005     CHAR *buffer = NULL;
1006     WCHAR *bufferw = NULL;
1007     DWORD sz = 0;
1008     UINT r;
1009 
1010     TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1011 
1012     row = MSI_QueryGetRecord(package->db, query, source);
1013     if (!row)
1014         return ERROR_FUNCTION_FAILED;
1015 
1016     r = MSI_RecordReadStream(row, 2, NULL, &sz);
1017     if (r != ERROR_SUCCESS) goto done;
1018 
1019     buffer = msi_alloc( sz + 1 );
1020     if (!buffer)
1021     {
1022        r = ERROR_FUNCTION_FAILED;
1023        goto done;
1024     }
1025 
1026     r = MSI_RecordReadStream(row, 2, buffer, &sz);
1027     if (r != ERROR_SUCCESS)
1028         goto done;
1029 
1030     buffer[sz] = 0;
1031     bufferw = strdupAtoW(buffer);
1032     if (!bufferw)
1033     {
1034         r = ERROR_FUNCTION_FAILED;
1035         goto done;
1036     }
1037 
1038     info = do_msidbCustomActionTypeScript( package, type, bufferw, target, action );
1039     r = wait_thread_handle( info );
1040 
1041 done:
1042     msi_free(bufferw);
1043     msi_free(buffer);
1044     msiobj_release(&row->hdr);
1045     return r;
1046 }
1047 
1048 static UINT HANDLE_CustomType21_22( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target,
1049                                     INT type, const WCHAR *action )
1050 {
1051     msi_custom_action_info *info;
1052     MSIFILE *file;
1053     HANDLE hFile;
1054     DWORD sz, szHighWord = 0, read;
1055     CHAR *buffer=NULL;
1056     WCHAR *bufferw=NULL;
1057     BOOL bRet;
1058     UINT r;
1059 
1060     TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1061 
1062     file = msi_get_loaded_file(package, source);
1063     if (!file)
1064     {
1065         ERR("invalid file key %s\n", debugstr_w(source));
1066         return ERROR_FUNCTION_FAILED;
1067     }
1068 
1069     hFile = CreateFileW(file->TargetPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
1070     if (hFile == INVALID_HANDLE_VALUE) return ERROR_FUNCTION_FAILED;
1071 
1072     sz = GetFileSize(hFile, &szHighWord);
1073     if (sz == INVALID_FILE_SIZE || szHighWord != 0)
1074     {
1075         CloseHandle(hFile);
1076         return ERROR_FUNCTION_FAILED;
1077     }
1078     buffer = msi_alloc( sz + 1 );
1079     if (!buffer)
1080     {
1081         CloseHandle(hFile);
1082         return ERROR_FUNCTION_FAILED;
1083     }
1084     bRet = ReadFile(hFile, buffer, sz, &read, NULL);
1085     CloseHandle(hFile);
1086     if (!bRet)
1087     {
1088         r = ERROR_FUNCTION_FAILED;
1089         goto done;
1090     }
1091     buffer[read] = 0;
1092     bufferw = strdupAtoW(buffer);
1093     if (!bufferw)
1094     {
1095         r = ERROR_FUNCTION_FAILED;
1096         goto done;
1097     }
1098     info = do_msidbCustomActionTypeScript( package, type, bufferw, target, action );
1099     r = wait_thread_handle( info );
1100 
1101 done:
1102     msi_free(bufferw);
1103     msi_free(buffer);
1104     return r;
1105 }
1106 
1107 static UINT HANDLE_CustomType53_54( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target,
1108                                     INT type, const WCHAR *action )
1109 {
1110     msi_custom_action_info *info;
1111     WCHAR *prop;
1112 
1113     TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1114 
1115     prop = msi_dup_property( package->db, source );
1116     if (!prop) return ERROR_SUCCESS;
1117 
1118     info = do_msidbCustomActionTypeScript( package, type, prop, NULL, action );
1119     msi_free(prop);
1120     return wait_thread_handle( info );
1121 }
1122 
1123 static BOOL action_type_matches_script( UINT type, UINT script )
1124 {
1125     switch (script)
1126     {
1127     case SCRIPT_NONE:
1128     case SCRIPT_INSTALL:
1129         return !(type & msidbCustomActionTypeCommit) && !(type & msidbCustomActionTypeRollback);
1130     case SCRIPT_COMMIT:
1131         return (type & msidbCustomActionTypeCommit);
1132     case SCRIPT_ROLLBACK:
1133         return (type & msidbCustomActionTypeRollback);
1134     default:
1135         ERR("unhandled script %u\n", script);
1136     }
1137     return FALSE;
1138 }
1139 
1140 static UINT defer_custom_action( MSIPACKAGE *package, const WCHAR *action, UINT type )
1141 {
1142     WCHAR *actiondata = msi_dup_property( package->db, action );
1143     WCHAR *usersid = msi_dup_property( package->db, szUserSID );
1144     WCHAR *prodcode = msi_dup_property( package->db, szProductCode );
1145     WCHAR *deferred = msi_get_deferred_action( action, actiondata, usersid, prodcode );
1146 
1147     if (!deferred)
1148     {
1149         msi_free( actiondata );
1150         msi_free( usersid );
1151         msi_free( prodcode );
1152         return ERROR_OUTOFMEMORY;
1153     }
1154     if (type & msidbCustomActionTypeCommit)
1155     {
1156         TRACE("deferring commit action\n");
1157         msi_schedule_action( package, SCRIPT_COMMIT, deferred );
1158     }
1159     else if (type & msidbCustomActionTypeRollback)
1160     {
1161         TRACE("deferring rollback action\n");
1162         msi_schedule_action( package, SCRIPT_ROLLBACK, deferred );
1163     }
1164     else
1165     {
1166         TRACE("deferring install action\n");
1167         msi_schedule_action( package, SCRIPT_INSTALL, deferred );
1168     }
1169 
1170     msi_free( actiondata );
1171     msi_free( usersid );
1172     msi_free( prodcode );
1173     msi_free( deferred );
1174     return ERROR_SUCCESS;
1175 }
1176 
1177 UINT ACTION_CustomAction( MSIPACKAGE *package, LPCWSTR action, UINT script )
1178 {
1179     static const WCHAR query[] = {
1180         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1181         '`','C','u','s','t','o','m','A','c','t','i','o','n','`',' ','W','H','E','R','E',' ',
1182         '`','A','c','t','i' ,'o','n','`',' ','=',' ','\'','%','s','\'',0};
1183     UINT rc = ERROR_SUCCESS;
1184     MSIRECORD *row;
1185     UINT type;
1186     const WCHAR *source, *target, *ptr, *deferred_data = NULL;
1187     WCHAR *deformated = NULL;
1188     int len;
1189 
1190     /* deferred action: [properties]Action */
1191     if ((ptr = strrchrW(action, ']')))
1192     {
1193         deferred_data = action;
1194         action = ptr + 1;
1195     }
1196 
1197     row = MSI_QueryGetRecord( package->db, query, action );
1198     if (!row)
1199         return ERROR_FUNCTION_NOT_CALLED;
1200 
1201     type = MSI_RecordGetInteger(row,2);
1202     source = MSI_RecordGetString(row,3);
1203     target = MSI_RecordGetString(row,4);
1204 
1205     TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type,
1206           debugstr_w(source), debugstr_w(target));
1207 
1208     /* handle some of the deferred actions */
1209     if (type & msidbCustomActionTypeTSAware)
1210         FIXME("msidbCustomActionTypeTSAware not handled\n");
1211 
1212     if (type & msidbCustomActionTypeInScript)
1213     {
1214         if (type & msidbCustomActionTypeNoImpersonate)
1215             WARN("msidbCustomActionTypeNoImpersonate not handled\n");
1216 
1217         if (!action_type_matches_script( type, script ))
1218         {
1219             rc = defer_custom_action( package, action, type );
1220             goto end;
1221         }
1222         else
1223         {
1224             LPWSTR actiondata = msi_dup_property( package->db, action );
1225 
1226             if (type & msidbCustomActionTypeInScript)
1227                 package->scheduled_action_running = TRUE;
1228 
1229             if (type & msidbCustomActionTypeCommit)
1230                 package->commit_action_running = TRUE;
1231 
1232             if (type & msidbCustomActionTypeRollback)
1233                 package->rollback_action_running = TRUE;
1234 
1235             if (deferred_data)
1236                 set_deferred_action_props(package, deferred_data);
1237             else if (actiondata)
1238                 msi_set_property( package->db, szCustomActionData, actiondata, -1 );
1239             else
1240                 msi_set_property( package->db, szCustomActionData, szEmpty, -1 );
1241 
1242             msi_free(actiondata);
1243         }
1244     }
1245     else if (!check_execution_scheduling_options(package,action,type))
1246     {
1247         rc = ERROR_SUCCESS;
1248         goto end;
1249     }
1250 
1251     switch (type & CUSTOM_ACTION_TYPE_MASK)
1252     {
1253         case 1: /* DLL file stored in a Binary table stream */
1254             rc = HANDLE_CustomType1(package,source,target,type,action);
1255             break;
1256         case 2: /* EXE file stored in a Binary table stream */
1257             rc = HANDLE_CustomType2(package,source,target,type,action);
1258             break;
1259         case 18: /*EXE file installed with package */
1260             rc = HANDLE_CustomType18(package,source,target,type,action);
1261             break;
1262         case 19: /* Error that halts install */
1263             rc = HANDLE_CustomType19(package,source,target,type,action);
1264             break;
1265         case 17:
1266             rc = HANDLE_CustomType17(package,source,target,type,action);
1267             break;
1268         case 23: /* installs another package in the source tree */
1269             deformat_string(package,target,&deformated);
1270             rc = HANDLE_CustomType23(package,source,deformated,type,action);
1271             msi_free(deformated);
1272             break;
1273         case 50: /*EXE file specified by a property value */
1274             rc = HANDLE_CustomType50(package,source,target,type,action);
1275             break;
1276         case 34: /*EXE to be run in specified directory */
1277             rc = HANDLE_CustomType34(package,source,target,type,action);
1278             break;
1279         case 35: /* Directory set with formatted text. */
1280             deformat_string(package,target,&deformated);
1281             MSI_SetTargetPathW(package, source, deformated);
1282             msi_free(deformated);
1283             break;
1284         case 51: /* Property set with formatted text. */
1285             if (!source)
1286                 break;
1287 
1288             len = deformat_string( package, target, &deformated );
1289             rc = msi_set_property( package->db, source, deformated, len );
1290             if (rc == ERROR_SUCCESS && !strcmpW( source, szSourceDir ))
1291                 msi_reset_folders( package, TRUE );
1292             msi_free(deformated);
1293             break;
1294     case 37: /* JScript/VBScript text stored in target column. */
1295     case 38:
1296         rc = HANDLE_CustomType37_38(package,source,target,type,action);
1297         break;
1298     case 5:
1299     case 6: /* JScript/VBScript file stored in a Binary table stream. */
1300         rc = HANDLE_CustomType5_6(package,source,target,type,action);
1301         break;
1302     case 21: /* JScript/VBScript file installed with the product. */
1303     case 22:
1304         rc = HANDLE_CustomType21_22(package,source,target,type,action);
1305         break;
1306     case 53: /* JScript/VBScript text specified by a property value. */
1307     case 54:
1308         rc = HANDLE_CustomType53_54(package,source,target,type,action);
1309         break;
1310     default:
1311         FIXME("unhandled action type %u (%s %s)\n", type & CUSTOM_ACTION_TYPE_MASK,
1312               debugstr_w(source), debugstr_w(target));
1313     }
1314 
1315 end:
1316     package->scheduled_action_running = FALSE;
1317     package->commit_action_running = FALSE;
1318     package->rollback_action_running = FALSE;
1319     msiobj_release(&row->hdr);
1320     return rc;
1321 }
1322 
1323 void ACTION_FinishCustomActions(const MSIPACKAGE* package)
1324 {
1325     struct list *item;
1326     HANDLE *wait_handles;
1327     unsigned int handle_count, i;
1328     msi_custom_action_info *info, *cursor;
1329 
1330     while ((item = list_head( &package->RunningActions )))
1331     {
1332         MSIRUNNINGACTION *action = LIST_ENTRY( item, MSIRUNNINGACTION, entry );
1333 
1334         list_remove( &action->entry );
1335 
1336         TRACE("waiting for %s\n", debugstr_w( action->name ) );
1337         msi_dialog_check_messages( action->handle );
1338 
1339         CloseHandle( action->handle );
1340         msi_free( action->name );
1341         msi_free( action );
1342     }
1343 
1344     EnterCriticalSection( &msi_custom_action_cs );
1345 
1346     handle_count = list_count( &msi_pending_custom_actions );
1347     wait_handles = msi_alloc( handle_count * sizeof(HANDLE) );
1348 
1349     handle_count = 0;
1350     LIST_FOR_EACH_ENTRY_SAFE( info, cursor, &msi_pending_custom_actions, msi_custom_action_info, entry )
1351     {
1352         if (info->package == package )
1353         {
1354             if (DuplicateHandle(GetCurrentProcess(), info->handle, GetCurrentProcess(), &wait_handles[handle_count], SYNCHRONIZE, FALSE, 0))
1355                 handle_count++;
1356         }
1357     }
1358 
1359     LeaveCriticalSection( &msi_custom_action_cs );
1360 
1361     for (i = 0; i < handle_count; i++)
1362     {
1363         msi_dialog_check_messages( wait_handles[i] );
1364         CloseHandle( wait_handles[i] );
1365     }
1366     msi_free( wait_handles );
1367 
1368     EnterCriticalSection( &msi_custom_action_cs );
1369     LIST_FOR_EACH_ENTRY_SAFE( info, cursor, &msi_pending_custom_actions, msi_custom_action_info, entry )
1370     {
1371         if (info->package == package) release_custom_action_data( info );
1372     }
1373     LeaveCriticalSection( &msi_custom_action_cs );
1374 }
1375 
1376 typedef struct _msi_custom_remote_impl {
1377     IWineMsiRemoteCustomAction IWineMsiRemoteCustomAction_iface;
1378     LONG refs;
1379 } msi_custom_remote_impl;
1380 
1381 static inline msi_custom_remote_impl *impl_from_IWineMsiRemoteCustomAction( IWineMsiRemoteCustomAction *iface )
1382 {
1383     return CONTAINING_RECORD(iface, msi_custom_remote_impl, IWineMsiRemoteCustomAction_iface);
1384 }
1385 
1386 static HRESULT WINAPI mcr_QueryInterface( IWineMsiRemoteCustomAction *iface,
1387                 REFIID riid,LPVOID *ppobj)
1388 {
1389     if( IsEqualCLSID( riid, &IID_IUnknown ) ||
1390         IsEqualCLSID( riid, &IID_IWineMsiRemoteCustomAction ) )
1391     {
1392         IWineMsiRemoteCustomAction_AddRef( iface );
1393         *ppobj = iface;
1394         return S_OK;
1395     }
1396 
1397     return E_NOINTERFACE;
1398 }
1399 
1400 static ULONG WINAPI mcr_AddRef( IWineMsiRemoteCustomAction *iface )
1401 {
1402     msi_custom_remote_impl* This = impl_from_IWineMsiRemoteCustomAction( iface );
1403 
1404     return InterlockedIncrement( &This->refs );
1405 }
1406 
1407 static ULONG WINAPI mcr_Release( IWineMsiRemoteCustomAction *iface )
1408 {
1409     msi_custom_remote_impl* This = impl_from_IWineMsiRemoteCustomAction( iface );
1410     ULONG r;
1411 
1412     r = InterlockedDecrement( &This->refs );
1413     if (r == 0)
1414         msi_free( This );
1415     return r;
1416 }
1417 
1418 static HRESULT WINAPI mcr_GetActionInfo( IWineMsiRemoteCustomAction *iface, LPCGUID custom_action_guid,
1419          INT *type, MSIHANDLE *handle, BSTR *dll, BSTR *func, IWineMsiRemotePackage **remote_package )
1420 {
1421     msi_custom_action_info *info;
1422 
1423     info = find_action_by_guid( custom_action_guid );
1424     if (!info)
1425         return E_FAIL;
1426 
1427     *type = info->type;
1428     *handle = alloc_msihandle( &info->package->hdr );
1429     *dll = SysAllocString( info->source );
1430     *func = SysAllocString( info->target );
1431 
1432     release_custom_action_data( info );
1433     return create_msi_remote_package( NULL, (LPVOID *)remote_package );
1434 }
1435 
1436 static const IWineMsiRemoteCustomActionVtbl msi_custom_remote_vtbl =
1437 {
1438     mcr_QueryInterface,
1439     mcr_AddRef,
1440     mcr_Release,
1441     mcr_GetActionInfo,
1442 };
1443 
1444 HRESULT create_msi_custom_remote( IUnknown *pOuter, LPVOID *ppObj )
1445 {
1446     msi_custom_remote_impl* This;
1447 
1448     This = msi_alloc( sizeof *This );
1449     if (!This)
1450         return E_OUTOFMEMORY;
1451 
1452     This->IWineMsiRemoteCustomAction_iface.lpVtbl = &msi_custom_remote_vtbl;
1453     This->refs = 1;
1454 
1455     *ppObj = &This->IWineMsiRemoteCustomAction_iface;
1456 
1457     return S_OK;
1458 }
1459