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