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