msi: automation: Installer::Products, verify HeapAlloc return value.
[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     if (SUCCEEDED(hr))
1391         ok(myint == MSICONDITION_FALSE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1392
1393     hr = Session_EvaluateCondition(pSession, szOneStateTrue, &myint);
1394     ok(SUCCEEDED(hr), "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1395     ok(myint == MSICONDITION_TRUE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1396
1397     /* Session::FeatureRequestState, put */
1398     hr = Session_FeatureRequestStatePut(pSession, szOne, INSTALLSTATE_ADVERTISED);
1399     ok(SUCCEEDED(hr), "Session_FeatureRequestStatePut failed, hresult 0x%08x\n", hr);
1400     hr = Session_FeatureRequestStateGet(pSession, szOne, &myint);
1401     ok(SUCCEEDED(hr), "Session_FeatureRequestStateGet failed, hresult 0x%08x\n", hr);
1402     ok(myint == INSTALLSTATE_ADVERTISED, "Feature request state was %d but expected %d\n", myint, INSTALLSTATE_ADVERTISED);
1403
1404     /* Session::EvaluateCondition */
1405     hr = Session_EvaluateCondition(pSession, szOneActionFalse, &myint);
1406     ok(SUCCEEDED(hr), "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1407     if (SUCCEEDED(hr))
1408         ok(myint == MSICONDITION_FALSE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1409
1410     hr = Session_EvaluateCondition(pSession, szOneActionTrue, &myint);
1411     ok(SUCCEEDED(hr), "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1412     ok(myint == MSICONDITION_TRUE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1413 }
1414
1415 /* delete key and all its subkeys */
1416 static DWORD delete_key( HKEY hkey )
1417 {
1418     char name[MAX_PATH];
1419     DWORD ret;
1420
1421     while (!(ret = RegEnumKeyA(hkey, 0, name, sizeof(name))))
1422     {
1423         HKEY tmp;
1424         if (!(ret = RegOpenKeyExA( hkey, name, 0, KEY_ENUMERATE_SUB_KEYS, &tmp )))
1425         {
1426             ret = delete_key( tmp );
1427             RegCloseKey( tmp );
1428         }
1429         if (ret) break;
1430     }
1431     if (ret != ERROR_NO_MORE_ITEMS) return ret;
1432     RegDeleteKeyA( hkey, "" );
1433     return 0;
1434 }
1435
1436 static void test_Installer_RegistryValue(void)
1437 {
1438     static const DWORD qw[2] = { 0x12345678, 0x87654321 };
1439     static const WCHAR szKey[] = { 'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\','T','e','s','t',0 };
1440     static const WCHAR szOne[] = { 'O','n','e',0 };
1441     static const WCHAR szTwo[] = { 'T','w','o',0 };
1442     static const WCHAR szThree[] = { 'T','h','r','e','e',0 };
1443     static const WCHAR szREG_BINARY[] = { '(','R','E','G','_','B','I','N','A','R','Y',')',0 };
1444     static const WCHAR szFour[] = { 'F','o','u','r',0 };
1445     static const WCHAR szExpand[] = { '%','M','S','I','T','E','S','T','%',0 };
1446     static const WCHAR szFive[] = { 'F','i','v','e',0,'H','i',0,0 };
1447     static const WCHAR szFiveHi[] = { 'F','i','v','e','\n','H','i',0 };
1448     static const WCHAR szSix[] = { 'S','i','x',0 };
1449     static const WCHAR szREG_[] = { '(','R','E','G','_',']',0 };
1450     static const WCHAR szSeven[] = { 'S','e','v','e','n',0 };
1451     static const WCHAR szEight[] = { 'E','i','g','h','t',0 };
1452     static const WCHAR szBlank[] = { 0 };
1453     VARIANT varresult;
1454     VARIANTARG vararg;
1455     WCHAR szString[MAX_PATH];
1456     HKEY hkey, hkey_sub;
1457     HRESULT hr;
1458     BOOL bRet;
1459
1460     /* Delete keys */
1461     if (!RegOpenKeyW( HKEY_CURRENT_USER, szKey, &hkey )) delete_key( hkey );
1462
1463     /* Does our key exist? Shouldn't; check with all three possible value parameter types */
1464     hr = Installer_RegistryValueE(HKEY_CURRENT_USER, szKey, &bRet);
1465     ok(SUCCEEDED(hr), "Installer_RegistryValueE failed, hresult 0x%08x\n", hr);
1466     if (SUCCEEDED(hr))
1467         ok(!bRet, "Registry key expected to not exist, but Installer_RegistryValue claims it does\n");
1468
1469     memset(szString, 0, sizeof(szString));
1470     hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, NULL, szString);
1471     ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1472
1473     memset(szString, 0, sizeof(szString));
1474     hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, 0, szString, VT_BSTR);
1475     ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
1476
1477     /* Create key */
1478     ok(!RegCreateKeyW( HKEY_CURRENT_USER, szKey, &hkey ), "RegCreateKeyW failed\n");
1479
1480     ok(!RegSetValueExW(hkey,szOne,0,REG_SZ, (const BYTE *)szOne, sizeof(szOne)),
1481         "RegSetValueExW failed\n");
1482     ok(!RegSetValueExW(hkey,szTwo,0,REG_DWORD, (const BYTE *)qw, 4),
1483         "RegSetValueExW failed\n");
1484     ok(!RegSetValueExW(hkey,szThree,0,REG_BINARY, (const BYTE *)qw, 4),
1485         "RegSetValueExW failed\n");
1486     ok(SetEnvironmentVariableA("MSITEST", "Four"), "SetEnvironmentVariableA failed %d\n", GetLastError());
1487     ok(!RegSetValueExW(hkey,szFour,0,REG_EXPAND_SZ, (const BYTE *)szExpand, sizeof(szExpand)),
1488         "RegSetValueExW failed\n");
1489     ok(!RegSetValueExW(hkey,szFive,0,REG_MULTI_SZ, (const BYTE *)szFive, sizeof(szFive)),
1490         "RegSetValueExW failed\n");
1491     ok(!RegSetValueExW(hkey,szSix,0,REG_QWORD, (const BYTE *)qw, 8),
1492         "RegSetValueExW failed\n");
1493     ok(!RegSetValueExW(hkey,szSeven,0,REG_NONE, (const BYTE *)NULL, 0),
1494         "RegSetValueExW failed\n");
1495
1496     ok(!RegSetValueExW(hkey,NULL,0,REG_SZ, (const BYTE *)szOne, sizeof(szOne)),
1497         "RegSetValueExW failed\n");
1498
1499     ok(!RegCreateKeyW( hkey, szEight, &hkey_sub ), "RegCreateKeyW failed\n");
1500
1501     /* Does our key exist? It should, and make sure we retrieve the correct default value */
1502     bRet = FALSE;
1503     hr = Installer_RegistryValueE(HKEY_CURRENT_USER, szKey, &bRet);
1504     ok(SUCCEEDED(hr), "Installer_RegistryValueE failed, hresult 0x%08x\n", hr);
1505     if (SUCCEEDED(hr))
1506         ok(bRet, "Registry key expected to exist, but Installer_RegistryValue claims it does not\n");
1507
1508     memset(szString, 0, sizeof(szString));
1509     hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, NULL, szString);
1510     ok(SUCCEEDED(hr), "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1511     ok_w2("Default registry value \"%s\" does not match expected \"%s\"\n", szString, szOne);
1512
1513     /* Ask for the value of a non-existent key */
1514     memset(szString, 0, sizeof(szString));
1515     hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szExpand, szString);
1516     ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1517
1518     /* Get values of keys */
1519     memset(szString, 0, sizeof(szString));
1520     hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szOne, szString);
1521     ok(SUCCEEDED(hr), "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1522     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szOne);
1523
1524     VariantInit(&vararg);
1525     V_VT(&vararg) = VT_BSTR;
1526     V_BSTR(&vararg) = SysAllocString(szTwo);
1527     hr = Installer_RegistryValue(HKEY_CURRENT_USER, szKey, vararg, &varresult, VT_I4);
1528     ok(SUCCEEDED(hr), "Installer_RegistryValue failed, hresult 0x%08x\n", hr);
1529     ok(V_I4(&varresult) == 305419896, "Registry value %d does not match expected value\n", V_I4(&varresult));
1530     VariantClear(&varresult);
1531
1532     memset(szString, 0, sizeof(szString));
1533     hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szThree, szString);
1534     ok(SUCCEEDED(hr), "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1535     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szREG_BINARY);
1536
1537     memset(szString, 0, sizeof(szString));
1538     hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szFour, szString);
1539     ok(SUCCEEDED(hr), "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1540     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szFour);
1541
1542     memset(szString, 0, sizeof(szString));
1543     hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szFive, szString);
1544     ok(SUCCEEDED(hr), "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1545     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szFiveHi);
1546
1547     memset(szString, 0, sizeof(szString));
1548     hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szSix, szString);
1549     ok(SUCCEEDED(hr), "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
1550     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szREG_);
1551
1552     VariantInit(&vararg);
1553     V_VT(&vararg) = VT_BSTR;
1554     V_BSTR(&vararg) = SysAllocString(szSeven);
1555     hr = Installer_RegistryValue(HKEY_CURRENT_USER, szKey, vararg, &varresult, VT_EMPTY);
1556     ok(SUCCEEDED(hr), "Installer_RegistryValue failed, hresult 0x%08x\n", hr);
1557
1558     /* Get string class name for the key */
1559     memset(szString, 0, sizeof(szString));
1560     hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, 0, szString, VT_BSTR);
1561     ok(SUCCEEDED(hr), "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
1562     ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szBlank);
1563
1564     /* Get name of a value by positive number (RegEnumValue like), valid index */
1565     memset(szString, 0, sizeof(szString));
1566     hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, 2, szString, VT_BSTR);
1567     ok(SUCCEEDED(hr), "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
1568     /* RegEnumValue order seems different on wine */
1569     todo_wine ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szTwo);
1570
1571     /* Get name of a value by positive number (RegEnumValue like), invalid index */
1572     memset(szString, 0, sizeof(szString));
1573     hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, 10, szString, VT_EMPTY);
1574     ok(SUCCEEDED(hr), "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
1575
1576     /* Get name of a subkey by negative number (RegEnumValue like), valid index */
1577     memset(szString, 0, sizeof(szString));
1578     hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, -1, szString, VT_BSTR);
1579     ok(SUCCEEDED(hr), "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
1580     ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szEight);
1581
1582     /* Get name of a subkey by negative number (RegEnumValue like), invalid index */
1583     memset(szString, 0, sizeof(szString));
1584     hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, -10, szString, VT_EMPTY);
1585     ok(SUCCEEDED(hr), "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
1586
1587     /* clean up */
1588     delete_key(hkey);
1589 }
1590
1591 /* Delete a registry subkey, including all its subkeys (RegDeleteKey does not work on keys with subkeys without
1592  * deleting the subkeys first) */
1593 static UINT delete_registry_key(HKEY hkeyParent, LPCSTR subkey)
1594 {
1595     UINT ret;
1596     CHAR *string = NULL;
1597     HKEY hkey;
1598     DWORD dwSize;
1599
1600     ret = RegOpenKey(hkeyParent, subkey, &hkey);
1601     if (ret != ERROR_SUCCESS) return ret;
1602     ret = RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL);
1603     if (ret != ERROR_SUCCESS) return ret;
1604     if (!(string = HeapAlloc(GetProcessHeap(), 0, ++dwSize))) return ERROR_NOT_ENOUGH_MEMORY;
1605
1606     while (RegEnumKeyA(hkey, 0, string, dwSize) == ERROR_SUCCESS)
1607         delete_registry_key(hkey, string);
1608
1609     RegCloseKey(hkey);
1610     HeapFree(GetProcessHeap(), 0, string);
1611     RegDeleteKeyA(hkeyParent, subkey);
1612     return ERROR_SUCCESS;
1613 }
1614
1615 /* Find a specific registry subkey at any depth within the given key and subkey and return its parent key. */
1616 static UINT find_registry_key(HKEY hkeyParent, LPCSTR subkey, LPCSTR findkey, HKEY *phkey)
1617 {
1618     UINT ret;
1619     CHAR *string = NULL;
1620     int idx = 0;
1621     HKEY hkey;
1622     DWORD dwSize;
1623     BOOL found = FALSE;
1624
1625     *phkey = 0;
1626
1627     ret = RegOpenKey(hkeyParent, subkey, &hkey);
1628     if (ret != ERROR_SUCCESS) return ret;
1629     ret = RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL);
1630     if (ret != ERROR_SUCCESS) return ret;
1631     if (!(string = HeapAlloc(GetProcessHeap(), 0, ++dwSize))) return ERROR_NOT_ENOUGH_MEMORY;
1632
1633     while (!found &&
1634            RegEnumKeyA(hkey, idx++, string, dwSize) == ERROR_SUCCESS)
1635     {
1636         if (!strcmp(string, findkey))
1637         {
1638             *phkey = hkey;
1639             found = TRUE;
1640         }
1641         else if (find_registry_key(hkey, string, findkey, phkey) == ERROR_SUCCESS) found = TRUE;
1642     }
1643
1644     if (*phkey != hkey) RegCloseKey(hkey);
1645     HeapFree(GetProcessHeap(), 0, string);
1646     return (found ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND);
1647 }
1648
1649 static void test_Installer_InstallProduct(LPCWSTR szPath)
1650 {
1651     HRESULT hr;
1652     CHAR path[MAX_PATH];
1653     WCHAR szString[MAX_PATH];
1654     LONG res;
1655     HKEY hkey;
1656     DWORD num, size, type;
1657     int iValue, iCount;
1658     IDispatch *pStringList = NULL;
1659
1660     create_test_files();
1661
1662     /* Installer::InstallProduct */
1663     hr = Installer_InstallProduct(szMsifile, NULL);
1664     ok(SUCCEEDED(hr), "Installer_InstallProduct failed, hresult 0x%08x\n", hr);
1665
1666     /* Installer::ProductState for our product code, which has been installed */
1667     hr = Installer_ProductState(szProductCode, &iValue);
1668     ok(SUCCEEDED(hr), "Installer_ProductState failed, hresult 0x%08x\n", hr);
1669     ok(iValue == INSTALLSTATE_DEFAULT, "Installer_ProductState returned %d, expected %d\n", iValue, INSTALLSTATE_DEFAULT);
1670
1671     /* Installer::RelatedProducts for our upgrade code */
1672     hr = Installer_RelatedProducts(szUpgradeCode, &pStringList);
1673     ok(SUCCEEDED(hr), "Installer_RelatedProducts failed, hresult 0x%08x\n", hr);
1674     if (SUCCEEDED(hr))
1675     {
1676         /* StringList::Count */
1677         hr = StringList_Count(pStringList, &iCount);
1678         ok(SUCCEEDED(hr), "StringList_Count failed, hresult 0x%08x\n", hr);
1679         ok(iCount == 1, "Expected one related product but found %d\n", iCount);
1680
1681         /* StringList::Item */
1682         memset(szString, 0, sizeof(szString));
1683         hr = StringList_Item(pStringList, 0, szString);
1684         ok(SUCCEEDED(hr), "StringList_Item failed (idx 0, count %d), hresult 0x%08x\n", iCount, hr);
1685         ok_w2("StringList_Item returned %s but expected %s\n", szString, szProductCode);
1686
1687         IDispatch_Release(pStringList);
1688     }
1689
1690     /* Check & clean up installed files & registry keys */
1691     ok(delete_pf("msitest\\cabout\\new\\five.txt", TRUE), "File not installed\n");
1692     ok(delete_pf("msitest\\cabout\\new", FALSE), "File not installed\n");
1693     ok(delete_pf("msitest\\cabout\\four.txt", TRUE), "File not installed\n");
1694     ok(delete_pf("msitest\\cabout", FALSE), "File not installed\n");
1695     ok(delete_pf("msitest\\changed\\three.txt", TRUE), "File not installed\n");
1696     ok(delete_pf("msitest\\changed", FALSE), "File not installed\n");
1697     ok(delete_pf("msitest\\first\\two.txt", TRUE), "File not installed\n");
1698     ok(delete_pf("msitest\\first", FALSE), "File not installed\n");
1699     ok(delete_pf("msitest\\one.txt", TRUE), "File not installed\n");
1700     ok(delete_pf("msitest\\filename", TRUE), "File not installed\n");
1701     ok(delete_pf("msitest\\service.exe", TRUE), "File not installed\n");
1702     ok(delete_pf("msitest", FALSE), "File not installed\n");
1703
1704     res = RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine\\msitest", &hkey);
1705     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1706
1707     size = MAX_PATH;
1708     type = REG_SZ;
1709     res = RegQueryValueExA(hkey, "Name", NULL, &type, (LPBYTE)path, &size);
1710     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1711     ok(!lstrcmpA(path, "imaname"), "Expected imaname, got %s\n", path);
1712
1713     size = MAX_PATH;
1714     type = REG_SZ;
1715     res = RegQueryValueExA(hkey, "blah", NULL, &type, (LPBYTE)path, &size);
1716     ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
1717
1718     size = sizeof(num);
1719     type = REG_DWORD;
1720     res = RegQueryValueExA(hkey, "number", NULL, &type, (LPBYTE)&num, &size);
1721     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1722     ok(num == 314, "Expected 314, got %d\n", num);
1723
1724     size = MAX_PATH;
1725     type = REG_SZ;
1726     res = RegQueryValueExA(hkey, "OrderTestName", NULL, &type, (LPBYTE)path, &size);
1727     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1728     ok(!lstrcmpA(path, "OrderTestValue"), "Expected imaname, got %s\n", path);
1729
1730     RegCloseKey(hkey);
1731
1732     res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine\\msitest");
1733     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1734
1735     check_service_is_installed();
1736
1737     /* Remove registry keys written by RegisterProduct standard action */
1738     res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{F1C3AF50-8B56-4A69-A00C-00773FE42F30}");
1739     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1740
1741     res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UpgradeCodes\\D8E760ECA1E276347B43E42BDBDA5656");
1742     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1743
1744     res = find_registry_key(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData", "05FA3C1F65B896A40AC00077F34EF203", &hkey);
1745     todo_wine ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1746     if (res == ERROR_SUCCESS)
1747     {
1748         res = delete_registry_key(hkey, "05FA3C1F65B896A40AC00077F34EF203");
1749         ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1750         RegCloseKey(hkey);
1751     }
1752
1753     /* Remove registry keys written by PublishProduct standard action */
1754     res = RegOpenKey(HKEY_CURRENT_USER, "SOFTWARE\\Microsoft\\Installer", &hkey);
1755     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1756
1757     res = delete_registry_key(hkey, "Products\\05FA3C1F65B896A40AC00077F34EF203");
1758     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1759
1760     res = RegDeleteKeyA(hkey, "UpgradeCodes\\D8E760ECA1E276347B43E42BDBDA5656");
1761     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
1762
1763     RegCloseKey(hkey);
1764
1765     /* Delete installation files we installed */
1766     delete_test_files();
1767 }
1768
1769 static void test_Installer(void)
1770 {
1771     static WCHAR szBackslash[] = { '\\',0 };
1772     static WCHAR szCreateRecordException[] = { 'C','r','e','a','t','e','R','e','c','o','r','d',',','C','o','u','n','t',0 };
1773     static WCHAR szIntegerDataException[] = { 'I','n','t','e','g','e','r','D','a','t','a',',','F','i','e','l','d',0 };
1774     WCHAR szPath[MAX_PATH];
1775     HRESULT hr;
1776     UINT len;
1777     IDispatch *pSession = NULL, *pRecord = NULL, *pStringList = NULL;
1778     int iValue, iCount;
1779
1780     if (!pInstaller) return;
1781
1782     /* Installer::CreateRecord */
1783
1784     /* Test for error */
1785     hr = Installer_CreateRecord(-1, &pRecord);
1786     ok(hr == DISP_E_EXCEPTION, "Installer_CreateRecord failed, hresult 0x%08x\n", hr);
1787     ok_exception(hr, szCreateRecordException);
1788
1789     /* Test for success */
1790     hr = Installer_CreateRecord(1, &pRecord);
1791     ok(SUCCEEDED(hr), "Installer_CreateRecord failed, hresult 0x%08x\n", hr);
1792     ok(pRecord != NULL, "Installer_CreateRecord should not have returned NULL record\n");
1793     if (pRecord)
1794     {
1795         /* Record::FieldCountGet */
1796         hr = Record_FieldCountGet(pRecord, &iValue);
1797         ok(SUCCEEDED(hr), "Record_FiledCountGet failed, hresult 0x%08x\n", hr);
1798         ok(iValue == 1, "Record_FieldCountGet result was %d but expected 1\n", iValue);
1799
1800         /* Record::IntegerDataGet */
1801         hr = Record_IntegerDataGet(pRecord, 1, &iValue);
1802         ok(SUCCEEDED(hr), "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
1803         ok(iValue == MSI_NULL_INTEGER, "Record_IntegerDataGet result was %d but expected %d\n", iValue, MSI_NULL_INTEGER);
1804
1805         /* Record::IntegerDataGet, bad index */
1806         hr = Record_IntegerDataGet(pRecord, 10, &iValue);
1807         ok(SUCCEEDED(hr), "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
1808         ok(iValue == MSI_NULL_INTEGER, "Record_IntegerDataGet result was %d but expected %d\n", iValue, MSI_NULL_INTEGER);
1809
1810         /* Record::IntegerDataPut */
1811         hr = Record_IntegerDataPut(pRecord, 1, 100);
1812         ok(SUCCEEDED(hr), "Record_IntegerDataPut failed, hresult 0x%08x\n", hr);
1813
1814         /* Record::IntegerDataPut, bad index */
1815         hr = Record_IntegerDataPut(pRecord, 10, 100);
1816         ok(hr == DISP_E_EXCEPTION, "Record_IntegerDataPut failed, hresult 0x%08x\n", hr);
1817         ok_exception(hr, szIntegerDataException);
1818
1819         /* Record::IntegerDataGet */
1820         hr = Record_IntegerDataGet(pRecord, 1, &iValue);
1821         ok(SUCCEEDED(hr), "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
1822         ok(iValue == 100, "Record_IntegerDataGet result was %d but expected 100\n", iValue);
1823
1824         IDispatch_Release(pRecord);
1825     }
1826
1827     /* Prepare package */
1828     create_database(msifile, tables, sizeof(tables) / sizeof(msi_table));
1829
1830     len = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, CURR_DIR, -1, szPath, MAX_PATH);
1831     ok(len, "MultiByteToWideChar returned error %d\n", GetLastError());
1832     if (!len) return;
1833
1834     lstrcatW(szPath, szBackslash);
1835     lstrcatW(szPath, szMsifile);
1836
1837     /* Installer::OpenPackage */
1838     hr = Installer_OpenPackage(szPath, 0, &pSession);
1839     ok(SUCCEEDED(hr), "Installer_OpenPackage failed, hresult 0x%08x\n", hr);
1840     if (SUCCEEDED(hr))
1841     {
1842         test_Session(pSession);
1843         IDispatch_Release(pSession);
1844     }
1845
1846     /* Installer::RegistryValue */
1847     test_Installer_RegistryValue();
1848
1849     /* Installer::Products */
1850     hr = Installer_Products(&pStringList);
1851     ok(SUCCEEDED(hr), "Installer_Products failed, hresult 0x%08x\n", hr);
1852     if (SUCCEEDED(hr))
1853     {
1854         int idx;
1855
1856         /* StringList::Count */
1857         hr = StringList_Count(pStringList, &iCount);
1858         ok(SUCCEEDED(hr), "StringList_Count failed, hresult 0x%08x\n", hr);
1859
1860         for (idx=0; idx<iCount; idx++)
1861         {
1862             /* StringList::Item */
1863             memset(szPath, 0, sizeof(szPath));
1864             hr = StringList_Item(pStringList, idx, szPath);
1865             ok(SUCCEEDED(hr), "StringList_Item failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);
1866
1867             if (SUCCEEDED(hr))
1868             {
1869                 /* Installer::ProductState */
1870                 hr = Installer_ProductState(szPath, &iValue);
1871                 ok(SUCCEEDED(hr), "Installer_ProductState failed, hresult 0x%08x\n", hr);
1872                 if (SUCCEEDED(hr))
1873                     ok(iValue == INSTALLSTATE_DEFAULT || iValue == INSTALLSTATE_ADVERTISED, "Installer_ProductState returned %d, expected %d or %d\n", iValue, INSTALLSTATE_DEFAULT, INSTALLSTATE_ADVERTISED);
1874             }
1875         }
1876
1877         /* StringList::Item using an invalid index */
1878         memset(szPath, 0, sizeof(szPath));
1879         hr = StringList_Item(pStringList, iCount, szPath);
1880         ok(hr == DISP_E_BADINDEX, "StringList_Item for an invalid index did not return DISP_E_BADINDEX, hresult 0x%08x\n", hr);
1881
1882         IDispatch_Release(pStringList);
1883     }
1884
1885     /* Installer::ProductState for our product code, which should not be installed */
1886     hr = Installer_ProductState(szProductCode, &iValue);
1887     ok(SUCCEEDED(hr), "Installer_ProductState failed, hresult 0x%08x\n", hr);
1888     if (SUCCEEDED(hr))
1889         ok(iValue == INSTALLSTATE_UNKNOWN, "Installer_ProductState returned %d, expected %d\n", iValue, INSTALLSTATE_UNKNOWN);
1890
1891     /* Installer::RelatedProducts for our upgrade code, should not find anything */
1892     hr = Installer_RelatedProducts(szUpgradeCode, &pStringList);
1893     ok(SUCCEEDED(hr), "Installer_RelatedProducts failed, hresult 0x%08x\n", hr);
1894     if (SUCCEEDED(hr))
1895     {
1896         /* StringList::Count */
1897         hr = StringList_Count(pStringList, &iCount);
1898         ok(SUCCEEDED(hr), "StringList_Count failed, hresult 0x%08x\n", hr);
1899         ok(!iCount, "Expected no related products but found %d\n", iCount);
1900
1901         IDispatch_Release(pStringList);
1902     }
1903
1904     /* Installer::Version */
1905     todo_wine {
1906         memset(szPath, 0, sizeof(szPath));
1907         hr = Installer_VersionGet(szPath);
1908         ok(SUCCEEDED(hr), "Installer_VersionGet failed, hresult 0x%08x\n", hr);
1909     }
1910
1911     /* Installer::InstallProduct and other tests that depend on our product being installed */
1912     test_Installer_InstallProduct(szPath);
1913 }
1914
1915 START_TEST(automation)
1916 {
1917     DWORD len;
1918     char temp_path[MAX_PATH], prev_path[MAX_PATH];
1919     HRESULT hr;
1920     CLSID clsid;
1921     IUnknown *pUnk;
1922
1923     GetCurrentDirectoryA(MAX_PATH, prev_path);
1924     GetTempPath(MAX_PATH, temp_path);
1925     SetCurrentDirectoryA(temp_path);
1926
1927     lstrcpyA(CURR_DIR, temp_path);
1928     len = lstrlenA(CURR_DIR);
1929
1930     if(len && (CURR_DIR[len - 1] == '\\'))
1931         CURR_DIR[len - 1] = 0;
1932
1933     get_program_files_dir(PROG_FILES_DIR);
1934
1935     hr = OleInitialize(NULL);
1936     ok (SUCCEEDED(hr), "OleInitialize returned 0x%08x\n", hr);
1937     hr = CLSIDFromProgID(szProgId, &clsid);
1938     ok (SUCCEEDED(hr), "CLSIDFromProgID returned 0x%08x\n", hr);
1939     hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&pUnk);
1940     ok(SUCCEEDED(hr), "CoCreateInstance returned 0x%08x\n", hr);
1941
1942     if (pUnk)
1943     {
1944         hr = IUnknown_QueryInterface(pUnk, &IID_IDispatch, (void **)&pInstaller);
1945         ok (SUCCEEDED(hr), "IUnknown::QueryInterface returned 0x%08x\n", hr);
1946
1947         test_dispid();
1948         test_dispatch();
1949         test_Installer();
1950
1951         hr = IUnknown_Release(pUnk);
1952         ok (SUCCEEDED(hr), "IUnknown::Release returned 0x%08x\n", hr);
1953     }
1954
1955     OleUninitialize();
1956
1957     SetCurrentDirectoryA(prev_path);
1958 }