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