Release 1.5.29.
[wine] / dlls / msi / tests / automation.c
1 /*
2  * Copyright (C) 2007 Mike McCormack for CodeWeavers
3  * Copyright (C) 2007 Misha Koshelev
4  *
5  * A test program for Microsoft Installer OLE automation functionality.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #define COBJMACROS
23
24 #include <stdio.h>
25
26 #include <initguid.h>
27 #include <windows.h>
28 #include <msiquery.h>
29 #include <msidefs.h>
30 #include <msi.h>
31 #include <fci.h>
32
33 #include "wine/test.h"
34
35 static BOOL is_wow64;
36
37 static LONG (WINAPI *pRegDeleteKeyExA)(HKEY, LPCSTR, REGSAM, DWORD);
38 static BOOL (WINAPI *pIsWow64Process)(HANDLE, PBOOL);
39
40 DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
41
42 static const char *msifile = "winetest-automation.msi";
43 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};
44 static const WCHAR szMSITEST[] = { 'M','S','I','T','E','S','T',0 };
45 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 };
46 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 };
47 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 };
48 static const WCHAR WINE_INSTALLPROPERTY_PACKAGENAMEW[] = {'P','a','c','k','a','g','e','N','a','m','e',0};
49 static const WCHAR WINE_INSTALLPROPERTY_PRODUCTNAMEW[] = {'P','r','o','d','u','c','t','N','a','m','e',0};
50 static const WCHAR WINE_INSTALLPROPERTY_LOCALPACKAGEW[] = {'L','o','c','a','l','P','a','c','k','a','g','e',0};
51 static FILETIME systemtime;
52 static CHAR CURR_DIR[MAX_PATH];
53 static EXCEPINFO excepinfo;
54
55 /*
56  * OLE automation data
57  **/
58 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 };
59 static IDispatch *pInstaller;
60
61 /* msi database data */
62
63 static const CHAR component_dat[] = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n"
64                                     "s72\tS38\ts72\ti2\tS255\tS72\n"
65                                     "Component\tComponent\n"
66                                     "Five\t{8CC92E9D-14B2-4CA4-B2AA-B11D02078087}\tNEWDIR\t2\t\tfive.txt\n"
67                                     "Four\t{FD37B4EA-7209-45C0-8917-535F35A2F080}\tCABOUTDIR\t2\t\tfour.txt\n"
68                                     "One\t{783B242E-E185-4A56-AF86-C09815EC053C}\tMSITESTDIR\t2\t\tone.txt\n"
69                                     "Three\t{010B6ADD-B27D-4EDD-9B3D-34C4F7D61684}\tCHANGEDDIR\t2\t\tthree.txt\n"
70                                     "Two\t{BF03D1A6-20DA-4A65-82F3-6CAC995915CE}\tFIRSTDIR\t2\t\ttwo.txt\n"
71                                     "dangler\t{6091DF25-EF96-45F1-B8E9-A9B1420C7A3C}\tTARGETDIR\t4\t\tregdata\n"
72                                     "component\t\tMSITESTDIR\t0\t1\tfile\n";
73
74 static const CHAR directory_dat[] = "Directory\tDirectory_Parent\tDefaultDir\n"
75                                     "s72\tS72\tl255\n"
76                                     "Directory\tDirectory\n"
77                                     "CABOUTDIR\tMSITESTDIR\tcabout\n"
78                                     "CHANGEDDIR\tMSITESTDIR\tchanged:second\n"
79                                     "FIRSTDIR\tMSITESTDIR\tfirst\n"
80                                     "MSITESTDIR\tProgramFilesFolder\tmsitest\n"
81                                     "NEWDIR\tCABOUTDIR\tnew\n"
82                                     "ProgramFilesFolder\tTARGETDIR\t.\n"
83                                     "TARGETDIR\t\tSourceDir\n";
84
85 static const CHAR feature_dat[] = "Feature\tFeature_Parent\tTitle\tDescription\tDisplay\tLevel\tDirectory_\tAttributes\n"
86                                   "s38\tS38\tL64\tL255\tI2\ti2\tS72\ti2\n"
87                                   "Feature\tFeature\n"
88                                   "Five\t\tFive\tThe Five Feature\t5\t3\tNEWDIR\t0\n"
89                                   "Four\t\tFour\tThe Four Feature\t4\t3\tCABOUTDIR\t0\n"
90                                   "One\t\tOne\tThe One Feature\t1\t3\tMSITESTDIR\t0\n"
91                                   "Three\tOne\tThree\tThe Three Feature\t3\t3\tCHANGEDDIR\t0\n"
92                                   "Two\tOne\tTwo\tThe Two Feature\t2\t3\tFIRSTDIR\t0\n"
93                                   "feature\t\t\t\t2\t1\tTARGETDIR\t0\n";
94
95 static const CHAR feature_comp_dat[] = "Feature_\tComponent_\n"
96                                        "s38\ts72\n"
97                                        "FeatureComponents\tFeature_\tComponent_\n"
98                                        "Five\tFive\n"
99                                        "Four\tFour\n"
100                                        "One\tOne\n"
101                                        "Three\tThree\n"
102                                        "Two\tTwo\n"
103                                        "feature\tcomponent\n";
104
105 static const CHAR file_dat[] = "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n"
106                                "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n"
107                                "File\tFile\n"
108                                "five.txt\tFive\tfive.txt\t1000\t\t\t0\t5\n"
109                                "four.txt\tFour\tfour.txt\t1000\t\t\t0\t4\n"
110                                "one.txt\tOne\tone.txt\t1000\t\t\t0\t1\n"
111                                "three.txt\tThree\tthree.txt\t1000\t\t\t0\t3\n"
112                                "two.txt\tTwo\ttwo.txt\t1000\t\t\t0\t2\n"
113                                "file\tcomponent\tfilename\t100\t\t\t8192\t1\n";
114
115 static const CHAR install_exec_seq_dat[] = "Action\tCondition\tSequence\n"
116                                            "s72\tS255\tI2\n"
117                                            "InstallExecuteSequence\tAction\n"
118                                            "AllocateRegistrySpace\tNOT Installed\t1550\n"
119                                            "CostFinalize\t\t1000\n"
120                                            "CostInitialize\t\t800\n"
121                                            "FileCost\t\t900\n"
122                                            "InstallFiles\t\t4000\n"
123                                            "RegisterProduct\t\t6100\n"
124                                            "PublishProduct\t\t6400\n"
125                                            "InstallFinalize\t\t6600\n"
126                                            "InstallInitialize\t\t1500\n"
127                                            "InstallValidate\t\t1400\n"
128                                            "LaunchConditions\t\t100\n"
129                                            "WriteRegistryValues\tSourceDir And SOURCEDIR\t5000\n";
130
131 static const CHAR media_dat[] = "DiskId\tLastSequence\tDiskPrompt\tCabinet\tVolumeLabel\tSource\n"
132                                 "i2\ti4\tL64\tS255\tS32\tS72\n"
133                                 "Media\tDiskId\n"
134                                 "1\t5\t\t\tDISK1\t\n";
135
136 static const CHAR property_dat[] = "Property\tValue\n"
137                                    "s72\tl0\n"
138                                    "Property\tProperty\n"
139                                    "DefaultUIFont\tDlgFont8\n"
140                                    "HASUIRUN\t0\n"
141                                    "INSTALLLEVEL\t3\n"
142                                    "InstallMode\tTypical\n"
143                                    "Manufacturer\tWine\n"
144                                    "PIDTemplate\t12345<###-%%%%%%%>@@@@@\n"
145                                    "ProductCode\t{837450fa-a39b-4bc8-b321-08b393f784b3}\n"
146                                    "ProductID\tnone\n"
147                                    "ProductLanguage\t1033\n"
148                                    "ProductName\tMSITEST\n"
149                                    "ProductVersion\t1.1.1\n"
150                                    "PROMPTROLLBACKCOST\tP\n"
151                                    "Setup\tSetup\n"
152                                    "UpgradeCode\t{CE067E8D-2E1A-4367-B734-4EB2BDAD6565}\n"
153                                    "MSIFASTINSTALL\t1\n";
154
155 static const CHAR registry_dat[] = "Registry\tRoot\tKey\tName\tValue\tComponent_\n"
156                                    "s72\ti2\tl255\tL255\tL0\ts72\n"
157                                    "Registry\tRegistry\n"
158                                    "Apples\t1\tSOFTWARE\\Wine\\msitest\tName\timaname\tOne\n"
159                                    "Oranges\t1\tSOFTWARE\\Wine\\msitest\tnumber\t#314\tTwo\n"
160                                    "regdata\t1\tSOFTWARE\\Wine\\msitest\tblah\tbad\tdangler\n"
161                                    "OrderTest\t1\tSOFTWARE\\Wine\\msitest\tOrderTestName\tOrderTestValue\tcomponent\n";
162
163 typedef struct _msi_table
164 {
165     const CHAR *filename;
166     const CHAR *data;
167     int size;
168 } msi_table;
169
170 #define ADD_TABLE(x) {#x".idt", x##_dat, sizeof(x##_dat)}
171
172 static const msi_table tables[] =
173 {
174     ADD_TABLE(component),
175     ADD_TABLE(directory),
176     ADD_TABLE(feature),
177     ADD_TABLE(feature_comp),
178     ADD_TABLE(file),
179     ADD_TABLE(install_exec_seq),
180     ADD_TABLE(media),
181     ADD_TABLE(property),
182     ADD_TABLE(registry)
183 };
184
185 typedef struct _msi_summary_info
186 {
187     UINT property;
188     UINT datatype;
189     INT iValue;
190     FILETIME *pftValue;
191     const CHAR *szValue;
192 } msi_summary_info;
193
194 #define ADD_INFO_I2(property, iValue) {property, VT_I2, iValue, NULL, NULL}
195 #define ADD_INFO_I4(property, iValue) {property, VT_I4, iValue, NULL, NULL}
196 #define ADD_INFO_LPSTR(property, szValue) {property, VT_LPSTR, 0, NULL, szValue}
197 #define ADD_INFO_FILETIME(property, pftValue) {property, VT_FILETIME, 0, pftValue, NULL}
198
199 static const msi_summary_info summary_info[] =
200 {
201     ADD_INFO_LPSTR(PID_TEMPLATE, ";1033"),
202     ADD_INFO_LPSTR(PID_REVNUMBER, "{004757CA-5092-49C2-AD20-28E1CE0DF5F2}"),
203     ADD_INFO_I4(PID_PAGECOUNT, 100),
204     ADD_INFO_I4(PID_WORDCOUNT, 0),
205     ADD_INFO_FILETIME(PID_CREATE_DTM, &systemtime),
206     ADD_INFO_FILETIME(PID_LASTPRINTED, &systemtime)
207 };
208
209 static void init_functionpointers(void)
210 {
211     HMODULE hadvapi32 = GetModuleHandleA("advapi32.dll");
212     HMODULE hkernel32 = GetModuleHandleA("kernel32.dll");
213
214 #define GET_PROC(dll, func) \
215     p ## func = (void *)GetProcAddress(dll, #func); \
216     if(!p ## func) \
217       trace("GetProcAddress(%s) failed\n", #func);
218
219     GET_PROC(hadvapi32, RegDeleteKeyExA)
220     GET_PROC(hkernel32, IsWow64Process)
221
222 #undef GET_PROC
223 }
224
225 static LONG delete_key_portable( HKEY key, LPCSTR subkey, REGSAM access )
226 {
227     if (pRegDeleteKeyExA)
228         return pRegDeleteKeyExA( key, subkey, access, 0 );
229     return RegDeleteKeyA( key, subkey );
230 }
231
232 /*
233  * Database Helpers
234  */
235
236 static void write_file(const CHAR *filename, const char *data, int data_size)
237 {
238     DWORD size;
239
240     HANDLE hf = CreateFile(filename, GENERIC_WRITE, 0, NULL,
241                            CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
242
243     WriteFile(hf, data, data_size, &size, NULL);
244     CloseHandle(hf);
245 }
246
247 static void write_msi_summary_info(MSIHANDLE db, const msi_summary_info *info, int num_info)
248 {
249     MSIHANDLE summary;
250     UINT r;
251     int j;
252
253     r = MsiGetSummaryInformationA(db, NULL, num_info, &summary);
254     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
255
256     /* import summary information into the stream */
257     for (j = 0; j < num_info; j++)
258     {
259         const msi_summary_info *entry = &info[j];
260
261         r = MsiSummaryInfoSetPropertyA(summary, entry->property, entry->datatype,
262                                        entry->iValue, entry->pftValue, entry->szValue);
263         ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
264     }
265
266     /* write the summary changes back to the stream */
267     r = MsiSummaryInfoPersist(summary);
268     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
269
270     MsiCloseHandle(summary);
271 }
272
273 static void create_database(const CHAR *name, const msi_table *tables, int num_tables,
274                             const msi_summary_info *info, int num_info)
275 {
276     MSIHANDLE db;
277     UINT r;
278     int j;
279
280     r = MsiOpenDatabaseA(name, MSIDBOPEN_CREATE, &db);
281     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
282
283     /* import the tables into the database */
284     for (j = 0; j < num_tables; j++)
285     {
286         const msi_table *table = &tables[j];
287
288         write_file(table->filename, table->data, (table->size - 1) * sizeof(char));
289
290         r = MsiDatabaseImportA(db, CURR_DIR, table->filename);
291         ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
292
293         DeleteFileA(table->filename);
294     }
295
296     write_msi_summary_info(db, info, num_info);
297
298     r = MsiDatabaseCommit(db);
299     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
300
301     MsiCloseHandle(db);
302 }
303
304 static BOOL create_package(LPWSTR path)
305 {
306     static const WCHAR slashW[] = {'\\',0};
307     DWORD len;
308
309     /* Prepare package */
310     create_database(msifile, tables,
311                     sizeof(tables) / sizeof(msi_table), summary_info,
312                     sizeof(summary_info) / sizeof(msi_summary_info));
313
314     len = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
315                               CURR_DIR, -1, path, MAX_PATH);
316     ok(len, "MultiByteToWideChar returned error %d\n", GetLastError());
317     if (!len)
318         return FALSE;
319
320     lstrcatW(path, slashW);
321     lstrcatW(path, szMsifile);
322     return TRUE;
323 }
324
325 /*
326  * Installation helpers
327  */
328
329 static char PROG_FILES_DIR[MAX_PATH];
330
331 static BOOL get_program_files_dir(LPSTR buf)
332 {
333     HKEY hkey;
334     DWORD type = REG_EXPAND_SZ, size;
335
336     if (RegOpenKey(HKEY_LOCAL_MACHINE,
337                    "Software\\Microsoft\\Windows\\CurrentVersion", &hkey))
338         return FALSE;
339
340     size = MAX_PATH;
341     if (RegQueryValueEx(hkey, "ProgramFilesDir (x86)", 0, &type, (LPBYTE)buf, &size) &&
342         RegQueryValueEx(hkey, "ProgramFilesDir", 0, &type, (LPBYTE)buf, &size))
343         return FALSE;
344
345     RegCloseKey(hkey);
346     return TRUE;
347 }
348
349 static void create_file(const CHAR *name, DWORD size)
350 {
351     HANDLE file;
352     DWORD written, left;
353
354     file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
355     ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n", name);
356     WriteFile(file, name, strlen(name), &written, NULL);
357     WriteFile(file, "\n", strlen("\n"), &written, NULL);
358
359     left = size - lstrlen(name) - 1;
360
361     SetFilePointer(file, left, NULL, FILE_CURRENT);
362     SetEndOfFile(file);
363
364     CloseHandle(file);
365 }
366
367 static void create_test_files(void)
368 {
369     CreateDirectoryA("msitest", NULL);
370     create_file("msitest\\one.txt", 100);
371     CreateDirectoryA("msitest\\first", NULL);
372     create_file("msitest\\first\\two.txt", 100);
373     CreateDirectoryA("msitest\\second", NULL);
374     create_file("msitest\\second\\three.txt", 100);
375     CreateDirectoryA("msitest\\cabout",NULL);
376     create_file("msitest\\cabout\\four.txt", 100);
377     CreateDirectoryA("msitest\\cabout\\new",NULL);
378     create_file("msitest\\cabout\\new\\five.txt", 100);
379     create_file("msitest\\filename", 100);
380 }
381
382 static BOOL delete_pf(const CHAR *rel_path, BOOL is_file)
383 {
384     CHAR path[MAX_PATH];
385
386     lstrcpyA(path, PROG_FILES_DIR);
387     lstrcatA(path, "\\");
388     lstrcatA(path, rel_path);
389
390     if (is_file)
391         return DeleteFileA(path);
392     else
393         return RemoveDirectoryA(path);
394 }
395
396 static void delete_test_files(void)
397 {
398     DeleteFileA(msifile);
399     DeleteFileA("msitest\\cabout\\new\\five.txt");
400     DeleteFileA("msitest\\cabout\\four.txt");
401     DeleteFileA("msitest\\second\\three.txt");
402     DeleteFileA("msitest\\first\\two.txt");
403     DeleteFileA("msitest\\one.txt");
404     DeleteFileA("msitest\\filename");
405     RemoveDirectoryA("msitest\\cabout\\new");
406     RemoveDirectoryA("msitest\\cabout");
407     RemoveDirectoryA("msitest\\second");
408     RemoveDirectoryA("msitest\\first");
409     RemoveDirectoryA("msitest");
410 }
411
412 /*
413  * Automation helpers and tests
414  */
415
416 /* ok-like statement which takes two unicode strings or one unicode and one ANSI string as arguments */
417 static CHAR string1[MAX_PATH], string2[MAX_PATH];
418
419 #define ok_w2(format, szString1, szString2) \
420 \
421     do { \
422     WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
423     WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
424     if (lstrcmpA(string1, string2) != 0) \
425         ok(0, format, string1, string2); \
426     } while(0);
427
428 #define ok_w2n(format, szString1, szString2, len) \
429 \
430     if (memcmp(szString1, szString2, len * sizeof(WCHAR)) != 0) \
431     { \
432         WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
433         WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
434         ok(0, format, string1, string2); \
435     }
436
437 #define ok_aw(format, aString, wString) \
438 \
439     WideCharToMultiByte(CP_ACP, 0, wString, -1, string1, MAX_PATH, NULL, NULL); \
440     if (lstrcmpA(string1, aString) != 0) \
441         ok(0, format, string1, aString); \
442
443 #define ok_awplus(format, extra, aString, wString)       \
444 \
445     WideCharToMultiByte(CP_ACP, 0, wString, -1, string1, MAX_PATH, NULL, NULL); \
446     if (lstrcmpA(string1, aString) != 0) \
447         ok(0, format, extra, string1, aString);  \
448
449 /* exception checker */
450 static WCHAR szSource[] = {'M','s','i',' ','A','P','I',' ','E','r','r','o','r',0};
451
452 #define ok_exception(hr, szDescription)           \
453     if (hr == DISP_E_EXCEPTION) \
454     { \
455         /* Compare wtype, source, and destination */                    \
456         ok(excepinfo.wCode == 1000, "Exception info was %d, expected 1000\n", excepinfo.wCode); \
457 \
458         ok(excepinfo.bstrSource != NULL, "Exception source was NULL\n"); \
459         if (excepinfo.bstrSource)                                       \
460             ok_w2("Exception source was \"%s\" but expected to be \"%s\"\n", excepinfo.bstrSource, szSource); \
461 \
462         ok(excepinfo.bstrDescription != NULL, "Exception description was NULL\n"); \
463         if (excepinfo.bstrDescription) \
464             ok_w2("Exception description was \"%s\" but expected to be \"%s\"\n", excepinfo.bstrDescription, szDescription); \
465 \
466         SysFreeString(excepinfo.bstrSource); \
467         SysFreeString(excepinfo.bstrDescription); \
468         SysFreeString(excepinfo.bstrHelpFile); \
469     }
470
471 static DISPID get_dispid( IDispatch *disp, const char *name )
472 {
473     LPOLESTR str;
474     UINT len;
475     DISPID id = -1;
476     HRESULT r;
477
478     len = MultiByteToWideChar(CP_ACP, 0, name, -1, NULL, 0 );
479     str = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR) );
480     if (str)
481     {
482         MultiByteToWideChar(CP_ACP, 0, name, -1, str, len );
483         r = IDispatch_GetIDsOfNames( disp, &IID_NULL, &str, 1, 0, &id );
484         HeapFree(GetProcessHeap(), 0, str);
485         if (r != S_OK)
486             return -1;
487     }
488
489     return id;
490 }
491
492 typedef struct {
493     DISPID did;
494     const char *name;
495     BOOL todo;
496 } get_did_t;
497
498 static const get_did_t get_did_data[] = {
499     { 1,  "CreateRecord" },
500     { 2,  "OpenPackage" },
501     { 3,  "OpenProduct" },
502     { 4,  "OpenDatabase" },
503     { 5,  "SummaryInformation" },
504     { 6,  "UILevel" },
505     { 7,  "EnableLog" },
506     { 8,  "InstallProduct" },
507     { 9,  "Version" },
508     { 10, "LastErrorRecord" },
509     { 11, "RegistryValue" },
510     { 12, "Environment" },
511     { 13, "FileAttributes" },
512     { 15, "FileSize" },
513     { 16, "FileVersion" },
514     { 17, "ProductState" },
515     { 18, "ProductInfo" },
516     { 19, "ConfigureProduct", TRUE },
517     { 20, "ReinstallProduct", TRUE },
518     { 21, "CollectUserInfo", TRUE },
519     { 22, "ApplyPatch", TRUE },
520     { 23, "FeatureParent", TRUE },
521     { 24, "FeatureState", TRUE },
522     { 25, "UseFeature", TRUE },
523     { 26, "FeatureUsageCount", TRUE },
524     { 27, "FeatureUsageDate", TRUE },
525     { 28, "ConfigureFeature", TRUE },
526     { 29, "ReinstallFeature", TRUE },
527     { 30, "ProvideComponent", TRUE },
528     { 31, "ComponentPath", TRUE },
529     { 32, "ProvideQualifiedComponent", TRUE },
530     { 33, "QualifierDescription", TRUE },
531     { 34, "ComponentQualifiers", TRUE },
532     { 35, "Products" },
533     { 36, "Features", TRUE },
534     { 37, "Components", TRUE },
535     { 38, "ComponentClients", TRUE },
536     { 39, "Patches", TRUE },
537     { 40, "RelatedProducts" },
538     { 41, "PatchInfo", TRUE },
539     { 42, "PatchTransforms", TRUE },
540     { 43, "AddSource", TRUE },
541     { 44, "ClearSourceList", TRUE },
542     { 45, "ForceSourceListResolution", TRUE },
543     { 46, "ShortcutTarget", TRUE },
544     { 47, "FileHash", TRUE },
545     { 48, "FileSignatureInfo", TRUE },
546     { 0 }
547 };
548
549 static void test_dispid(void)
550 {
551     const get_did_t *ptr = get_did_data;
552     DISPID dispid;
553
554     while (ptr->name)
555     {
556         dispid = get_dispid(pInstaller, ptr->name);
557         if (ptr->todo)
558         todo_wine
559             ok(dispid == ptr->did, "%s: expected %d, got %d\n", ptr->name, ptr->did, dispid);
560         else
561             ok(dispid == ptr->did, "%s: expected %d, got %d\n", ptr->name, ptr->did, dispid);
562         ptr++;
563     }
564
565     dispid = get_dispid(pInstaller, "RemovePatches");
566     ok(dispid == 49 || dispid == -1, "Expected 49 or -1, got %d\n", dispid);
567     dispid = get_dispid(pInstaller, "ApplyMultiplePatches");
568     ok(dispid == 51 || dispid == -1, "Expected 51 or -1, got %d\n", dispid);
569     dispid = get_dispid(pInstaller, "ProductsEx");
570     ok(dispid == 52 || dispid == -1, "Expected 52 or -1, got %d\n", dispid);
571     dispid = get_dispid(pInstaller, "PatchesEx");
572     ok(dispid == 55 || dispid == -1, "Expected 55 or -1, got %d\n", dispid);
573     dispid = get_dispid(pInstaller, "ExtractPatchXMLData");
574     ok(dispid == 57 || dispid == -1, "Expected 57 or -1, got %d\n", dispid);
575     dispid = get_dispid( pInstaller, "ProductElevated" );
576     ok(dispid == 59 || dispid == -1, "Expected 59 or -1, got %d\n", dispid);
577     dispid = get_dispid( pInstaller, "ProvideAssembly" );
578     ok(dispid == 60 || dispid == -1, "Expected 60 or -1, got %d\n", dispid);
579     dispid = get_dispid( pInstaller, "ProductInfoFromScript" );
580     ok(dispid == 61 || dispid == -1, "Expected 61 or -1, got %d\n", dispid);
581     dispid = get_dispid( pInstaller, "AdvertiseProduct" );
582     ok(dispid == 62 || dispid == -1, "Expected 62 or -1, got %d\n", dispid);
583     dispid = get_dispid( pInstaller, "CreateAdvertiseScript" );
584     ok(dispid == 63 || dispid == -1, "Expected 63 or -1, got %d\n", dispid);
585     dispid = get_dispid( pInstaller, "PatchFiles" );
586     ok(dispid == 65 || dispid == -1, "Expected 65 or -1, got %d\n", dispid);
587 }
588
589 /* Test basic IDispatch functions */
590 static void test_dispatch(void)
591 {
592     static WCHAR szOpenPackage[] = { 'O','p','e','n','P','a','c','k','a','g','e',0 };
593     static 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};
594     static WCHAR szProductState[] = { 'P','r','o','d','u','c','t','S','t','a','t','e',0 };
595     HRESULT hr;
596     DISPID dispid;
597     OLECHAR *name;
598     VARIANT varresult;
599     VARIANTARG vararg[3];
600     WCHAR path[MAX_PATH];
601     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
602
603     /* Test getting ID of a function name that does not exist */
604     name = (WCHAR *)szMsifile;
605     hr = IDispatch_GetIDsOfNames(pInstaller, &IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &dispid);
606     ok(hr == DISP_E_UNKNOWNNAME, "IDispatch::GetIDsOfNames returned 0x%08x\n", hr);
607
608     /* Test invoking this function */
609     hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, NULL, NULL, NULL, NULL);
610     ok(hr == DISP_E_MEMBERNOTFOUND, "IDispatch::Invoke returned 0x%08x\n", hr);
611
612     /* Test getting ID of a function name that does exist */
613     name = szOpenPackage;
614     hr = IDispatch_GetIDsOfNames(pInstaller, &IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &dispid);
615     ok(hr == S_OK, "IDispatch::GetIDsOfNames returned 0x%08x\n", hr);
616
617     /* Test invoking this function (without parameters passed) */
618     if (0) /* All of these crash MSI on Windows XP */
619     {
620         IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, NULL, NULL, NULL, NULL);
621         IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, NULL, NULL, &excepinfo, NULL);
622         VariantInit(&varresult);
623         IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, NULL, &varresult, &excepinfo, NULL);
624     }
625
626     /* Try with NULL params */
627     hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
628     ok(hr == DISP_E_TYPEMISMATCH, "IDispatch::Invoke returned 0x%08x\n", hr);
629
630     /* Try one empty parameter */
631     dispparams.rgvarg = vararg;
632     dispparams.cArgs = 1;
633     VariantInit(&vararg[0]);
634     hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
635     ok(hr == DISP_E_TYPEMISMATCH, "IDispatch::Invoke returned 0x%08x\n", hr);
636
637     /* Try two empty parameters */
638     dispparams.cArgs = 2;
639     VariantInit(&vararg[0]);
640     VariantInit(&vararg[1]);
641     hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
642     ok(hr == DISP_E_TYPEMISMATCH, "IDispatch::Invoke returned 0x%08x\n", hr);
643
644     /* Try one parameter, the required BSTR.  Second parameter is optional.
645      * NOTE: The specified package does not exist, which is why the call fails.
646      */
647     dispparams.cArgs = 1;
648     VariantInit(&vararg[0]);
649     V_VT(&vararg[0]) = VT_BSTR;
650     V_BSTR(&vararg[0]) = SysAllocString(szMsifile);
651     hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
652     ok(hr == DISP_E_EXCEPTION, "IDispatch::Invoke returned 0x%08x\n", hr);
653     ok_exception(hr, szOpenPackageException);
654     VariantClear(&vararg[0]);
655
656     /* Provide the required BSTR and an empty second parameter.
657      * NOTE: The specified package does not exist, which is why the call fails.
658      */
659     dispparams.cArgs = 2;
660     VariantInit(&vararg[1]);
661     V_VT(&vararg[1]) = VT_BSTR;
662     V_BSTR(&vararg[1]) = SysAllocString(szMsifile);
663     VariantInit(&vararg[0]);
664     hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
665     ok(hr == DISP_E_EXCEPTION, "IDispatch::Invoke returned 0x%08x\n", hr);
666     ok_exception(hr, szOpenPackageException);
667     VariantClear(&vararg[1]);
668
669     /* Provide the required BSTR and two empty parameters.
670      * NOTE: The specified package does not exist, which is why the call fails.
671      */
672     dispparams.cArgs = 3;
673     VariantInit(&vararg[2]);
674     V_VT(&vararg[2]) = VT_BSTR;
675     V_BSTR(&vararg[2]) = SysAllocString(szMsifile);
676     VariantInit(&vararg[1]);
677     VariantInit(&vararg[0]);
678     hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
679     ok(hr == DISP_E_EXCEPTION, "IDispatch::Invoke returned 0x%08x\n", hr);
680     ok_exception(hr, szOpenPackageException);
681     VariantClear(&vararg[2]);
682
683     /* Provide the required BSTR and a second parameter with the wrong type. */
684     dispparams.cArgs = 2;
685     VariantInit(&vararg[1]);
686     V_VT(&vararg[1]) = VT_BSTR;
687     V_BSTR(&vararg[1]) = SysAllocString(szMsifile);
688     VariantInit(&vararg[0]);
689     V_VT(&vararg[0]) = VT_BSTR;
690     V_BSTR(&vararg[0]) = SysAllocString(szMsifile);
691     hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
692     ok(hr == DISP_E_TYPEMISMATCH, "IDispatch::Invoke returned 0x%08x\n", hr);
693     VariantClear(&vararg[0]);
694     VariantClear(&vararg[1]);
695
696     /* Create a proper installer package. */
697     create_package(path);
698
699     /* Try one parameter, the required BSTR.  Second parameter is optional.
700      * Proper installer package exists.  Path to the package is relative.
701      */
702     dispparams.cArgs = 1;
703     VariantInit(&vararg[0]);
704     V_VT(&vararg[0]) = VT_BSTR;
705     V_BSTR(&vararg[0]) = SysAllocString(szMsifile);
706     hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
707     todo_wine ok(hr == DISP_E_EXCEPTION, "IDispatch::Invoke returned 0x%08x\n", hr);
708     ok_exception(hr, szOpenPackageException);
709     VariantClear(&vararg[0]);
710     if (hr != DISP_E_EXCEPTION)
711         VariantClear(&varresult);
712
713     /* Try one parameter, the required BSTR.  Second parameter is optional.
714      * Proper installer package exists.  Path to the package is absolute.
715      */
716     dispparams.cArgs = 1;
717     VariantInit(&vararg[0]);
718     V_VT(&vararg[0]) = VT_BSTR;
719     V_BSTR(&vararg[0]) = SysAllocString(path);
720     hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
721     if (hr == DISP_E_EXCEPTION)
722     {
723         skip("OpenPackage failed, insufficient rights?\n");
724         DeleteFileW(path);
725         return;
726     }
727     ok(hr == S_OK, "IDispatch::Invoke returned 0x%08x\n", hr);
728     VariantClear(&vararg[0]);
729     VariantClear(&varresult);
730
731     /* Provide the required BSTR and an empty second parameter.  Proper
732      * installation package exists.
733      */
734     dispparams.cArgs = 2;
735     VariantInit(&vararg[1]);
736     V_VT(&vararg[1]) = VT_BSTR;
737     V_BSTR(&vararg[1]) = SysAllocString(path);
738     VariantInit(&vararg[0]);
739     hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
740     ok(hr == S_OK, "IDispatch::Invoke returned 0x%08x\n", hr);
741     VariantClear(&vararg[1]);
742     VariantClear(&varresult);
743
744     /* Provide the required BSTR and two empty parameters.  Proper
745      * installation package exists.
746      */
747     dispparams.cArgs = 3;
748     VariantInit(&vararg[2]);
749     V_VT(&vararg[2]) = VT_BSTR;
750     V_BSTR(&vararg[2]) = SysAllocString(path);
751     VariantInit(&vararg[1]);
752     VariantInit(&vararg[0]);
753     hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
754     ok(hr == S_OK, "IDispatch::Invoke returned 0x%08x\n", hr);
755     VariantClear(&vararg[2]);
756     VariantClear(&varresult);
757
758     /* Provide the required BSTR and a second parameter with the wrong type. */
759     dispparams.cArgs = 2;
760     VariantInit(&vararg[1]);
761     V_VT(&vararg[1]) = VT_BSTR;
762     V_BSTR(&vararg[1]) = SysAllocString(path);
763     VariantInit(&vararg[0]);
764     V_VT(&vararg[0]) = VT_BSTR;
765     V_BSTR(&vararg[0]) = SysAllocString(path);
766     hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
767     ok(hr == DISP_E_TYPEMISMATCH, "IDispatch::Invoke returned 0x%08x\n", hr);
768     VariantClear(&vararg[0]);
769     VariantClear(&vararg[1]);
770
771     /* Provide the required BSTR and a second parameter that can be coerced to
772      * VT_I4.
773      */
774     dispparams.cArgs = 2;
775     VariantInit(&vararg[1]);
776     V_VT(&vararg[1]) = VT_BSTR;
777     V_BSTR(&vararg[1]) = SysAllocString(path);
778     VariantInit(&vararg[0]);
779     V_VT(&vararg[0]) = VT_I2;
780     V_BSTR(&vararg[0]) = 0;
781     hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
782     ok(hr == S_OK, "IDispatch::Invoke returned 0x%08x\n", hr);
783     VariantClear(&vararg[1]);
784     VariantClear(&varresult);
785
786     DeleteFileW(path);
787
788     /* Test invoking a method as a DISPATCH_PROPERTYGET or DISPATCH_PROPERTYPUT */
789     VariantInit(&vararg[0]);
790     hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYGET, &dispparams, &varresult, &excepinfo, NULL);
791     ok(hr == DISP_E_MEMBERNOTFOUND, "IDispatch::Invoke returned 0x%08x\n", hr);
792
793     VariantInit(&vararg[0]);
794     hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYPUT, &dispparams, &varresult, &excepinfo, NULL);
795     ok(hr == DISP_E_MEMBERNOTFOUND, "IDispatch::Invoke returned 0x%08x\n", hr);
796
797     /* Test invoking a read-only property as DISPATCH_PROPERTYPUT or as a DISPATCH_METHOD */
798     name = szProductState;
799     hr = IDispatch_GetIDsOfNames(pInstaller, &IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &dispid);
800     ok(hr == S_OK, "IDispatch::GetIDsOfNames returned 0x%08x\n", hr);
801
802     dispparams.rgvarg = NULL;
803     dispparams.cArgs = 0;
804     hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYPUT, &dispparams, &varresult, &excepinfo, NULL);
805     ok(hr == DISP_E_MEMBERNOTFOUND, "IDispatch::Invoke returned 0x%08x\n", hr);
806
807     dispparams.rgvarg = NULL;
808     dispparams.cArgs = 0;
809     hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
810     ok(hr == DISP_E_MEMBERNOTFOUND, "IDispatch::Invoke returned 0x%08x\n", hr);
811 }
812
813 /* invocation helper function */
814 static int _invoke_todo_vtResult = 0;
815
816 static HRESULT invoke(IDispatch *pDispatch, LPCSTR szName, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, VARTYPE vtResult)
817 {
818     OLECHAR *name = NULL;
819     DISPID dispid;
820     HRESULT hr;
821     UINT i;
822     UINT len;
823
824     memset(pVarResult, 0, sizeof(VARIANT));
825     VariantInit(pVarResult);
826
827     len = MultiByteToWideChar(CP_ACP, 0, szName, -1, NULL, 0 );
828     name = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR) );
829     if (!name) return E_FAIL;
830     MultiByteToWideChar(CP_ACP, 0, szName, -1, name, len );
831     hr = IDispatch_GetIDsOfNames(pDispatch, &IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &dispid);
832     HeapFree(GetProcessHeap(), 0, name);
833     ok(hr == S_OK, "IDispatch::GetIDsOfNames returned 0x%08x\n", hr);
834     if (!hr == S_OK) return hr;
835
836     memset(&excepinfo, 0, sizeof(excepinfo));
837     hr = IDispatch_Invoke(pDispatch, dispid, &IID_NULL, LOCALE_NEUTRAL, wFlags, pDispParams, pVarResult, &excepinfo, NULL);
838
839     if (hr == S_OK)
840     {
841         if (_invoke_todo_vtResult) todo_wine
842             ok(V_VT(pVarResult) == vtResult, "Variant result type is %d, expected %d\n", V_VT(pVarResult), vtResult);
843         else
844             ok(V_VT(pVarResult) == vtResult, "Variant result type is %d, expected %d\n", V_VT(pVarResult), vtResult);
845         if (vtResult != VT_EMPTY)
846         {
847             hr = VariantChangeTypeEx(pVarResult, pVarResult, LOCALE_NEUTRAL, 0, vtResult);
848             ok(hr == S_OK, "VariantChangeTypeEx returned 0x%08x\n", hr);
849         }
850     }
851
852     for (i=0; i<pDispParams->cArgs; i++)
853         VariantClear(&pDispParams->rgvarg[i]);
854
855     return hr;
856 }
857
858 /* Object_Property helper functions */
859
860 static HRESULT Installer_CreateRecord(int count, IDispatch **pRecord)
861 {
862     VARIANT varresult;
863     VARIANTARG vararg[1];
864     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
865     HRESULT hr;
866
867     VariantInit(&vararg[0]);
868     V_VT(&vararg[0]) = VT_I4;
869     V_I4(&vararg[0]) = count;
870
871     hr = invoke(pInstaller, "CreateRecord", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH);
872     *pRecord = V_DISPATCH(&varresult);
873     return hr;
874 }
875
876 static HRESULT Installer_RegistryValue(HKEY hkey, LPCWSTR szKey, VARIANT vValue, VARIANT *pVarResult, VARTYPE vtExpect)
877 {
878     VARIANTARG vararg[3];
879     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
880
881     VariantInit(&vararg[2]);
882     V_VT(&vararg[2]) = VT_I4;
883     V_I4(&vararg[2]) = (INT_PTR)hkey;
884     VariantInit(&vararg[1]);
885     V_VT(&vararg[1]) = VT_BSTR;
886     V_BSTR(&vararg[1]) = SysAllocString(szKey);
887     VariantInit(&vararg[0]);
888     VariantCopy(&vararg[0], &vValue);
889     VariantClear(&vValue);
890
891     return invoke(pInstaller, "RegistryValue", DISPATCH_METHOD, &dispparams, pVarResult, vtExpect);
892 }
893
894 static HRESULT Installer_RegistryValueE(HKEY hkey, LPCWSTR szKey, BOOL *pBool)
895 {
896     VARIANT varresult;
897     VARIANTARG vararg;
898     HRESULT hr;
899
900     VariantInit(&vararg);
901     V_VT(&vararg) = VT_EMPTY;
902     hr = Installer_RegistryValue(hkey, szKey, vararg, &varresult, VT_BOOL);
903     *pBool = V_BOOL(&varresult);
904     VariantClear(&varresult);
905     return hr;
906 }
907
908 static HRESULT Installer_RegistryValueW(HKEY hkey, LPCWSTR szKey, LPCWSTR szValue, LPWSTR szString)
909 {
910     VARIANT varresult;
911     VARIANTARG vararg;
912     HRESULT hr;
913
914     VariantInit(&vararg);
915     V_VT(&vararg) = VT_BSTR;
916     V_BSTR(&vararg) = SysAllocString(szValue);
917
918     hr = Installer_RegistryValue(hkey, szKey, vararg, &varresult, VT_BSTR);
919     if (V_BSTR(&varresult)) lstrcpyW(szString, V_BSTR(&varresult));
920     VariantClear(&varresult);
921     return hr;
922 }
923
924 static HRESULT Installer_RegistryValueI(HKEY hkey, LPCWSTR szKey, int iValue, LPWSTR szString, VARTYPE vtResult)
925 {
926     VARIANT varresult;
927     VARIANTARG vararg;
928     HRESULT hr;
929
930     VariantInit(&vararg);
931     V_VT(&vararg) = VT_I4;
932     V_I4(&vararg) = iValue;
933
934     hr = Installer_RegistryValue(hkey, szKey, vararg, &varresult, vtResult);
935     if (SUCCEEDED(hr) && vtResult == VT_BSTR) lstrcpyW(szString, V_BSTR(&varresult));
936     VariantClear(&varresult);
937     return hr;
938 }
939
940 static HRESULT Installer_OpenPackage(LPCWSTR szPackagePath, int options, IDispatch **pSession)
941 {
942     VARIANT varresult;
943     VARIANTARG vararg[2];
944     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
945     HRESULT hr;
946
947     VariantInit(&vararg[1]);
948     V_VT(&vararg[1]) = VT_BSTR;
949     V_BSTR(&vararg[1]) = SysAllocString(szPackagePath);
950     VariantInit(&vararg[0]);
951     V_VT(&vararg[0]) = VT_I4;
952     V_I4(&vararg[0]) = options;
953
954     hr = invoke(pInstaller, "OpenPackage", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH);
955     *pSession = V_DISPATCH(&varresult);
956     return hr;
957 }
958
959 static HRESULT Installer_OpenDatabase(LPCWSTR szDatabasePath, int openmode, IDispatch **pDatabase)
960 {
961     VARIANT varresult;
962     VARIANTARG vararg[2];
963     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
964     HRESULT hr;
965
966     VariantInit(&vararg[1]);
967     V_VT(&vararg[1]) = VT_BSTR;
968     V_BSTR(&vararg[1]) = SysAllocString(szDatabasePath);
969     VariantInit(&vararg[0]);
970     V_VT(&vararg[0]) = VT_I4;
971     V_I4(&vararg[0]) = openmode;
972
973     hr = invoke(pInstaller, "OpenDatabase", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH);
974     *pDatabase = V_DISPATCH(&varresult);
975     return hr;
976 }
977
978 static HRESULT Installer_InstallProduct(LPCWSTR szPackagePath, LPCWSTR szPropertyValues)
979 {
980     VARIANT varresult;
981     VARIANTARG vararg[2];
982     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
983
984     VariantInit(&vararg[1]);
985     V_VT(&vararg[1]) = VT_BSTR;
986     V_BSTR(&vararg[1]) = SysAllocString(szPackagePath);
987     VariantInit(&vararg[0]);
988     V_VT(&vararg[0]) = VT_BSTR;
989     V_BSTR(&vararg[0]) = SysAllocString(szPropertyValues);
990
991     return invoke(pInstaller, "InstallProduct", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY);
992 }
993
994 static HRESULT Installer_ProductState(LPCWSTR szProduct, int *pInstallState)
995 {
996     VARIANT varresult;
997     VARIANTARG vararg[1];
998     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
999     HRESULT hr;
1000
1001     VariantInit(&vararg[0]);
1002     V_VT(&vararg[0]) = VT_BSTR;
1003     V_BSTR(&vararg[0]) = SysAllocString(szProduct);
1004
1005     hr = invoke(pInstaller, "ProductState", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1006     *pInstallState = V_I4(&varresult);
1007     VariantClear(&varresult);
1008     return hr;
1009 }
1010
1011 static HRESULT Installer_ProductInfo(LPCWSTR szProduct, LPCWSTR szAttribute, LPWSTR szString)
1012 {
1013     VARIANT varresult;
1014     VARIANTARG vararg[2];
1015     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1016     HRESULT hr;
1017
1018     VariantInit(&vararg[1]);
1019     V_VT(&vararg[1]) = VT_BSTR;
1020     V_BSTR(&vararg[1]) = SysAllocString(szProduct);
1021     VariantInit(&vararg[0]);
1022     V_VT(&vararg[0]) = VT_BSTR;
1023     V_BSTR(&vararg[0]) = SysAllocString(szAttribute);
1024
1025     hr = invoke(pInstaller, "ProductInfo", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR);
1026     if (V_BSTR(&varresult)) lstrcpyW(szString, V_BSTR(&varresult));
1027     VariantClear(&varresult);
1028     return hr;
1029 }
1030
1031 static HRESULT Installer_Products(IDispatch **pStringList)
1032 {
1033     VARIANT varresult;
1034     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1035     HRESULT hr;
1036
1037     hr = invoke(pInstaller, "Products", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH);
1038     *pStringList = V_DISPATCH(&varresult);
1039     return hr;
1040 }
1041
1042 static HRESULT Installer_RelatedProducts(LPCWSTR szProduct, IDispatch **pStringList)
1043 {
1044     VARIANT varresult;
1045     VARIANTARG vararg[1];
1046     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1047     HRESULT hr;
1048
1049     VariantInit(&vararg[0]);
1050     V_VT(&vararg[0]) = VT_BSTR;
1051     V_BSTR(&vararg[0]) = SysAllocString(szProduct);
1052
1053     hr = invoke(pInstaller, "RelatedProducts", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH);
1054     *pStringList = V_DISPATCH(&varresult);
1055     return hr;
1056 }
1057
1058 static HRESULT Installer_VersionGet(LPWSTR szVersion)
1059 {
1060     VARIANT varresult;
1061     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1062     HRESULT hr;
1063
1064     hr = invoke(pInstaller, "Version", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR);
1065     if (V_BSTR(&varresult)) lstrcpyW(szVersion, V_BSTR(&varresult));
1066     VariantClear(&varresult);
1067     return hr;
1068 }
1069
1070 static HRESULT Installer_UILevelPut(int level)
1071 {
1072     VARIANT varresult;
1073     VARIANTARG vararg;
1074     DISPID dispid = DISPID_PROPERTYPUT;
1075     DISPPARAMS dispparams = {&vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};
1076
1077     VariantInit(&vararg);
1078     V_VT(&vararg) = VT_I4;
1079     V_I4(&vararg) = level;
1080
1081     return invoke(pInstaller, "UILevel", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
1082 }
1083
1084 static HRESULT Session_Installer(IDispatch *pSession, IDispatch **pInst)
1085 {
1086     VARIANT varresult;
1087     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1088     HRESULT hr;
1089
1090     hr = invoke(pSession, "Installer", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH);
1091     *pInst = V_DISPATCH(&varresult);
1092     return hr;
1093 }
1094
1095 static HRESULT Session_PropertyGet(IDispatch *pSession, LPCWSTR szName, LPWSTR szReturn)
1096 {
1097     VARIANT varresult;
1098     VARIANTARG vararg[1];
1099     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1100     HRESULT hr;
1101
1102     VariantInit(&vararg[0]);
1103     V_VT(&vararg[0]) = VT_BSTR;
1104     V_BSTR(&vararg[0]) = SysAllocString(szName);
1105
1106     hr = invoke(pSession, "Property", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR);
1107     if (V_BSTR(&varresult)) lstrcpyW(szReturn, V_BSTR(&varresult));
1108     VariantClear(&varresult);
1109     return hr;
1110 }
1111
1112 static HRESULT Session_PropertyPut(IDispatch *pSession, LPCWSTR szName, LPCWSTR szValue)
1113 {
1114     VARIANT varresult;
1115     VARIANTARG vararg[2];
1116     DISPID dispid = DISPID_PROPERTYPUT;
1117     DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};
1118
1119     VariantInit(&vararg[1]);
1120     V_VT(&vararg[1]) = VT_BSTR;
1121     V_BSTR(&vararg[1]) = SysAllocString(szName);
1122     VariantInit(&vararg[0]);
1123     V_VT(&vararg[0]) = VT_BSTR;
1124     V_BSTR(&vararg[0]) = SysAllocString(szValue);
1125
1126     return invoke(pSession, "Property", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
1127 }
1128
1129 static HRESULT Session_LanguageGet(IDispatch *pSession, UINT *pLangId)
1130 {
1131     VARIANT varresult;
1132     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1133     HRESULT hr;
1134
1135     hr = invoke(pSession, "Language", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1136     *pLangId = V_I4(&varresult);
1137     VariantClear(&varresult);
1138     return hr;
1139 }
1140
1141 static HRESULT Session_ModeGet(IDispatch *pSession, int iFlag, BOOL *pMode)
1142 {
1143     VARIANT varresult;
1144     VARIANTARG vararg[1];
1145     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1146     HRESULT hr;
1147
1148     VariantInit(&vararg[0]);
1149     V_VT(&vararg[0]) = VT_I4;
1150     V_I4(&vararg[0]) = iFlag;
1151
1152     hr = invoke(pSession, "Mode", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BOOL);
1153     *pMode = V_BOOL(&varresult);
1154     VariantClear(&varresult);
1155     return hr;
1156 }
1157
1158 static HRESULT Session_ModePut(IDispatch *pSession, int iFlag, BOOL bMode)
1159 {
1160     VARIANT varresult;
1161     VARIANTARG vararg[2];
1162     DISPID dispid = DISPID_PROPERTYPUT;
1163     DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};
1164
1165     VariantInit(&vararg[1]);
1166     V_VT(&vararg[1]) = VT_I4;
1167     V_I4(&vararg[1]) = iFlag;
1168     VariantInit(&vararg[0]);
1169     V_VT(&vararg[0]) = VT_BOOL;
1170     V_BOOL(&vararg[0]) = bMode;
1171
1172     return invoke(pSession, "Mode", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
1173 }
1174
1175 static HRESULT Session_Database(IDispatch *pSession, IDispatch **pDatabase)
1176 {
1177     VARIANT varresult;
1178     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1179     HRESULT hr;
1180
1181     hr = invoke(pSession, "Database", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH);
1182     *pDatabase = V_DISPATCH(&varresult);
1183     return hr;
1184 }
1185
1186 static HRESULT Session_DoAction(IDispatch *pSession, LPCWSTR szAction, int *iReturn)
1187 {
1188     VARIANT varresult;
1189     VARIANTARG vararg[1];
1190     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1191     HRESULT hr;
1192
1193     VariantInit(&vararg[0]);
1194     V_VT(&vararg[0]) = VT_BSTR;
1195     V_BSTR(&vararg[0]) = SysAllocString(szAction);
1196
1197     hr = invoke(pSession, "DoAction", DISPATCH_METHOD, &dispparams, &varresult, VT_I4);
1198     *iReturn = V_I4(&varresult);
1199     VariantClear(&varresult);
1200     return hr;
1201 }
1202
1203 static HRESULT Session_EvaluateCondition(IDispatch *pSession, LPCWSTR szCondition, int *iReturn)
1204 {
1205     VARIANT varresult;
1206     VARIANTARG vararg[1];
1207     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1208     HRESULT hr;
1209
1210     VariantInit(&vararg[0]);
1211     V_VT(&vararg[0]) = VT_BSTR;
1212     V_BSTR(&vararg[0]) = SysAllocString(szCondition);
1213
1214     hr = invoke(pSession, "EvaluateCondition", DISPATCH_METHOD, &dispparams, &varresult, VT_I4);
1215     *iReturn = V_I4(&varresult);
1216     VariantClear(&varresult);
1217     return hr;
1218 }
1219
1220 static HRESULT Session_Message(IDispatch *pSession, LONG kind, IDispatch *record, int *ret)
1221 {
1222     VARIANT varresult;
1223     VARIANTARG vararg[2];
1224     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1225     HRESULT hr;
1226
1227     VariantInit(&varresult);
1228     V_VT(vararg) = VT_DISPATCH;
1229     V_DISPATCH(vararg) = record;
1230     V_VT(vararg+1) = VT_I4;
1231     V_I4(vararg+1) = kind;
1232
1233     hr = invoke(pSession, "Message", DISPATCH_METHOD, &dispparams, &varresult, VT_I4);
1234
1235     ok(V_VT(&varresult) == VT_I4, "V_VT(varresult) = %d\n", V_VT(&varresult));
1236     *ret = V_I4(&varresult);
1237
1238     return hr;
1239 }
1240
1241 static HRESULT Session_SetInstallLevel(IDispatch *pSession, LONG iInstallLevel)
1242 {
1243     VARIANT varresult;
1244     VARIANTARG vararg[1];
1245     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1246
1247     VariantInit(&vararg[0]);
1248     V_VT(&vararg[0]) = VT_I4;
1249     V_I4(&vararg[0]) = iInstallLevel;
1250
1251     return invoke(pSession, "SetInstallLevel", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY);
1252 }
1253
1254 static HRESULT Session_FeatureCurrentState(IDispatch *pSession, LPCWSTR szName, int *pState)
1255 {
1256     VARIANT varresult;
1257     VARIANTARG vararg[1];
1258     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1259     HRESULT hr;
1260
1261     VariantInit(&vararg[0]);
1262     V_VT(&vararg[0]) = VT_BSTR;
1263     V_BSTR(&vararg[0]) = SysAllocString(szName);
1264
1265     hr = invoke(pSession, "FeatureCurrentState", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1266     *pState = V_I4(&varresult);
1267     VariantClear(&varresult);
1268     return hr;
1269 }
1270
1271 static HRESULT Session_FeatureRequestStateGet(IDispatch *pSession, LPCWSTR szName, int *pState)
1272 {
1273     VARIANT varresult;
1274     VARIANTARG vararg[1];
1275     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1276     HRESULT hr;
1277
1278     VariantInit(&vararg[0]);
1279     V_VT(&vararg[0]) = VT_BSTR;
1280     V_BSTR(&vararg[0]) = SysAllocString(szName);
1281
1282     hr = invoke(pSession, "FeatureRequestState", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1283     *pState = V_I4(&varresult);
1284     VariantClear(&varresult);
1285     return hr;
1286 }
1287
1288 static HRESULT Session_FeatureRequestStatePut(IDispatch *pSession, LPCWSTR szName, int iState)
1289 {
1290     VARIANT varresult;
1291     VARIANTARG vararg[2];
1292     DISPID dispid = DISPID_PROPERTYPUT;
1293     DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};
1294
1295     VariantInit(&vararg[1]);
1296     V_VT(&vararg[1]) = VT_BSTR;
1297     V_BSTR(&vararg[1]) = SysAllocString(szName);
1298     VariantInit(&vararg[0]);
1299     V_VT(&vararg[0]) = VT_I4;
1300     V_I4(&vararg[0]) = iState;
1301
1302     return invoke(pSession, "FeatureRequestState", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
1303 }
1304
1305 static HRESULT Database_OpenView(IDispatch *pDatabase, LPCWSTR szSql, IDispatch **pView)
1306 {
1307     VARIANT varresult;
1308     VARIANTARG vararg[1];
1309     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1310     HRESULT hr;
1311
1312     VariantInit(&vararg[0]);
1313     V_VT(&vararg[0]) = VT_BSTR;
1314     V_BSTR(&vararg[0]) = SysAllocString(szSql);
1315
1316     hr = invoke(pDatabase, "OpenView", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH);
1317     *pView = V_DISPATCH(&varresult);
1318     return hr;
1319 }
1320
1321 static HRESULT Database_SummaryInformation(IDispatch *pDatabase, int iUpdateCount, IDispatch **pSummaryInfo)
1322 {
1323     VARIANT varresult;
1324     VARIANTARG vararg[1];
1325     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1326     HRESULT hr;
1327
1328     VariantInit(&vararg[0]);
1329     V_VT(&vararg[0]) = VT_I4;
1330     V_I4(&vararg[0]) = iUpdateCount;
1331
1332     hr = invoke(pDatabase, "SummaryInformation", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH);
1333     *pSummaryInfo = V_DISPATCH(&varresult);
1334     return hr;
1335 }
1336
1337 static HRESULT View_Execute(IDispatch *pView, IDispatch *pRecord)
1338 {
1339     VARIANT varresult;
1340     VARIANTARG vararg[1];
1341     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1342
1343     VariantInit(&vararg[0]);
1344     V_VT(&vararg[0]) = VT_DISPATCH;
1345     V_DISPATCH(&vararg[0]) = pRecord;
1346
1347     return invoke(pView, "Execute", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY);
1348 }
1349
1350 static HRESULT View_Fetch(IDispatch *pView, IDispatch **ppRecord)
1351 {
1352     VARIANT varresult;
1353     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1354     HRESULT hr = invoke(pView, "Fetch", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH);
1355     *ppRecord = V_DISPATCH(&varresult);
1356     return hr;
1357 }
1358
1359 static HRESULT View_Modify(IDispatch *pView, int iMode, IDispatch *pRecord)
1360 {
1361     VARIANT varresult;
1362     VARIANTARG vararg[2];
1363     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1364
1365     VariantInit(&vararg[1]);
1366     V_VT(&vararg[1]) = VT_I4;
1367     V_I4(&vararg[1]) = iMode;
1368     VariantInit(&vararg[0]);
1369     V_VT(&vararg[0]) = VT_DISPATCH;
1370     V_DISPATCH(&vararg[0]) = pRecord;
1371     if (pRecord)
1372         IDispatch_AddRef(pRecord);   /* VariantClear in invoke will call IDispatch_Release */
1373
1374     return invoke(pView, "Modify", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY);
1375 }
1376
1377 static HRESULT View_Close(IDispatch *pView)
1378 {
1379     VARIANT varresult;
1380     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1381     return invoke(pView, "Close", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY);
1382 }
1383
1384 static HRESULT Record_FieldCountGet(IDispatch *pRecord, int *pFieldCount)
1385 {
1386     VARIANT varresult;
1387     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1388     HRESULT hr = invoke(pRecord, "FieldCount", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1389     *pFieldCount = V_I4(&varresult);
1390     VariantClear(&varresult);
1391     return hr;
1392 }
1393
1394 static HRESULT Record_StringDataGet(IDispatch *pRecord, int iField, LPWSTR szString)
1395 {
1396     VARIANT varresult;
1397     VARIANTARG vararg[1];
1398     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1399     HRESULT hr;
1400
1401     VariantInit(&vararg[0]);
1402     V_VT(&vararg[0]) = VT_I4;
1403     V_I4(&vararg[0]) = iField;
1404
1405     hr = invoke(pRecord, "StringData", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR);
1406     if (V_BSTR(&varresult)) lstrcpyW(szString, V_BSTR(&varresult));
1407     VariantClear(&varresult);
1408     return hr;
1409 }
1410
1411 static HRESULT Record_StringDataPut(IDispatch *pRecord, int iField, LPCWSTR szString)
1412 {
1413     VARIANT varresult;
1414     VARIANTARG vararg[2];
1415     DISPID dispid = DISPID_PROPERTYPUT;
1416     DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};
1417
1418     VariantInit(&vararg[1]);
1419     V_VT(&vararg[1]) = VT_I4;
1420     V_I4(&vararg[1]) = iField;
1421     VariantInit(&vararg[0]);
1422     V_VT(&vararg[0]) = VT_BSTR;
1423     V_BSTR(&vararg[0]) = SysAllocString(szString);
1424
1425     return invoke(pRecord, "StringData", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
1426 }
1427
1428 static HRESULT Record_IntegerDataGet(IDispatch *pRecord, int iField, int *pValue)
1429 {
1430     VARIANT varresult;
1431     VARIANTARG vararg[1];
1432     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1433     HRESULT hr;
1434
1435     VariantInit(&vararg[0]);
1436     V_VT(&vararg[0]) = VT_I4;
1437     V_I4(&vararg[0]) = iField;
1438
1439     hr = invoke(pRecord, "IntegerData", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1440     *pValue = V_I4(&varresult);
1441     VariantClear(&varresult);
1442     return hr;
1443 }
1444
1445 static HRESULT Record_IntegerDataPut(IDispatch *pRecord, int iField, int iValue)
1446 {
1447     VARIANT varresult;
1448     VARIANTARG vararg[2];
1449     DISPID dispid = DISPID_PROPERTYPUT;
1450     DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};
1451
1452     VariantInit(&vararg[1]);
1453     V_VT(&vararg[1]) = VT_I4;
1454     V_I4(&vararg[1]) = iField;
1455     VariantInit(&vararg[0]);
1456     V_VT(&vararg[0]) = VT_I4;
1457     V_I4(&vararg[0]) = iValue;
1458
1459     return invoke(pRecord, "IntegerData", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
1460 }
1461
1462 static HRESULT StringList__NewEnum(IDispatch *pList, IUnknown **ppEnumVARIANT)
1463 {
1464     VARIANT varresult;
1465     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1466     HRESULT hr = invoke(pList, "_NewEnum", DISPATCH_METHOD, &dispparams, &varresult, VT_UNKNOWN);
1467     *ppEnumVARIANT = V_UNKNOWN(&varresult);
1468     return hr;
1469 }
1470
1471 static HRESULT StringList_Item(IDispatch *pStringList, int iIndex, LPWSTR szString)
1472 {
1473     VARIANT varresult;
1474     VARIANTARG vararg[1];
1475     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1476     HRESULT hr;
1477
1478     VariantInit(&vararg[0]);
1479     V_VT(&vararg[0]) = VT_I4;
1480     V_I4(&vararg[0]) = iIndex;
1481
1482     hr = invoke(pStringList, "Item", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR);
1483     if (V_BSTR(&varresult)) lstrcpyW(szString, V_BSTR(&varresult));
1484     VariantClear(&varresult);
1485     return hr;
1486 }
1487
1488 static HRESULT StringList_Count(IDispatch *pStringList, int *pCount)
1489 {
1490     VARIANT varresult;
1491     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1492     HRESULT hr = invoke(pStringList, "Count", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1493     *pCount = V_I4(&varresult);
1494     VariantClear(&varresult);
1495     return hr;
1496 }
1497
1498 static HRESULT SummaryInfo_PropertyGet(IDispatch *pSummaryInfo, int pid, VARIANT *pVarResult, VARTYPE vtExpect)
1499 {
1500     VARIANTARG vararg[1];
1501     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1502
1503     VariantInit(&vararg[0]);
1504     V_VT(&vararg[0]) = VT_I4;
1505     V_I4(&vararg[0]) = pid;
1506     return invoke(pSummaryInfo, "Property", DISPATCH_PROPERTYGET, &dispparams, pVarResult, vtExpect);
1507 }
1508
1509 static HRESULT SummaryInfo_PropertyPut(IDispatch *pSummaryInfo, int pid, VARIANT *pVariant)
1510 {
1511     VARIANT varresult;
1512     VARIANTARG vararg[2];
1513     DISPID dispid = DISPID_PROPERTYPUT;
1514     DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};
1515
1516     VariantInit(&vararg[1]);
1517     V_VT(&vararg[1]) = VT_I4;
1518     V_I4(&vararg[1]) = pid;
1519     VariantInit(&vararg[0]);
1520     VariantCopyInd(vararg, pVariant);
1521
1522     return invoke(pSummaryInfo, "Property", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
1523 }
1524
1525 static HRESULT SummaryInfo_PropertyCountGet(IDispatch *pSummaryInfo, int *pCount)
1526 {
1527     VARIANT varresult;
1528     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1529     HRESULT hr;
1530
1531     hr = invoke(pSummaryInfo, "PropertyCount", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1532     *pCount = V_I4(&varresult);
1533     VariantClear(&varresult);
1534     return hr;
1535 }
1536
1537 /* Test the various objects */
1538
1539 #define TEST_SUMMARYINFO_PROPERTIES_MODIFIED 4
1540
1541 static void test_SummaryInfo(IDispatch *pSummaryInfo, const msi_summary_info *info, int num_info, BOOL readonly)
1542 {
1543     static const WCHAR szPropertyException[] = { 'P','r','o','p','e','r','t','y',',','P','i','d',0 };
1544     static const WCHAR szTitle[] = { 'T','i','t','l','e',0 };
1545     VARIANT varresult, var;
1546     SYSTEMTIME st;
1547     HRESULT hr;
1548     int j;
1549
1550     /* SummaryInfo::PropertyCount */
1551     hr = SummaryInfo_PropertyCountGet(pSummaryInfo, &j);
1552     ok(hr == S_OK, "SummaryInfo_PropertyCount failed, hresult 0x%08x\n", hr);
1553     ok(j == num_info, "SummaryInfo_PropertyCount returned %d, expected %d\n", j, num_info);
1554
1555     /* SummaryInfo::Property, get for properties we have set */
1556     for (j = 0; j < num_info; j++)
1557     {
1558         const msi_summary_info *entry = &info[j];
1559
1560         int vt = entry->datatype;
1561         if (vt == VT_LPSTR) vt = VT_BSTR;
1562         else if (vt == VT_FILETIME) vt = VT_DATE;
1563         else if (vt == VT_I2) vt = VT_I4;
1564
1565         hr = SummaryInfo_PropertyGet(pSummaryInfo, entry->property, &varresult, vt);
1566         ok(hr == S_OK, "SummaryInfo_Property (pid %d) failed, hresult 0x%08x\n", entry->property, hr);
1567         if (V_VT(&varresult) != vt)
1568             skip("Skipping property tests due to type mismatch\n");
1569         else if (vt == VT_I4)
1570             ok(V_I4(&varresult) == entry->iValue, "SummaryInfo_Property (pid %d) I4 result expected to be %d, but was %d\n",
1571                entry->property, entry->iValue, V_I4(&varresult));
1572         else if (vt == VT_DATE)
1573         {
1574             FILETIME ft;
1575             DATE d;
1576
1577             FileTimeToLocalFileTime(entry->pftValue, &ft);
1578             FileTimeToSystemTime(&ft, &st);
1579             SystemTimeToVariantTime(&st, &d);
1580             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));
1581         }
1582         else if (vt == VT_BSTR)
1583         {
1584             ok_awplus("SummaryInfo_Property (pid %d) BSTR result expected to be %s, but was %s\n", entry->property, entry->szValue, V_BSTR(&varresult));
1585         }
1586         else
1587             skip("SummaryInfo_Property (pid %d) unhandled result type %d\n", entry->property, vt);
1588
1589         VariantClear(&varresult);
1590     }
1591
1592     /* SummaryInfo::Property, get; invalid arguments */
1593
1594     /* Invalid pids */
1595     hr = SummaryInfo_PropertyGet(pSummaryInfo, -1, &varresult, VT_EMPTY);
1596     ok(hr == DISP_E_EXCEPTION, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1597     ok_exception(hr, szPropertyException);
1598
1599     hr = SummaryInfo_PropertyGet(pSummaryInfo, 1000, &varresult, VT_EMPTY);
1600     ok(hr == DISP_E_EXCEPTION, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1601     ok_exception(hr, szPropertyException);
1602
1603     /* Unsupported pids */
1604     hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_DICTIONARY, &varresult, VT_EMPTY);
1605     ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1606
1607     hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_THUMBNAIL, &varresult, VT_EMPTY);
1608     ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1609
1610     /* Pids we have not set, one for each type */
1611     hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_CODEPAGE, &varresult, VT_EMPTY);
1612     ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1613
1614     hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_TITLE, &varresult, VT_EMPTY);
1615     ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1616
1617     hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_EDITTIME, &varresult, VT_EMPTY);
1618     ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1619
1620     hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_CHARCOUNT, &varresult, VT_EMPTY);
1621     ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1622
1623     if (!readonly)
1624     {
1625         /* SummaryInfo::Property, put; one for each type */
1626
1627         /* VT_I2 */
1628         VariantInit(&var);
1629         V_VT(&var) = VT_I2;
1630         V_I2(&var) = 1;
1631         hr = SummaryInfo_PropertyPut(pSummaryInfo, PID_CODEPAGE, &var);
1632         ok(hr == S_OK, "SummaryInfo_PropertyPut failed, hresult 0x%08x\n", hr);
1633
1634         hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_CODEPAGE, &varresult, VT_I4 /* NOT VT_I2 */);
1635         ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1636         ok(V_I2(&var) == V_I2(&varresult), "SummaryInfo_PropertyGet expected %d, but returned %d\n", V_I2(&var), V_I2(&varresult));
1637         VariantClear(&varresult);
1638         VariantClear(&var);
1639
1640         /* VT_BSTR */
1641         V_VT(&var) = VT_BSTR;
1642         V_BSTR(&var) = SysAllocString(szTitle);
1643         hr = SummaryInfo_PropertyPut(pSummaryInfo, PID_TITLE, &var);
1644         ok(hr == S_OK, "SummaryInfo_PropertyPut failed, hresult 0x%08x\n", hr);
1645
1646         hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_TITLE, &varresult, V_VT(&var));
1647         ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1648         ok_w2("SummaryInfo_PropertyGet expected %s, but returned %s\n", V_BSTR(&var), V_BSTR(&varresult));
1649         VariantClear(&varresult);
1650         VariantClear(&var);
1651
1652         /* VT_DATE */
1653         V_VT(&var) = VT_DATE;
1654         FileTimeToSystemTime(&systemtime, &st);
1655         SystemTimeToVariantTime(&st, &V_DATE(&var));
1656         hr = SummaryInfo_PropertyPut(pSummaryInfo, PID_LASTSAVE_DTM, &var);
1657         ok(hr == S_OK, "SummaryInfo_PropertyPut failed, hresult 0x%08x\n", hr);
1658
1659         hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_LASTSAVE_DTM, &varresult, V_VT(&var));
1660         ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1661         ok(V_DATE(&var) == V_DATE(&varresult), "SummaryInfo_PropertyGet expected %lf, but returned %lf\n", V_DATE(&var), V_DATE(&varresult));
1662         VariantClear(&varresult);
1663         VariantClear(&var);
1664
1665         /* VT_I4 */
1666         V_VT(&var) = VT_I4;
1667         V_I4(&var) = 1000;
1668         hr = SummaryInfo_PropertyPut(pSummaryInfo, PID_CHARCOUNT, &var);
1669         ok(hr == S_OK, "SummaryInfo_PropertyPut failed, hresult 0x%08x\n", hr);
1670
1671         hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_CHARCOUNT, &varresult, V_VT(&var));
1672         ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1673         ok(V_I4(&var) == V_I4(&varresult), "SummaryInfo_PropertyGet expected %d, but returned %d\n", V_I4(&var), V_I4(&varresult));
1674         VariantClear(&varresult);
1675         VariantClear(&var);
1676
1677         /* SummaryInfo::PropertyCount */
1678         hr = SummaryInfo_PropertyCountGet(pSummaryInfo, &j);
1679         ok(hr == S_OK, "SummaryInfo_PropertyCount failed, hresult 0x%08x\n", hr);
1680         ok(j == num_info+4, "SummaryInfo_PropertyCount returned %d, expected %d\n", j, num_info);
1681     }
1682 }
1683
1684 static void test_Database(IDispatch *pDatabase, BOOL readonly)
1685 {
1686     static 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 };
1687     static WCHAR szThree[] = { 'T','h','r','e','e',0 };
1688     static WCHAR szTwo[] = { 'T','w','o',0 };
1689     static WCHAR szStringDataField[] = { 'S','t','r','i','n','g','D','a','t','a',',','F','i','e','l','d',0 };
1690     static WCHAR szModifyModeRecord[] = { 'M','o','d','i','f','y',',','M','o','d','e',',','R','e','c','o','r','d',0 };
1691     IDispatch *pView = NULL, *pSummaryInfo = NULL;
1692     HRESULT hr;
1693
1694     hr = Database_OpenView(pDatabase, szSql, &pView);
1695     ok(hr == S_OK, "Database_OpenView failed, hresult 0x%08x\n", hr);
1696     if (hr == S_OK)
1697     {
1698         IDispatch *pRecord = NULL;
1699         WCHAR szString[MAX_PATH];
1700
1701         /* View::Execute */
1702         hr = View_Execute(pView, NULL);
1703         ok(hr == S_OK, "View_Execute failed, hresult 0x%08x\n", hr);
1704
1705         /* View::Fetch */
1706         hr = View_Fetch(pView, &pRecord);
1707         ok(hr == S_OK, "View_Fetch failed, hresult 0x%08x\n", hr);
1708         ok(pRecord != NULL, "View_Fetch should not have returned NULL record\n");
1709         if (pRecord)
1710         {
1711             /* Record::StringDataGet */
1712             memset(szString, 0, sizeof(szString));
1713             hr = Record_StringDataGet(pRecord, 1, szString);
1714             ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr);
1715             ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szThree);
1716
1717             /* Record::StringDataPut with correct index */
1718             hr = Record_StringDataPut(pRecord, 1, szTwo);
1719             ok(hr == S_OK, "Record_StringDataPut failed, hresult 0x%08x\n", hr);
1720
1721             /* Record::StringDataGet */
1722             memset(szString, 0, sizeof(szString));
1723             hr = Record_StringDataGet(pRecord, 1, szString);
1724             ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr);
1725             ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szTwo);
1726
1727             /* Record::StringDataPut with incorrect index */
1728             hr = Record_StringDataPut(pRecord, -1, szString);
1729             ok(hr == DISP_E_EXCEPTION, "Record_StringDataPut failed, hresult 0x%08x\n", hr);
1730             ok_exception(hr, szStringDataField);
1731
1732             /* View::Modify with incorrect parameters */
1733             hr = View_Modify(pView, -5, NULL);
1734             ok(hr == DISP_E_EXCEPTION, "View_Modify failed, hresult 0x%08x\n", hr);
1735             ok_exception(hr, szModifyModeRecord);
1736
1737             hr = View_Modify(pView, -5, pRecord);
1738             ok(hr == DISP_E_EXCEPTION, "View_Modify failed, hresult 0x%08x\n", hr);
1739             ok_exception(hr, szModifyModeRecord);
1740
1741             hr = View_Modify(pView, MSIMODIFY_REFRESH, NULL);
1742             ok(hr == DISP_E_EXCEPTION, "View_Modify failed, hresult 0x%08x\n", hr);
1743             ok_exception(hr, szModifyModeRecord);
1744
1745             hr = View_Modify(pView, MSIMODIFY_REFRESH, pRecord);
1746             ok(hr == S_OK, "View_Modify failed, hresult 0x%08x\n", hr);
1747
1748             /* Record::StringDataGet, confirm that the record is back to its unmodified value */
1749             memset(szString, 0, sizeof(szString));
1750             hr = Record_StringDataGet(pRecord, 1, szString);
1751             ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr);
1752             todo_wine ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szThree);
1753
1754             IDispatch_Release(pRecord);
1755         }
1756
1757         /* View::Fetch */
1758         hr = View_Fetch(pView, &pRecord);
1759         ok(hr == S_OK, "View_Fetch failed, hresult 0x%08x\n", hr);
1760         ok(pRecord != NULL, "View_Fetch should not have returned NULL record\n");
1761         if (pRecord)
1762         {
1763             /* Record::StringDataGet */
1764             memset(szString, 0, sizeof(szString));
1765             hr = Record_StringDataGet(pRecord, 1, szString);
1766             ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr);
1767             ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szTwo);
1768
1769             IDispatch_Release(pRecord);
1770         }
1771
1772         /* View::Fetch */
1773         hr = View_Fetch(pView, &pRecord);
1774         ok(hr == S_OK, "View_Fetch failed, hresult 0x%08x\n", hr);
1775         ok(pRecord == NULL, "View_Fetch should have returned NULL record\n");
1776         if (pRecord)
1777             IDispatch_Release(pRecord);
1778
1779         /* View::Close */
1780         hr = View_Close(pView);
1781         ok(hr == S_OK, "View_Close failed, hresult 0x%08x\n", hr);
1782
1783         IDispatch_Release(pView);
1784     }
1785
1786     /* Database::SummaryInformation */
1787     hr = Database_SummaryInformation(pDatabase, TEST_SUMMARYINFO_PROPERTIES_MODIFIED, &pSummaryInfo);
1788     ok(hr == S_OK, "Database_SummaryInformation failed, hresult 0x%08x\n", hr);
1789     ok(pSummaryInfo != NULL, "Database_SummaryInformation should not have returned NULL record\n");
1790     if (pSummaryInfo)
1791     {
1792         test_SummaryInfo(pSummaryInfo, summary_info, sizeof(summary_info)/sizeof(msi_summary_info), readonly);
1793         IDispatch_Release(pSummaryInfo);
1794     }
1795 }
1796
1797 static void test_Session(IDispatch *pSession)
1798 {
1799     static WCHAR szProductName[] = { 'P','r','o','d','u','c','t','N','a','m','e',0 };
1800     static WCHAR szOne[] = { 'O','n','e',0 };
1801     static WCHAR szOneStateFalse[] = { '!','O','n','e','>','0',0 };
1802     static WCHAR szOneStateTrue[] = { '!','O','n','e','=','-','1',0 };
1803     static WCHAR szOneActionFalse[] = { '$','O','n','e','=','-','1',0 };
1804     static WCHAR szOneActionTrue[] = { '$','O','n','e','>','0',0 };
1805     static WCHAR szCostInitialize[] = { 'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0 };
1806     static WCHAR szEmpty[] = { 0 };
1807     static WCHAR szEquals[] = { '=',0 };
1808     static WCHAR szPropertyName[] = { 'P','r','o','p','e','r','t','y',',','N','a','m','e',0 };
1809     static WCHAR szModeFlag[] = { 'M','o','d','e',',','F','l','a','g',0 };
1810     WCHAR stringw[MAX_PATH];
1811     CHAR string[MAX_PATH];
1812     UINT len;
1813     BOOL bool;
1814     int myint;
1815     IDispatch *pDatabase = NULL, *pInst = NULL, *record = NULL;
1816     ULONG refs_before, refs_after;
1817     HRESULT hr;
1818
1819     /* Session::Installer */
1820     hr = Session_Installer(pSession, &pInst);
1821     ok(hr == S_OK, "Session_Installer failed, hresult 0x%08x\n", hr);
1822     ok(pInst != NULL, "Session_Installer returned NULL IDispatch pointer\n");
1823     ok(pInst == pInstaller, "Session_Installer does not match Installer instance from CoCreateInstance\n");
1824     refs_before = IDispatch_AddRef(pInst);
1825
1826     hr = Session_Installer(pSession, &pInst);
1827     ok(hr == S_OK, "Session_Installer failed, hresult 0x%08x\n", hr);
1828     ok(pInst != NULL, "Session_Installer returned NULL IDispatch pointer\n");
1829     ok(pInst == pInstaller, "Session_Installer does not match Installer instance from CoCreateInstance\n");
1830     refs_after = IDispatch_Release(pInst);
1831     ok(refs_before == refs_after, "got %u and %u\n", refs_before, refs_after);
1832
1833     /* Session::Property, get */
1834     memset(stringw, 0, sizeof(stringw));
1835     hr = Session_PropertyGet(pSession, szProductName, stringw);
1836     ok(hr == S_OK, "Session_PropertyGet failed, hresult 0x%08x\n", hr);
1837     if (lstrcmpW(stringw, szMSITEST) != 0)
1838     {
1839         len = WideCharToMultiByte(CP_ACP, 0, stringw, -1, string, MAX_PATH, NULL, NULL);
1840         ok(len, "WideCharToMultiByteChar returned error %d\n", GetLastError());
1841         ok(0, "Property \"ProductName\" expected to be \"MSITEST\" but was \"%s\"\n", string);
1842     }
1843
1844     /* Session::Property, put */
1845     hr = Session_PropertyPut(pSession, szProductName, szProductName);
1846     ok(hr == S_OK, "Session_PropertyPut failed, hresult 0x%08x\n", hr);
1847     memset(stringw, 0, sizeof(stringw));
1848     hr = Session_PropertyGet(pSession, szProductName, stringw);
1849     ok(hr == S_OK, "Session_PropertyGet failed, hresult 0x%08x\n", hr);
1850     if (lstrcmpW(stringw, szProductName) != 0)
1851     {
1852         len = WideCharToMultiByte(CP_ACP, 0, stringw, -1, string, MAX_PATH, NULL, NULL);
1853         ok(len, "WideCharToMultiByteChar returned error %d\n", GetLastError());
1854         ok(0, "Property \"ProductName\" expected to be \"ProductName\" but was \"%s\"\n", string);
1855     }
1856
1857     /* Try putting a property using empty property identifier */
1858     hr = Session_PropertyPut(pSession, szEmpty, szProductName);
1859     ok(hr == DISP_E_EXCEPTION, "Session_PropertyPut failed, hresult 0x%08x\n", hr);
1860     ok_exception(hr, szPropertyName);
1861
1862     /* Try putting a property using illegal property identifier */
1863     hr = Session_PropertyPut(pSession, szEquals, szProductName);
1864     ok(hr == S_OK, "Session_PropertyPut failed, hresult 0x%08x\n", hr);
1865
1866     /* Session::Language, get */
1867     hr = Session_LanguageGet(pSession, &len);
1868     ok(hr == S_OK, "Session_LanguageGet failed, hresult 0x%08x\n", hr);
1869     /* Not sure how to check the language is correct */
1870
1871     /* Session::Mode, get */
1872     hr = Session_ModeGet(pSession, MSIRUNMODE_REBOOTATEND, &bool);
1873     ok(hr == S_OK, "Session_ModeGet failed, hresult 0x%08x\n", hr);
1874     ok(!bool, "Reboot at end session mode is %d\n", bool);
1875
1876     hr = Session_ModeGet(pSession, MSIRUNMODE_MAINTENANCE, &bool);
1877     ok(hr == S_OK, "Session_ModeGet failed, hresult 0x%08x\n", hr);
1878     ok(!bool, "Maintenance mode is %d\n", bool);
1879
1880     /* Session::Mode, put */
1881     hr = Session_ModePut(pSession, MSIRUNMODE_REBOOTATEND, TRUE);
1882     ok(hr == S_OK, "Session_ModePut failed, hresult 0x%08x\n", hr);
1883     hr = Session_ModeGet(pSession, MSIRUNMODE_REBOOTATEND, &bool);
1884     ok(hr == S_OK, "Session_ModeGet failed, hresult 0x%08x\n", hr);
1885     ok(bool, "Reboot at end session mode is %d, expected 1\n", bool);
1886     hr = Session_ModePut(pSession, MSIRUNMODE_REBOOTATEND, FALSE);  /* set it again so we don't reboot */
1887     ok(hr == S_OK, "Session_ModePut failed, hresult 0x%08x\n", hr);
1888
1889     hr = Session_ModePut(pSession, MSIRUNMODE_REBOOTNOW, TRUE);
1890     ok(hr == S_OK, "Session_ModePut failed, hresult 0x%08x\n", hr);
1891     if (hr == DISP_E_EXCEPTION) ok_exception(hr, szModeFlag);
1892
1893     hr = Session_ModeGet(pSession, MSIRUNMODE_REBOOTNOW, &bool);
1894     ok(hr == S_OK, "Session_ModeGet failed, hresult 0x%08x\n", hr);
1895     ok(bool, "Reboot now mode is %d, expected 1\n", bool);
1896
1897     hr = Session_ModePut(pSession, MSIRUNMODE_REBOOTNOW, FALSE);  /* set it again so we don't reboot */
1898     ok(hr == S_OK, "Session_ModePut failed, hresult 0x%08x\n", hr);
1899     if (hr == DISP_E_EXCEPTION) ok_exception(hr, szModeFlag);
1900
1901     hr = Session_ModePut(pSession, MSIRUNMODE_MAINTENANCE, TRUE);
1902     ok(hr == DISP_E_EXCEPTION, "Session_ModePut failed, hresult 0x%08x\n", hr);
1903     ok_exception(hr, szModeFlag);
1904
1905     /* Session::Database, get */
1906     hr = Session_Database(pSession, &pDatabase);
1907     ok(hr == S_OK, "Session_Database failed, hresult 0x%08x\n", hr);
1908     if (hr == S_OK)
1909     {
1910         test_Database(pDatabase, TRUE);
1911         IDispatch_Release(pDatabase);
1912     }
1913
1914     /* Session::EvaluateCondition */
1915     hr = Session_EvaluateCondition(pSession, NULL, &myint);
1916     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1917     ok(myint == MSICONDITION_NONE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1918
1919     hr = Session_EvaluateCondition(pSession, szEmpty, &myint);
1920     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1921     ok(myint == MSICONDITION_NONE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1922
1923     hr = Session_EvaluateCondition(pSession, szEquals, &myint);
1924     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1925     ok(myint == MSICONDITION_ERROR, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1926
1927     /* Session::DoAction(CostInitialize) must occur before the next statements */
1928     hr = Session_DoAction(pSession, szCostInitialize, &myint);
1929     ok(hr == S_OK, "Session_DoAction failed, hresult 0x%08x\n", hr);
1930     ok(myint == IDOK, "DoAction(CostInitialize) returned %d, %d expected\n", myint, IDOK);
1931
1932     /* Session::SetInstallLevel */
1933     hr = Session_SetInstallLevel(pSession, INSTALLLEVEL_MINIMUM);
1934     ok(hr == S_OK, "Session_SetInstallLevel failed, hresult 0x%08x\n", hr);
1935
1936     /* Session::FeatureCurrentState, get */
1937     hr = Session_FeatureCurrentState(pSession, szOne, &myint);
1938     ok(hr == S_OK, "Session_FeatureCurrentState failed, hresult 0x%08x\n", hr);
1939     ok(myint == INSTALLSTATE_UNKNOWN, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1940
1941     /* Session::Message */
1942     hr = Installer_CreateRecord(0, &record);
1943     ok(hr == S_OK, "Installer_CreateRecord failed: %08x\n", hr);
1944     hr = Session_Message(pSession, INSTALLMESSAGE_INFO, record, &myint);
1945     ok(hr == S_OK, "Session_Message failed: %08x\n", hr);
1946     ok(myint == 0, "Session_Message returned %x\n", myint);
1947
1948     /* Session::EvaluateCondition */
1949     hr = Session_EvaluateCondition(pSession, szOneStateFalse, &myint);
1950     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1951     ok(myint == MSICONDITION_FALSE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1952
1953     hr = Session_EvaluateCondition(pSession, szOneStateTrue, &myint);
1954     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1955     ok(myint == MSICONDITION_TRUE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1956
1957     /* Session::FeatureRequestState, put */
1958     hr = Session_FeatureRequestStatePut(pSession, szOne, INSTALLSTATE_ADVERTISED);
1959     ok(hr == S_OK, "Session_FeatureRequestStatePut failed, hresult 0x%08x\n", hr);
1960     hr = Session_FeatureRequestStateGet(pSession, szOne, &myint);
1961     ok(hr == S_OK, "Session_FeatureRequestStateGet failed, hresult 0x%08x\n", hr);
1962     ok(myint == INSTALLSTATE_ADVERTISED, "Feature request state was %d but expected %d\n", myint, INSTALLSTATE_ADVERTISED);
1963
1964     /* Session::EvaluateCondition */
1965     hr = Session_EvaluateCondition(pSession, szOneActionFalse, &myint);
1966     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1967     ok(myint == MSICONDITION_FALSE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1968
1969     hr = Session_EvaluateCondition(pSession, szOneActionTrue, &myint);
1970     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1971     ok(myint == MSICONDITION_TRUE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1972 }
1973
1974 /* delete key and all its subkeys */
1975 static DWORD delete_key( HKEY hkey )
1976 {
1977     char name[MAX_PATH];
1978     DWORD ret;
1979
1980     while (!(ret = RegEnumKeyA(hkey, 0, name, sizeof(name))))
1981     {
1982         HKEY tmp;
1983         if (!(ret = RegOpenKeyExA( hkey, name, 0, KEY_ENUMERATE_SUB_KEYS, &tmp )))
1984         {
1985             ret = delete_key( tmp );
1986             RegCloseKey( tmp );
1987         }
1988         if (ret) break;
1989     }
1990     if (ret != ERROR_NO_MORE_ITEMS) return ret;
1991     RegDeleteKeyA( hkey, "" );
1992     return 0;
1993 }
1994
1995 static void test_Installer_RegistryValue(void)
1996 {
1997     static const DWORD qw[2] = { 0x12345678, 0x87654321 };
1998     static const WCHAR szKey[] = { 'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\','T','e','s','t',0 };
1999     static const WCHAR szOne[] = { 'O','n','e',0 };
2000     static const WCHAR szTwo[] = { 'T','w','o',0 };
2001     static const WCHAR szThree[] = { 'T','h','r','e','e',0 };
2002     static const WCHAR szREG_BINARY[] = { '(','R','E','G','_','B','I','N','A','R','Y',')',0 };
2003     static const WCHAR szFour[] = { 'F','o','u','r',0 };
2004     static const WCHAR szExpand[] = { '%','M','S','I','T','E','S','T','%',0 };
2005     static const WCHAR szFive[] = { 'F','i','v','e',0,'H','i',0,0 };
2006     static const WCHAR szFiveHi[] = { 'F','i','v','e','\n','H','i',0 };
2007     static const WCHAR szSix[] = { 'S','i','x',0 };
2008     static const WCHAR szREG_[] = { '(','R','E','G','_',']',0 };
2009     static const WCHAR szREG_2[] = { '(','R','E','G','_','?','?',')',0 };
2010     static const WCHAR szSeven[] = { 'S','e','v','e','n',0 };
2011     static const WCHAR szEight[] = { 'E','i','g','h','t',0 };
2012     static const WCHAR szBlank[] = { 0 };
2013     VARIANT varresult;
2014     VARIANTARG vararg;
2015     WCHAR szString[MAX_PATH];
2016     HKEY hkey, hkey_sub;
2017     HKEY curr_user = (HKEY)1;
2018     HRESULT hr;
2019     BOOL bRet;
2020     LONG lRet;
2021
2022     /* Delete keys */
2023     SetLastError(0xdeadbeef);
2024     lRet = RegOpenKeyW( HKEY_CURRENT_USER, szKey, &hkey );
2025     if (!lRet && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
2026     {
2027         win_skip("Needed W-functions are not implemented\n");
2028         return;
2029     }
2030     if (!lRet)
2031         delete_key( hkey );
2032
2033     /* Does our key exist? Shouldn't; check with all three possible value parameter types */
2034     hr = Installer_RegistryValueE(curr_user, szKey, &bRet);
2035     ok(hr == S_OK, "Installer_RegistryValueE failed, hresult 0x%08x\n", hr);
2036     ok(!bRet, "Registry key expected to not exist, but Installer_RegistryValue claims it does\n");
2037
2038     memset(szString, 0, sizeof(szString));
2039     hr = Installer_RegistryValueW(curr_user, szKey, NULL, szString);
2040     ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2041
2042     memset(szString, 0, sizeof(szString));
2043     hr = Installer_RegistryValueI(curr_user, szKey, 0, szString, VT_BSTR);
2044     ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2045
2046     /* Create key */
2047     ok(!RegCreateKeyW( HKEY_CURRENT_USER, szKey, &hkey ), "RegCreateKeyW failed\n");
2048
2049     ok(!RegSetValueExW(hkey,szOne,0,REG_SZ, (const BYTE *)szOne, sizeof(szOne)),
2050         "RegSetValueExW failed\n");
2051     ok(!RegSetValueExW(hkey,szTwo,0,REG_DWORD, (const BYTE *)qw, 4),
2052         "RegSetValueExW failed\n");
2053     ok(!RegSetValueExW(hkey,szThree,0,REG_BINARY, (const BYTE *)qw, 4),
2054         "RegSetValueExW failed\n");
2055     bRet = SetEnvironmentVariableA("MSITEST", "Four");
2056     ok(bRet, "SetEnvironmentVariableA failed %d\n", GetLastError());
2057     ok(!RegSetValueExW(hkey,szFour,0,REG_EXPAND_SZ, (const BYTE *)szExpand, sizeof(szExpand)),
2058         "RegSetValueExW failed\n");
2059     ok(!RegSetValueExW(hkey,szFive,0,REG_MULTI_SZ, (const BYTE *)szFive, sizeof(szFive)),
2060         "RegSetValueExW failed\n");
2061     ok(!RegSetValueExW(hkey,szSix,0,REG_QWORD, (const BYTE *)qw, 8),
2062         "RegSetValueExW failed\n");
2063     ok(!RegSetValueExW(hkey,szSeven,0,REG_NONE, NULL, 0),
2064         "RegSetValueExW failed\n");
2065
2066     ok(!RegSetValueExW(hkey,NULL,0,REG_SZ, (const BYTE *)szOne, sizeof(szOne)),
2067         "RegSetValueExW failed\n");
2068
2069     ok(!RegCreateKeyW( hkey, szEight, &hkey_sub ), "RegCreateKeyW failed\n");
2070
2071     /* Does our key exist? It should, and make sure we retrieve the correct default value */
2072     bRet = FALSE;
2073     hr = Installer_RegistryValueE(curr_user, szKey, &bRet);
2074     ok(hr == S_OK, "Installer_RegistryValueE failed, hresult 0x%08x\n", hr);
2075     ok(bRet, "Registry key expected to exist, but Installer_RegistryValue claims it does not\n");
2076
2077     memset(szString, 0, sizeof(szString));
2078     hr = Installer_RegistryValueW(curr_user, szKey, NULL, szString);
2079     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2080     ok_w2("Default registry value \"%s\" does not match expected \"%s\"\n", szString, szOne);
2081
2082     /* Ask for the value of a nonexistent key */
2083     memset(szString, 0, sizeof(szString));
2084     hr = Installer_RegistryValueW(curr_user, szKey, szExpand, szString);
2085     ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2086
2087     /* Get values of keys */
2088     memset(szString, 0, sizeof(szString));
2089     hr = Installer_RegistryValueW(curr_user, szKey, szOne, szString);
2090     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2091     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szOne);
2092
2093     VariantInit(&vararg);
2094     V_VT(&vararg) = VT_BSTR;
2095     V_BSTR(&vararg) = SysAllocString(szTwo);
2096     hr = Installer_RegistryValue(curr_user, szKey, vararg, &varresult, VT_I4);
2097     ok(hr == S_OK, "Installer_RegistryValue failed, hresult 0x%08x\n", hr);
2098     ok(V_I4(&varresult) == 305419896, "Registry value %d does not match expected value\n", V_I4(&varresult));
2099     VariantClear(&varresult);
2100
2101     memset(szString, 0, sizeof(szString));
2102     hr = Installer_RegistryValueW(curr_user, szKey, szThree, szString);
2103     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2104     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szREG_BINARY);
2105
2106     memset(szString, 0, sizeof(szString));
2107     hr = Installer_RegistryValueW(curr_user, szKey, szFour, szString);
2108     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2109     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szFour);
2110
2111     /* Vista does not NULL-terminate this case */
2112     memset(szString, 0, sizeof(szString));
2113     hr = Installer_RegistryValueW(curr_user, szKey, szFive, szString);
2114     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2115     ok_w2n("Registry value \"%s\" does not match expected \"%s\"\n",
2116            szString, szFiveHi, lstrlenW(szFiveHi));
2117
2118     memset(szString, 0, sizeof(szString));
2119     hr = Installer_RegistryValueW(curr_user, szKey, szSix, szString);
2120     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2121     ok(!lstrcmpW(szString, szREG_2) || broken(!lstrcmpW(szString, szREG_)),
2122        "Registry value does not match\n");
2123
2124     VariantInit(&vararg);
2125     V_VT(&vararg) = VT_BSTR;
2126     V_BSTR(&vararg) = SysAllocString(szSeven);
2127     hr = Installer_RegistryValue(curr_user, szKey, vararg, &varresult, VT_EMPTY);
2128     ok(hr == S_OK, "Installer_RegistryValue failed, hresult 0x%08x\n", hr);
2129
2130     /* Get string class name for the key */
2131     memset(szString, 0, sizeof(szString));
2132     hr = Installer_RegistryValueI(curr_user, szKey, 0, szString, VT_BSTR);
2133     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2134     ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szBlank);
2135
2136     /* Get name of a value by positive number (RegEnumValue like), valid index */
2137     memset(szString, 0, sizeof(szString));
2138     hr = Installer_RegistryValueI(curr_user, szKey, 2, szString, VT_BSTR);
2139     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2140     /* RegEnumValue order seems different on wine */
2141     todo_wine ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szTwo);
2142
2143     /* Get name of a value by positive number (RegEnumValue like), invalid index */
2144     memset(szString, 0, sizeof(szString));
2145     hr = Installer_RegistryValueI(curr_user, szKey, 10, szString, VT_EMPTY);
2146     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2147
2148     /* Get name of a subkey by negative number (RegEnumValue like), valid index */
2149     memset(szString, 0, sizeof(szString));
2150     hr = Installer_RegistryValueI(curr_user, szKey, -1, szString, VT_BSTR);
2151     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2152     ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szEight);
2153
2154     /* Get name of a subkey by negative number (RegEnumValue like), invalid index */
2155     memset(szString, 0, sizeof(szString));
2156     hr = Installer_RegistryValueI(curr_user, szKey, -10, szString, VT_EMPTY);
2157     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2158
2159     /* clean up */
2160     delete_key(hkey);
2161 }
2162
2163 static void test_Installer_Products(BOOL bProductInstalled)
2164 {
2165     WCHAR szString[MAX_PATH];
2166     HRESULT hr;
2167     int idx;
2168     IUnknown *pUnk = NULL;
2169     IEnumVARIANT *pEnum = NULL;
2170     VARIANT var;
2171     ULONG celt;
2172     int iCount, iValue;
2173     IDispatch *pStringList = NULL;
2174     BOOL bProductFound = FALSE;
2175
2176     /* Installer::Products */
2177     hr = Installer_Products(&pStringList);
2178     ok(hr == S_OK, "Installer_Products failed, hresult 0x%08x\n", hr);
2179     if (hr == S_OK)
2180     {
2181         /* StringList::_NewEnum */
2182         hr = StringList__NewEnum(pStringList, &pUnk);
2183         ok(hr == S_OK, "StringList_NewEnum failed, hresult 0x%08x\n", hr);
2184         if (hr == S_OK)
2185         {
2186             hr = IUnknown_QueryInterface(pUnk, &IID_IEnumVARIANT, (void **)&pEnum);
2187             ok (hr == S_OK, "IUnknown::QueryInterface returned 0x%08x\n", hr);
2188         }
2189         if (!pEnum)
2190             skip("IEnumVARIANT tests\n");
2191
2192         /* StringList::Count */
2193         hr = StringList_Count(pStringList, &iCount);
2194         ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr);
2195
2196         for (idx=0; idx<iCount; idx++)
2197         {
2198             /* StringList::Item */
2199             memset(szString, 0, sizeof(szString));
2200             hr = StringList_Item(pStringList, idx, szString);
2201             ok(hr == S_OK, "StringList_Item failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);
2202
2203             if (hr == S_OK)
2204             {
2205                 /* Installer::ProductState */
2206                 hr = Installer_ProductState(szString, &iValue);
2207                 ok(hr == S_OK, "Installer_ProductState failed, hresult 0x%08x\n", hr);
2208                 if (hr == S_OK)
2209                     ok(iValue == INSTALLSTATE_DEFAULT || iValue == INSTALLSTATE_ADVERTISED, "Installer_ProductState returned %d, expected %d or %d\n", iValue, INSTALLSTATE_DEFAULT, INSTALLSTATE_ADVERTISED);
2210
2211                 /* Not found our product code yet? Check */
2212                 if (!bProductFound && !lstrcmpW(szString, szProductCode))
2213                     bProductFound = TRUE;
2214
2215                 /* IEnumVARIANT::Next */
2216                 if (pEnum)
2217                 {
2218                     hr = IEnumVARIANT_Next(pEnum, 1, &var, &celt);
2219                     ok(hr == S_OK, "IEnumVARIANT_Next failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);
2220                     ok(celt == 1, "%d items were retrieved, expected 1\n", celt);
2221                     ok(V_VT(&var) == VT_BSTR, "IEnumVARIANT_Next returned variant of type %d, expected %d\n", V_VT(&var), VT_BSTR);
2222                     ok_w2("%s returned by StringList_Item does not match %s returned by IEnumVARIANT_Next\n", szString, V_BSTR(&var));
2223                     VariantClear(&var);
2224                 }
2225             }
2226         }
2227
2228         if (bProductInstalled)
2229         {
2230             ok(bProductInstalled == bProductFound, "Product expected to %s installed but product code was %s\n",
2231                bProductInstalled ? "be" : "not be",
2232                bProductFound ? "found" : "not found");
2233         }
2234
2235         if (pEnum)
2236         {
2237             IEnumVARIANT *pEnum2 = NULL;
2238
2239             if (0) /* Crashes on Windows XP */
2240             {
2241                 /* IEnumVARIANT::Clone, NULL pointer */
2242                 IEnumVARIANT_Clone(pEnum, NULL);
2243             }
2244
2245             /* IEnumVARIANT::Clone */
2246             hr = IEnumVARIANT_Clone(pEnum, &pEnum2);
2247             ok(hr == S_OK, "IEnumVARIANT_Clone failed, hresult 0x%08x\n", hr);
2248             if (hr == S_OK)
2249             {
2250                 /* IEnumVARIANT::Clone is supposed to save the position, but it actually just goes back to the beginning */
2251
2252                 /* IEnumVARIANT::Next of the clone */
2253                 if (iCount)
2254                 {
2255                     hr = IEnumVARIANT_Next(pEnum2, 1, &var, &celt);
2256                     ok(hr == S_OK, "IEnumVARIANT_Next failed, hresult 0x%08x\n", hr);
2257                     ok(celt == 1, "%d items were retrieved, expected 0\n", celt);
2258                     ok(V_VT(&var) == VT_BSTR, "IEnumVARIANT_Next returned variant of type %d, expected %d\n", V_VT(&var), VT_BSTR);
2259                     VariantClear(&var);
2260                 }
2261                 else
2262                     skip("IEnumVARIANT::Next of clone will not return success with 0 products\n");
2263
2264                 IEnumVARIANT_Release(pEnum2);
2265             }
2266
2267             /* IEnumVARIANT::Skip should fail */
2268             hr = IEnumVARIANT_Skip(pEnum, 1);
2269             ok(hr == S_FALSE, "IEnumVARIANT_Skip failed, hresult 0x%08x\n", hr);
2270
2271             /* IEnumVARIANT::Next, NULL variant pointer */
2272             hr = IEnumVARIANT_Next(pEnum, 1, NULL, &celt);
2273             ok(hr == S_FALSE, "IEnumVARIANT_Next failed, hresult 0x%08x\n", hr);
2274             ok(celt == 0, "%d items were retrieved, expected 0\n", celt);
2275
2276             /* IEnumVARIANT::Next, should not return any more items */
2277             hr = IEnumVARIANT_Next(pEnum, 1, &var, &celt);
2278             ok(hr == S_FALSE, "IEnumVARIANT_Next failed, hresult 0x%08x\n", hr);
2279             ok(celt == 0, "%d items were retrieved, expected 0\n", celt);
2280             VariantClear(&var);
2281
2282             /* IEnumVARIANT::Reset */
2283             hr = IEnumVARIANT_Reset(pEnum);
2284             ok(hr == S_OK, "IEnumVARIANT_Reset failed, hresult 0x%08x\n", hr);
2285
2286             if (iCount)
2287             {
2288                 /* IEnumVARIANT::Skip to the last product */
2289                 hr = IEnumVARIANT_Skip(pEnum, iCount-1);
2290                 ok(hr == S_OK, "IEnumVARIANT_Skip failed, hresult 0x%08x\n", hr);
2291
2292                 /* IEnumVARIANT::Next should match the very last retrieved value, also makes sure it works with
2293                  * NULL celt pointer. */
2294                 hr = IEnumVARIANT_Next(pEnum, 1, &var, NULL);
2295                 ok(hr == S_OK, "IEnumVARIANT_Next failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);
2296                 ok(V_VT(&var) == VT_BSTR, "IEnumVARIANT_Next returned variant of type %d, expected %d\n", V_VT(&var), VT_BSTR);
2297                 ok_w2("%s returned by StringList_Item does not match %s returned by IEnumVARIANT_Next\n", szString, V_BSTR(&var));
2298                 VariantClear(&var);
2299             }
2300             else
2301                 skip("IEnumVARIANT::Skip impossible for 0 products\n");
2302         }
2303
2304         /* StringList::Item using an invalid index */
2305         memset(szString, 0, sizeof(szString));
2306         hr = StringList_Item(pStringList, iCount, szString);
2307         ok(hr == DISP_E_BADINDEX, "StringList_Item for an invalid index did not return DISP_E_BADINDEX, hresult 0x%08x\n", hr);
2308
2309         if (pEnum) IEnumVARIANT_Release(pEnum);
2310         if (pUnk) IUnknown_Release(pUnk);
2311         IDispatch_Release(pStringList);
2312     }
2313 }
2314
2315 /* Delete a registry subkey, including all its subkeys (RegDeleteKey does not work on keys with subkeys without
2316  * deleting the subkeys first) */
2317 static UINT delete_registry_key(HKEY hkeyParent, LPCSTR subkey, REGSAM access)
2318 {
2319     UINT ret;
2320     CHAR *string = NULL;
2321     HKEY hkey;
2322     DWORD dwSize;
2323
2324     ret = RegOpenKeyEx(hkeyParent, subkey, 0, access, &hkey);
2325     if (ret != ERROR_SUCCESS) return ret;
2326     ret = RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL);
2327     if (ret != ERROR_SUCCESS) return ret;
2328     if (!(string = HeapAlloc(GetProcessHeap(), 0, ++dwSize))) return ERROR_NOT_ENOUGH_MEMORY;
2329
2330     while (RegEnumKeyA(hkey, 0, string, dwSize) == ERROR_SUCCESS)
2331         delete_registry_key(hkey, string, access);
2332
2333     RegCloseKey(hkey);
2334     HeapFree(GetProcessHeap(), 0, string);
2335     delete_key_portable(hkeyParent, subkey, access);
2336     return ERROR_SUCCESS;
2337 }
2338
2339 /* Find a specific registry subkey at any depth within the given key and subkey and return its parent key. */
2340 static UINT find_registry_key(HKEY hkeyParent, LPCSTR subkey, LPCSTR findkey, REGSAM access, HKEY *phkey)
2341 {
2342     UINT ret;
2343     CHAR *string = NULL;
2344     int idx = 0;
2345     HKEY hkey;
2346     DWORD dwSize;
2347     BOOL found = FALSE;
2348
2349     *phkey = 0;
2350
2351     ret = RegOpenKeyEx(hkeyParent, subkey, 0, access, &hkey);
2352     if (ret != ERROR_SUCCESS) return ret;
2353     ret = RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL);
2354     if (ret != ERROR_SUCCESS) return ret;
2355     if (!(string = HeapAlloc(GetProcessHeap(), 0, ++dwSize))) return ERROR_NOT_ENOUGH_MEMORY;
2356
2357     while (!found &&
2358            RegEnumKeyA(hkey, idx++, string, dwSize) == ERROR_SUCCESS)
2359     {
2360         if (!strcmp(string, findkey))
2361         {
2362             *phkey = hkey;
2363             found = TRUE;
2364         }
2365         else if (find_registry_key(hkey, string, findkey, access, phkey) == ERROR_SUCCESS) found = TRUE;
2366     }
2367
2368     if (*phkey != hkey) RegCloseKey(hkey);
2369     HeapFree(GetProcessHeap(), 0, string);
2370     return (found ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND);
2371 }
2372
2373 static void test_Installer_InstallProduct(void)
2374 {
2375     HRESULT hr;
2376     CHAR path[MAX_PATH];
2377     WCHAR szString[MAX_PATH];
2378     LONG res;
2379     HKEY hkey;
2380     DWORD num, size, type;
2381     int iValue, iCount;
2382     IDispatch *pStringList = NULL;
2383     REGSAM access = KEY_ALL_ACCESS;
2384
2385     if (is_wow64)
2386         access |= KEY_WOW64_64KEY;
2387
2388     create_test_files();
2389
2390     /* Avoid an interactive dialog in case of insufficient privileges. */
2391     hr = Installer_UILevelPut(INSTALLUILEVEL_NONE);
2392     ok(hr == S_OK, "Expected UILevel property put invoke to return S_OK, got 0x%08x\n", hr);
2393
2394     /* Installer::InstallProduct */
2395     hr = Installer_InstallProduct(szMsifile, NULL);
2396     if (hr == DISP_E_EXCEPTION)
2397     {
2398         skip("InstallProduct failed, insufficient rights?\n");
2399         delete_test_files();
2400         return;
2401     }
2402     ok(hr == S_OK, "Installer_InstallProduct failed, hresult 0x%08x\n", hr);
2403
2404     /* Installer::ProductState for our product code, which has been installed */
2405     hr = Installer_ProductState(szProductCode, &iValue);
2406     ok(hr == S_OK, "Installer_ProductState failed, hresult 0x%08x\n", hr);
2407     ok(iValue == INSTALLSTATE_DEFAULT, "Installer_ProductState returned %d, expected %d\n", iValue, INSTALLSTATE_DEFAULT);
2408
2409     /* Installer::ProductInfo for our product code */
2410
2411     /* NULL attribute */
2412     memset(szString, 0, sizeof(szString));
2413     hr = Installer_ProductInfo(szProductCode, NULL, szString);
2414     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2415     ok_exception(hr, szProductInfoException);
2416
2417     /* Nonexistent attribute */
2418     memset(szString, 0, sizeof(szString));
2419     hr = Installer_ProductInfo(szProductCode, szMsifile, szString);
2420     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2421     ok_exception(hr, szProductInfoException);
2422
2423     /* Package name */
2424     memset(szString, 0, sizeof(szString));
2425     hr = Installer_ProductInfo(szProductCode, WINE_INSTALLPROPERTY_PACKAGENAMEW, szString);
2426     ok(hr == S_OK, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2427     todo_wine ok_w2("Installer_ProductInfo returned %s but expected %s\n", szString, szMsifile);
2428
2429     /* Product name */
2430     memset(szString, 0, sizeof(szString));
2431     hr = Installer_ProductInfo(szProductCode, WINE_INSTALLPROPERTY_PRODUCTNAMEW, szString);
2432     ok(hr == S_OK, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2433     todo_wine ok_w2("Installer_ProductInfo returned %s but expected %s\n", szString, szMSITEST);
2434
2435     /* Installer::Products */
2436     test_Installer_Products(TRUE);
2437
2438     /* Installer::RelatedProducts for our upgrade code */
2439     hr = Installer_RelatedProducts(szUpgradeCode, &pStringList);
2440     ok(hr == S_OK, "Installer_RelatedProducts failed, hresult 0x%08x\n", hr);
2441     if (hr == S_OK)
2442     {
2443         /* StringList::Count */
2444         hr = StringList_Count(pStringList, &iCount);
2445         ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr);
2446         ok(iCount == 1, "Expected one related product but found %d\n", iCount);
2447
2448         /* StringList::Item */
2449         memset(szString, 0, sizeof(szString));
2450         hr = StringList_Item(pStringList, 0, szString);
2451         ok(hr == S_OK, "StringList_Item failed (idx 0, count %d), hresult 0x%08x\n", iCount, hr);
2452         ok_w2("StringList_Item returned %s but expected %s\n", szString, szProductCode);
2453
2454         IDispatch_Release(pStringList);
2455     }
2456
2457     hr = Installer_ProductInfo(szProductCode, WINE_INSTALLPROPERTY_LOCALPACKAGEW, szString);
2458     ok(hr == S_OK, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2459     DeleteFileW( szString );
2460
2461     /* Check & clean up installed files & registry keys */
2462     ok(delete_pf("msitest\\cabout\\new\\five.txt", TRUE), "File not installed\n");
2463     ok(delete_pf("msitest\\cabout\\new", FALSE), "Directory not created\n");
2464     ok(delete_pf("msitest\\cabout\\four.txt", TRUE), "File not installed\n");
2465     ok(delete_pf("msitest\\cabout", FALSE), "Directory not created\n");
2466     ok(delete_pf("msitest\\changed\\three.txt", TRUE), "File not installed\n");
2467     ok(delete_pf("msitest\\changed", FALSE), "Directory not created\n");
2468     ok(delete_pf("msitest\\first\\two.txt", TRUE), "File not installed\n");
2469     ok(delete_pf("msitest\\first", FALSE), "Directory not created\n");
2470     ok(delete_pf("msitest\\one.txt", TRUE), "File not installed\n");
2471     ok(delete_pf("msitest\\filename", TRUE), "File not installed\n");
2472     ok(delete_pf("msitest", FALSE), "Directory not created\n");
2473
2474     res = RegOpenKeyA(HKEY_CURRENT_USER, "SOFTWARE\\Wine\\msitest", &hkey);
2475     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2476
2477     size = MAX_PATH;
2478     type = REG_SZ;
2479     res = RegQueryValueExA(hkey, "Name", NULL, &type, (LPBYTE)path, &size);
2480     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2481     ok(!lstrcmpA(path, "imaname"), "Expected imaname, got %s\n", path);
2482
2483     size = MAX_PATH;
2484     type = REG_SZ;
2485     res = RegQueryValueExA(hkey, "blah", NULL, &type, (LPBYTE)path, &size);
2486     ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
2487
2488     size = sizeof(num);
2489     type = REG_DWORD;
2490     res = RegQueryValueExA(hkey, "number", NULL, &type, (LPBYTE)&num, &size);
2491     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2492     ok(num == 314, "Expected 314, got %d\n", num);
2493
2494     size = MAX_PATH;
2495     type = REG_SZ;
2496     res = RegQueryValueExA(hkey, "OrderTestName", NULL, &type, (LPBYTE)path, &size);
2497     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2498     ok(!lstrcmpA(path, "OrderTestValue"), "Expected imaname, got %s\n", path);
2499
2500     RegCloseKey(hkey);
2501
2502     res = RegDeleteKeyA(HKEY_CURRENT_USER, "SOFTWARE\\Wine\\msitest");
2503     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2504
2505     /* Remove registry keys written by RegisterProduct standard action */
2506     res = delete_key_portable(HKEY_LOCAL_MACHINE,
2507         "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{837450fa-a39b-4bc8-b321-08b393f784b3}",
2508                               KEY_WOW64_32KEY);
2509     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2510
2511     res = delete_key_portable(HKEY_LOCAL_MACHINE,
2512         "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UpgradeCodes\\D8E760ECA1E276347B43E42BDBDA5656", access);
2513     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2514
2515     res = find_registry_key(HKEY_LOCAL_MACHINE,
2516         "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData", "af054738b93a8cb43b12803b397f483b", access, &hkey);
2517     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2518
2519     res = delete_registry_key(hkey, "af054738b93a8cb43b12803b397f483b", access);
2520     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2521     RegCloseKey(hkey);
2522
2523     res = delete_key_portable(HKEY_LOCAL_MACHINE,
2524         "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\Products\\af054738b93a8cb43b12803b397f483b", access);
2525     ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
2526
2527     /* Remove registry keys written by PublishProduct standard action */
2528     res = RegOpenKey(HKEY_CURRENT_USER, "SOFTWARE\\Microsoft\\Installer", &hkey);
2529     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2530
2531     res = delete_registry_key(hkey, "Products\\af054738b93a8cb43b12803b397f483b", KEY_ALL_ACCESS);
2532     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2533
2534     res = RegDeleteKeyA(hkey, "UpgradeCodes\\D8E760ECA1E276347B43E42BDBDA5656");
2535     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2536
2537     RegCloseKey(hkey);
2538
2539     /* Delete installation files we created */
2540     delete_test_files();
2541 }
2542
2543 static void test_Installer(void)
2544 {
2545     static WCHAR szCreateRecordException[] = { 'C','r','e','a','t','e','R','e','c','o','r','d',',','C','o','u','n','t',0 };
2546     static WCHAR szIntegerDataException[] = { 'I','n','t','e','g','e','r','D','a','t','a',',','F','i','e','l','d',0 };
2547     WCHAR szPath[MAX_PATH];
2548     HRESULT hr;
2549     IDispatch *pSession = NULL, *pDatabase = NULL, *pRecord = NULL, *pStringList = NULL;
2550     int iValue, iCount;
2551
2552     if (!pInstaller) return;
2553
2554     /* Installer::CreateRecord */
2555
2556     /* Test for error */
2557     hr = Installer_CreateRecord(-1, &pRecord);
2558     ok(hr == DISP_E_EXCEPTION, "Installer_CreateRecord failed, hresult 0x%08x\n", hr);
2559     ok_exception(hr, szCreateRecordException);
2560
2561     /* Test for success */
2562     hr = Installer_CreateRecord(1, &pRecord);
2563     ok(hr == S_OK, "Installer_CreateRecord failed, hresult 0x%08x\n", hr);
2564     ok(pRecord != NULL, "Installer_CreateRecord should not have returned NULL record\n");
2565     if (pRecord)
2566     {
2567         /* Record::FieldCountGet */
2568         hr = Record_FieldCountGet(pRecord, &iValue);
2569         ok(hr == S_OK, "Record_FiledCountGet failed, hresult 0x%08x\n", hr);
2570         ok(iValue == 1, "Record_FieldCountGet result was %d but expected 1\n", iValue);
2571
2572         /* Record::IntegerDataGet */
2573         hr = Record_IntegerDataGet(pRecord, 1, &iValue);
2574         ok(hr == S_OK, "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
2575         ok(iValue == MSI_NULL_INTEGER, "Record_IntegerDataGet result was %d but expected %d\n", iValue, MSI_NULL_INTEGER);
2576
2577         /* Record::IntegerDataGet, bad index */
2578         hr = Record_IntegerDataGet(pRecord, 10, &iValue);
2579         ok(hr == S_OK, "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
2580         ok(iValue == MSI_NULL_INTEGER, "Record_IntegerDataGet result was %d but expected %d\n", iValue, MSI_NULL_INTEGER);
2581
2582         /* Record::IntegerDataPut */
2583         hr = Record_IntegerDataPut(pRecord, 1, 100);
2584         ok(hr == S_OK, "Record_IntegerDataPut failed, hresult 0x%08x\n", hr);
2585
2586         /* Record::IntegerDataPut, bad index */
2587         hr = Record_IntegerDataPut(pRecord, 10, 100);
2588         ok(hr == DISP_E_EXCEPTION, "Record_IntegerDataPut failed, hresult 0x%08x\n", hr);
2589         ok_exception(hr, szIntegerDataException);
2590
2591         /* Record::IntegerDataGet */
2592         hr = Record_IntegerDataGet(pRecord, 1, &iValue);
2593         ok(hr == S_OK, "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
2594         ok(iValue == 100, "Record_IntegerDataGet result was %d but expected 100\n", iValue);
2595
2596         IDispatch_Release(pRecord);
2597     }
2598
2599     create_package(szPath);
2600
2601     /* Installer::OpenPackage */
2602     hr = Installer_OpenPackage(szPath, 0, &pSession);
2603     if (hr == DISP_E_EXCEPTION)
2604     {
2605         skip("OpenPackage failed, insufficient rights?\n");
2606         DeleteFileW(szPath);
2607         return;
2608     }
2609     ok(hr == S_OK, "Installer_OpenPackage failed, hresult 0x%08x\n", hr);
2610     if (hr == S_OK)
2611     {
2612         test_Session(pSession);
2613         IDispatch_Release(pSession);
2614     }
2615
2616     /* Installer::OpenDatabase */
2617     hr = Installer_OpenDatabase(szPath, (INT_PTR)MSIDBOPEN_TRANSACT, &pDatabase);
2618     ok(hr == S_OK, "Installer_OpenDatabase failed, hresult 0x%08x\n", hr);
2619     if (hr == S_OK)
2620     {
2621         test_Database(pDatabase, FALSE);
2622         IDispatch_Release(pDatabase);
2623     }
2624
2625     /* Installer::RegistryValue */
2626     test_Installer_RegistryValue();
2627
2628     /* Installer::ProductState for our product code, which should not be installed */
2629     hr = Installer_ProductState(szProductCode, &iValue);
2630     ok(hr == S_OK, "Installer_ProductState failed, hresult 0x%08x\n", hr);
2631     ok(iValue == INSTALLSTATE_UNKNOWN, "Installer_ProductState returned %d, expected %d\n", iValue, INSTALLSTATE_UNKNOWN);
2632
2633     /* Installer::ProductInfo for our product code, which should not be installed */
2634
2635     /* Package name */
2636     memset(szPath, 0, sizeof(szPath));
2637     hr = Installer_ProductInfo(szProductCode, WINE_INSTALLPROPERTY_PACKAGENAMEW, szPath);
2638     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2639     ok_exception(hr, szProductInfoException);
2640
2641     /* NULL attribute and NULL product code */
2642     memset(szPath, 0, sizeof(szPath));
2643     hr = Installer_ProductInfo(NULL, NULL, szPath);
2644     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2645     ok_exception(hr, szProductInfoException);
2646
2647     /* Installer::Products */
2648     test_Installer_Products(FALSE);
2649
2650     /* Installer::RelatedProducts for our upgrade code, should not find anything */
2651     hr = Installer_RelatedProducts(szUpgradeCode, &pStringList);
2652     ok(hr == S_OK, "Installer_RelatedProducts failed, hresult 0x%08x\n", hr);
2653     if (hr == S_OK)
2654     {
2655         /* StringList::Count */
2656         hr = StringList_Count(pStringList, &iCount);
2657         ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr);
2658         ok(!iCount, "Expected no related products but found %d\n", iCount);
2659
2660         IDispatch_Release(pStringList);
2661     }
2662
2663     /* Installer::Version */
2664     memset(szPath, 0, sizeof(szPath));
2665     hr = Installer_VersionGet(szPath);
2666     ok(hr == S_OK, "Installer_VersionGet failed, hresult 0x%08x\n", hr);
2667
2668     /* Installer::InstallProduct and other tests that depend on our product being installed */
2669     test_Installer_InstallProduct();
2670 }
2671
2672 START_TEST(automation)
2673 {
2674     DWORD len;
2675     char temp_path[MAX_PATH], prev_path[MAX_PATH];
2676     HRESULT hr;
2677     CLSID clsid;
2678     IUnknown *pUnk;
2679
2680     init_functionpointers();
2681
2682     if (pIsWow64Process)
2683         pIsWow64Process(GetCurrentProcess(), &is_wow64);
2684
2685     GetSystemTimeAsFileTime(&systemtime);
2686
2687     GetCurrentDirectoryA(MAX_PATH, prev_path);
2688     GetTempPath(MAX_PATH, temp_path);
2689     SetCurrentDirectoryA(temp_path);
2690
2691     lstrcpyA(CURR_DIR, temp_path);
2692     len = lstrlenA(CURR_DIR);
2693
2694     if(len && (CURR_DIR[len - 1] == '\\'))
2695         CURR_DIR[len - 1] = 0;
2696
2697     get_program_files_dir(PROG_FILES_DIR);
2698
2699     hr = OleInitialize(NULL);
2700     ok (hr == S_OK, "OleInitialize returned 0x%08x\n", hr);
2701     hr = CLSIDFromProgID(szProgId, &clsid);
2702     ok (hr == S_OK, "CLSIDFromProgID returned 0x%08x\n", hr);
2703     hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&pUnk);
2704     ok(hr == S_OK, "CoCreateInstance returned 0x%08x\n", hr);
2705
2706     if (pUnk)
2707     {
2708         hr = IUnknown_QueryInterface(pUnk, &IID_IDispatch, (void **)&pInstaller);
2709         ok (hr == S_OK, "IUnknown::QueryInterface returned 0x%08x\n", hr);
2710
2711         test_dispid();
2712         test_dispatch();
2713         test_Installer();
2714
2715         IDispatch_Release(pInstaller);
2716         IUnknown_Release(pUnk);
2717     }
2718
2719     OleUninitialize();
2720
2721     SetCurrentDirectoryA(prev_path);
2722 }