custom.c 42.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Custom Action processing for the Microsoft Installer (msi.dll)
 *
 * Copyright 2005 Aric Stewart for CodeWeavers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 22 23
#include "config.h"
#include "wine/port.h"

24 25
#define COBJMACROS

26
#include <stdarg.h>
27 28 29
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
30
#include "msidefs.h"
31
#include "winuser.h"
32 33
#include "objbase.h"
#include "oleauto.h"
34 35

#include "msipriv.h"
36
#include "msiserver.h"
37
#include "wine/debug.h"
38
#include "wine/unicode.h"
39
#include "wine/exception.h"
40 41 42 43 44 45 46

WINE_DEFAULT_DEBUG_CHANNEL(msi);

#define CUSTOM_ACTION_TYPE_MASK 0x3F
static const WCHAR c_collen[] = {'C',':','\\',0};
static const WCHAR cszTempFolder[]= {'T','e','m','p','F','o','l','d','e','r',0};

47 48 49 50 51 52 53 54 55

static const WCHAR szActionData[] = {
    'C','u','s','t','o','m','A','c','t','i','o','n','D','a','t','a',0
};
static const WCHAR ProdCode[] = {
    'P','r','o','d','u','c','t','C','o','d','e',0
};
static const WCHAR UserSID[] = {'U','s','e','r','S','I','D',0};

56 57
typedef struct tagMSIRUNNINGACTION
{
58
    struct list entry;
59 60 61 62 63 64 65 66 67
    HANDLE handle;
    BOOL   process;
    LPWSTR name;
} MSIRUNNINGACTION;

static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source,
                               LPCWSTR target, const INT type, LPCWSTR action);
static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source,
                               LPCWSTR target, const INT type, LPCWSTR action);
68 69
static UINT HANDLE_CustomType17(MSIPACKAGE *package, LPCWSTR source,
                                LPCWSTR target, const INT type, LPCWSTR action);
70 71
static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source,
                                LPCWSTR target, const INT type, LPCWSTR action);
72 73
static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
                                LPCWSTR target, const INT type, LPCWSTR action);
74 75
static UINT HANDLE_CustomType23(MSIPACKAGE *package, LPCWSTR source,
                                LPCWSTR target, const INT type, LPCWSTR action);
76 77 78 79
static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source,
                                LPCWSTR target, const INT type, LPCWSTR action);
static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
                                LPCWSTR target, const INT type, LPCWSTR action);
80 81 82 83 84 85 86 87
static UINT HANDLE_CustomType37_38(MSIPACKAGE *package, LPCWSTR source,
                                LPCWSTR target, const INT type, LPCWSTR action);
static UINT HANDLE_CustomType5_6(MSIPACKAGE *package, LPCWSTR source,
                                LPCWSTR target, const INT type, LPCWSTR action);
static UINT HANDLE_CustomType21_22(MSIPACKAGE *package, LPCWSTR source,
                                LPCWSTR target, const INT type, LPCWSTR action);
static UINT HANDLE_CustomType53_54(MSIPACKAGE *package, LPCWSTR source,
                                LPCWSTR target, const INT type, LPCWSTR action);
88

89
typedef UINT (WINAPI *MsiCustomActionEntryPoint)( MSIHANDLE );
90

91 92 93 94 95 96 97 98 99 100 101 102
static CRITICAL_SECTION msi_custom_action_cs;
static CRITICAL_SECTION_DEBUG msi_custom_action_cs_debug =
{
    0, 0, &msi_custom_action_cs,
    { &msi_custom_action_cs_debug.ProcessLocksList,
      &msi_custom_action_cs_debug.ProcessLocksList },
      0, 0, { (DWORD_PTR)(__FILE__ ": msi_custom_action_cs") }
};
static CRITICAL_SECTION msi_custom_action_cs = { &msi_custom_action_cs_debug, -1, 0, 0, 0, 0 };

static struct list msi_pending_custom_actions = LIST_INIT( msi_pending_custom_actions );

103 104 105 106 107
static BOOL check_execution_scheduling_options(MSIPACKAGE *package, LPCWSTR action, UINT options)
{
    if (!package->script)
        return TRUE;

108
    if ((options & msidbCustomActionTypeClientRepeat) ==
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
            msidbCustomActionTypeClientRepeat)
    {
        if (!(package->script->InWhatSequence & SEQUENCE_UI &&
            package->script->InWhatSequence & SEQUENCE_EXEC))
        {
            TRACE("Skipping action due to dbCustomActionTypeClientRepeat option.\n");
            return FALSE;
        }
    }
    else if (options & msidbCustomActionTypeFirstSequence)
    {
        if (package->script->InWhatSequence & SEQUENCE_UI &&
            package->script->InWhatSequence & SEQUENCE_EXEC )
        {
            TRACE("Skipping action due to msidbCustomActionTypeFirstSequence option.\n");
            return FALSE;
        }
    }
    else if (options & msidbCustomActionTypeOncePerProcess)
    {
        if (check_unique_action(package,action))
        {
            TRACE("Skipping action due to msidbCustomActionTypeOncePerProcess option.\n");
            return FALSE;
        }
        else
            register_unique_action(package,action);
    }

    return TRUE;
}

141 142
/* stores the following properties before the action:
 *
143
 *    [CustomActionData<=>UserSID<=>ProductCode]Action
144
 */
145 146
static LPWSTR msi_get_deferred_action(LPCWSTR action, LPCWSTR actiondata,
                                      LPCWSTR usersid, LPCWSTR prodcode)
147 148 149 150
{
    LPWSTR deferred;
    DWORD len;

151 152 153
    static const WCHAR format[] = {
            '[','%','s','<','=','>','%','s','<','=','>','%','s',']','%','s',0
    };
154 155 156 157

    if (!actiondata)
        return strdupW(action);

158
    len = lstrlenW(action) + lstrlenW(actiondata) +
159 160
          lstrlenW(usersid) + lstrlenW(prodcode) +
          lstrlenW(format) - 7;
161 162
    deferred = msi_alloc(len * sizeof(WCHAR));

163
    sprintfW(deferred, format, actiondata, usersid, prodcode, action);
164 165 166
    return deferred;
}

