winmm: Fix a failing mixer test on 98 and ME.
[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 = (WCHAR *)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 = (WCHAR *)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)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
1820     /* Delete keys */
1821     if (!RegOpenKeyW( HKEY_CURRENT_USER, szKey, &hkey )) delete_key( hkey );
1822
1823     /* Does our key exist? Shouldn't; check with all three possible value parameter types */
1824     hr = Installer_RegistryValueE(curr_user, szKey, &bRet);
1825     ok(hr == S_OK, "Installer_RegistryValueE failed, hresult 0x%08x\n", hr);
1826     ok(!bRet, "Registry key expected to not exist, but Installer_RegistryValue claims it does\n");
1827
1828     memset(szString, 0, sizeof(szString));
1829     hr = Installer_RegistryValueW(curr_user, szKey, NULL, szString);
1830     ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1831
1832     memset(szString, 0, sizeof(szString));
1833     hr = Installer_RegistryValueI(curr_user, szKey, 0, szString, VT_BSTR);
1834     ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
1835
1836     /* Create key */
1837     ok(!RegCreateKeyW( HKEY_CURRENT_USER, szKey, &hkey ), "RegCreateKeyW failed\n");
1838
1839     ok(!RegSetValueExW(hkey,szOne,0,REG_SZ, (const BYTE *)szOne, sizeof(szOne)),
1840         "RegSetValueExW failed\n");
1841     ok(!RegSetValueExW(hkey,szTwo,0,REG_DWORD, (const BYTE *)qw, 4),
1842         "RegSetValueExW failed\n");
1843     ok(!RegSetValueExW(hkey,szThree,0,REG_BINARY, (const BYTE *)qw, 4),
1844         "RegSetValueExW failed\n");
1845     ok(SetEnvironmentVariableA("MSITEST", "Four"), "SetEnvironmentVariableA failed %d\n", GetLastError());
1846     ok(!RegSetValueExW(hkey,szFour,0,REG_EXPAND_SZ, (const BYTE *)szExpand, sizeof(szExpand)),
1847         "RegSetValueExW failed\n");
1848     ok(!RegSetValueExW(hkey,szFive,0,REG_MULTI_SZ, (const BYTE *)szFive, sizeof(szFive)),
1849         "RegSetValueExW failed\n");
1850     ok(!RegSetValueExW(hkey,szSix,0,REG_QWORD, (const BYTE *)qw, 8),
1851         "RegSetValueExW failed\n");
1852     ok(!RegSetValueExW(hkey,szSeven,0,REG_NONE, (const BYTE *)NULL, 0),
1853         "RegSetValueExW failed\n");
1854
1855     ok(!RegSetValueExW(hkey,NULL,0,REG_SZ, (const BYTE *)szOne, sizeof(szOne)),
1856         "RegSetValueExW failed\n");
1857
1858     ok(!RegCreateKeyW( hkey, szEight, &hkey_sub ), "RegCreateKeyW failed\n");
1859
1860     /* Does our key exist? It should, and make sure we retrieve the correct default value */
1861     bRet = FALSE;
1862     hr = Installer_RegistryValueE(curr_user, szKey, &bRet);
1863     ok(hr == S_OK, "Installer_RegistryValueE failed, hresult 0x%08x\n", hr);
1864     ok(bRet, "Registry key expected to exist, but Installer_RegistryValue claims it does not\n");
1865
1866     memset(szString, 0, sizeof(szString));
1867     hr = Installer_RegistryValueW(curr_user, szKey, NULL, szString);
1868     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1869     ok_w2("Default registry value \"%s\" does not match expected \"%s\"\n", szString, szOne);
1870
1871     /* Ask for the value of a nonexistent key */
1872     memset(szString, 0, sizeof(szString));
1873     hr = Installer_RegistryValueW(curr_user, szKey, szExpand, szString);
1874     ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1875
1876     /* Get values of keys */
1877     memset(szString, 0, sizeof(szString));
1878     hr = Installer_RegistryValueW(curr_user, szKey, szOne, szString);
1879     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1880     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szOne);
1881
1882     VariantInit(&vararg);
1883     V_VT(&vararg) = VT_BSTR;
1884     V_BSTR(&vararg) = SysAllocString(szTwo);
1885     hr = Installer_RegistryValue(curr_user, szKey, vararg, &varresult, VT_I4);
1886     ok(hr == S_OK, "Installer_RegistryValue failed, hresult 0x%08x\n", hr);
1887     ok(V_I4(&varresult) == 305419896, "Registry value %d does not match expected value\n", V_I4(&varresult));
1888     VariantClear(&varresult);
1889
1890     memset(szString, 0, sizeof(szString));
1891     hr = Installer_RegistryValueW(curr_user, szKey, szThree, szString);
1892     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1893     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szREG_BINARY);
1894
1895     memset(szString, 0, sizeof(szString));
1896     hr = Installer_RegistryValueW(curr_user, szKey, szFour, szString);
1897     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1898     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szFour);
1899
1900     /* Vista does not NULL-terminate this case */
1901     memset(szString, 0, sizeof(szString));
1902     hr = Installer_RegistryValueW(curr_user, szKey, szFive, szString);
1903     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1904     ok_w2n("Registry value \"%s\" does not match expected \"%s\"\n",
1905            szString, szFiveHi, lstrlenW(szFiveHi));
1906
1907     memset(szString, 0, sizeof(szString));
1908     hr = Installer_RegistryValueW(curr_user, szKey, szSix, szString);
1909     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1910     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szREG_);
1911
1912     VariantInit(&vararg);
1913     V_VT(&vararg) = VT_BSTR;
1914     V_BSTR(&vararg) = SysAllocString(szSeven);
1915     hr = Installer_RegistryValue(curr_user, szKey, vararg, &varresult, VT_EMPTY);
1916     ok(hr == S_OK, "Installer_RegistryValue failed, hresult 0x%08x\n", hr);
1917
1918     /* Get string class name for the key */
1919     memset(szString, 0, sizeof(szString));
1920     hr = Installer_RegistryValueI(curr_user, szKey, 0, szString, VT_BSTR);
1921     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
1922     ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szBlank);
1923
1924     /* Get name of a value by positive number (RegEnumValue like), valid index */
1925     memset(szString, 0, sizeof(szString));
1926     hr = Installer_RegistryValueI(curr_user, szKey, 2, szString, VT_BSTR);
1927     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
1928     /* RegEnumValue order seems different on wine */
1929     todo_wine ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szTwo);
1930
1931     /* Get name of a value by positive number (RegEnumValue like), invalid index */
1932     memset(szString, 0, sizeof(szString));
1933     hr = Installer_RegistryValueI(curr_user, szKey, 10, szString, VT_EMPTY);
1934     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
1935
1936     /* Get name of a subkey by negative number (RegEnumValue like), valid index */
1937     memset(szString, 0, sizeof(szString));
1938     hr = Installer_RegistryValueI(curr_user, szKey, -1, szString, VT_BSTR);
1939     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
1940     ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szEight);
1941
1942     /* Get name of a subkey by negative number (RegEnumValue like), invalid index */
1943     memset(szString, 0, sizeof(szString));
1944     hr = Installer_RegistryValueI(curr_user, szKey, -10, szString, VT_EMPTY);
1945     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
1946
1947     /* clean up */
1948     delete_key(hkey);
1949 }
1950
1951 static void test_Installer_Products(BOOL bProductInstalled)
1952 {
1953     WCHAR szString[MAX_PATH];
1954     HRESULT hr;
1955     int idx;
1956     IUnknown *pUnk = NULL;
1957     IEnumVARIANT *pEnum = NULL;
1958     VARIANT var;
1959     ULONG celt;
1960     int iCount, iValue;
1961     IDispatch *pStringList = NULL;
1962     BOOL bProductFound = FALSE;
1963
1964     /* Installer::Products */
1965     hr = Installer_Products(&pStringList);
1966     ok(hr == S_OK, "Installer_Products failed, hresult 0x%08x\n", hr);
1967     if (hr == S_OK)
1968     {
1969         /* StringList::_NewEnum */
1970         hr = StringList__NewEnum(pStringList, &pUnk);
1971         ok(hr == S_OK, "StringList_NewEnum failed, hresult 0x%08x\n", hr);
1972         if (hr == S_OK)
1973         {
1974             hr = IUnknown_QueryInterface(pUnk, &IID_IEnumVARIANT, (void **)&pEnum);
1975             ok (hr == S_OK, "IUnknown::QueryInterface returned 0x%08x\n", hr);
1976         }
1977         if (!pEnum)
1978             skip("IEnumVARIANT tests\n");
1979
1980         /* StringList::Count */
1981         hr = StringList_Count(pStringList, &iCount);
1982         ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr);
1983
1984         for (idx=0; idx<iCount; idx++)
1985         {
1986             /* StringList::Item */
1987             memset(szString, 0, sizeof(szString));
1988             hr = StringList_Item(pStringList, idx, szString);
1989             ok(hr == S_OK, "StringList_Item failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);
1990
1991             if (hr == S_OK)
1992             {
1993                 /* Installer::ProductState */
1994                 hr = Installer_ProductState(szString, &iValue);
1995                 ok(hr == S_OK, "Installer_ProductState failed, hresult 0x%08x\n", hr);
1996                 if (hr == S_OK)
1997                     ok(iValue == INSTALLSTATE_DEFAULT || iValue == INSTALLSTATE_ADVERTISED, "Installer_ProductState returned %d, expected %d or %d\n", iValue, INSTALLSTATE_DEFAULT, INSTALLSTATE_ADVERTISED);
1998
1999                 /* Not found our product code yet? Check */
2000                 if (!bProductFound && !lstrcmpW(szString, szProductCode))
2001                     bProductFound = TRUE;
2002
2003                 /* IEnumVARIANT::Next */
2004                 if (pEnum)
2005                 {
2006                     hr = IEnumVARIANT_Next(pEnum, 1, &var, &celt);
2007                     ok(hr == S_OK, "IEnumVARIANT_Next failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);
2008                     ok(celt == 1, "%d items were retrieved, expected 1\n", celt);
2009                     ok(V_VT(&var) == VT_BSTR, "IEnumVARIANT_Next returned variant of type %d, expected %d\n", V_VT(&var), VT_BSTR);
2010                     ok_w2("%s returned by StringList_Item does not match %s returned by IEnumVARIANT_Next\n", szString, V_BSTR(&var));
2011                     VariantClear(&var);
2012                 }
2013             }
2014         }
2015
2016         if (bProductInstalled) todo_wine
2017         {
2018             ok(bProductInstalled == bProductFound, "Product expected to %s installed but product code was %s\n",
2019                bProductInstalled ? "be" : "not be",
2020                bProductFound ? "found" : "not found");
2021         }
2022
2023         if (pEnum)
2024         {
2025             IEnumVARIANT *pEnum2 = NULL;
2026
2027             if (0) /* Crashes on Windows XP */
2028             {
2029                 /* IEnumVARIANT::Clone, NULL pointer */
2030                 hr = IEnumVARIANT_Clone(pEnum, NULL);
2031             }
2032
2033             /* IEnumVARIANT::Clone */
2034             hr = IEnumVARIANT_Clone(pEnum, &pEnum2);
2035             ok(hr == S_OK, "IEnumVARIANT_Clone failed, hresult 0x%08x\n", hr);
2036             if (hr == S_OK)
2037             {
2038                 /* IEnumVARIANT::Clone is supposed to save the position, but it actually just goes back to the beginning */
2039
2040                 /* IEnumVARIANT::Next of the clone */
2041                 if (iCount)
2042                 {
2043                     hr = IEnumVARIANT_Next(pEnum2, 1, &var, &celt);
2044                     ok(hr == S_OK, "IEnumVARIANT_Next failed, hresult 0x%08x\n", hr);
2045                     ok(celt == 1, "%d items were retrieved, expected 0\n", celt);
2046                     ok(V_VT(&var) == VT_BSTR, "IEnumVARIANT_Next returned variant of type %d, expected %d\n", V_VT(&var), VT_BSTR);
2047                     VariantClear(&var);
2048                 }
2049                 else
2050                     skip("IEnumVARIANT::Next of clone will not return success with 0 products\n");
2051
2052                 IEnumVARIANT_Release(pEnum2);
2053             }
2054
2055             /* IEnumVARIANT::Skip should fail */
2056             hr = IEnumVARIANT_Skip(pEnum, 1);
2057             ok(hr == S_FALSE, "IEnumVARIANT_Skip failed, hresult 0x%08x\n", hr);
2058
2059             /* IEnumVARIANT::Next, NULL variant pointer */
2060             hr = IEnumVARIANT_Next(pEnum, 1, NULL, &celt);
2061             ok(hr == S_FALSE, "IEnumVARIANT_Next failed, hresult 0x%08x\n", hr);
2062             ok(celt == 0, "%d items were retrieved, expected 0\n", celt);
2063
2064             /* IEnumVARIANT::Next, should not return any more items */
2065             hr = IEnumVARIANT_Next(pEnum, 1, &var, &celt);
2066             ok(hr == S_FALSE, "IEnumVARIANT_Next failed, hresult 0x%08x\n", hr);
2067             ok(celt == 0, "%d items were retrieved, expected 0\n", celt);
2068             VariantClear(&var);
2069
2070             /* IEnumVARIANT::Reset */
2071             hr = IEnumVARIANT_Reset(pEnum);
2072             ok(hr == S_OK, "IEnumVARIANT_Reset failed, hresult 0x%08x\n", hr);
2073
2074             if (iCount)
2075             {
2076                 /* IEnumVARIANT::Skip to the last product */
2077                 hr = IEnumVARIANT_Skip(pEnum, iCount-1);
2078                 ok(hr == S_OK, "IEnumVARIANT_Skip failed, hresult 0x%08x\n", hr);
2079
2080                 /* IEnumVARIANT::Next should match the very last retrieved value, also makes sure it works with
2081                  * NULL celt pointer. */
2082                 hr = IEnumVARIANT_Next(pEnum, 1, &var, NULL);
2083                 ok(hr == S_OK, "IEnumVARIANT_Next failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);
2084                 ok(V_VT(&var) == VT_BSTR, "IEnumVARIANT_Next returned variant of type %d, expected %d\n", V_VT(&var), VT_BSTR);
2085                 ok_w2("%s returned by StringList_Item does not match %s returned by IEnumVARIANT_Next\n", szString, V_BSTR(&var));
2086                 VariantClear(&var);
2087             }
2088             else
2089                 skip("IEnumVARIANT::Skip impossible for 0 products\n");
2090         }
2091
2092         /* StringList::Item using an invalid index */
2093         memset(szString, 0, sizeof(szString));
2094         hr = StringList_Item(pStringList, iCount, szString);
2095         ok(hr == DISP_E_BADINDEX, "StringList_Item for an invalid index did not return DISP_E_BADINDEX, hresult 0x%08x\n", hr);
2096
2097         if (pEnum) IEnumVARIANT_Release(pEnum);
2098         if (pUnk) IUnknown_Release(pUnk);
2099         IDispatch_Release(pStringList);
2100     }
2101 }
2102
2103 /* Delete a registry subkey, including all its subkeys (RegDeleteKey does not work on keys with subkeys without
2104  * deleting the subkeys first) */
2105 static UINT delete_registry_key(HKEY hkeyParent, LPCSTR subkey)
2106 {
2107     UINT ret;
2108     CHAR *string = NULL;
2109     HKEY hkey;
2110     DWORD dwSize;
2111
2112     ret = RegOpenKey(hkeyParent, subkey, &hkey);
2113     if (ret != ERROR_SUCCESS) return ret;
2114     ret = RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL);
2115     if (ret != ERROR_SUCCESS) return ret;
2116     if (!(string = HeapAlloc(GetProcessHeap(), 0, ++dwSize))) return ERROR_NOT_ENOUGH_MEMORY;
2117
2118     while (RegEnumKeyA(hkey, 0, string, dwSize) == ERROR_SUCCESS)
2119         delete_registry_key(hkey, string);
2120
2121     RegCloseKey(hkey);
2122     HeapFree(GetProcessHeap(), 0, string);
2123     RegDeleteKeyA(hkeyParent, subkey);
2124     return ERROR_SUCCESS;
2125 }
2126
2127 /* Find a specific registry subkey at any depth within the given key and subkey and return its parent key. */
2128 static UINT find_registry_key(HKEY hkeyParent, LPCSTR subkey, LPCSTR findkey, HKEY *phkey)
2129 {
2130     UINT ret;
2131     CHAR *string = NULL;
2132     int idx = 0;
2133     HKEY hkey;
2134     DWORD dwSize;
2135     BOOL found = FALSE;
2136
2137     *phkey = 0;
2138
2139     ret = RegOpenKey(hkeyParent, subkey, &hkey);
2140     if (ret != ERROR_SUCCESS) return ret;
2141     ret = RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL);
2142     if (ret != ERROR_SUCCESS) return ret;
2143     if (!(string = HeapAlloc(GetProcessHeap(), 0, ++dwSize))) return ERROR_NOT_ENOUGH_MEMORY;
2144
2145     while (!found &&
2146            RegEnumKeyA(hkey, idx++, string, dwSize) == ERROR_SUCCESS)
2147     {
2148         if (!strcmp(string, findkey))
2149         {
2150             *phkey = hkey;
2151             found = TRUE;
2152         }
2153         else if (find_registry_key(hkey, string, findkey, phkey) == ERROR_SUCCESS) found = TRUE;
2154     }
2155
2156     if (*phkey != hkey) RegCloseKey(hkey);
2157     HeapFree(GetProcessHeap(), 0, string);
2158     return (found ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND);
2159 }
2160
2161 static void test_Installer_InstallProduct(void)
2162 {
2163     HRESULT hr;
2164     CHAR path[MAX_PATH];
2165     WCHAR szString[MAX_PATH];
2166     LONG res;
2167     HKEY hkey;
2168     DWORD num, size, type;
2169     int iValue, iCount;
2170     IDispatch *pStringList = NULL;
2171
2172     create_test_files();
2173
2174     /* Installer::InstallProduct */
2175     hr = Installer_InstallProduct(szMsifile, NULL);
2176     if (hr == DISP_E_EXCEPTION)
2177     {
2178         skip("Installer object not supported.\n");
2179         delete_test_files();
2180         return;
2181     }
2182     ok(hr == S_OK, "Installer_InstallProduct failed, hresult 0x%08x\n", hr);
2183
2184     /* Installer::ProductState for our product code, which has been installed */
2185     hr = Installer_ProductState(szProductCode, &iValue);
2186     ok(hr == S_OK, "Installer_ProductState failed, hresult 0x%08x\n", hr);
2187     ok(iValue == INSTALLSTATE_DEFAULT, "Installer_ProductState returned %d, expected %d\n", iValue, INSTALLSTATE_DEFAULT);
2188
2189     /* Installer::ProductInfo for our product code */
2190
2191     /* NULL attribute */
2192     memset(szString, 0, sizeof(szString));
2193     hr = Installer_ProductInfo(szProductCode, NULL, szString);
2194     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2195     ok_exception(hr, szProductInfoException);
2196
2197     /* Nonexistent attribute */
2198     memset(szString, 0, sizeof(szString));
2199     hr = Installer_ProductInfo(szProductCode, szMsifile, szString);
2200     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2201     ok_exception(hr, szProductInfoException);
2202
2203     /* Package name */
2204     memset(szString, 0, sizeof(szString));
2205     hr = Installer_ProductInfo(szProductCode, WINE_INSTALLPROPERTY_PACKAGENAMEW, szString);
2206     ok(hr == S_OK, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2207     todo_wine ok_w2("Installer_ProductInfo returned %s but expected %s\n", szString, szMsifile);
2208
2209     /* Product name */
2210     memset(szString, 0, sizeof(szString));
2211     hr = Installer_ProductInfo(szProductCode, WINE_INSTALLPROPERTY_PRODUCTNAMEW, szString);
2212     ok(hr == S_OK, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2213     todo_wine ok_w2("Installer_ProductInfo returned %s but expected %s\n", szString, szMSITEST);
2214
2215     /* Installer::Products */
2216     test_Installer_Products(TRUE);
2217
2218     /* Installer::RelatedProducts for our upgrade code */
2219     hr = Installer_RelatedProducts(szUpgradeCode, &pStringList);
2220     ok(hr == S_OK, "Installer_RelatedProducts failed, hresult 0x%08x\n", hr);
2221     if (hr == S_OK)
2222     {
2223         /* StringList::Count */
2224         hr = StringList_Count(pStringList, &iCount);
2225         ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr);
2226         ok(iCount == 1, "Expected one related product but found %d\n", iCount);
2227
2228         /* StringList::Item */
2229         memset(szString, 0, sizeof(szString));
2230         hr = StringList_Item(pStringList, 0, szString);
2231         ok(hr == S_OK, "StringList_Item failed (idx 0, count %d), hresult 0x%08x\n", iCount, hr);
2232         ok_w2("StringList_Item returned %s but expected %s\n", szString, szProductCode);
2233
2234         IDispatch_Release(pStringList);
2235     }
2236
2237     /* Check & clean up installed files & registry keys */
2238     ok(delete_pf("msitest\\cabout\\new\\five.txt", TRUE), "File not installed\n");
2239     ok(delete_pf("msitest\\cabout\\new", FALSE), "File not installed\n");
2240     ok(delete_pf("msitest\\cabout\\four.txt", TRUE), "File not installed\n");
2241     ok(delete_pf("msitest\\cabout", FALSE), "File not installed\n");
2242     ok(delete_pf("msitest\\changed\\three.txt", TRUE), "File not installed\n");
2243     ok(delete_pf("msitest\\changed", FALSE), "File not installed\n");
2244     ok(delete_pf("msitest\\first\\two.txt", TRUE), "File not installed\n");
2245     ok(delete_pf("msitest\\first", FALSE), "File not installed\n");
2246     ok(delete_pf("msitest\\one.txt", TRUE), "File not installed\n");
2247     ok(delete_pf("msitest\\filename", TRUE), "File not installed\n");
2248     ok(delete_pf("msitest", FALSE), "File not installed\n");
2249
2250     res = RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine\\msitest", &hkey);
2251     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2252
2253     size = MAX_PATH;
2254     type = REG_SZ;
2255     res = RegQueryValueExA(hkey, "Name", NULL, &type, (LPBYTE)path, &size);
2256     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2257     ok(!lstrcmpA(path, "imaname"), "Expected imaname, got %s\n", path);
2258
2259     size = MAX_PATH;
2260     type = REG_SZ;
2261     res = RegQueryValueExA(hkey, "blah", NULL, &type, (LPBYTE)path, &size);
2262     ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
2263
2264     size = sizeof(num);
2265     type = REG_DWORD;
2266     res = RegQueryValueExA(hkey, "number", NULL, &type, (LPBYTE)&num, &size);
2267     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2268     ok(num == 314, "Expected 314, got %d\n", num);
2269
2270     size = MAX_PATH;
2271     type = REG_SZ;
2272     res = RegQueryValueExA(hkey, "OrderTestName", NULL, &type, (LPBYTE)path, &size);
2273     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2274     ok(!lstrcmpA(path, "OrderTestValue"), "Expected imaname, got %s\n", path);
2275
2276     RegCloseKey(hkey);
2277
2278     res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine\\msitest");
2279     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2280
2281     /* Remove registry keys written by RegisterProduct standard action */
2282     res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{F1C3AF50-8B56-4A69-A00C-00773FE42F30}");
2283     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2284
2285     res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UpgradeCodes\\D8E760ECA1E276347B43E42BDBDA5656");
2286     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2287
2288     res = find_registry_key(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData", "05FA3C1F65B896A40AC00077F34EF203", &hkey);
2289     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2290     if (res == ERROR_SUCCESS)
2291     {
2292         res = delete_registry_key(hkey, "05FA3C1F65B896A40AC00077F34EF203");
2293         ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2294         RegCloseKey(hkey);
2295     }
2296
2297     /* Remove registry keys written by PublishProduct standard action */
2298     res = RegOpenKey(HKEY_CURRENT_USER, "SOFTWARE\\Microsoft\\Installer", &hkey);
2299     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2300
2301     res = delete_registry_key(hkey, "Products\\05FA3C1F65B896A40AC00077F34EF203");
2302     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2303
2304     res = RegDeleteKeyA(hkey, "UpgradeCodes\\D8E760ECA1E276347B43E42BDBDA5656");
2305     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2306
2307     RegCloseKey(hkey);
2308
2309     res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\Products\\05FA3C1F65B896A40AC00077F34EF203");
2310     ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
2311
2312     /* Delete installation files we installed */
2313     delete_test_files();
2314 }
2315
2316 static void test_Installer(void)
2317 {
2318     static WCHAR szBackslash[] = { '\\',0 };
2319     static WCHAR szCreateRecordException[] = { 'C','r','e','a','t','e','R','e','c','o','r','d',',','C','o','u','n','t',0 };
2320     static WCHAR szIntegerDataException[] = { 'I','n','t','e','g','e','r','D','a','t','a',',','F','i','e','l','d',0 };
2321     WCHAR szPath[MAX_PATH];
2322     HRESULT hr;
2323     UINT len;
2324     IDispatch *pSession = NULL, *pDatabase = NULL, *pRecord = NULL, *pStringList = NULL;
2325     int iValue, iCount;
2326
2327     if (!pInstaller) return;
2328
2329     /* Installer::CreateRecord */
2330
2331     /* Test for error */
2332     hr = Installer_CreateRecord(-1, &pRecord);
2333     ok(hr == DISP_E_EXCEPTION, "Installer_CreateRecord failed, hresult 0x%08x\n", hr);
2334     ok_exception(hr, szCreateRecordException);
2335
2336     /* Test for success */
2337     hr = Installer_CreateRecord(1, &pRecord);
2338     ok(hr == S_OK, "Installer_CreateRecord failed, hresult 0x%08x\n", hr);
2339     ok(pRecord != NULL, "Installer_CreateRecord should not have returned NULL record\n");
2340     if (pRecord)
2341     {
2342         /* Record::FieldCountGet */
2343         hr = Record_FieldCountGet(pRecord, &iValue);
2344         ok(hr == S_OK, "Record_FiledCountGet failed, hresult 0x%08x\n", hr);
2345         ok(iValue == 1, "Record_FieldCountGet result was %d but expected 1\n", iValue);
2346
2347         /* Record::IntegerDataGet */
2348         hr = Record_IntegerDataGet(pRecord, 1, &iValue);
2349         ok(hr == S_OK, "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
2350         ok(iValue == MSI_NULL_INTEGER, "Record_IntegerDataGet result was %d but expected %d\n", iValue, MSI_NULL_INTEGER);
2351
2352         /* Record::IntegerDataGet, bad index */
2353         hr = Record_IntegerDataGet(pRecord, 10, &iValue);
2354         ok(hr == S_OK, "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
2355         ok(iValue == MSI_NULL_INTEGER, "Record_IntegerDataGet result was %d but expected %d\n", iValue, MSI_NULL_INTEGER);
2356
2357         /* Record::IntegerDataPut */
2358         hr = Record_IntegerDataPut(pRecord, 1, 100);
2359         ok(hr == S_OK, "Record_IntegerDataPut failed, hresult 0x%08x\n", hr);
2360
2361         /* Record::IntegerDataPut, bad index */
2362         hr = Record_IntegerDataPut(pRecord, 10, 100);
2363         ok(hr == DISP_E_EXCEPTION, "Record_IntegerDataPut failed, hresult 0x%08x\n", hr);
2364         ok_exception(hr, szIntegerDataException);
2365
2366         /* Record::IntegerDataGet */
2367         hr = Record_IntegerDataGet(pRecord, 1, &iValue);
2368         ok(hr == S_OK, "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
2369         ok(iValue == 100, "Record_IntegerDataGet result was %d but expected 100\n", iValue);
2370
2371         IDispatch_Release(pRecord);
2372     }
2373
2374     /* Prepare package */
2375     create_database(msifile, tables, sizeof(tables) / sizeof(msi_table),
2376                     summary_info, sizeof(summary_info) / sizeof(msi_summary_info));
2377
2378     len = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, CURR_DIR, -1, szPath, MAX_PATH);
2379     ok(len, "MultiByteToWideChar returned error %d\n", GetLastError());
2380     if (!len) return;
2381
2382     lstrcatW(szPath, szBackslash);
2383     lstrcatW(szPath, szMsifile);
2384
2385     /* Installer::OpenPackage */
2386     hr = Installer_OpenPackage(szPath, 0, &pSession);
2387     ok(hr == S_OK, "Installer_OpenPackage failed, hresult 0x%08x\n", hr);
2388     if (hr == S_OK)
2389     {
2390         test_Session(pSession);
2391         IDispatch_Release(pSession);
2392     }
2393
2394     /* Installer::OpenDatabase */
2395     hr = Installer_OpenDatabase(szPath, (int)MSIDBOPEN_TRANSACT, &pDatabase);
2396     ok(hr == S_OK, "Installer_OpenDatabase failed, hresult 0x%08x\n", hr);
2397     if (hr == S_OK)
2398     {
2399         test_Database(pDatabase, FALSE);
2400         IDispatch_Release(pDatabase);
2401     }
2402
2403     /* Installer::RegistryValue */
2404     test_Installer_RegistryValue();
2405
2406     /* Installer::ProductState for our product code, which should not be installed */
2407     hr = Installer_ProductState(szProductCode, &iValue);
2408     ok(hr == S_OK, "Installer_ProductState failed, hresult 0x%08x\n", hr);
2409     ok(iValue == INSTALLSTATE_UNKNOWN, "Installer_ProductState returned %d, expected %d\n", iValue, INSTALLSTATE_UNKNOWN);
2410
2411     /* Installer::ProductInfo for our product code, which should not be installed */
2412
2413     /* Package name */
2414     memset(szPath, 0, sizeof(szPath));
2415     hr = Installer_ProductInfo(szProductCode, WINE_INSTALLPROPERTY_PACKAGENAMEW, szPath);
2416     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2417     ok_exception(hr, szProductInfoException);
2418
2419     /* NULL attribute and NULL product code */
2420     memset(szPath, 0, sizeof(szPath));
2421     hr = Installer_ProductInfo(NULL, NULL, szPath);
2422     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2423     ok_exception(hr, szProductInfoException);
2424
2425     /* Installer::Products */
2426     test_Installer_Products(FALSE);
2427
2428     /* Installer::RelatedProducts for our upgrade code, should not find anything */
2429     hr = Installer_RelatedProducts(szUpgradeCode, &pStringList);
2430     ok(hr == S_OK, "Installer_RelatedProducts failed, hresult 0x%08x\n", hr);
2431     if (hr == S_OK)
2432     {
2433         /* StringList::Count */
2434         hr = StringList_Count(pStringList, &iCount);
2435         ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr);
2436         ok(!iCount, "Expected no related products but found %d\n", iCount);
2437
2438         IDispatch_Release(pStringList);
2439     }
2440
2441     /* Installer::Version */
2442     memset(szPath, 0, sizeof(szPath));
2443     hr = Installer_VersionGet(szPath);
2444     ok(hr == S_OK, "Installer_VersionGet failed, hresult 0x%08x\n", hr);
2445
2446     /* Installer::InstallProduct and other tests that depend on our product being installed */
2447     test_Installer_InstallProduct();
2448 }
2449
2450 START_TEST(automation)
2451 {
2452     DWORD len;
2453     char temp_path[MAX_PATH], prev_path[MAX_PATH];
2454     HRESULT hr;
2455     CLSID clsid;
2456     IUnknown *pUnk;
2457
2458     GetSystemTimeAsFileTime(&systemtime);
2459
2460     GetCurrentDirectoryA(MAX_PATH, prev_path);
2461     GetTempPath(MAX_PATH, temp_path);
2462     SetCurrentDirectoryA(temp_path);
2463
2464     lstrcpyA(CURR_DIR, temp_path);
2465     len = lstrlenA(CURR_DIR);
2466
2467     if(len && (CURR_DIR[len - 1] == '\\'))
2468         CURR_DIR[len - 1] = 0;
2469
2470     get_program_files_dir(PROG_FILES_DIR);
2471
2472     hr = OleInitialize(NULL);
2473     ok (hr == S_OK, "OleInitialize returned 0x%08x\n", hr);
2474     hr = CLSIDFromProgID(szProgId, &clsid);
2475     ok (hr == S_OK, "CLSIDFromProgID returned 0x%08x\n", hr);
2476     hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&pUnk);
2477     ok(hr == S_OK, "CoCreateInstance returned 0x%08x\n", hr);
2478
2479     if (pUnk)
2480     {
2481         hr = IUnknown_QueryInterface(pUnk, &IID_IDispatch, (void **)&pInstaller);
2482         ok (hr == S_OK, "IUnknown::QueryInterface returned 0x%08x\n", hr);
2483
2484         test_dispid();
2485         test_dispatch();
2486         test_Installer();
2487
2488         IDispatch_Release(pInstaller);
2489         IUnknown_Release(pUnk);
2490     }
2491
2492     OleUninitialize();
2493
2494     SetCurrentDirectoryA(prev_path);
2495 }