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