msi: Use the correct registry key when detecting a published product.
[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 static HRESULT SummaryInfo_PropertyCountGet(IDispatch *pSummaryInfo, int *pCount)
1300 {
1301     VARIANT varresult;
1302     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1303     HRESULT hr;
1304
1305     hr = invoke(pSummaryInfo, "PropertyCount", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1306     *pCount = V_I4(&varresult);
1307     VariantClear(&varresult);
1308     return hr;
1309 }
1310
1311 /* Test the various objects */
1312
1313 #define TEST_SUMMARYINFO_PROPERTIES_MODIFIED 4
1314
1315 static void test_SummaryInfo(IDispatch *pSummaryInfo, const msi_summary_info *info, int num_info, BOOL readonly)
1316 {
1317     static const WCHAR szPropertyException[] = { 'P','r','o','p','e','r','t','y',',','P','i','d',0 };
1318     static const WCHAR szTitle[] = { 'T','i','t','l','e',0 };
1319     VARIANT varresult, var;
1320     SYSTEMTIME st;
1321     HRESULT hr;
1322     int j;
1323
1324     /* SummaryInfo::PropertyCount */
1325     hr = SummaryInfo_PropertyCountGet(pSummaryInfo, &j);
1326     ok(hr == S_OK, "SummaryInfo_PropertyCount failed, hresult 0x%08x\n", hr);
1327     ok(j == num_info, "SummaryInfo_PropertyCount returned %d, expected %d\n", j, num_info);
1328
1329     /* SummaryInfo::Property, get for properties we have set */
1330     for (j = 0; j < num_info; j++)
1331     {
1332         const msi_summary_info *entry = &info[j];
1333
1334         int vt = entry->datatype;
1335         if (vt == VT_LPSTR) vt = VT_BSTR;
1336         else if (vt == VT_FILETIME) vt = VT_DATE;
1337         else if (vt == VT_I2) vt = VT_I4;
1338
1339         hr = SummaryInfo_PropertyGet(pSummaryInfo, entry->property, &varresult, vt);
1340         ok(hr == S_OK, "SummaryInfo_Property (pid %d) failed, hresult 0x%08x\n", entry->property, hr);
1341         if (V_VT(&varresult) != vt)
1342             skip("Skipping property tests due to type mismatch\n");
1343         else if (vt == VT_I4)
1344             ok(V_I4(&varresult) == entry->iValue, "SummaryInfo_Property (pid %d) I4 result expected to be %d, but was %d\n",
1345                entry->property, entry->iValue, V_I4(&varresult));
1346         else if (vt == VT_DATE)
1347         {
1348             FILETIME ft;
1349             DATE d;
1350
1351             FileTimeToLocalFileTime(entry->pftValue, &ft);
1352             FileTimeToSystemTime(&ft, &st);
1353             SystemTimeToVariantTime(&st, &d);
1354             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));
1355         }
1356         else if (vt == VT_BSTR)
1357         {
1358             ok_awplus("SummaryInfo_Property (pid %d) BSTR result expected to be %s, but was %s\n", entry->property, entry->szValue, V_BSTR(&varresult));
1359         }
1360         else
1361             skip("SummaryInfo_Property (pid %d) unhandled result type %d\n", entry->property, vt);
1362     }
1363
1364     /* SummaryInfo::Property, get; invalid arguments */
1365
1366     /* Invalid pids */
1367     hr = SummaryInfo_PropertyGet(pSummaryInfo, -1, &varresult, VT_EMPTY);
1368     ok(hr == DISP_E_EXCEPTION, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1369     ok_exception(hr, szPropertyException);
1370
1371     hr = SummaryInfo_PropertyGet(pSummaryInfo, 1000, &varresult, VT_EMPTY);
1372     ok(hr == DISP_E_EXCEPTION, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1373     ok_exception(hr, szPropertyException);
1374
1375     /* Unsupported pids */
1376     hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_DICTIONARY, &varresult, VT_EMPTY);
1377     ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1378
1379     hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_THUMBNAIL, &varresult, VT_EMPTY);
1380     ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1381
1382     /* Pids we have not set, one for each type */
1383     hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_CODEPAGE, &varresult, VT_EMPTY);
1384     ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1385
1386     hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_TITLE, &varresult, VT_EMPTY);
1387     ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1388
1389     hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_EDITTIME, &varresult, VT_EMPTY);
1390     ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1391
1392     hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_CHARCOUNT, &varresult, VT_EMPTY);
1393     ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1394
1395     if (!readonly)
1396     {
1397         /* SummaryInfo::Property, put; one for each type */
1398
1399         /* VT_I2 */
1400         VariantInit(&var);
1401         V_VT(&var) = VT_I2;
1402         V_I2(&var) = 1;
1403         hr = SummaryInfo_PropertyPut(pSummaryInfo, PID_CODEPAGE, &var);
1404         ok(hr == S_OK, "SummaryInfo_PropertyPut failed, hresult 0x%08x\n", hr);
1405
1406         hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_CODEPAGE, &varresult, VT_I4 /* NOT VT_I2 */);
1407         ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1408         ok(V_I2(&var) == V_I2(&varresult), "SummaryInfo_PropertyGet expected %d, but returned %d\n", V_I2(&var), V_I2(&varresult));
1409         VariantClear(&varresult);
1410         VariantClear(&var);
1411
1412         /* VT_BSTR */
1413         V_VT(&var) = VT_BSTR;
1414         V_BSTR(&var) = SysAllocString(szTitle);
1415         hr = SummaryInfo_PropertyPut(pSummaryInfo, PID_TITLE, &var);
1416         ok(hr == S_OK, "SummaryInfo_PropertyPut failed, hresult 0x%08x\n", hr);
1417
1418         hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_TITLE, &varresult, V_VT(&var));
1419         ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1420         ok_w2("SummaryInfo_PropertyGet expected %s, but returned %s\n", V_BSTR(&var), V_BSTR(&varresult));
1421         VariantClear(&varresult);
1422         VariantClear(&var);
1423
1424         /* VT_DATE */
1425         V_VT(&var) = VT_DATE;
1426         FileTimeToSystemTime(&systemtime, &st);
1427         SystemTimeToVariantTime(&st, &V_DATE(&var));
1428         hr = SummaryInfo_PropertyPut(pSummaryInfo, PID_LASTSAVE_DTM, &var);
1429         ok(hr == S_OK, "SummaryInfo_PropertyPut failed, hresult 0x%08x\n", hr);
1430
1431         hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_LASTSAVE_DTM, &varresult, V_VT(&var));
1432         ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1433         /* FIXME: Off by one second */
1434         todo_wine ok(V_DATE(&var) == V_DATE(&varresult), "SummaryInfo_PropertyGet expected %lf, but returned %lf\n", V_DATE(&var), V_DATE(&varresult));
1435         VariantClear(&varresult);
1436         VariantClear(&var);
1437
1438         /* VT_I4 */
1439         V_VT(&var) = VT_I4;
1440         V_I4(&var) = 1000;
1441         hr = SummaryInfo_PropertyPut(pSummaryInfo, PID_CHARCOUNT, &var);
1442         ok(hr == S_OK, "SummaryInfo_PropertyPut failed, hresult 0x%08x\n", hr);
1443
1444         hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_CHARCOUNT, &varresult, V_VT(&var));
1445         ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1446         ok(V_I4(&var) == V_I4(&varresult), "SummaryInfo_PropertyGet expected %d, but returned %d\n", V_I4(&var), V_I4(&varresult));
1447         VariantClear(&varresult);
1448         VariantClear(&var);
1449
1450         /* SummaryInfo::PropertyCount */
1451         hr = SummaryInfo_PropertyCountGet(pSummaryInfo, &j);
1452         ok(hr == S_OK, "SummaryInfo_PropertyCount failed, hresult 0x%08x\n", hr);
1453         ok(j == num_info+4, "SummaryInfo_PropertyCount returned %d, expected %d\n", j, num_info);
1454     }
1455 }
1456
1457 static void test_Database(IDispatch *pDatabase, BOOL readonly)
1458 {
1459     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 };
1460     static WCHAR szThree[] = { 'T','h','r','e','e',0 };
1461     static WCHAR szTwo[] = { 'T','w','o',0 };
1462     static WCHAR szStringDataField[] = { 'S','t','r','i','n','g','D','a','t','a',',','F','i','e','l','d',0 };
1463     static WCHAR szModifyModeRecord[] = { 'M','o','d','i','f','y',',','M','o','d','e',',','R','e','c','o','r','d',0 };
1464     IDispatch *pView = NULL, *pSummaryInfo = NULL;
1465     HRESULT hr;
1466
1467     hr = Database_OpenView(pDatabase, szSql, &pView);
1468     ok(hr == S_OK, "Database_OpenView failed, hresult 0x%08x\n", hr);
1469     if (hr == S_OK)
1470     {
1471         IDispatch *pRecord = NULL;
1472         WCHAR szString[MAX_PATH];
1473
1474         /* View::Execute */
1475         hr = View_Execute(pView, NULL);
1476         ok(hr == S_OK, "View_Execute failed, hresult 0x%08x\n", hr);
1477
1478         /* View::Fetch */
1479         hr = View_Fetch(pView, &pRecord);
1480         ok(hr == S_OK, "View_Fetch failed, hresult 0x%08x\n", hr);
1481         ok(pRecord != NULL, "View_Fetch should not have returned NULL record\n");
1482         if (pRecord)
1483         {
1484             /* Record::StringDataGet */
1485             memset(szString, 0, sizeof(szString));
1486             hr = Record_StringDataGet(pRecord, 1, szString);
1487             ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr);
1488             ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szThree);
1489
1490             /* Record::StringDataPut with correct index */
1491             hr = Record_StringDataPut(pRecord, 1, szTwo);
1492             ok(hr == S_OK, "Record_StringDataPut failed, hresult 0x%08x\n", hr);
1493
1494             /* Record::StringDataGet */
1495             memset(szString, 0, sizeof(szString));
1496             hr = Record_StringDataGet(pRecord, 1, szString);
1497             ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr);
1498             ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szTwo);
1499
1500             /* Record::StringDataPut with incorrect index */
1501             hr = Record_StringDataPut(pRecord, -1, szString);
1502             ok(hr == DISP_E_EXCEPTION, "Record_StringDataPut failed, hresult 0x%08x\n", hr);
1503             ok_exception(hr, szStringDataField);
1504
1505             /* View::Modify with incorrect parameters */
1506             hr = View_Modify(pView, -5, NULL);
1507             ok(hr == DISP_E_EXCEPTION, "View_Modify failed, hresult 0x%08x\n", hr);
1508             ok_exception(hr, szModifyModeRecord);
1509
1510             hr = View_Modify(pView, -5, pRecord);
1511             ok(hr == DISP_E_EXCEPTION, "View_Modify failed, hresult 0x%08x\n", hr);
1512             ok_exception(hr, szModifyModeRecord);
1513
1514             hr = View_Modify(pView, MSIMODIFY_REFRESH, NULL);
1515             ok(hr == DISP_E_EXCEPTION, "View_Modify failed, hresult 0x%08x\n", hr);
1516             ok_exception(hr, szModifyModeRecord);
1517
1518             /* View::Modify with MSIMODIFY_REFRESH should undo our changes */
1519             hr = View_Modify(pView, MSIMODIFY_REFRESH, pRecord);
1520             /* Wine's MsiViewModify currently does not support MSIMODIFY_REFRESH */
1521             todo_wine ok(hr == S_OK, "View_Modify failed, hresult 0x%08x\n", hr);
1522
1523             /* Record::StringDataGet, confirm that the record is back to its unmodified value */
1524             memset(szString, 0, sizeof(szString));
1525             hr = Record_StringDataGet(pRecord, 1, szString);
1526             ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr);
1527             todo_wine ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szThree);
1528
1529             IDispatch_Release(pRecord);
1530         }
1531
1532         /* View::Fetch */
1533         hr = View_Fetch(pView, &pRecord);
1534         ok(hr == S_OK, "View_Fetch failed, hresult 0x%08x\n", hr);
1535         ok(pRecord != NULL, "View_Fetch should not have returned NULL record\n");
1536         if (pRecord)
1537         {
1538             /* Record::StringDataGet */
1539             memset(szString, 0, sizeof(szString));
1540             hr = Record_StringDataGet(pRecord, 1, szString);
1541             ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr);
1542             ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szTwo);
1543
1544             IDispatch_Release(pRecord);
1545         }
1546
1547         /* View::Fetch */
1548         hr = View_Fetch(pView, &pRecord);
1549         ok(hr == S_OK, "View_Fetch failed, hresult 0x%08x\n", hr);
1550         ok(pRecord == NULL, "View_Fetch should have returned NULL record\n");
1551         if (pRecord)
1552             IDispatch_Release(pRecord);
1553
1554         /* View::Close */
1555         hr = View_Close(pView);
1556         ok(hr == S_OK, "View_Close failed, hresult 0x%08x\n", hr);
1557
1558         IDispatch_Release(pView);
1559     }
1560
1561     /* Database::SummaryInformation */
1562     hr = Database_SummaryInformation(pDatabase, TEST_SUMMARYINFO_PROPERTIES_MODIFIED, &pSummaryInfo);
1563     ok(hr == S_OK, "Database_SummaryInformation failed, hresult 0x%08x\n", hr);
1564     ok(pSummaryInfo != NULL, "Database_SummaryInformation should not have returned NULL record\n");
1565     if (pSummaryInfo)
1566     {
1567         test_SummaryInfo(pSummaryInfo, summary_info, sizeof(summary_info)/sizeof(msi_summary_info), readonly);
1568         IDispatch_Release(pSummaryInfo);
1569     }
1570 }
1571
1572 static void test_Session(IDispatch *pSession)
1573 {
1574     static WCHAR szProductName[] = { 'P','r','o','d','u','c','t','N','a','m','e',0 };
1575     static WCHAR szOne[] = { 'O','n','e',0 };
1576     static WCHAR szOneStateFalse[] = { '!','O','n','e','>','0',0 };
1577     static WCHAR szOneStateTrue[] = { '!','O','n','e','=','-','1',0 };
1578     static WCHAR szOneActionFalse[] = { '$','O','n','e','=','-','1',0 };
1579     static WCHAR szOneActionTrue[] = { '$','O','n','e','>','0',0 };
1580     static WCHAR szCostInitialize[] = { 'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0 };
1581     static WCHAR szEmpty[] = { 0 };
1582     static WCHAR szEquals[] = { '=',0 };
1583     static WCHAR szPropertyName[] = { 'P','r','o','p','e','r','t','y',',','N','a','m','e',0 };
1584     WCHAR stringw[MAX_PATH];
1585     CHAR string[MAX_PATH];
1586     UINT len;
1587     BOOL bool;
1588     int myint;
1589     IDispatch *pDatabase = NULL, *pInst = NULL;
1590     HRESULT hr;
1591
1592     /* Session::Installer */
1593     hr = Session_Installer(pSession, &pInst);
1594     ok(hr == S_OK, "Session_Installer failed, hresult 0x%08x\n", hr);
1595     ok(pInst != NULL, "Session_Installer returned NULL IDispatch pointer\n");
1596     ok(pInst == pInstaller, "Session_Installer does not match Installer instance from CoCreateInstance\n");
1597
1598     /* Session::Property, get */
1599     memset(stringw, 0, sizeof(stringw));
1600     hr = Session_PropertyGet(pSession, szProductName, stringw);
1601     ok(hr == S_OK, "Session_PropertyGet failed, hresult 0x%08x\n", hr);
1602     if (lstrcmpW(stringw, szMSITEST) != 0)
1603     {
1604         len = WideCharToMultiByte(CP_ACP, 0, stringw, -1, string, MAX_PATH, NULL, NULL);
1605         ok(len, "WideCharToMultiByteChar returned error %d\n", GetLastError());
1606         ok(0, "Property \"ProductName\" expected to be \"MSITEST\" but was \"%s\"\n", string);
1607     }
1608
1609     /* Session::Property, put */
1610     hr = Session_PropertyPut(pSession, szProductName, szProductName);
1611     ok(hr == S_OK, "Session_PropertyPut failed, hresult 0x%08x\n", hr);
1612     memset(stringw, 0, sizeof(stringw));
1613     hr = Session_PropertyGet(pSession, szProductName, stringw);
1614     ok(hr == S_OK, "Session_PropertyGet failed, hresult 0x%08x\n", hr);
1615     if (lstrcmpW(stringw, szProductName) != 0)
1616     {
1617         len = WideCharToMultiByte(CP_ACP, 0, stringw, -1, string, MAX_PATH, NULL, NULL);
1618         ok(len, "WideCharToMultiByteChar returned error %d\n", GetLastError());
1619         ok(0, "Property \"ProductName\" expected to be \"ProductName\" but was \"%s\"\n", string);
1620     }
1621
1622     /* Try putting a property using empty property identifier */
1623     hr = Session_PropertyPut(pSession, szEmpty, szProductName);
1624     ok(hr == DISP_E_EXCEPTION, "Session_PropertyPut failed, hresult 0x%08x\n", hr);
1625     ok_exception(hr, szPropertyName);
1626
1627     /* Try putting a property using illegal property identifier */
1628     hr = Session_PropertyPut(pSession, szEquals, szProductName);
1629     ok(hr == S_OK, "Session_PropertyPut failed, hresult 0x%08x\n", hr);
1630
1631     /* Session::Language, get */
1632     hr = Session_LanguageGet(pSession, &len);
1633     ok(hr == S_OK, "Session_LanguageGet failed, hresult 0x%08x\n", hr);
1634     /* Not sure how to check the language is correct */
1635
1636     /* Session::Mode, get */
1637     hr = Session_ModeGet(pSession, MSIRUNMODE_REBOOTATEND, &bool);
1638     ok(hr == S_OK, "Session_ModeGet failed, hresult 0x%08x\n", hr);
1639     todo_wine ok(!bool, "Reboot at end session mode is %d\n", bool);
1640
1641     /* Session::Mode, put */
1642     hr = Session_ModePut(pSession, MSIRUNMODE_REBOOTATEND, TRUE);
1643     todo_wine ok(hr == S_OK, "Session_ModePut failed, hresult 0x%08x\n", hr);
1644     hr = Session_ModeGet(pSession, MSIRUNMODE_REBOOTATEND, &bool);
1645     ok(hr == S_OK, "Session_ModeGet failed, hresult 0x%08x\n", hr);
1646     ok(bool, "Reboot at end session mode is %d, expected 1\n", bool);
1647     hr = Session_ModePut(pSession, MSIRUNMODE_REBOOTATEND, FALSE);  /* set it again so we don't reboot */
1648     todo_wine ok(hr == S_OK, "Session_ModePut failed, hresult 0x%08x\n", hr);
1649
1650     /* Session::Database, get */
1651     hr = Session_Database(pSession, &pDatabase);
1652     ok(hr == S_OK, "Session_Database failed, hresult 0x%08x\n", hr);
1653     if (hr == S_OK)
1654     {
1655         test_Database(pDatabase, TRUE);
1656         IDispatch_Release(pDatabase);
1657     }
1658
1659     /* Session::EvaluateCondition */
1660     hr = Session_EvaluateCondition(pSession, NULL, &myint);
1661     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1662     ok(myint == MSICONDITION_NONE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1663
1664     hr = Session_EvaluateCondition(pSession, szEmpty, &myint);
1665     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1666     ok(myint == MSICONDITION_NONE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1667
1668     hr = Session_EvaluateCondition(pSession, szEquals, &myint);
1669     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1670     ok(myint == MSICONDITION_ERROR, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1671
1672     /* Session::DoAction(CostInitialize) must occur before the next statements */
1673     hr = Session_DoAction(pSession, szCostInitialize, &myint);
1674     ok(hr == S_OK, "Session_DoAction failed, hresult 0x%08x\n", hr);
1675     ok(myint == IDOK, "DoAction(CostInitialize) returned %d, %d expected\n", myint, IDOK);
1676
1677     /* Session::SetInstallLevel */
1678     hr = Session_SetInstallLevel(pSession, INSTALLLEVEL_MINIMUM);
1679     ok(hr == S_OK, "Session_SetInstallLevel failed, hresult 0x%08x\n", hr);
1680
1681     /* Session::FeatureCurrentState, get */
1682     hr = Session_FeatureCurrentState(pSession, szOne, &myint);
1683     ok(hr == S_OK, "Session_FeatureCurrentState failed, hresult 0x%08x\n", hr);
1684     ok(myint == INSTALLSTATE_UNKNOWN, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1685
1686     /* Session::EvaluateCondition */
1687     hr = Session_EvaluateCondition(pSession, szOneStateFalse, &myint);
1688     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1689     ok(myint == MSICONDITION_FALSE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1690
1691     hr = Session_EvaluateCondition(pSession, szOneStateTrue, &myint);
1692     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1693     ok(myint == MSICONDITION_TRUE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1694
1695     /* Session::FeatureRequestState, put */
1696     hr = Session_FeatureRequestStatePut(pSession, szOne, INSTALLSTATE_ADVERTISED);
1697     ok(hr == S_OK, "Session_FeatureRequestStatePut failed, hresult 0x%08x\n", hr);
1698     hr = Session_FeatureRequestStateGet(pSession, szOne, &myint);
1699     ok(hr == S_OK, "Session_FeatureRequestStateGet failed, hresult 0x%08x\n", hr);
1700     ok(myint == INSTALLSTATE_ADVERTISED, "Feature request state was %d but expected %d\n", myint, INSTALLSTATE_ADVERTISED);
1701
1702     /* Session::EvaluateCondition */
1703     hr = Session_EvaluateCondition(pSession, szOneActionFalse, &myint);
1704     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1705     ok(myint == MSICONDITION_FALSE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1706
1707     hr = Session_EvaluateCondition(pSession, szOneActionTrue, &myint);
1708     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1709     ok(myint == MSICONDITION_TRUE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1710 }
1711
1712 /* delete key and all its subkeys */
1713 static DWORD delete_key( HKEY hkey )
1714 {
1715     char name[MAX_PATH];
1716     DWORD ret;
1717
1718     while (!(ret = RegEnumKeyA(hkey, 0, name, sizeof(name))))
1719     {
1720         HKEY tmp;
1721         if (!(ret = RegOpenKeyExA( hkey, name, 0, KEY_ENUMERATE_SUB_KEYS, &tmp )))
1722         {
1723             ret = delete_key( tmp );
1724             RegCloseKey( tmp );
1725         }
1726         if (ret) break;
1727     }
1728     if (ret != ERROR_NO_MORE_ITEMS) return ret;
1729     RegDeleteKeyA( hkey, "" );
1730     return 0;
1731 }
1732
1733 static void test_Installer_RegistryValue(void)
1734 {
1735     static const DWORD qw[2] = { 0x12345678, 0x87654321 };
1736     static const WCHAR szKey[] = { 'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\','T','e','s','t',0 };
1737     static const WCHAR szOne[] = { 'O','n','e',0 };
1738     static const WCHAR szTwo[] = { 'T','w','o',0 };
1739     static const WCHAR szThree[] = { 'T','h','r','e','e',0 };
1740     static const WCHAR szREG_BINARY[] = { '(','R','E','G','_','B','I','N','A','R','Y',')',0 };
1741     static const WCHAR szFour[] = { 'F','o','u','r',0 };
1742     static const WCHAR szExpand[] = { '%','M','S','I','T','E','S','T','%',0 };
1743     static const WCHAR szFive[] = { 'F','i','v','e',0,'H','i',0,0 };
1744     static const WCHAR szFiveHi[] = { 'F','i','v','e','\n','H','i',0 };
1745     static const WCHAR szSix[] = { 'S','i','x',0 };
1746     static const WCHAR szREG_[] = { '(','R','E','G','_',']',0 };
1747     static const WCHAR szSeven[] = { 'S','e','v','e','n',0 };
1748     static const WCHAR szEight[] = { 'E','i','g','h','t',0 };
1749     static const WCHAR szBlank[] = { 0 };
1750     VARIANT varresult;
1751     VARIANTARG vararg;
1752     WCHAR szString[MAX_PATH];
1753     HKEY hkey, hkey_sub;
1754     HRESULT hr;
1755     BOOL bRet;
1756
1757     /* Delete keys */
1758     if (!RegOpenKeyW( HKEY_CURRENT_USER, szKey, &hkey )) delete_key( hkey );
1759
1760     /* Does our key exist? Shouldn't; check with all three possible value parameter types */
1761     hr = Installer_RegistryValueE(HKEY_CURRENT_USER, szKey, &bRet);
1762     ok(hr == S_OK, "Installer_RegistryValueE failed, hresult 0x%08x\n", hr);
1763     ok(!bRet, "Registry key expected to not exist, but Installer_RegistryValue claims it does\n");
1764
1765     memset(szString, 0, sizeof(szString));
1766     hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, NULL, szString);
1767     ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1768
1769     memset(szString, 0, sizeof(szString));
1770     hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, 0, szString, VT_BSTR);
1771     ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
1772
1773     /* Create key */
1774     ok(!RegCreateKeyW( HKEY_CURRENT_USER, szKey, &hkey ), "RegCreateKeyW failed\n");
1775
1776     ok(!RegSetValueExW(hkey,szOne,0,REG_SZ, (const BYTE *)szOne, sizeof(szOne)),
1777         "RegSetValueExW failed\n");
1778     ok(!RegSetValueExW(hkey,szTwo,0,REG_DWORD, (const BYTE *)qw, 4),
1779         "RegSetValueExW failed\n");
1780     ok(!RegSetValueExW(hkey,szThree,0,REG_BINARY, (const BYTE *)qw, 4),
1781         "RegSetValueExW failed\n");
1782     ok(SetEnvironmentVariableA("MSITEST", "Four"), "SetEnvironmentVariableA failed %d\n", GetLastError());
1783     ok(!RegSetValueExW(hkey,szFour,0,REG_EXPAND_SZ, (const BYTE *)szExpand, sizeof(szExpand)),
1784         "RegSetValueExW failed\n");
1785     ok(!RegSetValueExW(hkey,szFive,0,REG_MULTI_SZ, (const BYTE *)szFive, sizeof(szFive)),
1786         "RegSetValueExW failed\n");
1787     ok(!RegSetValueExW(hkey,szSix,0,REG_QWORD, (const BYTE *)qw, 8),
1788         "RegSetValueExW failed\n");
1789     ok(!RegSetValueExW(hkey,szSeven,0,REG_NONE, (const BYTE *)NULL, 0),
1790         "RegSetValueExW failed\n");
1791
1792     ok(!RegSetValueExW(hkey,NULL,0,REG_SZ, (const BYTE *)szOne, sizeof(szOne)),
1793         "RegSetValueExW failed\n");
1794
1795     ok(!RegCreateKeyW( hkey, szEight, &hkey_sub ), "RegCreateKeyW failed\n");
1796
1797     /* Does our key exist? It should, and make sure we retrieve the correct default value */
1798     bRet = FALSE;
1799     hr = Installer_RegistryValueE(HKEY_CURRENT_USER, szKey, &bRet);
1800     ok(hr == S_OK, "Installer_RegistryValueE failed, hresult 0x%08x\n", hr);
1801     ok(bRet, "Registry key expected to exist, but Installer_RegistryValue claims it does not\n");
1802
1803     memset(szString, 0, sizeof(szString));
1804     hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, NULL, szString);
1805     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1806     ok_w2("Default registry value \"%s\" does not match expected \"%s\"\n", szString, szOne);
1807
1808     /* Ask for the value of a nonexistent key */
1809     memset(szString, 0, sizeof(szString));
1810     hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szExpand, szString);
1811     ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1812
1813     /* Get values of keys */
1814     memset(szString, 0, sizeof(szString));
1815     hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szOne, szString);
1816     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1817     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szOne);
1818
1819     VariantInit(&vararg);
1820     V_VT(&vararg) = VT_BSTR;
1821     V_BSTR(&vararg) = SysAllocString(szTwo);
1822     hr = Installer_RegistryValue(HKEY_CURRENT_USER, szKey, vararg, &varresult, VT_I4);
1823     ok(hr == S_OK, "Installer_RegistryValue failed, hresult 0x%08x\n", hr);
1824     ok(V_I4(&varresult) == 305419896, "Registry value %d does not match expected value\n", V_I4(&varresult));
1825     VariantClear(&varresult);
1826
1827     memset(szString, 0, sizeof(szString));
1828     hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szThree, szString);
1829     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1830     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szREG_BINARY);
1831
1832     memset(szString, 0, sizeof(szString));
1833     hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szFour, szString);
1834     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1835     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szFour);
1836
1837     memset(szString, 0, sizeof(szString));
1838     hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szFive, szString);
1839     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1840     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szFiveHi);
1841
1842     memset(szString, 0, sizeof(szString));
1843     hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szSix, szString);
1844     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1845     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szREG_);
1846
1847     VariantInit(&vararg);
1848     V_VT(&vararg) = VT_BSTR;
1849     V_BSTR(&vararg) = SysAllocString(szSeven);
1850     hr = Installer_RegistryValue(HKEY_CURRENT_USER, szKey, vararg, &varresult, VT_EMPTY);
1851     ok(hr == S_OK, "Installer_RegistryValue failed, hresult 0x%08x\n", hr);
1852
1853     /* Get string class name for the key */
1854     memset(szString, 0, sizeof(szString));
1855     hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, 0, szString, VT_BSTR);
1856     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
1857     ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szBlank);
1858
1859     /* Get name of a value by positive number (RegEnumValue like), valid index */
1860     memset(szString, 0, sizeof(szString));
1861     hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, 2, szString, VT_BSTR);
1862     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
1863     /* RegEnumValue order seems different on wine */
1864     todo_wine ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szTwo);
1865
1866     /* Get name of a value by positive number (RegEnumValue like), invalid index */
1867     memset(szString, 0, sizeof(szString));
1868     hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, 10, szString, VT_EMPTY);
1869     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
1870
1871     /* Get name of a subkey by negative number (RegEnumValue like), valid index */
1872     memset(szString, 0, sizeof(szString));
1873     hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, -1, szString, VT_BSTR);
1874     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
1875     ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szEight);
1876
1877     /* Get name of a subkey by negative number (RegEnumValue like), invalid index */
1878     memset(szString, 0, sizeof(szString));
1879     hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, -10, szString, VT_EMPTY);
1880     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
1881
1882     /* clean up */
1883     delete_key(hkey);
1884 }
1885
1886 static void test_Installer_Products(BOOL bProductInstalled)
1887 {
1888     WCHAR szString[MAX_PATH];
1889     HRESULT hr;
1890     int idx;
1891     IUnknown *pUnk = NULL;
1892     IEnumVARIANT *pEnum = NULL;
1893     VARIANT var;
1894     ULONG celt;
1895     int iCount, iValue;
1896     IDispatch *pStringList = NULL;
1897     BOOL bProductFound = FALSE;
1898
1899     /* Installer::Products */
1900     hr = Installer_Products(&pStringList);
1901     ok(hr == S_OK, "Installer_Products failed, hresult 0x%08x\n", hr);
1902     if (hr == S_OK)
1903     {
1904         /* StringList::_NewEnum */
1905         hr = StringList__NewEnum(pStringList, &pUnk);
1906         ok(hr == S_OK, "StringList_NewEnum failed, hresult 0x%08x\n", hr);
1907         if (hr == S_OK)
1908         {
1909             hr = IUnknown_QueryInterface(pUnk, &IID_IEnumVARIANT, (void **)&pEnum);
1910             ok (hr == S_OK, "IUnknown::QueryInterface returned 0x%08x\n", hr);
1911         }
1912         if (!pEnum)
1913             skip("IEnumVARIANT tests\n");
1914
1915         /* StringList::Count */
1916         hr = StringList_Count(pStringList, &iCount);
1917         ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr);
1918
1919         for (idx=0; idx<iCount; idx++)
1920         {
1921             /* StringList::Item */
1922             memset(szString, 0, sizeof(szString));
1923             hr = StringList_Item(pStringList, idx, szString);
1924             ok(hr == S_OK, "StringList_Item failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);
1925
1926             if (hr == S_OK)
1927             {
1928                 /* Installer::ProductState */
1929                 hr = Installer_ProductState(szString, &iValue);
1930                 ok(hr == S_OK, "Installer_ProductState failed, hresult 0x%08x\n", hr);
1931                 if (hr == S_OK)
1932                     ok(iValue == INSTALLSTATE_DEFAULT || iValue == INSTALLSTATE_ADVERTISED, "Installer_ProductState returned %d, expected %d or %d\n", iValue, INSTALLSTATE_DEFAULT, INSTALLSTATE_ADVERTISED);
1933
1934                 /* Not found our product code yet? Check */
1935                 if (!bProductFound && !lstrcmpW(szString, szProductCode))
1936                     bProductFound = TRUE;
1937
1938                 /* IEnumVARIANT::Next */
1939                 if (pEnum)
1940                 {
1941                     hr = IEnumVARIANT_Next(pEnum, 1, &var, &celt);
1942                     ok(hr == S_OK, "IEnumVARIANT_Next failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);
1943                     ok(celt == 1, "%d items were retrieved, expected 1\n", celt);
1944                     ok(V_VT(&var) == VT_BSTR, "IEnumVARIANT_Next returned variant of type %d, expected %d\n", V_VT(&var), VT_BSTR);
1945                     ok_w2("%s returned by StringList_Item does not match %s returned by IEnumVARIANT_Next\n", szString, V_BSTR(&var));
1946                     VariantClear(&var);
1947                 }
1948             }
1949         }
1950
1951         ok(bProductInstalled == bProductFound, "Product expected to %s installed but product code was %s\n",
1952            bProductInstalled ? "be" : "not be",
1953            bProductFound ? "found" : "not found");
1954
1955         if (pEnum)
1956         {
1957             IEnumVARIANT *pEnum2 = NULL;
1958
1959             if (0) /* Crashes on Windows XP */
1960             {
1961                 /* IEnumVARIANT::Clone, NULL pointer */
1962                 hr = IEnumVARIANT_Clone(pEnum, NULL);
1963             }
1964
1965             /* IEnumVARIANT::Clone */
1966             hr = IEnumVARIANT_Clone(pEnum, &pEnum2);
1967             ok(hr == S_OK, "IEnumVARIANT_Clone failed, hresult 0x%08x\n", hr);
1968             if (hr == S_OK)
1969             {
1970                 /* IEnumVARIANT::Clone is supposed to save the position, but it actually just goes back to the beginning */
1971
1972                 /* IEnumVARIANT::Next of the clone */
1973                 if (iCount)
1974                 {
1975                     hr = IEnumVARIANT_Next(pEnum2, 1, &var, &celt);
1976                     ok(hr == S_OK, "IEnumVARIANT_Next failed, hresult 0x%08x\n", hr);
1977                     ok(celt == 1, "%d items were retrieved, expected 0\n", celt);
1978                     ok(V_VT(&var) == VT_BSTR, "IEnumVARIANT_Next returned variant of type %d, expected %d\n", V_VT(&var), VT_BSTR);
1979                     VariantClear(&var);
1980                 }
1981                 else
1982                     skip("IEnumVARIANT::Next of clone will not return success with 0 products\n");
1983
1984                 IEnumVARIANT_Release(pEnum2);
1985             }
1986
1987             /* IEnumVARIANT::Skip should fail */
1988             hr = IEnumVARIANT_Skip(pEnum, 1);
1989             ok(hr == S_FALSE, "IEnumVARIANT_Skip failed, hresult 0x%08x\n", hr);
1990
1991             /* IEnumVARIANT::Next, NULL variant pointer */
1992             hr = IEnumVARIANT_Next(pEnum, 1, NULL, &celt);
1993             ok(hr == S_FALSE, "IEnumVARIANT_Next failed, hresult 0x%08x\n", hr);
1994             ok(celt == 0, "%d items were retrieved, expected 0\n", celt);
1995
1996             /* IEnumVARIANT::Next, should not return any more items */
1997             hr = IEnumVARIANT_Next(pEnum, 1, &var, &celt);
1998             ok(hr == S_FALSE, "IEnumVARIANT_Next failed, hresult 0x%08x\n", hr);
1999             ok(celt == 0, "%d items were retrieved, expected 0\n", celt);
2000             VariantClear(&var);
2001
2002             /* IEnumVARIANT::Reset */
2003             hr = IEnumVARIANT_Reset(pEnum);
2004             ok(hr == S_OK, "IEnumVARIANT_Reset failed, hresult 0x%08x\n", hr);
2005
2006             if (iCount)
2007             {
2008                 /* IEnumVARIANT::Skip to the last product */
2009                 hr = IEnumVARIANT_Skip(pEnum, iCount-1);
2010                 ok(hr == S_OK, "IEnumVARIANT_Skip failed, hresult 0x%08x\n", hr);
2011
2012                 /* IEnumVARIANT::Next should match the very last retrieved value, also makes sure it works with
2013                  * NULL celt pointer. */
2014                 hr = IEnumVARIANT_Next(pEnum, 1, &var, NULL);
2015                 ok(hr == S_OK, "IEnumVARIANT_Next failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);
2016                 ok(V_VT(&var) == VT_BSTR, "IEnumVARIANT_Next returned variant of type %d, expected %d\n", V_VT(&var), VT_BSTR);
2017                 ok_w2("%s returned by StringList_Item does not match %s returned by IEnumVARIANT_Next\n", szString, V_BSTR(&var));
2018                 VariantClear(&var);
2019             }
2020             else
2021                 skip("IEnumVARIANT::Skip impossible for 0 products\n");
2022         }
2023
2024         /* StringList::Item using an invalid index */
2025         memset(szString, 0, sizeof(szString));
2026         hr = StringList_Item(pStringList, iCount, szString);
2027         ok(hr == DISP_E_BADINDEX, "StringList_Item for an invalid index did not return DISP_E_BADINDEX, hresult 0x%08x\n", hr);
2028
2029         if (pEnum) IEnumVARIANT_Release(pEnum);
2030         if (pUnk) IUnknown_Release(pUnk);
2031         IDispatch_Release(pStringList);
2032     }
2033 }
2034
2035 /* Delete a registry subkey, including all its subkeys (RegDeleteKey does not work on keys with subkeys without
2036  * deleting the subkeys first) */
2037 static UINT delete_registry_key(HKEY hkeyParent, LPCSTR subkey)
2038 {
2039     UINT ret;
2040     CHAR *string = NULL;
2041     HKEY hkey;
2042     DWORD dwSize;
2043
2044     ret = RegOpenKey(hkeyParent, subkey, &hkey);
2045     if (ret != ERROR_SUCCESS) return ret;
2046     ret = RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL);
2047     if (ret != ERROR_SUCCESS) return ret;
2048     if (!(string = HeapAlloc(GetProcessHeap(), 0, ++dwSize))) return ERROR_NOT_ENOUGH_MEMORY;
2049
2050     while (RegEnumKeyA(hkey, 0, string, dwSize) == ERROR_SUCCESS)
2051         delete_registry_key(hkey, string);
2052
2053     RegCloseKey(hkey);
2054     HeapFree(GetProcessHeap(), 0, string);
2055     RegDeleteKeyA(hkeyParent, subkey);
2056     return ERROR_SUCCESS;
2057 }
2058
2059 /* Find a specific registry subkey at any depth within the given key and subkey and return its parent key. */
2060 static UINT find_registry_key(HKEY hkeyParent, LPCSTR subkey, LPCSTR findkey, HKEY *phkey)
2061 {
2062     UINT ret;
2063     CHAR *string = NULL;
2064     int idx = 0;
2065     HKEY hkey;
2066     DWORD dwSize;
2067     BOOL found = FALSE;
2068
2069     *phkey = 0;
2070
2071     ret = RegOpenKey(hkeyParent, subkey, &hkey);
2072     if (ret != ERROR_SUCCESS) return ret;
2073     ret = RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL);
2074     if (ret != ERROR_SUCCESS) return ret;
2075     if (!(string = HeapAlloc(GetProcessHeap(), 0, ++dwSize))) return ERROR_NOT_ENOUGH_MEMORY;
2076
2077     while (!found &&
2078            RegEnumKeyA(hkey, idx++, string, dwSize) == ERROR_SUCCESS)
2079     {
2080         if (!strcmp(string, findkey))
2081         {
2082             *phkey = hkey;
2083             found = TRUE;
2084         }
2085         else if (find_registry_key(hkey, string, findkey, phkey) == ERROR_SUCCESS) found = TRUE;
2086     }
2087
2088     if (*phkey != hkey) RegCloseKey(hkey);
2089     HeapFree(GetProcessHeap(), 0, string);
2090     return (found ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND);
2091 }
2092
2093 static void test_Installer_InstallProduct(LPCWSTR szPath)
2094 {
2095     HRESULT hr;
2096     CHAR path[MAX_PATH];
2097     WCHAR szString[MAX_PATH];
2098     LONG res;
2099     HKEY hkey;
2100     DWORD num, size, type;
2101     int iValue, iCount;
2102     IDispatch *pStringList = NULL;
2103
2104     create_test_files();
2105
2106     /* Installer::InstallProduct */
2107     hr = Installer_InstallProduct(szMsifile, NULL);
2108     ok(hr == S_OK, "Installer_InstallProduct failed, hresult 0x%08x\n", hr);
2109
2110     /* Installer::ProductState for our product code, which has been installed */
2111     hr = Installer_ProductState(szProductCode, &iValue);
2112     ok(hr == S_OK, "Installer_ProductState failed, hresult 0x%08x\n", hr);
2113     todo_wine
2114     {
2115         ok(iValue == INSTALLSTATE_DEFAULT, "Installer_ProductState returned %d, expected %d\n", iValue, INSTALLSTATE_DEFAULT);
2116     }
2117
2118     /* Installer::ProductInfo for our product code */
2119
2120     /* NULL attribute */
2121     memset(szString, 0, sizeof(szString));
2122     hr = Installer_ProductInfo(szProductCode, NULL, szString);
2123     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2124     ok_exception(hr, szProductInfoException);
2125
2126     /* Nonexistent attribute */
2127     memset(szString, 0, sizeof(szString));
2128     hr = Installer_ProductInfo(szProductCode, szMsifile, szString);
2129     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2130     ok_exception(hr, szProductInfoException);
2131
2132     /* Package name */
2133     memset(szString, 0, sizeof(szString));
2134     hr = Installer_ProductInfo(szProductCode, INSTALLPROPERTY_PACKAGENAMEW, szString);
2135     todo_wine ok(hr == S_OK, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2136     todo_wine ok_w2("Installer_ProductInfo returned %s but expected %s\n", szString, szMsifile);
2137
2138     /* Product name */
2139     memset(szString, 0, sizeof(szString));
2140     hr = Installer_ProductInfo(szProductCode, INSTALLPROPERTY_PRODUCTNAMEW, szString);
2141     ok(hr == S_OK, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2142     ok_w2("Installer_ProductInfo returned %s but expected %s\n", szString, szMSITEST);
2143
2144     /* Installer::Products */
2145     test_Installer_Products(TRUE);
2146
2147     /* Installer::RelatedProducts for our upgrade code */
2148     hr = Installer_RelatedProducts(szUpgradeCode, &pStringList);
2149     ok(hr == S_OK, "Installer_RelatedProducts failed, hresult 0x%08x\n", hr);
2150     if (hr == S_OK)
2151     {
2152         /* StringList::Count */
2153         hr = StringList_Count(pStringList, &iCount);
2154         ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr);
2155         ok(iCount == 1, "Expected one related product but found %d\n", iCount);
2156
2157         /* StringList::Item */
2158         memset(szString, 0, sizeof(szString));
2159         hr = StringList_Item(pStringList, 0, szString);
2160         ok(hr == S_OK, "StringList_Item failed (idx 0, count %d), hresult 0x%08x\n", iCount, hr);
2161         ok_w2("StringList_Item returned %s but expected %s\n", szString, szProductCode);
2162
2163         IDispatch_Release(pStringList);
2164     }
2165
2166     /* Check & clean up installed files & registry keys */
2167     ok(delete_pf("msitest\\cabout\\new\\five.txt", TRUE), "File not installed\n");
2168     ok(delete_pf("msitest\\cabout\\new", FALSE), "File not installed\n");
2169     ok(delete_pf("msitest\\cabout\\four.txt", TRUE), "File not installed\n");
2170     ok(delete_pf("msitest\\cabout", FALSE), "File not installed\n");
2171     ok(delete_pf("msitest\\changed\\three.txt", TRUE), "File not installed\n");
2172     ok(delete_pf("msitest\\changed", FALSE), "File not installed\n");
2173     ok(delete_pf("msitest\\first\\two.txt", TRUE), "File not installed\n");
2174     ok(delete_pf("msitest\\first", FALSE), "File not installed\n");
2175     ok(delete_pf("msitest\\one.txt", TRUE), "File not installed\n");
2176     ok(delete_pf("msitest\\filename", TRUE), "File not installed\n");
2177     ok(delete_pf("msitest\\service.exe", TRUE), "File not installed\n");
2178     ok(delete_pf("msitest", FALSE), "File not installed\n");
2179
2180     res = RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine\\msitest", &hkey);
2181     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2182
2183     size = MAX_PATH;
2184     type = REG_SZ;
2185     res = RegQueryValueExA(hkey, "Name", NULL, &type, (LPBYTE)path, &size);
2186     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2187     ok(!lstrcmpA(path, "imaname"), "Expected imaname, got %s\n", path);
2188
2189     size = MAX_PATH;
2190     type = REG_SZ;
2191     res = RegQueryValueExA(hkey, "blah", NULL, &type, (LPBYTE)path, &size);
2192     ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
2193
2194     size = sizeof(num);
2195     type = REG_DWORD;
2196     res = RegQueryValueExA(hkey, "number", NULL, &type, (LPBYTE)&num, &size);
2197     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2198     ok(num == 314, "Expected 314, got %d\n", num);
2199
2200     size = MAX_PATH;
2201     type = REG_SZ;
2202     res = RegQueryValueExA(hkey, "OrderTestName", NULL, &type, (LPBYTE)path, &size);
2203     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2204     ok(!lstrcmpA(path, "OrderTestValue"), "Expected imaname, got %s\n", path);
2205
2206     RegCloseKey(hkey);
2207
2208     res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine\\msitest");
2209     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2210
2211     check_service_is_installed();
2212
2213     /* Remove registry keys written by RegisterProduct standard action */
2214     res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{F1C3AF50-8B56-4A69-A00C-00773FE42F30}");
2215     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2216
2217     res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UpgradeCodes\\D8E760ECA1E276347B43E42BDBDA5656");
2218     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2219
2220     res = find_registry_key(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData", "05FA3C1F65B896A40AC00077F34EF203", &hkey);
2221     todo_wine ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2222     if (res == ERROR_SUCCESS)
2223     {
2224         res = delete_registry_key(hkey, "05FA3C1F65B896A40AC00077F34EF203");
2225         ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2226         RegCloseKey(hkey);
2227     }
2228
2229     /* Remove registry keys written by PublishProduct standard action */
2230     res = RegOpenKey(HKEY_CURRENT_USER, "SOFTWARE\\Microsoft\\Installer", &hkey);
2231     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2232
2233     res = delete_registry_key(hkey, "Products\\05FA3C1F65B896A40AC00077F34EF203");
2234     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2235
2236     res = RegDeleteKeyA(hkey, "UpgradeCodes\\D8E760ECA1E276347B43E42BDBDA5656");
2237     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2238
2239     RegCloseKey(hkey);
2240
2241     res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\Products\\05FA3C1F65B896A40AC00077F34EF203");
2242     todo_wine ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_SUCCESS, got %d\n", res);
2243
2244     /* Delete installation files we installed */
2245     delete_test_files();
2246 }
2247
2248 static void test_Installer(void)
2249 {
2250     static WCHAR szBackslash[] = { '\\',0 };
2251     static WCHAR szCreateRecordException[] = { 'C','r','e','a','t','e','R','e','c','o','r','d',',','C','o','u','n','t',0 };
2252     static WCHAR szIntegerDataException[] = { 'I','n','t','e','g','e','r','D','a','t','a',',','F','i','e','l','d',0 };
2253     WCHAR szPath[MAX_PATH];
2254     HRESULT hr;
2255     UINT len;
2256     IDispatch *pSession = NULL, *pDatabase = NULL, *pRecord = NULL, *pStringList = NULL;
2257     int iValue, iCount;
2258
2259     if (!pInstaller) return;
2260
2261     /* Installer::CreateRecord */
2262
2263     /* Test for error */
2264     hr = Installer_CreateRecord(-1, &pRecord);
2265     ok(hr == DISP_E_EXCEPTION, "Installer_CreateRecord failed, hresult 0x%08x\n", hr);
2266     ok_exception(hr, szCreateRecordException);
2267
2268     /* Test for success */
2269     hr = Installer_CreateRecord(1, &pRecord);
2270     ok(hr == S_OK, "Installer_CreateRecord failed, hresult 0x%08x\n", hr);
2271     ok(pRecord != NULL, "Installer_CreateRecord should not have returned NULL record\n");
2272     if (pRecord)
2273     {
2274         /* Record::FieldCountGet */
2275         hr = Record_FieldCountGet(pRecord, &iValue);
2276         ok(hr == S_OK, "Record_FiledCountGet failed, hresult 0x%08x\n", hr);
2277         ok(iValue == 1, "Record_FieldCountGet result was %d but expected 1\n", iValue);
2278
2279         /* Record::IntegerDataGet */
2280         hr = Record_IntegerDataGet(pRecord, 1, &iValue);
2281         ok(hr == S_OK, "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
2282         ok(iValue == MSI_NULL_INTEGER, "Record_IntegerDataGet result was %d but expected %d\n", iValue, MSI_NULL_INTEGER);
2283
2284         /* Record::IntegerDataGet, bad index */
2285         hr = Record_IntegerDataGet(pRecord, 10, &iValue);
2286         ok(hr == S_OK, "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
2287         ok(iValue == MSI_NULL_INTEGER, "Record_IntegerDataGet result was %d but expected %d\n", iValue, MSI_NULL_INTEGER);
2288
2289         /* Record::IntegerDataPut */
2290         hr = Record_IntegerDataPut(pRecord, 1, 100);
2291         ok(hr == S_OK, "Record_IntegerDataPut failed, hresult 0x%08x\n", hr);
2292
2293         /* Record::IntegerDataPut, bad index */
2294         hr = Record_IntegerDataPut(pRecord, 10, 100);
2295         ok(hr == DISP_E_EXCEPTION, "Record_IntegerDataPut failed, hresult 0x%08x\n", hr);
2296         ok_exception(hr, szIntegerDataException);
2297
2298         /* Record::IntegerDataGet */
2299         hr = Record_IntegerDataGet(pRecord, 1, &iValue);
2300         ok(hr == S_OK, "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
2301         ok(iValue == 100, "Record_IntegerDataGet result was %d but expected 100\n", iValue);
2302
2303         IDispatch_Release(pRecord);
2304     }
2305
2306     /* Prepare package */
2307     create_database(msifile, tables, sizeof(tables) / sizeof(msi_table),
2308                     summary_info, sizeof(summary_info) / sizeof(msi_summary_info));
2309
2310     len = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, CURR_DIR, -1, szPath, MAX_PATH);
2311     ok(len, "MultiByteToWideChar returned error %d\n", GetLastError());
2312     if (!len) return;
2313
2314     lstrcatW(szPath, szBackslash);
2315     lstrcatW(szPath, szMsifile);
2316
2317     /* Installer::OpenPackage */
2318     hr = Installer_OpenPackage(szPath, 0, &pSession);
2319     ok(hr == S_OK, "Installer_OpenPackage failed, hresult 0x%08x\n", hr);
2320     if (hr == S_OK)
2321     {
2322         test_Session(pSession);
2323         IDispatch_Release(pSession);
2324     }
2325
2326     /* Installer::OpenDatabase */
2327     hr = Installer_OpenDatabase(szPath, (int)MSIDBOPEN_TRANSACT, &pDatabase);
2328     ok(hr == S_OK, "Installer_OpenDatabase failed, hresult 0x%08x\n", hr);
2329     if (hr == S_OK)
2330     {
2331         test_Database(pDatabase, FALSE);
2332         IDispatch_Release(pDatabase);
2333     }
2334
2335     /* Installer::RegistryValue */
2336     test_Installer_RegistryValue();
2337
2338     /* Installer::ProductState for our product code, which should not be installed */
2339     hr = Installer_ProductState(szProductCode, &iValue);
2340     ok(hr == S_OK, "Installer_ProductState failed, hresult 0x%08x\n", hr);
2341     ok(iValue == INSTALLSTATE_UNKNOWN, "Installer_ProductState returned %d, expected %d\n", iValue, INSTALLSTATE_UNKNOWN);
2342
2343     /* Installer::ProductInfo for our product code, which should not be installed */
2344
2345     /* Package name */
2346     memset(szPath, 0, sizeof(szPath));
2347     hr = Installer_ProductInfo(szProductCode, INSTALLPROPERTY_PACKAGENAMEW, szPath);
2348     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2349     ok_exception(hr, szProductInfoException);
2350
2351     /* NULL attribute and NULL product code */
2352     memset(szPath, 0, sizeof(szPath));
2353     hr = Installer_ProductInfo(NULL, NULL, szPath);
2354     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2355     ok_exception(hr, szProductInfoException);
2356
2357     /* Installer::Products */
2358     test_Installer_Products(FALSE);
2359
2360     /* Installer::RelatedProducts for our upgrade code, should not find anything */
2361     hr = Installer_RelatedProducts(szUpgradeCode, &pStringList);
2362     ok(hr == S_OK, "Installer_RelatedProducts failed, hresult 0x%08x\n", hr);
2363     if (hr == S_OK)
2364     {
2365         /* StringList::Count */
2366         hr = StringList_Count(pStringList, &iCount);
2367         ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr);
2368         ok(!iCount, "Expected no related products but found %d\n", iCount);
2369
2370         IDispatch_Release(pStringList);
2371     }
2372
2373     /* Installer::Version */
2374     todo_wine {
2375         memset(szPath, 0, sizeof(szPath));
2376         hr = Installer_VersionGet(szPath);
2377         ok(hr == S_OK, "Installer_VersionGet failed, hresult 0x%08x\n", hr);
2378     }
2379
2380     /* Installer::InstallProduct and other tests that depend on our product being installed */
2381     test_Installer_InstallProduct(szPath);
2382 }
2383
2384 START_TEST(automation)
2385 {
2386     DWORD len;
2387     char temp_path[MAX_PATH], prev_path[MAX_PATH];
2388     HRESULT hr;
2389     CLSID clsid;
2390     IUnknown *pUnk;
2391
2392     GetSystemTimeAsFileTime(&systemtime);
2393
2394     GetCurrentDirectoryA(MAX_PATH, prev_path);
2395     GetTempPath(MAX_PATH, temp_path);
2396     SetCurrentDirectoryA(temp_path);
2397
2398     lstrcpyA(CURR_DIR, temp_path);
2399     len = lstrlenA(CURR_DIR);
2400
2401     if(len && (CURR_DIR[len - 1] == '\\'))
2402         CURR_DIR[len - 1] = 0;
2403
2404     get_program_files_dir(PROG_FILES_DIR);
2405
2406     hr = OleInitialize(NULL);
2407     ok (hr == S_OK, "OleInitialize returned 0x%08x\n", hr);
2408     hr = CLSIDFromProgID(szProgId, &clsid);
2409     ok (hr == S_OK, "CLSIDFromProgID returned 0x%08x\n", hr);
2410     hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&pUnk);
2411     ok(hr == S_OK, "CoCreateInstance returned 0x%08x\n", hr);
2412
2413     if (pUnk)
2414     {
2415         hr = IUnknown_QueryInterface(pUnk, &IID_IDispatch, (void **)&pInstaller);
2416         ok (hr == S_OK, "IUnknown::QueryInterface returned 0x%08x\n", hr);
2417
2418         test_dispid();
2419         test_dispatch();
2420         test_Installer();
2421
2422         IDispatch_Release(pInstaller);
2423         IUnknown_Release(pUnk);
2424     }
2425
2426     OleUninitialize();
2427
2428     SetCurrentDirectoryA(prev_path);
2429 }