167 168
static void set_deferred_action_props(MSIPACKAGE *package, LPWSTR deferred_data)
{
169
    LPWSTR end, beg = deferred_data + 1;
170

171 172 173
    static const WCHAR sep[] = {'<','=','>',0};

    end = strstrW(beg, sep);
174 175
    *end = '\0';
    MSI_SetPropertyW(package, szActionData, beg);
176
    beg = end + 3;
177

178
    end = strstrW(beg, sep);
179 180
    *end = '\0';
    MSI_SetPropertyW(package, UserSID, beg);
181
    beg = end + 3;
182 183 184 185 186 187

    end = strchrW(beg, ']');
    *end = '\0';
    MSI_SetPropertyW(package, ProdCode, beg);
}

188
UINT ACTION_CustomAction(MSIPACKAGE *package, LPCWSTR action, UINT script, BOOL execute)
189 190 191 192
{
    UINT rc = ERROR_SUCCESS;
    MSIRECORD * row = 0;
    static const WCHAR ExecSeqQuery[] =
193 194 195 196
    {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
     '`','C','u','s','t','o' ,'m','A','c','t','i','o','n','`',
     ' ','W','H','E','R','E',' ','`','A','c','t','i' ,'o','n','`',' ',
     '=',' ','\'','%','s','\'',0};
197
    UINT type;
198
    LPCWSTR source, target;
199 200
    LPWSTR ptr, deferred_data = NULL;
    LPWSTR action_copy = strdupW(action);
201 202
    WCHAR *deformated=NULL;

203
    /* deferred action: [properties]Action */
204
    if ((ptr = strrchrW(action_copy, ']')))
205
    {
206
        deferred_data = action_copy;
207 208 209
        action = ptr + 1;
    }

210 211
    row = MSI_QueryGetRecord( package->db, ExecSeqQuery, action );
    if (!row)
212 213
    {
        msi_free(action_copy);
214
        return ERROR_CALL_NOT_IMPLEMENTED;
215
    }
216 217 218

    type = MSI_RecordGetInteger(row,2);

219 220
    source = MSI_RecordGetString(row,3);
    target = MSI_RecordGetString(row,4);
221 222 223 224 225

    TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type,
          debugstr_w(source), debugstr_w(target));

    /* handle some of the deferred actions */
226 227 228 229
    if (type & msidbCustomActionTypeTSAware)
        FIXME("msidbCustomActionTypeTSAware not handled\n");

    if (type & msidbCustomActionTypeInScript)
230
    {
231
        if (type & msidbCustomActionTypeNoImpersonate)
232
            WARN("msidbCustomActionTypeNoImpersonate not handled\n");
233 234

        if (type & msidbCustomActionTypeRollback)
235 236
        {
            FIXME("Rollback only action... rollbacks not supported yet\n");
237
            schedule_action(package, ROLLBACK_SCRIPT, action);
Mike McCormack's avatar
Mike McCormack committed
238 239
            rc = ERROR_SUCCESS;
            goto end;
240 241 242
        }
        if (!execute)
        {
243
            LPWSTR actiondata = msi_dup_property(package, action);
244 245 246
            LPWSTR usersid = msi_dup_property(package, UserSID);
            LPWSTR prodcode = msi_dup_property(package, ProdCode);
            LPWSTR deferred = msi_get_deferred_action(action, actiondata, usersid, prodcode);
247

248
            if (type & msidbCustomActionTypeCommit)
249 250
            {
                TRACE("Deferring Commit Action!\n");
251
                schedule_action(package, COMMIT_SCRIPT, deferred);
252 253 254 255
            }
            else
            {
                TRACE("Deferring Action!\n");
256
                schedule_action(package, INSTALL_SCRIPT, deferred);
257 258
            }

Mike McCormack's avatar
Mike McCormack committed
259
            rc = ERROR_SUCCESS;
260 261 262
            msi_free(actiondata);
            msi_free(usersid);
            msi_free(prodcode);
263
            msi_free(deferred);
Mike McCormack's avatar
Mike McCormack committed
264
            goto end;
265 266 267
        }
        else
        {
268
            static const WCHAR szBlank[] = {0};
269

270
            LPWSTR actiondata = msi_dup_property( package, action );
271

272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
            switch (script)
            {
            case INSTALL_SCRIPT:
                package->scheduled_action_running = TRUE;
                break;
            case COMMIT_SCRIPT:
                package->commit_action_running = TRUE;
                break;
            case ROLLBACK_SCRIPT:
                package->rollback_action_running = TRUE;
                break;
            default:
                break;
            }

287
            if (deferred_data)
288
                set_deferred_action_props(package, deferred_data);
289
            else if (actiondata)
290
                MSI_SetPropertyW(package,szActionData,actiondata);
291 292
            else
                MSI_SetPropertyW(package,szActionData,szBlank);
293

294
            msi_free(actiondata);
295 296
        }
    }
297
    else if (!check_execution_scheduling_options(package,action,type))
298
    {
Mike McCormack's avatar
Mike McCormack committed
299 300
        rc = ERROR_SUCCESS;
        goto end;
301
    }
302 303 304 305 306 307

    switch (type & CUSTOM_ACTION_TYPE_MASK)
    {
        case 1: /* DLL file stored in a Binary table stream */
            rc = HANDLE_CustomType1(package,source,target,type,action);
            break;
308
        case 2: /* EXE file stored in a Binary table stream */
309 310 311 312 313
            rc = HANDLE_CustomType2(package,source,target,type,action);
            break;
        case 18: /*EXE file installed with package */
            rc = HANDLE_CustomType18(package,source,target,type,action);
            break;
314
        case 19: /* Error that halts install */
315
            rc = HANDLE_CustomType19(package,source,target,type,action);
316
            break;
317 318 319
        case 17:
            rc = HANDLE_CustomType17(package,source,target,type,action);
            break;
320 321 322
        case 23: /* installs another package in the source tree */
            deformat_string(package,target,&deformated);
            rc = HANDLE_CustomType23(package,source,deformated,type,action);
323
            msi_free(deformated);
324
            break;
325 326 327 328 329 330 331 332 333
        case 50: /*EXE file specified by a property value */
            rc = HANDLE_CustomType50(package,source,target,type,action);
            break;
        case 34: /*EXE to be run in specified directory */
            rc = HANDLE_CustomType34(package,source,target,type,action);
            break;
        case 35: /* Directory set with formatted text. */
            deformat_string(package,target,&deformated);
            MSI_SetTargetPathW(package, source, deformated);
334
            msi_free(deformated);
335 336
            break;
        case 51: /* Property set with formatted text. */
337 338 339
            if (!source)
                break;

340 341
            deformat_string(package,target,&deformated);
            rc = MSI_SetPropertyW(package,source,deformated);
342
            msi_free(deformated);
343
            break;
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
	case 37: /* JScript/VBScript text stored in target column. */
	case 38:
	    rc = HANDLE_CustomType37_38(package,source,target,type,action);
	    break;
	case 5:
	case 6: /* JScript/VBScript file stored in a Binary table stream. */
	    rc = HANDLE_CustomType5_6(package,source,target,type,action);
	    break;
	case 21: /* JScript/VBScript file installed with the product. */
	case 22:
	    rc = HANDLE_CustomType21_22(package,source,target,type,action);
	    break;
	case 53: /* JScript/VBScript text specified by a property value. */
	case 54:
	    rc = HANDLE_CustomType53_54(package,source,target,type,action);
	    break;
360 361 362 363 364 365
        default:
            FIXME("UNHANDLED ACTION TYPE %i (%s %s)\n",
             type & CUSTOM_ACTION_TYPE_MASK, debugstr_w(source),
             debugstr_w(target));
    }

