automation.c 110 KB
Newer Older
1
/*
2
 * Copyright (C) 2007 Mike McCormack for CodeWeavers
3 4
 * Copyright (C) 2007 Misha Koshelev
 *
5
 * A test program for Microsoft Installer OLE automation functionality.
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 *
 * 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
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#define COBJMACROS

#include <stdio.h>

26
#include <initguid.h>
27 28 29 30 31
#include <windows.h>
#include <msiquery.h>
#include <msidefs.h>
#include <msi.h>
#include <fci.h>
32
#include <oaidl.h>
33 34 35

#include "wine/test.h"

36 37
static BOOL is_wow64;

38 39
static BOOL (WINAPI *pCheckTokenMembership)(HANDLE,PSID,PBOOL);
static BOOL (WINAPI *pOpenProcessToken)(HANDLE, DWORD, PHANDLE);
40 41 42
static LONG (WINAPI *pRegDeleteKeyExA)(HKEY, LPCSTR, REGSAM, DWORD);
static BOOL (WINAPI *pIsWow64Process)(HANDLE, PBOOL);

43 44
DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);

45 46
static const char *msifile = "winetest-automation.msi";
static const WCHAR szMsifile[] = {'w','i','n','e','t','e','s','t','-','a','u','t','o','m','a','t','i','o','n','.','m','s','i',0};
47
static const WCHAR szMSITEST[] = { 'M','S','I','T','E','S','T',0 };
48
static const WCHAR szProductCode[] = { '{','8','3','7','4','5','0','f','a','-','a','3','9','b','-','4','b','c','8','-','b','3','2','1','-','0','8','b','3','9','3','f','7','8','4','b','3','}',0 };
49
static const WCHAR szUpgradeCode[] = { '{','C','E','0','6','7','E','8','D','-','2','E','1','A','-','4','3','6','7','-','B','7','3','4','-','4','E','B','2','B','D','A','D','6','5','6','5','}',0 };
50
static const WCHAR szProductInfoException[] = { 'P','r','o','d','u','c','t','I','n','f','o',',','P','r','o','d','u','c','t',',','A','t','t','r','i','b','u','t','e',0 };
51 52
static const WCHAR WINE_INSTALLPROPERTY_PACKAGENAMEW[] = {'P','a','c','k','a','g','e','N','a','m','e',0};
static const WCHAR WINE_INSTALLPROPERTY_PRODUCTNAMEW[] = {'P','r','o','d','u','c','t','N','a','m','e',0};
53
static const WCHAR WINE_INSTALLPROPERTY_LOCALPACKAGEW[] = {'L','o','c','a','l','P','a','c','k','a','g','e',0};
54 55 56
static FILETIME systemtime;
static CHAR CURR_DIR[MAX_PATH];
static EXCEPINFO excepinfo;
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74

/*
 * OLE automation data
 **/
static const WCHAR szProgId[] = { 'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r','.','I','n','s','t','a','l','l','e','r',0 };
static IDispatch *pInstaller;

/* msi database data */

static const CHAR component_dat[] = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n"
                                    "s72\tS38\ts72\ti2\tS255\tS72\n"
                                    "Component\tComponent\n"
                                    "Five\t{8CC92E9D-14B2-4CA4-B2AA-B11D02078087}\tNEWDIR\t2\t\tfive.txt\n"
                                    "Four\t{FD37B4EA-7209-45C0-8917-535F35A2F080}\tCABOUTDIR\t2\t\tfour.txt\n"
                                    "One\t{783B242E-E185-4A56-AF86-C09815EC053C}\tMSITESTDIR\t2\t\tone.txt\n"
                                    "Three\t{010B6ADD-B27D-4EDD-9B3D-34C4F7D61684}\tCHANGEDDIR\t2\t\tthree.txt\n"
                                    "Two\t{BF03D1A6-20DA-4A65-82F3-6CAC995915CE}\tFIRSTDIR\t2\t\ttwo.txt\n"
                                    "dangler\t{6091DF25-EF96-45F1-B8E9-A9B1420C7A3C}\tTARGETDIR\t4\t\tregdata\n"
75
                                    "component\t\tMSITESTDIR\t0\t1\tfile\n";
76 77 78 79 80 81 82 83 84 85

static const CHAR directory_dat[] = "Directory\tDirectory_Parent\tDefaultDir\n"
                                    "s72\tS72\tl255\n"
                                    "Directory\tDirectory\n"
                                    "CABOUTDIR\tMSITESTDIR\tcabout\n"
                                    "CHANGEDDIR\tMSITESTDIR\tchanged:second\n"
                                    "FIRSTDIR\tMSITESTDIR\tfirst\n"
                                    "MSITESTDIR\tProgramFilesFolder\tmsitest\n"
                                    "NEWDIR\tCABOUTDIR\tnew\n"
                                    "ProgramFilesFolder\tTARGETDIR\t.\n"
86
                                    "TARGETDIR\t\tSourceDir\n";
87 88 89 90 91 92 93 94 95

static const CHAR feature_dat[] = "Feature\tFeature_Parent\tTitle\tDescription\tDisplay\tLevel\tDirectory_\tAttributes\n"
                                  "s38\tS38\tL64\tL255\tI2\ti2\tS72\ti2\n"
                                  "Feature\tFeature\n"
                                  "Five\t\tFive\tThe Five Feature\t5\t3\tNEWDIR\t0\n"
                                  "Four\t\tFour\tThe Four Feature\t4\t3\tCABOUTDIR\t0\n"
                                  "One\t\tOne\tThe One Feature\t1\t3\tMSITESTDIR\t0\n"
                                  "Three\tOne\tThree\tThe Three Feature\t3\t3\tCHANGEDDIR\t0\n"
                                  "Two\tOne\tTwo\tThe Two Feature\t2\t3\tFIRSTDIR\t0\n"
96
                                  "feature\t\t\t\t2\t1\tTARGETDIR\t0\n";
97 98 99 100 101 102 103 104 105

static const CHAR feature_comp_dat[] = "Feature_\tComponent_\n"
                                       "s38\ts72\n"
                                       "FeatureComponents\tFeature_\tComponent_\n"
                                       "Five\tFive\n"
                                       "Four\tFour\n"
                                       "One\tOne\n"
                                       "Three\tThree\n"
                                       "Two\tTwo\n"
106
                                       "feature\tcomponent\n";
107 108 109 110

static const CHAR file_dat[] = "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n"
                               "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n"
                               "File\tFile\n"
111 112
                               "five.txt\tFive\tfive.txt\t1000\t\t\t0\t5\n"
                               "four.txt\tFour\tfour.txt\t1000\t\t\t0\t4\n"
113 114 115
                               "one.txt\tOne\tone.txt\t1000\t\t\t0\t1\n"
                               "three.txt\tThree\tthree.txt\t1000\t\t\t0\t3\n"
                               "two.txt\tTwo\ttwo.txt\t1000\t\t\t0\t2\n"
116
                               "file\tcomponent\tfilename\t100\t\t\t8192\t1\n";
117 118 119 120 121 122 123 124 125

static const CHAR install_exec_seq_dat[] = "Action\tCondition\tSequence\n"
                                           "s72\tS255\tI2\n"
                                           "InstallExecuteSequence\tAction\n"
                                           "AllocateRegistrySpace\tNOT Installed\t1550\n"
                                           "CostFinalize\t\t1000\n"
                                           "CostInitialize\t\t800\n"
                                           "FileCost\t\t900\n"
                                           "InstallFiles\t\t4000\n"
126 127
                                           "RegisterProduct\t\t6100\n"
                                           "PublishProduct\t\t6400\n"
128 129 130 131
                                           "InstallFinalize\t\t6600\n"
                                           "InstallInitialize\t\t1500\n"
                                           "InstallValidate\t\t1400\n"
                                           "LaunchConditions\t\t100\n"
132
                                           "WriteRegistryValues\tSourceDir And SOURCEDIR\t5000\n";
133 134 135 136

static const CHAR media_dat[] = "DiskId\tLastSequence\tDiskPrompt\tCabinet\tVolumeLabel\tSource\n"
                                "i2\ti4\tL64\tS255\tS32\tS72\n"
                                "Media\tDiskId\n"
137
                                "1\t5\t\t\tDISK1\t\n";
138 139 140 141 142 143 144 145 146 147

static const CHAR property_dat[] = "Property\tValue\n"
                                   "s72\tl0\n"
                                   "Property\tProperty\n"
                                   "DefaultUIFont\tDlgFont8\n"
                                   "HASUIRUN\t0\n"
                                   "INSTALLLEVEL\t3\n"
                                   "InstallMode\tTypical\n"
                                   "Manufacturer\tWine\n"
                                   "PIDTemplate\t12345<###-%%%%%%%>@@@@@\n"
148
                                   "ProductCode\t{837450fa-a39b-4bc8-b321-08b393f784b3}\n"
149 150 151 152 153 154
                                   "ProductID\tnone\n"
                                   "ProductLanguage\t1033\n"
                                   "ProductName\tMSITEST\n"
                                   "ProductVersion\t1.1.1\n"
                                   "PROMPTROLLBACKCOST\tP\n"
                                   "Setup\tSetup\n"
155 156
                                   "UpgradeCode\t{CE067E8D-2E1A-4367-B734-4EB2BDAD6565}\n"
                                   "MSIFASTINSTALL\t1\n";
157 158 159 160

static const CHAR registry_dat[] = "Registry\tRoot\tKey\tName\tValue\tComponent_\n"
                                   "s72\ti2\tl255\tL255\tL0\ts72\n"
                                   "Registry\tRegistry\n"
161 162 163
                                   "Apples\t1\tSOFTWARE\\Wine\\msitest\tName\timaname\tOne\n"
                                   "Oranges\t1\tSOFTWARE\\Wine\\msitest\tnumber\t#314\tTwo\n"
                                   "regdata\t1\tSOFTWARE\\Wine\\msitest\tblah\tbad\tdangler\n"
164
                                   "OrderTest\t1\tSOFTWARE\\Wine\\msitest\tOrderTestName\tOrderTestValue\tcomponent\n";
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184

typedef struct _msi_table
{
    const CHAR *filename;
    const CHAR *data;
    int size;
} msi_table;

#define ADD_TABLE(x) {#x".idt", x##_dat, sizeof(x##_dat)}

static const msi_table tables[] =
{
    ADD_TABLE(component),
    ADD_TABLE(directory),
    ADD_TABLE(feature),
    ADD_TABLE(feature_comp),
    ADD_TABLE(file),
    ADD_TABLE(install_exec_seq),
    ADD_TABLE(media),
    ADD_TABLE(property),
185
    ADD_TABLE(registry)
186 187
};

188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
typedef struct _msi_summary_info
{
    UINT property;
    UINT datatype;
    INT iValue;
    FILETIME *pftValue;
    const CHAR *szValue;
} msi_summary_info;

#define ADD_INFO_I2(property, iValue) {property, VT_I2, iValue, NULL, NULL}
#define ADD_INFO_I4(property, iValue) {property, VT_I4, iValue, NULL, NULL}
#define ADD_INFO_LPSTR(property, szValue) {property, VT_LPSTR, 0, NULL, szValue}
#define ADD_INFO_FILETIME(property, pftValue) {property, VT_FILETIME, 0, pftValue, NULL}

static const msi_summary_info summary_info[] =
{
    ADD_INFO_LPSTR(PID_TEMPLATE, ";1033"),
205
    ADD_INFO_LPSTR(PID_REVNUMBER, "{004757CA-5092-49C2-AD20-28E1CE0DF5F2}"),
206 207 208 209 210 211
    ADD_INFO_I4(PID_PAGECOUNT, 100),
    ADD_INFO_I4(PID_WORDCOUNT, 0),
    ADD_INFO_FILETIME(PID_CREATE_DTM, &systemtime),
    ADD_INFO_FILETIME(PID_LASTPRINTED, &systemtime)
};

212 213 214 215 216 217 218 219 220 221
static void init_functionpointers(void)
{
    HMODULE hadvapi32 = GetModuleHandleA("advapi32.dll");
    HMODULE hkernel32 = GetModuleHandleA("kernel32.dll");

#define GET_PROC(dll, func) \
    p ## func = (void *)GetProcAddress(dll, #func); \
    if(!p ## func) \
      trace("GetProcAddress(%s) failed\n", #func);

222 223
    GET_PROC(hadvapi32, CheckTokenMembership);
    GET_PROC(hadvapi32, OpenProcessToken);
224 225 226 227 228 229
    GET_PROC(hadvapi32, RegDeleteKeyExA)
    GET_PROC(hkernel32, IsWow64Process)

#undef GET_PROC
}

230 231 232
static BOOL is_process_limited(void)
{
    SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
233
    PSID Group = NULL;
234 235 236 237 238 239
    BOOL IsInGroup;
    HANDLE token;

    if (!pCheckTokenMembership || !pOpenProcessToken) return FALSE;

    if (!AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID,
240
                                  DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &Group) ||
241 242 243
        !pCheckTokenMembership(NULL, Group, &IsInGroup))
    {
        trace("Could not check if the current user is an administrator\n");
244
        FreeSid(Group);
245 246
        return FALSE;
    }
247 248
    FreeSid(Group);

249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
    if (!IsInGroup)
    {
        /* Only administrators have enough privileges for these tests */
        return TRUE;
    }

    if (pOpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
    {
        BOOL ret;
        TOKEN_ELEVATION_TYPE type = TokenElevationTypeDefault;
        DWORD size;

        ret = GetTokenInformation(token, TokenElevationType, &type, sizeof(type), &size);
        CloseHandle(token);
        return (ret && type == TokenElevationTypeLimited);
    }
    return FALSE;
}

268 269 270 271 272 273 274
static LONG delete_key_portable( HKEY key, LPCSTR subkey, REGSAM access )
{
    if (pRegDeleteKeyExA)
        return pRegDeleteKeyExA( key, subkey, access, 0 );
    return RegDeleteKeyA( key, subkey );
}

275 276 277 278 279 280 281 282
/*
 * Database Helpers
 */

static void write_file(const CHAR *filename, const char *data, int data_size)
{
    DWORD size;

283 284
    HANDLE hf = CreateFileA(filename, GENERIC_WRITE, 0, NULL,
                            CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
285 286 287 288
    WriteFile(hf, data, data_size, &size, NULL);
    CloseHandle(hf);
}

289
static void write_msi_summary_info(MSIHANDLE db, const msi_summary_info *info, int num_info)
290 291 292
{
    MSIHANDLE summary;
    UINT r;
293
    int j;
294

295
    r = MsiGetSummaryInformationA(db, NULL, num_info, &summary);
296 297
    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);

298 299 300 301
    /* import summary information into the stream */
    for (j = 0; j < num_info; j++)
    {
        const msi_summary_info *entry = &info[j];
302

303 304 305 306
        r = MsiSummaryInfoSetPropertyA(summary, entry->property, entry->datatype,
                                       entry->iValue, entry->pftValue, entry->szValue);
        ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
    }
307 308 309 310 311 312 313 314

    /* write the summary changes back to the stream */
    r = MsiSummaryInfoPersist(summary);
    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);

    MsiCloseHandle(summary);
}

315 316
static void create_database(const CHAR *name, const msi_table *tables, int num_tables,
                            const msi_summary_info *info, int num_info)
317 318 319
{
    MSIHANDLE db;
    UINT r;
320 321 322 323 324 325
    WCHAR *nameW;
    int j, len;

    len = MultiByteToWideChar( CP_ACP, 0, name, -1, NULL, 0 );
    if (!(nameW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return;
    MultiByteToWideChar( CP_ACP, 0, name, -1, nameW, len );
326

327
    r = MsiOpenDatabaseW(nameW, MSIDBOPEN_CREATE, &db);
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);

    /* import the tables into the database */
    for (j = 0; j < num_tables; j++)
    {
        const msi_table *table = &tables[j];

        write_file(table->filename, table->data, (table->size - 1) * sizeof(char));

        r = MsiDatabaseImportA(db, CURR_DIR, table->filename);
        ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);

        DeleteFileA(table->filename);
    }

343
    write_msi_summary_info(db, info, num_info);
344 345 346 347 348

    r = MsiDatabaseCommit(db);
    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);

    MsiCloseHandle(db);
349
    HeapFree( GetProcessHeap(), 0, nameW );
350 351
}

