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