Mike McCormack's avatar
Mike McCormack committed
366
end:
367 368 369
    package->scheduled_action_running = FALSE;
    package->commit_action_running = FALSE;
    package->rollback_action_running = FALSE;
370
    msi_free(action_copy);
371 372 373 374 375
    msiobj_release(&row->hdr);
    return rc;
}


376
static UINT store_binary_to_temp(MSIPACKAGE *package, LPCWSTR source,
377 378
                                LPWSTR tmp_file)
{
379 380 381 382 383 384 385
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',' ',
        '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
    MSIRECORD *row = 0;
    HANDLE file;
    CHAR buffer[1024];
386 387
    static const WCHAR f1[] = {'m','s','i',0};
    WCHAR fmt[MAX_PATH];
388
    DWORD sz = MAX_PATH;
389
    UINT r;
390

Mike McCormack's avatar
Mike McCormack committed
391
    if (MSI_GetPropertyW(package, cszTempFolder, fmt, &sz) != ERROR_SUCCESS)
392
        GetTempPathW(MAX_PATH, fmt);
393

394
    if (GetTempFileNameW(fmt, f1, 0, tmp_file) == 0)
395
    {
396 397
        TRACE("Unable to create file\n");
        return ERROR_FUNCTION_FAILED;
398
    }
399
    track_tempfile(package, tmp_file);
400

401 402 403
    row = MSI_QueryGetRecord(package->db, query, source);
    if (!row)
        return ERROR_FUNCTION_FAILED;
404

405 406 407 408 409 410
    /* write out the file */
    file = CreateFileW(tmp_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
                       FILE_ATTRIBUTE_NORMAL, NULL);
    if (file == INVALID_HANDLE_VALUE)
        r = ERROR_FUNCTION_FAILED;
    else
411
    {
412
        do
413
        {
414 415 416 417 418 419 420 421 422 423 424 425
            DWORD write;
            sz = sizeof buffer;
            r = MSI_RecordReadStream(row, 2, buffer, &sz);
            if (r != ERROR_SUCCESS)
            {
                ERR("Failed to get stream\n");
                break;
            }
            WriteFile(file, buffer, sz, &write, NULL);
        } while (sz == sizeof buffer);
        CloseHandle(file);
    }
426

427
    msiobj_release(&row->hdr);
428

429
    return r;
430 431
}

432
static void file_running_action(MSIPACKAGE* package, HANDLE Handle,
433 434
                                BOOL process, LPCWSTR name)
{
435 436
    MSIRUNNINGACTION *action;

437
    action = msi_alloc( sizeof(MSIRUNNINGACTION) );
438

439 440 441
    action->handle = Handle;
    action->process = process;
    action->name = strdupW(name);
442

443
    list_add_tail( &package->RunningActions, &action->entry );
444 445
}

446
static UINT custom_get_process_return( HANDLE process )
447
{
448
    DWORD rc = 0;
449

450 451 452 453 454 455
    GetExitCodeProcess( process, &rc );
    if (rc != 0)
        return ERROR_FUNCTION_FAILED;
    return ERROR_SUCCESS;
}

456
static UINT custom_get_thread_return( MSIPACKAGE *package, HANDLE thread )
457 458 459 460
{
    DWORD rc = 0;

    GetExitCodeThread( thread, &rc );
461 462 463

    switch (rc)
    {
464 465 466 467 468 469 470
    case ERROR_FUNCTION_NOT_CALLED:
    case ERROR_SUCCESS:
    case ERROR_INSTALL_USEREXIT:
    case ERROR_INSTALL_FAILURE:
        return rc;
    case ERROR_NO_MORE_ITEMS:
        return ERROR_SUCCESS;
471 472 473
    case ERROR_INSTALL_SUSPEND:
        ACTION_ForceReboot( package );
        return ERROR_SUCCESS;
474
    default:
475
        ERR("Invalid Return Code %d\n",rc);
476
        return ERROR_INSTALL_FAILURE;
477 478 479
    }
}

480 481
static UINT wait_process_handle(MSIPACKAGE* package, UINT type,
                           HANDLE ProcessHandle, LPCWSTR name)
482 483 484
{
    UINT rc = ERROR_SUCCESS;

485
    if (!(type & msidbCustomActionTypeAsync))
486
    {
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
        TRACE("waiting for %s\n", debugstr_w(name));

        msi_dialog_check_messages(ProcessHandle);

        if (!(type & msidbCustomActionTypeContinue))
            rc = custom_get_process_return(ProcessHandle);

        CloseHandle(ProcessHandle);
    }
    else
    {
        TRACE("%s running in background\n", debugstr_w(name));

        if (!(type & msidbCustomActionTypeContinue))
            file_running_action(package, ProcessHandle, TRUE, name);
502
        else
503 504 505 506 507 508
            CloseHandle(ProcessHandle);
    }

    return rc;
}

509 510
typedef struct _msi_custom_action_info {
    struct list entry;
511
    LONG refs;
512
    MSIPACKAGE *package;
513 514
    LPWSTR source;
    LPWSTR target;
515 516 517
    HANDLE handle;
    LPWSTR action;
    INT type;
518
    GUID guid;
519 520
} msi_custom_action_info;

