Release 0.9.39.
[wine/gsoc-2012-control.git] / dlls / msi / custom.c
blobc89acbf7f1524bfc00da5738c5798c588f3ffce5
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;
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, 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: [CustomActionData]Action */
193 if ((ptr = strchrW(action_copy, ']')))
195 deferred_data = action_copy + 1;
196 *ptr = '\0';
197 action = ptr + 1;
200 row = MSI_QueryGetRecord( package->db, ExecSeqQuery, action );
201 if (!row)
203 msi_free(action_copy);
204 return ERROR_CALL_NOT_IMPLEMENTED;
207 type = MSI_RecordGetInteger(row,2);
209 source = MSI_RecordGetString(row,3);
210 target = MSI_RecordGetString(row,4);
212 TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type,
213 debugstr_w(source), debugstr_w(target));
215 /* handle some of the deferred actions */
216 if (type & msidbCustomActionTypeTSAware)
217 FIXME("msidbCustomActionTypeTSAware not handled\n");
219 if (type & msidbCustomActionTypeInScript)
221 if (type & msidbCustomActionTypeNoImpersonate)
222 FIXME("msidbCustomActionTypeNoImpersonate not handled\n");
224 if (type & msidbCustomActionTypeRollback)
226 FIXME("Rollback only action... rollbacks not supported yet\n");
227 schedule_action(package, ROLLBACK_SCRIPT, action);
228 rc = ERROR_SUCCESS;
229 goto end;
231 if (!execute)
233 LPWSTR actiondata = msi_dup_property(package, action);
234 LPWSTR usersid = msi_dup_property(package, UserSID);
235 LPWSTR prodcode = msi_dup_property(package, ProdCode);
236 LPWSTR deferred = msi_get_deferred_action(action, actiondata, usersid, prodcode);
238 if (type & msidbCustomActionTypeCommit)
240 TRACE("Deferring Commit Action!\n");
241 schedule_action(package, COMMIT_SCRIPT, deferred);
243 else
245 TRACE("Deferring Action!\n");
246 schedule_action(package, INSTALL_SCRIPT, deferred);
249 rc = ERROR_SUCCESS;
250 msi_free(actiondata);
251 msi_free(usersid);
252 msi_free(prodcode);
253 msi_free(deferred);
254 goto end;
256 else
258 static const WCHAR szBlank[] = {0};
260 LPWSTR actiondata = msi_dup_property( package, action );
262 if (deferred_data)
263 set_deferred_action_props(package, deferred_data);
264 else if (actiondata)
265 MSI_SetPropertyW(package,szActionData,actiondata);
266 else
267 MSI_SetPropertyW(package,szActionData,szBlank);
269 msi_free(actiondata);
272 else if (!check_execution_scheduling_options(package,action,type))
274 rc = ERROR_SUCCESS;
275 goto end;
278 switch (type & CUSTOM_ACTION_TYPE_MASK)
280 case 1: /* DLL file stored in a Binary table stream */
281 rc = HANDLE_CustomType1(package,source,target,type,action);
282 break;
283 case 2: /* EXE file stored in a Binary table stream */
284 rc = HANDLE_CustomType2(package,source,target,type,action);
285 break;
286 case 18: /*EXE file installed with package */
287 rc = HANDLE_CustomType18(package,source,target,type,action);
288 break;
289 case 19: /* Error that halts install */
290 rc = HANDLE_CustomType19(package,source,target,type,action);
291 break;
292 case 17:
293 rc = HANDLE_CustomType17(package,source,target,type,action);
294 break;
295 case 23: /* installs another package in the source tree */
296 deformat_string(package,target,&deformated);
297 rc = HANDLE_CustomType23(package,source,deformated,type,action);
298 break;
299 case 50: /*EXE file specified by a property value */
300 rc = HANDLE_CustomType50(package,source,target,type,action);
301 break;
302 case 34: /*EXE to be run in specified directory */
303 rc = HANDLE_CustomType34(package,source,target,type,action);
304 break;
305 case 35: /* Directory set with formatted text. */
306 deformat_string(package,target,&deformated);
307 MSI_SetTargetPathW(package, source, deformated);
308 msi_free(deformated);
309 break;
310 case 51: /* Property set with formatted text. */
311 deformat_string(package,target,&deformated);
312 rc = MSI_SetPropertyW(package,source,deformated);
313 msi_free(deformated);
314 break;
315 case 37: /* JScript/VBScript text stored in target column. */
316 case 38:
317 rc = HANDLE_CustomType37_38(package,source,target,type,action);
318 break;
319 case 5:
320 case 6: /* JScript/VBScript file stored in a Binary table stream. */
321 rc = HANDLE_CustomType5_6(package,source,target,type,action);
322 break;
323 case 21: /* JScript/VBScript file installed with the product. */
324 case 22:
325 rc = HANDLE_CustomType21_22(package,source,target,type,action);
326 break;
327 case 53: /* JScript/VBScript text specified by a property value. */
328 case 54:
329 rc = HANDLE_CustomType53_54(package,source,target,type,action);
330 break;
331 default:
332 FIXME("UNHANDLED ACTION TYPE %i (%s %s)\n",
333 type & CUSTOM_ACTION_TYPE_MASK, debugstr_w(source),
334 debugstr_w(target));
337 end:
338 msi_free(action_copy);
339 msiobj_release(&row->hdr);
340 return rc;
344 static UINT store_binary_to_temp(MSIPACKAGE *package, LPCWSTR source,
345 LPWSTR tmp_file)
347 static const WCHAR query[] = {
348 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
349 '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',' ',
350 '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
351 MSIRECORD *row = 0;
352 HANDLE file;
353 CHAR buffer[1024];
354 static const WCHAR f1[] = {'m','s','i',0};
355 WCHAR fmt[MAX_PATH];
356 DWORD sz = MAX_PATH;
357 UINT r;
359 if (MSI_GetPropertyW(package, cszTempFolder, fmt, &sz) != ERROR_SUCCESS)
360 GetTempPathW(MAX_PATH, fmt);
362 if (GetTempFileNameW(fmt, f1, 0, tmp_file) == 0)
364 TRACE("Unable to create file\n");
365 return ERROR_FUNCTION_FAILED;
367 track_tempfile(package, tmp_file);
369 row = MSI_QueryGetRecord(package->db, query, source);
370 if (!row)
371 return ERROR_FUNCTION_FAILED;
373 /* write out the file */
374 file = CreateFileW(tmp_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
375 FILE_ATTRIBUTE_NORMAL, NULL);
376 if (file == INVALID_HANDLE_VALUE)
377 r = ERROR_FUNCTION_FAILED;
378 else
382 DWORD write;
383 sz = sizeof buffer;
384 r = MSI_RecordReadStream(row, 2, buffer, &sz);
385 if (r != ERROR_SUCCESS)
387 ERR("Failed to get stream\n");
388 break;
390 WriteFile(file, buffer, sz, &write, NULL);
391 } while (sz == sizeof buffer);
392 CloseHandle(file);
395 msiobj_release(&row->hdr);
397 return r;
400 static void file_running_action(MSIPACKAGE* package, HANDLE Handle,
401 BOOL process, LPCWSTR name)
403 MSIRUNNINGACTION *action;
405 action = msi_alloc( sizeof(MSIRUNNINGACTION) );
407 action->handle = Handle;
408 action->process = process;
409 action->name = strdupW(name);
411 list_add_tail( &package->RunningActions, &action->entry );
414 static UINT custom_get_process_return( HANDLE process )
416 DWORD rc = 0;
418 GetExitCodeProcess( process, &rc );
419 if (rc != 0)
420 return ERROR_FUNCTION_FAILED;
421 return ERROR_SUCCESS;
424 static UINT custom_get_thread_return( MSIPACKAGE *package, HANDLE thread )
426 DWORD rc = 0;
428 GetExitCodeThread( thread, &rc );
430 switch (rc)
432 case ERROR_FUNCTION_NOT_CALLED:
433 case ERROR_SUCCESS:
434 case ERROR_INSTALL_USEREXIT:
435 case ERROR_INSTALL_FAILURE:
436 return rc;
437 case ERROR_NO_MORE_ITEMS:
438 return ERROR_SUCCESS;
439 case ERROR_INSTALL_SUSPEND:
440 ACTION_ForceReboot( package );
441 return ERROR_SUCCESS;
442 default:
443 ERR("Invalid Return Code %d\n",rc);
444 return ERROR_INSTALL_FAILURE;
448 static UINT wait_process_handle(MSIPACKAGE* package, UINT type,
449 HANDLE ProcessHandle, LPCWSTR name)
451 UINT rc = ERROR_SUCCESS;
453 if (!(type & msidbCustomActionTypeAsync))
455 TRACE("waiting for %s\n", debugstr_w(name));
457 msi_dialog_check_messages(ProcessHandle);
459 if (!(type & msidbCustomActionTypeContinue))
460 rc = custom_get_process_return(ProcessHandle);
462 CloseHandle(ProcessHandle);
464 else
466 TRACE("%s running in background\n", debugstr_w(name));
468 if (!(type & msidbCustomActionTypeContinue))
469 file_running_action(package, ProcessHandle, TRUE, name);
470 else
471 CloseHandle(ProcessHandle);
474 return rc;
477 typedef struct _msi_custom_action_info {
478 struct list entry;
479 MSIPACKAGE *package;
480 LPWSTR source;
481 LPWSTR target;
482 HANDLE handle;
483 LPWSTR action;
484 INT type;
485 GUID guid;
486 } msi_custom_action_info;
488 static void free_custom_action_data( msi_custom_action_info *info )
490 EnterCriticalSection( &msi_custom_action_cs );
491 list_remove( &info->entry );
492 LeaveCriticalSection( &msi_custom_action_cs );
493 if (info->handle)
494 CloseHandle( info->handle );
495 msi_free( info->action );
496 msi_free( info->source );
497 msi_free( info->target );
498 msiobj_release( &info->package->hdr );
499 msi_free( info );
502 static UINT wait_thread_handle( msi_custom_action_info *info )
504 UINT rc = ERROR_SUCCESS;
506 if (!(info->type & msidbCustomActionTypeAsync))
508 TRACE("waiting for %s\n", debugstr_w( info->action ));
510 msi_dialog_check_messages( info->handle );
512 if (!(info->type & msidbCustomActionTypeContinue))
513 rc = custom_get_thread_return( info->package, info->handle );
515 free_custom_action_data( info );
517 else
519 TRACE("%s running in background\n", debugstr_w( info->action ));
522 return rc;
525 static msi_custom_action_info *find_action_by_guid( const GUID *guid )
527 msi_custom_action_info *info;
528 BOOL found = FALSE;
530 EnterCriticalSection( &msi_custom_action_cs );
532 LIST_FOR_EACH_ENTRY( info, &msi_pending_custom_actions, msi_custom_action_info, entry )
534 if (IsEqualGUID( &info->guid, guid ))
536 found = TRUE;
537 break;
541 LeaveCriticalSection( &msi_custom_action_cs );
543 if (!found)
544 return NULL;
546 return info;
549 static void handle_msi_break( LPCWSTR target )
551 LPWSTR msg;
552 WCHAR val[MAX_PATH];
554 static const WCHAR MsiBreak[] = { 'M','s','i','B','r','e','a','k',0 };
555 static const WCHAR WindowsInstaller[] = {
556 'W','i','n','d','o','w','s',' ','I','n','s','t','a','l','l','e','r',0
559 static const WCHAR format[] = {
560 'T','o',' ','d','e','b','u','g',' ','y','o','u','r',' ',
561 'c','u','s','t','o','m',' ','a','c','t','i','o','n',',',' ',
562 'a','t','t','a','c','h',' ','y','o','u','r',' ','d','e','b','u','g','g','e','r',' ',
563 't','o',' ','p','r','o','c','e','s','s',' ','%','i',' ','(','0','x','%','X',')',' ',
564 'a','n','d',' ','p','r','e','s','s',' ','O','K',0
567 if( !GetEnvironmentVariableW( MsiBreak, val, MAX_PATH ))
568 return;
570 if( lstrcmpiW( val, target ))
571 return;
573 msg = msi_alloc( (lstrlenW(format) + 10) * sizeof(WCHAR) );
574 if (!msg)
575 return;
577 wsprintfW( msg, format, GetCurrentProcessId(), GetCurrentProcessId());
578 MessageBoxW( NULL, msg, WindowsInstaller, MB_OK);
579 msi_free(msg);
580 DebugBreak();
583 static DWORD WINAPI ACTION_CallDllFunction( const GUID *guid )
585 msi_custom_action_info *info;
586 MsiCustomActionEntryPoint fn;
587 MSIHANDLE hPackage;
588 HANDLE hModule;
589 LPSTR proc;
590 UINT r = ERROR_FUNCTION_FAILED;
592 info = find_action_by_guid( guid );
593 if (!info)
595 ERR("failed to find action %s\n", debugstr_guid( guid) );
596 return r;
599 TRACE("%s %s\n", debugstr_w( info->source ), debugstr_w( info->target ) );
601 hModule = LoadLibraryW( info->source );
602 if (!hModule)
604 ERR("failed to load dll %s\n", debugstr_w( info->source ) );
605 return r;
608 proc = strdupWtoA( info->target );
609 fn = (MsiCustomActionEntryPoint) GetProcAddress( hModule, proc );
610 msi_free( proc );
611 if (fn)
613 hPackage = alloc_msihandle( &info->package->hdr );
614 if (hPackage)
616 TRACE("calling %s\n", debugstr_w( info->target ) );
617 handle_msi_break( info->target );
619 __TRY
621 r = fn( hPackage );
623 __EXCEPT_PAGE_FAULT
625 ERR("Custom action (%s:%s) caused a page fault: %08x\n",
626 debugstr_w(info->source), debugstr_w(info->target), GetExceptionCode());
627 r = ERROR_SUCCESS;
629 __ENDTRY;
631 MsiCloseHandle( hPackage );
633 else
634 ERR("failed to create handle for %p\n", info->package );
636 else
637 ERR("GetProcAddress(%s) failed\n", debugstr_w( info->target ) );
639 FreeLibrary(hModule);
641 if (info->type & msidbCustomActionTypeAsync &&
642 info->type & msidbCustomActionTypeContinue)
643 free_custom_action_data( info );
645 return r;
648 static DWORD WINAPI DllThread( LPVOID arg )
650 LPGUID guid = arg;
651 DWORD rc = 0;
653 TRACE("custom action (%x) started\n", GetCurrentThreadId() );
655 rc = ACTION_CallDllFunction( guid );
657 TRACE("custom action (%x) returned %i\n", GetCurrentThreadId(), rc );
659 MsiCloseAllHandles();
660 return rc;
663 static DWORD WINAPI ACTION_CAInstallPackage(const GUID *guid)
665 msi_custom_action_info *info;
666 UINT r = ERROR_FUNCTION_FAILED;
667 INSTALLUILEVEL old_level;
669 info = find_action_by_guid(guid);
670 if (!info)
672 ERR("failed to find action %s\n", debugstr_guid(guid));
673 return r;
676 old_level = MsiSetInternalUI(INSTALLUILEVEL_BASIC, NULL);
677 r = MsiInstallProductW(info->source, info->target);
678 MsiSetInternalUI(old_level, NULL);
680 return r;
683 static DWORD WINAPI ConcurrentInstallThread(LPVOID arg)
685 LPGUID guid = arg;
686 DWORD rc;
688 TRACE("concurrent installation (%x) started\n", GetCurrentThreadId());
690 rc = ACTION_CAInstallPackage(guid);
692 TRACE("concurrent installation (%x) returned %i\n", GetCurrentThreadId(), rc);
694 MsiCloseAllHandles();
695 return rc;
698 static msi_custom_action_info *do_msidbCustomActionTypeDll(
699 MSIPACKAGE *package, INT type, LPCWSTR source, LPCWSTR target, LPCWSTR action )
701 msi_custom_action_info *info;
703 info = msi_alloc( sizeof *info );
704 if (!info)
705 return NULL;
707 msiobj_addref( &package->hdr );
708 info->package = package;
709 info->type = type;
710 info->target = strdupW( target );
711 info->source = strdupW( source );
712 info->action = strdupW( action );
713 CoCreateGuid( &info->guid );
715 EnterCriticalSection( &msi_custom_action_cs );
716 list_add_tail( &msi_pending_custom_actions, &info->entry );
717 LeaveCriticalSection( &msi_custom_action_cs );
719 info->handle = CreateThread( NULL, 0, DllThread, &info->guid, 0, NULL );
720 if (!info->handle)
722 free_custom_action_data( info );
723 return NULL;
726 return info;
729 static msi_custom_action_info *do_msidbCAConcurrentInstall(
730 MSIPACKAGE *package, INT type, LPCWSTR source, LPCWSTR target, LPCWSTR action)
732 msi_custom_action_info *info;
734 info = msi_alloc( sizeof *info );
735 if (!info)
736 return NULL;
738 msiobj_addref( &package->hdr );
739 info->package = package;
740 info->type = type;
741 info->target = strdupW( target );
742 info->source = strdupW( source );
743 info->action = strdupW( action );
744 CoCreateGuid( &info->guid );
746 EnterCriticalSection( &msi_custom_action_cs );
747 list_add_tail( &msi_pending_custom_actions, &info->entry );
748 LeaveCriticalSection( &msi_custom_action_cs );
750 info->handle = CreateThread( NULL, 0, ConcurrentInstallThread, &info->guid, 0, NULL );
751 if (!info->handle)
753 free_custom_action_data( info );
754 return NULL;
757 return info;
760 static UINT HANDLE_CustomType23(MSIPACKAGE *package, LPCWSTR source,
761 LPCWSTR target, const INT type, LPCWSTR action)
763 msi_custom_action_info *info;
764 WCHAR package_path[MAX_PATH];
765 DWORD size;
767 static const WCHAR backslash[] = {'\\',0};
769 MSI_GetPropertyW(package, cszSourceDir, package_path, &size);
770 lstrcatW(package_path, backslash);
771 lstrcatW(package_path, source);
773 if (GetFileAttributesW(package_path) == INVALID_FILE_ATTRIBUTES)
775 ERR("Source package does not exist: %s\n", debugstr_w(package_path));
776 return ERROR_FUNCTION_FAILED;
779 TRACE("Installing package %s concurrently\n", debugstr_w(package_path));
781 info = do_msidbCAConcurrentInstall(package, type, package_path, target, action);
783 return wait_thread_handle(info);
786 static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source,
787 LPCWSTR target, const INT type, LPCWSTR action)
789 msi_custom_action_info *info;
790 WCHAR tmp_file[MAX_PATH];
791 UINT r;
793 r = store_binary_to_temp(package, source, tmp_file);
794 if (r != ERROR_SUCCESS)
795 return r;
797 TRACE("Calling function %s from %s\n",debugstr_w(target),
798 debugstr_w(tmp_file));
800 if (!strchrW(tmp_file,'.'))
802 static const WCHAR dot[]={'.',0};
803 strcatW(tmp_file,dot);
806 info = do_msidbCustomActionTypeDll( package, type, tmp_file, target, action );
808 return wait_thread_handle( info );
811 static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source,
812 LPCWSTR target, const INT type, LPCWSTR action)
814 WCHAR tmp_file[MAX_PATH];
815 STARTUPINFOW si;
816 PROCESS_INFORMATION info;
817 BOOL rc;
818 INT len;
819 WCHAR *deformated = NULL;
820 WCHAR *cmd;
821 static const WCHAR spc[] = {' ',0};
822 UINT r;
824 memset(&si,0,sizeof(STARTUPINFOW));
826 r = store_binary_to_temp(package, source, tmp_file);
827 if (r != ERROR_SUCCESS)
828 return r;
830 deformat_string(package,target,&deformated);
832 len = strlenW(tmp_file)+2;
834 if (deformated)
835 len += strlenW(deformated);
837 cmd = msi_alloc(sizeof(WCHAR)*len);
839 strcpyW(cmd,tmp_file);
840 if (deformated)
842 strcatW(cmd,spc);
843 strcatW(cmd,deformated);
845 msi_free(deformated);
848 TRACE("executing exe %s\n", debugstr_w(cmd));
850 rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
851 c_collen, &si, &info);
852 msi_free(cmd);
854 if ( !rc )
856 ERR("Unable to execute command %s\n", debugstr_w(cmd));
857 return ERROR_SUCCESS;
859 CloseHandle( info.hThread );
861 r = wait_process_handle(package, type, info.hProcess, action);
863 return r;
866 static UINT HANDLE_CustomType17(MSIPACKAGE *package, LPCWSTR source,
867 LPCWSTR target, const INT type, LPCWSTR action)
869 msi_custom_action_info *info;
870 MSIFILE *file;
872 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
874 file = get_loaded_file( package, source );
875 if (!file)
877 ERR("invalid file key %s\n", debugstr_w( source ));
878 return ERROR_FUNCTION_FAILED;
881 info = do_msidbCustomActionTypeDll( package, type, file->TargetPath, target, action );
883 return wait_thread_handle( info );
886 static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source,
887 LPCWSTR target, const INT type, LPCWSTR action)
889 STARTUPINFOW si;
890 PROCESS_INFORMATION info;
891 BOOL rc;
892 WCHAR *deformated;
893 WCHAR *cmd;
894 INT len;
895 static const WCHAR spc[] = {' ',0};
896 MSIFILE *file;
898 memset(&si,0,sizeof(STARTUPINFOW));
900 file = get_loaded_file(package,source);
901 if( !file )
902 return ERROR_FUNCTION_FAILED;
904 len = lstrlenW( file->TargetPath );
906 deformat_string(package,target,&deformated);
907 if (deformated)
908 len += strlenW(deformated);
909 len += 2;
911 cmd = msi_alloc(len * sizeof(WCHAR));
913 lstrcpyW( cmd, file->TargetPath);
914 if (deformated)
916 strcatW(cmd, spc);
917 strcatW(cmd, deformated);
919 msi_free(deformated);
922 TRACE("executing exe %s\n", debugstr_w(cmd));
924 rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
925 c_collen, &si, &info);
927 if ( !rc )
929 ERR("Unable to execute command %s\n", debugstr_w(cmd));
930 msi_free(cmd);
931 return ERROR_SUCCESS;
933 msi_free(cmd);
934 CloseHandle( info.hThread );
936 return wait_process_handle(package, type, info.hProcess, action);
939 static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
940 LPCWSTR target, const INT type, LPCWSTR action)
942 static const WCHAR query[] = {
943 'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
944 'F','R','O','M',' ','`','E','r','r','o','r','`',' ',
945 'W','H','E','R','E',' ','`','E','r','r','o','r','`',' ','=',' ',
946 '%','s',0
948 MSIRECORD *row = 0;
949 LPWSTR deformated = NULL;
951 deformat_string( package, target, &deformated );
953 /* first try treat the error as a number */
954 row = MSI_QueryGetRecord( package->db, query, deformated );
955 if( row )
957 LPCWSTR error = MSI_RecordGetString( row, 1 );
958 MessageBoxW( NULL, error, NULL, MB_OK );
959 msiobj_release( &row->hdr );
961 else
962 MessageBoxW( NULL, deformated, NULL, MB_OK );
964 msi_free( deformated );
966 return ERROR_FUNCTION_FAILED;
969 static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source,
970 LPCWSTR target, const INT type, LPCWSTR action)
972 STARTUPINFOW si;
973 PROCESS_INFORMATION info;
974 WCHAR *prop;
975 BOOL rc;
976 WCHAR *deformated;
977 WCHAR *cmd;
978 INT len;
979 static const WCHAR spc[] = {' ',0};
981 memset(&si,0,sizeof(STARTUPINFOW));
982 memset(&info,0,sizeof(PROCESS_INFORMATION));
984 prop = msi_dup_property( package, source );
985 if (!prop)
986 return ERROR_SUCCESS;
988 deformat_string(package,target,&deformated);
989 len = strlenW(prop) + 2;
990 if (deformated)
991 len += strlenW(deformated);
993 cmd = msi_alloc(sizeof(WCHAR)*len);
995 strcpyW(cmd,prop);
996 if (deformated)
998 strcatW(cmd,spc);
999 strcatW(cmd,deformated);
1001 msi_free(deformated);
1003 msi_free(prop);
1005 TRACE("executing exe %s\n", debugstr_w(cmd));
1007 rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
1008 c_collen, &si, &info);
1010 if ( !rc )
1012 ERR("Unable to execute command %s\n", debugstr_w(cmd));
1013 msi_free(cmd);
1014 return ERROR_SUCCESS;
1016 msi_free(cmd);
1018 CloseHandle( info.hThread );
1020 return wait_process_handle(package, type, info.hProcess, action);
1023 static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
1024 LPCWSTR target, const INT type, LPCWSTR action)
1026 LPWSTR filename, deformated;
1027 STARTUPINFOW si;
1028 PROCESS_INFORMATION info;
1029 BOOL rc;
1031 memset(&si,0,sizeof(STARTUPINFOW));
1033 filename = resolve_folder(package, source, FALSE, FALSE, TRUE, NULL);
1035 if (!filename)
1036 return ERROR_FUNCTION_FAILED;
1038 SetCurrentDirectoryW(filename);
1039 msi_free(filename);
1041 deformat_string(package,target,&deformated);
1043 if (!deformated)
1044 return ERROR_FUNCTION_FAILED;
1046 TRACE("executing exe %s\n", debugstr_w(deformated));
1048 rc = CreateProcessW(NULL, deformated, NULL, NULL, FALSE, 0, NULL,
1049 c_collen, &si, &info);
1051 if ( !rc )
1053 ERR("Unable to execute command %s\n", debugstr_w(deformated));
1054 msi_free(deformated);
1055 return ERROR_SUCCESS;
1057 msi_free(deformated);
1058 CloseHandle( info.hThread );
1060 return wait_process_handle(package, type, info.hProcess, action);
1063 static DWORD WINAPI ACTION_CallScript( const GUID *guid )
1065 msi_custom_action_info *info;
1066 MSIHANDLE hPackage;
1067 UINT r = ERROR_FUNCTION_FAILED;
1069 info = find_action_by_guid( guid );
1070 if (!info)
1072 ERR("failed to find action %s\n", debugstr_guid( guid) );
1073 return r;
1076 TRACE("function %s, script %s\n", debugstr_w( info->target ), debugstr_w( info->source ) );
1078 hPackage = alloc_msihandle( &info->package->hdr );
1079 if (hPackage)
1081 r = call_script( hPackage, info->type, info->source, info->target, info->action );
1082 MsiCloseHandle( hPackage );
1084 else
1085 ERR("failed to create handle for %p\n", info->package );
1087 if (info->type & msidbCustomActionTypeAsync &&
1088 info->type & msidbCustomActionTypeContinue)
1089 free_custom_action_data( info );
1091 return S_OK;
1094 static DWORD WINAPI ScriptThread( LPVOID arg )
1096 LPGUID guid = arg;
1097 DWORD rc = 0;
1099 TRACE("custom action (%x) started\n", GetCurrentThreadId() );
1101 rc = ACTION_CallScript( guid );
1103 TRACE("custom action (%x) returned %i\n", GetCurrentThreadId(), rc );
1105 MsiCloseAllHandles();
1106 return rc;
1109 static msi_custom_action_info *do_msidbCustomActionTypeScript(
1110 MSIPACKAGE *package, INT type, LPCWSTR script, LPCWSTR function, LPCWSTR action )
1112 msi_custom_action_info *info;
1114 info = msi_alloc( sizeof *info );
1115 if (!info)
1116 return NULL;
1118 msiobj_addref( &package->hdr );
1119 info->package = package;
1120 info->type = type;
1121 info->target = strdupW( function );
1122 info->source = strdupW( script );
1123 info->action = strdupW( action );
1124 CoCreateGuid( &info->guid );
1126 EnterCriticalSection( &msi_custom_action_cs );
1127 list_add_tail( &msi_pending_custom_actions, &info->entry );
1128 LeaveCriticalSection( &msi_custom_action_cs );
1130 info->handle = CreateThread( NULL, 0, ScriptThread, &info->guid, 0, NULL );
1131 if (!info->handle)
1133 free_custom_action_data( info );
1134 return NULL;
1137 return info;
1140 static UINT HANDLE_CustomType37_38(MSIPACKAGE *package, LPCWSTR source,
1141 LPCWSTR target, const INT type, LPCWSTR action)
1143 msi_custom_action_info *info;
1145 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1147 info = do_msidbCustomActionTypeScript( package, type, target, NULL, action );
1149 return wait_thread_handle( info );
1152 static UINT HANDLE_CustomType5_6(MSIPACKAGE *package, LPCWSTR source,
1153 LPCWSTR target, const INT type, LPCWSTR action)
1155 static const WCHAR query[] = {
1156 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1157 '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',' ',
1158 '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
1159 MSIRECORD *row = 0;
1160 msi_custom_action_info *info;
1161 CHAR *buffer = NULL;
1162 WCHAR *bufferw = NULL;
1163 DWORD sz = 0;
1164 UINT r;
1166 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1168 row = MSI_QueryGetRecord(package->db, query, source);
1169 if (!row)
1170 return ERROR_FUNCTION_FAILED;
1172 r = MSI_RecordReadStream(row, 2, NULL, &sz);
1173 if (r != ERROR_SUCCESS)
1174 return r;
1176 buffer = msi_alloc(sizeof(CHAR)*(sz+1));
1177 if (!buffer)
1178 return ERROR_FUNCTION_FAILED;
1180 r = MSI_RecordReadStream(row, 2, buffer, &sz);
1181 if (r != ERROR_SUCCESS)
1182 goto done;
1184 buffer[sz] = 0;
1185 bufferw = strdupAtoW(buffer);
1186 if (!bufferw)
1188 r = ERROR_FUNCTION_FAILED;
1189 goto done;
1192 info = do_msidbCustomActionTypeScript( package, type, bufferw, target, action );
1193 r = wait_thread_handle( info );
1195 done:
1196 msi_free(bufferw);
1197 msi_free(buffer);
1198 return r;
1201 static UINT HANDLE_CustomType21_22(MSIPACKAGE *package, LPCWSTR source,
1202 LPCWSTR target, const INT type, LPCWSTR action)
1204 msi_custom_action_info *info;
1205 MSIFILE *file;
1206 HANDLE hFile;
1207 DWORD sz, szHighWord = 0, read;
1208 CHAR *buffer=NULL;
1209 WCHAR *bufferw=NULL;
1210 BOOL bRet;
1211 UINT r;
1213 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1215 file = get_loaded_file(package,source);
1216 if (!file)
1218 ERR("invalid file key %s\n", debugstr_w(source));
1219 return ERROR_FUNCTION_FAILED;
1222 hFile = CreateFileW(file->TargetPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
1223 if (hFile == INVALID_HANDLE_VALUE)
1224 return ERROR_FUNCTION_FAILED;
1226 sz = GetFileSize(hFile, &szHighWord);
1227 if (sz == INVALID_FILE_SIZE || szHighWord != 0)
1229 CloseHandle(hFile);
1230 return ERROR_FUNCTION_FAILED;
1233 buffer = msi_alloc(sizeof(CHAR)*(sz+1));
1234 if (!buffer)
1236 CloseHandle(hFile);
1237 return ERROR_FUNCTION_FAILED;
1240 bRet = ReadFile(hFile, buffer, sz, &read, NULL);
1241 CloseHandle(hFile);
1242 if (!bRet)
1244 r = ERROR_FUNCTION_FAILED;
1245 goto done;
1248 buffer[read] = 0;
1249 bufferw = strdupAtoW(buffer);
1250 if (!bufferw)
1252 r = ERROR_FUNCTION_FAILED;
1253 goto done;
1256 info = do_msidbCustomActionTypeScript( package, type, bufferw, target, action );
1257 r = wait_thread_handle( info );
1259 done:
1260 msi_free(bufferw);
1261 msi_free(buffer);
1262 return r;
1265 static UINT HANDLE_CustomType53_54(MSIPACKAGE *package, LPCWSTR source,
1266 LPCWSTR target, const INT type, LPCWSTR action)
1268 msi_custom_action_info *info;
1269 WCHAR *prop;
1271 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1273 prop = msi_dup_property(package,source);
1274 if (!prop)
1275 return ERROR_SUCCESS;
1277 info = do_msidbCustomActionTypeScript( package, type, prop, NULL, action );
1278 msi_free(prop);
1279 return wait_thread_handle( info );
1282 void ACTION_FinishCustomActions(const MSIPACKAGE* package)
1284 struct list *item;
1285 HANDLE *wait_handles;
1286 unsigned int handle_count, i;
1287 msi_custom_action_info *info, *cursor;
1289 while ((item = list_head( &package->RunningActions )))
1291 MSIRUNNINGACTION *action = LIST_ENTRY( item, MSIRUNNINGACTION, entry );
1293 list_remove( &action->entry );
1295 TRACE("waiting for %s\n", debugstr_w( action->name ) );
1296 msi_dialog_check_messages( action->handle );
1298 CloseHandle( action->handle );
1299 msi_free( action->name );
1300 msi_free( action );
1303 EnterCriticalSection( &msi_custom_action_cs );
1305 handle_count = list_count( &msi_pending_custom_actions );
1306 wait_handles = HeapAlloc( GetProcessHeap(), 0, handle_count * sizeof(HANDLE) );
1308 handle_count = 0;
1309 LIST_FOR_EACH_ENTRY_SAFE( info, cursor, &msi_pending_custom_actions, msi_custom_action_info, entry )
1311 if (info->package == package )
1313 if (DuplicateHandle(GetCurrentProcess(), info->handle, GetCurrentProcess(), &wait_handles[handle_count], SYNCHRONIZE, FALSE, 0))
1314 handle_count++;
1315 free_custom_action_data( info );
1319 LeaveCriticalSection( &msi_custom_action_cs );
1321 for (i = 0; i < handle_count; i++)
1323 msi_dialog_check_messages( wait_handles[i] );
1324 CloseHandle( wait_handles[i] );
1327 HeapFree( GetProcessHeap(), 0, wait_handles );