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