521
static void release_custom_action_data( msi_custom_action_info *info )
522
{
523
    EnterCriticalSection( &msi_custom_action_cs );
524 525 526 527 528 529 530 531 532 533 534 535 536

    if (!--info->refs)
    {
        list_remove( &info->entry );
        if (info->handle)
            CloseHandle( info->handle );
        msi_free( info->action );
        msi_free( info->source );
        msi_free( info->target );
        msiobj_release( &info->package->hdr );
        msi_free( info );
    }

537
    LeaveCriticalSection( &msi_custom_action_cs );
538 539
}

540 541 542 543 544 545
/* must be called inside msi_custom_action_cs if info is in the pending custom actions list */
static void addref_custom_action_data( msi_custom_action_info *info )
{
    info->refs++;
 }

546
static UINT wait_thread_handle( msi_custom_action_info *info )
547 548 549
{
    UINT rc = ERROR_SUCCESS;

550
    if (!(info->type & msidbCustomActionTypeAsync))
551
    {
552
        TRACE("waiting for %s\n", debugstr_w( info->action ));
553

554
        msi_dialog_check_messages( info->handle );
555

556
        if (!(info->type & msidbCustomActionTypeContinue))
557
            rc = custom_get_thread_return( info->package, info->handle );
558

559
        release_custom_action_data( info );
560
    }
561
    else
562
    {
563
        TRACE("%s running in background\n", debugstr_w( info->action ));
564 565 566 567 568
    }

    return rc;
}

569
static msi_custom_action_info *find_action_by_guid( const GUID *guid )
570 571 572 573 574 575 576 577 578 579
{
    msi_custom_action_info *info;
    BOOL found = FALSE;

    EnterCriticalSection( &msi_custom_action_cs );

    LIST_FOR_EACH_ENTRY( info, &msi_pending_custom_actions, msi_custom_action_info, entry )
    {
        if (IsEqualGUID( &info->guid, guid ))
        {
580
            addref_custom_action_data( info );
581 582 583 584 585 586 587 588 589 590 591 592
            found = TRUE;
            break;
        }
    }

    LeaveCriticalSection( &msi_custom_action_cs );

    if (!found)
        return NULL;

    return info;
}
593

594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
static void handle_msi_break( LPCWSTR target )
{
    LPWSTR msg;
    WCHAR val[MAX_PATH];

    static const WCHAR MsiBreak[] = { 'M','s','i','B','r','e','a','k',0 };
    static const WCHAR WindowsInstaller[] = {
        'W','i','n','d','o','w','s',' ','I','n','s','t','a','l','l','e','r',0
    };

    static const WCHAR format[] = {
        'T','o',' ','d','e','b','u','g',' ','y','o','u','r',' ',
        'c','u','s','t','o','m',' ','a','c','t','i','o','n',',',' ',
        'a','t','t','a','c','h',' ','y','o','u','r',' ','d','e','b','u','g','g','e','r',' ',
        't','o',' ','p','r','o','c','e','s','s',' ','%','i',' ','(','0','x','%','X',')',' ',
        'a','n','d',' ','p','r','e','s','s',' ','O','K',0
    };

    if( !GetEnvironmentVariableW( MsiBreak, val, MAX_PATH ))
        return;

    if( lstrcmpiW( val, target ))
        return;

    msg = msi_alloc( (lstrlenW(format) + 10) * sizeof(WCHAR) );
    if (!msg)
        return;

    wsprintfW( msg, format, GetCurrentProcessId(), GetCurrentProcessId());
    MessageBoxW( NULL, msg, WindowsInstaller, MB_OK);
    msi_free(msg);
    DebugBreak();
}

628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661
static UINT get_action_info( const GUID *guid, INT *type, MSIHANDLE *handle,
                             BSTR *dll, BSTR *funcname,
                             IWineMsiRemotePackage **package )
{
    IClassFactory *cf = NULL;
    IWineMsiRemoteCustomAction *rca = NULL;
    HRESULT r;

    r = DllGetClassObject( &CLSID_IWineMsiRemoteCustomAction,
                           &IID_IClassFactory, (LPVOID *)&cf );
    if (FAILED(r))
    {
        ERR("failed to get IClassFactory interface\n");
        return ERROR_FUNCTION_FAILED;
    }

    r = IClassFactory_CreateInstance( cf, NULL, &IID_IWineMsiRemoteCustomAction, (LPVOID *)&rca );
    if (FAILED(r))
    {
        ERR("failed to get IWineMsiRemoteCustomAction interface\n");
        return ERROR_FUNCTION_FAILED;
    }

    r = IWineMsiRemoteCustomAction_GetActionInfo( rca, guid, type, handle, dll, funcname, package );
    IWineMsiRemoteCustomAction_Release( rca );
    if (FAILED(r))
    {
        ERR("GetActionInfo failed\n");
        return ERROR_FUNCTION_FAILED;
    }

    return ERROR_SUCCESS;
}

662
static DWORD ACTION_CallDllFunction( const GUID *guid )
663
{
664
    MsiCustomActionEntryPoint fn;
665
    MSIHANDLE hPackage, handle;
666 667
    HANDLE hModule;
    LPSTR proc;
668
    UINT r = ERROR_FUNCTION_FAILED;
669 670 671
    BSTR dll = NULL, function = NULL;
    INT type;
    IWineMsiRemotePackage *remote_package = NULL;
672

673
    TRACE("%s\n", debugstr_guid( guid ));
674

675 676 677
    r = get_action_info( guid, &type, &handle, &dll, &function, &remote_package );
    if (r != ERROR_SUCCESS)
        return r;
678

679
    hModule = LoadLibraryW( dll );
680
    if (!hModule)
681
    {
682
        ERR("failed to load dll %s\n", debugstr_w( dll ) );
683 684
        return r;
    }
685

686
    proc = strdupWtoA( function );
687 688 689 690
    fn = (MsiCustomActionEntryPoint) GetProcAddress( hModule, proc );
    msi_free( proc );
    if (fn)
    {
691
        hPackage = alloc_msi_remote_handle( (IUnknown *)remote_package );
692 693
        if (hPackage)
        {
694 695 696
            IWineMsiRemotePackage_SetMsiHandle( remote_package, handle );
            TRACE("calling %s\n", debugstr_w( function ) );
            handle_msi_break( function );
697

698 699
            CoInitialize(NULL);

700 701 702 703 704 705 706
            __TRY
            {
                r = fn( hPackage );
            }
            __EXCEPT_PAGE_FAULT
            {
                ERR("Custom action (%s:%s) caused a page fault: %08x\n",
707
                    debugstr_w(dll), debugstr_w(function), GetExceptionCode());
708 709 710 711
                r = ERROR_SUCCESS;
            }
            __ENDTRY;

712 713
            CoUninitialize();

714
            MsiCloseHandle( hPackage );
715 716
        }
        else
717
            ERR("failed to create handle for %p\n", remote_package );
718 719
    }
    else
720
        ERR("GetProcAddress(%s) failed\n", debugstr_w( function ) );
721 722 723

    FreeLibrary(hModule);

724 725 726 727
    IWineMsiRemotePackage_Release( remote_package );
    SysFreeString( dll );
    SysFreeString( function );
    MsiCloseHandle( handle );
728

729
    return r;
730 731
}