352 353
static BOOL create_package(LPWSTR path)
{
354
    static const WCHAR slashW[] = {'\\',0};
355 356 357 358 359 360 361 362 363 364 365 366 367
    DWORD len;

    /* Prepare package */
    create_database(msifile, tables,
                    sizeof(tables) / sizeof(msi_table), summary_info,
                    sizeof(summary_info) / sizeof(msi_summary_info));

    len = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
                              CURR_DIR, -1, path, MAX_PATH);
    ok(len, "MultiByteToWideChar returned error %d\n", GetLastError());
    if (!len)
        return FALSE;

368 369
    lstrcatW(path, slashW);
    lstrcatW(path, szMsifile);
370 371 372
    return TRUE;
}

373 374 375 376 377 378 379 380 381 382 383
/*
 * Installation helpers
 */

static char PROG_FILES_DIR[MAX_PATH];

static BOOL get_program_files_dir(LPSTR buf)
{
    HKEY hkey;
    DWORD type = REG_EXPAND_SZ, size;

384
    if (RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion", &hkey))
385 386 387
        return FALSE;

    size = MAX_PATH;
388 389
    if (RegQueryValueExA(hkey, "ProgramFilesDir (x86)", 0, &type, (LPBYTE)buf, &size) &&
        RegQueryValueExA(hkey, "ProgramFilesDir", 0, &type, (LPBYTE)buf, &size))
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
        return FALSE;

    RegCloseKey(hkey);
    return TRUE;
}

static void create_file(const CHAR *name, DWORD size)
{
    HANDLE file;
    DWORD written, left;

    file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
    ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n", name);
    WriteFile(file, name, strlen(name), &written, NULL);
    WriteFile(file, "\n", strlen("\n"), &written, NULL);

406
    left = size - lstrlenA(name) - 1;
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458

    SetFilePointer(file, left, NULL, FILE_CURRENT);
    SetEndOfFile(file);

    CloseHandle(file);
}

static void create_test_files(void)
{
    CreateDirectoryA("msitest", NULL);
    create_file("msitest\\one.txt", 100);
    CreateDirectoryA("msitest\\first", NULL);
    create_file("msitest\\first\\two.txt", 100);
    CreateDirectoryA("msitest\\second", NULL);
    create_file("msitest\\second\\three.txt", 100);
    CreateDirectoryA("msitest\\cabout",NULL);
    create_file("msitest\\cabout\\four.txt", 100);
    CreateDirectoryA("msitest\\cabout\\new",NULL);
    create_file("msitest\\cabout\\new\\five.txt", 100);
    create_file("msitest\\filename", 100);
}

static BOOL delete_pf(const CHAR *rel_path, BOOL is_file)
{
    CHAR path[MAX_PATH];

    lstrcpyA(path, PROG_FILES_DIR);
    lstrcatA(path, "\\");
    lstrcatA(path, rel_path);

    if (is_file)
        return DeleteFileA(path);
    else
        return RemoveDirectoryA(path);
}

static void delete_test_files(void)
{
    DeleteFileA(msifile);
    DeleteFileA("msitest\\cabout\\new\\five.txt");
    DeleteFileA("msitest\\cabout\\four.txt");
    DeleteFileA("msitest\\second\\three.txt");
    DeleteFileA("msitest\\first\\two.txt");
    DeleteFileA("msitest\\one.txt");
    DeleteFileA("msitest\\filename");
    RemoveDirectoryA("msitest\\cabout\\new");
    RemoveDirectoryA("msitest\\cabout");
    RemoveDirectoryA("msitest\\second");
    RemoveDirectoryA("msitest\\first");
    RemoveDirectoryA("msitest");
}

459 460 461 462
/*
 * Automation helpers and tests
 */

463
/* ok-like statement which takes two unicode strings or one unicode and one ANSI string as arguments */
464 465 466 467
static CHAR string1[MAX_PATH], string2[MAX_PATH];

#define ok_w2(format, szString1, szString2) \
\
468 469 470 471
    do { \
    WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
    WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
    if (lstrcmpA(string1, string2) != 0) \
472
        ok(0, format, string1, string2); \
473
    } while(0);
474

475 476 477 478 479 480 481 482 483
#define ok_w2n(format, szString1, szString2, len) \
\
    if (memcmp(szString1, szString2, len * sizeof(WCHAR)) != 0) \
    { \
        WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
        WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
        ok(0, format, string1, string2); \
    }

484 485 486 487 488 489 490 491 492 493 494 495
#define ok_aw(format, aString, wString) \
\
    WideCharToMultiByte(CP_ACP, 0, wString, -1, string1, MAX_PATH, NULL, NULL); \
    if (lstrcmpA(string1, aString) != 0) \
        ok(0, format, string1, aString); \

#define ok_awplus(format, extra, aString, wString)       \
\
    WideCharToMultiByte(CP_ACP, 0, wString, -1, string1, MAX_PATH, NULL, NULL); \
    if (lstrcmpA(string1, aString) != 0) \
        ok(0, format, extra, string1, aString);  \

496
/* exception checker */
497
static const WCHAR szSource[] = {'M','s','i',' ','A','P','I',' ','E','r','r','o','r',0};
498 499 500 501 502 503 504 505 506 507 508 509

#define ok_exception(hr, szDescription)           \
    if (hr == DISP_E_EXCEPTION) \
    { \
        /* Compare wtype, source, and destination */                    \
        ok(excepinfo.wCode == 1000, "Exception info was %d, expected 1000\n", excepinfo.wCode); \
\
        ok(excepinfo.bstrSource != NULL, "Exception source was NULL\n"); \
        if (excepinfo.bstrSource)                                       \
            ok_w2("Exception source was \"%s\" but expected to be \"%s\"\n", excepinfo.bstrSource, szSource); \
\
        ok(excepinfo.bstrDescription != NULL, "Exception description was NULL\n"); \
510 511
        if (excepinfo.bstrDescription) \
            ok_w2("Exception description was \"%s\" but expected to be \"%s\"\n", excepinfo.bstrDescription, szDescription); \
512 513 514 515
\
        SysFreeString(excepinfo.bstrSource); \
        SysFreeString(excepinfo.bstrDescription); \
        SysFreeString(excepinfo.bstrHelpFile); \
516 517
    }

518 519 520 521
static DISPID get_dispid( IDispatch *disp, const char *name )
{
    LPOLESTR str;
    UINT len;
522
    DISPID id = -1;
523 524 525 526
    HRESULT r;

    len = MultiByteToWideChar(CP_ACP, 0, name, -1, NULL, 0 );
    str = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR) );
527 528
    if (str)
    {
529
        MultiByteToWideChar(CP_ACP, 0, name, -1, str, len );
530 531 532 533 534
        r = IDispatch_GetIDsOfNames( disp, &IID_NULL, &str, 1, 0, &id );
        HeapFree(GetProcessHeap(), 0, str);
        if (r != S_OK)
            return -1;
    }
535 536 537 538

    return id;
}

539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595
typedef struct {
    DISPID did;
    const char *name;
    BOOL todo;
} get_did_t;

static const get_did_t get_did_data[] = {
    { 1,  "CreateRecord" },
    { 2,  "OpenPackage" },
    { 3,  "OpenProduct" },
    { 4,  "OpenDatabase" },
    { 5,  "SummaryInformation" },
    { 6,  "UILevel" },
    { 7,  "EnableLog" },
    { 8,  "InstallProduct" },
    { 9,  "Version" },
    { 10, "LastErrorRecord" },
    { 11, "RegistryValue" },
    { 12, "Environment" },
    { 13, "FileAttributes" },
    { 15, "FileSize" },
    { 16, "FileVersion" },
    { 17, "ProductState" },
    { 18, "ProductInfo" },
    { 19, "ConfigureProduct", TRUE },
    { 20, "ReinstallProduct", TRUE },
    { 21, "CollectUserInfo", TRUE },
    { 22, "ApplyPatch", TRUE },
    { 23, "FeatureParent", TRUE },
    { 24, "FeatureState", TRUE },
    { 25, "UseFeature", TRUE },
    { 26, "FeatureUsageCount", TRUE },
    { 27, "FeatureUsageDate", TRUE },
    { 28, "ConfigureFeature", TRUE },
    { 29, "ReinstallFeature", TRUE },
    { 30, "ProvideComponent", TRUE },
    { 31, "ComponentPath", TRUE },
    { 32, "ProvideQualifiedComponent", TRUE },
    { 33, "QualifierDescription", TRUE },
    { 34, "ComponentQualifiers", TRUE },
    { 35, "Products" },
    { 36, "Features", TRUE },
    { 37, "Components", TRUE },
    { 38, "ComponentClients", TRUE },
    { 39, "Patches", TRUE },
    { 40, "RelatedProducts" },
    { 41, "PatchInfo", TRUE },
    { 42, "PatchTransforms", TRUE },
    { 43, "AddSource", TRUE },
    { 44, "ClearSourceList", TRUE },
    { 45, "ForceSourceListResolution", TRUE },
    { 46, "ShortcutTarget", TRUE },
    { 47, "FileHash", TRUE },
    { 48, "FileSignatureInfo", TRUE },
    { 0 }
};

596 597
static void test_dispid(void)
{
598
    const get_did_t *ptr = get_did_data;
599
    DISPID dispid;
600

601
    while (ptr->name)
602
    {
603
        dispid = get_dispid(pInstaller, ptr->name);
604
        todo_wine_if (ptr->todo)
605 606
            ok(dispid == ptr->did, "%s: expected %d, got %d\n", ptr->name, ptr->did, dispid);
        ptr++;
607
    }
608

609 610 611 612 613 614 615 616 617 618
    dispid = get_dispid(pInstaller, "RemovePatches");
    ok(dispid == 49 || dispid == -1, "Expected 49 or -1, got %d\n", dispid);
    dispid = get_dispid(pInstaller, "ApplyMultiplePatches");
    ok(dispid == 51 || dispid == -1, "Expected 51 or -1, got %d\n", dispid);
    dispid = get_dispid(pInstaller, "ProductsEx");
    ok(dispid == 52 || dispid == -1, "Expected 52 or -1, got %d\n", dispid);
    dispid = get_dispid(pInstaller, "PatchesEx");
    ok(dispid == 55 || dispid == -1, "Expected 55 or -1, got %d\n", dispid);
    dispid = get_dispid(pInstaller, "ExtractPatchXMLData");
    ok(dispid == 57 || dispid == -1, "Expected 57 or -1, got %d\n", dispid);
619
    dispid = get_dispid( pInstaller, "ProductElevated" );
620
    ok(dispid == 59 || dispid == -1, "Expected 59 or -1, got %d\n", dispid);
621
    dispid = get_dispid( pInstaller, "ProvideAssembly" );
622 623 624
    ok(dispid == 60 || dispid == -1, "Expected 60 or -1, got %d\n", dispid);
    dispid = get_dispid( pInstaller, "ProductInfoFromScript" );
    ok(dispid == 61 || dispid == -1, "Expected 61 or -1, got %d\n", dispid);
625
    dispid = get_dispid( pInstaller, "AdvertiseProduct" );
626 627 628
    ok(dispid == 62 || dispid == -1, "Expected 62 or -1, got %d\n", dispid);
    dispid = get_dispid( pInstaller, "CreateAdvertiseScript" );
    ok(dispid == 63 || dispid == -1, "Expected 63 or -1, got %d\n", dispid);
629
    dispid = get_dispid( pInstaller, "PatchFiles" );
630
    ok(dispid == 65 || dispid == -1, "Expected 65 or -1, got %d\n", dispid);
631 632
}

633 634 635
/* Test basic IDispatch functions */
static void test_dispatch(void)
{
636
    static WCHAR szOpenPackage[] = { 'O','p','e','n','P','a','c','k','a','g','e',0 };
637
    static const WCHAR szOpenPackageException[] = {'O','p','e','n','P','a','c','k','a','g','e',',','P','a','c','k','a','g','e','P','a','t','h',',','O','p','t','i','o','n','s',0};
638
    static WCHAR szProductState[] = { 'P','r','o','d','u','c','t','S','t','a','t','e',0 };
639 640 641 642
    HRESULT hr;
    DISPID dispid;
    OLECHAR *name;
    VARIANT varresult;
643 644
    VARIANTARG vararg[3];
    WCHAR path[MAX_PATH];
645 646 647 648 649 650 651 652 653 654 655 656
    DISPPARAMS dispparams = {NULL, NULL, 0, 0};

    /* Test getting ID of a function name that does not exist */
    name = (WCHAR *)szMsifile;
    hr = IDispatch_GetIDsOfNames(pInstaller, &IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &dispid);
    ok(hr == DISP_E_UNKNOWNNAME, "IDispatch::GetIDsOfNames returned 0x%08x\n", hr);

    /* Test invoking this function */
    hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, NULL, NULL, NULL, NULL);
    ok(hr == DISP_E_MEMBERNOTFOUND, "IDispatch::Invoke returned 0x%08x\n", hr);

    /* Test getting ID of a function name that does exist */
657
    name = szOpenPackage;
658
    hr = IDispatch_GetIDsOfNames(pInstaller, &IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &dispid);
659
    ok(hr == S_OK, "IDispatch::GetIDsOfNames returned 0x%08x\n", hr);
660 661 662 663

    /* Test invoking this function (without parameters passed) */
    if (0) /* All of these crash MSI on Windows XP */
    {
664 665
        IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, NULL, NULL, NULL, NULL);
        IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, NULL, NULL, &excepinfo, NULL);
666
        VariantInit(&varresult);
667
        IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, NULL, &varresult, &excepinfo, NULL);
668 669 670 671
    }

    /* Try with NULL params */
    hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
672
    ok(hr == DISP_E_TYPEMISMATCH, "IDispatch::Invoke returned 0x%08x\n", hr);
673 674 675 676 677 678

    /* Try one empty parameter */
    dispparams.rgvarg = vararg;
    dispparams.cArgs = 1;
    VariantInit(&vararg[0]);
    hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
679
    ok(hr == DISP_E_TYPEMISMATCH, "IDispatch::Invoke returned 0x%08x\n", hr);
680

681 682 683 684 685
    /* Try two empty parameters */
    dispparams.cArgs = 2;
    VariantInit(&vararg[0]);
    VariantInit(&vararg[1]);
    hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
686
    ok(hr == DISP_E_TYPEMISMATCH, "IDispatch::Invoke returned 0x%08x\n", hr);
687 688 689 690 691

    /* Try one parameter, the required BSTR.  Second parameter is optional.
     * NOTE: The specified package does not exist, which is why the call fails.
     */
    dispparams.cArgs = 1;
692 693 694 695
    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_BSTR;
    V_BSTR(&vararg[0]) = SysAllocString(szMsifile);
    hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
696 697
    ok(hr == DISP_E_EXCEPTION, "IDispatch::Invoke returned 0x%08x\n", hr);
    ok_exception(hr, szOpenPackageException);
698 699
    VariantClear(&vararg[0]);

700 701 702 703 704 705 706 707 708
    /* Provide the required BSTR and an empty second parameter.
     * NOTE: The specified package does not exist, which is why the call fails.
     */
    dispparams.cArgs = 2;
    VariantInit(&vararg[1]);
    V_VT(&vararg[1]) = VT_BSTR;
    V_BSTR(&vararg[1]) = SysAllocString(szMsifile);
    VariantInit(&vararg[0]);
    hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
709 710
    ok(hr == DISP_E_EXCEPTION, "IDispatch::Invoke returned 0x%08x\n", hr);
    ok_exception(hr, szOpenPackageException);
