d3drm: Implement D3DRMColorGetRed.
[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 static void test_Installer_Products(BOOL bProductInstalled)
1618 {
1619     WCHAR szString[MAX_PATH];
1620     HRESULT hr;
1621     int idx;
1622     IUnknown *pUnk = NULL;
1623     IEnumVARIANT *pEnum = NULL;
1624     VARIANT var;
1625     ULONG celt;
1626     int iCount, iValue;
1627     IDispatch *pStringList = NULL;
1628     BOOL bProductFound = FALSE;
1629
1630     /* Installer::Products */
1631     hr = Installer_Products(&pStringList);
1632     ok(hr == S_OK, "Installer_Products failed, hresult 0x%08x\n", hr);
1633     if (hr == S_OK)
1634     {
1635         /* StringList::_NewEnum */
1636         hr = StringList__NewEnum(pStringList, &pUnk);
1637         ok(hr == S_OK, "StringList_NewEnum failed, hresult 0x%08x\n", hr);
1638         if (hr == S_OK)
1639         {
1640             hr = IUnknown_QueryInterface(pUnk, &IID_IEnumVARIANT, (void **)&pEnum);
1641             ok (hr == S_OK, "IUnknown::QueryInterface returned 0x%08x\n", hr);
1642         }
1643         if (!pEnum)
1644             skip("IEnumVARIANT tests\n");
1645
1646         /* StringList::Count */
1647         hr = StringList_Count(pStringList, &iCount);
1648         ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr);
1649
1650         for (idx=0; idx<iCount; idx++)
1651         {
1652             /* StringList::Item */
1653             memset(szString, 0, sizeof(szString));
1654             hr = StringList_Item(pStringList, idx, szString);
1655             ok(hr == S_OK, "StringList_Item failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);
1656
1657             if (hr == S_OK)
1658             {
1659                 /* Installer::ProductState */
1660                 hr = Installer_ProductState(szString, &iValue);
1661                 ok(hr == S_OK, "Installer_ProductState failed, hresult 0x%08x\n", hr);
1662                 if (hr == S_OK)
1663                     ok(iValue == INSTALLSTATE_DEFAULT || iValue == INSTALLSTATE_ADVERTISED, "Installer_ProductState returned %d, expected %d or %d\n", iValue, INSTALLSTATE_DEFAULT, INSTALLSTATE_ADVERTISED);
1664
1665                 /* Not found our product code yet? Check */
1666                 if (!bProductFound && !lstrcmpW(szString, szProductCode))
1667                     bProductFound = TRUE;
1668
1669                 /* IEnumVARIANT::Next */
1670                 if (pEnum)
1671                 {
1672                     hr = IEnumVARIANT_Next(pEnum, 1, &var, &celt);
1673                     ok(hr == S_OK, "IEnumVARIANT_Next failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);
1674                     ok(celt == 1, "%d items were retrieved, expected 1\n", celt);
1675                     ok(V_VT(&var) == VT_BSTR, "IEnumVARIANT_Next returned variant of type %d, expected %d\n", V_VT(&var), VT_BSTR);
1676                     ok_w2("%s returned by StringList_Item does not match %s returned by IEnumVARIANT_Next\n", szString, V_BSTR(&var));
1677                     VariantClear(&var);
1678                 }
1679             }
1680         }
1681
1682         ok(bProductInstalled == bProductFound, "Product expected to %s installed but product code was %s\n",
1683            bProductInstalled ? "be" : "not be",
1684            bProductFound ? "found" : "not found");
1685
1686         if (pEnum)
1687         {
1688             IEnumVARIANT *pEnum2 = NULL;
1689
1690             if (0) /* Crashes on Windows XP */
1691             {
1692                 /* IEnumVARIANT::Clone, NULL pointer */
1693                 hr = IEnumVARIANT_Clone(pEnum, NULL);
1694             }
1695
1696             /* IEnumVARIANT::Clone */
1697             hr = IEnumVARIANT_Clone(pEnum, &pEnum2);
1698             ok(hr == S_OK, "IEnumVARIANT_Clone failed, hresult 0x%08x\n", hr);
1699             if (hr == S_OK)
1700             {
1701                 /* IEnumVARIANT::Clone is supposed to save the position, but it actually just goes back to the beginning */
1702
1703                 /* IEnumVARIANT::Next of the clone */
1704                 if (iCount)
1705                 {
1706                     hr = IEnumVARIANT_Next(pEnum2, 1, &var, &celt);
1707                     ok(hr == S_OK, "IEnumVARIANT_Next failed, hresult 0x%08x\n", hr);
1708                     ok(celt == 1, "%d items were retrieved, expected 0\n", celt);
1709                     ok(V_VT(&var) == VT_BSTR, "IEnumVARIANT_Next returned variant of type %d, expected %d\n", V_VT(&var), VT_BSTR);
1710                     VariantClear(&var);
1711                 }
1712                 else
1713                     skip("IEnumVARIANT::Next of clone will not return success with 0 products\n");
1714
1715                 IEnumVARIANT_Release(pEnum2);
1716             }
1717
1718             /* IEnumVARIANT::Skip should fail */
1719             hr = IEnumVARIANT_Skip(pEnum, 1);
1720             ok(hr == S_FALSE, "IEnumVARIANT_Skip failed, hresult 0x%08x\n", hr);
1721
1722             /* IEnumVARIANT::Next, NULL variant pointer */
1723             hr = IEnumVARIANT_Next(pEnum, 1, NULL, &celt);
1724             ok(hr == S_FALSE, "IEnumVARIANT_Next failed, hresult 0x%08x\n", hr);
1725             ok(celt == 0, "%d items were retrieved, expected 0\n", celt);
1726
1727             /* IEnumVARIANT::Next, should not return any more items */
1728             hr = IEnumVARIANT_Next(pEnum, 1, &var, &celt);
1729             ok(hr == S_FALSE, "IEnumVARIANT_Next failed, hresult 0x%08x\n", hr);
1730             ok(celt == 0, "%d items were retrieved, expected 0\n", celt);
1731             VariantClear(&var);
1732
1733             /* IEnumVARIANT::Reset */
1734             hr = IEnumVARIANT_Reset(pEnum);
1735             ok(hr == S_OK, "IEnumVARIANT_Reset failed, hresult 0x%08x\n", hr);
1736
1737             if (iCount)
1738             {
1739                 /* IEnumVARIANT::Skip to the last product */
1740                 hr = IEnumVARIANT_Skip(pEnum, iCount-1);
1741                 ok(hr == S_OK, "IEnumVARIANT_Skip failed, hresult 0x%08x\n", hr);
1742
1743                 /* IEnumVARIANT::Next should match the very last retrieved value, also makes sure it works with
1744                  * NULL celt pointer. */
1745                 hr = IEnumVARIANT_Next(pEnum, 1, &var, NULL);
1746                 ok(hr == S_OK, "IEnumVARIANT_Next failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);
1747                 ok(V_VT(&var) == VT_BSTR, "IEnumVARIANT_Next returned variant of type %d, expected %d\n", V_VT(&var), VT_BSTR);
1748                 ok_w2("%s returned by StringList_Item does not match %s returned by IEnumVARIANT_Next\n", szString, V_BSTR(&var));
1749                 VariantClear(&var);
1750             }
1751             else
1752                 skip("IEnumVARIANT::Skip impossible for 0 products\n");
1753         }
1754
1755         /* StringList::Item using an invalid index */
1756         memset(szString, 0, sizeof(szString));
1757         hr = StringList_Item(pStringList, iCount, szString);
1758         ok(hr == DISP_E_BADINDEX, "StringList_Item for an invalid index did not return DISP_E_BADINDEX, hresult 0x%08x\n", hr);
1759
1760         if (pEnum) IEnumVARIANT_Release(pEnum);
1761         if (pUnk) IUnknown_Release(pUnk);
1762         IDispatch_Release(pStringList);
1763     }
1764 }
1765
1766 /* Delete a registry subkey, including all its subkeys (RegDeleteKey does not work on keys with subkeys without
1767  * deleting the subkeys first) */
1768 static UINT delete_registry_key(HKEY hkeyParent, LPCSTR subkey)
1769 {
1770     UINT ret;
1771     CHAR *string = NULL;
1772     HKEY hkey;
1773     DWORD dwSize;
1774
1775     ret = RegOpenKey(hkeyParent, subkey, &hkey);
1776     if (ret != ERROR_SUCCESS) return ret;
1777     ret = RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL);
1778     if (ret != ERROR_SUCCESS) return ret;
1779     if (!(string = HeapAlloc(GetProcessHeap(), 0, ++dwSize))) return ERROR_NOT_ENOUGH_MEMORY;
1780
1781     while (RegEnumKeyA(hkey, 0, string, dwSize) == ERROR_SUCCESS)
1782         delete_registry_key(hkey, string);
1783
1784     RegCloseKey(hkey);
1785     HeapFree(GetProcessHeap(), 0, string);
1786     RegDeleteKeyA(hkeyParent, subkey);
1787     return ERROR_SUCCESS;
1788 }
1789
1790 /* Find a specific registry subkey at any depth within the given key and subkey and return its parent key. */
1791 static UINT find_registry_key(HKEY hkeyParent, LPCSTR subkey, LPCSTR findkey, HKEY *phkey)
1792 {
1793     UINT ret;
1794     CHAR *string = NULL;
1795     int idx = 0;
1796     HKEY hkey;
1797     DWORD dwSize;
1798     BOOL found = FALSE;
1799
1800     *phkey = 0;
1801
1802     ret = RegOpenKey(hkeyParent, subkey, &hkey);
1803     if (ret != ERROR_SUCCESS) return ret;
1804     ret = RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL);
1805     if (ret != ERROR_SUCCESS) return ret;
1806     if (!(string = HeapAlloc(GetProcessHeap(), 0, ++dwSize))) return ERROR_NOT_ENOUGH_MEMORY;
1807
1808     while (!found &&
1809            RegEnumKeyA(hkey, idx++, string, dwSize) == ERROR_SUCCESS)
1810     {
1811         if (!strcmp(string, findkey))
1812         {
1813             *phkey = hkey;
1814             found = TRUE;
1815         }
1816         else if (find_registry_key(hkey, string, findkey, phkey) == ERROR_SUCCESS) found = TRUE;
1817     }
1818
1819     if (*phkey != hkey) RegCloseKey(hkey);
1820     HeapFree(GetProcessHeap(), 0, string);
1821     return (found ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND);
1822 }
1823
1824 static void test_Installer_InstallProduct(LPCWSTR szPath)
1825 {
1826     HRESULT hr;
1827     CHAR path[MAX_PATH];
1828     WCHAR szString[MAX_PATH];
1829     LONG res;
1830     HKEY hkey;
1831     DWORD num, size, type;
1832     int iValue, iCount;
1833     IDispatch *pStringList = NULL;
1834
1835     create_test_files();
1836
1837     /* Installer::InstallProduct */
1838     hr = Installer_InstallProduct(szMsifile, NULL);
1839     ok(hr == S_OK, "Installer_InstallProduct failed, hresult 0x%08x\n", hr);
1840
1841     /* Installer::ProductState for our product code, which has been installed */
1842     hr = Installer_ProductState(szProductCode, &iValue);
1843     ok(hr == S_OK, "Installer_ProductState failed, hresult 0x%08x\n", hr);
1844     ok(iValue == INSTALLSTATE_DEFAULT, "Installer_ProductState returned %d, expected %d\n", iValue, INSTALLSTATE_DEFAULT);
1845
1846     /* Installer::ProductInfo for our product code */
1847
1848     /* NULL attribute */
1849     memset(szString, 0, sizeof(szString));
1850     hr = Installer_ProductInfo(szProductCode, NULL, szString);
1851     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
1852     ok_exception(hr, szProductInfoException);
1853
1854     /* Non-existent attribute */
1855     memset(szString, 0, sizeof(szString));
1856     hr = Installer_ProductInfo(szProductCode, szMsifile, szString);
1857     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
1858     ok_exception(hr, szProductInfoException);
1859
1860     /* Package name */
1861     memset(szString, 0, sizeof(szString));
1862     hr = Installer_ProductInfo(szProductCode, INSTALLPROPERTY_PACKAGENAMEW, szString);
1863     todo_wine ok(hr == S_OK, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
1864     todo_wine ok_w2("Installer_ProductInfo returned %s but expected %s\n", szString, szMsifile);
1865
1866     /* Product name */
1867     memset(szString, 0, sizeof(szString));
1868     hr = Installer_ProductInfo(szProductCode, INSTALLPROPERTY_PRODUCTNAMEW, szString);
1869     ok(hr == S_OK, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
1870     ok_w2("Installer_ProductInfo returned %s but expected %s\n", szString, szMSITEST);
1871
1872     /* Installer::Products */
1873     test_Installer_Products(TRUE);
1874
1875     /* Installer::RelatedProducts for our upgrade code */
1876     hr = Installer_RelatedProducts(szUpgradeCode, &pStringList);
1877     ok(hr == S_OK, "Installer_RelatedProducts failed, hresult 0x%08x\n", hr);
1878     if (hr == S_OK)
1879     {
1880         /* StringList::Count */
1881         hr = StringList_Count(pStringList, &iCount);
1882         ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr);
1883         ok(iCount == 1, "Expected one related product but found %d\n", iCount);
1884
1885         /* StringList::Item */
1886         memset(szString, 0, sizeof(szString));
1887         hr = StringList_Item(pStringList, 0, szString);
1888         ok(hr == S_OK, "StringList_Item failed (idx 0, count %d), hresult 0x%08x\n", iCount, hr);
1889         ok_w2("StringList_Item returned %s but expected %s\n", szString, szProductCode);
1890
1891         IDispatch_Release(pStringList);
1892     }
1893
1894     /* Check & clean up installed files & registry keys */
1895     ok(delete_pf("msitest\\cabout\\new\\five.txt", TRUE), "File not installed\n");
1896     ok(delete_pf("msitest\\cabout\\new", FALSE), "File not installed\n");
1897     ok(delete_pf("msitest\\cabout\\four.txt", TRUE), "File not installed\n");
1898     ok(delete_pf("msitest\\cabout", FALSE), "File not installed\n");
1899     ok(delete_pf("msitest\\changed\\three.txt", TRUE), "File not installed\n");
1900     ok(delete_pf("msitest\\changed", FALSE), "File not installed\n");
1901     ok(delete_pf("msitest\\first\\two.txt", TRUE), "File not installed\n");
1902     ok(delete_pf("msitest\\first", FALSE), "File not installed\n");
1903     ok(delete_pf("msitest\\one.txt", TRUE), "File not installed\n");
1904     ok(delete_pf("msitest\\filename", TRUE), "File not installed\n");
1905     ok(delete_pf("msitest\\service.exe", TRUE), "File not installed\n");
1906     ok(delete_pf("msitest", FALSE), "File not installed\n");
1907
1908     res = RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine\\msitest", &hkey);
1909     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1910
1911     size = MAX_PATH;
1912     type = REG_SZ;
1913     res = RegQueryValueExA(hkey, "Name", NULL, &type, (LPBYTE)path, &size);
1914     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1915     ok(!lstrcmpA(path, "imaname"), "Expected imaname, got %s\n", path);
1916
1917     size = MAX_PATH;
1918     type = REG_SZ;
1919     res = RegQueryValueExA(hkey, "blah", NULL, &type, (LPBYTE)path, &size);
1920     ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
1921
1922     size = sizeof(num);
1923     type = REG_DWORD;
1924     res = RegQueryValueExA(hkey, "number", NULL, &type, (LPBYTE)&num, &size);
1925     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1926     ok(num == 314, "Expected 314, got %d\n", num);
1927
1928     size = MAX_PATH;
1929     type = REG_SZ;
1930     res = RegQueryValueExA(hkey, "OrderTestName", NULL, &type, (LPBYTE)path, &size);
1931     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1932     ok(!lstrcmpA(path, "OrderTestValue"), "Expected imaname, got %s\n", path);
1933
1934     RegCloseKey(hkey);
1935
1936     res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine\\msitest");
1937     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1938
1939     check_service_is_installed();
1940
1941     /* Remove registry keys written by RegisterProduct standard action */
1942     res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{F1C3AF50-8B56-4A69-A00C-00773FE42F30}");
1943     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1944
1945     res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UpgradeCodes\\D8E760ECA1E276347B43E42BDBDA5656");
1946     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1947
1948     res = find_registry_key(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData", "05FA3C1F65B896A40AC00077F34EF203", &hkey);
1949     todo_wine ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1950     if (res == ERROR_SUCCESS)
1951     {
1952         res = delete_registry_key(hkey, "05FA3C1F65B896A40AC00077F34EF203");
1953         ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1954         RegCloseKey(hkey);
1955     }
1956
1957     /* Remove registry keys written by PublishProduct standard action */
1958     res = RegOpenKey(HKEY_CURRENT_USER, "SOFTWARE\\Microsoft\\Installer", &hkey);
1959     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1960
1961     res = delete_registry_key(hkey, "Products\\05FA3C1F65B896A40AC00077F34EF203");
1962     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1963
1964     res = RegDeleteKeyA(hkey, "UpgradeCodes\\D8E760ECA1E276347B43E42BDBDA5656");
1965     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1966
1967     RegCloseKey(hkey);
1968
1969     res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\Products\\05FA3C1F65B896A40AC00077F34EF203");
1970     todo_wine ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_SUCCESS, got %d\n", res);
1971
1972     /* Delete installation files we installed */
1973     delete_test_files();
1974 }
1975
1976 static void test_Installer(void)
1977 {
1978     static WCHAR szBackslash[] = { '\\',0 };
1979     static WCHAR szCreateRecordException[] = { 'C','r','e','a','t','e','R','e','c','o','r','d',',','C','o','u','n','t',0 };
1980     static WCHAR szIntegerDataException[] = { 'I','n','t','e','g','e','r','D','a','t','a',',','F','i','e','l','d',0 };
1981     WCHAR szPath[MAX_PATH];
1982     HRESULT hr;
1983     UINT len;
1984     IDispatch *pSession = NULL, *pRecord = NULL, *pStringList = NULL;
1985     int iValue, iCount;
1986
1987     if (!pInstaller) return;
1988
1989     /* Installer::CreateRecord */
1990
1991     /* Test for error */
1992     hr = Installer_CreateRecord(-1, &pRecord);
1993     ok(hr == DISP_E_EXCEPTION, "Installer_CreateRecord failed, hresult 0x%08x\n", hr);
1994     ok_exception(hr, szCreateRecordException);
1995
1996     /* Test for success */
1997     hr = Installer_CreateRecord(1, &pRecord);
1998     ok(hr == S_OK, "Installer_CreateRecord failed, hresult 0x%08x\n", hr);
1999     ok(pRecord != NULL, "Installer_CreateRecord should not have returned NULL record\n");
2000     if (pRecord)
2001     {
2002         /* Record::FieldCountGet */
2003         hr = Record_FieldCountGet(pRecord, &iValue);
2004         ok(hr == S_OK, "Record_FiledCountGet failed, hresult 0x%08x\n", hr);
2005         ok(iValue == 1, "Record_FieldCountGet result was %d but expected 1\n", iValue);
2006
2007         /* Record::IntegerDataGet */
2008         hr = Record_IntegerDataGet(pRecord, 1, &iValue);
2009         ok(hr == S_OK, "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
2010         ok(iValue == MSI_NULL_INTEGER, "Record_IntegerDataGet result was %d but expected %d\n", iValue, MSI_NULL_INTEGER);
2011
2012         /* Record::IntegerDataGet, bad index */
2013         hr = Record_IntegerDataGet(pRecord, 10, &iValue);
2014         ok(hr == S_OK, "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
2015         ok(iValue == MSI_NULL_INTEGER, "Record_IntegerDataGet result was %d but expected %d\n", iValue, MSI_NULL_INTEGER);
2016
2017         /* Record::IntegerDataPut */
2018         hr = Record_IntegerDataPut(pRecord, 1, 100);
2019         ok(hr == S_OK, "Record_IntegerDataPut failed, hresult 0x%08x\n", hr);
2020
2021         /* Record::IntegerDataPut, bad index */
2022         hr = Record_IntegerDataPut(pRecord, 10, 100);
2023         ok(hr == DISP_E_EXCEPTION, "Record_IntegerDataPut failed, hresult 0x%08x\n", hr);
2024         ok_exception(hr, szIntegerDataException);
2025
2026         /* Record::IntegerDataGet */
2027         hr = Record_IntegerDataGet(pRecord, 1, &iValue);
2028         ok(hr == S_OK, "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
2029         ok(iValue == 100, "Record_IntegerDataGet result was %d but expected 100\n", iValue);
2030
2031         IDispatch_Release(pRecord);
2032     }
2033
2034     /* Prepare package */
2035     create_database(msifile, tables, sizeof(tables) / sizeof(msi_table));
2036
2037     len = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, CURR_DIR, -1, szPath, MAX_PATH);
2038     ok(len, "MultiByteToWideChar returned error %d\n", GetLastError());
2039     if (!len) return;
2040
2041     lstrcatW(szPath, szBackslash);
2042     lstrcatW(szPath, szMsifile);
2043
2044     /* Installer::OpenPackage */
2045     hr = Installer_OpenPackage(szPath, 0, &pSession);
2046     ok(hr == S_OK, "Installer_OpenPackage failed, hresult 0x%08x\n", hr);
2047     if (hr == S_OK)
2048     {
2049         test_Session(pSession);
2050         IDispatch_Release(pSession);
2051     }
2052
2053     /* Installer::RegistryValue */
2054     test_Installer_RegistryValue();
2055
2056     /* Installer::ProductState for our product code, which should not be installed */
2057     hr = Installer_ProductState(szProductCode, &iValue);
2058     ok(hr == S_OK, "Installer_ProductState failed, hresult 0x%08x\n", hr);
2059     ok(iValue == INSTALLSTATE_UNKNOWN, "Installer_ProductState returned %d, expected %d\n", iValue, INSTALLSTATE_UNKNOWN);
2060
2061     /* Installer::ProductInfo for our product code, which should not be installed */
2062
2063     /* Package name */
2064     memset(szPath, 0, sizeof(szPath));
2065     hr = Installer_ProductInfo(szProductCode, INSTALLPROPERTY_PACKAGENAMEW, szPath);
2066     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2067     ok_exception(hr, szProductInfoException);
2068
2069     /* NULL attribute and NULL product code */
2070     memset(szPath, 0, sizeof(szPath));
2071     hr = Installer_ProductInfo(NULL, NULL, szPath);
2072     ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2073     ok_exception(hr, szProductInfoException);
2074
2075     /* Installer::Products */
2076     test_Installer_Products(FALSE);
2077
2078     /* Installer::RelatedProducts for our upgrade code, should not find anything */
2079     hr = Installer_RelatedProducts(szUpgradeCode, &pStringList);
2080     ok(hr == S_OK, "Installer_RelatedProducts failed, hresult 0x%08x\n", hr);
2081     if (hr == S_OK)
2082     {
2083         /* StringList::Count */
2084         hr = StringList_Count(pStringList, &iCount);
2085         ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr);
2086         ok(!iCount, "Expected no related products but found %d\n", iCount);
2087
2088         IDispatch_Release(pStringList);
2089     }
2090
2091     /* Installer::Version */
2092     todo_wine {
2093         memset(szPath, 0, sizeof(szPath));
2094         hr = Installer_VersionGet(szPath);
2095         ok(hr == S_OK, "Installer_VersionGet failed, hresult 0x%08x\n", hr);
2096     }
2097
2098     /* Installer::InstallProduct and other tests that depend on our product being installed */
2099     test_Installer_InstallProduct(szPath);
2100 }
2101
2102 START_TEST(automation)
2103 {
2104     DWORD len;
2105     char temp_path[MAX_PATH], prev_path[MAX_PATH];
2106     HRESULT hr;
2107     CLSID clsid;
2108     IUnknown *pUnk;
2109
2110     GetCurrentDirectoryA(MAX_PATH, prev_path);
2111     GetTempPath(MAX_PATH, temp_path);
2112     SetCurrentDirectoryA(temp_path);
2113
2114     lstrcpyA(CURR_DIR, temp_path);
2115     len = lstrlenA(CURR_DIR);
2116
2117     if(len && (CURR_DIR[len - 1] == '\\'))
2118         CURR_DIR[len - 1] = 0;
2119
2120     get_program_files_dir(PROG_FILES_DIR);
2121
2122     hr = OleInitialize(NULL);
2123     ok (hr == S_OK, "OleInitialize returned 0x%08x\n", hr);
2124     hr = CLSIDFromProgID(szProgId, &clsid);
2125     ok (hr == S_OK, "CLSIDFromProgID returned 0x%08x\n", hr);
2126     hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&pUnk);
2127     ok(hr == S_OK, "CoCreateInstance returned 0x%08x\n", hr);
2128
2129     if (pUnk)
2130     {
2131         hr = IUnknown_QueryInterface(pUnk, &IID_IDispatch, (void **)&pInstaller);
2132         ok (hr == S_OK, "IUnknown::QueryInterface returned 0x%08x\n", hr);
2133
2134         test_dispid();
2135         test_dispatch();
2136         test_Installer();
2137
2138         IDispatch_Release(pInstaller);
2139         IUnknown_Release(pUnk);
2140     }
2141
2142     OleUninitialize();
2143
2144     SetCurrentDirectoryA(prev_path);
2145 }