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