711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750
    VariantClear(&vararg[1]);

    /* Provide the required BSTR and two empty parameters.
     * NOTE: The specified package does not exist, which is why the call fails.
     */
    dispparams.cArgs = 3;
    VariantInit(&vararg[2]);
    V_VT(&vararg[2]) = VT_BSTR;
    V_BSTR(&vararg[2]) = SysAllocString(szMsifile);
    VariantInit(&vararg[1]);
    VariantInit(&vararg[0]);
    hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
    ok(hr == DISP_E_EXCEPTION, "IDispatch::Invoke returned 0x%08x\n", hr);
    ok_exception(hr, szOpenPackageException);
    VariantClear(&vararg[2]);

    /* Provide the required BSTR and a second parameter with the wrong type. */
    dispparams.cArgs = 2;
    VariantInit(&vararg[1]);
    V_VT(&vararg[1]) = VT_BSTR;
    V_BSTR(&vararg[1]) = SysAllocString(szMsifile);
    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_BSTR;
    V_BSTR(&vararg[0]) = SysAllocString(szMsifile);
    hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
    ok(hr == DISP_E_TYPEMISMATCH, "IDispatch::Invoke returned 0x%08x\n", hr);
    VariantClear(&vararg[0]);
    VariantClear(&vararg[1]);

    /* Create a proper installer package. */
    create_package(path);

    /* Try one parameter, the required BSTR.  Second parameter is optional.
     * Proper installer package exists.  Path to the package is relative.
     */
    dispparams.cArgs = 1;
    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_BSTR;
    V_BSTR(&vararg[0]) = SysAllocString(szMsifile);
    hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
751
    todo_wine ok(hr == DISP_E_EXCEPTION, "IDispatch::Invoke returned 0x%08x\n", hr);
752 753
    ok_exception(hr, szOpenPackageException);
    VariantClear(&vararg[0]);
754 755
    if (hr != DISP_E_EXCEPTION)
        VariantClear(&varresult);
756 757 758 759 760 761 762 763 764

    /* Try one parameter, the required BSTR.  Second parameter is optional.
     * Proper installer package exists.  Path to the package is absolute.
     */
    dispparams.cArgs = 1;
    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_BSTR;
    V_BSTR(&vararg[0]) = SysAllocString(path);
    hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
765 766 767 768 769 770
    if (hr == DISP_E_EXCEPTION)
    {
        skip("OpenPackage failed, insufficient rights?\n");
        DeleteFileW(path);
        return;
    }
771
    ok(hr == S_OK, "IDispatch::Invoke returned 0x%08x\n", hr);
772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830
    VariantClear(&vararg[0]);
    VariantClear(&varresult);

    /* Provide the required BSTR and an empty second parameter.  Proper
     * installation package exists.
     */
    dispparams.cArgs = 2;
    VariantInit(&vararg[1]);
    V_VT(&vararg[1]) = VT_BSTR;
    V_BSTR(&vararg[1]) = SysAllocString(path);
    VariantInit(&vararg[0]);
    hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
    ok(hr == S_OK, "IDispatch::Invoke returned 0x%08x\n", hr);
    VariantClear(&vararg[1]);
    VariantClear(&varresult);

    /* Provide the required BSTR and two empty parameters.  Proper
     * installation package exists.
     */
    dispparams.cArgs = 3;
    VariantInit(&vararg[2]);
    V_VT(&vararg[2]) = VT_BSTR;
    V_BSTR(&vararg[2]) = SysAllocString(path);
    VariantInit(&vararg[1]);
    VariantInit(&vararg[0]);
    hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
    ok(hr == S_OK, "IDispatch::Invoke returned 0x%08x\n", hr);
    VariantClear(&vararg[2]);
    VariantClear(&varresult);

    /* Provide the required BSTR and a second parameter with the wrong type. */
    dispparams.cArgs = 2;
    VariantInit(&vararg[1]);
    V_VT(&vararg[1]) = VT_BSTR;
    V_BSTR(&vararg[1]) = SysAllocString(path);
    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_BSTR;
    V_BSTR(&vararg[0]) = SysAllocString(path);
    hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
    ok(hr == DISP_E_TYPEMISMATCH, "IDispatch::Invoke returned 0x%08x\n", hr);
    VariantClear(&vararg[0]);
    VariantClear(&vararg[1]);

    /* Provide the required BSTR and a second parameter that can be coerced to
     * VT_I4.
     */
    dispparams.cArgs = 2;
    VariantInit(&vararg[1]);
    V_VT(&vararg[1]) = VT_BSTR;
    V_BSTR(&vararg[1]) = SysAllocString(path);
    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_I2;
    V_BSTR(&vararg[0]) = 0;
    hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
    ok(hr == S_OK, "IDispatch::Invoke returned 0x%08x\n", hr);
    VariantClear(&vararg[1]);
    VariantClear(&varresult);

    DeleteFileW(path);
831 832 833 834

    /* Test invoking a method as a DISPATCH_PROPERTYGET or DISPATCH_PROPERTYPUT */
    VariantInit(&vararg[0]);
    hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYGET, &dispparams, &varresult, &excepinfo, NULL);
835
    ok(hr == DISP_E_MEMBERNOTFOUND, "IDispatch::Invoke returned 0x%08x\n", hr);
836 837 838

    VariantInit(&vararg[0]);
    hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYPUT, &dispparams, &varresult, &excepinfo, NULL);
839
    ok(hr == DISP_E_MEMBERNOTFOUND, "IDispatch::Invoke returned 0x%08x\n", hr);
840 841

    /* Test invoking a read-only property as DISPATCH_PROPERTYPUT or as a DISPATCH_METHOD */
842
    name = szProductState;
843
    hr = IDispatch_GetIDsOfNames(pInstaller, &IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &dispid);
844
    ok(hr == S_OK, "IDispatch::GetIDsOfNames returned 0x%08x\n", hr);
845 846 847 848

    dispparams.rgvarg = NULL;
    dispparams.cArgs = 0;
    hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYPUT, &dispparams, &varresult, &excepinfo, NULL);
849
    ok(hr == DISP_E_MEMBERNOTFOUND, "IDispatch::Invoke returned 0x%08x\n", hr);
850 851 852 853

    dispparams.rgvarg = NULL;
    dispparams.cArgs = 0;
    hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
854
    ok(hr == DISP_E_MEMBERNOTFOUND, "IDispatch::Invoke returned 0x%08x\n", hr);
855 856 857
}

/* invocation helper function */
858 859
static int _invoke_todo_vtResult = 0;

860
static HRESULT invoke(IDispatch *pDispatch, LPCSTR szName, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, VARTYPE vtResult)
861
{
862
    OLECHAR *name = NULL;
863 864
    DISPID dispid;
    HRESULT hr;
865
    UINT i;
866
    UINT len;
867

868 869 870
    memset(pVarResult, 0, sizeof(VARIANT));
    VariantInit(pVarResult);

871 872 873
    len = MultiByteToWideChar(CP_ACP, 0, szName, -1, NULL, 0 );
    name = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR) );
    if (!name) return E_FAIL;
874
    MultiByteToWideChar(CP_ACP, 0, szName, -1, name, len );
875
    hr = IDispatch_GetIDsOfNames(pDispatch, &IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &dispid);
876
    HeapFree(GetProcessHeap(), 0, name);
877
    ok(hr == S_OK, "IDispatch::GetIDsOfNames returned 0x%08x\n", hr);
878
    if (hr != S_OK) return hr;
879 880 881 882

    memset(&excepinfo, 0, sizeof(excepinfo));
    hr = IDispatch_Invoke(pDispatch, dispid, &IID_NULL, LOCALE_NEUTRAL, wFlags, pDispParams, pVarResult, &excepinfo, NULL);

883
    if (hr == S_OK)
884
    {
885
        todo_wine_if (_invoke_todo_vtResult)
886
            ok(V_VT(pVarResult) == vtResult, "Variant result type is %d, expected %d\n", V_VT(pVarResult), vtResult);
887 888 889
        if (vtResult != VT_EMPTY)
        {
            hr = VariantChangeTypeEx(pVarResult, pVarResult, LOCALE_NEUTRAL, 0, vtResult);
890
            ok(hr == S_OK, "VariantChangeTypeEx returned 0x%08x\n", hr);
891 892 893 894 895 896 897 898 899 900 901
        }
    }

    for (i=0; i<pDispParams->cArgs; i++)
        VariantClear(&pDispParams->rgvarg[i]);

    return hr;
}

/* Object_Property helper functions */

902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917
static HRESULT Installer_CreateRecord(int count, IDispatch **pRecord)
{
    VARIANT varresult;
    VARIANTARG vararg[1];
    DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
    HRESULT hr;

    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_I4;
    V_I4(&vararg[0]) = count;

    hr = invoke(pInstaller, "CreateRecord", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH);
    *pRecord = V_DISPATCH(&varresult);
    return hr;
}

918
static HRESULT Installer_RegistryValue(HKEY hkey, LPCWSTR szKey, VARIANT vValue, VARIANT *pVarResult, VARTYPE vtExpect)
919 920 921 922 923 924
{
    VARIANTARG vararg[3];
    DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};

    VariantInit(&vararg[2]);
    V_VT(&vararg[2]) = VT_I4;
925
    V_I4(&vararg[2]) = (INT_PTR)hkey;
926 927 928 929
    VariantInit(&vararg[1]);
    V_VT(&vararg[1]) = VT_BSTR;
    V_BSTR(&vararg[1]) = SysAllocString(szKey);
    VariantInit(&vararg[0]);
930 931 932 933 934 935 936 937 938 939 940
    VariantCopy(&vararg[0], &vValue);
    VariantClear(&vValue);

    return invoke(pInstaller, "RegistryValue", DISPATCH_METHOD, &dispparams, pVarResult, vtExpect);
}

static HRESULT Installer_RegistryValueE(HKEY hkey, LPCWSTR szKey, BOOL *pBool)
{
    VARIANT varresult;
    VARIANTARG vararg;
    HRESULT hr;
941

942 943 944
    VariantInit(&vararg);
    V_VT(&vararg) = VT_EMPTY;
    hr = Installer_RegistryValue(hkey, szKey, vararg, &varresult, VT_BOOL);
945 946 947 948 949 950 951 952
    *pBool = V_BOOL(&varresult);
    VariantClear(&varresult);
    return hr;
}

static HRESULT Installer_RegistryValueW(HKEY hkey, LPCWSTR szKey, LPCWSTR szValue, LPWSTR szString)
{
    VARIANT varresult;
953
    VARIANTARG vararg;
954 955
    HRESULT hr;

956 957 958
    VariantInit(&vararg);
    V_VT(&vararg) = VT_BSTR;
    V_BSTR(&vararg) = SysAllocString(szValue);
959

960
    hr = Installer_RegistryValue(hkey, szKey, vararg, &varresult, VT_BSTR);
961
    if (V_BSTR(&varresult)) lstrcpyW(szString, V_BSTR(&varresult));
962 963 964 965 966 967 968
    VariantClear(&varresult);
    return hr;
}

static HRESULT Installer_RegistryValueI(HKEY hkey, LPCWSTR szKey, int iValue, LPWSTR szString, VARTYPE vtResult)
{
    VARIANT varresult;
969
    VARIANTARG vararg;
970 971
    HRESULT hr;

972 973 974
    VariantInit(&vararg);
    V_VT(&vararg) = VT_I4;
    V_I4(&vararg) = iValue;
975

976
    hr = Installer_RegistryValue(hkey, szKey, vararg, &varresult, vtResult);
977
    if (SUCCEEDED(hr) && vtResult == VT_BSTR) lstrcpyW(szString, V_BSTR(&varresult));
978 979 980 981
    VariantClear(&varresult);
    return hr;
}

982 983 984 985 986 987 988 989 990 991 992 993 994 995
static HRESULT Installer_OpenPackage(LPCWSTR szPackagePath, int options, IDispatch **pSession)
{
    VARIANT varresult;
    VARIANTARG vararg[2];
    DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
    HRESULT hr;

    VariantInit(&vararg[1]);
    V_VT(&vararg[1]) = VT_BSTR;
    V_BSTR(&vararg[1]) = SysAllocString(szPackagePath);
    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_I4;
    V_I4(&vararg[0]) = options;

996
    hr = invoke(pInstaller, "OpenPackage", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH);
997 998 999 1000
    *pSession = V_DISPATCH(&varresult);
    return hr;
}

1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
static HRESULT Installer_OpenDatabase(LPCWSTR szDatabasePath, int openmode, IDispatch **pDatabase)
{
    VARIANT varresult;
    VARIANTARG vararg[2];
    DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
    HRESULT hr;

    VariantInit(&vararg[1]);
    V_VT(&vararg[1]) = VT_BSTR;
    V_BSTR(&vararg[1]) = SysAllocString(szDatabasePath);
    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_I4;
    V_I4(&vararg[0]) = openmode;

    hr = invoke(pInstaller, "OpenDatabase", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH);
    *pDatabase = V_DISPATCH(&varresult);
    return hr;
}

1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035
static HRESULT Installer_InstallProduct(LPCWSTR szPackagePath, LPCWSTR szPropertyValues)
{
    VARIANT varresult;
    VARIANTARG vararg[2];
    DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};

    VariantInit(&vararg[1]);
    V_VT(&vararg[1]) = VT_BSTR;
    V_BSTR(&vararg[1]) = SysAllocString(szPackagePath);
    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_BSTR;
    V_BSTR(&vararg[0]) = SysAllocString(szPropertyValues);

    return invoke(pInstaller, "InstallProduct", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY);
}

