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