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