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