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