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