732
static DWORD WINAPI DllThread( LPVOID arg )
733
{
734
    LPGUID guid = arg;
735
    DWORD rc = 0;
736

737
    TRACE("custom action (%x) started\n", GetCurrentThreadId() );
738

739
    rc = ACTION_CallDllFunction( guid );
740 741

    TRACE("custom action (%x) returned %i\n", GetCurrentThreadId(), rc );
742 743 744 745 746

    MsiCloseAllHandles();
    return rc;
}

747
static DWORD ACTION_CAInstallPackage(const GUID *guid)
748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763
{
    msi_custom_action_info *info;
    UINT r = ERROR_FUNCTION_FAILED;
    INSTALLUILEVEL old_level;

    info = find_action_by_guid(guid);
    if (!info)
    {
        ERR("failed to find action %s\n", debugstr_guid(guid));
        return r;
    }

    old_level = MsiSetInternalUI(INSTALLUILEVEL_BASIC, NULL);
    r = MsiInstallProductW(info->source, info->target);
    MsiSetInternalUI(old_level, NULL);

764 765
    release_custom_action_data(info);

766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783
    return r;
}

static DWORD WINAPI ConcurrentInstallThread(LPVOID arg)
{
    LPGUID guid = arg;
    DWORD rc;

    TRACE("concurrent installation (%x) started\n", GetCurrentThreadId());

    rc = ACTION_CAInstallPackage(guid);

    TRACE("concurrent installation (%x) returned %i\n", GetCurrentThreadId(), rc);

    MsiCloseAllHandles();
    return rc;
}

784
static msi_custom_action_info *do_msidbCustomActionTypeDll(
785
    MSIPACKAGE *package, INT type, LPCWSTR source, LPCWSTR target, LPCWSTR action )
786
{
787 788 789 790 791
    msi_custom_action_info *info;

    info = msi_alloc( sizeof *info );
    if (!info)
        return NULL;
792 793

    msiobj_addref( &package->hdr );
794
    info->refs = 2; /* 1 for our caller and 1 for thread we created */
795
    info->package = package;
796
    info->type = type;
797 798
    info->target = strdupW( target );
    info->source = strdupW( source );
799
    info->action = strdupW( action );
800 801 802 803 804
    CoCreateGuid( &info->guid );

    EnterCriticalSection( &msi_custom_action_cs );
    list_add_tail( &msi_pending_custom_actions, &info->entry );
    LeaveCriticalSection( &msi_custom_action_cs );
805

806
    info->handle = CreateThread( NULL, 0, DllThread, &info->guid, 0, NULL );
807 808
    if (!info->handle)
    {
809 810 811
        /* release both references */
        release_custom_action_data( info );
        release_custom_action_data( info );
812 813
        return NULL;
    }
814

815
    return info;
816 817
}

818 819 820 821 822 823 824 825 826 827
static msi_custom_action_info *do_msidbCAConcurrentInstall(
    MSIPACKAGE *package, INT type, LPCWSTR source, LPCWSTR target, LPCWSTR action)
{
    msi_custom_action_info *info;

    info = msi_alloc( sizeof *info );
    if (!info)
        return NULL;

    msiobj_addref( &package->hdr );
828
    info->refs = 2; /* 1 for our caller and 1 for thread we created */
829 830 831 832 833 834 835 836 837 838 839 840 841 842
    info->package = package;
    info->type = type;
    info->target = strdupW( target );
    info->source = strdupW( source );
    info->action = strdupW( action );
    CoCreateGuid( &info->guid );

    EnterCriticalSection( &msi_custom_action_cs );
    list_add_tail( &msi_pending_custom_actions, &info->entry );
    LeaveCriticalSection( &msi_custom_action_cs );

    info->handle = CreateThread( NULL, 0, ConcurrentInstallThread, &info->guid, 0, NULL );
    if (!info->handle)
    {
843 844 845
        /* release both references */
        release_custom_action_data( info );
        release_custom_action_data( info );
846 847 848 849 850 851 852 853 854 855 856 857 858 859 860
        return NULL;
    }

    return info;
}

static UINT HANDLE_CustomType23(MSIPACKAGE *package, LPCWSTR source,
                                LPCWSTR target, const INT type, LPCWSTR action)
{
    msi_custom_action_info *info;
    WCHAR package_path[MAX_PATH];
    DWORD size;

    static const WCHAR backslash[] = {'\\',0};

861
    size = MAX_PATH;
862 863 864 865 866 867 868 869 870 871 872
    MSI_GetPropertyW(package, cszSourceDir, package_path, &size);
    lstrcatW(package_path, backslash);
    lstrcatW(package_path, source);

    TRACE("Installing package %s concurrently\n", debugstr_w(package_path));

    info = do_msidbCAConcurrentInstall(package, type, package_path, target, action);

    return wait_thread_handle(info);
}

873
static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source,
874 875
                               LPCWSTR target, const INT type, LPCWSTR action)
{
876
    msi_custom_action_info *info;
877
    WCHAR tmp_file[MAX_PATH];
878
    UINT r;
879

880 881 882
    r = store_binary_to_temp(package, source, tmp_file);
    if (r != ERROR_SUCCESS)
        return r;
883 884 885 886 887 888 889 890

    TRACE("Calling function %s from %s\n",debugstr_w(target),
          debugstr_w(tmp_file));

    if (!strchrW(tmp_file,'.'))
    {
        static const WCHAR dot[]={'.',0};
        strcatW(tmp_file,dot);
891
    }
892

893
    info = do_msidbCustomActionTypeDll( package, type, tmp_file, target, action );
894

895
    return wait_thread_handle( info );
896 897
}

