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