1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052
static HRESULT Installer_ProductState(LPCWSTR szProduct, int *pInstallState)
{
    VARIANT varresult;
    VARIANTARG vararg[1];
    DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
    HRESULT hr;

    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_BSTR;
    V_BSTR(&vararg[0]) = SysAllocString(szProduct);

    hr = invoke(pInstaller, "ProductState", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
    *pInstallState = V_I4(&varresult);
    VariantClear(&varresult);
    return hr;
}

1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067
static HRESULT Installer_ProductInfo(LPCWSTR szProduct, LPCWSTR szAttribute, LPWSTR szString)
{
    VARIANT varresult;
    VARIANTARG vararg[2];
    DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
    HRESULT hr;

    VariantInit(&vararg[1]);
    V_VT(&vararg[1]) = VT_BSTR;
    V_BSTR(&vararg[1]) = SysAllocString(szProduct);
    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_BSTR;
    V_BSTR(&vararg[0]) = SysAllocString(szAttribute);

    hr = invoke(pInstaller, "ProductInfo", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR);
1068
    if (V_BSTR(&varresult)) lstrcpyW(szString, V_BSTR(&varresult));
1069 1070 1071 1072
    VariantClear(&varresult);
    return hr;
}

1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083
static HRESULT Installer_Products(IDispatch **pStringList)
{
    VARIANT varresult;
    DISPPARAMS dispparams = {NULL, NULL, 0, 0};
    HRESULT hr;

    hr = invoke(pInstaller, "Products", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH);
    *pStringList = V_DISPATCH(&varresult);
    return hr;
}

1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099
static HRESULT Installer_RelatedProducts(LPCWSTR szProduct, IDispatch **pStringList)
{
    VARIANT varresult;
    VARIANTARG vararg[1];
    DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
    HRESULT hr;

    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_BSTR;
    V_BSTR(&vararg[0]) = SysAllocString(szProduct);

    hr = invoke(pInstaller, "RelatedProducts", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH);
    *pStringList = V_DISPATCH(&varresult);
    return hr;
}

1100
static HRESULT Installer_VersionGet(LPWSTR szVersion)
1101 1102 1103 1104 1105 1106
{
    VARIANT varresult;
    DISPPARAMS dispparams = {NULL, NULL, 0, 0};
    HRESULT hr;

    hr = invoke(pInstaller, "Version", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR);
1107
    if (V_BSTR(&varresult)) lstrcpyW(szVersion, V_BSTR(&varresult));
1108 1109 1110 1111
    VariantClear(&varresult);
    return hr;
}

1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125
static HRESULT Installer_UILevelPut(int level)
{
    VARIANT varresult;
    VARIANTARG vararg;
    DISPID dispid = DISPID_PROPERTYPUT;
    DISPPARAMS dispparams = {&vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};

    VariantInit(&vararg);
    V_VT(&vararg) = VT_I4;
    V_I4(&vararg) = level;

    return invoke(pInstaller, "UILevel", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
}

1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144
static HRESULT Installer_SummaryInformation(BSTR PackagePath, int UpdateCount, IDispatch **pSumInfo)
{
    VARIANT varresult;
    VARIANTARG vararg[2];
    DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
    HRESULT hr;

    VariantInit(&vararg[1]);
    V_VT(&vararg[1]) = VT_BSTR;
    V_BSTR(&vararg[1]) = SysAllocString(PackagePath);
    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_I4;
    V_I4(&vararg[0]) = UpdateCount;

    hr = invoke(pInstaller, "SummaryInformation", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH);
    *pSumInfo = V_DISPATCH(&varresult);
    return hr;
}

1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155
static HRESULT Session_Installer(IDispatch *pSession, IDispatch **pInst)
{
    VARIANT varresult;
    DISPPARAMS dispparams = {NULL, NULL, 0, 0};
    HRESULT hr;

    hr = invoke(pSession, "Installer", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH);
    *pInst = V_DISPATCH(&varresult);
    return hr;
}

1156
static HRESULT Session_PropertyGet(IDispatch *pSession, LPCWSTR szName, LPWSTR szReturn)
1157 1158 1159 1160 1161 1162 1163 1164 1165 1166
{
    VARIANT varresult;
    VARIANTARG vararg[1];
    DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
    HRESULT hr;

    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_BSTR;
    V_BSTR(&vararg[0]) = SysAllocString(szName);

1167
    hr = invoke(pSession, "Property", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR);
1168
    if (V_BSTR(&varresult)) lstrcpyW(szReturn, V_BSTR(&varresult));
1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186
    VariantClear(&varresult);
    return hr;
}

static HRESULT Session_PropertyPut(IDispatch *pSession, LPCWSTR szName, LPCWSTR szValue)
{
    VARIANT varresult;
    VARIANTARG vararg[2];
    DISPID dispid = DISPID_PROPERTYPUT;
    DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};

    VariantInit(&vararg[1]);
    V_VT(&vararg[1]) = VT_BSTR;
    V_BSTR(&vararg[1]) = SysAllocString(szName);
    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_BSTR;
    V_BSTR(&vararg[0]) = SysAllocString(szValue);

1187
    return invoke(pSession, "Property", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
1188 1189 1190 1191 1192 1193 1194 1195
}

static HRESULT Session_LanguageGet(IDispatch *pSession, UINT *pLangId)
{
    VARIANT varresult;
    DISPPARAMS dispparams = {NULL, NULL, 0, 0};
    HRESULT hr;

1196
    hr = invoke(pSession, "Language", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1197 1198 1199 1200 1201
    *pLangId = V_I4(&varresult);
    VariantClear(&varresult);
    return hr;
}

1202
static HRESULT Session_ModeGet(IDispatch *pSession, int iFlag, VARIANT_BOOL *mode)
1203 1204 1205 1206 1207 1208 1209 1210 1211 1212
{
    VARIANT varresult;
    VARIANTARG vararg[1];
    DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
    HRESULT hr;

    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_I4;
    V_I4(&vararg[0]) = iFlag;

1213
    hr = invoke(pSession, "Mode", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BOOL);
1214
    *mode = V_BOOL(&varresult);
1215 1216 1217 1218
    VariantClear(&varresult);
    return hr;
}

1219
static HRESULT Session_ModePut(IDispatch *pSession, int iFlag, VARIANT_BOOL mode)
1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230
{
    VARIANT varresult;
    VARIANTARG vararg[2];
    DISPID dispid = DISPID_PROPERTYPUT;
    DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};

    VariantInit(&vararg[1]);
    V_VT(&vararg[1]) = VT_I4;
    V_I4(&vararg[1]) = iFlag;
    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_BOOL;
1231
    V_BOOL(&vararg[0]) = mode;
1232

1233
    return invoke(pSession, "Mode", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
1234 1235 1236 1237 1238 1239 1240 1241
}

static HRESULT Session_Database(IDispatch *pSession, IDispatch **pDatabase)
{
    VARIANT varresult;
    DISPPARAMS dispparams = {NULL, NULL, 0, 0};
    HRESULT hr;

1242
    hr = invoke(pSession, "Database", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH);
1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257
    *pDatabase = V_DISPATCH(&varresult);
    return hr;
}

static HRESULT Session_DoAction(IDispatch *pSession, LPCWSTR szAction, int *iReturn)
{
    VARIANT varresult;
    VARIANTARG vararg[1];
    DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
    HRESULT hr;

    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_BSTR;
    V_BSTR(&vararg[0]) = SysAllocString(szAction);

1258
    hr = invoke(pSession, "DoAction", DISPATCH_METHOD, &dispparams, &varresult, VT_I4);
1259 1260 1261 1262 1263
    *iReturn = V_I4(&varresult);
    VariantClear(&varresult);
    return hr;
}

1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280
static HRESULT Session_EvaluateCondition(IDispatch *pSession, LPCWSTR szCondition, int *iReturn)
{
    VARIANT varresult;
    VARIANTARG vararg[1];
    DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
    HRESULT hr;

    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_BSTR;
    V_BSTR(&vararg[0]) = SysAllocString(szCondition);

    hr = invoke(pSession, "EvaluateCondition", DISPATCH_METHOD, &dispparams, &varresult, VT_I4);
    *iReturn = V_I4(&varresult);
    VariantClear(&varresult);
    return hr;
}

1281
static HRESULT Session_Message(IDispatch *pSession, LONG kind, IDispatch *record, int *ret)
1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301
{
    VARIANT varresult;
    VARIANTARG vararg[2];
    DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
    HRESULT hr;

    VariantInit(&varresult);
    V_VT(vararg) = VT_DISPATCH;
    V_DISPATCH(vararg) = record;
    V_VT(vararg+1) = VT_I4;
    V_I4(vararg+1) = kind;

    hr = invoke(pSession, "Message", DISPATCH_METHOD, &dispparams, &varresult, VT_I4);

    ok(V_VT(&varresult) == VT_I4, "V_VT(varresult) = %d\n", V_VT(&varresult));
    *ret = V_I4(&varresult);

    return hr;
}

1302
static HRESULT Session_SetInstallLevel(IDispatch *pSession, LONG iInstallLevel)
1303 1304 1305 1306 1307 1308 1309 1310 1311
{
    VARIANT varresult;
    VARIANTARG vararg[1];
    DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};

    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_I4;
    V_I4(&vararg[0]) = iInstallLevel;

1312
    return invoke(pSession, "SetInstallLevel", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY);
1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325
}

static HRESULT Session_FeatureCurrentState(IDispatch *pSession, LPCWSTR szName, int *pState)
{
    VARIANT varresult;
    VARIANTARG vararg[1];
    DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
    HRESULT hr;

    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_BSTR;
    V_BSTR(&vararg[0]) = SysAllocString(szName);

1326
    hr = invoke(pSession, "FeatureCurrentState", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342
    *pState = V_I4(&varresult);
    VariantClear(&varresult);
    return hr;
}

static HRESULT Session_FeatureRequestStateGet(IDispatch *pSession, LPCWSTR szName, int *pState)
{
    VARIANT varresult;
    VARIANTARG vararg[1];
    DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
    HRESULT hr;

    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_BSTR;
    V_BSTR(&vararg[0]) = SysAllocString(szName);

1343
    hr = invoke(pSession, "FeatureRequestState", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362
    *pState = V_I4(&varresult);
    VariantClear(&varresult);
    return hr;
}

static HRESULT Session_FeatureRequestStatePut(IDispatch *pSession, LPCWSTR szName, int iState)
{
    VARIANT varresult;
    VARIANTARG vararg[2];
    DISPID dispid = DISPID_PROPERTYPUT;
    DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};

    VariantInit(&vararg[1]);
    V_VT(&vararg[1]) = VT_BSTR;
    V_BSTR(&vararg[1]) = SysAllocString(szName);
    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_I4;
    V_I4(&vararg[0]) = iState;

1363
    return invoke(pSession, "FeatureRequestState", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376
}

static HRESULT Database_OpenView(IDispatch *pDatabase, LPCWSTR szSql, IDispatch **pView)
{
    VARIANT varresult;
    VARIANTARG vararg[1];
    DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
    HRESULT hr;

    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_BSTR;
    V_BSTR(&vararg[0]) = SysAllocString(szSql);

1377
    hr = invoke(pDatabase, "OpenView", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH);
1378 1379 1380 1381
    *pView = V_DISPATCH(&varresult);
    return hr;
}

1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397
static HRESULT Database_SummaryInformation(IDispatch *pDatabase, int iUpdateCount, IDispatch **pSummaryInfo)
{
    VARIANT varresult;
    VARIANTARG vararg[1];
    DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
    HRESULT hr;

    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_I4;
    V_I4(&vararg[0]) = iUpdateCount;

    hr = invoke(pDatabase, "SummaryInformation", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH);
    *pSummaryInfo = V_DISPATCH(&varresult);
    return hr;
}

1398 1399 1400 1401 1402 1403 1404 1405 1406 1407
static HRESULT View_Execute(IDispatch *pView, IDispatch *pRecord)
{
    VARIANT varresult;
    VARIANTARG vararg[1];
    DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};

    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_DISPATCH;
    V_DISPATCH(&vararg[0]) = pRecord;

1408
    return invoke(pView, "Execute", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY);
1409 1410 1411 1412 1413 1414
}

static HRESULT View_Fetch(IDispatch *pView, IDispatch **ppRecord)
{
    VARIANT varresult;
    DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1415
    HRESULT hr = invoke(pView, "Fetch", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH);
1416 1417 1418 1419
    *ppRecord = V_DISPATCH(&varresult);
    return hr;
}

1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437
static HRESULT View_Modify(IDispatch *pView, int iMode, IDispatch *pRecord)
{
    VARIANT varresult;
    VARIANTARG vararg[2];
    DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};

    VariantInit(&vararg[1]);
    V_VT(&vararg[1]) = VT_I4;
    V_I4(&vararg[1]) = iMode;
    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_DISPATCH;
    V_DISPATCH(&vararg[0]) = pRecord;
    if (pRecord)
        IDispatch_AddRef(pRecord);   /* VariantClear in invoke will call IDispatch_Release */

    return invoke(pView, "Modify", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY);
}

1438 1439 1440 1441
static HRESULT View_Close(IDispatch *pView)
{
    VARIANT varresult;
    DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1442
    return invoke(pView, "Close", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY);
1443 1444
}

1445 1446 1447 1448 1449 1450 1451 1452 1453 1454
static HRESULT Record_FieldCountGet(IDispatch *pRecord, int *pFieldCount)
{
    VARIANT varresult;
    DISPPARAMS dispparams = {NULL, NULL, 0, 0};
    HRESULT hr = invoke(pRecord, "FieldCount", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
    *pFieldCount = V_I4(&varresult);
    VariantClear(&varresult);
    return hr;
}

1455
static HRESULT Record_StringDataGet(IDispatch *pRecord, int iField, LPWSTR szString)
1456 1457 1458 1459 1460 1461 1462 1463 1464 1465
{
    VARIANT varresult;
    VARIANTARG vararg[1];
    DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
    HRESULT hr;

    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_I4;
    V_I4(&vararg[0]) = iField;

1466
    hr = invoke(pRecord, "StringData", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR);
1467
    if (V_BSTR(&varresult)) lstrcpyW(szString, V_BSTR(&varresult));
1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485
    VariantClear(&varresult);
    return hr;
}

static HRESULT Record_StringDataPut(IDispatch *pRecord, int iField, LPCWSTR szString)
{
    VARIANT varresult;
    VARIANTARG vararg[2];
    DISPID dispid = DISPID_PROPERTYPUT;
    DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};

    VariantInit(&vararg[1]);
    V_VT(&vararg[1]) = VT_I4;
    V_I4(&vararg[1]) = iField;
    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_BSTR;
    V_BSTR(&vararg[0]) = SysAllocString(szString);

1486
    return invoke(pRecord, "StringData", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
1487 1488
}

1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522
static HRESULT Record_IntegerDataGet(IDispatch *pRecord, int iField, int *pValue)
{
    VARIANT varresult;
    VARIANTARG vararg[1];
    DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
    HRESULT hr;

    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_I4;
    V_I4(&vararg[0]) = iField;

    hr = invoke(pRecord, "IntegerData", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
    *pValue = V_I4(&varresult);
    VariantClear(&varresult);
    return hr;
}

static HRESULT Record_IntegerDataPut(IDispatch *pRecord, int iField, int iValue)
{
    VARIANT varresult;
    VARIANTARG vararg[2];
    DISPID dispid = DISPID_PROPERTYPUT;
    DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};

    VariantInit(&vararg[1]);
    V_VT(&vararg[1]) = VT_I4;
    V_I4(&vararg[1]) = iField;
    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_I4;
    V_I4(&vararg[0]) = iValue;

    return invoke(pRecord, "IntegerData", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
}

1523 1524 1525 1526 1527 1528 1529 1530 1531
static HRESULT StringList__NewEnum(IDispatch *pList, IUnknown **ppEnumVARIANT)
{
    VARIANT varresult;
    DISPPARAMS dispparams = {NULL, NULL, 0, 0};
    HRESULT hr = invoke(pList, "_NewEnum", DISPATCH_METHOD, &dispparams, &varresult, VT_UNKNOWN);
    *ppEnumVARIANT = V_UNKNOWN(&varresult);
    return hr;
}

1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543
static HRESULT StringList_Item(IDispatch *pStringList, int iIndex, LPWSTR szString)
{
    VARIANT varresult;
    VARIANTARG vararg[1];
    DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
    HRESULT hr;

    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_I4;
    V_I4(&vararg[0]) = iIndex;

    hr = invoke(pStringList, "Item", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR);
1544
    if (V_BSTR(&varresult)) lstrcpyW(szString, V_BSTR(&varresult));
1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558
    VariantClear(&varresult);
    return hr;
}

static HRESULT StringList_Count(IDispatch *pStringList, int *pCount)
{
    VARIANT varresult;
    DISPPARAMS dispparams = {NULL, NULL, 0, 0};
    HRESULT hr = invoke(pStringList, "Count", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
    *pCount = V_I4(&varresult);
    VariantClear(&varresult);
    return hr;
}

1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569
static HRESULT SummaryInfo_PropertyGet(IDispatch *pSummaryInfo, int pid, VARIANT *pVarResult, VARTYPE vtExpect)
{
    VARIANTARG vararg[1];
    DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};

    VariantInit(&vararg[0]);
    V_VT(&vararg[0]) = VT_I4;
    V_I4(&vararg[0]) = pid;
    return invoke(pSummaryInfo, "Property", DISPATCH_PROPERTYGET, &dispparams, pVarResult, vtExpect);
}

1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585
static HRESULT SummaryInfo_PropertyPut(IDispatch *pSummaryInfo, int pid, VARIANT *pVariant)
{
    VARIANT varresult;
    VARIANTARG vararg[2];
    DISPID dispid = DISPID_PROPERTYPUT;
    DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};

    VariantInit(&vararg[1]);
    V_VT(&vararg[1]) = VT_I4;
    V_I4(&vararg[1]) = pid;
    VariantInit(&vararg[0]);
    VariantCopyInd(vararg, pVariant);

    return invoke(pSummaryInfo, "Property", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
}

1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597
static HRESULT SummaryInfo_PropertyCountGet(IDispatch *pSummaryInfo, int *pCount)
{
    VARIANT varresult;
    DISPPARAMS dispparams = {NULL, NULL, 0, 0};
    HRESULT hr;

    hr = invoke(pSummaryInfo, "PropertyCount", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
    *pCount = V_I4(&varresult);
    VariantClear(&varresult);
    return hr;
}

1598 1599
/* Test the various objects */

1600 1601
#define TEST_SUMMARYINFO_PROPERTIES_MODIFIED 4

1602
static void test_SummaryInfo(IDispatch *pSummaryInfo, const msi_summary_info *info, int num_info, BOOL readonly)
1603 1604
{
    static const WCHAR szPropertyException[] = { 'P','r','o','p','e','r','t','y',',','P','i','d',0 };
1605 1606 1607
    static const WCHAR szTitle[] = { 'T','i','t','l','e',0 };
    VARIANT varresult, var;
    SYSTEMTIME st;
1608 1609 1610
    HRESULT hr;
    int j;

1611
    /* SummaryInfo::PropertyCount */
1612 1613 1614
    hr = SummaryInfo_PropertyCountGet(pSummaryInfo, &j);
    ok(hr == S_OK, "SummaryInfo_PropertyCount failed, hresult 0x%08x\n", hr);
    ok(j == num_info, "SummaryInfo_PropertyCount returned %d, expected %d\n", j, num_info);
1615

1616 1617 1618 1619 1620 1621 1622 1623
    /* SummaryInfo::Property, get for properties we have set */
    for (j = 0; j < num_info; j++)
    {
        const msi_summary_info *entry = &info[j];

        int vt = entry->datatype;
        if (vt == VT_LPSTR) vt = VT_BSTR;
        else if (vt == VT_FILETIME) vt = VT_DATE;
1624
        else if (vt == VT_I2) vt = VT_I4;
1625 1626 1627 1628 1629 1630 1631 1632

        hr = SummaryInfo_PropertyGet(pSummaryInfo, entry->property, &varresult, vt);
        ok(hr == S_OK, "SummaryInfo_Property (pid %d) failed, hresult 0x%08x\n", entry->property, hr);
        if (V_VT(&varresult) != vt)
            skip("Skipping property tests due to type mismatch\n");
        else if (vt == VT_I4)
            ok(V_I4(&varresult) == entry->iValue, "SummaryInfo_Property (pid %d) I4 result expected to be %d, but was %d\n",
               entry->property, entry->iValue, V_I4(&varresult));
1633
        else if (vt == VT_DATE)
1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648
        {
            FILETIME ft;
            DATE d;

            FileTimeToLocalFileTime(entry->pftValue, &ft);
            FileTimeToSystemTime(&ft, &st);
            SystemTimeToVariantTime(&st, &d);
            ok(d == V_DATE(&varresult), "SummaryInfo_Property (pid %d) DATE result expected to be %lf, but was %lf\n", entry->property, d, V_DATE(&varresult));
        }
        else if (vt == VT_BSTR)
        {
            ok_awplus("SummaryInfo_Property (pid %d) BSTR result expected to be %s, but was %s\n", entry->property, entry->szValue, V_BSTR(&varresult));
        }
        else
            skip("SummaryInfo_Property (pid %d) unhandled result type %d\n", entry->property, vt);
1649 1650

        VariantClear(&varresult);
1651 1652 1653 1654 1655 1656
    }

    /* SummaryInfo::Property, get; invalid arguments */

    /* Invalid pids */
    hr = SummaryInfo_PropertyGet(pSummaryInfo, -1, &varresult, VT_EMPTY);
1657
    ok(hr == DISP_E_EXCEPTION, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1658 1659 1660
    ok_exception(hr, szPropertyException);

    hr = SummaryInfo_PropertyGet(pSummaryInfo, 1000, &varresult, VT_EMPTY);
1661
    ok(hr == DISP_E_EXCEPTION, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675
    ok_exception(hr, szPropertyException);

    /* Unsupported pids */
    hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_DICTIONARY, &varresult, VT_EMPTY);
    ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);

    hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_THUMBNAIL, &varresult, VT_EMPTY);
    ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);

    /* Pids we have not set, one for each type */
    hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_CODEPAGE, &varresult, VT_EMPTY);
    ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);

    hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_TITLE, &varresult, VT_EMPTY);
1676
    ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1677 1678 1679 1680 1681 1682

    hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_EDITTIME, &varresult, VT_EMPTY);
    ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);

    hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_CHARCOUNT, &varresult, VT_EMPTY);
    ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1683 1684 1685 1686 1687 1688 1689 1690 1691 1692

    if (!readonly)
    {
        /* SummaryInfo::Property, put; one for each type */

        /* VT_I2 */
        VariantInit(&var);
        V_VT(&var) = VT_I2;
        V_I2(&var) = 1;
        hr = SummaryInfo_PropertyPut(pSummaryInfo, PID_CODEPAGE, &var);
1693
        ok(hr == S_OK, "SummaryInfo_PropertyPut failed, hresult 0x%08x\n", hr);
1694 1695 1696

        hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_CODEPAGE, &varresult, VT_I4 /* NOT VT_I2 */);
        ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1697
        ok(V_I2(&var) == V_I2(&varresult), "SummaryInfo_PropertyGet expected %d, but returned %d\n", V_I2(&var), V_I2(&varresult));
