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