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