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