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