gdiplus: Select the font into the appropriate hdc in GdipMeasureString.
[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     DWORD len;
306
307     /* Prepare package */
308     create_database(msifile, tables,
309                     sizeof(tables) / sizeof(msi_table), summary_info,
310                     sizeof(summary_info) / sizeof(msi_summary_info));
311
312     len = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
313                               CURR_DIR, -1, path, MAX_PATH);
314     ok(len, "MultiByteToWideChar returned error %d\n", GetLastError());
315     if (!len)
316         return FALSE;
317
318     /* lstrcatW does not work on win95 */
319     path[len - 1] = '\\';
320     memcpy(&path[len], szMsifile, sizeof(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))
953         /* lstrcpyW is not implemented on Win95 (lstrlenW is though) */
954         memcpy(szString, V_BSTR(&varresult), (lstrlenW(V_BSTR(&varresult)) + 1) * sizeof(WCHAR));
955     VariantClear(&varresult);
956     return hr;
957 }
958
959 static HRESULT Installer_RegistryValueI(HKEY hkey, LPCWSTR szKey, int iValue, LPWSTR szString, VARTYPE vtResult)
960 {
961     VARIANT varresult;
962     VARIANTARG vararg;
963     HRESULT hr;
964
965     VariantInit(&vararg);
966     V_VT(&vararg) = VT_I4;
967     V_I4(&vararg) = iValue;
968
969     hr = Installer_RegistryValue(hkey, szKey, vararg, &varresult, vtResult);
970     if (SUCCEEDED(hr) && vtResult == VT_BSTR)
971         memcpy(szString, V_BSTR(&varresult), (lstrlenW(V_BSTR(&varresult)) + 1) * sizeof(WCHAR));
972     VariantClear(&varresult);
973     return hr;
974 }
975
976 static HRESULT Installer_OpenPackage(LPCWSTR szPackagePath, int options, IDispatch **pSession)
977 {
978     VARIANT varresult;
979     VARIANTARG vararg[2];
980     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
981     HRESULT hr;
982
983     VariantInit(&vararg[1]);
984     V_VT(&vararg[1]) = VT_BSTR;
985     V_BSTR(&vararg[1]) = SysAllocString(szPackagePath);
986     VariantInit(&vararg[0]);
987     V_VT(&vararg[0]) = VT_I4;
988     V_I4(&vararg[0]) = options;
989
990     hr = invoke(pInstaller, "OpenPackage", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH);
991     *pSession = V_DISPATCH(&varresult);
992     return hr;
993 }
994
995 static HRESULT Installer_OpenDatabase(LPCWSTR szDatabasePath, int openmode, IDispatch **pDatabase)
996 {
997     VARIANT varresult;
998     VARIANTARG vararg[2];
999     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1000     HRESULT hr;
1001
1002     VariantInit(&vararg[1]);
1003     V_VT(&vararg[1]) = VT_BSTR;
1004     V_BSTR(&vararg[1]) = SysAllocString(szDatabasePath);
1005     VariantInit(&vararg[0]);
1006     V_VT(&vararg[0]) = VT_I4;
1007     V_I4(&vararg[0]) = openmode;
1008
1009     hr = invoke(pInstaller, "OpenDatabase", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH);
1010     *pDatabase = V_DISPATCH(&varresult);
1011     return hr;
1012 }
1013
1014 static HRESULT Installer_InstallProduct(LPCWSTR szPackagePath, LPCWSTR szPropertyValues)
1015 {
1016     VARIANT varresult;
1017     VARIANTARG vararg[2];
1018     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1019
1020     VariantInit(&vararg[1]);
1021     V_VT(&vararg[1]) = VT_BSTR;
1022     V_BSTR(&vararg[1]) = SysAllocString(szPackagePath);
1023     VariantInit(&vararg[0]);
1024     V_VT(&vararg[0]) = VT_BSTR;
1025     V_BSTR(&vararg[0]) = SysAllocString(szPropertyValues);
1026
1027     return invoke(pInstaller, "InstallProduct", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY);
1028 }
1029
1030 static HRESULT Installer_ProductState(LPCWSTR szProduct, int *pInstallState)
1031 {
1032     VARIANT varresult;
1033     VARIANTARG vararg[1];
1034     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1035     HRESULT hr;
1036
1037     VariantInit(&vararg[0]);
1038     V_VT(&vararg[0]) = VT_BSTR;
1039     V_BSTR(&vararg[0]) = SysAllocString(szProduct);
1040
1041     hr = invoke(pInstaller, "ProductState", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1042     *pInstallState = V_I4(&varresult);
1043     VariantClear(&varresult);
1044     return hr;
1045 }
1046
1047 static HRESULT Installer_ProductInfo(LPCWSTR szProduct, LPCWSTR szAttribute, LPWSTR szString)
1048 {
1049     VARIANT varresult;
1050     VARIANTARG vararg[2];
1051     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1052     HRESULT hr;
1053
1054     VariantInit(&vararg[1]);
1055     V_VT(&vararg[1]) = VT_BSTR;
1056     V_BSTR(&vararg[1]) = SysAllocString(szProduct);
1057     VariantInit(&vararg[0]);
1058     V_VT(&vararg[0]) = VT_BSTR;
1059     V_BSTR(&vararg[0]) = SysAllocString(szAttribute);
1060
1061     hr = invoke(pInstaller, "ProductInfo", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR);
1062     if (V_BSTR(&varresult))
1063         memcpy(szString, V_BSTR(&varresult), (lstrlenW(V_BSTR(&varresult)) + 1) * sizeof(WCHAR));
1064     VariantClear(&varresult);
1065     return hr;
1066 }
1067
1068 static HRESULT Installer_Products(IDispatch **pStringList)
1069 {
1070     VARIANT varresult;
1071     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1072     HRESULT hr;
1073
1074     hr = invoke(pInstaller, "Products", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH);
1075     *pStringList = V_DISPATCH(&varresult);
1076     return hr;
1077 }
1078
1079 static HRESULT Installer_RelatedProducts(LPCWSTR szProduct, IDispatch **pStringList)
1080 {
1081     VARIANT varresult;
1082     VARIANTARG vararg[1];
1083     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1084     HRESULT hr;
1085
1086     VariantInit(&vararg[0]);
1087     V_VT(&vararg[0]) = VT_BSTR;
1088     V_BSTR(&vararg[0]) = SysAllocString(szProduct);
1089
1090     hr = invoke(pInstaller, "RelatedProducts", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH);
1091     *pStringList = V_DISPATCH(&varresult);
1092     return hr;
1093 }
1094
1095 static HRESULT Installer_VersionGet(LPWSTR szVersion)
1096 {
1097     VARIANT varresult;
1098     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1099     HRESULT hr;
1100
1101     hr = invoke(pInstaller, "Version", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR);
1102     if (V_BSTR(&varresult))
1103         memcpy(szVersion, V_BSTR(&varresult), (lstrlenW(V_BSTR(&varresult)) + 1) * sizeof(WCHAR));
1104     VariantClear(&varresult);
1105     return hr;
1106 }
1107
1108 static HRESULT Installer_UILevelPut(int level)
1109 {
1110     VARIANT varresult;
1111     VARIANTARG vararg;
1112     DISPID dispid = DISPID_PROPERTYPUT;
1113     DISPPARAMS dispparams = {&vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};
1114
1115     VariantInit(&vararg);
1116     V_VT(&vararg) = VT_I4;
1117     V_I4(&vararg) = level;
1118
1119     return invoke(pInstaller, "UILevel", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
1120 }
1121
1122 static HRESULT Session_Installer(IDispatch *pSession, IDispatch **pInst)
1123 {
1124     VARIANT varresult;
1125     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1126     HRESULT hr;
1127
1128     hr = invoke(pSession, "Installer", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH);
1129     *pInst = V_DISPATCH(&varresult);
1130     return hr;
1131 }
1132
1133 static HRESULT Session_PropertyGet(IDispatch *pSession, LPCWSTR szName, LPWSTR szReturn)
1134 {
1135     VARIANT varresult;
1136     VARIANTARG vararg[1];
1137     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1138     HRESULT hr;
1139
1140     VariantInit(&vararg[0]);
1141     V_VT(&vararg[0]) = VT_BSTR;
1142     V_BSTR(&vararg[0]) = SysAllocString(szName);
1143
1144     hr = invoke(pSession, "Property", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR);
1145     if (V_BSTR(&varresult))
1146         memcpy(szReturn, V_BSTR(&varresult), (lstrlenW(V_BSTR(&varresult)) + 1) * sizeof(WCHAR));
1147     VariantClear(&varresult);
1148     return hr;
1149 }
1150
1151 static HRESULT Session_PropertyPut(IDispatch *pSession, LPCWSTR szName, LPCWSTR szValue)
1152 {
1153     VARIANT varresult;
1154     VARIANTARG vararg[2];
1155     DISPID dispid = DISPID_PROPERTYPUT;
1156     DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};
1157
1158     VariantInit(&vararg[1]);
1159     V_VT(&vararg[1]) = VT_BSTR;
1160     V_BSTR(&vararg[1]) = SysAllocString(szName);
1161     VariantInit(&vararg[0]);
1162     V_VT(&vararg[0]) = VT_BSTR;
1163     V_BSTR(&vararg[0]) = SysAllocString(szValue);
1164
1165     return invoke(pSession, "Property", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
1166 }
1167
1168 static HRESULT Session_LanguageGet(IDispatch *pSession, UINT *pLangId)
1169 {
1170     VARIANT varresult;
1171     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1172     HRESULT hr;
1173
1174     hr = invoke(pSession, "Language", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1175     *pLangId = V_I4(&varresult);
1176     VariantClear(&varresult);
1177     return hr;
1178 }
1179
1180 static HRESULT Session_ModeGet(IDispatch *pSession, int iFlag, BOOL *pMode)
1181 {
1182     VARIANT varresult;
1183     VARIANTARG vararg[1];
1184     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1185     HRESULT hr;
1186
1187     VariantInit(&vararg[0]);
1188     V_VT(&vararg[0]) = VT_I4;
1189     V_I4(&vararg[0]) = iFlag;
1190
1191     hr = invoke(pSession, "Mode", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BOOL);
1192     *pMode = V_BOOL(&varresult);
1193     VariantClear(&varresult);
1194     return hr;
1195 }
1196
1197 static HRESULT Session_ModePut(IDispatch *pSession, int iFlag, BOOL bMode)
1198 {
1199     VARIANT varresult;
1200     VARIANTARG vararg[2];
1201     DISPID dispid = DISPID_PROPERTYPUT;
1202     DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};
1203
1204     VariantInit(&vararg[1]);
1205     V_VT(&vararg[1]) = VT_I4;
1206     V_I4(&vararg[1]) = iFlag;
1207     VariantInit(&vararg[0]);
1208     V_VT(&vararg[0]) = VT_BOOL;
1209     V_BOOL(&vararg[0]) = bMode;
1210
1211     return invoke(pSession, "Mode", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
1212 }
1213
1214 static HRESULT Session_Database(IDispatch *pSession, IDispatch **pDatabase)
1215 {
1216     VARIANT varresult;
1217     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1218     HRESULT hr;
1219
1220     hr = invoke(pSession, "Database", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH);
1221     *pDatabase = V_DISPATCH(&varresult);
1222     return hr;
1223 }
1224
1225 static HRESULT Session_DoAction(IDispatch *pSession, LPCWSTR szAction, int *iReturn)
1226 {
1227     VARIANT varresult;
1228     VARIANTARG vararg[1];
1229     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1230     HRESULT hr;
1231
1232     VariantInit(&vararg[0]);
1233     V_VT(&vararg[0]) = VT_BSTR;
1234     V_BSTR(&vararg[0]) = SysAllocString(szAction);
1235
1236     hr = invoke(pSession, "DoAction", DISPATCH_METHOD, &dispparams, &varresult, VT_I4);
1237     *iReturn = V_I4(&varresult);
1238     VariantClear(&varresult);
1239     return hr;
1240 }
1241
1242 static HRESULT Session_EvaluateCondition(IDispatch *pSession, LPCWSTR szCondition, int *iReturn)
1243 {
1244     VARIANT varresult;
1245     VARIANTARG vararg[1];
1246     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1247     HRESULT hr;
1248
1249     VariantInit(&vararg[0]);
1250     V_VT(&vararg[0]) = VT_BSTR;
1251     V_BSTR(&vararg[0]) = SysAllocString(szCondition);
1252
1253     hr = invoke(pSession, "EvaluateCondition", DISPATCH_METHOD, &dispparams, &varresult, VT_I4);
1254     *iReturn = V_I4(&varresult);
1255     VariantClear(&varresult);
1256     return hr;
1257 }
1258
1259 static HRESULT Session_Message(IDispatch *pSession, LONG kind, IDispatch *record, int *ret)
1260 {
1261     VARIANT varresult;
1262     VARIANTARG vararg[2];
1263     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1264     HRESULT hr;
1265
1266     VariantInit(&varresult);
1267     V_VT(vararg) = VT_DISPATCH;
1268     V_DISPATCH(vararg) = record;
1269     V_VT(vararg+1) = VT_I4;
1270     V_I4(vararg+1) = kind;
1271
1272     hr = invoke(pSession, "Message", DISPATCH_METHOD, &dispparams, &varresult, VT_I4);
1273
1274     ok(V_VT(&varresult) == VT_I4, "V_VT(varresult) = %d\n", V_VT(&varresult));
1275     *ret = V_I4(&varresult);
1276
1277     return hr;
1278 }
1279
1280 static HRESULT Session_SetInstallLevel(IDispatch *pSession, LONG iInstallLevel)
1281 {
1282     VARIANT varresult;
1283     VARIANTARG vararg[1];
1284     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1285
1286     VariantInit(&vararg[0]);
1287     V_VT(&vararg[0]) = VT_I4;
1288     V_I4(&vararg[0]) = iInstallLevel;
1289
1290     return invoke(pSession, "SetInstallLevel", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY);
1291 }
1292
1293 static HRESULT Session_FeatureCurrentState(IDispatch *pSession, LPCWSTR szName, int *pState)
1294 {
1295     VARIANT varresult;
1296     VARIANTARG vararg[1];
1297     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1298     HRESULT hr;
1299
1300     VariantInit(&vararg[0]);
1301     V_VT(&vararg[0]) = VT_BSTR;
1302     V_BSTR(&vararg[0]) = SysAllocString(szName);
1303
1304     hr = invoke(pSession, "FeatureCurrentState", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1305     *pState = V_I4(&varresult);
1306     VariantClear(&varresult);
1307     return hr;
1308 }
1309
1310 static HRESULT Session_FeatureRequestStateGet(IDispatch *pSession, LPCWSTR szName, int *pState)
1311 {
1312     VARIANT varresult;
1313     VARIANTARG vararg[1];
1314     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1315     HRESULT hr;
1316
1317     VariantInit(&vararg[0]);
1318     V_VT(&vararg[0]) = VT_BSTR;
1319     V_BSTR(&vararg[0]) = SysAllocString(szName);
1320
1321     hr = invoke(pSession, "FeatureRequestState", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1322     *pState = V_I4(&varresult);
1323     VariantClear(&varresult);
1324     return hr;
1325 }
1326
1327 static HRESULT Session_FeatureRequestStatePut(IDispatch *pSession, LPCWSTR szName, int iState)
1328 {
1329     VARIANT varresult;
1330     VARIANTARG vararg[2];
1331     DISPID dispid = DISPID_PROPERTYPUT;
1332     DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};
1333
1334     VariantInit(&vararg[1]);
1335     V_VT(&vararg[1]) = VT_BSTR;
1336     V_BSTR(&vararg[1]) = SysAllocString(szName);
1337     VariantInit(&vararg[0]);
1338     V_VT(&vararg[0]) = VT_I4;
1339     V_I4(&vararg[0]) = iState;
1340
1341     return invoke(pSession, "FeatureRequestState", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
1342 }
1343
1344 static HRESULT Database_OpenView(IDispatch *pDatabase, LPCWSTR szSql, IDispatch **pView)
1345 {
1346     VARIANT varresult;
1347     VARIANTARG vararg[1];
1348     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1349     HRESULT hr;
1350
1351     VariantInit(&vararg[0]);
1352     V_VT(&vararg[0]) = VT_BSTR;
1353     V_BSTR(&vararg[0]) = SysAllocString(szSql);
1354
1355     hr = invoke(pDatabase, "OpenView", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH);
1356     *pView = V_DISPATCH(&varresult);
1357     return hr;
1358 }
1359
1360 static HRESULT Database_SummaryInformation(IDispatch *pDatabase, int iUpdateCount, IDispatch **pSummaryInfo)
1361 {
1362     VARIANT varresult;
1363     VARIANTARG vararg[1];
1364     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1365     HRESULT hr;
1366
1367     VariantInit(&vararg[0]);
1368     V_VT(&vararg[0]) = VT_I4;
1369     V_I4(&vararg[0]) = iUpdateCount;
1370
1371     hr = invoke(pDatabase, "SummaryInformation", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH);
1372     *pSummaryInfo = V_DISPATCH(&varresult);
1373     return hr;
1374 }
1375
1376 static HRESULT View_Execute(IDispatch *pView, IDispatch *pRecord)
1377 {
1378     VARIANT varresult;
1379     VARIANTARG vararg[1];
1380     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1381
1382     VariantInit(&vararg[0]);
1383     V_VT(&vararg[0]) = VT_DISPATCH;
1384     V_DISPATCH(&vararg[0]) = pRecord;
1385
1386     return invoke(pView, "Execute", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY);
1387 }
1388
1389 static HRESULT View_Fetch(IDispatch *pView, IDispatch **ppRecord)
1390 {
1391     VARIANT varresult;
1392     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1393     HRESULT hr = invoke(pView, "Fetch", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH);
1394     *ppRecord = V_DISPATCH(&varresult);
1395     return hr;
1396 }
1397
1398 static HRESULT View_Modify(IDispatch *pView, int iMode, IDispatch *pRecord)
1399 {
1400     VARIANT varresult;
1401     VARIANTARG vararg[2];
1402     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1403
1404     VariantInit(&vararg[1]);
1405     V_VT(&vararg[1]) = VT_I4;
1406     V_I4(&vararg[1]) = iMode;
1407     VariantInit(&vararg[0]);
1408     V_VT(&vararg[0]) = VT_DISPATCH;
1409     V_DISPATCH(&vararg[0]) = pRecord;
1410     if (pRecord)
1411         IDispatch_AddRef(pRecord);   /* VariantClear in invoke will call IDispatch_Release */
1412
1413     return invoke(pView, "Modify", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY);
1414 }
1415
1416 static HRESULT View_Close(IDispatch *pView)
1417 {
1418     VARIANT varresult;
1419     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1420     return invoke(pView, "Close", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY);
1421 }
1422
1423 static HRESULT Record_FieldCountGet(IDispatch *pRecord, int *pFieldCount)
1424 {
1425     VARIANT varresult;
1426     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1427     HRESULT hr = invoke(pRecord, "FieldCount", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1428     *pFieldCount = V_I4(&varresult);
1429     VariantClear(&varresult);
1430     return hr;
1431 }
1432
1433 static HRESULT Record_StringDataGet(IDispatch *pRecord, int iField, LPWSTR szString)
1434 {
1435     VARIANT varresult;
1436     VARIANTARG vararg[1];
1437     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1438     HRESULT hr;
1439
1440     VariantInit(&vararg[0]);
1441     V_VT(&vararg[0]) = VT_I4;
1442     V_I4(&vararg[0]) = iField;
1443
1444     hr = invoke(pRecord, "StringData", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR);
1445     if (V_BSTR(&varresult))
1446         memcpy(szString, V_BSTR(&varresult), (lstrlenW(V_BSTR(&varresult)) + 1) * sizeof(WCHAR));
1447     VariantClear(&varresult);
1448     return hr;
1449 }
1450
1451 static HRESULT Record_StringDataPut(IDispatch *pRecord, int iField, LPCWSTR szString)
1452 {
1453     VARIANT varresult;
1454     VARIANTARG vararg[2];
1455     DISPID dispid = DISPID_PROPERTYPUT;
1456     DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};
1457
1458     VariantInit(&vararg[1]);
1459     V_VT(&vararg[1]) = VT_I4;
1460     V_I4(&vararg[1]) = iField;
1461     VariantInit(&vararg[0]);
1462     V_VT(&vararg[0]) = VT_BSTR;
1463     V_BSTR(&vararg[0]) = SysAllocString(szString);
1464
1465     return invoke(pRecord, "StringData", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
1466 }
1467
1468 static HRESULT Record_IntegerDataGet(IDispatch *pRecord, int iField, int *pValue)
1469 {
1470     VARIANT varresult;
1471     VARIANTARG vararg[1];
1472     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1473     HRESULT hr;
1474
1475     VariantInit(&vararg[0]);
1476     V_VT(&vararg[0]) = VT_I4;
1477     V_I4(&vararg[0]) = iField;
1478
1479     hr = invoke(pRecord, "IntegerData", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1480     *pValue = V_I4(&varresult);
1481     VariantClear(&varresult);
1482     return hr;
1483 }
1484
1485 static HRESULT Record_IntegerDataPut(IDispatch *pRecord, int iField, int iValue)
1486 {
1487     VARIANT varresult;
1488     VARIANTARG vararg[2];
1489     DISPID dispid = DISPID_PROPERTYPUT;
1490     DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};
1491
1492     VariantInit(&vararg[1]);
1493     V_VT(&vararg[1]) = VT_I4;
1494     V_I4(&vararg[1]) = iField;
1495     VariantInit(&vararg[0]);
1496     V_VT(&vararg[0]) = VT_I4;
1497     V_I4(&vararg[0]) = iValue;
1498
1499     return invoke(pRecord, "IntegerData", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
1500 }
1501
1502 static HRESULT StringList__NewEnum(IDispatch *pList, IUnknown **ppEnumVARIANT)
1503 {
1504     VARIANT varresult;
1505     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1506     HRESULT hr = invoke(pList, "_NewEnum", DISPATCH_METHOD, &dispparams, &varresult, VT_UNKNOWN);
1507     *ppEnumVARIANT = V_UNKNOWN(&varresult);
1508     return hr;
1509 }
1510
1511 static HRESULT StringList_Item(IDispatch *pStringList, int iIndex, LPWSTR szString)
1512 {
1513     VARIANT varresult;
1514     VARIANTARG vararg[1];
1515     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1516     HRESULT hr;
1517
1518     VariantInit(&vararg[0]);
1519     V_VT(&vararg[0]) = VT_I4;
1520     V_I4(&vararg[0]) = iIndex;
1521
1522     hr = invoke(pStringList, "Item", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR);
1523     if (V_BSTR(&varresult))
1524         memcpy(szString, V_BSTR(&varresult), (lstrlenW(V_BSTR(&varresult)) + 1) * sizeof(WCHAR));
1525     VariantClear(&varresult);
1526     return hr;
1527 }
1528
1529 static HRESULT StringList_Count(IDispatch *pStringList, int *pCount)
1530 {
1531     VARIANT varresult;
1532     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1533     HRESULT hr = invoke(pStringList, "Count", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1534     *pCount = V_I4(&varresult);
1535     VariantClear(&varresult);
1536     return hr;
1537 }
1538
1539 static HRESULT SummaryInfo_PropertyGet(IDispatch *pSummaryInfo, int pid, VARIANT *pVarResult, VARTYPE vtExpect)
1540 {
1541     VARIANTARG vararg[1];
1542     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1543
1544     VariantInit(&vararg[0]);
1545     V_VT(&vararg[0]) = VT_I4;
1546     V_I4(&vararg[0]) = pid;
1547     return invoke(pSummaryInfo, "Property", DISPATCH_PROPERTYGET, &dispparams, pVarResult, vtExpect);
1548 }
1549
1550 static HRESULT SummaryInfo_PropertyPut(IDispatch *pSummaryInfo, int pid, VARIANT *pVariant)
1551 {
1552     VARIANT varresult;
1553     VARIANTARG vararg[2];
1554     DISPID dispid = DISPID_PROPERTYPUT;
1555     DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};
1556
1557     VariantInit(&vararg[1]);
1558     V_VT(&vararg[1]) = VT_I4;
1559     V_I4(&vararg[1]) = pid;
1560     VariantInit(&vararg[0]);
1561     VariantCopyInd(vararg, pVariant);
1562
1563     return invoke(pSummaryInfo, "Property", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
1564 }
1565
1566 static HRESULT SummaryInfo_PropertyCountGet(IDispatch *pSummaryInfo, int *pCount)
1567 {
1568     VARIANT varresult;
1569     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1570     HRESULT hr;
1571
1572     hr = invoke(pSummaryInfo, "PropertyCount", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1573     *pCount = V_I4(&varresult);
1574     VariantClear(&varresult);
1575     return hr;
1576 }
1577
1578 /* Test the various objects */
1579
1580 #define TEST_SUMMARYINFO_PROPERTIES_MODIFIED 4
1581
1582 static void test_SummaryInfo(IDispatch *pSummaryInfo, const msi_summary_info *info, int num_info, BOOL readonly)
1583 {
1584     static const WCHAR szPropertyException[] = { 'P','r','o','p','e','r','t','y',',','P','i','d',0 };
1585     static const WCHAR szTitle[] = { 'T','i','t','l','e',0 };
1586     VARIANT varresult, var;
1587     SYSTEMTIME st;
1588     HRESULT hr;
1589     int j;
1590
1591     /* SummaryInfo::PropertyCount */
1592     hr = SummaryInfo_PropertyCountGet(pSummaryInfo, &j);
1593     ok(hr == S_OK, "SummaryInfo_PropertyCount failed, hresult 0x%08x\n", hr);
1594     ok(j == num_info, "SummaryInfo_PropertyCount returned %d, expected %d\n", j, num_info);
1595
1596     /* SummaryInfo::Property, get for properties we have set */
1597     for (j = 0; j < num_info; j++)
1598     {
1599         const msi_summary_info *entry = &info[j];
1600
1601         int vt = entry->datatype;
1602         if (vt == VT_LPSTR) vt = VT_BSTR;
1603         else if (vt == VT_FILETIME) vt = VT_DATE;
1604         else if (vt == VT_I2) vt = VT_I4;
1605
1606         hr = SummaryInfo_PropertyGet(pSummaryInfo, entry->property, &varresult, vt);
1607         ok(hr == S_OK, "SummaryInfo_Property (pid %d) failed, hresult 0x%08x\n", entry->property, hr);
1608         if (V_VT(&varresult) != vt)
1609             skip("Skipping property tests due to type mismatch\n");
1610         else if (vt == VT_I4)
1611             ok(V_I4(&varresult) == entry->iValue, "SummaryInfo_Property (pid %d) I4 result expected to be %d, but was %d\n",
1612                entry->property, entry->iValue, V_I4(&varresult));
1613         else if (vt == VT_DATE)
1614         {
1615             FILETIME ft;
1616             DATE d;
1617
1618             FileTimeToLocalFileTime(entry->pftValue, &ft);
1619             FileTimeToSystemTime(&ft, &st);
1620             SystemTimeToVariantTime(&st, &d);
1621             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));
1622         }
1623         else if (vt == VT_BSTR)
1624         {
1625             ok_awplus("SummaryInfo_Property (pid %d) BSTR result expected to be %s, but was %s\n", entry->property, entry->szValue, V_BSTR(&varresult));
1626         }
1627         else
1628             skip("SummaryInfo_Property (pid %d) unhandled result type %d\n", entry->property, vt);
1629
1630         VariantClear(&varresult);
1631     }
1632
1633     /* SummaryInfo::Property, get; invalid arguments */
1634
1635     /* Invalid pids */
1636     hr = SummaryInfo_PropertyGet(pSummaryInfo, -1, &varresult, VT_EMPTY);
1637     ok(hr == DISP_E_EXCEPTION, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1638     ok_exception(hr, szPropertyException);
1639
1640     hr = SummaryInfo_PropertyGet(pSummaryInfo, 1000, &varresult, VT_EMPTY);
1641     ok(hr == DISP_E_EXCEPTION, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1642     ok_exception(hr, szPropertyException);
1643
1644     /* Unsupported pids */
1645     hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_DICTIONARY, &varresult, VT_EMPTY);
1646     ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1647
1648     hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_THUMBNAIL, &varresult, VT_EMPTY);
1649     ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1650
1651     /* Pids we have not set, one for each type */
1652     hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_CODEPAGE, &varresult, VT_EMPTY);
1653     ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1654
1655     hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_TITLE, &varresult, VT_EMPTY);
1656     ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1657
1658     hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_EDITTIME, &varresult, VT_EMPTY);
1659     ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1660
1661     hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_CHARCOUNT, &varresult, VT_EMPTY);
1662     ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1663
1664     if (!readonly)
1665     {
1666         /* SummaryInfo::Property, put; one for each type */
1667
1668         /* VT_I2 */
1669         VariantInit(&var);
1670         V_VT(&var) = VT_I2;
1671         V_I2(&var) = 1;
1672         hr = SummaryInfo_PropertyPut(pSummaryInfo, PID_CODEPAGE, &var);
1673         ok(hr == S_OK, "SummaryInfo_PropertyPut failed, hresult 0x%08x\n", hr);
1674
1675         hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_CODEPAGE, &varresult, VT_I4 /* NOT VT_I2 */);
1676         ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1677         ok(V_I2(&var) == V_I2(&varresult), "SummaryInfo_PropertyGet expected %d, but returned %d\n", V_I2(&var), V_I2(&varresult));
1678         VariantClear(&varresult);
1679         VariantClear(&var);
1680
1681         /* VT_BSTR */
1682         V_VT(&var) = VT_BSTR;
1683         V_BSTR(&var) = SysAllocString(szTitle);
1684         hr = SummaryInfo_PropertyPut(pSummaryInfo, PID_TITLE, &var);
1685         ok(hr == S_OK, "SummaryInfo_PropertyPut failed, hresult 0x%08x\n", hr);
1686
1687         hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_TITLE, &varresult, V_VT(&var));
1688         ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1689         ok_w2("SummaryInfo_PropertyGet expected %s, but returned %s\n", V_BSTR(&var), V_BSTR(&varresult));
1690         VariantClear(&varresult);
1691         VariantClear(&var);
1692
1693         /* VT_DATE */
1694         V_VT(&var) = VT_DATE;
1695         FileTimeToSystemTime(&systemtime, &st);
1696         SystemTimeToVariantTime(&st, &V_DATE(&var));
1697         hr = SummaryInfo_PropertyPut(pSummaryInfo, PID_LASTSAVE_DTM, &var);
1698         ok(hr == S_OK, "SummaryInfo_PropertyPut failed, hresult 0x%08x\n", hr);
1699
1700         hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_LASTSAVE_DTM, &varresult, V_VT(&var));
1701         ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1702         ok(V_DATE(&var) == V_DATE(&varresult), "SummaryInfo_PropertyGet expected %lf, but returned %lf\n", V_DATE(&var), V_DATE(&varresult));
1703         VariantClear(&varresult);
1704         VariantClear(&var);
1705
1706         /* VT_I4 */
1707         V_VT(&var) = VT_I4;
1708         V_I4(&var) = 1000;
1709         hr = SummaryInfo_PropertyPut(pSummaryInfo, PID_CHARCOUNT, &var);
1710         ok(hr == S_OK, "SummaryInfo_PropertyPut failed, hresult 0x%08x\n", hr);
1711
1712         hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_CHARCOUNT, &varresult, V_VT(&var));
1713         ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1714         ok(V_I4(&var) == V_I4(&varresult), "SummaryInfo_PropertyGet expected %d, but returned %d\n", V_I4(&var), V_I4(&varresult));
1715         VariantClear(&varresult);
1716         VariantClear(&var);
1717
1718         /* SummaryInfo::PropertyCount */
1719         hr = SummaryInfo_PropertyCountGet(pSummaryInfo, &j);
1720         ok(hr == S_OK, "SummaryInfo_PropertyCount failed, hresult 0x%08x\n", hr);
1721         ok(j == num_info+4, "SummaryInfo_PropertyCount returned %d, expected %d\n", j, num_info);
1722     }
1723 }
1724
1725 static void test_Database(IDispatch *pDatabase, BOOL readonly)
1726 {
1727     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 };
1728     static WCHAR szThree[] = { 'T','h','r','e','e',0 };
1729     static WCHAR szTwo[] = { 'T','w','o',0 };
1730     static WCHAR szStringDataField[] = { 'S','t','r','i','n','g','D','a','t','a',',','F','i','e','l','d',0 };
1731     static WCHAR szModifyModeRecord[] = { 'M','o','d','i','f','y',',','M','o','d','e',',','R','e','c','o','r','d',0 };
1732     IDispatch *pView = NULL, *pSummaryInfo = NULL;
1733     HRESULT hr;
1734
1735     hr = Database_OpenView(pDatabase, szSql, &pView);
1736     ok(hr == S_OK, "Database_OpenView failed, hresult 0x%08x\n", hr);
1737     if (hr == S_OK)
1738     {
1739         IDispatch *pRecord = NULL;
1740         WCHAR szString[MAX_PATH];
1741
1742         /* View::Execute */
1743         hr = View_Execute(pView, NULL);
1744         ok(hr == S_OK, "View_Execute failed, hresult 0x%08x\n", hr);
1745
1746         /* View::Fetch */
1747         hr = View_Fetch(pView, &pRecord);
1748         ok(hr == S_OK, "View_Fetch failed, hresult 0x%08x\n", hr);
1749         ok(pRecord != NULL, "View_Fetch should not have returned NULL record\n");
1750         if (pRecord)
1751         {
1752             /* Record::StringDataGet */
1753             memset(szString, 0, sizeof(szString));
1754             hr = Record_StringDataGet(pRecord, 1, szString);
1755             ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr);
1756             ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szThree);
1757
1758             /* Record::StringDataPut with correct index */
1759             hr = Record_StringDataPut(pRecord, 1, szTwo);
1760             ok(hr == S_OK, "Record_StringDataPut failed, hresult 0x%08x\n", hr);
1761
1762             /* Record::StringDataGet */
1763             memset(szString, 0, sizeof(szString));
1764             hr = Record_StringDataGet(pRecord, 1, szString);
1765             ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr);
1766             ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szTwo);
1767
1768             /* Record::StringDataPut with incorrect index */
1769             hr = Record_StringDataPut(pRecord, -1, szString);
1770             ok(hr == DISP_E_EXCEPTION, "Record_StringDataPut failed, hresult 0x%08x\n", hr);
1771             ok_exception(hr, szStringDataField);
1772
1773             /* View::Modify with incorrect parameters */
1774             hr = View_Modify(pView, -5, 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, -5, pRecord);
1779             ok(hr == DISP_E_EXCEPTION, "View_Modify failed, hresult 0x%08x\n", hr);
1780             ok_exception(hr, szModifyModeRecord);
1781
1782             hr = View_Modify(pView, MSIMODIFY_REFRESH, NULL);
1783             ok(hr == DISP_E_EXCEPTION, "View_Modify failed, hresult 0x%08x\n", hr);
1784             ok_exception(hr, szModifyModeRecord);
1785
1786             hr = View_Modify(pView, MSIMODIFY_REFRESH, pRecord);
1787             ok(hr == S_OK, "View_Modify failed, hresult 0x%08x\n", hr);
1788
1789             /* Record::StringDataGet, confirm that the record is back to its unmodified value */
1790             memset(szString, 0, sizeof(szString));
1791             hr = Record_StringDataGet(pRecord, 1, szString);
1792             ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr);
1793             todo_wine ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szThree);
1794
1795             IDispatch_Release(pRecord);
1796         }
1797
1798         /* View::Fetch */
1799         hr = View_Fetch(pView, &pRecord);
1800         ok(hr == S_OK, "View_Fetch failed, hresult 0x%08x\n", hr);
1801         ok(pRecord != NULL, "View_Fetch should not have returned NULL record\n");
1802         if (pRecord)
1803         {
1804             /* Record::StringDataGet */
1805             memset(szString, 0, sizeof(szString));
1806             hr = Record_StringDataGet(pRecord, 1, szString);
1807             ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr);
1808             ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szTwo);
1809
1810             IDispatch_Release(pRecord);
1811         }
1812
1813         /* View::Fetch */
1814         hr = View_Fetch(pView, &pRecord);
1815         ok(hr == S_OK, "View_Fetch failed, hresult 0x%08x\n", hr);
1816         ok(pRecord == NULL, "View_Fetch should have returned NULL record\n");
1817         if (pRecord)
1818             IDispatch_Release(pRecord);
1819
1820         /* View::Close */
1821         hr = View_Close(pView);
1822         ok(hr == S_OK, "View_Close failed, hresult 0x%08x\n", hr);
1823
1824         IDispatch_Release(pView);
1825     }
1826
1827     /* Database::SummaryInformation */
1828     hr = Database_SummaryInformation(pDatabase, TEST_SUMMARYINFO_PROPERTIES_MODIFIED, &pSummaryInfo);
1829     ok(hr == S_OK, "Database_SummaryInformation failed, hresult 0x%08x\n", hr);
1830     ok(pSummaryInfo != NULL, "Database_SummaryInformation should not have returned NULL record\n");
1831     if (pSummaryInfo)
1832     {
1833         test_SummaryInfo(pSummaryInfo, summary_info, sizeof(summary_info)/sizeof(msi_summary_info), readonly);
1834         IDispatch_Release(pSummaryInfo);
1835     }
1836 }
1837
1838 static void test_Session(IDispatch *pSession)
1839 {
1840     static WCHAR szProductName[] = { 'P','r','o','d','u','c','t','N','a','m','e',0 };
1841     static WCHAR szOne[] = { 'O','n','e',0 };
1842     static WCHAR szOneStateFalse[] = { '!','O','n','e','>','0',0 };
1843     static WCHAR szOneStateTrue[] = { '!','O','n','e','=','-','1',0 };
1844     static WCHAR szOneActionFalse[] = { '$','O','n','e','=','-','1',0 };
1845     static WCHAR szOneActionTrue[] = { '$','O','n','e','>','0',0 };
1846     static WCHAR szCostInitialize[] = { 'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0 };
1847     static WCHAR szEmpty[] = { 0 };
1848     static WCHAR szEquals[] = { '=',0 };
1849     static WCHAR szPropertyName[] = { 'P','r','o','p','e','r','t','y',',','N','a','m','e',0 };
1850     static WCHAR szModeFlag[] = { 'M','o','d','e',',','F','l','a','g',0 };
1851     WCHAR stringw[MAX_PATH];
1852     CHAR string[MAX_PATH];
1853     UINT len;
1854     BOOL bool;
1855     int myint;
1856     IDispatch *pDatabase = NULL, *pInst = NULL, *record = NULL;
1857     ULONG refs_before, refs_after;
1858     HRESULT hr;
1859
1860     /* Session::Installer */
1861     hr = Session_Installer(pSession, &pInst);
1862     ok(hr == S_OK, "Session_Installer failed, hresult 0x%08x\n", hr);
1863     ok(pInst != NULL, "Session_Installer returned NULL IDispatch pointer\n");
1864     ok(pInst == pInstaller, "Session_Installer does not match Installer instance from CoCreateInstance\n");
1865     refs_before = IDispatch_AddRef(pInst);
1866
1867     hr = Session_Installer(pSession, &pInst);
1868     ok(hr == S_OK, "Session_Installer failed, hresult 0x%08x\n", hr);
1869     ok(pInst != NULL, "Session_Installer returned NULL IDispatch pointer\n");
1870     ok(pInst == pInstaller, "Session_Installer does not match Installer instance from CoCreateInstance\n");
1871     refs_after = IDispatch_Release(pInst);
1872     ok(refs_before == refs_after, "got %u and %u\n", refs_before, refs_after);
1873
1874     /* Session::Property, get */
1875     memset(stringw, 0, sizeof(stringw));
1876     hr = Session_PropertyGet(pSession, szProductName, stringw);
1877     ok(hr == S_OK, "Session_PropertyGet failed, hresult 0x%08x\n", hr);
1878     if (lstrcmpW(stringw, szMSITEST) != 0)
1879     {
1880         len = WideCharToMultiByte(CP_ACP, 0, stringw, -1, string, MAX_PATH, NULL, NULL);
1881         ok(len, "WideCharToMultiByteChar returned error %d\n", GetLastError());
1882         ok(0, "Property \"ProductName\" expected to be \"MSITEST\" but was \"%s\"\n", string);
1883     }
1884
1885     /* Session::Property, put */
1886     hr = Session_PropertyPut(pSession, szProductName, szProductName);
1887     ok(hr == S_OK, "Session_PropertyPut failed, hresult 0x%08x\n", hr);
1888     memset(stringw, 0, sizeof(stringw));
1889     hr = Session_PropertyGet(pSession, szProductName, stringw);
1890     ok(hr == S_OK, "Session_PropertyGet failed, hresult 0x%08x\n", hr);
1891     if (lstrcmpW(stringw, szProductName) != 0)
1892     {
1893         len = WideCharToMultiByte(CP_ACP, 0, stringw, -1, string, MAX_PATH, NULL, NULL);
1894         ok(len, "WideCharToMultiByteChar returned error %d\n", GetLastError());
1895         ok(0, "Property \"ProductName\" expected to be \"ProductName\" but was \"%s\"\n", string);
1896     }
1897
1898     /* Try putting a property using empty property identifier */
1899     hr = Session_PropertyPut(pSession, szEmpty, szProductName);
1900     ok(hr == DISP_E_EXCEPTION, "Session_PropertyPut failed, hresult 0x%08x\n", hr);
1901     ok_exception(hr, szPropertyName);
1902
1903     /* Try putting a property using illegal property identifier */
1904     hr = Session_PropertyPut(pSession, szEquals, szProductName);
1905     ok(hr == S_OK, "Session_PropertyPut failed, hresult 0x%08x\n", hr);
1906
1907     /* Session::Language, get */
1908     hr = Session_LanguageGet(pSession, &len);
1909     ok(hr == S_OK, "Session_LanguageGet failed, hresult 0x%08x\n", hr);
1910     /* Not sure how to check the language is correct */
1911
1912     /* Session::Mode, get */
1913     hr = Session_ModeGet(pSession, MSIRUNMODE_REBOOTATEND, &bool);
1914     ok(hr == S_OK, "Session_ModeGet failed, hresult 0x%08x\n", hr);
1915     ok(!bool, "Reboot at end session mode is %d\n", bool);
1916
1917     hr = Session_ModeGet(pSession, MSIRUNMODE_MAINTENANCE, &bool);
1918     ok(hr == S_OK, "Session_ModeGet failed, hresult 0x%08x\n", hr);
1919     ok(!bool, "Maintenance mode is %d\n", bool);
1920
1921     /* Session::Mode, put */
1922     hr = Session_ModePut(pSession, MSIRUNMODE_REBOOTATEND, TRUE);
1923     ok(hr == S_OK, "Session_ModePut failed, hresult 0x%08x\n", hr);
1924     hr = Session_ModeGet(pSession, MSIRUNMODE_REBOOTATEND, &bool);
1925     ok(hr == S_OK, "Session_ModeGet failed, hresult 0x%08x\n", hr);
1926     ok(bool, "Reboot at end session mode is %d, expected 1\n", bool);
1927     hr = Session_ModePut(pSession, MSIRUNMODE_REBOOTATEND, FALSE);  /* set it again so we don't reboot */
1928     ok(hr == S_OK, "Session_ModePut failed, hresult 0x%08x\n", hr);
1929
1930     hr = Session_ModePut(pSession, MSIRUNMODE_REBOOTNOW, TRUE);
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_ModeGet(pSession, MSIRUNMODE_REBOOTNOW, &bool);
1935     ok(hr == S_OK, "Session_ModeGet failed, hresult 0x%08x\n", hr);
1936     ok(bool, "Reboot now mode is %d, expected 1\n", bool);
1937
1938     hr = Session_ModePut(pSession, MSIRUNMODE_REBOOTNOW, FALSE);  /* set it again so we don't reboot */
1939     todo_wine ok(hr == S_OK, "Session_ModePut failed, hresult 0x%08x\n", hr);
1940     if (hr == DISP_E_EXCEPTION) ok_exception(hr, szModeFlag);
1941
1942     hr = Session_ModePut(pSession, MSIRUNMODE_MAINTENANCE, TRUE);
1943     ok(hr == DISP_E_EXCEPTION, "Session_ModePut failed, hresult 0x%08x\n", hr);
1944     ok_exception(hr, szModeFlag);
1945
1946     /* Session::Database, get */
1947     hr = Session_Database(pSession, &pDatabase);
1948     ok(hr == S_OK, "Session_Database failed, hresult 0x%08x\n", hr);
1949     if (hr == S_OK)
1950     {
1951         test_Database(pDatabase, TRUE);
1952         IDispatch_Release(pDatabase);
1953     }
1954
1955     /* Session::EvaluateCondition */
1956     hr = Session_EvaluateCondition(pSession, NULL, &myint);
1957     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1958     ok(myint == MSICONDITION_NONE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1959
1960     hr = Session_EvaluateCondition(pSession, szEmpty, &myint);
1961     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1962     ok(myint == MSICONDITION_NONE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1963
1964     hr = Session_EvaluateCondition(pSession, szEquals, &myint);
1965     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1966     ok(myint == MSICONDITION_ERROR, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1967
1968     /* Session::DoAction(CostInitialize) must occur before the next statements */
1969     hr = Session_DoAction(pSession, szCostInitialize, &myint);
1970     ok(hr == S_OK, "Session_DoAction failed, hresult 0x%08x\n", hr);
1971     ok(myint == IDOK, "DoAction(CostInitialize) returned %d, %d expected\n", myint, IDOK);
1972
1973     /* Session::SetInstallLevel */
1974     hr = Session_SetInstallLevel(pSession, INSTALLLEVEL_MINIMUM);
1975     ok(hr == S_OK, "Session_SetInstallLevel failed, hresult 0x%08x\n", hr);
1976
1977     /* Session::FeatureCurrentState, get */
1978     hr = Session_FeatureCurrentState(pSession, szOne, &myint);
1979     ok(hr == S_OK, "Session_FeatureCurrentState failed, hresult 0x%08x\n", hr);
1980     ok(myint == INSTALLSTATE_UNKNOWN, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1981
1982     /* Session::Message */
1983     hr = Installer_CreateRecord(0, &record);
1984     ok(hr == S_OK, "Installer_CreateRecord failed: %08x\n", hr);
1985     hr = Session_Message(pSession, INSTALLMESSAGE_INFO, record, &myint);
1986     ok(hr == S_OK, "Session_Message failed: %08x\n", hr);
1987     ok(myint == 0, "Session_Message returned %x\n", myint);
1988
1989     /* Session::EvaluateCondition */
1990     hr = Session_EvaluateCondition(pSession, szOneStateFalse, &myint);
1991     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1992     ok(myint == MSICONDITION_FALSE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1993
1994     hr = Session_EvaluateCondition(pSession, szOneStateTrue, &myint);
1995     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1996     ok(myint == MSICONDITION_TRUE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1997
1998     /* Session::FeatureRequestState, put */
1999     hr = Session_FeatureRequestStatePut(pSession, szOne, INSTALLSTATE_ADVERTISED);
2000     ok(hr == S_OK, "Session_FeatureRequestStatePut failed, hresult 0x%08x\n", hr);
2001     hr = Session_FeatureRequestStateGet(pSession, szOne, &myint);
2002     ok(hr == S_OK, "Session_FeatureRequestStateGet failed, hresult 0x%08x\n", hr);
2003     ok(myint == INSTALLSTATE_ADVERTISED, "Feature request state was %d but expected %d\n", myint, INSTALLSTATE_ADVERTISED);
2004
2005     /* Session::EvaluateCondition */
2006     hr = Session_EvaluateCondition(pSession, szOneActionFalse, &myint);
2007     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
2008     ok(myint == MSICONDITION_FALSE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
2009
2010     hr = Session_EvaluateCondition(pSession, szOneActionTrue, &myint);
2011     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
2012     ok(myint == MSICONDITION_TRUE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
2013 }
2014
2015 /* delete key and all its subkeys */
2016 static DWORD delete_key( HKEY hkey )
2017 {
2018     char name[MAX_PATH];
2019     DWORD ret;
2020
2021     while (!(ret = RegEnumKeyA(hkey, 0, name, sizeof(name))))
2022     {
2023         HKEY tmp;
2024         if (!(ret = RegOpenKeyExA( hkey, name, 0, KEY_ENUMERATE_SUB_KEYS, &tmp )))
2025         {
2026             ret = delete_key( tmp );
2027             RegCloseKey( tmp );
2028         }
2029         if (ret) break;
2030     }
2031     if (ret != ERROR_NO_MORE_ITEMS) return ret;
2032     RegDeleteKeyA( hkey, "" );
2033     return 0;
2034 }
2035
2036 static void test_Installer_RegistryValue(void)
2037 {
2038     static const DWORD qw[2] = { 0x12345678, 0x87654321 };
2039     static const WCHAR szKey[] = { 'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\','T','e','s','t',0 };
2040     static const WCHAR szOne[] = { 'O','n','e',0 };
2041     static const WCHAR szTwo[] = { 'T','w','o',0 };
2042     static const WCHAR szThree[] = { 'T','h','r','e','e',0 };
2043     static const WCHAR szREG_BINARY[] = { '(','R','E','G','_','B','I','N','A','R','Y',')',0 };
2044     static const WCHAR szFour[] = { 'F','o','u','r',0 };
2045     static const WCHAR szExpand[] = { '%','M','S','I','T','E','S','T','%',0 };
2046     static const WCHAR szFive[] = { 'F','i','v','e',0,'H','i',0,0 };
2047     static const WCHAR szFiveHi[] = { 'F','i','v','e','\n','H','i',0 };
2048     static const WCHAR szSix[] = { 'S','i','x',0 };
2049     static const WCHAR szREG_[] = { '(','R','E','G','_',']',0 };
2050     static const WCHAR szSeven[] = { 'S','e','v','e','n',0 };
2051     static const WCHAR szEight[] = { 'E','i','g','h','t',0 };
2052     static const WCHAR szBlank[] = { 0 };
2053     VARIANT varresult;
2054     VARIANTARG vararg;
2055     WCHAR szString[MAX_PATH];
2056     HKEY hkey, hkey_sub;
2057     HKEY curr_user = (HKEY)1;
2058     HRESULT hr;
2059     BOOL bRet;
2060     LONG lRet;
2061
2062     /* Delete keys */
2063     SetLastError(0xdeadbeef);
2064     lRet = RegOpenKeyW( HKEY_CURRENT_USER, szKey, &hkey );
2065     if (!lRet && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
2066     {
2067         win_skip("Needed W-functions are not implemented\n");
2068         return;
2069     }
2070     if (!lRet)
2071         delete_key( hkey );
2072
2073     /* Does our key exist? Shouldn't; check with all three possible value parameter types */
2074     hr = Installer_RegistryValueE(curr_user, szKey, &bRet);
2075     ok(hr == S_OK, "Installer_RegistryValueE failed, hresult 0x%08x\n", hr);
2076     ok(!bRet, "Registry key expected to not exist, but Installer_RegistryValue claims it does\n");
2077
2078     memset(szString, 0, sizeof(szString));
2079     hr = Installer_RegistryValueW(curr_user, szKey, NULL, szString);
2080     ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2081
2082     memset(szString, 0, sizeof(szString));
2083     hr = Installer_RegistryValueI(curr_user, szKey, 0, szString, VT_BSTR);
2084     ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2085
2086     /* Create key */
2087     ok(!RegCreateKeyW( HKEY_CURRENT_USER, szKey, &hkey ), "RegCreateKeyW failed\n");
2088
2089     ok(!RegSetValueExW(hkey,szOne,0,REG_SZ, (const BYTE *)szOne, sizeof(szOne)),
2090         "RegSetValueExW failed\n");
2091     ok(!RegSetValueExW(hkey,szTwo,0,REG_DWORD, (const BYTE *)qw, 4),
2092         "RegSetValueExW failed\n");
2093     ok(!RegSetValueExW(hkey,szThree,0,REG_BINARY, (const BYTE *)qw, 4),
2094         "RegSetValueExW failed\n");
2095     bRet = SetEnvironmentVariableA("MSITEST", "Four");
2096     ok(bRet, "SetEnvironmentVariableA failed %d\n", GetLastError());
2097     ok(!RegSetValueExW(hkey,szFour,0,REG_EXPAND_SZ, (const BYTE *)szExpand, sizeof(szExpand)),
2098         "RegSetValueExW failed\n");
2099     ok(!RegSetValueExW(hkey,szFive,0,REG_MULTI_SZ, (const BYTE *)szFive, sizeof(szFive)),
2100         "RegSetValueExW failed\n");
2101     ok(!RegSetValueExW(hkey,szSix,0,REG_QWORD, (const BYTE *)qw, 8),
2102         "RegSetValueExW failed\n");
2103     ok(!RegSetValueExW(hkey,szSeven,0,REG_NONE, NULL, 0),
2104         "RegSetValueExW failed\n");
2105
2106     ok(!RegSetValueExW(hkey,NULL,0,REG_SZ, (const BYTE *)szOne, sizeof(szOne)),
2107         "RegSetValueExW failed\n");
2108
2109     ok(!RegCreateKeyW( hkey, szEight, &hkey_sub ), "RegCreateKeyW failed\n");
2110
2111     /* Does our key exist? It should, and make sure we retrieve the correct default value */
2112     bRet = FALSE;
2113     hr = Installer_RegistryValueE(curr_user, szKey, &bRet);
2114     ok(hr == S_OK, "Installer_RegistryValueE failed, hresult 0x%08x\n", hr);
2115     ok(bRet, "Registry key expected to exist, but Installer_RegistryValue claims it does not\n");
2116
2117     memset(szString, 0, sizeof(szString));
2118     hr = Installer_RegistryValueW(curr_user, szKey, NULL, szString);
2119     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2120     ok_w2("Default registry value \"%s\" does not match expected \"%s\"\n", szString, szOne);
2121
2122     /* Ask for the value of a nonexistent key */
2123     memset(szString, 0, sizeof(szString));
2124     hr = Installer_RegistryValueW(curr_user, szKey, szExpand, szString);
2125     ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2126
2127     /* Get values of keys */
2128     memset(szString, 0, sizeof(szString));
2129     hr = Installer_RegistryValueW(curr_user, szKey, szOne, szString);
2130     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2131     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szOne);
2132
2133     VariantInit(&vararg);
2134     V_VT(&vararg) = VT_BSTR;
2135     V_BSTR(&vararg) = SysAllocString(szTwo);
2136     hr = Installer_RegistryValue(curr_user, szKey, vararg, &varresult, VT_I4);
2137     ok(hr == S_OK, "Installer_RegistryValue failed, hresult 0x%08x\n", hr);
2138     ok(V_I4(&varresult) == 305419896, "Registry value %d does not match expected value\n", V_I4(&varresult));
2139     VariantClear(&varresult);
2140
2141     memset(szString, 0, sizeof(szString));
2142     hr = Installer_RegistryValueW(curr_user, szKey, szThree, szString);
2143     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2144     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szREG_BINARY);
2145
2146     memset(szString, 0, sizeof(szString));
2147     hr = Installer_RegistryValueW(curr_user, szKey, szFour, szString);
2148     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2149     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szFour);
2150
2151     /* Vista does not NULL-terminate this case */
2152     memset(szString, 0, sizeof(szString));
2153     hr = Installer_RegistryValueW(curr_user, szKey, szFive, szString);
2154     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2155     ok_w2n("Registry value \"%s\" does not match expected \"%s\"\n",
2156            szString, szFiveHi, lstrlenW(szFiveHi));
2157
2158     memset(szString, 0, sizeof(szString));
2159     hr = Installer_RegistryValueW(curr_user, szKey, szSix, szString);
2160     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2161     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szREG_);
2162
2163     VariantInit(&vararg);
2164     V_VT(&vararg) = VT_BSTR;
2165     V_BSTR(&vararg) = SysAllocString(szSeven);
2166     hr = Installer_RegistryValue(curr_user, szKey, vararg, &varresult, VT_EMPTY);
2167     ok(hr == S_OK, "Installer_RegistryValue failed, hresult 0x%08x\n", hr);
2168
2169     /* Get string class name for the key */
2170     memset(szString, 0, sizeof(szString));
2171     hr = Installer_RegistryValueI(curr_user, szKey, 0, szString, VT_BSTR);
2172     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2173     ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szBlank);
2174
2175     /* Get name of a value by positive number (RegEnumValue like), valid index */
2176     memset(szString, 0, sizeof(szString));
2177     hr = Installer_RegistryValueI(curr_user, szKey, 2, szString, VT_BSTR);
2178     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2179     /* RegEnumValue order seems different on wine */
2180     todo_wine ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szTwo);
2181
2182     /* Get name of a value by positive number (RegEnumValue like), invalid index */
2183     memset(szString, 0, sizeof(szString));
2184     hr = Installer_RegistryValueI(curr_user, szKey, 10, szString, VT_EMPTY);
2185     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2186
2187     /* Get name of a subkey by negative number (RegEnumValue like), valid index */
2188     memset(szString, 0, sizeof(szString));
2189     hr = Installer_RegistryValueI(curr_user, szKey, -1, szString, VT_BSTR);
2190     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2191     ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szEight);
2192
2193     /* Get name of a subkey by negative number (RegEnumValue like), invalid index */
2194     memset(szString, 0, sizeof(szString));
2195     hr = Installer_RegistryValueI(curr_user, szKey, -10, szString, VT_EMPTY);
2196     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2197
2198     /* clean up */
2199     delete_key(hkey);
2200 }
2201
2202 static void test_Installer_Products(BOOL bProductInstalled)
2203 {
2204     WCHAR szString[MAX_PATH];
2205     HRESULT hr;
2206     int idx;
2207     IUnknown *pUnk = NULL;
2208     IEnumVARIANT *pEnum = NULL;
2209     VARIANT var;
2210     ULONG celt;
2211     int iCount, iValue;
2212     IDispatch *pStringList = NULL;
2213     BOOL bProductFound = FALSE;
2214
2215     /* Installer::Products */
2216     hr = Installer_Products(&pStringList);
2217     ok(hr == S_OK, "Installer_Products failed, hresult 0x%08x\n", hr);
2218     if (hr == S_OK)
2219     {
2220         /* StringList::_NewEnum */
2221         hr = StringList__NewEnum(pStringList, &pUnk);
2222         ok(hr == S_OK, "StringList_NewEnum failed, hresult 0x%08x\n", hr);
2223         if (hr == S_OK)
2224         {
2225             hr = IUnknown_QueryInterface(pUnk, &IID_IEnumVARIANT, (void **)&pEnum);
2226             ok (hr == S_OK, "IUnknown::QueryInterface returned 0x%08x\n", hr);
2227         }
2228         if (!pEnum)
2229             skip("IEnumVARIANT tests\n");
2230
2231         /* StringList::Count */
2232         hr = StringList_Count(pStringList, &iCount);
2233         ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr);
2234
2235         for (idx=0; idx<iCount; idx++)
2236         {
2237             /* StringList::Item */
2238             memset(szString, 0, sizeof(szString));
2239             hr = StringList_Item(pStringList, idx, szString);
2240             ok(hr == S_OK, "StringList_Item failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);
2241
2242             if (hr == S_OK)
2243             {
2244                 /* Installer::ProductState */
2245                 hr = Installer_ProductState(szString, &iValue);
2246                 ok(hr == S_OK, "Installer_ProductState failed, hresult 0x%08x\n", hr);
2247                 if (hr == S_OK)
2248                     ok(iValue == INSTALLSTATE_DEFAULT || iValue == INSTALLSTATE_ADVERTISED, "Installer_ProductState returned %d, expected %d or %d\n", iValue, INSTALLSTATE_DEFAULT, INSTALLSTATE_ADVERTISED);
2249
2250                 /* Not found our product code yet? Check */
2251                 if (!bProductFound && !lstrcmpW(szString, szProductCode))
2252                     bProductFound = TRUE;
2253
2254                 /* IEnumVARIANT::Next */
2255                 if (pEnum)
2256                 {
2257                     hr = IEnumVARIANT_Next(pEnum, 1, &var, &celt);
2258                     ok(hr == S_OK, "IEnumVARIANT_Next failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);
2259                     ok(celt == 1, "%d items were retrieved, expected 1\n", celt);
2260                     ok(V_VT(&var) == VT_BSTR, "IEnumVARIANT_Next returned variant of type %d, expected %d\n", V_VT(&var), VT_BSTR);
2261                     ok_w2("%s returned by StringList_Item does not match %s returned by IEnumVARIANT_Next\n", szString, V_BSTR(&var));
2262                     VariantClear(&var);
2263                 }
2264             }
2265         }
2266
2267         if (bProductInstalled)
2268         {
2269             ok(bProductInstalled == bProductFound, "Product expected to %s installed but product code was %s\n",
2270                bProductInstalled ? "be" : "not be",
2271                bProductFound ? "found" : "not found");
2272         }
2273
2274         if (pEnum)
2275         {
2276             IEnumVARIANT *pEnum2 = NULL;
2277
2278             if (0) /* Crashes on Windows XP */
2279             {
2280                 /* IEnumVARIANT::Clone, NULL pointer */
2281                 IEnumVARIANT_Clone(pEnum, NULL);
2282             }
2283
2284             /* IEnumVARIANT::Clone */
2285             hr = IEnumVARIANT_Clone(pEnum, &pEnum2);
2286             ok(hr == S_OK, "IEnumVARIANT_Clone failed, hresult 0x%08x\n", hr);
2287             if (hr == S_OK)
2288             {
2289                 /* IEnumVARIANT::Clone is supposed to save the position, but it actually just goes back to the beginning */
2290
2291                 /* IEnumVARIANT::Next of the clone */
2292                 if (iCount)
2293                 {
2294                     hr = IEnumVARIANT_Next(pEnum2, 1, &var, &celt);
2295                     ok(hr == S_OK, "IEnumVARIANT_Next failed, hresult 0x%08x\n", hr);
2296                     ok(celt == 1, "%d items were retrieved, expected 0\n", celt);
2297                     ok(V_VT(&var) == VT_BSTR, "IEnumVARIANT_Next returned variant of type %d, expected %d\n", V_VT(&var), VT_BSTR);
2298                     VariantClear(&var);
2299                 }
2300                 else
2301                     skip("IEnumVARIANT::Next of clone will not return success with 0 products\n");
2302
2303                 IEnumVARIANT_Release(pEnum2);
2304             }
2305
2306             /* IEnumVARIANT::Skip should fail */
2307             hr = IEnumVARIANT_Skip(pEnum, 1);
2308             ok(hr == S_FALSE, "IEnumVARIANT_Skip failed, hresult 0x%08x\n", hr);
2309
2310             /* IEnumVARIANT::Next, NULL variant pointer */
2311             hr = IEnumVARIANT_Next(pEnum, 1, NULL, &celt);
2312             ok(hr == S_FALSE, "IEnumVARIANT_Next failed, hresult 0x%08x\n", hr);
2313             ok(celt == 0, "%d items were retrieved, expected 0\n", celt);
2314
2315             /* IEnumVARIANT::Next, should not return any more items */
2316             hr = IEnumVARIANT_Next(pEnum, 1, &var, &celt);
2317             ok(hr == S_FALSE, "IEnumVARIANT_Next failed, hresult 0x%08x\n", hr);
2318             ok(celt == 0, "%d items were retrieved, expected 0\n", celt);
2319             VariantClear(&var);
2320
2321             /* IEnumVARIANT::Reset */
2322             hr = IEnumVARIANT_Reset(pEnum);
2323             ok(hr == S_OK, "IEnumVARIANT_Reset failed, hresult 0x%08x\n", hr);
2324
2325             if (iCount)
2326             {
2327                 /* IEnumVARIANT::Skip to the last product */
2328                 hr = IEnumVARIANT_Skip(pEnum, iCount-1);
2329                 ok(hr == S_OK, "IEnumVARIANT_Skip failed, hresult 0x%08x\n", hr);
2330
2331                 /* IEnumVARIANT::Next should match the very last retrieved value, also makes sure it works with
2332                  * NULL celt pointer. */
2333                 hr = IEnumVARIANT_Next(pEnum, 1, &var, NULL);
2334                 ok(hr == S_OK, "IEnumVARIANT_Next failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);
2335                 ok(V_VT(&var) == VT_BSTR, "IEnumVARIANT_Next returned variant of type %d, expected %d\n", V_VT(&var), VT_BSTR);
2336                 ok_w2("%s returned by StringList_Item does not match %s returned by IEnumVARIANT_Next\n", szString, V_BSTR(&var));
2337                 VariantClear(&var);
2338             }
2339             else
2340                 skip("IEnumVARIANT::Skip impossible for 0 products\n");
2341         }
2342
2343         /* StringList::Item using an invalid index */
2344         memset(szString, 0, sizeof(szString));
2345         hr = StringList_Item(pStringList, iCount, szString);
2346         ok(hr == DISP_E_BADINDEX, "StringList_Item for an invalid index did not return DISP_E_BADINDEX, hresult 0x%08x\n", hr);
2347
2348         if (pEnum) IEnumVARIANT_Release(pEnum);
2349         if (pUnk) IUnknown_Release(pUnk);
2350         IDispatch_Release(pStringList);
2351     }
2352 }
2353
2354 /* Delete a registry subkey, including all its subkeys (RegDeleteKey does not work on keys with subkeys without
2355  * deleting the subkeys first) */
2356 static UINT delete_registry_key(HKEY hkeyParent, LPCSTR subkey, REGSAM access)
2357 {
2358     UINT ret;
2359     CHAR *string = NULL;
2360     HKEY hkey;
2361     DWORD dwSize;
2362
2363     ret = RegOpenKeyEx(hkeyParent, subkey, 0, access, &hkey);
2364     if (ret != ERROR_SUCCESS) return ret;
2365     ret = RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL);
2366     if (ret != ERROR_SUCCESS) return ret;
2367     if (!(string = HeapAlloc(GetProcessHeap(), 0, ++dwSize))) return ERROR_NOT_ENOUGH_MEMORY;
2368
2369     while (RegEnumKeyA(hkey, 0, string, dwSize) == ERROR_SUCCESS)
2370         delete_registry_key(hkey, string, access);
2371
2372     RegCloseKey(hkey);
2373     HeapFree(GetProcessHeap(), 0, string);
2374     delete_key_portable(hkeyParent, subkey, access);
2375     return ERROR_SUCCESS;
2376 }
2377
2378 /* Find a specific registry subkey at any depth within the given key and subkey and return its parent key. */
2379 static UINT find_registry_key(HKEY hkeyParent, LPCSTR subkey, LPCSTR findkey, REGSAM access, HKEY *phkey)
2380 {
2381     UINT ret;
2382     CHAR *string = NULL;
2383     int idx = 0;
2384     HKEY hkey;
2385     DWORD dwSize;
2386     BOOL found = FALSE;
2387
2388     *phkey = 0;
2389
2390     ret = RegOpenKeyEx(hkeyParent, subkey, 0, access, &hkey);
2391     if (ret != ERROR_SUCCESS) return ret;
2392     ret = RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL);
2393     if (ret != ERROR_SUCCESS) return ret;
2394     if (!(string = HeapAlloc(GetProcessHeap(), 0, ++dwSize))) return ERROR_NOT_ENOUGH_MEMORY;
2395
2396     while (!found &&
2397            RegEnumKeyA(hkey, idx++, string, dwSize) == ERROR_SUCCESS)
2398     {
2399         if (!strcmp(string, findkey))
2400         {
2401             *phkey = hkey;
2402             found = TRUE;
2403         }
2404         else if (find_registry_key(hkey, string, findkey, access, phkey) == ERROR_SUCCESS) found = TRUE;
2405     }
2406
2407     if (*phkey != hkey) RegCloseKey(hkey);
2408     HeapFree(GetProcessHeap(), 0, string);
2409     return (found ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND);
2410 }
2411
2412 static void test_Installer_InstallProduct(void)
2413 {
2414     HRESULT hr;
2415     CHAR path[MAX_PATH];
2416     WCHAR szString[MAX_PATH];
2417     LONG res;
2418     HKEY hkey;
2419     DWORD num, size, type;
2420     int iValue, iCount;
2421     IDispatch *pStringList = NULL;
2422     REGSAM access = KEY_ALL_ACCESS;
2423
2424     if (is_wow64)
2425         access |= KEY_WOW64_64KEY;
2426
2427     create_test_files();
2428
2429     /* Avoid an interactive dialog in case of insufficient privileges. */
2430     hr = Installer_UILevelPut(INSTALLUILEVEL_NONE);
2431     ok(hr == S_OK, "Expected UILevel propery put invoke to return S_OK, got 0x%08x\n", hr);
2432
2433     /* Installer::InstallProduct */
2434     hr = Installer_InstallProduct(szMsifile, NULL);
2435     if (hr == DISP_E_EXCEPTION)
2436     {
2437         skip("InstallProduct failed, insufficient rights?\n");
2438         delete_test_files();
2439         return;
2440     }
2441     ok(hr == S_OK, "Installer_InstallProduct failed, hresult 0x%08x\n", hr);
2442
2443     /* Installer::ProductState for our product code, which has been installed */
2444     hr = Installer_ProductState(szProductCode, &iValue);
2445     ok(hr == S_OK, "Installer_ProductState failed, hresult 0x%08x\n", hr);
2446     ok(iValue == INSTALLSTATE_DEFAULT, "Installer_ProductState returned %d, expected %d\n", iValue, INSTALLSTATE_DEFAULT);
2447
2448     /* Installer::ProductInfo for our product code */
2449
2450     /* NULL attribute */
2451     memset(szString, 0, sizeof(szString));
2452     hr = Installer_ProductInfo(szProductCode, NULL, szString);
2453     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2454     ok_exception(hr, szProductInfoException);
2455
2456     /* Nonexistent attribute */
2457     memset(szString, 0, sizeof(szString));
2458     hr = Installer_ProductInfo(szProductCode, szMsifile, szString);
2459     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2460     ok_exception(hr, szProductInfoException);
2461
2462     /* Package name */
2463     memset(szString, 0, sizeof(szString));
2464     hr = Installer_ProductInfo(szProductCode, WINE_INSTALLPROPERTY_PACKAGENAMEW, szString);
2465     ok(hr == S_OK, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2466     todo_wine ok_w2("Installer_ProductInfo returned %s but expected %s\n", szString, szMsifile);
2467
2468     /* Product name */
2469     memset(szString, 0, sizeof(szString));
2470     hr = Installer_ProductInfo(szProductCode, WINE_INSTALLPROPERTY_PRODUCTNAMEW, szString);
2471     ok(hr == S_OK, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2472     todo_wine ok_w2("Installer_ProductInfo returned %s but expected %s\n", szString, szMSITEST);
2473
2474     /* Installer::Products */
2475     test_Installer_Products(TRUE);
2476
2477     /* Installer::RelatedProducts for our upgrade code */
2478     hr = Installer_RelatedProducts(szUpgradeCode, &pStringList);
2479     ok(hr == S_OK, "Installer_RelatedProducts failed, hresult 0x%08x\n", hr);
2480     if (hr == S_OK)
2481     {
2482         /* StringList::Count */
2483         hr = StringList_Count(pStringList, &iCount);
2484         ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr);
2485         ok(iCount == 1, "Expected one related product but found %d\n", iCount);
2486
2487         /* StringList::Item */
2488         memset(szString, 0, sizeof(szString));
2489         hr = StringList_Item(pStringList, 0, szString);
2490         ok(hr == S_OK, "StringList_Item failed (idx 0, count %d), hresult 0x%08x\n", iCount, hr);
2491         ok_w2("StringList_Item returned %s but expected %s\n", szString, szProductCode);
2492
2493         IDispatch_Release(pStringList);
2494     }
2495
2496     /* Check & clean up installed files & registry keys */
2497     ok(delete_pf("msitest\\cabout\\new\\five.txt", TRUE), "File not installed\n");
2498     ok(delete_pf("msitest\\cabout\\new", FALSE), "File not installed\n");
2499     ok(delete_pf("msitest\\cabout\\four.txt", TRUE), "File not installed\n");
2500     ok(delete_pf("msitest\\cabout", FALSE), "File not installed\n");
2501     ok(delete_pf("msitest\\changed\\three.txt", TRUE), "File not installed\n");
2502     ok(delete_pf("msitest\\changed", FALSE), "File not installed\n");
2503     ok(delete_pf("msitest\\first\\two.txt", TRUE), "File not installed\n");
2504     ok(delete_pf("msitest\\first", FALSE), "File not installed\n");
2505     ok(delete_pf("msitest\\one.txt", TRUE), "File not installed\n");
2506     ok(delete_pf("msitest\\filename", TRUE), "File not installed\n");
2507     ok(delete_pf("msitest", FALSE), "File not installed\n");
2508
2509     res = RegOpenKeyA(HKEY_CURRENT_USER, "SOFTWARE\\Wine\\msitest", &hkey);
2510     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2511
2512     size = MAX_PATH;
2513     type = REG_SZ;
2514     res = RegQueryValueExA(hkey, "Name", NULL, &type, (LPBYTE)path, &size);
2515     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2516     ok(!lstrcmpA(path, "imaname"), "Expected imaname, got %s\n", path);
2517
2518     size = MAX_PATH;
2519     type = REG_SZ;
2520     res = RegQueryValueExA(hkey, "blah", NULL, &type, (LPBYTE)path, &size);
2521     ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
2522
2523     size = sizeof(num);
2524     type = REG_DWORD;
2525     res = RegQueryValueExA(hkey, "number", NULL, &type, (LPBYTE)&num, &size);
2526     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2527     ok(num == 314, "Expected 314, got %d\n", num);
2528
2529     size = MAX_PATH;
2530     type = REG_SZ;
2531     res = RegQueryValueExA(hkey, "OrderTestName", NULL, &type, (LPBYTE)path, &size);
2532     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2533     ok(!lstrcmpA(path, "OrderTestValue"), "Expected imaname, got %s\n", path);
2534
2535     RegCloseKey(hkey);
2536
2537     res = RegDeleteKeyA(HKEY_CURRENT_USER, "SOFTWARE\\Wine\\msitest");
2538     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2539
2540     /* Remove registry keys written by RegisterProduct standard action */
2541     res = delete_key_portable(HKEY_LOCAL_MACHINE,
2542         "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{837450fa-a39b-4bc8-b321-08b393f784b3}",
2543                               KEY_WOW64_32KEY);
2544     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2545
2546     res = delete_key_portable(HKEY_LOCAL_MACHINE,
2547         "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UpgradeCodes\\D8E760ECA1E276347B43E42BDBDA5656", access);
2548     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2549
2550     res = find_registry_key(HKEY_LOCAL_MACHINE,
2551         "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData", "af054738b93a8cb43b12803b397f483b", access, &hkey);
2552     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2553
2554     res = delete_registry_key(hkey, "af054738b93a8cb43b12803b397f483b", access);
2555     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2556     RegCloseKey(hkey);
2557
2558     res = delete_key_portable(HKEY_LOCAL_MACHINE,
2559         "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\Products\\af054738b93a8cb43b12803b397f483b", access);
2560     ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
2561
2562     /* Remove registry keys written by PublishProduct standard action */
2563     res = RegOpenKey(HKEY_CURRENT_USER, "SOFTWARE\\Microsoft\\Installer", &hkey);
2564     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2565
2566     res = delete_registry_key(hkey, "Products\\af054738b93a8cb43b12803b397f483b", KEY_ALL_ACCESS);
2567     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2568
2569     res = RegDeleteKeyA(hkey, "UpgradeCodes\\D8E760ECA1E276347B43E42BDBDA5656");
2570     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2571
2572     RegCloseKey(hkey);
2573
2574     /* Delete installation files we installed */
2575     delete_test_files();
2576 }
2577
2578 static void test_Installer(void)
2579 {
2580     static WCHAR szCreateRecordException[] = { 'C','r','e','a','t','e','R','e','c','o','r','d',',','C','o','u','n','t',0 };
2581     static WCHAR szIntegerDataException[] = { 'I','n','t','e','g','e','r','D','a','t','a',',','F','i','e','l','d',0 };
2582     WCHAR szPath[MAX_PATH];
2583     HRESULT hr;
2584     IDispatch *pSession = NULL, *pDatabase = NULL, *pRecord = NULL, *pStringList = NULL;
2585     int iValue, iCount;
2586
2587     if (!pInstaller) return;
2588
2589     /* Installer::CreateRecord */
2590
2591     /* Test for error */
2592     hr = Installer_CreateRecord(-1, &pRecord);
2593     ok(hr == DISP_E_EXCEPTION, "Installer_CreateRecord failed, hresult 0x%08x\n", hr);
2594     ok_exception(hr, szCreateRecordException);
2595
2596     /* Test for success */
2597     hr = Installer_CreateRecord(1, &pRecord);
2598     ok(hr == S_OK, "Installer_CreateRecord failed, hresult 0x%08x\n", hr);
2599     ok(pRecord != NULL, "Installer_CreateRecord should not have returned NULL record\n");
2600     if (pRecord)
2601     {
2602         /* Record::FieldCountGet */
2603         hr = Record_FieldCountGet(pRecord, &iValue);
2604         ok(hr == S_OK, "Record_FiledCountGet failed, hresult 0x%08x\n", hr);
2605         ok(iValue == 1, "Record_FieldCountGet result was %d but expected 1\n", iValue);
2606
2607         /* Record::IntegerDataGet */
2608         hr = Record_IntegerDataGet(pRecord, 1, &iValue);
2609         ok(hr == S_OK, "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
2610         ok(iValue == MSI_NULL_INTEGER, "Record_IntegerDataGet result was %d but expected %d\n", iValue, MSI_NULL_INTEGER);
2611
2612         /* Record::IntegerDataGet, bad index */
2613         hr = Record_IntegerDataGet(pRecord, 10, &iValue);
2614         ok(hr == S_OK, "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
2615         ok(iValue == MSI_NULL_INTEGER, "Record_IntegerDataGet result was %d but expected %d\n", iValue, MSI_NULL_INTEGER);
2616
2617         /* Record::IntegerDataPut */
2618         hr = Record_IntegerDataPut(pRecord, 1, 100);
2619         ok(hr == S_OK, "Record_IntegerDataPut failed, hresult 0x%08x\n", hr);
2620
2621         /* Record::IntegerDataPut, bad index */
2622         hr = Record_IntegerDataPut(pRecord, 10, 100);
2623         ok(hr == DISP_E_EXCEPTION, "Record_IntegerDataPut failed, hresult 0x%08x\n", hr);
2624         ok_exception(hr, szIntegerDataException);
2625
2626         /* Record::IntegerDataGet */
2627         hr = Record_IntegerDataGet(pRecord, 1, &iValue);
2628         ok(hr == S_OK, "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
2629         ok(iValue == 100, "Record_IntegerDataGet result was %d but expected 100\n", iValue);
2630
2631         IDispatch_Release(pRecord);
2632     }
2633
2634     create_package(szPath);
2635
2636     /* Installer::OpenPackage */
2637     hr = Installer_OpenPackage(szPath, 0, &pSession);
2638     if (hr == DISP_E_EXCEPTION)
2639     {
2640         skip("OpenPackage failed, insufficient rights?\n");
2641         DeleteFileW(szPath);
2642         return;
2643     }
2644     ok(hr == S_OK, "Installer_OpenPackage failed, hresult 0x%08x\n", hr);
2645     if (hr == S_OK)
2646     {
2647         test_Session(pSession);
2648         IDispatch_Release(pSession);
2649     }
2650
2651     /* Installer::OpenDatabase */
2652     hr = Installer_OpenDatabase(szPath, (INT_PTR)MSIDBOPEN_TRANSACT, &pDatabase);
2653     ok(hr == S_OK, "Installer_OpenDatabase failed, hresult 0x%08x\n", hr);
2654     if (hr == S_OK)
2655     {
2656         test_Database(pDatabase, FALSE);
2657         IDispatch_Release(pDatabase);
2658     }
2659
2660     /* Installer::RegistryValue */
2661     test_Installer_RegistryValue();
2662
2663     /* Installer::ProductState for our product code, which should not be installed */
2664     hr = Installer_ProductState(szProductCode, &iValue);
2665     ok(hr == S_OK, "Installer_ProductState failed, hresult 0x%08x\n", hr);
2666     ok(iValue == INSTALLSTATE_UNKNOWN, "Installer_ProductState returned %d, expected %d\n", iValue, INSTALLSTATE_UNKNOWN);
2667
2668     /* Installer::ProductInfo for our product code, which should not be installed */
2669
2670     /* Package name */
2671     memset(szPath, 0, sizeof(szPath));
2672     hr = Installer_ProductInfo(szProductCode, WINE_INSTALLPROPERTY_PACKAGENAMEW, szPath);
2673     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2674     ok_exception(hr, szProductInfoException);
2675
2676     /* NULL attribute and NULL product code */
2677     memset(szPath, 0, sizeof(szPath));
2678     hr = Installer_ProductInfo(NULL, NULL, szPath);
2679     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2680     ok_exception(hr, szProductInfoException);
2681
2682     /* Installer::Products */
2683     test_Installer_Products(FALSE);
2684
2685     /* Installer::RelatedProducts for our upgrade code, should not find anything */
2686     hr = Installer_RelatedProducts(szUpgradeCode, &pStringList);
2687     ok(hr == S_OK, "Installer_RelatedProducts failed, hresult 0x%08x\n", hr);
2688     if (hr == S_OK)
2689     {
2690         /* StringList::Count */
2691         hr = StringList_Count(pStringList, &iCount);
2692         ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr);
2693         ok(!iCount, "Expected no related products but found %d\n", iCount);
2694
2695         IDispatch_Release(pStringList);
2696     }
2697
2698     /* Installer::Version */
2699     memset(szPath, 0, sizeof(szPath));
2700     hr = Installer_VersionGet(szPath);
2701     ok(hr == S_OK, "Installer_VersionGet failed, hresult 0x%08x\n", hr);
2702
2703     /* Installer::InstallProduct and other tests that depend on our product being installed */
2704     test_Installer_InstallProduct();
2705 }
2706
2707 START_TEST(automation)
2708 {
2709     DWORD len;
2710     char temp_path[MAX_PATH], prev_path[MAX_PATH];
2711     HRESULT hr;
2712     CLSID clsid;
2713     IUnknown *pUnk;
2714
2715     init_functionpointers();
2716
2717     if (pIsWow64Process)
2718         pIsWow64Process(GetCurrentProcess(), &is_wow64);
2719
2720     GetSystemTimeAsFileTime(&systemtime);
2721
2722     GetCurrentDirectoryA(MAX_PATH, prev_path);
2723     GetTempPath(MAX_PATH, temp_path);
2724     SetCurrentDirectoryA(temp_path);
2725
2726     lstrcpyA(CURR_DIR, temp_path);
2727     len = lstrlenA(CURR_DIR);
2728
2729     if(len && (CURR_DIR[len - 1] == '\\'))
2730         CURR_DIR[len - 1] = 0;
2731
2732     get_program_files_dir(PROG_FILES_DIR);
2733
2734     hr = OleInitialize(NULL);
2735     ok (hr == S_OK, "OleInitialize returned 0x%08x\n", hr);
2736     hr = CLSIDFromProgID(szProgId, &clsid);
2737     ok (hr == S_OK, "CLSIDFromProgID returned 0x%08x\n", hr);
2738     hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&pUnk);
2739     ok(hr == S_OK, "CoCreateInstance returned 0x%08x\n", hr);
2740
2741     if (pUnk)
2742     {
2743         hr = IUnknown_QueryInterface(pUnk, &IID_IDispatch, (void **)&pInstaller);
2744         ok (hr == S_OK, "IUnknown::QueryInterface returned 0x%08x\n", hr);
2745
2746         test_dispid();
2747         test_dispatch();
2748         test_Installer();
2749
2750         IDispatch_Release(pInstaller);
2751         IUnknown_Release(pUnk);
2752     }
2753
2754     OleUninitialize();
2755
2756     SetCurrentDirectoryA(prev_path);
2757 }