msi: Reference count the custom action data to avoid freeing the data by another...
[wine.git] / dlls / msi / custom.c
blobcd38af6fd7af1bc6458e96f844e6a2fcb8e7d8ac
1 /*
2 * Custom Action processing for the Microsoft Installer (msi.dll)
4 * Copyright 2005 Aric Stewart for CodeWeavers
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.
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.
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
21 #define COBJMACROS
23 #include <stdarg.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winerror.h"
27 #include "msidefs.h"
28 #include "winuser.h"
30 #include "msipriv.h"
31 #include "wine/debug.h"
32 #include "wine/unicode.h"
33 #include "wine/exception.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(msi);
37 #define CUSTOM_ACTION_TYPE_MASK 0x3F
38 static const WCHAR c_collen[] = {'C',':','\\',0};
39 static const WCHAR cszTempFolder[]= {'T','e','m','p','F','o','l','d','e','r',0};
42 static const WCHAR szActionData[] = {
43 'C','u','s','t','o','m','A','c','t','i','o','n','D','a','t','a',0
45 static const WCHAR ProdCode[] = {
46 'P','r','o','d','u','c','t','C','o','d','e',0
48 static const WCHAR UserSID[] = {'U','s','e','r','S','I','D',0};
50 typedef struct tagMSIRUNNINGACTION
52 struct list entry;
53 HANDLE handle;
54 BOOL process;
55 LPWSTR name;
56 } MSIRUNNINGACTION;
58 static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source,
59 LPCWSTR target, const INT type, LPCWSTR action);
60 static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source,
61 LPCWSTR target, const INT type, LPCWSTR action);
62 static UINT HANDLE_CustomType17(MSIPACKAGE *package, LPCWSTR source,
63 LPCWSTR target, const INT type, LPCWSTR action);
64 static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source,
65 LPCWSTR target, const INT type, LPCWSTR action);
66 static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
67 LPCWSTR target, const INT type, LPCWSTR action);
68 static UINT HANDLE_CustomType23(MSIPACKAGE *package, LPCWSTR source,
69 LPCWSTR target, const INT type, LPCWSTR action);
70 static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source,
71 LPCWSTR target, const INT type, LPCWSTR action);
72 static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
73 LPCWSTR target, const INT type, LPCWSTR action);
74 static UINT HANDLE_CustomType37_38(MSIPACKAGE *package, LPCWSTR source,
75 LPCWSTR target, const INT type, LPCWSTR action);
76 static UINT HANDLE_CustomType5_6(MSIPACKAGE *package, LPCWSTR source,
77 LPCWSTR target, const INT type, LPCWSTR action);
78 static UINT HANDLE_CustomType21_22(MSIPACKAGE *package, LPCWSTR source,
79 LPCWSTR target, const INT type, LPCWSTR action);
80 static UINT HANDLE_CustomType53_54(MSIPACKAGE *package, LPCWSTR source,
81 LPCWSTR target, const INT type, LPCWSTR action);
83 typedef UINT (WINAPI *MsiCustomActionEntryPoint)( MSIHANDLE );
85 static CRITICAL_SECTION msi_custom_action_cs;
86 static CRITICAL_SECTION_DEBUG msi_custom_action_cs_debug =
88 0, 0, &msi_custom_action_cs,
89 { &msi_custom_action_cs_debug.ProcessLocksList,
90 &msi_custom_action_cs_debug.ProcessLocksList },
91 0, 0, { (DWORD_PTR)(__FILE__ ": msi_custom_action_cs") }
93 static CRITICAL_SECTION msi_custom_action_cs = { &msi_custom_action_cs_debug, -1, 0, 0, 0, 0 };
95 static struct list msi_pending_custom_actions = LIST_INIT( msi_pending_custom_actions );
97 static BOOL check_execution_scheduling_options(MSIPACKAGE *package, LPCWSTR action, UINT options)
99 if (!package->script)
100 return TRUE;
102 if ((options & msidbCustomActionTypeClientRepeat) ==
103 msidbCustomActionTypeClientRepeat)
105 if (!(package->script->InWhatSequence & SEQUENCE_UI &&
106 package->script->InWhatSequence & SEQUENCE_EXEC))
108 TRACE("Skipping action due to dbCustomActionTypeClientRepeat option.\n");
109 return FALSE;
112 else if (options & msidbCustomActionTypeFirstSequence)
114 if (package->script->InWhatSequence & SEQUENCE_UI &&
115 package->script->InWhatSequence & SEQUENCE_EXEC )
117 TRACE("Skipping action due to msidbCustomActionTypeFirstSequence option.\n");
118 return FALSE;
121 else if (options & msidbCustomActionTypeOncePerProcess)
123 if (check_unique_action(package,action))
125 TRACE("Skipping action due to msidbCustomActionTypeOncePerProcess option.\n");
126 return FALSE;
128 else
129 register_unique_action(package,action);
132 return TRUE;
135 /* stores the following properties before the action:
137 * [CustomActionData;UserSID;ProductCode]Action
139 static LPWSTR msi_get_deferred_action(LPCWSTR action, LPCWSTR actiondata,
140 LPCWSTR usersid, LPCWSTR prodcode)
142 LPWSTR deferred;
143 DWORD len;
145 static const WCHAR format[] = {'[','%','s',';','%','s',';','%','s',']','%','s',0};
147 if (!actiondata)
148 return strdupW(action);
150 len = lstrlenW(action) + lstrlenW(actiondata) +
151 lstrlenW(usersid) + lstrlenW(prodcode) + 5;
152 deferred = msi_alloc(len * sizeof(WCHAR));
154 sprintfW(deferred, format, actiondata, usersid, prodcode, action);
155 return deferred;
158 static void set_deferred_action_props(MSIPACKAGE *package, LPWSTR deferred_data)
160 LPWSTR end, beg = deferred_data + 1;
162 end = strchrW(beg, ';');
163 *end = '\0';
164 MSI_SetPropertyW(package, szActionData, beg);
165 beg = end + 1;
167 end = strchrW(beg, ';');
168 *end = '\0';
169 MSI_SetPropertyW(package, UserSID, beg);
170 beg = end + 1;
172 end = strchrW(beg, ']');
173 *end = '\0';
174 MSI_SetPropertyW(package, ProdCode, beg);
177 UINT ACTION_CustomAction(MSIPACKAGE *package, LPCWSTR action, UINT script, BOOL execute)
179 UINT rc = ERROR_SUCCESS;
180 MSIRECORD * row = 0;
181 static const WCHAR ExecSeqQuery[] =
182 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
183 '`','C','u','s','t','o' ,'m','A','c','t','i','o','n','`',
184 ' ','W','H','E','R','E',' ','`','A','c','t','i' ,'o','n','`',' ',
185 '=',' ','\'','%','s','\'',0};
186 UINT type;
187 LPCWSTR source, target;
188 LPWSTR ptr, deferred_data = NULL;
189 LPWSTR action_copy = strdupW(action);
190 WCHAR *deformated=NULL;
192 /* deferred action: [properties]Action */
193 if ((ptr = strchrW(action_copy, ']')))
195 deferred_data = action_copy;
196 action = ptr + 1;
199 row = MSI_QueryGetRecord( package->db, ExecSeqQuery, action );
200 if (!row)
202 msi_free(action_copy);
203 return ERROR_CALL_NOT_IMPLEMENTED;
206 type = MSI_RecordGetInteger(row,2);
208 source = MSI_RecordGetString(row,3);
209 target = MSI_RecordGetString(row,4);
211 TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type,
212 debugstr_w(source), debugstr_w(target));
214 /* handle some of the deferred actions */
215 if (type & msidbCustomActionTypeTSAware)
216 FIXME("msidbCustomActionTypeTSAware not handled\n");
218 if (type & msidbCustomActionTypeInScript)
220 if (type & msidbCustomActionTypeNoImpersonate)
221 FIXME("msidbCustomActionTypeNoImpersonate not handled\n");
223 if (type & msidbCustomActionTypeRollback)
225 FIXME("Rollback only action... rollbacks not supported yet\n");
226 schedule_action(package, ROLLBACK_SCRIPT, action);
227 rc = ERROR_SUCCESS;
228 goto end;
230 if (!execute)
232 LPWSTR actiondata = msi_dup_property(package, action);
233 LPWSTR usersid = msi_dup_property(package, UserSID);
234 LPWSTR prodcode = msi_dup_property(package, ProdCode);
235 LPWSTR deferred = msi_get_deferred_action(action, actiondata, usersid, prodcode);
237 if (type & msidbCustomActionTypeCommit)
239 TRACE("Deferring Commit Action!\n");
240 schedule_action(package, COMMIT_SCRIPT, deferred);
242 else
244 TRACE("Deferring Action!\n");
245 schedule_action(package, INSTALL_SCRIPT, deferred);
248 rc = ERROR_SUCCESS;
249 msi_free(actiondata);
250 msi_free(usersid);
251 msi_free(prodcode);
252 msi_free(deferred);
253 goto end;
255 else
257 static const WCHAR szBlank[] = {0};
259 LPWSTR actiondata = msi_dup_property( package, action );
261 switch (script)
263 case INSTALL_SCRIPT:
264 package->scheduled_action_running = TRUE;
265 break;
266 case COMMIT_SCRIPT:
267 package->commit_action_running = TRUE;
268 break;
269 case ROLLBACK_SCRIPT:
270 package->rollback_action_running = TRUE;
271 break;
272 default:
273 break;
276 if (deferred_data)
277 set_deferred_action_props(package, deferred_data);
278 else if (actiondata)
279 MSI_SetPropertyW(package,szActionData,actiondata);
280 else
281 MSI_SetPropertyW(package,szActionData,szBlank);
283 msi_free(actiondata);
286 else if (!check_execution_scheduling_options(package,action,type))
288 rc = ERROR_SUCCESS;
289 goto end;
292 switch (type & CUSTOM_ACTION_TYPE_MASK)
294 case 1: /* DLL file stored in a Binary table stream */
295 rc = HANDLE_CustomType1(package,source,target,type,action);
296 break;
297 case 2: /* EXE file stored in a Binary table stream */
298 rc = HANDLE_CustomType2(package,source,target,type,action);
299 break;
300 case 18: /*EXE file installed with package */
301 rc = HANDLE_CustomType18(package,source,target,type,action);
302 break;
303 case 19: /* Error that halts install */
304 rc = HANDLE_CustomType19(package,source,target,type,action);
305 break;
306 case 17:
307 rc = HANDLE_CustomType17(package,source,target,type,action);
308 break;
309 case 23: /* installs another package in the source tree */
310 deformat_string(package,target,&deformated);
311 rc = HANDLE_CustomType23(package,source,deformated,type,action);
312 break;
313 case 50: /*EXE file specified by a property value */
314 rc = HANDLE_CustomType50(package,source,target,type,action);
315 break;
316 case 34: /*EXE to be run in specified directory */
317 rc = HANDLE_CustomType34(package,source,target,type,action);
318 break;
319 case 35: /* Directory set with formatted text. */
320 deformat_string(package,target,&deformated);
321 MSI_SetTargetPathW(package, source, deformated);
322 msi_free(deformated);
323 break;
324 case 51: /* Property set with formatted text. */
325 deformat_string(package,target,&deformated);
326 rc = MSI_SetPropertyW(package,source,deformated);
327 msi_free(deformated);
328 break;
329 case 37: /* JScript/VBScript text stored in target column. */
330 case 38:
331 rc = HANDLE_CustomType37_38(package,source,target,type,action);
332 break;
333 case 5:
334 case 6: /* JScript/VBScript file stored in a Binary table stream. */
335 rc = HANDLE_CustomType5_6(package,source,target,type,action);
336 break;
337 case 21: /* JScript/VBScript file installed with the product. */
338 case 22:
339 rc = HANDLE_CustomType21_22(package,source,target,type,action);
340 break;
341 case 53: /* JScript/VBScript text specified by a property value. */
342 case 54:
343 rc = HANDLE_CustomType53_54(package,source,target,type,action);
344 break;
345 default:
346 FIXME("UNHANDLED ACTION TYPE %i (%s %s)\n",
347 type & CUSTOM_ACTION_TYPE_MASK, debugstr_w(source),
348 debugstr_w(target));
351 end:
352 package->scheduled_action_running = FALSE;
353 package->commit_action_running = FALSE;
354 package->rollback_action_running = FALSE;
355 msi_free(action_copy);
356 msiobj_release(&row->hdr);
357 return rc;
361 static UINT store_binary_to_temp(MSIPACKAGE *package, LPCWSTR source,
362 LPWSTR tmp_file)
364 static const WCHAR query[] = {
365 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
366 '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',' ',
367 '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
368 MSIRECORD *row = 0;
369 HANDLE file;
370 CHAR buffer[1024];
371 static const WCHAR f1[] = {'m','s','i',0};
372 WCHAR fmt[MAX_PATH];
373 DWORD sz = MAX_PATH;
374 UINT r;
376 if (MSI_GetPropertyW(package, cszTempFolder, fmt, &sz) != ERROR_SUCCESS)
377 GetTempPathW(MAX_PATH, fmt);
379 if (GetTempFileNameW(fmt, f1, 0, tmp_file) == 0)
381 TRACE("Unable to create file\n");
382 return ERROR_FUNCTION_FAILED;
384 track_tempfile(package, tmp_file);
386 row = MSI_QueryGetRecord(package->db, query, source);
387 if (!row)
388 return ERROR_FUNCTION_FAILED;
390 /* write out the file */
391 file = CreateFileW(tmp_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
392 FILE_ATTRIBUTE_NORMAL, NULL);
393 if (file == INVALID_HANDLE_VALUE)
394 r = ERROR_FUNCTION_FAILED;
395 else
399 DWORD write;
400 sz = sizeof buffer;
401 r = MSI_RecordReadStream(row, 2, buffer, &sz);
402 if (r != ERROR_SUCCESS)
404 ERR("Failed to get stream\n");
405 break;
407 WriteFile(file, buffer, sz, &write, NULL);
408 } while (sz == sizeof buffer);
409 CloseHandle(file);
412 msiobj_release(&row->hdr);
414 return r;
417 static void file_running_action(MSIPACKAGE* package, HANDLE Handle,
418 BOOL process, LPCWSTR name)
420 MSIRUNNINGACTION *action;
422 action = msi_alloc( sizeof(MSIRUNNINGACTION) );
424 action->handle = Handle;
425 action->process = process;
426 action->name = strdupW(name);
428 list_add_tail( &package->RunningActions, &action->entry );
431 static UINT custom_get_process_return( HANDLE process )
433 DWORD rc = 0;
435 GetExitCodeProcess( process, &rc );
436 if (rc != 0)
437 return ERROR_FUNCTION_FAILED;
438 return ERROR_SUCCESS;
441 static UINT custom_get_thread_return( MSIPACKAGE *package, HANDLE thread )
443 DWORD rc = 0;
445 GetExitCodeThread( thread, &rc );
447 switch (rc)
449 case ERROR_FUNCTION_NOT_CALLED:
450 case ERROR_SUCCESS:
451 case ERROR_INSTALL_USEREXIT:
452 case ERROR_INSTALL_FAILURE:
453 return rc;
454 case ERROR_NO_MORE_ITEMS:
455 return ERROR_SUCCESS;
456 case ERROR_INSTALL_SUSPEND:
457 ACTION_ForceReboot( package );
458 return ERROR_SUCCESS;
459 default:
460 ERR("Invalid Return Code %d\n",rc);
461 return ERROR_INSTALL_FAILURE;
465 static UINT wait_process_handle(MSIPACKAGE* package, UINT type,
466 HANDLE ProcessHandle, LPCWSTR name)
468 UINT rc = ERROR_SUCCESS;
470 if (!(type & msidbCustomActionTypeAsync))
472 TRACE("waiting for %s\n", debugstr_w(name));
474 msi_dialog_check_messages(ProcessHandle);
476 if (!(type & msidbCustomActionTypeContinue))
477 rc = custom_get_process_return(ProcessHandle);
479 CloseHandle(ProcessHandle);
481 else
483 TRACE("%s running in background\n", debugstr_w(name));
485 if (!(type & msidbCustomActionTypeContinue))
486 file_running_action(package, ProcessHandle, TRUE, name);
487 else
488 CloseHandle(ProcessHandle);
491 return rc;
494 typedef struct _msi_custom_action_info {
495 struct list entry;
496 LONG refs;
497 MSIPACKAGE *package;
498 LPWSTR source;
499 LPWSTR target;
500 HANDLE handle;
501 LPWSTR action;
502 INT type;
503 GUID guid;
504 } msi_custom_action_info;
506 static void release_custom_action_data( msi_custom_action_info *info )
508 EnterCriticalSection( &msi_custom_action_cs );
510 if (!--info->refs)
512 list_remove( &info->entry );
513 if (info->handle)
514 CloseHandle( info->handle );
515 msi_free( info->action );
516 msi_free( info->source );
517 msi_free( info->target );
518 msiobj_release( &info->package->hdr );
519 msi_free( info );
522 LeaveCriticalSection( &msi_custom_action_cs );
525 /* must be called inside msi_custom_action_cs if info is in the pending custom actions list */
526 static void addref_custom_action_data( msi_custom_action_info *info )
528 info->refs++;
531 static UINT wait_thread_handle( msi_custom_action_info *info )
533 UINT rc = ERROR_SUCCESS;
535 if (!(info->type & msidbCustomActionTypeAsync))
537 TRACE("waiting for %s\n", debugstr_w( info->action ));
539 msi_dialog_check_messages( info->handle );
541 if (!(info->type & msidbCustomActionTypeContinue))
542 rc = custom_get_thread_return( info->package, info->handle );
544 release_custom_action_data( info );
546 else
548 TRACE("%s running in background\n", debugstr_w( info->action ));
551 return rc;
554 static msi_custom_action_info *find_action_by_guid( const GUID *guid )
556 msi_custom_action_info *info;
557 BOOL found = FALSE;
559 EnterCriticalSection( &msi_custom_action_cs );
561 LIST_FOR_EACH_ENTRY( info, &msi_pending_custom_actions, msi_custom_action_info, entry )
563 if (IsEqualGUID( &info->guid, guid ))
565 addref_custom_action_data( info );
566 found = TRUE;
567 break;
571 LeaveCriticalSection( &msi_custom_action_cs );
573 if (!found)
574 return NULL;
576 return info;
579 static void handle_msi_break( LPCWSTR target )
581 LPWSTR msg;
582 WCHAR val[MAX_PATH];
584 static const WCHAR MsiBreak[] = { 'M','s','i','B','r','e','a','k',0 };
585 static const WCHAR WindowsInstaller[] = {
586 'W','i','n','d','o','w','s',' ','I','n','s','t','a','l','l','e','r',0
589 static const WCHAR format[] = {
590 'T','o',' ','d','e','b','u','g',' ','y','o','u','r',' ',
591 'c','u','s','t','o','m',' ','a','c','t','i','o','n',',',' ',
592 'a','t','t','a','c','h',' ','y','o','u','r',' ','d','e','b','u','g','g','e','r',' ',
593 't','o',' ','p','r','o','c','e','s','s',' ','%','i',' ','(','0','x','%','X',')',' ',
594 'a','n','d',' ','p','r','e','s','s',' ','O','K',0
597 if( !GetEnvironmentVariableW( MsiBreak, val, MAX_PATH ))
598 return;
600 if( lstrcmpiW( val, target ))
601 return;
603 msg = msi_alloc( (lstrlenW(format) + 10) * sizeof(WCHAR) );
604 if (!msg)
605 return;
607 wsprintfW( msg, format, GetCurrentProcessId(), GetCurrentProcessId());
608 MessageBoxW( NULL, msg, WindowsInstaller, MB_OK);
609 msi_free(msg);
610 DebugBreak();
613 static DWORD WINAPI ACTION_CallDllFunction( const GUID *guid )
615 msi_custom_action_info *info;
616 MsiCustomActionEntryPoint fn;
617 MSIHANDLE hPackage;
618 HANDLE hModule;
619 LPSTR proc;
620 UINT r = ERROR_FUNCTION_FAILED;
622 info = find_action_by_guid( guid );
623 if (!info)
625 ERR("failed to find action %s\n", debugstr_guid( guid) );
626 return r;
629 TRACE("%s %s\n", debugstr_w( info->source ), debugstr_w( info->target ) );
631 hModule = LoadLibraryW( info->source );
632 if (!hModule)
634 ERR("failed to load dll %s\n", debugstr_w( info->source ) );
635 return r;
638 proc = strdupWtoA( info->target );
639 fn = (MsiCustomActionEntryPoint) GetProcAddress( hModule, proc );
640 msi_free( proc );
641 if (fn)
643 hPackage = alloc_msihandle( &info->package->hdr );
644 if (hPackage)
646 TRACE("calling %s\n", debugstr_w( info->target ) );
647 handle_msi_break( info->target );
649 __TRY
651 r = fn( hPackage );
653 __EXCEPT_PAGE_FAULT
655 ERR("Custom action (%s:%s) caused a page fault: %08x\n",
656 debugstr_w(info->source), debugstr_w(info->target), GetExceptionCode());
657 r = ERROR_SUCCESS;
659 __ENDTRY;
661 MsiCloseHandle( hPackage );
663 else
664 ERR("failed to create handle for %p\n", info->package );
666 else
667 ERR("GetProcAddress(%s) failed\n", debugstr_w( info->target ) );
669 FreeLibrary(hModule);
671 if (info->type & msidbCustomActionTypeAsync &&
672 info->type & msidbCustomActionTypeContinue)
673 release_custom_action_data( info );
675 return r;
678 static DWORD WINAPI DllThread( LPVOID arg )
680 LPGUID guid = arg;
681 DWORD rc = 0;
683 TRACE("custom action (%x) started\n", GetCurrentThreadId() );
685 rc = ACTION_CallDllFunction( guid );
687 TRACE("custom action (%x) returned %i\n", GetCurrentThreadId(), rc );
689 MsiCloseAllHandles();
690 return rc;
693 static DWORD WINAPI ACTION_CAInstallPackage(const GUID *guid)
695 msi_custom_action_info *info;
696 UINT r = ERROR_FUNCTION_FAILED;
697 INSTALLUILEVEL old_level;
699 info = find_action_by_guid(guid);
700 if (!info)
702 ERR("failed to find action %s\n", debugstr_guid(guid));
703 return r;
706 old_level = MsiSetInternalUI(INSTALLUILEVEL_BASIC, NULL);
707 r = MsiInstallProductW(info->source, info->target);
708 MsiSetInternalUI(old_level, NULL);
710 release_custom_action_data(info);
712 return r;
715 static DWORD WINAPI ConcurrentInstallThread(LPVOID arg)
717 LPGUID guid = arg;
718 DWORD rc;
720 TRACE("concurrent installation (%x) started\n", GetCurrentThreadId());
722 rc = ACTION_CAInstallPackage(guid);
724 TRACE("concurrent installation (%x) returned %i\n", GetCurrentThreadId(), rc);
726 MsiCloseAllHandles();
727 return rc;
730 static msi_custom_action_info *do_msidbCustomActionTypeDll(
731 MSIPACKAGE *package, INT type, LPCWSTR source, LPCWSTR target, LPCWSTR action )
733 msi_custom_action_info *info;
735 info = msi_alloc( sizeof *info );
736 if (!info)
737 return NULL;
739 msiobj_addref( &package->hdr );
740 info->refs = 2; /* 1 for our caller and 1 for thread we created */
741 info->package = package;
742 info->type = type;
743 info->target = strdupW( target );
744 info->source = strdupW( source );
745 info->action = strdupW( action );
746 CoCreateGuid( &info->guid );
748 EnterCriticalSection( &msi_custom_action_cs );
749 list_add_tail( &msi_pending_custom_actions, &info->entry );
750 LeaveCriticalSection( &msi_custom_action_cs );
752 info->handle = CreateThread( NULL, 0, DllThread, &info->guid, 0, NULL );
753 if (!info->handle)
755 /* release both references */
756 release_custom_action_data( info );
757 release_custom_action_data( info );
758 return NULL;
761 return info;
764 static msi_custom_action_info *do_msidbCAConcurrentInstall(
765 MSIPACKAGE *package, INT type, LPCWSTR source, LPCWSTR target, LPCWSTR action)
767 msi_custom_action_info *info;
769 info = msi_alloc( sizeof *info );
770 if (!info)
771 return NULL;
773 msiobj_addref( &package->hdr );
774 info->refs = 2; /* 1 for our caller and 1 for thread we created */
775 info->package = package;
776 info->type = type;
777 info->target = strdupW( target );
778 info->source = strdupW( source );
779 info->action = strdupW( action );
780 CoCreateGuid( &info->guid );
782 EnterCriticalSection( &msi_custom_action_cs );
783 list_add_tail( &msi_pending_custom_actions, &info->entry );
784 LeaveCriticalSection( &msi_custom_action_cs );
786 info->handle = CreateThread( NULL, 0, ConcurrentInstallThread, &info->guid, 0, NULL );
787 if (!info->handle)
789 /* release both references */
790 release_custom_action_data( info );
791 release_custom_action_data( info );
792 return NULL;
795 return info;
798 static UINT HANDLE_CustomType23(MSIPACKAGE *package, LPCWSTR source,
799 LPCWSTR target, const INT type, LPCWSTR action)
801 msi_custom_action_info *info;
802 WCHAR package_path[MAX_PATH];
803 DWORD size;
805 static const WCHAR backslash[] = {'\\',0};
807 MSI_GetPropertyW(package, cszSourceDir, package_path, &size);
808 lstrcatW(package_path, backslash);
809 lstrcatW(package_path, source);
811 if (GetFileAttributesW(package_path) == INVALID_FILE_ATTRIBUTES)
813 ERR("Source package does not exist: %s\n", debugstr_w(package_path));
814 return ERROR_FUNCTION_FAILED;
817 TRACE("Installing package %s concurrently\n", debugstr_w(package_path));
819 info = do_msidbCAConcurrentInstall(package, type, package_path, target, action);
821 return wait_thread_handle(info);
824 static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source,
825 LPCWSTR target, const INT type, LPCWSTR action)
827 msi_custom_action_info *info;
828 WCHAR tmp_file[MAX_PATH];
829 UINT r;
831 r = store_binary_to_temp(package, source, tmp_file);
832 if (r != ERROR_SUCCESS)
833 return r;
835 TRACE("Calling function %s from %s\n",debugstr_w(target),
836 debugstr_w(tmp_file));
838 if (!strchrW(tmp_file,'.'))
840 static const WCHAR dot[]={'.',0};
841 strcatW(tmp_file,dot);
844 info = do_msidbCustomActionTypeDll( package, type, tmp_file, target, action );
846 return wait_thread_handle( info );
849 static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source,
850 LPCWSTR target, const INT type, LPCWSTR action)
852 WCHAR tmp_file[MAX_PATH];
853 STARTUPINFOW si;
854 PROCESS_INFORMATION info;
855 BOOL rc;
856 INT len;
857 WCHAR *deformated = NULL;
858 WCHAR *cmd;
859 static const WCHAR spc[] = {' ',0};
860 UINT r;
862 memset(&si,0,sizeof(STARTUPINFOW));
864 r = store_binary_to_temp(package, source, tmp_file);
865 if (r != ERROR_SUCCESS)
866 return r;
868 deformat_string(package,target,&deformated);
870 len = strlenW(tmp_file)+2;
872 if (deformated)
873 len += strlenW(deformated);
875 cmd = msi_alloc(sizeof(WCHAR)*len);
877 strcpyW(cmd,tmp_file);
878 if (deformated)
880 strcatW(cmd,spc);
881 strcatW(cmd,deformated);
883 msi_free(deformated);
886 TRACE("executing exe %s\n", debugstr_w(cmd));
888 rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
889 c_collen, &si, &info);
890 msi_free(cmd);
892 if ( !rc )
894 ERR("Unable to execute command %s\n", debugstr_w(cmd));
895 return ERROR_SUCCESS;
897 CloseHandle( info.hThread );
899 r = wait_process_handle(package, type, info.hProcess, action);
901 return r;
904 static UINT HANDLE_CustomType17(MSIPACKAGE *package, LPCWSTR source,
905 LPCWSTR target, const INT type, LPCWSTR action)
907 msi_custom_action_info *info;
908 MSIFILE *file;
910 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
912 file = get_loaded_file( package, source );
913 if (!file)
915 ERR("invalid file key %s\n", debugstr_w( source ));
916 return ERROR_FUNCTION_FAILED;
919 info = do_msidbCustomActionTypeDll( package, type, file->TargetPath, target, action );
921 return wait_thread_handle( info );
924 static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source,
925 LPCWSTR target, const INT type, LPCWSTR action)
927 STARTUPINFOW si;
928 PROCESS_INFORMATION info;
929 BOOL rc;
930 WCHAR *deformated;
931 WCHAR *cmd;
932 INT len;
933 static const WCHAR spc[] = {' ',0};
934 MSIFILE *file;
936 memset(&si,0,sizeof(STARTUPINFOW));
938 file = get_loaded_file(package,source);
939 if( !file )
940 return ERROR_FUNCTION_FAILED;
942 len = lstrlenW( file->TargetPath );
944 deformat_string(package,target,&deformated);
945 if (deformated)
946 len += strlenW(deformated);
947 len += 2;
949 cmd = msi_alloc(len * sizeof(WCHAR));
951 lstrcpyW( cmd, file->TargetPath);
952 if (deformated)
954 strcatW(cmd, spc);
955 strcatW(cmd, deformated);
957 msi_free(deformated);
960 TRACE("executing exe %s\n", debugstr_w(cmd));
962 rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
963 c_collen, &si, &info);
965 if ( !rc )
967 ERR("Unable to execute command %s\n", debugstr_w(cmd));
968 msi_free(cmd);
969 return ERROR_SUCCESS;
971 msi_free(cmd);
972 CloseHandle( info.hThread );
974 return wait_process_handle(package, type, info.hProcess, action);
977 static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
978 LPCWSTR target, const INT type, LPCWSTR action)
980 static const WCHAR query[] = {
981 'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
982 'F','R','O','M',' ','`','E','r','r','o','r','`',' ',
983 'W','H','E','R','E',' ','`','E','r','r','o','r','`',' ','=',' ',
984 '%','s',0
986 MSIRECORD *row = 0;
987 LPWSTR deformated = NULL;
989 deformat_string( package, target, &deformated );
991 /* first try treat the error as a number */
992 row = MSI_QueryGetRecord( package->db, query, deformated );
993 if( row )
995 LPCWSTR error = MSI_RecordGetString( row, 1 );
996 MessageBoxW( NULL, error, NULL, MB_OK );
997 msiobj_release( &row->hdr );
999 else
1000 MessageBoxW( NULL, deformated, NULL, MB_OK );
1002 msi_free( deformated );
1004 return ERROR_FUNCTION_FAILED;
1007 static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source,
1008 LPCWSTR target, const INT type, LPCWSTR action)
1010 STARTUPINFOW si;
1011 PROCESS_INFORMATION info;
1012 WCHAR *prop;
1013 BOOL rc;
1014 WCHAR *deformated;
1015 WCHAR *cmd;
1016 INT len;
1017 static const WCHAR spc[] = {' ',0};
1019 memset(&si,0,sizeof(STARTUPINFOW));
1020 memset(&info,0,sizeof(PROCESS_INFORMATION));
1022 prop = msi_dup_property( package, source );
1023 if (!prop)
1024 return ERROR_SUCCESS;
1026 deformat_string(package,target,&deformated);
1027 len = strlenW(prop) + 2;
1028 if (deformated)
1029 len += strlenW(deformated);
1031 cmd = msi_alloc(sizeof(WCHAR)*len);
1033 strcpyW(cmd,prop);
1034 if (deformated)
1036 strcatW(cmd,spc);
1037 strcatW(cmd,deformated);
1039 msi_free(deformated);
1041 msi_free(prop);
1043 TRACE("executing exe %s\n", debugstr_w(cmd));
1045 rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
1046 c_collen, &si, &info);
1048 if ( !rc )
1050 ERR("Unable to execute command %s\n", debugstr_w(cmd));
1051 msi_free(cmd);
1052 return ERROR_SUCCESS;
1054 msi_free(cmd);
1056 CloseHandle( info.hThread );
1058 return wait_process_handle(package, type, info.hProcess, action);
1061 static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
1062 LPCWSTR target, const INT type, LPCWSTR action)
1064 LPWSTR filename, deformated;
1065 STARTUPINFOW si;
1066 PROCESS_INFORMATION info;
1067 BOOL rc;
1069 memset(&si,0,sizeof(STARTUPINFOW));
1071 filename = resolve_folder(package, source, FALSE, FALSE, TRUE, NULL);
1073 if (!filename)
1074 return ERROR_FUNCTION_FAILED;
1076 SetCurrentDirectoryW(filename);
1077 msi_free(filename);
1079 deformat_string(package,target,&deformated);
1081 if (!deformated)
1082 return ERROR_FUNCTION_FAILED;
1084 TRACE("executing exe %s\n", debugstr_w(deformated));
1086 rc = CreateProcessW(NULL, deformated, NULL, NULL, FALSE, 0, NULL,
1087 c_collen, &si, &info);
1089 if ( !rc )
1091 ERR("Unable to execute command %s\n", debugstr_w(deformated));
1092 msi_free(deformated);
1093 return ERROR_SUCCESS;
1095 msi_free(deformated);
1096 CloseHandle( info.hThread );
1098 return wait_process_handle(package, type, info.hProcess, action);
1101 static DWORD WINAPI ACTION_CallScript( const GUID *guid )
1103 msi_custom_action_info *info;
1104 MSIHANDLE hPackage;
1105 UINT r = ERROR_FUNCTION_FAILED;
1107 info = find_action_by_guid( guid );
1108 if (!info)
1110 ERR("failed to find action %s\n", debugstr_guid( guid) );
1111 return r;
1114 TRACE("function %s, script %s\n", debugstr_w( info->target ), debugstr_w( info->source ) );
1116 hPackage = alloc_msihandle( &info->package->hdr );
1117 if (hPackage)
1119 r = call_script( hPackage, info->type, info->source, info->target, info->action );
1120 MsiCloseHandle( hPackage );
1122 else
1123 ERR("failed to create handle for %p\n", info->package );
1125 if (info->type & msidbCustomActionTypeAsync &&
1126 info->type & msidbCustomActionTypeContinue)
1127 release_custom_action_data( info );
1129 return S_OK;
1132 static DWORD WINAPI ScriptThread( LPVOID arg )
1134 LPGUID guid = arg;
1135 DWORD rc = 0;
1137 TRACE("custom action (%x) started\n", GetCurrentThreadId() );
1139 rc = ACTION_CallScript( guid );
1141 TRACE("custom action (%x) returned %i\n", GetCurrentThreadId(), rc );
1143 MsiCloseAllHandles();
1144 return rc;
1147 static msi_custom_action_info *do_msidbCustomActionTypeScript(
1148 MSIPACKAGE *package, INT type, LPCWSTR script, LPCWSTR function, LPCWSTR action )
1150 msi_custom_action_info *info;
1152 info = msi_alloc( sizeof *info );
1153 if (!info)
1154 return NULL;
1156 msiobj_addref( &package->hdr );
1157 info->refs = 2; /* 1 for our caller and 1 for thread we created */
1158 info->package = package;
1159 info->type = type;
1160 info->target = strdupW( function );
1161 info->source = strdupW( script );
1162 info->action = strdupW( action );
1163 CoCreateGuid( &info->guid );
1165 EnterCriticalSection( &msi_custom_action_cs );
1166 list_add_tail( &msi_pending_custom_actions, &info->entry );
1167 LeaveCriticalSection( &msi_custom_action_cs );
1169 info->handle = CreateThread( NULL, 0, ScriptThread, &info->guid, 0, NULL );
1170 if (!info->handle)
1172 /* release both references */
1173 release_custom_action_data( info );
1174 release_custom_action_data( info );
1175 return NULL;
1178 return info;
1181 static UINT HANDLE_CustomType37_38(MSIPACKAGE *package, LPCWSTR source,
1182 LPCWSTR target, const INT type, LPCWSTR action)
1184 msi_custom_action_info *info;
1186 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1188 info = do_msidbCustomActionTypeScript( package, type, target, NULL, action );
1190 return wait_thread_handle( info );
1193 static UINT HANDLE_CustomType5_6(MSIPACKAGE *package, LPCWSTR source,
1194 LPCWSTR target, const INT type, LPCWSTR action)
1196 static const WCHAR query[] = {
1197 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1198 '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',' ',
1199 '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
1200 MSIRECORD *row = 0;
1201 msi_custom_action_info *info;
1202 CHAR *buffer = NULL;
1203 WCHAR *bufferw = NULL;
1204 DWORD sz = 0;
1205 UINT r;
1207 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1209 row = MSI_QueryGetRecord(package->db, query, source);
1210 if (!row)
1211 return ERROR_FUNCTION_FAILED;
1213 r = MSI_RecordReadStream(row, 2, NULL, &sz);
1214 if (r != ERROR_SUCCESS)
1215 return r;
1217 buffer = msi_alloc(sizeof(CHAR)*(sz+1));
1218 if (!buffer)
1219 return ERROR_FUNCTION_FAILED;
1221 r = MSI_RecordReadStream(row, 2, buffer, &sz);
1222 if (r != ERROR_SUCCESS)
1223 goto done;
1225 buffer[sz] = 0;
1226 bufferw = strdupAtoW(buffer);
1227 if (!bufferw)
1229 r = ERROR_FUNCTION_FAILED;
1230 goto done;
1233 info = do_msidbCustomActionTypeScript( package, type, bufferw, target, action );
1234 r = wait_thread_handle( info );
1236 done:
1237 msi_free(bufferw);
1238 msi_free(buffer);
1239 return r;
1242 static UINT HANDLE_CustomType21_22(MSIPACKAGE *package, LPCWSTR source,
1243 LPCWSTR target, const INT type, LPCWSTR action)
1245 msi_custom_action_info *info;
1246 MSIFILE *file;
1247 HANDLE hFile;
1248 DWORD sz, szHighWord = 0, read;
1249 CHAR *buffer=NULL;
1250 WCHAR *bufferw=NULL;
1251 BOOL bRet;
1252 UINT r;
1254 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1256 file = get_loaded_file(package,source);
1257 if (!file)
1259 ERR("invalid file key %s\n", debugstr_w(source));
1260 return ERROR_FUNCTION_FAILED;
1263 hFile = CreateFileW(file->TargetPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
1264 if (hFile == INVALID_HANDLE_VALUE)
1265 return ERROR_FUNCTION_FAILED;
1267 sz = GetFileSize(hFile, &szHighWord);
1268 if (sz == INVALID_FILE_SIZE || szHighWord != 0)
1270 CloseHandle(hFile);
1271 return ERROR_FUNCTION_FAILED;
1274 buffer = msi_alloc(sizeof(CHAR)*(sz+1));
1275 if (!buffer)
1277 CloseHandle(hFile);
1278 return ERROR_FUNCTION_FAILED;
1281 bRet = ReadFile(hFile, buffer, sz, &read, NULL);
1282 CloseHandle(hFile);
1283 if (!bRet)
1285 r = ERROR_FUNCTION_FAILED;
1286 goto done;
1289 buffer[read] = 0;
1290 bufferw = strdupAtoW(buffer);
1291 if (!bufferw)
1293 r = ERROR_FUNCTION_FAILED;
1294 goto done;
1297 info = do_msidbCustomActionTypeScript( package, type, bufferw, target, action );
1298 r = wait_thread_handle( info );
1300 done:
1301 msi_free(bufferw);
1302 msi_free(buffer);
1303 return r;
1306 static UINT HANDLE_CustomType53_54(MSIPACKAGE *package, LPCWSTR source,
1307 LPCWSTR target, const INT type, LPCWSTR action)
1309 msi_custom_action_info *info;
1310 WCHAR *prop;
1312 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1314 prop = msi_dup_property(package,source);
1315 if (!prop)
1316 return ERROR_SUCCESS;
1318 info = do_msidbCustomActionTypeScript( package, type, prop, NULL, action );
1319 msi_free(prop);
1320 return wait_thread_handle( info );
1323 void ACTION_FinishCustomActions(const MSIPACKAGE* package)
1325 struct list *item;
1326 HANDLE *wait_handles;
1327 unsigned int handle_count, i;
1328 msi_custom_action_info *info, *cursor;
1330 while ((item = list_head( &package->RunningActions )))
1332 MSIRUNNINGACTION *action = LIST_ENTRY( item, MSIRUNNINGACTION, entry );
1334 list_remove( &action->entry );
1336 TRACE("waiting for %s\n", debugstr_w( action->name ) );
1337 msi_dialog_check_messages( action->handle );
1339 CloseHandle( action->handle );
1340 msi_free( action->name );
1341 msi_free( action );
1344 EnterCriticalSection( &msi_custom_action_cs );
1346 handle_count = list_count( &msi_pending_custom_actions );
1347 wait_handles = HeapAlloc( GetProcessHeap(), 0, handle_count * sizeof(HANDLE) );
1349 handle_count = 0;
1350 LIST_FOR_EACH_ENTRY_SAFE( info, cursor, &msi_pending_custom_actions, msi_custom_action_info, entry )
1352 if (info->package == package )
1354 if (DuplicateHandle(GetCurrentProcess(), info->handle, GetCurrentProcess(), &wait_handles[handle_count], SYNCHRONIZE, FALSE, 0))
1355 handle_count++;
1359 LeaveCriticalSection( &msi_custom_action_cs );
1361 for (i = 0; i < handle_count; i++)
1363 msi_dialog_check_messages( wait_handles[i] );
1364 CloseHandle( wait_handles[i] );
1367 HeapFree( GetProcessHeap(), 0, wait_handles );