1698 1699 1700 1701 1702 1703 1704
        VariantClear(&varresult);
        VariantClear(&var);

        /* VT_BSTR */
        V_VT(&var) = VT_BSTR;
        V_BSTR(&var) = SysAllocString(szTitle);
        hr = SummaryInfo_PropertyPut(pSummaryInfo, PID_TITLE, &var);
1705
        ok(hr == S_OK, "SummaryInfo_PropertyPut failed, hresult 0x%08x\n", hr);
1706 1707 1708

        hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_TITLE, &varresult, V_VT(&var));
        ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1709
        ok_w2("SummaryInfo_PropertyGet expected %s, but returned %s\n", V_BSTR(&var), V_BSTR(&varresult));
1710 1711 1712 1713 1714 1715 1716 1717
        VariantClear(&varresult);
        VariantClear(&var);

        /* VT_DATE */
        V_VT(&var) = VT_DATE;
        FileTimeToSystemTime(&systemtime, &st);
        SystemTimeToVariantTime(&st, &V_DATE(&var));
        hr = SummaryInfo_PropertyPut(pSummaryInfo, PID_LASTSAVE_DTM, &var);
1718
        ok(hr == S_OK, "SummaryInfo_PropertyPut failed, hresult 0x%08x\n", hr);
1719 1720 1721

        hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_LASTSAVE_DTM, &varresult, V_VT(&var));
        ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1722
        ok(V_DATE(&var) == V_DATE(&varresult), "SummaryInfo_PropertyGet expected %lf, but returned %lf\n", V_DATE(&var), V_DATE(&varresult));
1723 1724 1725 1726 1727 1728 1729
        VariantClear(&varresult);
        VariantClear(&var);

        /* VT_I4 */
        V_VT(&var) = VT_I4;
        V_I4(&var) = 1000;
        hr = SummaryInfo_PropertyPut(pSummaryInfo, PID_CHARCOUNT, &var);
1730
        ok(hr == S_OK, "SummaryInfo_PropertyPut failed, hresult 0x%08x\n", hr);
1731 1732 1733

        hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_CHARCOUNT, &varresult, V_VT(&var));
        ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1734
        ok(V_I4(&var) == V_I4(&varresult), "SummaryInfo_PropertyGet expected %d, but returned %d\n", V_I4(&var), V_I4(&varresult));
1735 1736
        VariantClear(&varresult);
        VariantClear(&var);
1737 1738

        /* SummaryInfo::PropertyCount */
1739 1740 1741
        hr = SummaryInfo_PropertyCountGet(pSummaryInfo, &j);
        ok(hr == S_OK, "SummaryInfo_PropertyCount failed, hresult 0x%08x\n", hr);
        ok(j == num_info+4, "SummaryInfo_PropertyCount returned %d, expected %d\n", j, num_info);
1742
    }
1743 1744
}

1745
static void test_Database(IDispatch *pDatabase, BOOL readonly)
1746
{
1747 1748 1749 1750 1751
    static const WCHAR szSql[] = { 'S','E','L','E','C','T',' ','`','F','e','a','t','u','r','e','`',' ','F','R','O','M',' ','`','F','e','a','t','u','r','e','`',' ','W','H','E','R','E',' ','`','F','e','a','t','u','r','e','_','P','a','r','e','n','t','`','=','\'','O','n','e','\'',0 };
    static const WCHAR szThree[] = { 'T','h','r','e','e',0 };
    static const WCHAR szTwo[] = { 'T','w','o',0 };
    static const WCHAR szStringDataField[] = { 'S','t','r','i','n','g','D','a','t','a',',','F','i','e','l','d',0 };
    static const WCHAR szModifyModeRecord[] = { 'M','o','d','i','f','y',',','M','o','d','e',',','R','e','c','o','r','d',0 };
1752
    IDispatch *pView = NULL, *pSummaryInfo = NULL;
1753 1754 1755
    HRESULT hr;

    hr = Database_OpenView(pDatabase, szSql, &pView);
1756 1757
    ok(hr == S_OK, "Database_OpenView failed, hresult 0x%08x\n", hr);
    if (hr == S_OK)
1758 1759 1760 1761 1762 1763
    {
        IDispatch *pRecord = NULL;
        WCHAR szString[MAX_PATH];

        /* View::Execute */
        hr = View_Execute(pView, NULL);
1764
        ok(hr == S_OK, "View_Execute failed, hresult 0x%08x\n", hr);
1765 1766 1767

        /* View::Fetch */
        hr = View_Fetch(pView, &pRecord);
1768
        ok(hr == S_OK, "View_Fetch failed, hresult 0x%08x\n", hr);
1769 1770 1771 1772 1773 1774
        ok(pRecord != NULL, "View_Fetch should not have returned NULL record\n");
        if (pRecord)
        {
            /* Record::StringDataGet */
            memset(szString, 0, sizeof(szString));
            hr = Record_StringDataGet(pRecord, 1, szString);
1775
            ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr);
1776 1777
            ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szThree);

1778 1779
            /* Record::StringDataPut with correct index */
            hr = Record_StringDataPut(pRecord, 1, szTwo);
1780
            ok(hr == S_OK, "Record_StringDataPut failed, hresult 0x%08x\n", hr);
1781 1782 1783 1784

            /* Record::StringDataGet */
            memset(szString, 0, sizeof(szString));
            hr = Record_StringDataGet(pRecord, 1, szString);
1785
            ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr);
1786 1787
            ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szTwo);

1788 1789 1790 1791 1792
            /* Record::StringDataPut with incorrect index */
            hr = Record_StringDataPut(pRecord, -1, szString);
            ok(hr == DISP_E_EXCEPTION, "Record_StringDataPut failed, hresult 0x%08x\n", hr);
            ok_exception(hr, szStringDataField);

1793
            /* View::Modify with incorrect parameters */
1794 1795 1796
            hr = View_Modify(pView, -5, NULL);
            ok(hr == DISP_E_EXCEPTION, "View_Modify failed, hresult 0x%08x\n", hr);
            ok_exception(hr, szModifyModeRecord);
1797

1798 1799 1800
            hr = View_Modify(pView, -5, pRecord);
            ok(hr == DISP_E_EXCEPTION, "View_Modify failed, hresult 0x%08x\n", hr);
            ok_exception(hr, szModifyModeRecord);
1801

1802 1803 1804
            hr = View_Modify(pView, MSIMODIFY_REFRESH, NULL);
            ok(hr == DISP_E_EXCEPTION, "View_Modify failed, hresult 0x%08x\n", hr);
            ok_exception(hr, szModifyModeRecord);
1805

1806
            hr = View_Modify(pView, MSIMODIFY_REFRESH, pRecord);
1807
            ok(hr == S_OK, "View_Modify failed, hresult 0x%08x\n", hr);
1808 1809 1810 1811

            /* Record::StringDataGet, confirm that the record is back to its unmodified value */
            memset(szString, 0, sizeof(szString));
            hr = Record_StringDataGet(pRecord, 1, szString);
1812
            ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr);
1813 1814
            todo_wine ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szThree);

1815 1816 1817 1818 1819
            IDispatch_Release(pRecord);
        }

        /* View::Fetch */
        hr = View_Fetch(pView, &pRecord);
1820
        ok(hr == S_OK, "View_Fetch failed, hresult 0x%08x\n", hr);
1821 1822 1823 1824 1825 1826
        ok(pRecord != NULL, "View_Fetch should not have returned NULL record\n");
        if (pRecord)
        {
            /* Record::StringDataGet */
            memset(szString, 0, sizeof(szString));
            hr = Record_StringDataGet(pRecord, 1, szString);
1827
            ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr);
1828 1829 1830 1831 1832 1833 1834
            ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szTwo);

            IDispatch_Release(pRecord);
        }

        /* View::Fetch */
        hr = View_Fetch(pView, &pRecord);
1835
        ok(hr == S_OK, "View_Fetch failed, hresult 0x%08x\n", hr);
1836 1837 1838 1839 1840 1841
        ok(pRecord == NULL, "View_Fetch should have returned NULL record\n");
        if (pRecord)
            IDispatch_Release(pRecord);

        /* View::Close */
        hr = View_Close(pView);
1842
        ok(hr == S_OK, "View_Close failed, hresult 0x%08x\n", hr);
1843 1844 1845

        IDispatch_Release(pView);
    }
1846 1847

    /* Database::SummaryInformation */
1848
    hr = Database_SummaryInformation(pDatabase, TEST_SUMMARYINFO_PROPERTIES_MODIFIED, &pSummaryInfo);
1849 1850 1851 1852
    ok(hr == S_OK, "Database_SummaryInformation failed, hresult 0x%08x\n", hr);
    ok(pSummaryInfo != NULL, "Database_SummaryInformation should not have returned NULL record\n");
    if (pSummaryInfo)
    {
1853
        test_SummaryInfo(pSummaryInfo, summary_info, sizeof(summary_info)/sizeof(msi_summary_info), readonly);
1854 1855
        IDispatch_Release(pSummaryInfo);
    }
1856 1857 1858 1859
}

