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