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