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