crypt32: Introduce function to encode an array of items as a set.
[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[] = {'[','%','s',';','%','s',';','%','s',']','%','s',0};
149
150     if (!actiondata)
151         return strdupW(action);
152
153     len = lstrlenW(action) + lstrlenW(actiondata) +
154           lstrlenW(usersid) + lstrlenW(prodcode) + 5;
155     deferred = msi_alloc(len * sizeof(WCHAR));
156
157     sprintfW(deferred, format, actiondata, usersid, prodcode, action);
158     return deferred;
159 }
160
161 static void set_deferred_action_props(MSIPACKAGE *package, LPWSTR deferred_data)
162 {
163     LPWSTR end, beg = deferred_data + 1;
164
165     end = strchrW(beg, ';');
166     *end = '\0';
167     MSI_SetPropertyW(package, szActionData, beg);
168     beg = end + 1;
169
170     end = strchrW(beg, ';');
171     *end = '\0';
172     MSI_SetPropertyW(package, UserSID, beg);
173     beg = end + 1;
174
175     end = strchrW(beg, ']');
176     *end = '\0';
177     MSI_SetPropertyW(package, ProdCode, beg);
178 }
179
180 UINT ACTION_CustomAction(MSIPACKAGE *package, LPCWSTR action, UINT script, BOOL execute)
181 {
182     UINT rc = ERROR_SUCCESS;
183     MSIRECORD * row = 0;
184     static const WCHAR ExecSeqQuery[] =
185     {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
186      '`','C','u','s','t','o' ,'m','A','c','t','i','o','n','`',
187      ' ','W','H','E','R','E',' ','`','A','c','t','i' ,'o','n','`',' ',
188      '=',' ','\'','%','s','\'',0};
189     UINT type;
190     LPCWSTR source, target;
191     LPWSTR ptr, deferred_data = NULL;
192     LPWSTR action_copy = strdupW(action);
193     WCHAR *deformated=NULL;
194
195     /* deferred action: [properties]Action */
196     if ((ptr = strchrW(action_copy, ']')))
197     {
198         deferred_data = action_copy;
199         action = ptr + 1;
200     }
201
202     row = MSI_QueryGetRecord( package->db, ExecSeqQuery, action );
203     if (!row)
204     {
205         msi_free(action_copy);
206         return ERROR_CALL_NOT_IMPLEMENTED;
207     }
208
209     type = MSI_RecordGetInteger(row,2);
210
211     source = MSI_RecordGetString(row,3);
212     target = MSI_RecordGetString(row,4);
213
214     TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type,
215           debugstr_w(source), debugstr_w(target));
216
217     /* handle some of the deferred actions */
218     if (type & msidbCustomActionTypeTSAware)
219         FIXME("msidbCustomActionTypeTSAware not handled\n");
220
221     if (type & msidbCustomActionTypeInScript)
222     {
223         if (type & msidbCustomActionTypeNoImpersonate)
224             FIXME("msidbCustomActionTypeNoImpersonate not handled\n");
225
226         if (type & msidbCustomActionTypeRollback)
227         {
228             FIXME("Rollback only action... rollbacks not supported yet\n");
229             schedule_action(package, ROLLBACK_SCRIPT, action);
230             rc = ERROR_SUCCESS;
231             goto end;
232         }
233         if (!execute)
234         {
235             LPWSTR actiondata = msi_dup_property(package, action);
236             LPWSTR usersid = msi_dup_property(package, UserSID);
237             LPWSTR prodcode = msi_dup_property(package, ProdCode);
238             LPWSTR deferred = msi_get_deferred_action(action, actiondata, usersid, prodcode);
239
240             if (type & msidbCustomActionTypeCommit)
241             {
242                 TRACE("Deferring Commit Action!\n");
243                 schedule_action(package, COMMIT_SCRIPT, deferred);
244             }
245             else
246             {
247                 TRACE("Deferring Action!\n");
248                 schedule_action(package, INSTALL_SCRIPT, deferred);
249             }
250
251             rc = ERROR_SUCCESS;
252             msi_free(actiondata);
253             msi_free(usersid);
254             msi_free(prodcode);
255             msi_free(deferred);
256             goto end;
257         }
258         else
259         {
260             static const WCHAR szBlank[] = {0};
261
262             LPWSTR actiondata = msi_dup_property( package, action );
263
264             switch (script)
265             {
266             case INSTALL_SCRIPT:
267                 package->scheduled_action_running = TRUE;
268                 break;
269             case COMMIT_SCRIPT:
270                 package->commit_action_running = TRUE;
271                 break;
272             case ROLLBACK_SCRIPT:
273                 package->rollback_action_running = TRUE;
274                 break;
275             default:
276                 break;
277             }
278
279             if (deferred_data)
280                 set_deferred_action_props(package, deferred_data);
281             else if (actiondata)
282                 MSI_SetPropertyW(package,szActionData,actiondata);
283             else
284                 MSI_SetPropertyW(package,szActionData,szBlank);
285
286             msi_free(actiondata);
287         }
288     }
289     else if (!check_execution_scheduling_options(package,action,type))
290     {
291         rc = ERROR_SUCCESS;
292         goto end;
293     }
294
295     switch (type & CUSTOM_ACTION_TYPE_MASK)
296     {
297         case 1: /* DLL file stored in a Binary table stream */
298             rc = HANDLE_CustomType1(package,source,target,type,action);
299             break;
300         case 2: /* EXE file stored in a Binary table stream */
301             rc = HANDLE_CustomType2(package,source,target,type,action);
302             break;
303         case 18: /*EXE file installed with package */
304             rc = HANDLE_CustomType18(package,source,target,type,action);
305             break;
306         case 19: /* Error that halts install */
307             rc = HANDLE_CustomType19(package,source,target,type,action);
308             break;
309         case 17:
310             rc = HANDLE_CustomType17(package,source,target,type,action);
311             break;
312         case 23: /* installs another package in the source tree */
313             deformat_string(package,target,&deformated);
314             rc = HANDLE_CustomType23(package,source,deformated,type,action);
315             break;
316         case 50: /*EXE file specified by a property value */
317             rc = HANDLE_CustomType50(package,source,target,type,action);
318             break;
319         case 34: /*EXE to be run in specified directory */
320             rc = HANDLE_CustomType34(package,source,target,type,action);
321             break;
322         case 35: /* Directory set with formatted text. */
323             deformat_string(package,target,&deformated);
324             MSI_SetTargetPathW(package, source, deformated);
325             msi_free(deformated);
326             break;
327         case 51: /* Property set with formatted text. */
328             deformat_string(package,target,&deformated);
329             rc = MSI_SetPropertyW(package,source,deformated);
330             msi_free(deformated);
331             break;
332         case 37: /* JScript/VBScript text stored in target column. */
333         case 38:
334             rc = HANDLE_CustomType37_38(package,source,target,type,action);
335             break;
336         case 5:
337         case 6: /* JScript/VBScript file stored in a Binary table stream. */
338             rc = HANDLE_CustomType5_6(package,source,target,type,action);
339             break;
340         case 21: /* JScript/VBScript file installed with the product. */
341         case 22:
342             rc = HANDLE_CustomType21_22(package,source,target,type,action);
343             break;
344         case 53: /* JScript/VBScript text specified by a property value. */
345         case 54:
346             rc = HANDLE_CustomType53_54(package,source,target,type,action);
347             break;
348         default:
349             FIXME("UNHANDLED ACTION TYPE %i (%s %s)\n",
350              type & CUSTOM_ACTION_TYPE_MASK, debugstr_w(source),
351              debugstr_w(target));
352     }
353
354 end:
355     package->scheduled_action_running = FALSE;
356     package->commit_action_running = FALSE;
357     package->rollback_action_running = FALSE;
358     msi_free(action_copy);
359     msiobj_release(&row->hdr);
360     return rc;
361 }
362
363
364 static UINT store_binary_to_temp(MSIPACKAGE *package, LPCWSTR source,
365                                 LPWSTR tmp_file)
366 {
367     static const WCHAR query[] = {
368         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
369         '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',' ',
370         '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
371     MSIRECORD *row = 0;
372     HANDLE file;
373     CHAR buffer[1024];
374     static const WCHAR f1[] = {'m','s','i',0};
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, f1, 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 WINAPI 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\n", debugstr_w( dll ) );
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 WINAPI 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
843     static const WCHAR backslash[] = {'\\',0};
844
845     MSI_GetPropertyW(package, cszSourceDir, package_path, &size);
846     lstrcatW(package_path, backslash);
847     lstrcatW(package_path, source);
848
849     if (GetFileAttributesW(package_path) == INVALID_FILE_ATTRIBUTES)
850     {
851         ERR("Source package does not exist: %s\n", debugstr_w(package_path));
852         return ERROR_FUNCTION_FAILED;
853     }
854
855     TRACE("Installing package %s concurrently\n", debugstr_w(package_path));
856
857     info = do_msidbCAConcurrentInstall(package, type, package_path, target, action);
858
859     return wait_thread_handle(info);
860 }
861
862 static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source,
863                                LPCWSTR target, const INT type, LPCWSTR action)
864 {
865     msi_custom_action_info *info;
866     WCHAR tmp_file[MAX_PATH];
867     UINT r;
868
869     r = store_binary_to_temp(package, source, tmp_file);
870     if (r != ERROR_SUCCESS)
871         return r;
872
873     TRACE("Calling function %s from %s\n",debugstr_w(target),
874           debugstr_w(tmp_file));
875
876     if (!strchrW(tmp_file,'.'))
877     {
878         static const WCHAR dot[]={'.',0};
879         strcatW(tmp_file,dot);
880     }
881
882     info = do_msidbCustomActionTypeDll( package, type, tmp_file, target, action );
883
884     return wait_thread_handle( info );
885 }
886
887 static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source,
888                                LPCWSTR target, const INT type, LPCWSTR action)
889 {
890     WCHAR tmp_file[MAX_PATH];
891     STARTUPINFOW si;
892     PROCESS_INFORMATION info;
893     BOOL rc;
894     INT len;
895     WCHAR *deformated = NULL;
896     WCHAR *cmd;
897     static const WCHAR spc[] = {' ',0};
898     UINT r;
899
900     memset(&si,0,sizeof(STARTUPINFOW));
901
902     r = store_binary_to_temp(package, source, tmp_file);
903     if (r != ERROR_SUCCESS)
904         return r;
905
906     deformat_string(package,target,&deformated);
907
908     len = strlenW(tmp_file)+2;
909
910     if (deformated)
911         len += strlenW(deformated);
912
913     cmd = msi_alloc(sizeof(WCHAR)*len);
914
915     strcpyW(cmd,tmp_file);
916     if (deformated)
917     {
918         strcatW(cmd,spc);
919         strcatW(cmd,deformated);
920
921         msi_free(deformated);
922     }
923
924     TRACE("executing exe %s\n", debugstr_w(cmd));
925
926     rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
927                   c_collen, &si, &info);
928     msi_free(cmd);
929
930     if ( !rc )
931     {
932         ERR("Unable to execute command %s\n", debugstr_w(cmd));
933         return ERROR_SUCCESS;
934     }
935     CloseHandle( info.hThread );
936
937     r = wait_process_handle(package, type, info.hProcess, action);
938
939     return r;
940 }
941
942 static UINT HANDLE_CustomType17(MSIPACKAGE *package, LPCWSTR source,
943                                 LPCWSTR target, const INT type, LPCWSTR action)
944 {
945     msi_custom_action_info *info;
946     MSIFILE *file;
947
948     TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
949
950     file = get_loaded_file( package, source );
951     if (!file)
952     {
953         ERR("invalid file key %s\n", debugstr_w( source ));
954         return ERROR_FUNCTION_FAILED;
955     }
956
957     info = do_msidbCustomActionTypeDll( package, type, file->TargetPath, target, action );
958
959     return wait_thread_handle( info );
960 }
961
962 static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source,
963                                 LPCWSTR target, const INT type, LPCWSTR action)
964 {
965     STARTUPINFOW si;
966     PROCESS_INFORMATION info;
967     BOOL rc;
968     WCHAR *deformated;
969     WCHAR *cmd;
970     INT len;
971     static const WCHAR spc[] = {' ',0};
972     MSIFILE *file;
973
974     memset(&si,0,sizeof(STARTUPINFOW));
975
976     file = get_loaded_file(package,source);
977     if( !file )
978         return ERROR_FUNCTION_FAILED;
979
980     len = lstrlenW( file->TargetPath );
981
982     deformat_string(package,target,&deformated);
983     if (deformated)
984         len += strlenW(deformated);
985     len += 2;
986
987     cmd = msi_alloc(len * sizeof(WCHAR));
988
989     lstrcpyW( cmd, file->TargetPath);
990     if (deformated)
991     {
992         strcatW(cmd, spc);
993         strcatW(cmd, deformated);
994
995         msi_free(deformated);
996     }
997
998     TRACE("executing exe %s\n", debugstr_w(cmd));
999
1000     rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
1001                   c_collen, &si, &info);
1002
1003     if ( !rc )
1004     {
1005         ERR("Unable to execute command %s\n", debugstr_w(cmd));
1006         msi_free(cmd);
1007         return ERROR_SUCCESS;
1008     }
1009     msi_free(cmd);
1010     CloseHandle( info.hThread );
1011
1012     return wait_process_handle(package, type, info.hProcess, action);
1013 }
1014
1015 static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
1016                                 LPCWSTR target, const INT type, LPCWSTR action)
1017 {
1018     static const WCHAR query[] = {
1019       'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
1020       'F','R','O','M',' ','`','E','r','r','o','r','`',' ',
1021       'W','H','E','R','E',' ','`','E','r','r','o','r','`',' ','=',' ',
1022       '%','s',0
1023     };
1024     MSIRECORD *row = 0;
1025     LPWSTR deformated = NULL;
1026
1027     deformat_string( package, target, &deformated );
1028
1029     /* first try treat the error as a number */
1030     row = MSI_QueryGetRecord( package->db, query, deformated );
1031     if( row )
1032     {
1033         LPCWSTR error = MSI_RecordGetString( row, 1 );
1034         MessageBoxW( NULL, error, NULL, MB_OK );
1035         msiobj_release( &row->hdr );
1036     }
1037     else
1038         MessageBoxW( NULL, deformated, NULL, MB_OK );
1039
1040     msi_free( deformated );
1041
1042     return ERROR_FUNCTION_FAILED;
1043 }
1044
1045 static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source,
1046                                 LPCWSTR target, const INT type, LPCWSTR action)
1047 {
1048     STARTUPINFOW si;
1049     PROCESS_INFORMATION info;
1050     WCHAR *prop;
1051     BOOL rc;
1052     WCHAR *deformated;
1053     WCHAR *cmd;
1054     INT len;
1055     static const WCHAR spc[] = {' ',0};
1056
1057     memset(&si,0,sizeof(STARTUPINFOW));
1058     memset(&info,0,sizeof(PROCESS_INFORMATION));
1059
1060     prop = msi_dup_property( package, source );
1061     if (!prop)
1062         return ERROR_SUCCESS;
1063
1064     deformat_string(package,target,&deformated);
1065     len = strlenW(prop) + 2;
1066     if (deformated)
1067          len += strlenW(deformated);
1068
1069     cmd = msi_alloc(sizeof(WCHAR)*len);
1070
1071     strcpyW(cmd,prop);
1072     if (deformated)
1073     {
1074         strcatW(cmd,spc);
1075         strcatW(cmd,deformated);
1076
1077         msi_free(deformated);
1078     }
1079     msi_free(prop);
1080
1081     TRACE("executing exe %s\n", debugstr_w(cmd));
1082
1083     rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
1084                   c_collen, &si, &info);
1085
1086     if ( !rc )
1087     {
1088         ERR("Unable to execute command %s\n", debugstr_w(cmd));
1089         msi_free(cmd);
1090         return ERROR_SUCCESS;
1091     }
1092     msi_free(cmd);
1093
1094     CloseHandle( info.hThread );
1095
1096     return wait_process_handle(package, type, info.hProcess, action);
1097 }
1098
1099 static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
1100                                 LPCWSTR target, const INT type, LPCWSTR action)
1101 {
1102     LPWSTR filename, deformated;
1103     STARTUPINFOW si;
1104     PROCESS_INFORMATION info;
1105     BOOL rc;
1106
1107     memset(&si,0,sizeof(STARTUPINFOW));
1108
1109     filename = resolve_folder(package, source, FALSE, FALSE, TRUE, NULL);
1110
1111     if (!filename)
1112         return ERROR_FUNCTION_FAILED;
1113
1114     SetCurrentDirectoryW(filename);
1115     msi_free(filename);
1116
1117     deformat_string(package,target,&deformated);
1118
1119     if (!deformated)
1120         return ERROR_FUNCTION_FAILED;
1121
1122     TRACE("executing exe %s\n", debugstr_w(deformated));
1123
1124     rc = CreateProcessW(NULL, deformated, NULL, NULL, FALSE, 0, NULL,
1125                   c_collen, &si, &info);
1126
1127     if ( !rc )
1128     {
1129         ERR("Unable to execute command %s\n", debugstr_w(deformated));
1130         msi_free(deformated);
1131         return ERROR_SUCCESS;
1132     }
1133     msi_free(deformated);
1134     CloseHandle( info.hThread );
1135
1136     return wait_process_handle(package, type, info.hProcess, action);
1137 }
1138
1139 static DWORD WINAPI ACTION_CallScript( const GUID *guid )
1140 {
1141     msi_custom_action_info *info;
1142     MSIHANDLE hPackage;
1143     UINT r = ERROR_FUNCTION_FAILED;
1144
1145     info = find_action_by_guid( guid );
1146     if (!info)
1147     {
1148         ERR("failed to find action %s\n", debugstr_guid( guid) );
1149         return r;
1150     }
1151
1152     TRACE("function %s, script %s\n", debugstr_w( info->target ), debugstr_w( info->source ) );
1153
1154     hPackage = alloc_msihandle( &info->package->hdr );
1155     if (hPackage)
1156     {
1157         r = call_script( hPackage, info->type, info->source, info->target, info->action );
1158         MsiCloseHandle( hPackage );
1159     }
1160     else
1161         ERR("failed to create handle for %p\n", info->package );
1162
1163     if (info->type & msidbCustomActionTypeAsync &&
1164         info->type & msidbCustomActionTypeContinue)
1165         release_custom_action_data( info );
1166
1167     return S_OK;
1168 }
1169
1170 static DWORD WINAPI ScriptThread( LPVOID arg )
1171 {
1172     LPGUID guid = arg;
1173     DWORD rc = 0;
1174
1175     TRACE("custom action (%x) started\n", GetCurrentThreadId() );
1176
1177     rc = ACTION_CallScript( guid );
1178
1179     TRACE("custom action (%x) returned %i\n", GetCurrentThreadId(), rc );
1180
1181     MsiCloseAllHandles();
1182     return rc;
1183 }
1184
1185 static msi_custom_action_info *do_msidbCustomActionTypeScript(
1186     MSIPACKAGE *package, INT type, LPCWSTR script, LPCWSTR function, LPCWSTR action )
1187 {
1188     msi_custom_action_info *info;
1189
1190     info = msi_alloc( sizeof *info );
1191     if (!info)
1192         return NULL;
1193
1194     msiobj_addref( &package->hdr );
1195     info->refs = 2; /* 1 for our caller and 1 for thread we created */
1196     info->package = package;
1197     info->type = type;
1198     info->target = strdupW( function );
1199     info->source = strdupW( script );
1200     info->action = strdupW( action );
1201     CoCreateGuid( &info->guid );
1202
1203     EnterCriticalSection( &msi_custom_action_cs );
1204     list_add_tail( &msi_pending_custom_actions, &info->entry );
1205     LeaveCriticalSection( &msi_custom_action_cs );
1206
1207     info->handle = CreateThread( NULL, 0, ScriptThread, &info->guid, 0, NULL );
1208     if (!info->handle)
1209     {
1210         /* release both references */
1211         release_custom_action_data( info );
1212         release_custom_action_data( info );
1213         return NULL;
1214     }
1215
1216     return info;
1217 }
1218
1219 static UINT HANDLE_CustomType37_38(MSIPACKAGE *package, LPCWSTR source,
1220                                LPCWSTR target, const INT type, LPCWSTR action)
1221 {
1222     msi_custom_action_info *info;
1223
1224     TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1225
1226     info = do_msidbCustomActionTypeScript( package, type, target, NULL, action );
1227
1228     return wait_thread_handle( info );
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
1274 done:
1275     msi_free(bufferw);
1276     msi_free(buffer);
1277     return r;
1278 }
1279
1280 static UINT HANDLE_CustomType21_22(MSIPACKAGE *package, LPCWSTR source,
1281                                LPCWSTR target, const INT type, LPCWSTR action)
1282 {
1283     msi_custom_action_info *info;
1284     MSIFILE *file;
1285     HANDLE hFile;
1286     DWORD sz, szHighWord = 0, read;
1287     CHAR *buffer=NULL;
1288     WCHAR *bufferw=NULL;
1289     BOOL bRet;
1290     UINT r;
1291
1292     TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1293
1294     file = get_loaded_file(package,source);
1295     if (!file)
1296     {
1297         ERR("invalid file key %s\n", debugstr_w(source));
1298         return ERROR_FUNCTION_FAILED;
1299     }
1300
1301     hFile = CreateFileW(file->TargetPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
1302     if (hFile == INVALID_HANDLE_VALUE)
1303         return ERROR_FUNCTION_FAILED;
1304
1305     sz = GetFileSize(hFile, &szHighWord);
1306     if (sz == INVALID_FILE_SIZE || szHighWord != 0)
1307     {
1308         CloseHandle(hFile);
1309         return ERROR_FUNCTION_FAILED;
1310     }
1311
1312     buffer = msi_alloc(sizeof(CHAR)*(sz+1));
1313     if (!buffer)
1314     {
1315         CloseHandle(hFile);
1316         return ERROR_FUNCTION_FAILED;
1317     }
1318
1319     bRet = ReadFile(hFile, buffer, sz, &read, NULL);
1320     CloseHandle(hFile);
1321     if (!bRet)
1322     {
1323         r = ERROR_FUNCTION_FAILED;
1324         goto done;
1325     }
1326
1327     buffer[read] = 0;
1328     bufferw = strdupAtoW(buffer);
1329     if (!bufferw)
1330     {
1331         r = ERROR_FUNCTION_FAILED;
1332         goto done;
1333     }
1334
1335     info = do_msidbCustomActionTypeScript( package, type, bufferw, target, action );
1336     r = wait_thread_handle( info );
1337
1338 done:
1339     msi_free(bufferw);
1340     msi_free(buffer);
1341     return r;
1342 }
1343
1344 static UINT HANDLE_CustomType53_54(MSIPACKAGE *package, LPCWSTR source,
1345                                LPCWSTR target, const INT type, LPCWSTR action)
1346 {
1347     msi_custom_action_info *info;
1348     WCHAR *prop;
1349
1350     TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1351
1352     prop = msi_dup_property(package,source);
1353     if (!prop)
1354         return ERROR_SUCCESS;
1355
1356     info = do_msidbCustomActionTypeScript( package, type, prop, NULL, action );
1357     msi_free(prop);
1358     return wait_thread_handle( info );
1359 }
1360
1361 void ACTION_FinishCustomActions(const MSIPACKAGE* package)
1362 {
1363     struct list *item;
1364     HANDLE *wait_handles;
1365     unsigned int handle_count, i;
1366     msi_custom_action_info *info, *cursor;
1367
1368     while ((item = list_head( &package->RunningActions )))
1369     {
1370         MSIRUNNINGACTION *action = LIST_ENTRY( item, MSIRUNNINGACTION, entry );
1371
1372         list_remove( &action->entry );
1373
1374         TRACE("waiting for %s\n", debugstr_w( action->name ) );
1375         msi_dialog_check_messages( action->handle );
1376
1377         CloseHandle( action->handle );
1378         msi_free( action->name );
1379         msi_free( action );
1380     }
1381
1382     EnterCriticalSection( &msi_custom_action_cs );
1383
1384     handle_count = list_count( &msi_pending_custom_actions );
1385     wait_handles = HeapAlloc( GetProcessHeap(), 0, handle_count * sizeof(HANDLE) );
1386
1387     handle_count = 0;
1388     LIST_FOR_EACH_ENTRY_SAFE( info, cursor, &msi_pending_custom_actions, msi_custom_action_info, entry )
1389     {
1390         if (info->package == package )
1391         {
1392             if (DuplicateHandle(GetCurrentProcess(), info->handle, GetCurrentProcess(), &wait_handles[handle_count], SYNCHRONIZE, FALSE, 0))
1393                 handle_count++;
1394         }
1395     }
1396
1397     LeaveCriticalSection( &msi_custom_action_cs );
1398
1399     for (i = 0; i < handle_count; i++)
1400     {
1401         msi_dialog_check_messages( wait_handles[i] );
1402         CloseHandle( wait_handles[i] );
1403     }
1404
1405     HeapFree( GetProcessHeap(), 0, wait_handles );
1406 }
1407
1408 typedef struct _msi_custom_remote_impl {
1409     const IWineMsiRemoteCustomActionVtbl *lpVtbl;
1410     LONG refs;
1411 } msi_custom_remote_impl;
1412
1413 static inline msi_custom_remote_impl* mcr_from_IWineMsiRemoteCustomAction( IWineMsiRemoteCustomAction* iface )
1414 {
1415     return (msi_custom_remote_impl*) iface;
1416 }
1417
1418 static HRESULT WINAPI mcr_QueryInterface( IWineMsiRemoteCustomAction *iface,
1419                 REFIID riid,LPVOID *ppobj)
1420 {
1421     if( IsEqualCLSID( riid, &IID_IUnknown ) ||
1422         IsEqualCLSID( riid, &IID_IWineMsiRemoteCustomAction ) )
1423     {
1424         IUnknown_AddRef( iface );
1425         *ppobj = iface;
1426         return S_OK;
1427     }
1428
1429     return E_NOINTERFACE;
1430 }
1431
1432 static ULONG WINAPI mcr_AddRef( IWineMsiRemoteCustomAction *iface )
1433 {
1434     msi_custom_remote_impl* This = mcr_from_IWineMsiRemoteCustomAction( iface );
1435
1436     return InterlockedIncrement( &This->refs );
1437 }
1438
1439 static ULONG WINAPI mcr_Release( IWineMsiRemoteCustomAction *iface )
1440 {
1441     msi_custom_remote_impl* This = mcr_from_IWineMsiRemoteCustomAction( iface );
1442     ULONG r;
1443
1444     r = InterlockedDecrement( &This->refs );
1445     if (r == 0)
1446         msi_free( This );
1447     return r;
1448 }
1449
1450 static HRESULT WINAPI mcr_GetActionInfo( IWineMsiRemoteCustomAction *iface, LPCGUID custom_action_guid,
1451          INT *type, MSIHANDLE *handle, BSTR *dll, BSTR *func, IWineMsiRemotePackage **remote_package )
1452 {
1453     msi_custom_action_info *info;
1454
1455     info = find_action_by_guid( custom_action_guid );
1456     if (!info)
1457         return E_FAIL;
1458
1459     *type = info->type;
1460     *handle = alloc_msihandle( &info->package->hdr );
1461     *dll = SysAllocString( info->source );
1462     *func = SysAllocString( info->target );
1463
1464     release_custom_action_data( info );
1465     return create_msi_remote_package( NULL, (LPVOID *)remote_package );
1466 }
1467
1468 static const IWineMsiRemoteCustomActionVtbl msi_custom_remote_vtbl =
1469 {
1470     mcr_QueryInterface,
1471     mcr_AddRef,
1472     mcr_Release,
1473     mcr_GetActionInfo,
1474 };
1475
1476 HRESULT create_msi_custom_remote( IUnknown *pOuter, LPVOID *ppObj )
1477 {
1478     msi_custom_remote_impl* This;
1479
1480     This = msi_alloc( sizeof *This );
1481     if (!This)
1482         return E_OUTOFMEMORY;
1483
1484     This->lpVtbl = &msi_custom_remote_vtbl;
1485     This->refs = 1;
1486
1487     *ppObj = This;
1488
1489     return S_OK;
1490 }