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