static void test_Session(IDispatch *pSession)
{
1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870
    static const WCHAR szProductName[] = { 'P','r','o','d','u','c','t','N','a','m','e',0 };
    static const WCHAR szOne[] = { 'O','n','e',0 };
    static const WCHAR szOneStateFalse[] = { '!','O','n','e','>','0',0 };
    static const WCHAR szOneStateTrue[] = { '!','O','n','e','=','-','1',0 };
    static const WCHAR szOneActionFalse[] = { '$','O','n','e','=','-','1',0 };
    static const WCHAR szOneActionTrue[] = { '$','O','n','e','>','0',0 };
    static const WCHAR szCostInitialize[] = { 'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0 };
    static const WCHAR szEmpty[] = { 0 };
    static const WCHAR szEquals[] = { '=',0 };
    static const WCHAR szPropertyName[] = { 'P','r','o','p','e','r','t','y',',','N','a','m','e',0 };
    static const WCHAR szModeFlag[] = { 'M','o','d','e',',','F','l','a','g',0 };
1871 1872 1873
    WCHAR stringw[MAX_PATH];
    CHAR string[MAX_PATH];
    UINT len;
1874
    VARIANT_BOOL bool;
1875
    int myint;
1876
    IDispatch *pDatabase = NULL, *pInst = NULL, *record = NULL;
1877
    ULONG refs_before, refs_after;
1878 1879
    HRESULT hr;

1880
    /* Session::Installer */
1881
    hr = Session_Installer(pSession, &pInst);
1882
    ok(hr == S_OK, "Session_Installer failed, hresult 0x%08x\n", hr);
1883 1884
    ok(pInst != NULL, "Session_Installer returned NULL IDispatch pointer\n");
    ok(pInst == pInstaller, "Session_Installer does not match Installer instance from CoCreateInstance\n");
1885 1886 1887 1888 1889 1890 1891 1892
    refs_before = IDispatch_AddRef(pInst);

    hr = Session_Installer(pSession, &pInst);
    ok(hr == S_OK, "Session_Installer failed, hresult 0x%08x\n", hr);
    ok(pInst != NULL, "Session_Installer returned NULL IDispatch pointer\n");
    ok(pInst == pInstaller, "Session_Installer does not match Installer instance from CoCreateInstance\n");
    refs_after = IDispatch_Release(pInst);
    ok(refs_before == refs_after, "got %u and %u\n", refs_before, refs_after);
1893

1894 1895 1896
    /* Session::Property, get */
    memset(stringw, 0, sizeof(stringw));
    hr = Session_PropertyGet(pSession, szProductName, stringw);
1897
    ok(hr == S_OK, "Session_PropertyGet failed, hresult 0x%08x\n", hr);
1898
    if (lstrcmpW(stringw, szMSITEST) != 0)
1899 1900 1901 1902 1903 1904 1905 1906
    {
        len = WideCharToMultiByte(CP_ACP, 0, stringw, -1, string, MAX_PATH, NULL, NULL);
        ok(len, "WideCharToMultiByteChar returned error %d\n", GetLastError());
        ok(0, "Property \"ProductName\" expected to be \"MSITEST\" but was \"%s\"\n", string);
    }

    /* Session::Property, put */
    hr = Session_PropertyPut(pSession, szProductName, szProductName);
1907
    ok(hr == S_OK, "Session_PropertyPut failed, hresult 0x%08x\n", hr);
1908 1909
    memset(stringw, 0, sizeof(stringw));
    hr = Session_PropertyGet(pSession, szProductName, stringw);
1910
    ok(hr == S_OK, "Session_PropertyGet failed, hresult 0x%08x\n", hr);
1911
    if (lstrcmpW(stringw, szProductName) != 0)
1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924
    {
        len = WideCharToMultiByte(CP_ACP, 0, stringw, -1, string, MAX_PATH, NULL, NULL);
        ok(len, "WideCharToMultiByteChar returned error %d\n", GetLastError());
        ok(0, "Property \"ProductName\" expected to be \"ProductName\" but was \"%s\"\n", string);
    }

    /* Try putting a property using empty property identifier */
    hr = Session_PropertyPut(pSession, szEmpty, szProductName);
    ok(hr == DISP_E_EXCEPTION, "Session_PropertyPut failed, hresult 0x%08x\n", hr);
    ok_exception(hr, szPropertyName);

    /* Try putting a property using illegal property identifier */
    hr = Session_PropertyPut(pSession, szEquals, szProductName);
1925
    ok(hr == S_OK, "Session_PropertyPut failed, hresult 0x%08x\n", hr);
1926 1927 1928

    /* Session::Language, get */
    hr = Session_LanguageGet(pSession, &len);
1929
    ok(hr == S_OK, "Session_LanguageGet failed, hresult 0x%08x\n", hr);
1930 1931 1932 1933
    /* Not sure how to check the language is correct */

    /* Session::Mode, get */
    hr = Session_ModeGet(pSession, MSIRUNMODE_REBOOTATEND, &bool);
1934
    ok(hr == S_OK, "Session_ModeGet failed, hresult 0x%08x\n", hr);
1935
    ok(!bool, "Reboot at end session mode is %d\n", bool);
1936

1937 1938
    hr = Session_ModeGet(pSession, MSIRUNMODE_MAINTENANCE, &bool);
    ok(hr == S_OK, "Session_ModeGet failed, hresult 0x%08x\n", hr);
1939
    ok(!bool, "Maintenance mode is %d\n", bool);
1940

1941
    /* Session::Mode, put */
1942
    hr = Session_ModePut(pSession, MSIRUNMODE_REBOOTATEND, VARIANT_TRUE);
1943
    ok(hr == S_OK, "Session_ModePut failed, hresult 0x%08x\n", hr);
1944
    hr = Session_ModeGet(pSession, MSIRUNMODE_REBOOTATEND, &bool);
1945
    ok(hr == S_OK, "Session_ModeGet failed, hresult 0x%08x\n", hr);
1946
    ok(bool, "Reboot at end session mode is %d, expected 1\n", bool);
1947
    hr = Session_ModePut(pSession, MSIRUNMODE_REBOOTATEND, VARIANT_FALSE);  /* set it again so we don't reboot */
1948
    ok(hr == S_OK, "Session_ModePut failed, hresult 0x%08x\n", hr);
1949

1950
    hr = Session_ModePut(pSession, MSIRUNMODE_REBOOTNOW, VARIANT_TRUE);
1951
    ok(hr == S_OK, "Session_ModePut failed, hresult 0x%08x\n", hr);
1952
    ok_exception(hr, szModeFlag);
1953

1954 1955 1956
    hr = Session_ModeGet(pSession, MSIRUNMODE_REBOOTNOW, &bool);
    ok(hr == S_OK, "Session_ModeGet failed, hresult 0x%08x\n", hr);
    ok(bool, "Reboot now mode is %d, expected 1\n", bool);
1957

1958
    hr = Session_ModePut(pSession, MSIRUNMODE_REBOOTNOW, VARIANT_FALSE);  /* set it again so we don't reboot */
1959
    ok(hr == S_OK, "Session_ModePut failed, hresult 0x%08x\n", hr);
1960
    ok_exception(hr, szModeFlag);
1961

1962
    hr = Session_ModePut(pSession, MSIRUNMODE_MAINTENANCE, VARIANT_TRUE);
1963
    ok(hr == DISP_E_EXCEPTION, "Session_ModePut failed, hresult 0x%08x\n", hr);
1964
    ok_exception(hr, szModeFlag);
1965

1966 1967
    /* Session::Database, get */
    hr = Session_Database(pSession, &pDatabase);
1968 1969
    ok(hr == S_OK, "Session_Database failed, hresult 0x%08x\n", hr);
    if (hr == S_OK)
1970
    {
1971
        test_Database(pDatabase, TRUE);
1972 1973 1974
        IDispatch_Release(pDatabase);
    }

1975
    /* Session::EvaluateCondition */
1976
    hr = Session_EvaluateCondition(pSession, NULL, &myint);
1977
    ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1978
    ok(myint == MSICONDITION_NONE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1979

1980
    hr = Session_EvaluateCondition(pSession, szEmpty, &myint);
1981
    ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1982
    ok(myint == MSICONDITION_NONE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1983

1984
    hr = Session_EvaluateCondition(pSession, szEquals, &myint);
1985
    ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1986
    ok(myint == MSICONDITION_ERROR, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1987

1988 1989
    /* Session::DoAction(CostInitialize) must occur before the next statements */
    hr = Session_DoAction(pSession, szCostInitialize, &myint);
1990
    ok(hr == S_OK, "Session_DoAction failed, hresult 0x%08x\n", hr);
1991 1992 1993 1994
    ok(myint == IDOK, "DoAction(CostInitialize) returned %d, %d expected\n", myint, IDOK);

    /* Session::SetInstallLevel */
    hr = Session_SetInstallLevel(pSession, INSTALLLEVEL_MINIMUM);
1995
    ok(hr == S_OK, "Session_SetInstallLevel failed, hresult 0x%08x\n", hr);
1996 1997 1998

    /* Session::FeatureCurrentState, get */
    hr = Session_FeatureCurrentState(pSession, szOne, &myint);
1999
    ok(hr == S_OK, "Session_FeatureCurrentState failed, hresult 0x%08x\n", hr);
2000 2001
    ok(myint == INSTALLSTATE_UNKNOWN, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);

2002 2003 2004 2005 2006 2007 2008
    /* Session::Message */
    hr = Installer_CreateRecord(0, &record);
    ok(hr == S_OK, "Installer_CreateRecord failed: %08x\n", hr);
    hr = Session_Message(pSession, INSTALLMESSAGE_INFO, record, &myint);
    ok(hr == S_OK, "Session_Message failed: %08x\n", hr);
    ok(myint == 0, "Session_Message returned %x\n", myint);

2009
    /* Session::EvaluateCondition */
2010
    hr = Session_EvaluateCondition(pSession, szOneStateFalse, &myint);
2011
    ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
2012
    ok(myint == MSICONDITION_FALSE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
2013 2014

    hr = Session_EvaluateCondition(pSession, szOneStateTrue, &myint);
2015
    ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
2016
    ok(myint == MSICONDITION_TRUE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
2017

2018 2019
    /* Session::FeatureRequestState, put */
    hr = Session_FeatureRequestStatePut(pSession, szOne, INSTALLSTATE_ADVERTISED);
2020
    ok(hr == S_OK, "Session_FeatureRequestStatePut failed, hresult 0x%08x\n", hr);
2021
    hr = Session_FeatureRequestStateGet(pSession, szOne, &myint);
2022
    ok(hr == S_OK, "Session_FeatureRequestStateGet failed, hresult 0x%08x\n", hr);
2023
    ok(myint == INSTALLSTATE_ADVERTISED, "Feature request state was %d but expected %d\n", myint, INSTALLSTATE_ADVERTISED);
2024 2025

    /* Session::EvaluateCondition */
2026
    hr = Session_EvaluateCondition(pSession, szOneActionFalse, &myint);
2027
    ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
2028
    ok(myint == MSICONDITION_FALSE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
2029 2030

    hr = Session_EvaluateCondition(pSession, szOneActionTrue, &myint);
2031
    ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
2032
    ok(myint == MSICONDITION_TRUE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
2033 2034
}

2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062
/* delete key and all its subkeys */
static DWORD delete_key( HKEY hkey )
{
    char name[MAX_PATH];
    DWORD ret;

    while (!(ret = RegEnumKeyA(hkey, 0, name, sizeof(name))))
    {
        HKEY tmp;
        if (!(ret = RegOpenKeyExA( hkey, name, 0, KEY_ENUMERATE_SUB_KEYS, &tmp )))
        {
            ret = delete_key( tmp );
            RegCloseKey( tmp );
        }
        if (ret) break;
    }
    if (ret != ERROR_NO_MORE_ITEMS) return ret;
    RegDeleteKeyA( hkey, "" );
    return 0;
}

static void test_Installer_RegistryValue(void)
{
    static const DWORD qw[2] = { 0x12345678, 0x87654321 };
    static const WCHAR szKey[] = { 'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\','T','e','s','t',0 };
    static const WCHAR szOne[] = { 'O','n','e',0 };
    static const WCHAR szTwo[] = { 'T','w','o',0 };
    static const WCHAR szThree[] = { 'T','h','r','e','e',0 };
2063
    static const WCHAR szREG_BINARY[] = { '(','R','E','G','_','B','I','N','A','R','Y',')',0 };
2064
    static const WCHAR szFour[] = { 'F','o','u','r',0 };
2065 2066 2067 2068 2069
    static const WCHAR szExpand[] = { '%','M','S','I','T','E','S','T','%',0 };
    static const WCHAR szFive[] = { 'F','i','v','e',0,'H','i',0,0 };
    static const WCHAR szFiveHi[] = { 'F','i','v','e','\n','H','i',0 };
    static const WCHAR szSix[] = { 'S','i','x',0 };
    static const WCHAR szREG_[] = { '(','R','E','G','_',']',0 };
2070
    static const WCHAR szREG_2[] = { '(','R','E','G','_','?','?',')',0 };
2071 2072
    static const WCHAR szSeven[] = { 'S','e','v','e','n',0 };
    static const WCHAR szEight[] = { 'E','i','g','h','t',0 };
2073
    static const WCHAR szBlank[] = { 0 };
2074 2075
    VARIANT varresult;
    VARIANTARG vararg;
2076 2077
    WCHAR szString[MAX_PATH];
    HKEY hkey, hkey_sub;
2078
    HKEY curr_user = (HKEY)1;
2079 2080
    HRESULT hr;
    BOOL bRet;
2081
    LONG lRet;
2082 2083

    /* Delete keys */
2084 2085 2086 2087 2088 2089 2090 2091 2092
    SetLastError(0xdeadbeef);
    lRet = RegOpenKeyW( HKEY_CURRENT_USER, szKey, &hkey );
    if (!lRet && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
    {
        win_skip("Needed W-functions are not implemented\n");
        return;
    }
    if (!lRet)
        delete_key( hkey );
2093

2094
    /* Does our key exist? Shouldn't; check with all three possible value parameter types */
2095
    hr = Installer_RegistryValueE(curr_user, szKey, &bRet);
2096
    ok(hr == S_OK, "Installer_RegistryValueE failed, hresult 0x%08x\n", hr);
2097
    ok(!bRet, "Registry key expected to not exist, but Installer_RegistryValue claims it does\n");
2098 2099

    memset(szString, 0, sizeof(szString));
2100
    hr = Installer_RegistryValueW(curr_user, szKey, NULL, szString);
2101 2102 2103
    ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);

    memset(szString, 0, sizeof(szString));
2104
    hr = Installer_RegistryValueI(curr_user, szKey, 0, szString, VT_BSTR);
2105
    ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2106 2107 2108 2109 2110 2111 2112 2113 2114 2115

    /* Create key */
    ok(!RegCreateKeyW( HKEY_CURRENT_USER, szKey, &hkey ), "RegCreateKeyW failed\n");

    ok(!RegSetValueExW(hkey,szOne,0,REG_SZ, (const BYTE *)szOne, sizeof(szOne)),
        "RegSetValueExW failed\n");
    ok(!RegSetValueExW(hkey,szTwo,0,REG_DWORD, (const BYTE *)qw, 4),
        "RegSetValueExW failed\n");
    ok(!RegSetValueExW(hkey,szThree,0,REG_BINARY, (const BYTE *)qw, 4),
        "RegSetValueExW failed\n");
2116 2117
    bRet = SetEnvironmentVariableA("MSITEST", "Four");
    ok(bRet, "SetEnvironmentVariableA failed %d\n", GetLastError());
2118 2119 2120 2121 2122 2123
    ok(!RegSetValueExW(hkey,szFour,0,REG_EXPAND_SZ, (const BYTE *)szExpand, sizeof(szExpand)),
        "RegSetValueExW failed\n");
    ok(!RegSetValueExW(hkey,szFive,0,REG_MULTI_SZ, (const BYTE *)szFive, sizeof(szFive)),
        "RegSetValueExW failed\n");
    ok(!RegSetValueExW(hkey,szSix,0,REG_QWORD, (const BYTE *)qw, 8),
        "RegSetValueExW failed\n");
2124
    ok(!RegSetValueExW(hkey,szSeven,0,REG_NONE, NULL, 0),
2125 2126
        "RegSetValueExW failed\n");

2127 2128 2129
    ok(!RegSetValueExW(hkey,NULL,0,REG_SZ, (const BYTE *)szOne, sizeof(szOne)),
        "RegSetValueExW failed\n");

2130
    ok(!RegCreateKeyW( hkey, szEight, &hkey_sub ), "RegCreateKeyW failed\n");
2131

2132 2133
    /* Does our key exist? It should, and make sure we retrieve the correct default value */
    bRet = FALSE;
2134
    hr = Installer_RegistryValueE(curr_user, szKey, &bRet);
2135
    ok(hr == S_OK, "Installer_RegistryValueE failed, hresult 0x%08x\n", hr);
2136
    ok(bRet, "Registry key expected to exist, but Installer_RegistryValue claims it does not\n");
2137 2138

    memset(szString, 0, sizeof(szString));
2139
    hr = Installer_RegistryValueW(curr_user, szKey, NULL, szString);
2140
    ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2141 2142
    ok_w2("Default registry value \"%s\" does not match expected \"%s\"\n", szString, szOne);

2143
    /* Ask for the value of a nonexistent key */
2144
    memset(szString, 0, sizeof(szString));
2145
    hr = Installer_RegistryValueW(curr_user, szKey, szExpand, szString);
2146 2147 2148 2149
    ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);

    /* Get values of keys */
    memset(szString, 0, sizeof(szString));
2150
    hr = Installer_RegistryValueW(curr_user, szKey, szOne, szString);
2151
    ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2152 2153 2154 2155 2156
    ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szOne);

    VariantInit(&vararg);
    V_VT(&vararg) = VT_BSTR;
    V_BSTR(&vararg) = SysAllocString(szTwo);
2157
    hr = Installer_RegistryValue(curr_user, szKey, vararg, &varresult, VT_I4);
2158
    ok(hr == S_OK, "Installer_RegistryValue failed, hresult 0x%08x\n", hr);
2159 2160 2161 2162
    ok(V_I4(&varresult) == 305419896, "Registry value %d does not match expected value\n", V_I4(&varresult));
    VariantClear(&varresult);

    memset(szString, 0, sizeof(szString));
2163
    hr = Installer_RegistryValueW(curr_user, szKey, szThree, szString);
2164
    ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2165 2166 2167
    ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szREG_BINARY);

    memset(szString, 0, sizeof(szString));
2168
    hr = Installer_RegistryValueW(curr_user, szKey, szFour, szString);
2169
    ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2170 2171
    ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szFour);

2172
    /* Vista does not NULL-terminate this case */
2173
    memset(szString, 0, sizeof(szString));
2174
    hr = Installer_RegistryValueW(curr_user, szKey, szFive, szString);
2175
    ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2176 2177
    ok_w2n("Registry value \"%s\" does not match expected \"%s\"\n",
           szString, szFiveHi, lstrlenW(szFiveHi));
2178 2179

    memset(szString, 0, sizeof(szString));
2180
    hr = Installer_RegistryValueW(curr_user, szKey, szSix, szString);
2181
    ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2182 2183
    ok(!lstrcmpW(szString, szREG_2) || broken(!lstrcmpW(szString, szREG_)),
       "Registry value does not match\n");
2184 2185 2186 2187

    VariantInit(&vararg);
    V_VT(&vararg) = VT_BSTR;
    V_BSTR(&vararg) = SysAllocString(szSeven);
2188
    hr = Installer_RegistryValue(curr_user, szKey, vararg, &varresult, VT_EMPTY);
2189
    ok(hr == S_OK, "Installer_RegistryValue failed, hresult 0x%08x\n", hr);
2190 2191 2192

    /* Get string class name for the key */
    memset(szString, 0, sizeof(szString));
2193
    hr = Installer_RegistryValueI(curr_user, szKey, 0, szString, VT_BSTR);
2194
    ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2195 2196 2197 2198
    ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szBlank);

    /* Get name of a value by positive number (RegEnumValue like), valid index */
    memset(szString, 0, sizeof(szString));
2199
    hr = Installer_RegistryValueI(curr_user, szKey, 2, szString, VT_BSTR);
2200
    ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2201 2202 2203 2204 2205
    /* RegEnumValue order seems different on wine */
    todo_wine ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szTwo);

    /* Get name of a value by positive number (RegEnumValue like), invalid index */
    memset(szString, 0, sizeof(szString));
2206
    hr = Installer_RegistryValueI(curr_user, szKey, 10, szString, VT_EMPTY);
2207
    ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2208 2209 2210

    /* Get name of a subkey by negative number (RegEnumValue like), valid index */
    memset(szString, 0, sizeof(szString));
2211
    hr = Installer_RegistryValueI(curr_user, szKey, -1, szString, VT_BSTR);
2212
    ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2213 2214 2215 2216
    ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szEight);

    /* Get name of a subkey by negative number (RegEnumValue like), invalid index */
    memset(szString, 0, sizeof(szString));
2217
    hr = Installer_RegistryValueI(curr_user, szKey, -10, szString, VT_EMPTY);
2218
    ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2219 2220 2221 2222 2223

    /* clean up */
    delete_key(hkey);
}

2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272
static void test_Installer_Products(BOOL bProductInstalled)
{
    WCHAR szString[MAX_PATH];
    HRESULT hr;
    int idx;
    IUnknown *pUnk = NULL;
    IEnumVARIANT *pEnum = NULL;
    VARIANT var;
    ULONG celt;
    int iCount, iValue;
    IDispatch *pStringList = NULL;
    BOOL bProductFound = FALSE;

    /* Installer::Products */
    hr = Installer_Products(&pStringList);
    ok(hr == S_OK, "Installer_Products failed, hresult 0x%08x\n", hr);
    if (hr == S_OK)
    {
        /* StringList::_NewEnum */
        hr = StringList__NewEnum(pStringList, &pUnk);
        ok(hr == S_OK, "StringList_NewEnum failed, hresult 0x%08x\n", hr);
        if (hr == S_OK)
        {
            hr = IUnknown_QueryInterface(pUnk, &IID_IEnumVARIANT, (void **)&pEnum);
            ok (hr == S_OK, "IUnknown::QueryInterface returned 0x%08x\n", hr);
        }
        if (!pEnum)
            skip("IEnumVARIANT tests\n");

        /* StringList::Count */
        hr = StringList_Count(pStringList, &iCount);
        ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr);

        for (idx=0; idx<iCount; idx++)
        {
            /* StringList::Item */
            memset(szString, 0, sizeof(szString));
            hr = StringList_Item(pStringList, idx, szString);
            ok(hr == S_OK, "StringList_Item failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);

            if (hr == S_OK)
            {
                /* Installer::ProductState */
                hr = Installer_ProductState(szString, &iValue);
                ok(hr == S_OK, "Installer_ProductState failed, hresult 0x%08x\n", hr);
                if (hr == S_OK)
                    ok(iValue == INSTALLSTATE_DEFAULT || iValue == INSTALLSTATE_ADVERTISED, "Installer_ProductState returned %d, expected %d or %d\n", iValue, INSTALLSTATE_DEFAULT, INSTALLSTATE_ADVERTISED);

                /* Not found our product code yet? Check */
2273
                if (!bProductFound && !lstrcmpW(szString, szProductCode))
2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288
                    bProductFound = TRUE;

                /* IEnumVARIANT::Next */
                if (pEnum)
                {
                    hr = IEnumVARIANT_Next(pEnum, 1, &var, &celt);
                    ok(hr == S_OK, "IEnumVARIANT_Next failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);
                    ok(celt == 1, "%d items were retrieved, expected 1\n", celt);
                    ok(V_VT(&var) == VT_BSTR, "IEnumVARIANT_Next returned variant of type %d, expected %d\n", V_VT(&var), VT_BSTR);
                    ok_w2("%s returned by StringList_Item does not match %s returned by IEnumVARIANT_Next\n", szString, V_BSTR(&var));
                    VariantClear(&var);
                }
            }
        }

2289
        if (bProductInstalled)
2290 2291 2292 2293 2294
        {
            ok(bProductInstalled == bProductFound, "Product expected to %s installed but product code was %s\n",
               bProductInstalled ? "be" : "not be",
               bProductFound ? "found" : "not found");
        }
2295 2296 2297 2298 2299 2300 2301 2302

        if (pEnum)
        {
            IEnumVARIANT *pEnum2 = NULL;

            if (0) /* Crashes on Windows XP */
            {
                /* IEnumVARIANT::Clone, NULL pointer */
2303
                IEnumVARIANT_Clone(pEnum, NULL);
2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375
            }

            /* IEnumVARIANT::Clone */
            hr = IEnumVARIANT_Clone(pEnum, &pEnum2);
            ok(hr == S_OK, "IEnumVARIANT_Clone failed, hresult 0x%08x\n", hr);
            if (hr == S_OK)
            {
                /* IEnumVARIANT::Clone is supposed to save the position, but it actually just goes back to the beginning */

                /* IEnumVARIANT::Next of the clone */
                if (iCount)
                {
                    hr = IEnumVARIANT_Next(pEnum2, 1, &var, &celt);
                    ok(hr == S_OK, "IEnumVARIANT_Next failed, hresult 0x%08x\n", hr);
                    ok(celt == 1, "%d items were retrieved, expected 0\n", celt);
                    ok(V_VT(&var) == VT_BSTR, "IEnumVARIANT_Next returned variant of type %d, expected %d\n", V_VT(&var), VT_BSTR);
                    VariantClear(&var);
                }
                else
                    skip("IEnumVARIANT::Next of clone will not return success with 0 products\n");

                IEnumVARIANT_Release(pEnum2);
            }

            /* IEnumVARIANT::Skip should fail */
            hr = IEnumVARIANT_Skip(pEnum, 1);
            ok(hr == S_FALSE, "IEnumVARIANT_Skip failed, hresult 0x%08x\n", hr);

            /* IEnumVARIANT::Next, NULL variant pointer */
            hr = IEnumVARIANT_Next(pEnum, 1, NULL, &celt);
            ok(hr == S_FALSE, "IEnumVARIANT_Next failed, hresult 0x%08x\n", hr);
            ok(celt == 0, "%d items were retrieved, expected 0\n", celt);

            /* IEnumVARIANT::Next, should not return any more items */
            hr = IEnumVARIANT_Next(pEnum, 1, &var, &celt);
            ok(hr == S_FALSE, "IEnumVARIANT_Next failed, hresult 0x%08x\n", hr);
            ok(celt == 0, "%d items were retrieved, expected 0\n", celt);
            VariantClear(&var);

            /* IEnumVARIANT::Reset */
            hr = IEnumVARIANT_Reset(pEnum);
            ok(hr == S_OK, "IEnumVARIANT_Reset failed, hresult 0x%08x\n", hr);

            if (iCount)
            {
                /* IEnumVARIANT::Skip to the last product */
                hr = IEnumVARIANT_Skip(pEnum, iCount-1);
                ok(hr == S_OK, "IEnumVARIANT_Skip failed, hresult 0x%08x\n", hr);

                /* IEnumVARIANT::Next should match the very last retrieved value, also makes sure it works with
                 * NULL celt pointer. */
                hr = IEnumVARIANT_Next(pEnum, 1, &var, NULL);
                ok(hr == S_OK, "IEnumVARIANT_Next failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);
                ok(V_VT(&var) == VT_BSTR, "IEnumVARIANT_Next returned variant of type %d, expected %d\n", V_VT(&var), VT_BSTR);
                ok_w2("%s returned by StringList_Item does not match %s returned by IEnumVARIANT_Next\n", szString, V_BSTR(&var));
                VariantClear(&var);
            }
            else
                skip("IEnumVARIANT::Skip impossible for 0 products\n");
        }

        /* StringList::Item using an invalid index */
        memset(szString, 0, sizeof(szString));
        hr = StringList_Item(pStringList, iCount, szString);
        ok(hr == DISP_E_BADINDEX, "StringList_Item for an invalid index did not return DISP_E_BADINDEX, hresult 0x%08x\n", hr);

        if (pEnum) IEnumVARIANT_Release(pEnum);
        if (pUnk) IUnknown_Release(pUnk);
        IDispatch_Release(pStringList);
    }
}

2376 2377
/* Delete a registry subkey, including all its subkeys (RegDeleteKey does not work on keys with subkeys without
 * deleting the subkeys first) */
2378
static UINT delete_registry_key(HKEY hkeyParent, LPCSTR subkey, REGSAM access)
2379 2380 2381 2382 2383 2384
{
    UINT ret;
    CHAR *string = NULL;
    HKEY hkey;
    DWORD dwSize;

2385
    ret = RegOpenKeyExA(hkeyParent, subkey, 0, access, &hkey);
2386 2387 2388 2389 2390 2391
    if (ret != ERROR_SUCCESS) return ret;
    ret = RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL);
    if (ret != ERROR_SUCCESS) return ret;
    if (!(string = HeapAlloc(GetProcessHeap(), 0, ++dwSize))) return ERROR_NOT_ENOUGH_MEMORY;

    while (RegEnumKeyA(hkey, 0, string, dwSize) == ERROR_SUCCESS)
2392
        delete_registry_key(hkey, string, access);
2393 2394 2395

    RegCloseKey(hkey);
    HeapFree(GetProcessHeap(), 0, string);
2396
    delete_key_portable(hkeyParent, subkey, access);
2397 2398 2399 2400
    return ERROR_SUCCESS;
}

/* Find a specific registry subkey at any depth within the given key and subkey and return its parent key. */
2401
static UINT find_registry_key(HKEY hkeyParent, LPCSTR subkey, LPCSTR findkey, REGSAM access, HKEY *phkey)
2402 2403 2404 2405 2406 2407 2408 2409 2410 2411
{
    UINT ret;
    CHAR *string = NULL;
    int idx = 0;
    HKEY hkey;
    DWORD dwSize;
    BOOL found = FALSE;

    *phkey = 0;

2412
    ret = RegOpenKeyExA(hkeyParent, subkey, 0, access, &hkey);
2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425
    if (ret != ERROR_SUCCESS) return ret;
    ret = RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL);
    if (ret != ERROR_SUCCESS) return ret;
    if (!(string = HeapAlloc(GetProcessHeap(), 0, ++dwSize))) return ERROR_NOT_ENOUGH_MEMORY;

    while (!found &&
           RegEnumKeyA(hkey, idx++, string, dwSize) == ERROR_SUCCESS)
    {
        if (!strcmp(string, findkey))
        {
            *phkey = hkey;
            found = TRUE;
        }
2426
        else if (find_registry_key(hkey, string, findkey, access, phkey) == ERROR_SUCCESS) found = TRUE;
2427 2428 2429 2430 2431 2432 2433
    }

    if (*phkey != hkey) RegCloseKey(hkey);
    HeapFree(GetProcessHeap(), 0, string);
    return (found ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND);
}

2434
static void test_Installer_InstallProduct(void)
2435 2436 2437
{
    HRESULT hr;
    CHAR path[MAX_PATH];
2438
    WCHAR szString[MAX_PATH];
2439 2440 2441
    LONG res;
    HKEY hkey;
    DWORD num, size, type;
2442 2443
    int iValue, iCount;
    IDispatch *pStringList = NULL;
2444 2445
    REGSAM access = KEY_ALL_ACCESS;

2446 2447 2448 2449 2450 2451 2452 2453 2454
    if (is_process_limited())
    {
        /* In fact InstallProduct would succeed but then Windows XP
         * would not allow us to clean up the registry!
         */
        skip("Installer_InstallProduct (insufficient privileges)\n");
        return;
    }

2455
    if (is_wow64)
2456
        access |= KEY_WOW64_64KEY;
2457 2458 2459

    create_test_files();

2460 2461
    /* Avoid an interactive dialog in case of insufficient privileges. */
    hr = Installer_UILevelPut(INSTALLUILEVEL_NONE);
2462
    ok(hr == S_OK, "Expected UILevel property put invoke to return S_OK, got 0x%08x\n", hr);
2463

2464
    /* Installer::InstallProduct */
2465
    hr = Installer_InstallProduct(szMsifile, NULL);
2466 2467
    if (hr == DISP_E_EXCEPTION)
    {
2468
        skip("InstallProduct failed, insufficient rights?\n");
2469 2470 2471
        delete_test_files();
        return;
    }
2472
    ok(hr == S_OK, "Installer_InstallProduct failed, hresult 0x%08x\n", hr);
2473 2474 2475

    /* Installer::ProductState for our product code, which has been installed */
    hr = Installer_ProductState(szProductCode, &iValue);
2476
    ok(hr == S_OK, "Installer_ProductState failed, hresult 0x%08x\n", hr);
2477
    ok(iValue == INSTALLSTATE_DEFAULT, "Installer_ProductState returned %d, expected %d\n", iValue, INSTALLSTATE_DEFAULT);
2478

2479 2480
    /* Installer::ProductInfo for our product code */

2481 2482 2483 2484 2485
    /* NULL attribute */
    memset(szString, 0, sizeof(szString));
    hr = Installer_ProductInfo(szProductCode, NULL, szString);
    ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
    ok_exception(hr, szProductInfoException);
2486

2487
    /* Nonexistent attribute */
2488 2489 2490 2491
    memset(szString, 0, sizeof(szString));
    hr = Installer_ProductInfo(szProductCode, szMsifile, szString);
    ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
    ok_exception(hr, szProductInfoException);
2492

2493 2494
    /* Package name */
    memset(szString, 0, sizeof(szString));
2495
    hr = Installer_ProductInfo(szProductCode, WINE_INSTALLPROPERTY_PACKAGENAMEW, szString);
2496
    ok(hr == S_OK, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2497
    todo_wine ok_w2("Installer_ProductInfo returned %s but expected %s\n", szString, szMsifile);
2498 2499 2500

    /* Product name */
    memset(szString, 0, sizeof(szString));
2501
    hr = Installer_ProductInfo(szProductCode, WINE_INSTALLPROPERTY_PRODUCTNAMEW, szString);
2502
    ok(hr == S_OK, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2503
    todo_wine ok_w2("Installer_ProductInfo returned %s but expected %s\n", szString, szMSITEST);
2504

2505 2506 2507
    /* Installer::Products */
    test_Installer_Products(TRUE);

2508
    /* Installer::RelatedProducts for our upgrade code */
2509
    hr = Installer_RelatedProducts(szUpgradeCode, &pStringList);
2510 2511
    ok(hr == S_OK, "Installer_RelatedProducts failed, hresult 0x%08x\n", hr);
    if (hr == S_OK)
2512 2513 2514
    {
        /* StringList::Count */
        hr = StringList_Count(pStringList, &iCount);
2515
        ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr);
2516
        ok(iCount == 1, "Expected one related product but found %d\n", iCount);
2517

2518 2519 2520
        /* StringList::Item */
        memset(szString, 0, sizeof(szString));
        hr = StringList_Item(pStringList, 0, szString);
2521
        ok(hr == S_OK, "StringList_Item failed (idx 0, count %d), hresult 0x%08x\n", iCount, hr);
2522
        ok_w2("StringList_Item returned %s but expected %s\n", szString, szProductCode);
2523

2524
        IDispatch_Release(pStringList);
2525 2526
    }

2527 2528 2529 2530
    hr = Installer_ProductInfo(szProductCode, WINE_INSTALLPROPERTY_LOCALPACKAGEW, szString);
    ok(hr == S_OK, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
    DeleteFileW( szString );

2531
    /* Check & clean up installed files & registry keys */
2532
    ok(delete_pf("msitest\\cabout\\new\\five.txt", TRUE), "File not installed\n");
2533
    ok(delete_pf("msitest\\cabout\\new", FALSE), "Directory not created\n");
2534
    ok(delete_pf("msitest\\cabout\\four.txt", TRUE), "File not installed\n");
2535
    ok(delete_pf("msitest\\cabout", FALSE), "Directory not created\n");
2536
    ok(delete_pf("msitest\\changed\\three.txt", TRUE), "File not installed\n");
2537
    ok(delete_pf("msitest\\changed", FALSE), "Directory not created\n");
2538
    ok(delete_pf("msitest\\first\\two.txt", TRUE), "File not installed\n");
2539
    ok(delete_pf("msitest\\first", FALSE), "Directory not created\n");
2540 2541
    ok(delete_pf("msitest\\one.txt", TRUE), "File not installed\n");
    ok(delete_pf("msitest\\filename", TRUE), "File not installed\n");
2542
    ok(delete_pf("msitest", FALSE), "Directory not created\n");
2543

2544
    res = RegOpenKeyA(HKEY_CURRENT_USER, "SOFTWARE\\Wine\\msitest", &hkey);
2545
    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2546

2547 2548 2549 2550 2551
    size = MAX_PATH;
    type = REG_SZ;
    res = RegQueryValueExA(hkey, "Name", NULL, &type, (LPBYTE)path, &size);
    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
    ok(!lstrcmpA(path, "imaname"), "Expected imaname, got %s\n", path);
2552

2553 2554 2555 2556
    size = MAX_PATH;
    type = REG_SZ;
    res = RegQueryValueExA(hkey, "blah", NULL, &type, (LPBYTE)path, &size);
    ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
2557

2558 2559 2560 2561 2562
    size = sizeof(num);
    type = REG_DWORD;
    res = RegQueryValueExA(hkey, "number", NULL, &type, (LPBYTE)&num, &size);
    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
    ok(num == 314, "Expected 314, got %d\n", num);
2563

2564 2565 2566 2567 2568
    size = MAX_PATH;
    type = REG_SZ;
    res = RegQueryValueExA(hkey, "OrderTestName", NULL, &type, (LPBYTE)path, &size);
    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
    ok(!lstrcmpA(path, "OrderTestValue"), "Expected imaname, got %s\n", path);
2569

2570
    RegCloseKey(hkey);
2571

2572
    res = RegDeleteKeyA(HKEY_CURRENT_USER, "SOFTWARE\\Wine\\msitest");
2573
    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2574 2575

    /* Remove registry keys written by RegisterProduct standard action */
2576
    res = delete_key_portable(HKEY_LOCAL_MACHINE,
2577
        "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{837450fa-a39b-4bc8-b321-08b393f784b3}",
2578
                              KEY_WOW64_32KEY);
2579
    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2580

2581 2582
    res = delete_key_portable(HKEY_LOCAL_MACHINE,
        "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UpgradeCodes\\D8E760ECA1E276347B43E42BDBDA5656", access);
2583
    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2584

2585
    res = find_registry_key(HKEY_LOCAL_MACHINE,
2586
        "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData", "af054738b93a8cb43b12803b397f483b", access, &hkey);
2587
    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2588

2589 2590 2591 2592 2593 2594 2595
    res = delete_registry_key(hkey, "af054738b93a8cb43b12803b397f483b", access);
    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
    RegCloseKey(hkey);

    res = delete_key_portable(HKEY_LOCAL_MACHINE,
        "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\Products\\af054738b93a8cb43b12803b397f483b", access);
    ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
2596 2597

    /* Remove registry keys written by PublishProduct standard action */
2598
    res = RegOpenKeyA(HKEY_CURRENT_USER, "SOFTWARE\\Microsoft\\Installer", &hkey);
2599 2600
    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);

2601
    res = delete_registry_key(hkey, "Products\\af054738b93a8cb43b12803b397f483b", KEY_ALL_ACCESS);
2602
    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2603

2604
    res = RegDeleteKeyA(hkey, "UpgradeCodes\\D8E760ECA1E276347B43E42BDBDA5656");
2605
    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2606 2607 2608

    RegCloseKey(hkey);

2609
    /* Delete installation files we created */
2610 2611 2612
    delete_test_files();
}

2613 2614
static void test_Installer(void)
{
2615 2616
    static const WCHAR szCreateRecordException[] = { 'C','r','e','a','t','e','R','e','c','o','r','d',',','C','o','u','n','t',0 };
    static const WCHAR szIntegerDataException[] = { 'I','n','t','e','g','e','r','D','a','t','a',',','F','i','e','l','d',0 };
2617 2618
    WCHAR szPath[MAX_PATH];
    HRESULT hr;
2619
    IDispatch *pSession = NULL, *pDatabase = NULL, *pRecord = NULL, *pStringList = NULL, *pSumInfo = NULL;
2620
    int iValue, iCount;
2621 2622 2623

    if (!pInstaller) return;

2624
    /* Installer::CreateRecord */
2625 2626 2627 2628 2629 2630 2631 2632

    /* Test for error */
    hr = Installer_CreateRecord(-1, &pRecord);
    ok(hr == DISP_E_EXCEPTION, "Installer_CreateRecord failed, hresult 0x%08x\n", hr);
    ok_exception(hr, szCreateRecordException);

    /* Test for success */
    hr = Installer_CreateRecord(1, &pRecord);
2633
    ok(hr == S_OK, "Installer_CreateRecord failed, hresult 0x%08x\n", hr);
2634
    ok(pRecord != NULL, "Installer_CreateRecord should not have returned NULL record\n");
2635 2636 2637
    if (pRecord)
    {
        /* Record::FieldCountGet */
2638
        hr = Record_FieldCountGet(pRecord, &iValue);
2639
        ok(hr == S_OK, "Record_FiledCountGet failed, hresult 0x%08x\n", hr);
2640 2641
        ok(iValue == 1, "Record_FieldCountGet result was %d but expected 1\n", iValue);

2642 2643
        /* Record::IntegerDataGet */
        hr = Record_IntegerDataGet(pRecord, 1, &iValue);
2644
        ok(hr == S_OK, "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
2645 2646 2647 2648
        ok(iValue == MSI_NULL_INTEGER, "Record_IntegerDataGet result was %d but expected %d\n", iValue, MSI_NULL_INTEGER);

        /* Record::IntegerDataGet, bad index */
        hr = Record_IntegerDataGet(pRecord, 10, &iValue);
2649
        ok(hr == S_OK, "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
2650 2651 2652 2653
        ok(iValue == MSI_NULL_INTEGER, "Record_IntegerDataGet result was %d but expected %d\n", iValue, MSI_NULL_INTEGER);

        /* Record::IntegerDataPut */
        hr = Record_IntegerDataPut(pRecord, 1, 100);
2654
        ok(hr == S_OK, "Record_IntegerDataPut failed, hresult 0x%08x\n", hr);
2655 2656 2657 2658 2659 2660 2661 2662

        /* Record::IntegerDataPut, bad index */
        hr = Record_IntegerDataPut(pRecord, 10, 100);
        ok(hr == DISP_E_EXCEPTION, "Record_IntegerDataPut failed, hresult 0x%08x\n", hr);
        ok_exception(hr, szIntegerDataException);

        /* Record::IntegerDataGet */
        hr = Record_IntegerDataGet(pRecord, 1, &iValue);
2663
        ok(hr == S_OK, "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
2664
        ok(iValue == 100, "Record_IntegerDataGet result was %d but expected 100\n", iValue);
2665 2666 2667 2668

        IDispatch_Release(pRecord);
    }

2669
    create_package(szPath);
2670 2671 2672

    /* Installer::OpenPackage */
    hr = Installer_OpenPackage(szPath, 0, &pSession);
2673 2674 2675 2676 2677 2678
    if (hr == DISP_E_EXCEPTION)
    {
        skip("OpenPackage failed, insufficient rights?\n");
        DeleteFileW(szPath);
        return;
    }
2679 2680
    ok(hr == S_OK, "Installer_OpenPackage failed, hresult 0x%08x\n", hr);
    if (hr == S_OK)
2681 2682 2683 2684 2685
    {
        test_Session(pSession);
        IDispatch_Release(pSession);
    }

2686
    /* Installer::OpenDatabase */
2687
    hr = Installer_OpenDatabase(szPath, (INT_PTR)MSIDBOPEN_TRANSACT, &pDatabase);
2688 2689 2690 2691 2692 2693 2694
    ok(hr == S_OK, "Installer_OpenDatabase failed, hresult 0x%08x\n", hr);
    if (hr == S_OK)
    {
        test_Database(pDatabase, FALSE);
        IDispatch_Release(pDatabase);
    }

2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706
    /* Installer::SummaryInformation */
    hr = Installer_SummaryInformation(szPath, 0, &pSumInfo);
    ok(hr == S_OK, "Installer_SummaryInformation failed, hresult 0x%08x\n", hr);
    if (hr == S_OK)
    {
        test_SummaryInfo(pSumInfo, summary_info, sizeof(summary_info)/sizeof(msi_summary_info), TRUE);
        IDispatch_Release(pSumInfo);
    }

    hr = Installer_SummaryInformation(NULL, 0, &pSumInfo);
    ok(hr == DISP_E_EXCEPTION, "Installer_SummaryInformation failed, hresult 0x%08x\n", hr);

2707 2708 2709
    /* Installer::RegistryValue */
    test_Installer_RegistryValue();

2710
    /* Installer::ProductState for our product code, which should not be installed */
2711
    hr = Installer_ProductState(szProductCode, &iValue);
2712
    ok(hr == S_OK, "Installer_ProductState failed, hresult 0x%08x\n", hr);
2713
    ok(iValue == INSTALLSTATE_UNKNOWN, "Installer_ProductState returned %d, expected %d\n", iValue, INSTALLSTATE_UNKNOWN);
2714

2715 2716
    /* Installer::ProductInfo for our product code, which should not be installed */

2717 2718
    /* Package name */
    memset(szPath, 0, sizeof(szPath));
2719
    hr = Installer_ProductInfo(szProductCode, WINE_INSTALLPROPERTY_PACKAGENAMEW, szPath);
2720 2721 2722 2723 2724 2725 2726 2727
    ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
    ok_exception(hr, szProductInfoException);

    /* NULL attribute and NULL product code */
    memset(szPath, 0, sizeof(szPath));
    hr = Installer_ProductInfo(NULL, NULL, szPath);
    ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
    ok_exception(hr, szProductInfoException);
2728

2729 2730 2731
    /* Installer::Products */
    test_Installer_Products(FALSE);

2732
    /* Installer::RelatedProducts for our upgrade code, should not find anything */
2733
    hr = Installer_RelatedProducts(szUpgradeCode, &pStringList);
2734 2735
    ok(hr == S_OK, "Installer_RelatedProducts failed, hresult 0x%08x\n", hr);
    if (hr == S_OK)
2736 2737 2738
    {
        /* StringList::Count */
        hr = StringList_Count(pStringList, &iCount);
2739
        ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr);
2740
        ok(!iCount, "Expected no related products but found %d\n", iCount);
2741

2742
        IDispatch_Release(pStringList);
2743 2744
    }

2745
    /* Installer::Version */
2746 2747 2748
    memset(szPath, 0, sizeof(szPath));
    hr = Installer_VersionGet(szPath);
    ok(hr == S_OK, "Installer_VersionGet failed, hresult 0x%08x\n", hr);
2749 2750

    /* Installer::InstallProduct and other tests that depend on our product being installed */
2751
    test_Installer_InstallProduct();
2752 2753 2754 2755 2756 2757 2758 2759 2760 2761
}

START_TEST(automation)
{
    DWORD len;
    char temp_path[MAX_PATH], prev_path[MAX_PATH];
    HRESULT hr;
    CLSID clsid;
    IUnknown *pUnk;

2762
    init_functionpointers();
2763 2764 2765 2766

    if (pIsWow64Process)
        pIsWow64Process(GetCurrentProcess(), &is_wow64);

2767 2768
    GetSystemTimeAsFileTime(&systemtime);

2769
    GetCurrentDirectoryA(MAX_PATH, prev_path);
2770
    GetTempPathA(MAX_PATH, temp_path);
2771 2772 2773 2774 2775 2776 2777 2778
    SetCurrentDirectoryA(temp_path);

    lstrcpyA(CURR_DIR, temp_path);
    len = lstrlenA(CURR_DIR);

    if(len && (CURR_DIR[len - 1] == '\\'))
        CURR_DIR[len - 1] = 0;

2779 2780
    get_program_files_dir(PROG_FILES_DIR);

2781
    hr = OleInitialize(NULL);
2782
    ok (hr == S_OK, "OleInitialize returned 0x%08x\n", hr);
2783
    hr = CLSIDFromProgID(szProgId, &clsid);
2784
    ok (hr == S_OK, "CLSIDFromProgID returned 0x%08x\n", hr);
2785
    hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&pUnk);
2786
    ok(hr == S_OK, "CoCreateInstance returned 0x%08x\n", hr);
2787 2788 2789 2790

    if (pUnk)
    {
        hr = IUnknown_QueryInterface(pUnk, &IID_IDispatch, (void **)&pInstaller);
2791
        ok (hr == S_OK, "IUnknown::QueryInterface returned 0x%08x\n", hr);
2792

2793
        test_dispid();
2794 2795
        test_dispatch();
        test_Installer();
2796

2797 2798
        IDispatch_Release(pInstaller);
        IUnknown_Release(pUnk);
2799 2800 2801 2802 2803 2804
    }

    OleUninitialize();

    SetCurrentDirectoryA(prev_path);
}