898
static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source,
899 900 901 902 903 904 905
                               LPCWSTR target, const INT type, LPCWSTR action)
{
    WCHAR tmp_file[MAX_PATH];
    STARTUPINFOW si;
    PROCESS_INFORMATION info;
    BOOL rc;
    INT len;
906
    WCHAR *deformated = NULL;
907 908
    WCHAR *cmd;
    static const WCHAR spc[] = {' ',0};
909
    UINT r;
910 911 912

    memset(&si,0,sizeof(STARTUPINFOW));

913 914 915
    r = store_binary_to_temp(package, source, tmp_file);
    if (r != ERROR_SUCCESS)
        return r;
916 917 918 919 920 921 922

    deformat_string(package,target,&deformated);

    len = strlenW(tmp_file)+2;

    if (deformated)
        len += strlenW(deformated);
923

924
    cmd = msi_alloc(sizeof(WCHAR)*len);
925 926 927 928 929 930 931

    strcpyW(cmd,tmp_file);
    if (deformated)
    {
        strcatW(cmd,spc);
        strcatW(cmd,deformated);

932
        msi_free(deformated);
933 934
    }

935
    TRACE("executing exe %s\n", debugstr_w(cmd));
936 937 938

    rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
                  c_collen, &si, &info);
939
    msi_free(cmd);
940 941 942

    if ( !rc )
    {
943
        ERR("Unable to execute command %s\n", debugstr_w(cmd));
944 945
        return ERROR_SUCCESS;
    }
946
    CloseHandle( info.hThread );
947

948
    r = wait_process_handle(package, type, info.hProcess, action);
949

950
    return r;
951 952
}

953 954 955
static UINT HANDLE_CustomType17(MSIPACKAGE *package, LPCWSTR source,
                                LPCWSTR target, const INT type, LPCWSTR action)
{
956
    msi_custom_action_info *info;
957 958 959 960 961 962 963 964 965 966 967
    MSIFILE *file;

    TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));

    file = get_loaded_file( package, source );
    if (!file)
    {
        ERR("invalid file key %s\n", debugstr_w( source ));
        return ERROR_FUNCTION_FAILED;
    }

968
    info = do_msidbCustomActionTypeDll( package, type, file->TargetPath, target, action );
969

970
    return wait_thread_handle( info );
971 972
}

973 974 975 976 977 978 979 980 981 982
static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source,
                                LPCWSTR target, const INT type, LPCWSTR action)
{
    STARTUPINFOW si;
    PROCESS_INFORMATION info;
    BOOL rc;
    WCHAR *deformated;
    WCHAR *cmd;
    INT len;
    static const WCHAR spc[] = {' ',0};
983
    MSIFILE *file;
984 985 986

    memset(&si,0,sizeof(STARTUPINFOW));

987 988 989
    file = get_loaded_file(package,source);
    if( !file )
        return ERROR_FUNCTION_FAILED;
990

991
    len = lstrlenW( file->TargetPath );
992 993 994 995 996 997

    deformat_string(package,target,&deformated);
    if (deformated)
        len += strlenW(deformated);
    len += 2;

998
    cmd = msi_alloc(len * sizeof(WCHAR));
999

1000
    lstrcpyW( cmd, file->TargetPath);
1001 1002 1003 1004 1005
    if (deformated)
    {
        strcatW(cmd, spc);
        strcatW(cmd, deformated);

1006
        msi_free(deformated);
1007 1008
    }

1009
    TRACE("executing exe %s\n", debugstr_w(cmd));
1010 1011 1012 1013 1014 1015

    rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
                  c_collen, &si, &info);

    if ( !rc )
    {
1016 1017
        ERR("Unable to execute command %s\n", debugstr_w(cmd));
        msi_free(cmd);
1018 1019
        return ERROR_SUCCESS;
    }
1020
    msi_free(cmd);
1021
    CloseHandle( info.hThread );
1022

1023
    return wait_process_handle(package, type, info.hProcess, action);
1024 1025
}

1026 1027 1028 1029 1030 1031
static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
                                LPCWSTR target, const INT type, LPCWSTR action)
{
    static const WCHAR query[] = {
      'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
      'F','R','O','M',' ','`','E','r','r','o','r','`',' ',
1032
      'W','H','E','R','E',' ','`','E','r','r','o','r','`',' ','=',' ',
1033
      '%','s',0
1034 1035 1036 1037 1038 1039 1040
    };
    MSIRECORD *row = 0;
    LPWSTR deformated = NULL;

    deformat_string( package, target, &deformated );

    /* first try treat the error as a number */
1041 1042
    row = MSI_QueryGetRecord( package->db, query, deformated );
    if( row )
1043
    {
1044 1045 1046
        LPCWSTR error = MSI_RecordGetString( row, 1 );
        MessageBoxW( NULL, error, NULL, MB_OK );
        msiobj_release( &row->hdr );
1047
    }
1048
    else
1049
        MessageBoxW( NULL, deformated, NULL, MB_OK );
1050

1051
    msi_free( deformated );
1052 1053 1054 1055

    return ERROR_FUNCTION_FAILED;
}

1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070
static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source,
                                LPCWSTR target, const INT type, LPCWSTR action)
{
    STARTUPINFOW si;
    PROCESS_INFORMATION info;
    WCHAR *prop;
    BOOL rc;
    WCHAR *deformated;
    WCHAR *cmd;
    INT len;
    static const WCHAR spc[] = {' ',0};

    memset(&si,0,sizeof(STARTUPINFOW));
    memset(&info,0,sizeof(PROCESS_INFORMATION));

1071
    prop = msi_dup_property( package, source );
1072
    if (!prop)
1073
        return ERROR_SUCCESS;
1074 1075 1076 1077 1078 1079

    deformat_string(package,target,&deformated);
    len = strlenW(prop) + 2;
    if (deformated)
         len += strlenW(deformated);

1080
    cmd = msi_alloc(sizeof(WCHAR)*len);
1081 1082 1083 1084 1085 1086 1087

    strcpyW(cmd,prop);
    if (deformated)
    {
        strcatW(cmd,spc);
        strcatW(cmd,deformated);

1088
        msi_free(deformated);
1089
    }
1090
    msi_free(prop);
1091

1092
    TRACE("executing exe %s\n", debugstr_w(cmd));
1093 1094 1095 1096 1097 1098

    rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
                  c_collen, &si, &info);

    if ( !rc )
    {
1099 1100
        ERR("Unable to execute command %s\n", debugstr_w(cmd));
        msi_free(cmd);
1101 1102
        return ERROR_SUCCESS;
    }
