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