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