1103
    msi_free(cmd);
1104

1105 1106 1107
    CloseHandle( info.hThread );

    return wait_process_handle(package, type, info.hProcess, action);
1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119
}

static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
                                LPCWSTR target, const INT type, LPCWSTR action)
{
    LPWSTR filename, deformated;
    STARTUPINFOW si;
    PROCESS_INFORMATION info;
    BOOL rc;

    memset(&si,0,sizeof(STARTUPINFOW));

1120
    filename = resolve_folder(package, source, FALSE, FALSE, TRUE, NULL);
1121 1122 1123 1124 1125

    if (!filename)
        return ERROR_FUNCTION_FAILED;

    SetCurrentDirectoryW(filename);
1126
    msi_free(filename);
1127 1128 1129 1130 1131 1132

    deformat_string(package,target,&deformated);

    if (!deformated)
        return ERROR_FUNCTION_FAILED;

1133
    TRACE("executing exe %s\n", debugstr_w(deformated));
1134 1135 1136 1137 1138 1139

    rc = CreateProcessW(NULL, deformated, NULL, NULL, FALSE, 0, NULL,
                  c_collen, &si, &info);

    if ( !rc )
    {
1140 1141
        ERR("Unable to execute command %s\n", debugstr_w(deformated));
        msi_free(deformated);
1142 1143
        return ERROR_SUCCESS;
    }
1144
    msi_free(deformated);
1145
    CloseHandle( info.hThread );
1146

1147
    return wait_process_handle(package, type, info.hProcess, action);
1148 1149
}

1150
static DWORD ACTION_CallScript( const GUID *guid )
1151 1152
{
    msi_custom_action_info *info;
1153
    MSIHANDLE hPackage;
1154 1155 1156 1157 1158 1159 1160 1161 1162
    UINT r = ERROR_FUNCTION_FAILED;

    info = find_action_by_guid( guid );
    if (!info)
    {
        ERR("failed to find action %s\n", debugstr_guid( guid) );
        return r;
    }

1163 1164 1165 1166 1167 1168 1169 1170 1171 1172
    TRACE("function %s, script %s\n", debugstr_w( info->target ), debugstr_w( info->source ) );

    hPackage = alloc_msihandle( &info->package->hdr );
    if (hPackage)
    {
        r = call_script( hPackage, info->type, info->source, info->target, info->action );
        MsiCloseHandle( hPackage );
    }
    else
        ERR("failed to create handle for %p\n", info->package );
1173 1174 1175

    if (info->type & msidbCustomActionTypeAsync &&
        info->type & msidbCustomActionTypeContinue)
1176
        release_custom_action_data( info );
1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205

    return S_OK;
}

static DWORD WINAPI ScriptThread( LPVOID arg )
{
    LPGUID guid = arg;
    DWORD rc = 0;

    TRACE("custom action (%x) started\n", GetCurrentThreadId() );

    rc = ACTION_CallScript( guid );

    TRACE("custom action (%x) returned %i\n", GetCurrentThreadId(), rc );

    MsiCloseAllHandles();
    return rc;
}

static msi_custom_action_info *do_msidbCustomActionTypeScript(
    MSIPACKAGE *package, INT type, LPCWSTR script, LPCWSTR function, LPCWSTR action )
{
    msi_custom_action_info *info;

    info = msi_alloc( sizeof *info );
    if (!info)
        return NULL;

    msiobj_addref( &package->hdr );
1206
    info->refs = 2; /* 1 for our caller and 1 for thread we created */
1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220
    info->package = package;
    info->type = type;
    info->target = strdupW( function );
    info->source = strdupW( script );
    info->action = strdupW( action );
    CoCreateGuid( &info->guid );

    EnterCriticalSection( &msi_custom_action_cs );
    list_add_tail( &msi_pending_custom_actions, &info->entry );
    LeaveCriticalSection( &msi_custom_action_cs );

    info->handle = CreateThread( NULL, 0, ScriptThread, &info->guid, 0, NULL );
    if (!info->handle)
    {
1221 1222 1223
        /* release both references */
        release_custom_action_data( info );
        release_custom_action_data( info );
1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371
        return NULL;
    }

    return info;
}

static UINT HANDLE_CustomType37_38(MSIPACKAGE *package, LPCWSTR source,
                               LPCWSTR target, const INT type, LPCWSTR action)
{
    msi_custom_action_info *info;

    TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));

    info = do_msidbCustomActionTypeScript( package, type, target, NULL, action );

    return wait_thread_handle( info );
}

static UINT HANDLE_CustomType5_6(MSIPACKAGE *package, LPCWSTR source,
                               LPCWSTR target, const INT type, LPCWSTR action)
{
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',' ',
        '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
    MSIRECORD *row = 0;
    msi_custom_action_info *info;
    CHAR *buffer = NULL;
    WCHAR *bufferw = NULL;
    DWORD sz = 0;
    UINT r;

    TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));

    row = MSI_QueryGetRecord(package->db, query, source);
    if (!row)
        return ERROR_FUNCTION_FAILED;

    r = MSI_RecordReadStream(row, 2, NULL, &sz);
    if (r != ERROR_SUCCESS)
	return r;

    buffer = msi_alloc(sizeof(CHAR)*(sz+1));
    if (!buffer)
	return ERROR_FUNCTION_FAILED;

    r = MSI_RecordReadStream(row, 2, buffer, &sz);
    if (r != ERROR_SUCCESS)
        goto done;

    buffer[sz] = 0;
    bufferw = strdupAtoW(buffer);
    if (!bufferw)
    {
        r = ERROR_FUNCTION_FAILED;
        goto done;
    }

    info = do_msidbCustomActionTypeScript( package, type, bufferw, target, action );
    r = wait_thread_handle( info );

done:
    msi_free(bufferw);
    msi_free(buffer);
    return r;
}

