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