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