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