msi/tests: Free exception information.
[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     HRESULT hr;
1819
1820     /* Session::Installer */
1821     hr = Session_Installer(pSession, &pInst);
1822     ok(hr == S_OK, "Session_Installer failed, hresult 0x%08x\n", hr);
1823     ok(pInst != NULL, "Session_Installer returned NULL IDispatch pointer\n");
1824     ok(pInst == pInstaller, "Session_Installer does not match Installer instance from CoCreateInstance\n");
1825
1826     /* Session::Property, get */
1827     memset(stringw, 0, sizeof(stringw));
1828     hr = Session_PropertyGet(pSession, szProductName, stringw);
1829     ok(hr == S_OK, "Session_PropertyGet failed, hresult 0x%08x\n", hr);
1830     if (strcmp_ww(stringw, szMSITEST) != 0)
1831     {
1832         len = WideCharToMultiByte(CP_ACP, 0, stringw, -1, string, MAX_PATH, NULL, NULL);
1833         ok(len, "WideCharToMultiByteChar returned error %d\n", GetLastError());
1834         ok(0, "Property \"ProductName\" expected to be \"MSITEST\" but was \"%s\"\n", string);
1835     }
1836
1837     /* Session::Property, put */
1838     hr = Session_PropertyPut(pSession, szProductName, szProductName);
1839     ok(hr == S_OK, "Session_PropertyPut failed, hresult 0x%08x\n", hr);
1840     memset(stringw, 0, sizeof(stringw));
1841     hr = Session_PropertyGet(pSession, szProductName, stringw);
1842     ok(hr == S_OK, "Session_PropertyGet failed, hresult 0x%08x\n", hr);
1843     if (strcmp_ww(stringw, szProductName) != 0)
1844     {
1845         len = WideCharToMultiByte(CP_ACP, 0, stringw, -1, string, MAX_PATH, NULL, NULL);
1846         ok(len, "WideCharToMultiByteChar returned error %d\n", GetLastError());
1847         ok(0, "Property \"ProductName\" expected to be \"ProductName\" but was \"%s\"\n", string);
1848     }
1849
1850     /* Try putting a property using empty property identifier */
1851     hr = Session_PropertyPut(pSession, szEmpty, szProductName);
1852     ok(hr == DISP_E_EXCEPTION, "Session_PropertyPut failed, hresult 0x%08x\n", hr);
1853     ok_exception(hr, szPropertyName);
1854
1855     /* Try putting a property using illegal property identifier */
1856     hr = Session_PropertyPut(pSession, szEquals, szProductName);
1857     ok(hr == S_OK, "Session_PropertyPut failed, hresult 0x%08x\n", hr);
1858
1859     /* Session::Language, get */
1860     hr = Session_LanguageGet(pSession, &len);
1861     ok(hr == S_OK, "Session_LanguageGet failed, hresult 0x%08x\n", hr);
1862     /* Not sure how to check the language is correct */
1863
1864     /* Session::Mode, get */
1865     hr = Session_ModeGet(pSession, MSIRUNMODE_REBOOTATEND, &bool);
1866     ok(hr == S_OK, "Session_ModeGet failed, hresult 0x%08x\n", hr);
1867     ok(!bool, "Reboot at end session mode is %d\n", bool);
1868
1869     hr = Session_ModeGet(pSession, MSIRUNMODE_MAINTENANCE, &bool);
1870     ok(hr == S_OK, "Session_ModeGet failed, hresult 0x%08x\n", hr);
1871     ok(!bool, "Maintenance mode is %d\n", bool);
1872
1873     /* Session::Mode, put */
1874     hr = Session_ModePut(pSession, MSIRUNMODE_REBOOTATEND, TRUE);
1875     ok(hr == S_OK, "Session_ModePut failed, hresult 0x%08x\n", hr);
1876     hr = Session_ModeGet(pSession, MSIRUNMODE_REBOOTATEND, &bool);
1877     ok(hr == S_OK, "Session_ModeGet failed, hresult 0x%08x\n", hr);
1878     ok(bool, "Reboot at end session mode is %d, expected 1\n", bool);
1879     hr = Session_ModePut(pSession, MSIRUNMODE_REBOOTATEND, FALSE);  /* set it again so we don't reboot */
1880     ok(hr == S_OK, "Session_ModePut failed, hresult 0x%08x\n", hr);
1881
1882     hr = Session_ModePut(pSession, MSIRUNMODE_REBOOTNOW, TRUE);
1883     todo_wine ok(hr == S_OK, "Session_ModePut failed, hresult 0x%08x\n", hr);
1884     if (hr == DISP_E_EXCEPTION) ok_exception(hr, szModeFlag);
1885
1886     hr = Session_ModeGet(pSession, MSIRUNMODE_REBOOTNOW, &bool);
1887     ok(hr == S_OK, "Session_ModeGet failed, hresult 0x%08x\n", hr);
1888     ok(bool, "Reboot now mode is %d, expected 1\n", bool);
1889
1890     hr = Session_ModePut(pSession, MSIRUNMODE_REBOOTNOW, FALSE);  /* set it again so we don't reboot */
1891     todo_wine ok(hr == S_OK, "Session_ModePut failed, hresult 0x%08x\n", hr);
1892     if (hr == DISP_E_EXCEPTION) ok_exception(hr, szModeFlag);
1893
1894     hr = Session_ModePut(pSession, MSIRUNMODE_MAINTENANCE, TRUE);
1895     ok(hr == DISP_E_EXCEPTION, "Session_ModePut failed, hresult 0x%08x\n", hr);
1896     ok_exception(hr, szModeFlag);
1897
1898     /* Session::Database, get */
1899     hr = Session_Database(pSession, &pDatabase);
1900     ok(hr == S_OK, "Session_Database failed, hresult 0x%08x\n", hr);
1901     if (hr == S_OK)
1902     {
1903         test_Database(pDatabase, TRUE);
1904         IDispatch_Release(pDatabase);
1905     }
1906
1907     /* Session::EvaluateCondition */
1908     hr = Session_EvaluateCondition(pSession, NULL, &myint);
1909     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1910     ok(myint == MSICONDITION_NONE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1911
1912     hr = Session_EvaluateCondition(pSession, szEmpty, &myint);
1913     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1914     ok(myint == MSICONDITION_NONE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1915
1916     hr = Session_EvaluateCondition(pSession, szEquals, &myint);
1917     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1918     ok(myint == MSICONDITION_ERROR, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1919
1920     /* Session::DoAction(CostInitialize) must occur before the next statements */
1921     hr = Session_DoAction(pSession, szCostInitialize, &myint);
1922     ok(hr == S_OK, "Session_DoAction failed, hresult 0x%08x\n", hr);
1923     ok(myint == IDOK, "DoAction(CostInitialize) returned %d, %d expected\n", myint, IDOK);
1924
1925     /* Session::SetInstallLevel */
1926     hr = Session_SetInstallLevel(pSession, INSTALLLEVEL_MINIMUM);
1927     ok(hr == S_OK, "Session_SetInstallLevel failed, hresult 0x%08x\n", hr);
1928
1929     /* Session::FeatureCurrentState, get */
1930     hr = Session_FeatureCurrentState(pSession, szOne, &myint);
1931     ok(hr == S_OK, "Session_FeatureCurrentState failed, hresult 0x%08x\n", hr);
1932     ok(myint == INSTALLSTATE_UNKNOWN, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1933
1934     /* Session::Message */
1935     hr = Installer_CreateRecord(0, &record);
1936     ok(hr == S_OK, "Installer_CreateRecord failed: %08x\n", hr);
1937     hr = Session_Message(pSession, INSTALLMESSAGE_INFO, record, &myint);
1938     ok(hr == S_OK, "Session_Message failed: %08x\n", hr);
1939     ok(myint == 0, "Session_Message returned %x\n", myint);
1940
1941     /* Session::EvaluateCondition */
1942     hr = Session_EvaluateCondition(pSession, szOneStateFalse, &myint);
1943     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1944     ok(myint == MSICONDITION_FALSE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1945
1946     hr = Session_EvaluateCondition(pSession, szOneStateTrue, &myint);
1947     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1948     ok(myint == MSICONDITION_TRUE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1949
1950     /* Session::FeatureRequestState, put */
1951     hr = Session_FeatureRequestStatePut(pSession, szOne, INSTALLSTATE_ADVERTISED);
1952     ok(hr == S_OK, "Session_FeatureRequestStatePut failed, hresult 0x%08x\n", hr);
1953     hr = Session_FeatureRequestStateGet(pSession, szOne, &myint);
1954     ok(hr == S_OK, "Session_FeatureRequestStateGet failed, hresult 0x%08x\n", hr);
1955     ok(myint == INSTALLSTATE_ADVERTISED, "Feature request state was %d but expected %d\n", myint, INSTALLSTATE_ADVERTISED);
1956
1957     /* Session::EvaluateCondition */
1958     hr = Session_EvaluateCondition(pSession, szOneActionFalse, &myint);
1959     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1960     ok(myint == MSICONDITION_FALSE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1961
1962     hr = Session_EvaluateCondition(pSession, szOneActionTrue, &myint);
1963     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1964     ok(myint == MSICONDITION_TRUE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1965 }
1966
1967 /* delete key and all its subkeys */
1968 static DWORD delete_key( HKEY hkey )
1969 {
1970     char name[MAX_PATH];
1971     DWORD ret;
1972
1973     while (!(ret = RegEnumKeyA(hkey, 0, name, sizeof(name))))
1974     {
1975         HKEY tmp;
1976         if (!(ret = RegOpenKeyExA( hkey, name, 0, KEY_ENUMERATE_SUB_KEYS, &tmp )))
1977         {
1978             ret = delete_key( tmp );
1979             RegCloseKey( tmp );
1980         }
1981         if (ret) break;
1982     }
1983     if (ret != ERROR_NO_MORE_ITEMS) return ret;
1984     RegDeleteKeyA( hkey, "" );
1985     return 0;
1986 }
1987
1988 static void test_Installer_RegistryValue(void)
1989 {
1990     static const DWORD qw[2] = { 0x12345678, 0x87654321 };
1991     static const WCHAR szKey[] = { 'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\','T','e','s','t',0 };
1992     static const WCHAR szOne[] = { 'O','n','e',0 };
1993     static const WCHAR szTwo[] = { 'T','w','o',0 };
1994     static const WCHAR szThree[] = { 'T','h','r','e','e',0 };
1995     static const WCHAR szREG_BINARY[] = { '(','R','E','G','_','B','I','N','A','R','Y',')',0 };
1996     static const WCHAR szFour[] = { 'F','o','u','r',0 };
1997     static const WCHAR szExpand[] = { '%','M','S','I','T','E','S','T','%',0 };
1998     static const WCHAR szFive[] = { 'F','i','v','e',0,'H','i',0,0 };
1999     static const WCHAR szFiveHi[] = { 'F','i','v','e','\n','H','i',0 };
2000     static const WCHAR szSix[] = { 'S','i','x',0 };
2001     static const WCHAR szREG_[] = { '(','R','E','G','_',']',0 };
2002     static const WCHAR szSeven[] = { 'S','e','v','e','n',0 };
2003     static const WCHAR szEight[] = { 'E','i','g','h','t',0 };
2004     static const WCHAR szBlank[] = { 0 };
2005     VARIANT varresult;
2006     VARIANTARG vararg;
2007     WCHAR szString[MAX_PATH];
2008     HKEY hkey, hkey_sub;
2009     HKEY curr_user = (HKEY)1;
2010     HRESULT hr;
2011     BOOL bRet;
2012     LONG lRet;
2013
2014     /* Delete keys */
2015     SetLastError(0xdeadbeef);
2016     lRet = RegOpenKeyW( HKEY_CURRENT_USER, szKey, &hkey );
2017     if (!lRet && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
2018     {
2019         win_skip("Needed W-functions are not implemented\n");
2020         return;
2021     }
2022     if (!lRet)
2023         delete_key( hkey );
2024
2025     /* Does our key exist? Shouldn't; check with all three possible value parameter types */
2026     hr = Installer_RegistryValueE(curr_user, szKey, &bRet);
2027     ok(hr == S_OK, "Installer_RegistryValueE failed, hresult 0x%08x\n", hr);
2028     ok(!bRet, "Registry key expected to not exist, but Installer_RegistryValue claims it does\n");
2029
2030     memset(szString, 0, sizeof(szString));
2031     hr = Installer_RegistryValueW(curr_user, szKey, NULL, szString);
2032     ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2033
2034     memset(szString, 0, sizeof(szString));
2035     hr = Installer_RegistryValueI(curr_user, szKey, 0, szString, VT_BSTR);
2036     ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2037
2038     /* Create key */
2039     ok(!RegCreateKeyW( HKEY_CURRENT_USER, szKey, &hkey ), "RegCreateKeyW failed\n");
2040
2041     ok(!RegSetValueExW(hkey,szOne,0,REG_SZ, (const BYTE *)szOne, sizeof(szOne)),
2042         "RegSetValueExW failed\n");
2043     ok(!RegSetValueExW(hkey,szTwo,0,REG_DWORD, (const BYTE *)qw, 4),
2044         "RegSetValueExW failed\n");
2045     ok(!RegSetValueExW(hkey,szThree,0,REG_BINARY, (const BYTE *)qw, 4),
2046         "RegSetValueExW failed\n");
2047     ok(SetEnvironmentVariableA("MSITEST", "Four"), "SetEnvironmentVariableA failed %d\n", GetLastError());
2048     ok(!RegSetValueExW(hkey,szFour,0,REG_EXPAND_SZ, (const BYTE *)szExpand, sizeof(szExpand)),
2049         "RegSetValueExW failed\n");
2050     ok(!RegSetValueExW(hkey,szFive,0,REG_MULTI_SZ, (const BYTE *)szFive, sizeof(szFive)),
2051         "RegSetValueExW failed\n");
2052     ok(!RegSetValueExW(hkey,szSix,0,REG_QWORD, (const BYTE *)qw, 8),
2053         "RegSetValueExW failed\n");
2054     ok(!RegSetValueExW(hkey,szSeven,0,REG_NONE, NULL, 0),
2055         "RegSetValueExW failed\n");
2056
2057     ok(!RegSetValueExW(hkey,NULL,0,REG_SZ, (const BYTE *)szOne, sizeof(szOne)),
2058         "RegSetValueExW failed\n");
2059
2060     ok(!RegCreateKeyW( hkey, szEight, &hkey_sub ), "RegCreateKeyW failed\n");
2061
2062     /* Does our key exist? It should, and make sure we retrieve the correct default value */
2063     bRet = FALSE;
2064     hr = Installer_RegistryValueE(curr_user, szKey, &bRet);
2065     ok(hr == S_OK, "Installer_RegistryValueE failed, hresult 0x%08x\n", hr);
2066     ok(bRet, "Registry key expected to exist, but Installer_RegistryValue claims it does not\n");
2067
2068     memset(szString, 0, sizeof(szString));
2069     hr = Installer_RegistryValueW(curr_user, szKey, NULL, szString);
2070     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2071     ok_w2("Default registry value \"%s\" does not match expected \"%s\"\n", szString, szOne);
2072
2073     /* Ask for the value of a nonexistent key */
2074     memset(szString, 0, sizeof(szString));
2075     hr = Installer_RegistryValueW(curr_user, szKey, szExpand, szString);
2076     ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2077
2078     /* Get values of keys */
2079     memset(szString, 0, sizeof(szString));
2080     hr = Installer_RegistryValueW(curr_user, szKey, szOne, szString);
2081     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2082     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szOne);
2083
2084     VariantInit(&vararg);
2085     V_VT(&vararg) = VT_BSTR;
2086     V_BSTR(&vararg) = SysAllocString(szTwo);
2087     hr = Installer_RegistryValue(curr_user, szKey, vararg, &varresult, VT_I4);
2088     ok(hr == S_OK, "Installer_RegistryValue failed, hresult 0x%08x\n", hr);
2089     ok(V_I4(&varresult) == 305419896, "Registry value %d does not match expected value\n", V_I4(&varresult));
2090     VariantClear(&varresult);
2091
2092     memset(szString, 0, sizeof(szString));
2093     hr = Installer_RegistryValueW(curr_user, szKey, szThree, szString);
2094     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2095     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szREG_BINARY);
2096
2097     memset(szString, 0, sizeof(szString));
2098     hr = Installer_RegistryValueW(curr_user, szKey, szFour, szString);
2099     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2100     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szFour);
2101
2102     /* Vista does not NULL-terminate this case */
2103     memset(szString, 0, sizeof(szString));
2104     hr = Installer_RegistryValueW(curr_user, szKey, szFive, szString);
2105     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2106     ok_w2n("Registry value \"%s\" does not match expected \"%s\"\n",
2107            szString, szFiveHi, lstrlenW(szFiveHi));
2108
2109     memset(szString, 0, sizeof(szString));
2110     hr = Installer_RegistryValueW(curr_user, szKey, szSix, szString);
2111     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2112     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szREG_);
2113
2114     VariantInit(&vararg);
2115     V_VT(&vararg) = VT_BSTR;
2116     V_BSTR(&vararg) = SysAllocString(szSeven);
2117     hr = Installer_RegistryValue(curr_user, szKey, vararg, &varresult, VT_EMPTY);
2118     ok(hr == S_OK, "Installer_RegistryValue failed, hresult 0x%08x\n", hr);
2119
2120     /* Get string class name for the key */
2121     memset(szString, 0, sizeof(szString));
2122     hr = Installer_RegistryValueI(curr_user, szKey, 0, szString, VT_BSTR);
2123     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2124     ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szBlank);
2125
2126     /* Get name of a value by positive number (RegEnumValue like), valid index */
2127     memset(szString, 0, sizeof(szString));
2128     hr = Installer_RegistryValueI(curr_user, szKey, 2, szString, VT_BSTR);
2129     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2130     /* RegEnumValue order seems different on wine */
2131     todo_wine ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szTwo);
2132
2133     /* Get name of a value by positive number (RegEnumValue like), invalid index */
2134     memset(szString, 0, sizeof(szString));
2135     hr = Installer_RegistryValueI(curr_user, szKey, 10, szString, VT_EMPTY);
2136     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2137
2138     /* Get name of a subkey by negative number (RegEnumValue like), valid index */
2139     memset(szString, 0, sizeof(szString));
2140     hr = Installer_RegistryValueI(curr_user, szKey, -1, szString, VT_BSTR);
2141     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2142     ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szEight);
2143
2144     /* Get name of a subkey by negative number (RegEnumValue like), invalid index */
2145     memset(szString, 0, sizeof(szString));
2146     hr = Installer_RegistryValueI(curr_user, szKey, -10, szString, VT_EMPTY);
2147     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2148
2149     /* clean up */
2150     delete_key(hkey);
2151 }
2152
2153 static void test_Installer_Products(BOOL bProductInstalled)
2154 {
2155     WCHAR szString[MAX_PATH];
2156     HRESULT hr;
2157     int idx;
2158     IUnknown *pUnk = NULL;
2159     IEnumVARIANT *pEnum = NULL;
2160     VARIANT var;
2161     ULONG celt;
2162     int iCount, iValue;
2163     IDispatch *pStringList = NULL;
2164     BOOL bProductFound = FALSE;
2165
2166     /* Installer::Products */
2167     hr = Installer_Products(&pStringList);
2168     ok(hr == S_OK, "Installer_Products failed, hresult 0x%08x\n", hr);
2169     if (hr == S_OK)
2170     {
2171         /* StringList::_NewEnum */
2172         hr = StringList__NewEnum(pStringList, &pUnk);
2173         ok(hr == S_OK, "StringList_NewEnum failed, hresult 0x%08x\n", hr);
2174         if (hr == S_OK)
2175         {
2176             hr = IUnknown_QueryInterface(pUnk, &IID_IEnumVARIANT, (void **)&pEnum);
2177             ok (hr == S_OK, "IUnknown::QueryInterface returned 0x%08x\n", hr);
2178         }
2179         if (!pEnum)
2180             skip("IEnumVARIANT tests\n");
2181
2182         /* StringList::Count */
2183         hr = StringList_Count(pStringList, &iCount);
2184         ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr);
2185
2186         for (idx=0; idx<iCount; idx++)
2187         {
2188             /* StringList::Item */
2189             memset(szString, 0, sizeof(szString));
2190             hr = StringList_Item(pStringList, idx, szString);
2191             ok(hr == S_OK, "StringList_Item failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);
2192
2193             if (hr == S_OK)
2194             {
2195                 /* Installer::ProductState */
2196                 hr = Installer_ProductState(szString, &iValue);
2197                 ok(hr == S_OK, "Installer_ProductState failed, hresult 0x%08x\n", hr);
2198                 if (hr == S_OK)
2199                     ok(iValue == INSTALLSTATE_DEFAULT || iValue == INSTALLSTATE_ADVERTISED, "Installer_ProductState returned %d, expected %d or %d\n", iValue, INSTALLSTATE_DEFAULT, INSTALLSTATE_ADVERTISED);
2200
2201                 /* Not found our product code yet? Check */
2202                 if (!bProductFound && !strcmp_ww(szString, szProductCode))
2203                     bProductFound = TRUE;
2204
2205                 /* IEnumVARIANT::Next */
2206                 if (pEnum)
2207                 {
2208                     hr = IEnumVARIANT_Next(pEnum, 1, &var, &celt);
2209                     ok(hr == S_OK, "IEnumVARIANT_Next failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);
2210                     ok(celt == 1, "%d items were retrieved, expected 1\n", celt);
2211                     ok(V_VT(&var) == VT_BSTR, "IEnumVARIANT_Next returned variant of type %d, expected %d\n", V_VT(&var), VT_BSTR);
2212                     ok_w2("%s returned by StringList_Item does not match %s returned by IEnumVARIANT_Next\n", szString, V_BSTR(&var));
2213                     VariantClear(&var);
2214                 }
2215             }
2216         }
2217
2218         if (bProductInstalled)
2219         {
2220             ok(bProductInstalled == bProductFound, "Product expected to %s installed but product code was %s\n",
2221                bProductInstalled ? "be" : "not be",
2222                bProductFound ? "found" : "not found");
2223         }
2224
2225         if (pEnum)
2226         {
2227             IEnumVARIANT *pEnum2 = NULL;
2228
2229             if (0) /* Crashes on Windows XP */
2230             {
2231                 /* IEnumVARIANT::Clone, NULL pointer */
2232                 hr = IEnumVARIANT_Clone(pEnum, NULL);
2233             }
2234
2235             /* IEnumVARIANT::Clone */
2236             hr = IEnumVARIANT_Clone(pEnum, &pEnum2);
2237             ok(hr == S_OK, "IEnumVARIANT_Clone failed, hresult 0x%08x\n", hr);
2238             if (hr == S_OK)
2239             {
2240                 /* IEnumVARIANT::Clone is supposed to save the position, but it actually just goes back to the beginning */
2241
2242                 /* IEnumVARIANT::Next of the clone */
2243                 if (iCount)
2244                 {
2245                     hr = IEnumVARIANT_Next(pEnum2, 1, &var, &celt);
2246                     ok(hr == S_OK, "IEnumVARIANT_Next failed, hresult 0x%08x\n", hr);
2247                     ok(celt == 1, "%d items were retrieved, expected 0\n", celt);
2248                     ok(V_VT(&var) == VT_BSTR, "IEnumVARIANT_Next returned variant of type %d, expected %d\n", V_VT(&var), VT_BSTR);
2249                     VariantClear(&var);
2250                 }
2251                 else
2252                     skip("IEnumVARIANT::Next of clone will not return success with 0 products\n");
2253
2254                 IEnumVARIANT_Release(pEnum2);
2255             }
2256
2257             /* IEnumVARIANT::Skip should fail */
2258             hr = IEnumVARIANT_Skip(pEnum, 1);
2259             ok(hr == S_FALSE, "IEnumVARIANT_Skip failed, hresult 0x%08x\n", hr);
2260
2261             /* IEnumVARIANT::Next, NULL variant pointer */
2262             hr = IEnumVARIANT_Next(pEnum, 1, NULL, &celt);
2263             ok(hr == S_FALSE, "IEnumVARIANT_Next failed, hresult 0x%08x\n", hr);
2264             ok(celt == 0, "%d items were retrieved, expected 0\n", celt);
2265
2266             /* IEnumVARIANT::Next, should not return any more items */
2267             hr = IEnumVARIANT_Next(pEnum, 1, &var, &celt);
2268             ok(hr == S_FALSE, "IEnumVARIANT_Next failed, hresult 0x%08x\n", hr);
2269             ok(celt == 0, "%d items were retrieved, expected 0\n", celt);
2270             VariantClear(&var);
2271
2272             /* IEnumVARIANT::Reset */
2273             hr = IEnumVARIANT_Reset(pEnum);
2274             ok(hr == S_OK, "IEnumVARIANT_Reset failed, hresult 0x%08x\n", hr);
2275
2276             if (iCount)
2277             {
2278                 /* IEnumVARIANT::Skip to the last product */
2279                 hr = IEnumVARIANT_Skip(pEnum, iCount-1);
2280                 ok(hr == S_OK, "IEnumVARIANT_Skip failed, hresult 0x%08x\n", hr);
2281
2282                 /* IEnumVARIANT::Next should match the very last retrieved value, also makes sure it works with
2283                  * NULL celt pointer. */
2284                 hr = IEnumVARIANT_Next(pEnum, 1, &var, NULL);
2285                 ok(hr == S_OK, "IEnumVARIANT_Next failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);
2286                 ok(V_VT(&var) == VT_BSTR, "IEnumVARIANT_Next returned variant of type %d, expected %d\n", V_VT(&var), VT_BSTR);
2287                 ok_w2("%s returned by StringList_Item does not match %s returned by IEnumVARIANT_Next\n", szString, V_BSTR(&var));
2288                 VariantClear(&var);
2289             }
2290             else
2291                 skip("IEnumVARIANT::Skip impossible for 0 products\n");
2292         }
2293
2294         /* StringList::Item using an invalid index */
2295         memset(szString, 0, sizeof(szString));
2296         hr = StringList_Item(pStringList, iCount, szString);
2297         ok(hr == DISP_E_BADINDEX, "StringList_Item for an invalid index did not return DISP_E_BADINDEX, hresult 0x%08x\n", hr);
2298
2299         if (pEnum) IEnumVARIANT_Release(pEnum);
2300         if (pUnk) IUnknown_Release(pUnk);
2301         IDispatch_Release(pStringList);
2302     }
2303 }
2304
2305 /* Delete a registry subkey, including all its subkeys (RegDeleteKey does not work on keys with subkeys without
2306  * deleting the subkeys first) */
2307 static UINT delete_registry_key(HKEY hkeyParent, LPCSTR subkey)
2308 {
2309     UINT ret;
2310     CHAR *string = NULL;
2311     HKEY hkey;
2312     DWORD dwSize;
2313
2314     ret = RegOpenKey(hkeyParent, subkey, &hkey);
2315     if (ret != ERROR_SUCCESS) return ret;
2316     ret = RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL);
2317     if (ret != ERROR_SUCCESS) return ret;
2318     if (!(string = HeapAlloc(GetProcessHeap(), 0, ++dwSize))) return ERROR_NOT_ENOUGH_MEMORY;
2319
2320     while (RegEnumKeyA(hkey, 0, string, dwSize) == ERROR_SUCCESS)
2321         delete_registry_key(hkey, string);
2322
2323     RegCloseKey(hkey);
2324     HeapFree(GetProcessHeap(), 0, string);
2325     RegDeleteKeyA(hkeyParent, subkey);
2326     return ERROR_SUCCESS;
2327 }
2328
2329 /* Find a specific registry subkey at any depth within the given key and subkey and return its parent key. */
2330 static UINT find_registry_key(HKEY hkeyParent, LPCSTR subkey, LPCSTR findkey, HKEY *phkey)
2331 {
2332     UINT ret;
2333     CHAR *string = NULL;
2334     int idx = 0;
2335     HKEY hkey;
2336     DWORD dwSize;
2337     BOOL found = FALSE;
2338
2339     *phkey = 0;
2340
2341     ret = RegOpenKey(hkeyParent, subkey, &hkey);
2342     if (ret != ERROR_SUCCESS) return ret;
2343     ret = RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL);
2344     if (ret != ERROR_SUCCESS) return ret;
2345     if (!(string = HeapAlloc(GetProcessHeap(), 0, ++dwSize))) return ERROR_NOT_ENOUGH_MEMORY;
2346
2347     while (!found &&
2348            RegEnumKeyA(hkey, idx++, string, dwSize) == ERROR_SUCCESS)
2349     {
2350         if (!strcmp(string, findkey))
2351         {
2352             *phkey = hkey;
2353             found = TRUE;
2354         }
2355         else if (find_registry_key(hkey, string, findkey, phkey) == ERROR_SUCCESS) found = TRUE;
2356     }
2357
2358     if (*phkey != hkey) RegCloseKey(hkey);
2359     HeapFree(GetProcessHeap(), 0, string);
2360     return (found ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND);
2361 }
2362
2363 static void test_Installer_InstallProduct(void)
2364 {
2365     HRESULT hr;
2366     CHAR path[MAX_PATH];
2367     WCHAR szString[MAX_PATH];
2368     LONG res;
2369     HKEY hkey;
2370     DWORD num, size, type;
2371     int iValue, iCount;
2372     IDispatch *pStringList = NULL;
2373
2374     create_test_files();
2375
2376     /* Installer::InstallProduct */
2377     hr = Installer_InstallProduct(szMsifile, NULL);
2378     if (hr == DISP_E_EXCEPTION)
2379     {
2380         skip("Installer object not supported.\n");
2381         delete_test_files();
2382         return;
2383     }
2384     ok(hr == S_OK, "Installer_InstallProduct failed, hresult 0x%08x\n", hr);
2385
2386     /* Installer::ProductState for our product code, which has been installed */
2387     hr = Installer_ProductState(szProductCode, &iValue);
2388     ok(hr == S_OK, "Installer_ProductState failed, hresult 0x%08x\n", hr);
2389     ok(iValue == INSTALLSTATE_DEFAULT, "Installer_ProductState returned %d, expected %d\n", iValue, INSTALLSTATE_DEFAULT);
2390
2391     /* Installer::ProductInfo for our product code */
2392
2393     /* NULL attribute */
2394     memset(szString, 0, sizeof(szString));
2395     hr = Installer_ProductInfo(szProductCode, NULL, szString);
2396     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2397     ok_exception(hr, szProductInfoException);
2398
2399     /* Nonexistent attribute */
2400     memset(szString, 0, sizeof(szString));
2401     hr = Installer_ProductInfo(szProductCode, szMsifile, szString);
2402     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2403     ok_exception(hr, szProductInfoException);
2404
2405     /* Package name */
2406     memset(szString, 0, sizeof(szString));
2407     hr = Installer_ProductInfo(szProductCode, WINE_INSTALLPROPERTY_PACKAGENAMEW, szString);
2408     ok(hr == S_OK, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2409     todo_wine ok_w2("Installer_ProductInfo returned %s but expected %s\n", szString, szMsifile);
2410
2411     /* Product name */
2412     memset(szString, 0, sizeof(szString));
2413     hr = Installer_ProductInfo(szProductCode, WINE_INSTALLPROPERTY_PRODUCTNAMEW, szString);
2414     ok(hr == S_OK, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2415     todo_wine ok_w2("Installer_ProductInfo returned %s but expected %s\n", szString, szMSITEST);
2416
2417     /* Installer::Products */
2418     test_Installer_Products(TRUE);
2419
2420     /* Installer::RelatedProducts for our upgrade code */
2421     hr = Installer_RelatedProducts(szUpgradeCode, &pStringList);
2422     ok(hr == S_OK, "Installer_RelatedProducts failed, hresult 0x%08x\n", hr);
2423     if (hr == S_OK)
2424     {
2425         /* StringList::Count */
2426         hr = StringList_Count(pStringList, &iCount);
2427         ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr);
2428         ok(iCount == 1, "Expected one related product but found %d\n", iCount);
2429
2430         /* StringList::Item */
2431         memset(szString, 0, sizeof(szString));
2432         hr = StringList_Item(pStringList, 0, szString);
2433         ok(hr == S_OK, "StringList_Item failed (idx 0, count %d), hresult 0x%08x\n", iCount, hr);
2434         ok_w2("StringList_Item returned %s but expected %s\n", szString, szProductCode);
2435
2436         IDispatch_Release(pStringList);
2437     }
2438
2439     /* Check & clean up installed files & registry keys */
2440     ok(delete_pf("msitest\\cabout\\new\\five.txt", TRUE), "File not installed\n");
2441     ok(delete_pf("msitest\\cabout\\new", FALSE), "File not installed\n");
2442     ok(delete_pf("msitest\\cabout\\four.txt", TRUE), "File not installed\n");
2443     ok(delete_pf("msitest\\cabout", FALSE), "File not installed\n");
2444     ok(delete_pf("msitest\\changed\\three.txt", TRUE), "File not installed\n");
2445     ok(delete_pf("msitest\\changed", FALSE), "File not installed\n");
2446     ok(delete_pf("msitest\\first\\two.txt", TRUE), "File not installed\n");
2447     ok(delete_pf("msitest\\first", FALSE), "File not installed\n");
2448     ok(delete_pf("msitest\\one.txt", TRUE), "File not installed\n");
2449     ok(delete_pf("msitest\\filename", TRUE), "File not installed\n");
2450     ok(delete_pf("msitest", FALSE), "File not installed\n");
2451
2452     res = RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine\\msitest", &hkey);
2453     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2454
2455     size = MAX_PATH;
2456     type = REG_SZ;
2457     res = RegQueryValueExA(hkey, "Name", NULL, &type, (LPBYTE)path, &size);
2458     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2459     ok(!lstrcmpA(path, "imaname"), "Expected imaname, got %s\n", path);
2460
2461     size = MAX_PATH;
2462     type = REG_SZ;
2463     res = RegQueryValueExA(hkey, "blah", NULL, &type, (LPBYTE)path, &size);
2464     ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
2465
2466     size = sizeof(num);
2467     type = REG_DWORD;
2468     res = RegQueryValueExA(hkey, "number", NULL, &type, (LPBYTE)&num, &size);
2469     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2470     ok(num == 314, "Expected 314, got %d\n", num);
2471
2472     size = MAX_PATH;
2473     type = REG_SZ;
2474     res = RegQueryValueExA(hkey, "OrderTestName", NULL, &type, (LPBYTE)path, &size);
2475     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2476     ok(!lstrcmpA(path, "OrderTestValue"), "Expected imaname, got %s\n", path);
2477
2478     RegCloseKey(hkey);
2479
2480     res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine\\msitest");
2481     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2482
2483     /* Remove registry keys written by RegisterProduct standard action */
2484     res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{F1C3AF50-8B56-4A69-A00C-00773FE42F30}");
2485     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2486
2487     res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UpgradeCodes\\D8E760ECA1E276347B43E42BDBDA5656");
2488     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2489
2490     res = find_registry_key(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData", "05FA3C1F65B896A40AC00077F34EF203", &hkey);
2491     ok(res == ERROR_SUCCESS ||
2492        broken(res == ERROR_FILE_NOT_FOUND), /* win9x */
2493        "Expected ERROR_SUCCESS, got %d\n", res);
2494     if (res == ERROR_SUCCESS)
2495     {
2496         res = delete_registry_key(hkey, "05FA3C1F65B896A40AC00077F34EF203");
2497         ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2498         RegCloseKey(hkey);
2499
2500         res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\Products\\05FA3C1F65B896A40AC00077F34EF203");
2501         ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
2502     }
2503     else
2504     {
2505         /* win9x defaults to a per-machine install. */
2506         RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\Products\\05FA3C1F65B896A40AC00077F34EF203");
2507     }
2508
2509     /* Remove registry keys written by PublishProduct standard action */
2510     res = RegOpenKey(HKEY_CURRENT_USER, "SOFTWARE\\Microsoft\\Installer", &hkey);
2511     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2512
2513     res = delete_registry_key(hkey, "Products\\05FA3C1F65B896A40AC00077F34EF203");
2514     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2515
2516     res = RegDeleteKeyA(hkey, "UpgradeCodes\\D8E760ECA1E276347B43E42BDBDA5656");
2517     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2518
2519     RegCloseKey(hkey);
2520
2521     /* Delete installation files we installed */
2522     delete_test_files();
2523 }
2524
2525 static void test_Installer(void)
2526 {
2527     static WCHAR szCreateRecordException[] = { 'C','r','e','a','t','e','R','e','c','o','r','d',',','C','o','u','n','t',0 };
2528     static WCHAR szIntegerDataException[] = { 'I','n','t','e','g','e','r','D','a','t','a',',','F','i','e','l','d',0 };
2529     WCHAR szPath[MAX_PATH];
2530     HRESULT hr;
2531     IDispatch *pSession = NULL, *pDatabase = NULL, *pRecord = NULL, *pStringList = NULL;
2532     int iValue, iCount;
2533
2534     if (!pInstaller) return;
2535
2536     /* Installer::CreateRecord */
2537
2538     /* Test for error */
2539     hr = Installer_CreateRecord(-1, &pRecord);
2540     ok(hr == DISP_E_EXCEPTION, "Installer_CreateRecord failed, hresult 0x%08x\n", hr);
2541     ok_exception(hr, szCreateRecordException);
2542
2543     /* Test for success */
2544     hr = Installer_CreateRecord(1, &pRecord);
2545     ok(hr == S_OK, "Installer_CreateRecord failed, hresult 0x%08x\n", hr);
2546     ok(pRecord != NULL, "Installer_CreateRecord should not have returned NULL record\n");
2547     if (pRecord)
2548     {
2549         /* Record::FieldCountGet */
2550         hr = Record_FieldCountGet(pRecord, &iValue);
2551         ok(hr == S_OK, "Record_FiledCountGet failed, hresult 0x%08x\n", hr);
2552         ok(iValue == 1, "Record_FieldCountGet result was %d but expected 1\n", iValue);
2553
2554         /* Record::IntegerDataGet */
2555         hr = Record_IntegerDataGet(pRecord, 1, &iValue);
2556         ok(hr == S_OK, "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
2557         ok(iValue == MSI_NULL_INTEGER, "Record_IntegerDataGet result was %d but expected %d\n", iValue, MSI_NULL_INTEGER);
2558
2559         /* Record::IntegerDataGet, bad index */
2560         hr = Record_IntegerDataGet(pRecord, 10, &iValue);
2561         ok(hr == S_OK, "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
2562         ok(iValue == MSI_NULL_INTEGER, "Record_IntegerDataGet result was %d but expected %d\n", iValue, MSI_NULL_INTEGER);
2563
2564         /* Record::IntegerDataPut */
2565         hr = Record_IntegerDataPut(pRecord, 1, 100);
2566         ok(hr == S_OK, "Record_IntegerDataPut failed, hresult 0x%08x\n", hr);
2567
2568         /* Record::IntegerDataPut, bad index */
2569         hr = Record_IntegerDataPut(pRecord, 10, 100);
2570         ok(hr == DISP_E_EXCEPTION, "Record_IntegerDataPut failed, hresult 0x%08x\n", hr);
2571         ok_exception(hr, szIntegerDataException);
2572
2573         /* Record::IntegerDataGet */
2574         hr = Record_IntegerDataGet(pRecord, 1, &iValue);
2575         ok(hr == S_OK, "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
2576         ok(iValue == 100, "Record_IntegerDataGet result was %d but expected 100\n", iValue);
2577
2578         IDispatch_Release(pRecord);
2579     }
2580
2581     create_package(szPath);
2582
2583     /* Installer::OpenPackage */
2584     hr = Installer_OpenPackage(szPath, 0, &pSession);
2585     ok(hr == S_OK, "Installer_OpenPackage failed, hresult 0x%08x\n", hr);
2586     if (hr == S_OK)
2587     {
2588         test_Session(pSession);
2589         IDispatch_Release(pSession);
2590     }
2591
2592     /* Installer::OpenDatabase */
2593     hr = Installer_OpenDatabase(szPath, (INT_PTR)MSIDBOPEN_TRANSACT, &pDatabase);
2594     ok(hr == S_OK, "Installer_OpenDatabase failed, hresult 0x%08x\n", hr);
2595     if (hr == S_OK)
2596     {
2597         test_Database(pDatabase, FALSE);
2598         IDispatch_Release(pDatabase);
2599     }
2600
2601     /* Installer::RegistryValue */
2602     test_Installer_RegistryValue();
2603
2604     /* Installer::ProductState for our product code, which should not be installed */
2605     hr = Installer_ProductState(szProductCode, &iValue);
2606     ok(hr == S_OK, "Installer_ProductState failed, hresult 0x%08x\n", hr);
2607     ok(iValue == INSTALLSTATE_UNKNOWN, "Installer_ProductState returned %d, expected %d\n", iValue, INSTALLSTATE_UNKNOWN);
2608
2609     /* Installer::ProductInfo for our product code, which should not be installed */
2610
2611     /* Package name */
2612     memset(szPath, 0, sizeof(szPath));
2613     hr = Installer_ProductInfo(szProductCode, WINE_INSTALLPROPERTY_PACKAGENAMEW, szPath);
2614     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2615     ok_exception(hr, szProductInfoException);
2616
2617     /* NULL attribute and NULL product code */
2618     memset(szPath, 0, sizeof(szPath));
2619     hr = Installer_ProductInfo(NULL, NULL, szPath);
2620     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2621     ok_exception(hr, szProductInfoException);
2622
2623     /* Installer::Products */
2624     test_Installer_Products(FALSE);
2625
2626     /* Installer::RelatedProducts for our upgrade code, should not find anything */
2627     hr = Installer_RelatedProducts(szUpgradeCode, &pStringList);
2628     ok(hr == S_OK, "Installer_RelatedProducts failed, hresult 0x%08x\n", hr);
2629     if (hr == S_OK)
2630     {
2631         /* StringList::Count */
2632         hr = StringList_Count(pStringList, &iCount);
2633         ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr);
2634         ok(!iCount, "Expected no related products but found %d\n", iCount);
2635
2636         IDispatch_Release(pStringList);
2637     }
2638
2639     /* Installer::Version */
2640     memset(szPath, 0, sizeof(szPath));
2641     hr = Installer_VersionGet(szPath);
2642     ok(hr == S_OK, "Installer_VersionGet failed, hresult 0x%08x\n", hr);
2643
2644     /* Installer::InstallProduct and other tests that depend on our product being installed */
2645     test_Installer_InstallProduct();
2646 }
2647
2648 START_TEST(automation)
2649 {
2650     DWORD len;
2651     char temp_path[MAX_PATH], prev_path[MAX_PATH];
2652     HRESULT hr;
2653     CLSID clsid;
2654     IUnknown *pUnk;
2655
2656     GetSystemTimeAsFileTime(&systemtime);
2657
2658     GetCurrentDirectoryA(MAX_PATH, prev_path);
2659     GetTempPath(MAX_PATH, temp_path);
2660     SetCurrentDirectoryA(temp_path);
2661
2662     lstrcpyA(CURR_DIR, temp_path);
2663     len = lstrlenA(CURR_DIR);
2664
2665     if(len && (CURR_DIR[len - 1] == '\\'))
2666         CURR_DIR[len - 1] = 0;
2667
2668     get_program_files_dir(PROG_FILES_DIR);
2669
2670     hr = OleInitialize(NULL);
2671     ok (hr == S_OK, "OleInitialize returned 0x%08x\n", hr);
2672     hr = CLSIDFromProgID(szProgId, &clsid);
2673     ok (hr == S_OK, "CLSIDFromProgID returned 0x%08x\n", hr);
2674     hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&pUnk);
2675     ok(hr == S_OK, "CoCreateInstance returned 0x%08x\n", hr);
2676
2677     if (pUnk)
2678     {
2679         hr = IUnknown_QueryInterface(pUnk, &IID_IDispatch, (void **)&pInstaller);
2680         ok (hr == S_OK, "IUnknown::QueryInterface returned 0x%08x\n", hr);
2681
2682         test_dispid();
2683         test_dispatch();
2684         test_Installer();
2685
2686         IDispatch_Release(pInstaller);
2687         IUnknown_Release(pUnk);
2688     }
2689
2690     OleUninitialize();
2691
2692     SetCurrentDirectoryA(prev_path);
2693 }