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