static UINT HANDLE_CustomType21_22(MSIPACKAGE *package, LPCWSTR source,
                               LPCWSTR target, const INT type, LPCWSTR action)
{
    msi_custom_action_info *info;
    MSIFILE *file;
    HANDLE hFile;
    DWORD sz, szHighWord = 0, read;
    CHAR *buffer=NULL;
    WCHAR *bufferw=NULL;
    BOOL bRet;
    UINT r;

    TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));

    file = get_loaded_file(package,source);
    if (!file)
    {
	ERR("invalid file key %s\n", debugstr_w(source));
	return ERROR_FUNCTION_FAILED;
    }

    hFile = CreateFileW(file->TargetPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
	return ERROR_FUNCTION_FAILED;

    sz = GetFileSize(hFile, &szHighWord);
    if (sz == INVALID_FILE_SIZE || szHighWord != 0)
    {
	CloseHandle(hFile);
	return ERROR_FUNCTION_FAILED;
    }

    buffer = msi_alloc(sizeof(CHAR)*(sz+1));
    if (!buffer)
    {
	CloseHandle(hFile);
	return ERROR_FUNCTION_FAILED;
    }

    bRet = ReadFile(hFile, buffer, sz, &read, NULL);
    CloseHandle(hFile);
    if (!bRet)
    {
	r = ERROR_FUNCTION_FAILED;
        goto done;
    }

    buffer[read] = 0;
    bufferw = strdupAtoW(buffer);
    if (!bufferw)
    {
        r = ERROR_FUNCTION_FAILED;
        goto done;
    }

    info = do_msidbCustomActionTypeScript( package, type, bufferw, target, action );
    r = wait_thread_handle( info );

done:
    msi_free(bufferw);
    msi_free(buffer);
    return r;
}

static UINT HANDLE_CustomType53_54(MSIPACKAGE *package, LPCWSTR source,
                               LPCWSTR target, const INT type, LPCWSTR action)
{
    msi_custom_action_info *info;
    WCHAR *prop;

    TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));

    prop = msi_dup_property(package,source);
    if (!prop)
	return ERROR_SUCCESS;

    info = do_msidbCustomActionTypeScript( package, type, prop, NULL, action );
    msi_free(prop);
    return wait_thread_handle( info );
}

1372
void ACTION_FinishCustomActions(const MSIPACKAGE* package)
1373
{
1374
    struct list *item;
1375 1376 1377
    HANDLE *wait_handles;
    unsigned int handle_count, i;
    msi_custom_action_info *info, *cursor;
1378

1379
    while ((item = list_head( &package->RunningActions )))
1380
    {
1381 1382 1383 1384
        MSIRUNNINGACTION *action = LIST_ENTRY( item, MSIRUNNINGACTION, entry );

        list_remove( &action->entry );

1385 1386
        TRACE("waiting for %s\n", debugstr_w( action->name ) );
        msi_dialog_check_messages( action->handle );
1387

1388
        CloseHandle( action->handle );
1389 1390
        msi_free( action->name );
        msi_free( action );
1391
    }
1392

1393
    EnterCriticalSection( &msi_custom_action_cs );
1394

1395 1396
    handle_count = list_count( &msi_pending_custom_actions );
    wait_handles = HeapAlloc( GetProcessHeap(), 0, handle_count * sizeof(HANDLE) );
1397

1398 1399 1400 1401
    handle_count = 0;
    LIST_FOR_EACH_ENTRY_SAFE( info, cursor, &msi_pending_custom_actions, msi_custom_action_info, entry )
    {
        if (info->package == package )
1402
        {
1403 1404
            if (DuplicateHandle(GetCurrentProcess(), info->handle, GetCurrentProcess(), &wait_handles[handle_count], SYNCHRONIZE, FALSE, 0))
                handle_count++;
1405
        }
1406
    }
1407

1408
    LeaveCriticalSection( &msi_custom_action_cs );
1409

1410
    for (i = 0; i < handle_count; i++)
1411
    {
1412
        msi_dialog_check_messages( wait_handles[i] );
1413 1414
        CloseHandle( wait_handles[i] );
    }
1415 1416

    HeapFree( GetProcessHeap(), 0, wait_handles );
1417
}
1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461

typedef struct _msi_custom_remote_impl {
    const IWineMsiRemoteCustomActionVtbl *lpVtbl;
    LONG refs;
} msi_custom_remote_impl;

static inline msi_custom_remote_impl* mcr_from_IWineMsiRemoteCustomAction( IWineMsiRemoteCustomAction* iface )
{
    return (msi_custom_remote_impl*) iface;
}

static HRESULT WINAPI mcr_QueryInterface( IWineMsiRemoteCustomAction *iface,
                REFIID riid,LPVOID *ppobj)
{
    if( IsEqualCLSID( riid, &IID_IUnknown ) ||
        IsEqualCLSID( riid, &IID_IWineMsiRemoteCustomAction ) )
    {
        IUnknown_AddRef( iface );
        *ppobj = iface;
        return S_OK;
    }

    return E_NOINTERFACE;
}

static ULONG WINAPI mcr_AddRef( IWineMsiRemoteCustomAction *iface )
{
    msi_custom_remote_impl* This = mcr_from_IWineMsiRemoteCustomAction( iface );

    return InterlockedIncrement( &This->refs );
}

static ULONG WINAPI mcr_Release( IWineMsiRemoteCustomAction *iface )
{
    msi_custom_remote_impl* This = mcr_from_IWineMsiRemoteCustomAction( iface );
    ULONG r;

    r = InterlockedDecrement( &This->refs );
    if (r == 0)
        msi_free( This );
    return r;
}

static HRESULT WINAPI mcr_GetActionInfo( IWineMsiRemoteCustomAction *iface, LPCGUID custom_action_guid,
1462
         INT *type, MSIHANDLE *handle, BSTR *dll, BSTR *func, IWineMsiRemotePackage **remote_package )
1463 1464 1465 1466 1467 1468 1469
{
    msi_custom_action_info *info;

    info = find_action_by_guid( custom_action_guid );
    if (!info)
        return E_FAIL;

1470
    *type = info->type;
1471 1472 1473 1474
    *handle = alloc_msihandle( &info->package->hdr );
    *dll = SysAllocString( info->source );
    *func = SysAllocString( info->target );

1475
    release_custom_action_data( info );
1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501
    return create_msi_remote_package( NULL, (LPVOID *)remote_package );
}

static const IWineMsiRemoteCustomActionVtbl msi_custom_remote_vtbl =
{
    mcr_QueryInterface,
    mcr_AddRef,
    mcr_Release,
    mcr_GetActionInfo,
};

HRESULT create_msi_custom_remote( IUnknown *pOuter, LPVOID *ppObj )
{
    msi_custom_remote_impl* This;

    This = msi_alloc( sizeof *This );
    if (!This)
        return E_OUTOFMEMORY;

    This->lpVtbl = &msi_custom_remote_vtbl;
    This->refs = 1;

    *ppObj = This;

    return S_OK;
}