msi: automation: Implement StringList::_NewEnum.
[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 <windows.h>
27 #include <msiquery.h>
28 #include <msidefs.h>
29 #include <msi.h>
30 #include <fci.h>
31
32 #include "wine/test.h"
33
34 static const char *msifile = "winetest.msi";
35 static const WCHAR szMsifile[] = {'w','i','n','e','t','e','s','t','.','m','s','i',0};
36 static const WCHAR szMSITEST[] = { 'M','S','I','T','E','S','T',0 };
37 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 };
38 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 };
39 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 };
40 CHAR CURR_DIR[MAX_PATH];
41 EXCEPINFO excepinfo;
42
43 /*
44  * OLE automation data
45  **/
46 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 };
47 static IDispatch *pInstaller;
48
49 /* msi database data */
50
51 static const CHAR component_dat[] = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n"
52                                     "s72\tS38\ts72\ti2\tS255\tS72\n"
53                                     "Component\tComponent\n"
54                                     "Five\t{8CC92E9D-14B2-4CA4-B2AA-B11D02078087}\tNEWDIR\t2\t\tfive.txt\n"
55                                     "Four\t{FD37B4EA-7209-45C0-8917-535F35A2F080}\tCABOUTDIR\t2\t\tfour.txt\n"
56                                     "One\t{783B242E-E185-4A56-AF86-C09815EC053C}\tMSITESTDIR\t2\t\tone.txt\n"
57                                     "Three\t{010B6ADD-B27D-4EDD-9B3D-34C4F7D61684}\tCHANGEDDIR\t2\t\tthree.txt\n"
58                                     "Two\t{BF03D1A6-20DA-4A65-82F3-6CAC995915CE}\tFIRSTDIR\t2\t\ttwo.txt\n"
59                                     "dangler\t{6091DF25-EF96-45F1-B8E9-A9B1420C7A3C}\tTARGETDIR\t4\t\tregdata\n"
60                                     "component\t\tMSITESTDIR\t0\t1\tfile\n"
61                                     "service_comp\t\tMSITESTDIR\t0\t1\tservice_file";
62
63 static const CHAR directory_dat[] = "Directory\tDirectory_Parent\tDefaultDir\n"
64                                     "s72\tS72\tl255\n"
65                                     "Directory\tDirectory\n"
66                                     "CABOUTDIR\tMSITESTDIR\tcabout\n"
67                                     "CHANGEDDIR\tMSITESTDIR\tchanged:second\n"
68                                     "FIRSTDIR\tMSITESTDIR\tfirst\n"
69                                     "MSITESTDIR\tProgramFilesFolder\tmsitest\n"
70                                     "NEWDIR\tCABOUTDIR\tnew\n"
71                                     "ProgramFilesFolder\tTARGETDIR\t.\n"
72                                     "TARGETDIR\t\tSourceDir";
73
74 static const CHAR feature_dat[] = "Feature\tFeature_Parent\tTitle\tDescription\tDisplay\tLevel\tDirectory_\tAttributes\n"
75                                   "s38\tS38\tL64\tL255\tI2\ti2\tS72\ti2\n"
76                                   "Feature\tFeature\n"
77                                   "Five\t\tFive\tThe Five Feature\t5\t3\tNEWDIR\t0\n"
78                                   "Four\t\tFour\tThe Four Feature\t4\t3\tCABOUTDIR\t0\n"
79                                   "One\t\tOne\tThe One Feature\t1\t3\tMSITESTDIR\t0\n"
80                                   "Three\tOne\tThree\tThe Three Feature\t3\t3\tCHANGEDDIR\t0\n"
81                                   "Two\tOne\tTwo\tThe Two Feature\t2\t3\tFIRSTDIR\t0\n"
82                                   "feature\t\t\t\t2\t1\tTARGETDIR\t0\n"
83                                   "service_feature\t\t\t\t2\t1\tTARGETDIR\t0";
84
85 static const CHAR feature_comp_dat[] = "Feature_\tComponent_\n"
86                                        "s38\ts72\n"
87                                        "FeatureComponents\tFeature_\tComponent_\n"
88                                        "Five\tFive\n"
89                                        "Four\tFour\n"
90                                        "One\tOne\n"
91                                        "Three\tThree\n"
92                                        "Two\tTwo\n"
93                                        "feature\tcomponent\n"
94                                        "service_feature\tservice_comp\n";
95
96 static const CHAR file_dat[] = "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n"
97                                "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n"
98                                "File\tFile\n"
99                                "five.txt\tFive\tfive.txt\t1000\t\t\t0\t5\n"
100                                "four.txt\tFour\tfour.txt\t1000\t\t\t0\t4\n"
101                                "one.txt\tOne\tone.txt\t1000\t\t\t0\t1\n"
102                                "three.txt\tThree\tthree.txt\t1000\t\t\t0\t3\n"
103                                "two.txt\tTwo\ttwo.txt\t1000\t\t\t0\t2\n"
104                                "file\tcomponent\tfilename\t100\t\t\t8192\t1\n"
105                                "service_file\tservice_comp\tservice.exe\t100\t\t\t8192\t1";
106
107 static const CHAR install_exec_seq_dat[] = "Action\tCondition\tSequence\n"
108                                            "s72\tS255\tI2\n"
109                                            "InstallExecuteSequence\tAction\n"
110                                            "AllocateRegistrySpace\tNOT Installed\t1550\n"
111                                            "CostFinalize\t\t1000\n"
112                                            "CostInitialize\t\t800\n"
113                                            "FileCost\t\t900\n"
114                                            "InstallFiles\t\t4000\n"
115                                            "InstallServices\t\t5000\n"
116                                            "RegisterProduct\t\t6100\n"
117                                            "PublishProduct\t\t6400\n"
118                                            "InstallFinalize\t\t6600\n"
119                                            "InstallInitialize\t\t1500\n"
120                                            "InstallValidate\t\t1400\n"
121                                            "LaunchConditions\t\t100\n"
122                                            "WriteRegistryValues\tSourceDir And SOURCEDIR\t5000";
123
124 static const CHAR media_dat[] = "DiskId\tLastSequence\tDiskPrompt\tCabinet\tVolumeLabel\tSource\n"
125                                 "i2\ti4\tL64\tS255\tS32\tS72\n"
126                                 "Media\tDiskId\n"
127                                 "1\t5\t\t\tDISK1\t\n";
128
129 static const CHAR property_dat[] = "Property\tValue\n"
130                                    "s72\tl0\n"
131                                    "Property\tProperty\n"
132                                    "DefaultUIFont\tDlgFont8\n"
133                                    "HASUIRUN\t0\n"
134                                    "INSTALLLEVEL\t3\n"
135                                    "InstallMode\tTypical\n"
136                                    "Manufacturer\tWine\n"
137                                    "PIDTemplate\t12345<###-%%%%%%%>@@@@@\n"
138                                    "ProductCode\t{F1C3AF50-8B56-4A69-A00C-00773FE42F30}\n"
139                                    "ProductID\tnone\n"
140                                    "ProductLanguage\t1033\n"
141                                    "ProductName\tMSITEST\n"
142                                    "ProductVersion\t1.1.1\n"
143                                    "PROMPTROLLBACKCOST\tP\n"
144                                    "Setup\tSetup\n"
145                                    "UpgradeCode\t{CE067E8D-2E1A-4367-B734-4EB2BDAD6565}";
146
147 static const CHAR registry_dat[] = "Registry\tRoot\tKey\tName\tValue\tComponent_\n"
148                                    "s72\ti2\tl255\tL255\tL0\ts72\n"
149                                    "Registry\tRegistry\n"
150                                    "Apples\t2\tSOFTWARE\\Wine\\msitest\tName\timaname\tOne\n"
151                                    "Oranges\t2\tSOFTWARE\\Wine\\msitest\tnumber\t#314\tTwo\n"
152                                    "regdata\t2\tSOFTWARE\\Wine\\msitest\tblah\tbad\tdangler\n"
153                                    "OrderTest\t2\tSOFTWARE\\Wine\\msitest\tOrderTestName\tOrderTestValue\tcomponent";
154
155 static const CHAR service_install_dat[] = "ServiceInstall\tName\tDisplayName\tServiceType\tStartType\tErrorControl\t"
156                                           "LoadOrderGroup\tDependencies\tStartName\tPassword\tArguments\tComponent_\tDescription\n"
157                                           "s72\ts255\tL255\ti4\ti4\ti4\tS255\tS255\tS255\tS255\tS255\ts72\tL255\n"
158                                           "ServiceInstall\tServiceInstall\n"
159                                           "TestService\tTestService\tTestService\t2\t3\t0\t\t\tTestService\t\t\tservice_comp\t\t";
160
161 static const CHAR service_control_dat[] = "ServiceControl\tName\tEvent\tArguments\tWait\tComponent_\n"
162                                           "s72\tl255\ti2\tL255\tI2\ts72\n"
163                                           "ServiceControl\tServiceControl\n"
164                                           "ServiceControl\tTestService\t8\t\t0\tservice_comp";
165
166 typedef struct _msi_table
167 {
168     const CHAR *filename;
169     const CHAR *data;
170     int size;
171 } msi_table;
172
173 #define ADD_TABLE(x) {#x".idt", x##_dat, sizeof(x##_dat)}
174
175 static const msi_table tables[] =
176 {
177     ADD_TABLE(component),
178     ADD_TABLE(directory),
179     ADD_TABLE(feature),
180     ADD_TABLE(feature_comp),
181     ADD_TABLE(file),
182     ADD_TABLE(install_exec_seq),
183     ADD_TABLE(media),
184     ADD_TABLE(property),
185     ADD_TABLE(registry),
186     ADD_TABLE(service_install),
187     ADD_TABLE(service_control)
188 };
189
190 /*
191  * Database Helpers
192  */
193
194 static void write_file(const CHAR *filename, const char *data, int data_size)
195 {
196     DWORD size;
197
198     HANDLE hf = CreateFile(filename, GENERIC_WRITE, 0, NULL,
199                            CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
200
201     WriteFile(hf, data, data_size, &size, NULL);
202     CloseHandle(hf);
203 }
204
205 static void write_msi_summary_info(MSIHANDLE db)
206 {
207     MSIHANDLE summary;
208     UINT r;
209
210     r = MsiGetSummaryInformationA(db, NULL, 4, &summary);
211     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
212
213     r = MsiSummaryInfoSetPropertyA(summary, PID_TEMPLATE, VT_LPSTR, 0, NULL, ";1033");
214     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
215
216     r = MsiSummaryInfoSetPropertyA(summary, PID_REVNUMBER, VT_LPSTR, 0, NULL,
217                                    "{004757CA-5092-49c2-AD20-28E1CE0DF5F2}");
218     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
219
220     r = MsiSummaryInfoSetPropertyA(summary, PID_PAGECOUNT, VT_I4, 100, NULL, NULL);
221     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
222
223     r = MsiSummaryInfoSetPropertyA(summary, PID_WORDCOUNT, VT_I4, 0, NULL, NULL);
224     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
225
226     /* write the summary changes back to the stream */
227     r = MsiSummaryInfoPersist(summary);
228     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
229
230     MsiCloseHandle(summary);
231 }
232
233 static void create_database(const CHAR *name, const msi_table *tables, int num_tables)
234 {
235     MSIHANDLE db;
236     UINT r;
237     int j;
238
239     r = MsiOpenDatabaseA(name, MSIDBOPEN_CREATE, &db);
240     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
241
242     /* import the tables into the database */
243     for (j = 0; j < num_tables; j++)
244     {
245         const msi_table *table = &tables[j];
246
247         write_file(table->filename, table->data, (table->size - 1) * sizeof(char));
248
249         r = MsiDatabaseImportA(db, CURR_DIR, table->filename);
250         ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
251
252         DeleteFileA(table->filename);
253     }
254
255     write_msi_summary_info(db);
256
257     r = MsiDatabaseCommit(db);
258     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
259
260     MsiCloseHandle(db);
261 }
262
263 /*
264  * Installation helpers
265  */
266
267 static char PROG_FILES_DIR[MAX_PATH];
268
269 static BOOL get_program_files_dir(LPSTR buf)
270 {
271     HKEY hkey;
272     DWORD type = REG_EXPAND_SZ, size;
273
274     if (RegOpenKey(HKEY_LOCAL_MACHINE,
275                    "Software\\Microsoft\\Windows\\CurrentVersion", &hkey))
276         return FALSE;
277
278     size = MAX_PATH;
279     if (RegQueryValueEx(hkey, "ProgramFilesDir", 0, &type, (LPBYTE)buf, &size))
280         return FALSE;
281
282     RegCloseKey(hkey);
283     return TRUE;
284 }
285
286 static void create_file(const CHAR *name, DWORD size)
287 {
288     HANDLE file;
289     DWORD written, left;
290
291     file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
292     ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n", name);
293     WriteFile(file, name, strlen(name), &written, NULL);
294     WriteFile(file, "\n", strlen("\n"), &written, NULL);
295
296     left = size - lstrlen(name) - 1;
297
298     SetFilePointer(file, left, NULL, FILE_CURRENT);
299     SetEndOfFile(file);
300
301     CloseHandle(file);
302 }
303
304 static void create_test_files(void)
305 {
306     CreateDirectoryA("msitest", NULL);
307     create_file("msitest\\one.txt", 100);
308     CreateDirectoryA("msitest\\first", NULL);
309     create_file("msitest\\first\\two.txt", 100);
310     CreateDirectoryA("msitest\\second", NULL);
311     create_file("msitest\\second\\three.txt", 100);
312     CreateDirectoryA("msitest\\cabout",NULL);
313     create_file("msitest\\cabout\\four.txt", 100);
314     CreateDirectoryA("msitest\\cabout\\new",NULL);
315     create_file("msitest\\cabout\\new\\five.txt", 100);
316     create_file("msitest\\filename", 100);
317     create_file("msitest\\service.exe", 100);
318 }
319
320 static BOOL delete_pf(const CHAR *rel_path, BOOL is_file)
321 {
322     CHAR path[MAX_PATH];
323
324     lstrcpyA(path, PROG_FILES_DIR);
325     lstrcatA(path, "\\");
326     lstrcatA(path, rel_path);
327
328     if (is_file)
329         return DeleteFileA(path);
330     else
331         return RemoveDirectoryA(path);
332 }
333
334 static void delete_test_files(void)
335 {
336     DeleteFileA(msifile);
337     DeleteFileA("msitest\\cabout\\new\\five.txt");
338     DeleteFileA("msitest\\cabout\\four.txt");
339     DeleteFileA("msitest\\second\\three.txt");
340     DeleteFileA("msitest\\first\\two.txt");
341     DeleteFileA("msitest\\one.txt");
342     DeleteFileA("msitest\\service.exe");
343     DeleteFileA("msitest\\filename");
344     RemoveDirectoryA("msitest\\cabout\\new");
345     RemoveDirectoryA("msitest\\cabout");
346     RemoveDirectoryA("msitest\\second");
347     RemoveDirectoryA("msitest\\first");
348     RemoveDirectoryA("msitest");
349 }
350
351 static void check_service_is_installed(void)
352 {
353     SC_HANDLE scm, service;
354     BOOL res;
355
356     scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
357     ok(scm != NULL, "Failed to open the SC Manager\n");
358
359     service = OpenService(scm, "TestService", SC_MANAGER_ALL_ACCESS);
360     ok(service != NULL, "Failed to open TestService\n");
361
362     res = DeleteService(service);
363     ok(res, "Failed to delete TestService\n");
364 }
365
366 /*
367  * Automation helpers and tests
368  */
369
370 /* ok-like statement which takes two unicode strings as arguments */
371 static CHAR string1[MAX_PATH], string2[MAX_PATH];
372
373 #define ok_w2(format, szString1, szString2) \
374 \
375     if (lstrcmpW(szString1, szString2) != 0) \
376     { \
377         WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
378         WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
379         ok(0, format, string1, string2); \
380     }
381
382 /* exception checker */
383 static WCHAR szSource[] = {'M','s','i',' ','A','P','I',' ','E','r','r','o','r',0};
384
385 #define ok_exception(hr, szDescription)           \
386     if (hr == DISP_E_EXCEPTION) \
387     { \
388         /* Compare wtype, source, and destination */                    \
389         ok(excepinfo.wCode == 1000, "Exception info was %d, expected 1000\n", excepinfo.wCode); \
390 \
391         ok(excepinfo.bstrSource != NULL, "Exception source was NULL\n"); \
392         if (excepinfo.bstrSource)                                       \
393             ok_w2("Exception source was \"%s\" but expected to be \"%s\"\n", excepinfo.bstrSource, szSource); \
394 \
395         ok(excepinfo.bstrDescription != NULL, "Exception description was NULL\n"); \
396         if (excepinfo.bstrDescription) \
397             ok_w2("Exception description was \"%s\" but expected to be \"%s\"\n", excepinfo.bstrDescription, szDescription); \
398     }
399
400 static DISPID get_dispid( IDispatch *disp, const char *name )
401 {
402     LPOLESTR str;
403     UINT len;
404     DISPID id = -1;
405     HRESULT r;
406
407     len = MultiByteToWideChar(CP_ACP, 0, name, -1, NULL, 0 );
408     str = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR) );
409     if (str)
410     {
411         len = MultiByteToWideChar(CP_ACP, 0, name, -1, str, len );
412         r = IDispatch_GetIDsOfNames( disp, &IID_NULL, &str, 1, 0, &id );
413         HeapFree(GetProcessHeap(), 0, str);
414         if (r != S_OK)
415             return -1;
416     }
417
418     return id;
419 }
420
421 static void test_dispid(void)
422 {
423     ok( get_dispid( pInstaller, "CreateRecord" ) == 1, "dispid wrong\n");
424     ok( get_dispid( pInstaller, "OpenPackage" ) == 2, "dispid wrong\n");
425     todo_wine {
426     ok( get_dispid( pInstaller, "OpenProduct" ) == 3, "dispid wrong\n");
427     ok( get_dispid( pInstaller, "OpenDatabase" ) == 4, "dispid wrong\n");
428     ok( get_dispid( pInstaller, "SummaryInformation" ) == 5, "dispid wrong\n");
429     ok( get_dispid( pInstaller, "UILevel" ) == 6, "dispid wrong\n");
430     ok( get_dispid( pInstaller, "EnableLog" ) == 7, "dispid wrong\n");
431     }
432     ok( get_dispid( pInstaller, "InstallProduct" ) == 8, "dispid wrong\n");
433     todo_wine {
434     ok( get_dispid( pInstaller, "Version" ) == 9, "dispid wrong\n");
435     ok( get_dispid( pInstaller, "LastErrorRecord" ) == 10, "dispid wrong\n");
436     }
437     ok( get_dispid( pInstaller, "RegistryValue" ) == 11, "dispid wrong\n");
438     todo_wine {
439     ok( get_dispid( pInstaller, "Environment" ) == 12, "dispid wrong\n");
440     ok( get_dispid( pInstaller, "FileAttributes" ) == 13, "dispid wrong\n");
441
442     ok( get_dispid( pInstaller, "FileSize" ) == 15, "dispid wrong\n");
443     ok( get_dispid( pInstaller, "FileVersion" ) == 16, "dispid wrong\n");
444     }
445     ok( get_dispid( pInstaller, "ProductState" ) == 17, "dispid wrong\n");
446     ok( get_dispid( pInstaller, "ProductInfo" ) == 18, "dispid wrong\n");
447     todo_wine {
448     ok( get_dispid( pInstaller, "ConfigureProduct" ) == 19, "dispid wrong\n");
449     ok( get_dispid( pInstaller, "ReinstallProduct" ) == 20 , "dispid wrong\n");
450     ok( get_dispid( pInstaller, "CollectUserInfo" ) == 21, "dispid wrong\n");
451     ok( get_dispid( pInstaller, "ApplyPatch" ) == 22, "dispid wrong\n");
452     ok( get_dispid( pInstaller, "FeatureParent" ) == 23, "dispid wrong\n");
453     ok( get_dispid( pInstaller, "FeatureState" ) == 24, "dispid wrong\n");
454     ok( get_dispid( pInstaller, "UseFeature" ) == 25, "dispid wrong\n");
455     ok( get_dispid( pInstaller, "FeatureUsageCount" ) == 26, "dispid wrong\n");
456     ok( get_dispid( pInstaller, "FeatureUsageDate" ) == 27, "dispid wrong\n");
457     ok( get_dispid( pInstaller, "ConfigureFeature" ) == 28, "dispid wrong\n");
458     ok( get_dispid( pInstaller, "ReinstallFeature" ) == 29, "dispid wrong\n");
459     ok( get_dispid( pInstaller, "ProvideComponent" ) == 30, "dispid wrong\n");
460     ok( get_dispid( pInstaller, "ComponentPath" ) == 31, "dispid wrong\n");
461     ok( get_dispid( pInstaller, "ProvideQualifiedComponent" ) == 32, "dispid wrong\n");
462     ok( get_dispid( pInstaller, "QualifierDescription" ) == 33, "dispid wrong\n");
463     ok( get_dispid( pInstaller, "ComponentQualifiers" ) == 34, "dispid wrong\n");
464     }
465     ok( get_dispid( pInstaller, "Products" ) == 35, "dispid wrong\n");
466     todo_wine {
467     ok( get_dispid( pInstaller, "Features" ) == 36, "dispid wrong\n");
468     ok( get_dispid( pInstaller, "Components" ) == 37, "dispid wrong\n");
469     ok( get_dispid( pInstaller, "ComponentClients" ) == 38, "dispid wrong\n");
470     ok( get_dispid( pInstaller, "Patches" ) == 39, "dispid wrong\n");
471     }
472     ok( get_dispid( pInstaller, "RelatedProducts" ) == 40, "dispid wrong\n");
473     todo_wine {
474     ok( get_dispid( pInstaller, "PatchInfo" ) == 41, "dispid wrong\n");
475     ok( get_dispid( pInstaller, "PatchTransforms" ) == 42, "dispid wrong\n");
476     ok( get_dispid( pInstaller, "AddSource" ) == 43, "dispid wrong\n");
477     ok( get_dispid( pInstaller, "ClearSourceList" ) == 44, "dispid wrong\n");
478     ok( get_dispid( pInstaller, "ForceSourceListResolution" ) == 45, "dispid wrong\n");
479     ok( get_dispid( pInstaller, "ShortcutTarget" ) == 46, "dispid wrong\n");
480     ok( get_dispid( pInstaller, "FileHash" ) == 47, "dispid wrong\n");
481     ok( get_dispid( pInstaller, "FileSignatureInfo" ) == 48, "dispid wrong\n");
482     ok( get_dispid( pInstaller, "RemovePatches" ) == 49, "dispid wrong\n");
483
484     ok( get_dispid( pInstaller, "ApplyMultiplePatches" ) == 51, "dispid wrong\n");
485     ok( get_dispid( pInstaller, "ProductsEx" ) ==  52, "dispid wrong\n");
486
487     ok( get_dispid( pInstaller, "PatchesEx" ) == 55, "dispid wrong\n");
488
489     ok( get_dispid( pInstaller, "ExtractPatchXMLData" ) == 57, "dispid wrong\n");
490     }
491
492     /* MSDN claims the following functions exist but IDispatch->GetIDsOfNames disagrees */
493     if (0)
494     {
495         get_dispid( pInstaller, "ProductElevated" );
496         get_dispid( pInstaller, "ProductInfoFromScript" );
497         get_dispid( pInstaller, "ProvideAssembly" );
498         get_dispid( pInstaller, "CreateAdvertiseScript" );
499         get_dispid( pInstaller, "AdvertiseProduct" );
500         get_dispid( pInstaller, "PatchFiles" );
501     }
502 }
503
504 /* Test basic IDispatch functions */
505 static void test_dispatch(void)
506 {
507     static WCHAR szOpenPackage[] = { 'O','p','e','n','P','a','c','k','a','g','e',0 };
508     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};
509     static WCHAR szProductState[] = { 'P','r','o','d','u','c','t','S','t','a','t','e',0 };
510     HRESULT hr;
511     DISPID dispid;
512     OLECHAR *name;
513     VARIANT varresult;
514     VARIANTARG vararg[2];
515     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
516
517     /* Test getting ID of a function name that does not exist */
518     name = (WCHAR *)szMsifile;
519     hr = IDispatch_GetIDsOfNames(pInstaller, &IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &dispid);
520     ok(hr == DISP_E_UNKNOWNNAME, "IDispatch::GetIDsOfNames returned 0x%08x\n", hr);
521
522     /* Test invoking this function */
523     hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, NULL, NULL, NULL, NULL);
524     ok(hr == DISP_E_MEMBERNOTFOUND, "IDispatch::Invoke returned 0x%08x\n", hr);
525
526     /* Test getting ID of a function name that does exist */
527     name = (WCHAR *)szOpenPackage;
528     hr = IDispatch_GetIDsOfNames(pInstaller, &IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &dispid);
529     ok(hr == S_OK, "IDispatch::GetIDsOfNames returned 0x%08x\n", hr);
530
531     /* Test invoking this function (without parameters passed) */
532     if (0) /* All of these crash MSI on Windows XP */
533     {
534         hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, NULL, NULL, NULL, NULL);
535         hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, NULL, NULL, &excepinfo, NULL);
536         VariantInit(&varresult);
537         hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, NULL, &varresult, &excepinfo, NULL);
538     }
539
540     /* Try with NULL params */
541     hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
542     todo_wine ok(hr == DISP_E_TYPEMISMATCH, "IDispatch::Invoke returned 0x%08x\n", hr);
543
544     /* Try one empty parameter */
545     dispparams.rgvarg = vararg;
546     dispparams.cArgs = 1;
547     VariantInit(&vararg[0]);
548     hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
549     todo_wine ok(hr == DISP_E_TYPEMISMATCH, "IDispatch::Invoke returned 0x%08x\n", hr);
550
551     /* Try one parameter, function requires two */
552     VariantInit(&vararg[0]);
553     V_VT(&vararg[0]) = VT_BSTR;
554     V_BSTR(&vararg[0]) = SysAllocString(szMsifile);
555     hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
556     VariantClear(&vararg[0]);
557
558     ok(hr == DISP_E_EXCEPTION, "IDispatch::Invoke returned 0x%08x\n", hr);
559     ok_exception(hr, szOpenPackageException);
560
561     /* Test invoking a method as a DISPATCH_PROPERTYGET or DISPATCH_PROPERTYPUT */
562     VariantInit(&vararg[0]);
563     hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYGET, &dispparams, &varresult, &excepinfo, NULL);
564     ok(hr == DISP_E_MEMBERNOTFOUND, "IDispatch::Invoke returned 0x%08x\n", hr);
565
566     VariantInit(&vararg[0]);
567     hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYPUT, &dispparams, &varresult, &excepinfo, NULL);
568     ok(hr == DISP_E_MEMBERNOTFOUND, "IDispatch::Invoke returned 0x%08x\n", hr);
569
570     /* Test invoking a read-only property as DISPATCH_PROPERTYPUT or as a DISPATCH_METHOD */
571     name = (WCHAR *)szProductState;
572     hr = IDispatch_GetIDsOfNames(pInstaller, &IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &dispid);
573     ok(hr == S_OK, "IDispatch::GetIDsOfNames returned 0x%08x\n", hr);
574
575     dispparams.rgvarg = NULL;
576     dispparams.cArgs = 0;
577     hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYPUT, &dispparams, &varresult, &excepinfo, NULL);
578     ok(hr == DISP_E_MEMBERNOTFOUND, "IDispatch::Invoke returned 0x%08x\n", hr);
579
580     dispparams.rgvarg = NULL;
581     dispparams.cArgs = 0;
582     hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
583     ok(hr == DISP_E_MEMBERNOTFOUND, "IDispatch::Invoke returned 0x%08x\n", hr);
584 }
585
586 /* invocation helper function */
587 static HRESULT invoke(IDispatch *pDispatch, LPCSTR szName, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, VARTYPE vtResult)
588 {
589     OLECHAR *name = NULL;
590     DISPID dispid;
591     HRESULT hr;
592     int i;
593     UINT len;
594
595     memset(pVarResult, 0, sizeof(VARIANT));
596     VariantInit(pVarResult);
597
598     len = MultiByteToWideChar(CP_ACP, 0, szName, -1, NULL, 0 );
599     name = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR) );
600     if (!name) return E_FAIL;
601     len = MultiByteToWideChar(CP_ACP, 0, szName, -1, name, len );
602     hr = IDispatch_GetIDsOfNames(pDispatch, &IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &dispid);
603     HeapFree(GetProcessHeap(), 0, name);
604     ok(hr == S_OK, "IDispatch::GetIDsOfNames returned 0x%08x\n", hr);
605     if (!hr == S_OK) return hr;
606
607     memset(&excepinfo, 0, sizeof(excepinfo));
608     hr = IDispatch_Invoke(pDispatch, dispid, &IID_NULL, LOCALE_NEUTRAL, wFlags, pDispParams, pVarResult, &excepinfo, NULL);
609
610     if (hr == S_OK)
611     {
612         ok(V_VT(pVarResult) == vtResult, "Variant result type is %d, expected %d\n", V_VT(pVarResult), vtResult);
613         if (vtResult != VT_EMPTY)
614         {
615             hr = VariantChangeTypeEx(pVarResult, pVarResult, LOCALE_NEUTRAL, 0, vtResult);
616             ok(hr == S_OK, "VariantChangeTypeEx returned 0x%08x\n", hr);
617         }
618     }
619
620     for (i=0; i<pDispParams->cArgs; i++)
621         VariantClear(&pDispParams->rgvarg[i]);
622
623     return hr;
624 }
625
626 /* Object_Property helper functions */
627
628 static HRESULT Installer_CreateRecord(int count, IDispatch **pRecord)
629 {
630     VARIANT varresult;
631     VARIANTARG vararg[1];
632     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
633     HRESULT hr;
634
635     VariantInit(&vararg[0]);
636     V_VT(&vararg[0]) = VT_I4;
637     V_I4(&vararg[0]) = count;
638
639     hr = invoke(pInstaller, "CreateRecord", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH);
640     *pRecord = V_DISPATCH(&varresult);
641     return hr;
642 }
643
644 static HRESULT Installer_RegistryValue(HKEY hkey, LPCWSTR szKey, VARIANT vValue, VARIANT *pVarResult, VARTYPE vtExpect)
645 {
646     VARIANTARG vararg[3];
647     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
648
649     VariantInit(&vararg[2]);
650     V_VT(&vararg[2]) = VT_I4;
651     V_I4(&vararg[2]) = (int)hkey;
652     VariantInit(&vararg[1]);
653     V_VT(&vararg[1]) = VT_BSTR;
654     V_BSTR(&vararg[1]) = SysAllocString(szKey);
655     VariantInit(&vararg[0]);
656     VariantCopy(&vararg[0], &vValue);
657     VariantClear(&vValue);
658
659     return invoke(pInstaller, "RegistryValue", DISPATCH_METHOD, &dispparams, pVarResult, vtExpect);
660 }
661
662 static HRESULT Installer_RegistryValueE(HKEY hkey, LPCWSTR szKey, BOOL *pBool)
663 {
664     VARIANT varresult;
665     VARIANTARG vararg;
666     HRESULT hr;
667
668     VariantInit(&vararg);
669     V_VT(&vararg) = VT_EMPTY;
670     hr = Installer_RegistryValue(hkey, szKey, vararg, &varresult, VT_BOOL);
671     *pBool = V_BOOL(&varresult);
672     VariantClear(&varresult);
673     return hr;
674 }
675
676 static HRESULT Installer_RegistryValueW(HKEY hkey, LPCWSTR szKey, LPCWSTR szValue, LPWSTR szString)
677 {
678     VARIANT varresult;
679     VARIANTARG vararg;
680     HRESULT hr;
681
682     VariantInit(&vararg);
683     V_VT(&vararg) = VT_BSTR;
684     V_BSTR(&vararg) = SysAllocString(szValue);
685
686     hr = Installer_RegistryValue(hkey, szKey, vararg, &varresult, VT_BSTR);
687     if (V_BSTR(&varresult)) lstrcpyW(szString, V_BSTR(&varresult));
688     VariantClear(&varresult);
689     return hr;
690 }
691
692 static HRESULT Installer_RegistryValueI(HKEY hkey, LPCWSTR szKey, int iValue, LPWSTR szString, VARTYPE vtResult)
693 {
694     VARIANT varresult;
695     VARIANTARG vararg;
696     HRESULT hr;
697
698     VariantInit(&vararg);
699     V_VT(&vararg) = VT_I4;
700     V_I4(&vararg) = iValue;
701
702     hr = Installer_RegistryValue(hkey, szKey, vararg, &varresult, vtResult);
703     if (vtResult == VT_BSTR) lstrcpyW(szString, V_BSTR(&varresult));
704     VariantClear(&varresult);
705     return hr;
706 }
707
708 static HRESULT Installer_OpenPackage(LPCWSTR szPackagePath, int options, IDispatch **pSession)
709 {
710     VARIANT varresult;
711     VARIANTARG vararg[2];
712     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
713     HRESULT hr;
714
715     VariantInit(&vararg[1]);
716     V_VT(&vararg[1]) = VT_BSTR;
717     V_BSTR(&vararg[1]) = SysAllocString(szPackagePath);
718     VariantInit(&vararg[0]);
719     V_VT(&vararg[0]) = VT_I4;
720     V_I4(&vararg[0]) = options;
721
722     hr = invoke(pInstaller, "OpenPackage", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH);
723     *pSession = V_DISPATCH(&varresult);
724     return hr;
725 }
726
727 static HRESULT Installer_InstallProduct(LPCWSTR szPackagePath, LPCWSTR szPropertyValues)
728 {
729     VARIANT varresult;
730     VARIANTARG vararg[2];
731     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
732
733     VariantInit(&vararg[1]);
734     V_VT(&vararg[1]) = VT_BSTR;
735     V_BSTR(&vararg[1]) = SysAllocString(szPackagePath);
736     VariantInit(&vararg[0]);
737     V_VT(&vararg[0]) = VT_BSTR;
738     V_BSTR(&vararg[0]) = SysAllocString(szPropertyValues);
739
740     return invoke(pInstaller, "InstallProduct", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY);
741 }
742
743 static HRESULT Installer_ProductState(LPCWSTR szProduct, int *pInstallState)
744 {
745     VARIANT varresult;
746     VARIANTARG vararg[1];
747     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
748     HRESULT hr;
749
750     VariantInit(&vararg[0]);
751     V_VT(&vararg[0]) = VT_BSTR;
752     V_BSTR(&vararg[0]) = SysAllocString(szProduct);
753
754     hr = invoke(pInstaller, "ProductState", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
755     *pInstallState = V_I4(&varresult);
756     VariantClear(&varresult);
757     return hr;
758 }
759
760 static HRESULT Installer_ProductInfo(LPCWSTR szProduct, LPCWSTR szAttribute, LPWSTR szString)
761 {
762     VARIANT varresult;
763     VARIANTARG vararg[2];
764     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
765     HRESULT hr;
766
767     VariantInit(&vararg[1]);
768     V_VT(&vararg[1]) = VT_BSTR;
769     V_BSTR(&vararg[1]) = SysAllocString(szProduct);
770     VariantInit(&vararg[0]);
771     V_VT(&vararg[0]) = VT_BSTR;
772     V_BSTR(&vararg[0]) = SysAllocString(szAttribute);
773
774     hr = invoke(pInstaller, "ProductInfo", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR);
775     if (V_BSTR(&varresult)) lstrcpyW(szString, V_BSTR(&varresult));
776     VariantClear(&varresult);
777     return hr;
778 }
779
780 static HRESULT Installer_Products(IDispatch **pStringList)
781 {
782     VARIANT varresult;
783     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
784     HRESULT hr;
785
786     hr = invoke(pInstaller, "Products", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH);
787     *pStringList = V_DISPATCH(&varresult);
788     return hr;
789 }
790
791 static HRESULT Installer_RelatedProducts(LPCWSTR szProduct, IDispatch **pStringList)
792 {
793     VARIANT varresult;
794     VARIANTARG vararg[1];
795     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
796     HRESULT hr;
797
798     VariantInit(&vararg[0]);
799     V_VT(&vararg[0]) = VT_BSTR;
800     V_BSTR(&vararg[0]) = SysAllocString(szProduct);
801
802     hr = invoke(pInstaller, "RelatedProducts", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH);
803     *pStringList = V_DISPATCH(&varresult);
804     return hr;
805 }
806
807 static HRESULT Installer_VersionGet(LPWSTR szVersion)
808 {
809     VARIANT varresult;
810     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
811     HRESULT hr;
812
813     hr = invoke(pInstaller, "Version", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR);
814     if (V_BSTR(&varresult)) lstrcpyW(szVersion, V_BSTR(&varresult));
815     VariantClear(&varresult);
816     return hr;
817 }
818
819 static HRESULT Session_Installer(IDispatch *pSession, IDispatch **pInst)
820 {
821     VARIANT varresult;
822     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
823     HRESULT hr;
824
825     hr = invoke(pSession, "Installer", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH);
826     *pInst = V_DISPATCH(&varresult);
827     return hr;
828 }
829
830 static HRESULT Session_PropertyGet(IDispatch *pSession, LPCWSTR szName, LPWSTR szReturn)
831 {
832     VARIANT varresult;
833     VARIANTARG vararg[1];
834     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
835     HRESULT hr;
836
837     VariantInit(&vararg[0]);
838     V_VT(&vararg[0]) = VT_BSTR;
839     V_BSTR(&vararg[0]) = SysAllocString(szName);
840
841     hr = invoke(pSession, "Property", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR);
842     if (V_BSTR(&varresult)) lstrcpyW(szReturn, V_BSTR(&varresult));
843     VariantClear(&varresult);
844     return hr;
845 }
846
847 static HRESULT Session_PropertyPut(IDispatch *pSession, LPCWSTR szName, LPCWSTR szValue)
848 {
849     VARIANT varresult;
850     VARIANTARG vararg[2];
851     DISPID dispid = DISPID_PROPERTYPUT;
852     DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};
853
854     VariantInit(&vararg[1]);
855     V_VT(&vararg[1]) = VT_BSTR;
856     V_BSTR(&vararg[1]) = SysAllocString(szName);
857     VariantInit(&vararg[0]);
858     V_VT(&vararg[0]) = VT_BSTR;
859     V_BSTR(&vararg[0]) = SysAllocString(szValue);
860
861     return invoke(pSession, "Property", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
862 }
863
864 static HRESULT Session_LanguageGet(IDispatch *pSession, UINT *pLangId)
865 {
866     VARIANT varresult;
867     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
868     HRESULT hr;
869
870     hr = invoke(pSession, "Language", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
871     *pLangId = V_I4(&varresult);
872     VariantClear(&varresult);
873     return hr;
874 }
875
876 static HRESULT Session_ModeGet(IDispatch *pSession, int iFlag, BOOL *pMode)
877 {
878     VARIANT varresult;
879     VARIANTARG vararg[1];
880     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
881     HRESULT hr;
882
883     VariantInit(&vararg[0]);
884     V_VT(&vararg[0]) = VT_I4;
885     V_I4(&vararg[0]) = iFlag;
886
887     hr = invoke(pSession, "Mode", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BOOL);
888     *pMode = V_BOOL(&varresult);
889     VariantClear(&varresult);
890     return hr;
891 }
892
893 static HRESULT Session_ModePut(IDispatch *pSession, int iFlag, BOOL bMode)
894 {
895     VARIANT varresult;
896     VARIANTARG vararg[2];
897     DISPID dispid = DISPID_PROPERTYPUT;
898     DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};
899
900     VariantInit(&vararg[1]);
901     V_VT(&vararg[1]) = VT_I4;
902     V_I4(&vararg[1]) = iFlag;
903     VariantInit(&vararg[0]);
904     V_VT(&vararg[0]) = VT_BOOL;
905     V_BOOL(&vararg[0]) = bMode;
906
907     return invoke(pSession, "Mode", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
908 }
909
910 static HRESULT Session_Database(IDispatch *pSession, IDispatch **pDatabase)
911 {
912     VARIANT varresult;
913     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
914     HRESULT hr;
915
916     hr = invoke(pSession, "Database", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH);
917     *pDatabase = V_DISPATCH(&varresult);
918     return hr;
919 }
920
921 static HRESULT Session_DoAction(IDispatch *pSession, LPCWSTR szAction, int *iReturn)
922 {
923     VARIANT varresult;
924     VARIANTARG vararg[1];
925     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
926     HRESULT hr;
927
928     VariantInit(&vararg[0]);
929     V_VT(&vararg[0]) = VT_BSTR;
930     V_BSTR(&vararg[0]) = SysAllocString(szAction);
931
932     hr = invoke(pSession, "DoAction", DISPATCH_METHOD, &dispparams, &varresult, VT_I4);
933     *iReturn = V_I4(&varresult);
934     VariantClear(&varresult);
935     return hr;
936 }
937
938 static HRESULT Session_EvaluateCondition(IDispatch *pSession, LPCWSTR szCondition, int *iReturn)
939 {
940     VARIANT varresult;
941     VARIANTARG vararg[1];
942     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
943     HRESULT hr;
944
945     VariantInit(&vararg[0]);
946     V_VT(&vararg[0]) = VT_BSTR;
947     V_BSTR(&vararg[0]) = SysAllocString(szCondition);
948
949     hr = invoke(pSession, "EvaluateCondition", DISPATCH_METHOD, &dispparams, &varresult, VT_I4);
950     *iReturn = V_I4(&varresult);
951     VariantClear(&varresult);
952     return hr;
953 }
954
955 static HRESULT Session_SetInstallLevel(IDispatch *pSession, long iInstallLevel)
956 {
957     VARIANT varresult;
958     VARIANTARG vararg[1];
959     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
960
961     VariantInit(&vararg[0]);
962     V_VT(&vararg[0]) = VT_I4;
963     V_I4(&vararg[0]) = iInstallLevel;
964
965     return invoke(pSession, "SetInstallLevel", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY);
966 }
967
968 static HRESULT Session_FeatureCurrentState(IDispatch *pSession, LPCWSTR szName, int *pState)
969 {
970     VARIANT varresult;
971     VARIANTARG vararg[1];
972     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
973     HRESULT hr;
974
975     VariantInit(&vararg[0]);
976     V_VT(&vararg[0]) = VT_BSTR;
977     V_BSTR(&vararg[0]) = SysAllocString(szName);
978
979     hr = invoke(pSession, "FeatureCurrentState", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
980     *pState = V_I4(&varresult);
981     VariantClear(&varresult);
982     return hr;
983 }
984
985 static HRESULT Session_FeatureRequestStateGet(IDispatch *pSession, LPCWSTR szName, int *pState)
986 {
987     VARIANT varresult;
988     VARIANTARG vararg[1];
989     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
990     HRESULT hr;
991
992     VariantInit(&vararg[0]);
993     V_VT(&vararg[0]) = VT_BSTR;
994     V_BSTR(&vararg[0]) = SysAllocString(szName);
995
996     hr = invoke(pSession, "FeatureRequestState", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
997     *pState = V_I4(&varresult);
998     VariantClear(&varresult);
999     return hr;
1000 }
1001
1002 static HRESULT Session_FeatureRequestStatePut(IDispatch *pSession, LPCWSTR szName, int iState)
1003 {
1004     VARIANT varresult;
1005     VARIANTARG vararg[2];
1006     DISPID dispid = DISPID_PROPERTYPUT;
1007     DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};
1008
1009     VariantInit(&vararg[1]);
1010     V_VT(&vararg[1]) = VT_BSTR;
1011     V_BSTR(&vararg[1]) = SysAllocString(szName);
1012     VariantInit(&vararg[0]);
1013     V_VT(&vararg[0]) = VT_I4;
1014     V_I4(&vararg[0]) = iState;
1015
1016     return invoke(pSession, "FeatureRequestState", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
1017 }
1018
1019 static HRESULT Database_OpenView(IDispatch *pDatabase, LPCWSTR szSql, IDispatch **pView)
1020 {
1021     VARIANT varresult;
1022     VARIANTARG vararg[1];
1023     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1024     HRESULT hr;
1025
1026     VariantInit(&vararg[0]);
1027     V_VT(&vararg[0]) = VT_BSTR;
1028     V_BSTR(&vararg[0]) = SysAllocString(szSql);
1029
1030     hr = invoke(pDatabase, "OpenView", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH);
1031     *pView = V_DISPATCH(&varresult);
1032     return hr;
1033 }
1034
1035 static HRESULT View_Execute(IDispatch *pView, IDispatch *pRecord)
1036 {
1037     VARIANT varresult;
1038     VARIANTARG vararg[1];
1039     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1040
1041     VariantInit(&vararg[0]);
1042     V_VT(&vararg[0]) = VT_DISPATCH;
1043     V_DISPATCH(&vararg[0]) = pRecord;
1044
1045     return invoke(pView, "Execute", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY);
1046 }
1047
1048 static HRESULT View_Fetch(IDispatch *pView, IDispatch **ppRecord)
1049 {
1050     VARIANT varresult;
1051     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1052     HRESULT hr = invoke(pView, "Fetch", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH);
1053     *ppRecord = V_DISPATCH(&varresult);
1054     return hr;
1055 }
1056
1057 static HRESULT View_Modify(IDispatch *pView, int iMode, IDispatch *pRecord)
1058 {
1059     VARIANT varresult;
1060     VARIANTARG vararg[2];
1061     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1062
1063     VariantInit(&vararg[1]);
1064     V_VT(&vararg[1]) = VT_I4;
1065     V_I4(&vararg[1]) = iMode;
1066     VariantInit(&vararg[0]);
1067     V_VT(&vararg[0]) = VT_DISPATCH;
1068     V_DISPATCH(&vararg[0]) = pRecord;
1069     if (pRecord)
1070         IDispatch_AddRef(pRecord);   /* VariantClear in invoke will call IDispatch_Release */
1071
1072     return invoke(pView, "Modify", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY);
1073 }
1074
1075 static HRESULT View_Close(IDispatch *pView)
1076 {
1077     VARIANT varresult;
1078     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1079     return invoke(pView, "Close", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY);
1080 }
1081
1082 static HRESULT Record_FieldCountGet(IDispatch *pRecord, int *pFieldCount)
1083 {
1084     VARIANT varresult;
1085     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1086     HRESULT hr = invoke(pRecord, "FieldCount", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1087     *pFieldCount = V_I4(&varresult);
1088     VariantClear(&varresult);
1089     return hr;
1090 }
1091
1092 static HRESULT Record_StringDataGet(IDispatch *pRecord, int iField, LPWSTR szString)
1093 {
1094     VARIANT varresult;
1095     VARIANTARG vararg[1];
1096     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1097     HRESULT hr;
1098
1099     VariantInit(&vararg[0]);
1100     V_VT(&vararg[0]) = VT_I4;
1101     V_I4(&vararg[0]) = iField;
1102
1103     hr = invoke(pRecord, "StringData", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR);
1104     if (V_BSTR(&varresult)) lstrcpyW(szString, V_BSTR(&varresult));
1105     VariantClear(&varresult);
1106     return hr;
1107 }
1108
1109 static HRESULT Record_StringDataPut(IDispatch *pRecord, int iField, LPCWSTR szString)
1110 {
1111     VARIANT varresult;
1112     VARIANTARG vararg[2];
1113     DISPID dispid = DISPID_PROPERTYPUT;
1114     DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};
1115
1116     VariantInit(&vararg[1]);
1117     V_VT(&vararg[1]) = VT_I4;
1118     V_I4(&vararg[1]) = iField;
1119     VariantInit(&vararg[0]);
1120     V_VT(&vararg[0]) = VT_BSTR;
1121     V_BSTR(&vararg[0]) = SysAllocString(szString);
1122
1123     return invoke(pRecord, "StringData", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
1124 }
1125
1126 static HRESULT Record_IntegerDataGet(IDispatch *pRecord, int iField, int *pValue)
1127 {
1128     VARIANT varresult;
1129     VARIANTARG vararg[1];
1130     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1131     HRESULT hr;
1132
1133     VariantInit(&vararg[0]);
1134     V_VT(&vararg[0]) = VT_I4;
1135     V_I4(&vararg[0]) = iField;
1136
1137     hr = invoke(pRecord, "IntegerData", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1138     *pValue = V_I4(&varresult);
1139     VariantClear(&varresult);
1140     return hr;
1141 }
1142
1143 static HRESULT Record_IntegerDataPut(IDispatch *pRecord, int iField, int iValue)
1144 {
1145     VARIANT varresult;
1146     VARIANTARG vararg[2];
1147     DISPID dispid = DISPID_PROPERTYPUT;
1148     DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};
1149
1150     VariantInit(&vararg[1]);
1151     V_VT(&vararg[1]) = VT_I4;
1152     V_I4(&vararg[1]) = iField;
1153     VariantInit(&vararg[0]);
1154     V_VT(&vararg[0]) = VT_I4;
1155     V_I4(&vararg[0]) = iValue;
1156
1157     return invoke(pRecord, "IntegerData", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
1158 }
1159
1160 static HRESULT StringList__NewEnum(IDispatch *pList, IUnknown **ppEnumVARIANT)
1161 {
1162     VARIANT varresult;
1163     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1164     HRESULT hr = invoke(pList, "_NewEnum", DISPATCH_METHOD, &dispparams, &varresult, VT_UNKNOWN);
1165     *ppEnumVARIANT = V_UNKNOWN(&varresult);
1166     return hr;
1167 }
1168
1169 static HRESULT StringList_Item(IDispatch *pStringList, int iIndex, LPWSTR szString)
1170 {
1171     VARIANT varresult;
1172     VARIANTARG vararg[1];
1173     DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1174     HRESULT hr;
1175
1176     VariantInit(&vararg[0]);
1177     V_VT(&vararg[0]) = VT_I4;
1178     V_I4(&vararg[0]) = iIndex;
1179
1180     hr = invoke(pStringList, "Item", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR);
1181     if (V_BSTR(&varresult)) lstrcpyW(szString, V_BSTR(&varresult));
1182     VariantClear(&varresult);
1183     return hr;
1184 }
1185
1186 static HRESULT StringList_Count(IDispatch *pStringList, int *pCount)
1187 {
1188     VARIANT varresult;
1189     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1190     HRESULT hr = invoke(pStringList, "Count", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1191     *pCount = V_I4(&varresult);
1192     VariantClear(&varresult);
1193     return hr;
1194 }
1195
1196 /* Test the various objects */
1197
1198 static void test_Database(IDispatch *pDatabase)
1199 {
1200     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 };
1201     static WCHAR szThree[] = { 'T','h','r','e','e',0 };
1202     static WCHAR szTwo[] = { 'T','w','o',0 };
1203     static WCHAR szStringDataField[] = { 'S','t','r','i','n','g','D','a','t','a',',','F','i','e','l','d',0 };
1204     static WCHAR szModifyModeRecord[] = { 'M','o','d','i','f','y',',','M','o','d','e',',','R','e','c','o','r','d',0 };
1205     IDispatch *pView = NULL;
1206     HRESULT hr;
1207
1208     hr = Database_OpenView(pDatabase, szSql, &pView);
1209     ok(hr == S_OK, "Database_OpenView failed, hresult 0x%08x\n", hr);
1210     if (hr == S_OK)
1211     {
1212         IDispatch *pRecord = NULL;
1213         WCHAR szString[MAX_PATH];
1214
1215         /* View::Execute */
1216         hr = View_Execute(pView, NULL);
1217         ok(hr == S_OK, "View_Execute failed, hresult 0x%08x\n", hr);
1218
1219         /* View::Fetch */
1220         hr = View_Fetch(pView, &pRecord);
1221         ok(hr == S_OK, "View_Fetch failed, hresult 0x%08x\n", hr);
1222         ok(pRecord != NULL, "View_Fetch should not have returned NULL record\n");
1223         if (pRecord)
1224         {
1225             /* Record::StringDataGet */
1226             memset(szString, 0, sizeof(szString));
1227             hr = Record_StringDataGet(pRecord, 1, szString);
1228             ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr);
1229             ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szThree);
1230
1231             /* Record::StringDataPut with correct index */
1232             hr = Record_StringDataPut(pRecord, 1, szTwo);
1233             ok(hr == S_OK, "Record_StringDataPut failed, hresult 0x%08x\n", hr);
1234
1235             /* Record::StringDataGet */
1236             memset(szString, 0, sizeof(szString));
1237             hr = Record_StringDataGet(pRecord, 1, szString);
1238             ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr);
1239             ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szTwo);
1240
1241             /* Record::StringDataPut with incorrect index */
1242             hr = Record_StringDataPut(pRecord, -1, szString);
1243             ok(hr == DISP_E_EXCEPTION, "Record_StringDataPut failed, hresult 0x%08x\n", hr);
1244             ok_exception(hr, szStringDataField);
1245
1246             /* View::Modify with incorrect parameters */
1247             hr = View_Modify(pView, -5, NULL);
1248             ok(hr == DISP_E_EXCEPTION, "View_Modify failed, hresult 0x%08x\n", hr);
1249             ok_exception(hr, szModifyModeRecord);
1250
1251             hr = View_Modify(pView, -5, pRecord);
1252             ok(hr == DISP_E_EXCEPTION, "View_Modify failed, hresult 0x%08x\n", hr);
1253             ok_exception(hr, szModifyModeRecord);
1254
1255             hr = View_Modify(pView, MSIMODIFY_REFRESH, NULL);
1256             ok(hr == DISP_E_EXCEPTION, "View_Modify failed, hresult 0x%08x\n", hr);
1257             ok_exception(hr, szModifyModeRecord);
1258
1259             /* View::Modify with MSIMODIFY_REFRESH should undo our changes */
1260             hr = View_Modify(pView, MSIMODIFY_REFRESH, pRecord);
1261             /* Wine's MsiViewModify currently does not support MSIMODIFY_REFRESH */
1262             todo_wine ok(hr == S_OK, "View_Modify failed, hresult 0x%08x\n", hr);
1263
1264             /* Record::StringDataGet, confirm that the record is back to its unmodified value */
1265             memset(szString, 0, sizeof(szString));
1266             hr = Record_StringDataGet(pRecord, 1, szString);
1267             ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr);
1268             todo_wine ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szThree);
1269
1270             IDispatch_Release(pRecord);
1271         }
1272
1273         /* View::Fetch */
1274         hr = View_Fetch(pView, &pRecord);
1275         ok(hr == S_OK, "View_Fetch failed, hresult 0x%08x\n", hr);
1276         ok(pRecord != NULL, "View_Fetch should not have returned NULL record\n");
1277         if (pRecord)
1278         {
1279             /* Record::StringDataGet */
1280             memset(szString, 0, sizeof(szString));
1281             hr = Record_StringDataGet(pRecord, 1, szString);
1282             ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr);
1283             ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szTwo);
1284
1285             IDispatch_Release(pRecord);
1286         }
1287
1288         /* View::Fetch */
1289         hr = View_Fetch(pView, &pRecord);
1290         ok(hr == S_OK, "View_Fetch failed, hresult 0x%08x\n", hr);
1291         ok(pRecord == NULL, "View_Fetch should have returned NULL record\n");
1292         if (pRecord)
1293             IDispatch_Release(pRecord);
1294
1295         /* View::Close */
1296         hr = View_Close(pView);
1297         ok(hr == S_OK, "View_Close failed, hresult 0x%08x\n", hr);
1298
1299         IDispatch_Release(pView);
1300     }
1301 }
1302
1303 static void test_Session(IDispatch *pSession)
1304 {
1305     static WCHAR szProductName[] = { 'P','r','o','d','u','c','t','N','a','m','e',0 };
1306     static WCHAR szOne[] = { 'O','n','e',0 };
1307     static WCHAR szOneStateFalse[] = { '!','O','n','e','>','0',0 };
1308     static WCHAR szOneStateTrue[] = { '!','O','n','e','=','-','1',0 };
1309     static WCHAR szOneActionFalse[] = { '$','O','n','e','=','-','1',0 };
1310     static WCHAR szOneActionTrue[] = { '$','O','n','e','>','0',0 };
1311     static WCHAR szCostInitialize[] = { 'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0 };
1312     static WCHAR szEmpty[] = { 0 };
1313     static WCHAR szEquals[] = { '=',0 };
1314     static WCHAR szPropertyName[] = { 'P','r','o','p','e','r','t','y',',','N','a','m','e',0 };
1315     WCHAR stringw[MAX_PATH];
1316     CHAR string[MAX_PATH];
1317     UINT len;
1318     BOOL bool;
1319     int myint;
1320     IDispatch *pDatabase = NULL, *pInst = NULL;
1321     HRESULT hr;
1322
1323     /* Session::Installer */
1324     hr = Session_Installer(pSession, &pInst);
1325     ok(hr == S_OK, "Session_Installer failed, hresult 0x%08x\n", hr);
1326     ok(pInst != NULL, "Session_Installer returned NULL IDispatch pointer\n");
1327     ok(pInst == pInstaller, "Session_Installer does not match Installer instance from CoCreateInstance\n");
1328
1329     /* Session::Property, get */
1330     memset(stringw, 0, sizeof(stringw));
1331     hr = Session_PropertyGet(pSession, szProductName, stringw);
1332     ok(hr == S_OK, "Session_PropertyGet failed, hresult 0x%08x\n", hr);
1333     if (lstrcmpW(stringw, szMSITEST) != 0)
1334     {
1335         len = WideCharToMultiByte(CP_ACP, 0, stringw, -1, string, MAX_PATH, NULL, NULL);
1336         ok(len, "WideCharToMultiByteChar returned error %d\n", GetLastError());
1337         ok(0, "Property \"ProductName\" expected to be \"MSITEST\" but was \"%s\"\n", string);
1338     }
1339
1340     /* Session::Property, put */
1341     hr = Session_PropertyPut(pSession, szProductName, szProductName);
1342     ok(hr == S_OK, "Session_PropertyPut failed, hresult 0x%08x\n", hr);
1343     memset(stringw, 0, sizeof(stringw));
1344     hr = Session_PropertyGet(pSession, szProductName, stringw);
1345     ok(hr == S_OK, "Session_PropertyGet failed, hresult 0x%08x\n", hr);
1346     if (lstrcmpW(stringw, szProductName) != 0)
1347     {
1348         len = WideCharToMultiByte(CP_ACP, 0, stringw, -1, string, MAX_PATH, NULL, NULL);
1349         ok(len, "WideCharToMultiByteChar returned error %d\n", GetLastError());
1350         ok(0, "Property \"ProductName\" expected to be \"ProductName\" but was \"%s\"\n", string);
1351     }
1352
1353     /* Try putting a property using empty property identifier */
1354     hr = Session_PropertyPut(pSession, szEmpty, szProductName);
1355     ok(hr == DISP_E_EXCEPTION, "Session_PropertyPut failed, hresult 0x%08x\n", hr);
1356     ok_exception(hr, szPropertyName);
1357
1358     /* Try putting a property using illegal property identifier */
1359     hr = Session_PropertyPut(pSession, szEquals, szProductName);
1360     ok(hr == S_OK, "Session_PropertyPut failed, hresult 0x%08x\n", hr);
1361
1362     /* Session::Language, get */
1363     hr = Session_LanguageGet(pSession, &len);
1364     ok(hr == S_OK, "Session_LanguageGet failed, hresult 0x%08x\n", hr);
1365     /* Not sure how to check the language is correct */
1366
1367     /* Session::Mode, get */
1368     hr = Session_ModeGet(pSession, MSIRUNMODE_REBOOTATEND, &bool);
1369     ok(hr == S_OK, "Session_ModeGet failed, hresult 0x%08x\n", hr);
1370     todo_wine ok(!bool, "Reboot at end session mode is %d\n", bool);
1371
1372     /* Session::Mode, put */
1373     hr = Session_ModePut(pSession, MSIRUNMODE_REBOOTATEND, TRUE);
1374     todo_wine ok(hr == S_OK, "Session_ModePut failed, hresult 0x%08x\n", hr);
1375     hr = Session_ModeGet(pSession, MSIRUNMODE_REBOOTATEND, &bool);
1376     ok(hr == S_OK, "Session_ModeGet failed, hresult 0x%08x\n", hr);
1377     ok(bool, "Reboot at end session mode is %d, expected 1\n", bool);
1378     hr = Session_ModePut(pSession, MSIRUNMODE_REBOOTATEND, FALSE);  /* set it again so we don't reboot */
1379     todo_wine ok(hr == S_OK, "Session_ModePut failed, hresult 0x%08x\n", hr);
1380
1381     /* Session::Database, get */
1382     hr = Session_Database(pSession, &pDatabase);
1383     ok(hr == S_OK, "Session_Database failed, hresult 0x%08x\n", hr);
1384     if (hr == S_OK)
1385     {
1386         test_Database(pDatabase);
1387         IDispatch_Release(pDatabase);
1388     }
1389
1390     /* Session::EvaluateCondition */
1391     hr = Session_EvaluateCondition(pSession, NULL, &myint);
1392     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1393     ok(myint == MSICONDITION_NONE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1394
1395     hr = Session_EvaluateCondition(pSession, szEmpty, &myint);
1396     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1397     ok(myint == MSICONDITION_NONE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1398
1399     hr = Session_EvaluateCondition(pSession, szEquals, &myint);
1400     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1401     ok(myint == MSICONDITION_ERROR, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1402
1403     /* Session::DoAction(CostInitialize) must occur before the next statements */
1404     hr = Session_DoAction(pSession, szCostInitialize, &myint);
1405     ok(hr == S_OK, "Session_DoAction failed, hresult 0x%08x\n", hr);
1406     ok(myint == IDOK, "DoAction(CostInitialize) returned %d, %d expected\n", myint, IDOK);
1407
1408     /* Session::SetInstallLevel */
1409     hr = Session_SetInstallLevel(pSession, INSTALLLEVEL_MINIMUM);
1410     ok(hr == S_OK, "Session_SetInstallLevel failed, hresult 0x%08x\n", hr);
1411
1412     /* Session::FeatureCurrentState, get */
1413     hr = Session_FeatureCurrentState(pSession, szOne, &myint);
1414     ok(hr == S_OK, "Session_FeatureCurrentState failed, hresult 0x%08x\n", hr);
1415     ok(myint == INSTALLSTATE_UNKNOWN, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1416
1417     /* Session::EvaluateCondition */
1418     hr = Session_EvaluateCondition(pSession, szOneStateFalse, &myint);
1419     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1420     ok(myint == MSICONDITION_FALSE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1421
1422     hr = Session_EvaluateCondition(pSession, szOneStateTrue, &myint);
1423     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1424     ok(myint == MSICONDITION_TRUE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1425
1426     /* Session::FeatureRequestState, put */
1427     hr = Session_FeatureRequestStatePut(pSession, szOne, INSTALLSTATE_ADVERTISED);
1428     ok(hr == S_OK, "Session_FeatureRequestStatePut failed, hresult 0x%08x\n", hr);
1429     hr = Session_FeatureRequestStateGet(pSession, szOne, &myint);
1430     ok(hr == S_OK, "Session_FeatureRequestStateGet failed, hresult 0x%08x\n", hr);
1431     ok(myint == INSTALLSTATE_ADVERTISED, "Feature request state was %d but expected %d\n", myint, INSTALLSTATE_ADVERTISED);
1432
1433     /* Session::EvaluateCondition */
1434     hr = Session_EvaluateCondition(pSession, szOneActionFalse, &myint);
1435     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1436     ok(myint == MSICONDITION_FALSE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1437
1438     hr = Session_EvaluateCondition(pSession, szOneActionTrue, &myint);
1439     ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1440     ok(myint == MSICONDITION_TRUE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1441 }
1442
1443 /* delete key and all its subkeys */
1444 static DWORD delete_key( HKEY hkey )
1445 {
1446     char name[MAX_PATH];
1447     DWORD ret;
1448
1449     while (!(ret = RegEnumKeyA(hkey, 0, name, sizeof(name))))
1450     {
1451         HKEY tmp;
1452         if (!(ret = RegOpenKeyExA( hkey, name, 0, KEY_ENUMERATE_SUB_KEYS, &tmp )))
1453         {
1454             ret = delete_key( tmp );
1455             RegCloseKey( tmp );
1456         }
1457         if (ret) break;
1458     }
1459     if (ret != ERROR_NO_MORE_ITEMS) return ret;
1460     RegDeleteKeyA( hkey, "" );
1461     return 0;
1462 }
1463
1464 static void test_Installer_RegistryValue(void)
1465 {
1466     static const DWORD qw[2] = { 0x12345678, 0x87654321 };
1467     static const WCHAR szKey[] = { 'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\','T','e','s','t',0 };
1468     static const WCHAR szOne[] = { 'O','n','e',0 };
1469     static const WCHAR szTwo[] = { 'T','w','o',0 };
1470     static const WCHAR szThree[] = { 'T','h','r','e','e',0 };
1471     static const WCHAR szREG_BINARY[] = { '(','R','E','G','_','B','I','N','A','R','Y',')',0 };
1472     static const WCHAR szFour[] = { 'F','o','u','r',0 };
1473     static const WCHAR szExpand[] = { '%','M','S','I','T','E','S','T','%',0 };
1474     static const WCHAR szFive[] = { 'F','i','v','e',0,'H','i',0,0 };
1475     static const WCHAR szFiveHi[] = { 'F','i','v','e','\n','H','i',0 };
1476     static const WCHAR szSix[] = { 'S','i','x',0 };
1477     static const WCHAR szREG_[] = { '(','R','E','G','_',']',0 };
1478     static const WCHAR szSeven[] = { 'S','e','v','e','n',0 };
1479     static const WCHAR szEight[] = { 'E','i','g','h','t',0 };
1480     static const WCHAR szBlank[] = { 0 };
1481     VARIANT varresult;
1482     VARIANTARG vararg;
1483     WCHAR szString[MAX_PATH];
1484     HKEY hkey, hkey_sub;
1485     HRESULT hr;
1486     BOOL bRet;
1487
1488     /* Delete keys */
1489     if (!RegOpenKeyW( HKEY_CURRENT_USER, szKey, &hkey )) delete_key( hkey );
1490
1491     /* Does our key exist? Shouldn't; check with all three possible value parameter types */
1492     hr = Installer_RegistryValueE(HKEY_CURRENT_USER, szKey, &bRet);
1493     ok(hr == S_OK, "Installer_RegistryValueE failed, hresult 0x%08x\n", hr);
1494     ok(!bRet, "Registry key expected to not exist, but Installer_RegistryValue claims it does\n");
1495
1496     memset(szString, 0, sizeof(szString));
1497     hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, NULL, szString);
1498     ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1499
1500     memset(szString, 0, sizeof(szString));
1501     hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, 0, szString, VT_BSTR);
1502     ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
1503
1504     /* Create key */
1505     ok(!RegCreateKeyW( HKEY_CURRENT_USER, szKey, &hkey ), "RegCreateKeyW failed\n");
1506
1507     ok(!RegSetValueExW(hkey,szOne,0,REG_SZ, (const BYTE *)szOne, sizeof(szOne)),
1508         "RegSetValueExW failed\n");
1509     ok(!RegSetValueExW(hkey,szTwo,0,REG_DWORD, (const BYTE *)qw, 4),
1510         "RegSetValueExW failed\n");
1511     ok(!RegSetValueExW(hkey,szThree,0,REG_BINARY, (const BYTE *)qw, 4),
1512         "RegSetValueExW failed\n");
1513     ok(SetEnvironmentVariableA("MSITEST", "Four"), "SetEnvironmentVariableA failed %d\n", GetLastError());
1514     ok(!RegSetValueExW(hkey,szFour,0,REG_EXPAND_SZ, (const BYTE *)szExpand, sizeof(szExpand)),
1515         "RegSetValueExW failed\n");
1516     ok(!RegSetValueExW(hkey,szFive,0,REG_MULTI_SZ, (const BYTE *)szFive, sizeof(szFive)),
1517         "RegSetValueExW failed\n");
1518     ok(!RegSetValueExW(hkey,szSix,0,REG_QWORD, (const BYTE *)qw, 8),
1519         "RegSetValueExW failed\n");
1520     ok(!RegSetValueExW(hkey,szSeven,0,REG_NONE, (const BYTE *)NULL, 0),
1521         "RegSetValueExW failed\n");
1522
1523     ok(!RegSetValueExW(hkey,NULL,0,REG_SZ, (const BYTE *)szOne, sizeof(szOne)),
1524         "RegSetValueExW failed\n");
1525
1526     ok(!RegCreateKeyW( hkey, szEight, &hkey_sub ), "RegCreateKeyW failed\n");
1527
1528     /* Does our key exist? It should, and make sure we retrieve the correct default value */
1529     bRet = FALSE;
1530     hr = Installer_RegistryValueE(HKEY_CURRENT_USER, szKey, &bRet);
1531     ok(hr == S_OK, "Installer_RegistryValueE failed, hresult 0x%08x\n", hr);
1532     ok(bRet, "Registry key expected to exist, but Installer_RegistryValue claims it does not\n");
1533
1534     memset(szString, 0, sizeof(szString));
1535     hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, NULL, szString);
1536     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1537     ok_w2("Default registry value \"%s\" does not match expected \"%s\"\n", szString, szOne);
1538
1539     /* Ask for the value of a nonexistent key */
1540     memset(szString, 0, sizeof(szString));
1541     hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szExpand, szString);
1542     ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1543
1544     /* Get values of keys */
1545     memset(szString, 0, sizeof(szString));
1546     hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szOne, szString);
1547     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1548     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szOne);
1549
1550     VariantInit(&vararg);
1551     V_VT(&vararg) = VT_BSTR;
1552     V_BSTR(&vararg) = SysAllocString(szTwo);
1553     hr = Installer_RegistryValue(HKEY_CURRENT_USER, szKey, vararg, &varresult, VT_I4);
1554     ok(hr == S_OK, "Installer_RegistryValue failed, hresult 0x%08x\n", hr);
1555     ok(V_I4(&varresult) == 305419896, "Registry value %d does not match expected value\n", V_I4(&varresult));
1556     VariantClear(&varresult);
1557
1558     memset(szString, 0, sizeof(szString));
1559     hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szThree, szString);
1560     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1561     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szREG_BINARY);
1562
1563     memset(szString, 0, sizeof(szString));
1564     hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szFour, szString);
1565     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1566     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szFour);
1567
1568     memset(szString, 0, sizeof(szString));
1569     hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szFive, szString);
1570     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1571     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szFiveHi);
1572
1573     memset(szString, 0, sizeof(szString));
1574     hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szSix, szString);
1575     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1576     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szREG_);
1577
1578     VariantInit(&vararg);
1579     V_VT(&vararg) = VT_BSTR;
1580     V_BSTR(&vararg) = SysAllocString(szSeven);
1581     hr = Installer_RegistryValue(HKEY_CURRENT_USER, szKey, vararg, &varresult, VT_EMPTY);
1582     ok(hr == S_OK, "Installer_RegistryValue failed, hresult 0x%08x\n", hr);
1583
1584     /* Get string class name for the key */
1585     memset(szString, 0, sizeof(szString));
1586     hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, 0, szString, VT_BSTR);
1587     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
1588     ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szBlank);
1589
1590     /* Get name of a value by positive number (RegEnumValue like), valid index */
1591     memset(szString, 0, sizeof(szString));
1592     hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, 2, szString, VT_BSTR);
1593     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
1594     /* RegEnumValue order seems different on wine */
1595     todo_wine ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szTwo);
1596
1597     /* Get name of a value by positive number (RegEnumValue like), invalid index */
1598     memset(szString, 0, sizeof(szString));
1599     hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, 10, szString, VT_EMPTY);
1600     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
1601
1602     /* Get name of a subkey by negative number (RegEnumValue like), valid index */
1603     memset(szString, 0, sizeof(szString));
1604     hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, -1, szString, VT_BSTR);
1605     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
1606     ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szEight);
1607
1608     /* Get name of a subkey by negative number (RegEnumValue like), invalid index */
1609     memset(szString, 0, sizeof(szString));
1610     hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, -10, szString, VT_EMPTY);
1611     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
1612
1613     /* clean up */
1614     delete_key(hkey);
1615 }
1616
1617 /* Delete a registry subkey, including all its subkeys (RegDeleteKey does not work on keys with subkeys without
1618  * deleting the subkeys first) */
1619 static UINT delete_registry_key(HKEY hkeyParent, LPCSTR subkey)
1620 {
1621     UINT ret;
1622     CHAR *string = NULL;
1623     HKEY hkey;
1624     DWORD dwSize;
1625
1626     ret = RegOpenKey(hkeyParent, subkey, &hkey);
1627     if (ret != ERROR_SUCCESS) return ret;
1628     ret = RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL);
1629     if (ret != ERROR_SUCCESS) return ret;
1630     if (!(string = HeapAlloc(GetProcessHeap(), 0, ++dwSize))) return ERROR_NOT_ENOUGH_MEMORY;
1631
1632     while (RegEnumKeyA(hkey, 0, string, dwSize) == ERROR_SUCCESS)
1633         delete_registry_key(hkey, string);
1634
1635     RegCloseKey(hkey);
1636     HeapFree(GetProcessHeap(), 0, string);
1637     RegDeleteKeyA(hkeyParent, subkey);
1638     return ERROR_SUCCESS;
1639 }
1640
1641 /* Find a specific registry subkey at any depth within the given key and subkey and return its parent key. */
1642 static UINT find_registry_key(HKEY hkeyParent, LPCSTR subkey, LPCSTR findkey, HKEY *phkey)
1643 {
1644     UINT ret;
1645     CHAR *string = NULL;
1646     int idx = 0;
1647     HKEY hkey;
1648     DWORD dwSize;
1649     BOOL found = FALSE;
1650
1651     *phkey = 0;
1652
1653     ret = RegOpenKey(hkeyParent, subkey, &hkey);
1654     if (ret != ERROR_SUCCESS) return ret;
1655     ret = RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL);
1656     if (ret != ERROR_SUCCESS) return ret;
1657     if (!(string = HeapAlloc(GetProcessHeap(), 0, ++dwSize))) return ERROR_NOT_ENOUGH_MEMORY;
1658
1659     while (!found &&
1660            RegEnumKeyA(hkey, idx++, string, dwSize) == ERROR_SUCCESS)
1661     {
1662         if (!strcmp(string, findkey))
1663         {
1664             *phkey = hkey;
1665             found = TRUE;
1666         }
1667         else if (find_registry_key(hkey, string, findkey, phkey) == ERROR_SUCCESS) found = TRUE;
1668     }
1669
1670     if (*phkey != hkey) RegCloseKey(hkey);
1671     HeapFree(GetProcessHeap(), 0, string);
1672     return (found ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND);
1673 }
1674
1675 static void test_Installer_InstallProduct(LPCWSTR szPath)
1676 {
1677     HRESULT hr;
1678     CHAR path[MAX_PATH];
1679     WCHAR szString[MAX_PATH];
1680     LONG res;
1681     HKEY hkey;
1682     DWORD num, size, type;
1683     int iValue, iCount;
1684     IDispatch *pStringList = NULL;
1685
1686     create_test_files();
1687
1688     /* Installer::InstallProduct */
1689     hr = Installer_InstallProduct(szMsifile, NULL);
1690     ok(hr == S_OK, "Installer_InstallProduct failed, hresult 0x%08x\n", hr);
1691
1692     /* Installer::ProductState for our product code, which has been installed */
1693     hr = Installer_ProductState(szProductCode, &iValue);
1694     ok(hr == S_OK, "Installer_ProductState failed, hresult 0x%08x\n", hr);
1695     ok(iValue == INSTALLSTATE_DEFAULT, "Installer_ProductState returned %d, expected %d\n", iValue, INSTALLSTATE_DEFAULT);
1696
1697     /* Installer::ProductInfo for our product code */
1698
1699     /* NULL attribute */
1700     memset(szString, 0, sizeof(szString));
1701     hr = Installer_ProductInfo(szProductCode, NULL, szString);
1702     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
1703     ok_exception(hr, szProductInfoException);
1704
1705     /* Non-existent attribute */
1706     memset(szString, 0, sizeof(szString));
1707     hr = Installer_ProductInfo(szProductCode, szMsifile, szString);
1708     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
1709     ok_exception(hr, szProductInfoException);
1710
1711     /* Package name */
1712     memset(szString, 0, sizeof(szString));
1713     hr = Installer_ProductInfo(szProductCode, INSTALLPROPERTY_PACKAGENAMEW, szString);
1714     todo_wine ok(hr == S_OK, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
1715     todo_wine ok_w2("Installer_ProductInfo returned %s but expected %s\n", szString, szMsifile);
1716
1717     /* Product name */
1718     memset(szString, 0, sizeof(szString));
1719     hr = Installer_ProductInfo(szProductCode, INSTALLPROPERTY_PRODUCTNAMEW, szString);
1720     ok(hr == S_OK, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
1721     ok_w2("Installer_ProductInfo returned %s but expected %s\n", szString, szMSITEST);
1722
1723     /* Installer::RelatedProducts for our upgrade code */
1724     hr = Installer_RelatedProducts(szUpgradeCode, &pStringList);
1725     ok(hr == S_OK, "Installer_RelatedProducts failed, hresult 0x%08x\n", hr);
1726     if (hr == S_OK)
1727     {
1728         /* StringList::Count */
1729         hr = StringList_Count(pStringList, &iCount);
1730         ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr);
1731         ok(iCount == 1, "Expected one related product but found %d\n", iCount);
1732
1733         /* StringList::Item */
1734         memset(szString, 0, sizeof(szString));
1735         hr = StringList_Item(pStringList, 0, szString);
1736         ok(hr == S_OK, "StringList_Item failed (idx 0, count %d), hresult 0x%08x\n", iCount, hr);
1737         ok_w2("StringList_Item returned %s but expected %s\n", szString, szProductCode);
1738
1739         IDispatch_Release(pStringList);
1740     }
1741
1742     /* Check & clean up installed files & registry keys */
1743     ok(delete_pf("msitest\\cabout\\new\\five.txt", TRUE), "File not installed\n");
1744     ok(delete_pf("msitest\\cabout\\new", FALSE), "File not installed\n");
1745     ok(delete_pf("msitest\\cabout\\four.txt", TRUE), "File not installed\n");
1746     ok(delete_pf("msitest\\cabout", FALSE), "File not installed\n");
1747     ok(delete_pf("msitest\\changed\\three.txt", TRUE), "File not installed\n");
1748     ok(delete_pf("msitest\\changed", FALSE), "File not installed\n");
1749     ok(delete_pf("msitest\\first\\two.txt", TRUE), "File not installed\n");
1750     ok(delete_pf("msitest\\first", FALSE), "File not installed\n");
1751     ok(delete_pf("msitest\\one.txt", TRUE), "File not installed\n");
1752     ok(delete_pf("msitest\\filename", TRUE), "File not installed\n");
1753     ok(delete_pf("msitest\\service.exe", TRUE), "File not installed\n");
1754     ok(delete_pf("msitest", FALSE), "File not installed\n");
1755
1756     res = RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine\\msitest", &hkey);
1757     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1758
1759     size = MAX_PATH;
1760     type = REG_SZ;
1761     res = RegQueryValueExA(hkey, "Name", NULL, &type, (LPBYTE)path, &size);
1762     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1763     ok(!lstrcmpA(path, "imaname"), "Expected imaname, got %s\n", path);
1764
1765     size = MAX_PATH;
1766     type = REG_SZ;
1767     res = RegQueryValueExA(hkey, "blah", NULL, &type, (LPBYTE)path, &size);
1768     ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
1769
1770     size = sizeof(num);
1771     type = REG_DWORD;
1772     res = RegQueryValueExA(hkey, "number", NULL, &type, (LPBYTE)&num, &size);
1773     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1774     ok(num == 314, "Expected 314, got %d\n", num);
1775
1776     size = MAX_PATH;
1777     type = REG_SZ;
1778     res = RegQueryValueExA(hkey, "OrderTestName", NULL, &type, (LPBYTE)path, &size);
1779     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1780     ok(!lstrcmpA(path, "OrderTestValue"), "Expected imaname, got %s\n", path);
1781
1782     RegCloseKey(hkey);
1783
1784     res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine\\msitest");
1785     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1786
1787     check_service_is_installed();
1788
1789     /* Remove registry keys written by RegisterProduct standard action */
1790     res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{F1C3AF50-8B56-4A69-A00C-00773FE42F30}");
1791     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1792
1793     res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UpgradeCodes\\D8E760ECA1E276347B43E42BDBDA5656");
1794     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1795
1796     res = find_registry_key(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData", "05FA3C1F65B896A40AC00077F34EF203", &hkey);
1797     todo_wine ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1798     if (res == ERROR_SUCCESS)
1799     {
1800         res = delete_registry_key(hkey, "05FA3C1F65B896A40AC00077F34EF203");
1801         ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1802         RegCloseKey(hkey);
1803     }
1804
1805     /* Remove registry keys written by PublishProduct standard action */
1806     res = RegOpenKey(HKEY_CURRENT_USER, "SOFTWARE\\Microsoft\\Installer", &hkey);
1807     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1808
1809     res = delete_registry_key(hkey, "Products\\05FA3C1F65B896A40AC00077F34EF203");
1810     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1811
1812     res = RegDeleteKeyA(hkey, "UpgradeCodes\\D8E760ECA1E276347B43E42BDBDA5656");
1813     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1814
1815     RegCloseKey(hkey);
1816
1817     /* Delete installation files we installed */
1818     delete_test_files();
1819 }
1820
1821 static void test_Installer(void)
1822 {
1823     static WCHAR szBackslash[] = { '\\',0 };
1824     static WCHAR szCreateRecordException[] = { 'C','r','e','a','t','e','R','e','c','o','r','d',',','C','o','u','n','t',0 };
1825     static WCHAR szIntegerDataException[] = { 'I','n','t','e','g','e','r','D','a','t','a',',','F','i','e','l','d',0 };
1826     WCHAR szPath[MAX_PATH];
1827     HRESULT hr;
1828     UINT len;
1829     IDispatch *pSession = NULL, *pRecord = NULL, *pStringList = NULL;
1830     int iValue, iCount;
1831
1832     if (!pInstaller) return;
1833
1834     /* Installer::CreateRecord */
1835
1836     /* Test for error */
1837     hr = Installer_CreateRecord(-1, &pRecord);
1838     ok(hr == DISP_E_EXCEPTION, "Installer_CreateRecord failed, hresult 0x%08x\n", hr);
1839     ok_exception(hr, szCreateRecordException);
1840
1841     /* Test for success */
1842     hr = Installer_CreateRecord(1, &pRecord);
1843     ok(hr == S_OK, "Installer_CreateRecord failed, hresult 0x%08x\n", hr);
1844     ok(pRecord != NULL, "Installer_CreateRecord should not have returned NULL record\n");
1845     if (pRecord)
1846     {
1847         /* Record::FieldCountGet */
1848         hr = Record_FieldCountGet(pRecord, &iValue);
1849         ok(hr == S_OK, "Record_FiledCountGet failed, hresult 0x%08x\n", hr);
1850         ok(iValue == 1, "Record_FieldCountGet result was %d but expected 1\n", iValue);
1851
1852         /* Record::IntegerDataGet */
1853         hr = Record_IntegerDataGet(pRecord, 1, &iValue);
1854         ok(hr == S_OK, "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
1855         ok(iValue == MSI_NULL_INTEGER, "Record_IntegerDataGet result was %d but expected %d\n", iValue, MSI_NULL_INTEGER);
1856
1857         /* Record::IntegerDataGet, bad index */
1858         hr = Record_IntegerDataGet(pRecord, 10, &iValue);
1859         ok(hr == S_OK, "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
1860         ok(iValue == MSI_NULL_INTEGER, "Record_IntegerDataGet result was %d but expected %d\n", iValue, MSI_NULL_INTEGER);
1861
1862         /* Record::IntegerDataPut */
1863         hr = Record_IntegerDataPut(pRecord, 1, 100);
1864         ok(hr == S_OK, "Record_IntegerDataPut failed, hresult 0x%08x\n", hr);
1865
1866         /* Record::IntegerDataPut, bad index */
1867         hr = Record_IntegerDataPut(pRecord, 10, 100);
1868         ok(hr == DISP_E_EXCEPTION, "Record_IntegerDataPut failed, hresult 0x%08x\n", hr);
1869         ok_exception(hr, szIntegerDataException);
1870
1871         /* Record::IntegerDataGet */
1872         hr = Record_IntegerDataGet(pRecord, 1, &iValue);
1873         ok(hr == S_OK, "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
1874         ok(iValue == 100, "Record_IntegerDataGet result was %d but expected 100\n", iValue);
1875
1876         IDispatch_Release(pRecord);
1877     }
1878
1879     /* Prepare package */
1880     create_database(msifile, tables, sizeof(tables) / sizeof(msi_table));
1881
1882     len = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, CURR_DIR, -1, szPath, MAX_PATH);
1883     ok(len, "MultiByteToWideChar returned error %d\n", GetLastError());
1884     if (!len) return;
1885
1886     lstrcatW(szPath, szBackslash);
1887     lstrcatW(szPath, szMsifile);
1888
1889     /* Installer::OpenPackage */
1890     hr = Installer_OpenPackage(szPath, 0, &pSession);
1891     ok(hr == S_OK, "Installer_OpenPackage failed, hresult 0x%08x\n", hr);
1892     if (hr == S_OK)
1893     {
1894         test_Session(pSession);
1895         IDispatch_Release(pSession);
1896     }
1897
1898     /* Installer::RegistryValue */
1899     test_Installer_RegistryValue();
1900
1901     /* Installer::Products */
1902     hr = Installer_Products(&pStringList);
1903     ok(hr == S_OK, "Installer_Products failed, hresult 0x%08x\n", hr);
1904     if (hr == S_OK)
1905     {
1906         int idx;
1907         IUnknown *pUnk = NULL;
1908         IEnumVARIANT *pEnum = NULL;
1909         VARIANT var;
1910         ULONG celt;
1911
1912         /* StringList::_NewEnum */
1913         hr = StringList__NewEnum(pStringList, &pUnk);
1914         ok(hr == S_OK, "StringList_NewEnum failed, hresult 0x%08x\n", hr);
1915         if (hr == S_OK)
1916         {
1917             hr = IUnknown_QueryInterface(pUnk, &IID_IEnumVARIANT, (void **)&pEnum);
1918             ok (hr == S_OK, "IUnknown::QueryInterface returned 0x%08x\n", hr);
1919         }
1920         if (!pEnum)
1921             skip("IEnumVARIANT tests\n");
1922
1923         /* StringList::Count */
1924         hr = StringList_Count(pStringList, &iCount);
1925         ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr);
1926
1927         for (idx=0; idx<iCount; idx++)
1928         {
1929             /* StringList::Item */
1930             memset(szPath, 0, sizeof(szPath));
1931             hr = StringList_Item(pStringList, idx, szPath);
1932             ok(hr == S_OK, "StringList_Item failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);
1933
1934             /* Installer::ProductState */
1935             if (hr == S_OK)
1936             {
1937                 hr = Installer_ProductState(szPath, &iValue);
1938                 ok(hr == S_OK, "Installer_ProductState failed, hresult 0x%08x\n", hr);
1939                 if (hr == S_OK)
1940                     ok(iValue == INSTALLSTATE_DEFAULT || iValue == INSTALLSTATE_ADVERTISED, "Installer_ProductState returned %d, expected %d or %d\n", iValue, INSTALLSTATE_DEFAULT, INSTALLSTATE_ADVERTISED);
1941             }
1942
1943             /* IEnumVARIANT::Next */
1944             if (pEnum)
1945             {
1946                 hr = IEnumVARIANT_Next(pEnum, 1, &var, &celt);
1947                 ok(hr == S_OK, "IEnumVARIANT_Next failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);
1948                 ok(celt == 1, "%d items were retrieved, expected 1\n", celt);
1949                 ok(V_VT(&var) == VT_BSTR, "IEnumVARIANT_Next returned variant of type %d, expected %d\n", V_VT(&var), VT_BSTR);
1950                 ok_w2("%s returned by StringList_Item does not match %s returned by IEnumVARIANT_Next\n", szPath, V_BSTR(&var));
1951                 VariantClear(&var);
1952             }
1953         }
1954
1955         if (pEnum)
1956         {
1957             IEnumVARIANT *pEnum2 = NULL;
1958
1959             if (0) /* Crashes on Windows XP */
1960             {
1961                 /* IEnumVARIANT::Clone, NULL pointer */
1962                 hr = IEnumVARIANT_Clone(pEnum, NULL);
1963             }
1964
1965             /* IEnumVARIANT::Clone */
1966             hr = IEnumVARIANT_Clone(pEnum, &pEnum2);
1967             ok(hr == S_OK, "IEnumVARIANT_Clone failed, hresult 0x%08x\n", hr);
1968             if (hr == S_OK)
1969             {
1970                 /* IEnumVARIANT::Clone is supposed to save the position, but it actually just goes back to the beginning */
1971
1972                 /* IEnumVARIANT::Next of the clone */
1973                 if (iCount)
1974                 {
1975                     hr = IEnumVARIANT_Next(pEnum2, 1, &var, &celt);
1976                     ok(hr == S_OK, "IEnumVARIANT_Next failed, hresult 0x%08x\n", hr);
1977                     ok(celt == 1, "%d items were retrieved, expected 0\n", celt);
1978                     ok(V_VT(&var) == VT_BSTR, "IEnumVARIANT_Next returned variant of type %d, expected %d\n", V_VT(&var), VT_BSTR);
1979                     VariantClear(&var);
1980                 }
1981                 else
1982                     skip("IEnumVARIANT::Next of clone will not return success with 0 products\n");
1983
1984                 IEnumVARIANT_Release(pEnum2);
1985             }
1986
1987             /* IEnumVARIANT::Skip should fail */
1988             hr = IEnumVARIANT_Skip(pEnum, 1);
1989             ok(hr == S_FALSE, "IEnumVARIANT_Skip failed, hresult 0x%08x\n", hr);
1990
1991             /* IEnumVARIANT::Next, NULL variant pointer */
1992             hr = IEnumVARIANT_Next(pEnum, 1, NULL, &celt);
1993             ok(hr == S_FALSE, "IEnumVARIANT_Next failed, hresult 0x%08x\n", hr);
1994             ok(celt == 0, "%d items were retrieved, expected 0\n", celt);
1995
1996             /* IEnumVARIANT::Next, should not return any more items */
1997             hr = IEnumVARIANT_Next(pEnum, 1, &var, &celt);
1998             ok(hr == S_FALSE, "IEnumVARIANT_Next failed, hresult 0x%08x\n", hr);
1999             ok(celt == 0, "%d items were retrieved, expected 0\n", celt);
2000             VariantClear(&var);
2001
2002             /* IEnumVARIANT::Reset */
2003             hr = IEnumVARIANT_Reset(pEnum);
2004             ok(hr == S_OK, "IEnumVARIANT_Reset failed, hresult 0x%08x\n", hr);
2005
2006             if (iCount)
2007             {
2008                 /* IEnumVARIANT::Skip to the last product */
2009                 hr = IEnumVARIANT_Skip(pEnum, iCount-1);
2010                 ok(hr == S_OK, "IEnumVARIANT_Skip failed, hresult 0x%08x\n", hr);
2011
2012                 /* IEnumVARIANT::Next should match the very last retrieved value, also makes sure it works with
2013                  * NULL celt pointer. */
2014                 hr = IEnumVARIANT_Next(pEnum, 1, &var, NULL);
2015                 ok(hr == S_OK, "IEnumVARIANT_Next failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);
2016                 ok(V_VT(&var) == VT_BSTR, "IEnumVARIANT_Next returned variant of type %d, expected %d\n", V_VT(&var), VT_BSTR);
2017                 ok_w2("%s returned by StringList_Item does not match %s returned by IEnumVARIANT_Next\n", szPath, V_BSTR(&var));
2018                 VariantClear(&var);
2019             }
2020             else
2021                 skip("IEnumVARIANT::Skip impossible for 0 products\n");
2022         }
2023
2024         /* StringList::Item using an invalid index */
2025         memset(szPath, 0, sizeof(szPath));
2026         hr = StringList_Item(pStringList, iCount, szPath);
2027         ok(hr == DISP_E_BADINDEX, "StringList_Item for an invalid index did not return DISP_E_BADINDEX, hresult 0x%08x\n", hr);
2028
2029         if (pEnum) IEnumVARIANT_Release(pEnum);
2030         if (pUnk) IUnknown_Release(pUnk);
2031         IDispatch_Release(pStringList);
2032     }
2033
2034     /* Installer::ProductState for our product code, which should not be installed */
2035     hr = Installer_ProductState(szProductCode, &iValue);
2036     ok(hr == S_OK, "Installer_ProductState failed, hresult 0x%08x\n", hr);
2037     ok(iValue == INSTALLSTATE_UNKNOWN, "Installer_ProductState returned %d, expected %d\n", iValue, INSTALLSTATE_UNKNOWN);
2038
2039     /* Installer::ProductInfo for our product code, which should not be installed */
2040
2041     /* Package name */
2042     memset(szPath, 0, sizeof(szPath));
2043     hr = Installer_ProductInfo(szProductCode, INSTALLPROPERTY_PACKAGENAMEW, szPath);
2044     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2045     ok_exception(hr, szProductInfoException);
2046
2047     /* NULL attribute and NULL product code */
2048     memset(szPath, 0, sizeof(szPath));
2049     hr = Installer_ProductInfo(NULL, NULL, szPath);
2050     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2051     ok_exception(hr, szProductInfoException);
2052
2053     /* Installer::RelatedProducts for our upgrade code, should not find anything */
2054     hr = Installer_RelatedProducts(szUpgradeCode, &pStringList);
2055     ok(hr == S_OK, "Installer_RelatedProducts failed, hresult 0x%08x\n", hr);
2056     if (hr == S_OK)
2057     {
2058         /* StringList::Count */
2059         hr = StringList_Count(pStringList, &iCount);
2060         ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr);
2061         ok(!iCount, "Expected no related products but found %d\n", iCount);
2062
2063         IDispatch_Release(pStringList);
2064     }
2065
2066     /* Installer::Version */
2067     todo_wine {
2068         memset(szPath, 0, sizeof(szPath));
2069         hr = Installer_VersionGet(szPath);
2070         ok(hr == S_OK, "Installer_VersionGet failed, hresult 0x%08x\n", hr);
2071     }
2072
2073     /* Installer::InstallProduct and other tests that depend on our product being installed */
2074     test_Installer_InstallProduct(szPath);
2075 }
2076
2077 START_TEST(automation)
2078 {
2079     DWORD len;
2080     char temp_path[MAX_PATH], prev_path[MAX_PATH];
2081     HRESULT hr;
2082     CLSID clsid;
2083     IUnknown *pUnk;
2084
2085     GetCurrentDirectoryA(MAX_PATH, prev_path);
2086     GetTempPath(MAX_PATH, temp_path);
2087     SetCurrentDirectoryA(temp_path);
2088
2089     lstrcpyA(CURR_DIR, temp_path);
2090     len = lstrlenA(CURR_DIR);
2091
2092     if(len && (CURR_DIR[len - 1] == '\\'))
2093         CURR_DIR[len - 1] = 0;
2094
2095     get_program_files_dir(PROG_FILES_DIR);
2096
2097     hr = OleInitialize(NULL);
2098     ok (hr == S_OK, "OleInitialize returned 0x%08x\n", hr);
2099     hr = CLSIDFromProgID(szProgId, &clsid);
2100     ok (hr == S_OK, "CLSIDFromProgID returned 0x%08x\n", hr);
2101     hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&pUnk);
2102     ok(hr == S_OK, "CoCreateInstance returned 0x%08x\n", hr);
2103
2104     if (pUnk)
2105     {
2106         hr = IUnknown_QueryInterface(pUnk, &IID_IDispatch, (void **)&pInstaller);
2107         ok (hr == S_OK, "IUnknown::QueryInterface returned 0x%08x\n", hr);
2108
2109         test_dispid();
2110         test_dispatch();
2111         test_Installer();
2112
2113         IDispatch_Release(pInstaller);
2114         IUnknown_Release(pUnk);
2115     }
2116
2117     OleUninitialize();
2118
2119     SetCurrentDirectoryA(prev_path);
2120 }