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