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