[YAROTOWS] Reintegrate the branch. For a brighter future.
[reactos.git] / rostests / winetests / msi / automation.c
1 /*
2 * Copyright (C) 2007 Mike McCormack for CodeWeavers
3 * Copyright (C) 2007 Misha Koshelev
4 *
5 * A test program for Microsoft Installer OLE automation functionality.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 */
21
22 #define COBJMACROS
23
24 #include <stdio.h>
25
26 #include <initguid.h>
27 #include <windows.h>
28 #include <msiquery.h>
29 #include <msidefs.h>
30 #include <msi.h>
31 #include <fci.h>
32
33 #include "wine/test.h"
34
35 DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
36
37 static const char *msifile = "winetest-automation.msi";
38 static const WCHAR szMsifile[] = {'w','i','n','e','t','e','s','t','-','a','u','t','o','m','a','t','i','o','n','.','m','s','i',0};
39 static const WCHAR szMSITEST[] = { 'M','S','I','T','E','S','T',0 };
40 static const WCHAR szProductCode[] = { '{','F','1','C','3','A','F','5','0','-','8','B','5','6','-','4','A','6','9','-','A','0','0','C','-','0','0','7','7','3','F','E','4','2','F','3','0','}',0 };
41 static const WCHAR szUpgradeCode[] = { '{','C','E','0','6','7','E','8','D','-','2','E','1','A','-','4','3','6','7','-','B','7','3','4','-','4','E','B','2','B','D','A','D','6','5','6','5','}',0 };
42 static const WCHAR szProductInfoException[] = { 'P','r','o','d','u','c','t','I','n','f','o',',','P','r','o','d','u','c','t',',','A','t','t','r','i','b','u','t','e',0 };
43 static const WCHAR WINE_INSTALLPROPERTY_PACKAGENAMEW[] = {'P','a','c','k','a','g','e','N','a','m','e',0};
44 static const WCHAR WINE_INSTALLPROPERTY_PRODUCTNAMEW[] = {'P','r','o','d','u','c','t','N','a','m','e',0};
45 static FILETIME systemtime;
46 static CHAR CURR_DIR[MAX_PATH];
47 static EXCEPINFO excepinfo;
48
49 /*
50 * OLE automation data
51 **/
52 static const WCHAR szProgId[] = { 'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r','.','I','n','s','t','a','l','l','e','r',0 };
53 static IDispatch *pInstaller;
54
55 /* msi database data */
56
57 static const CHAR component_dat[] = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n"
58 "s72\tS38\ts72\ti2\tS255\tS72\n"
59 "Component\tComponent\n"
60 "Five\t{8CC92E9D-14B2-4CA4-B2AA-B11D02078087}\tNEWDIR\t2\t\tfive.txt\n"
61 "Four\t{FD37B4EA-7209-45C0-8917-535F35A2F080}\tCABOUTDIR\t2\t\tfour.txt\n"
62 "One\t{783B242E-E185-4A56-AF86-C09815EC053C}\tMSITESTDIR\t2\t\tone.txt\n"
63 "Three\t{010B6ADD-B27D-4EDD-9B3D-34C4F7D61684}\tCHANGEDDIR\t2\t\tthree.txt\n"
64 "Two\t{BF03D1A6-20DA-4A65-82F3-6CAC995915CE}\tFIRSTDIR\t2\t\ttwo.txt\n"
65 "dangler\t{6091DF25-EF96-45F1-B8E9-A9B1420C7A3C}\tTARGETDIR\t4\t\tregdata\n"
66 "component\t\tMSITESTDIR\t0\t1\tfile\n";
67
68 static const CHAR directory_dat[] = "Directory\tDirectory_Parent\tDefaultDir\n"
69 "s72\tS72\tl255\n"
70 "Directory\tDirectory\n"
71 "CABOUTDIR\tMSITESTDIR\tcabout\n"
72 "CHANGEDDIR\tMSITESTDIR\tchanged:second\n"
73 "FIRSTDIR\tMSITESTDIR\tfirst\n"
74 "MSITESTDIR\tProgramFilesFolder\tmsitest\n"
75 "NEWDIR\tCABOUTDIR\tnew\n"
76 "ProgramFilesFolder\tTARGETDIR\t.\n"
77 "TARGETDIR\t\tSourceDir";
78
79 static const CHAR feature_dat[] = "Feature\tFeature_Parent\tTitle\tDescription\tDisplay\tLevel\tDirectory_\tAttributes\n"
80 "s38\tS38\tL64\tL255\tI2\ti2\tS72\ti2\n"
81 "Feature\tFeature\n"
82 "Five\t\tFive\tThe Five Feature\t5\t3\tNEWDIR\t0\n"
83 "Four\t\tFour\tThe Four Feature\t4\t3\tCABOUTDIR\t0\n"
84 "One\t\tOne\tThe One Feature\t1\t3\tMSITESTDIR\t0\n"
85 "Three\tOne\tThree\tThe Three Feature\t3\t3\tCHANGEDDIR\t0\n"
86 "Two\tOne\tTwo\tThe Two Feature\t2\t3\tFIRSTDIR\t0\n"
87 "feature\t\t\t\t2\t1\tTARGETDIR\t0\n";
88
89 static const CHAR feature_comp_dat[] = "Feature_\tComponent_\n"
90 "s38\ts72\n"
91 "FeatureComponents\tFeature_\tComponent_\n"
92 "Five\tFive\n"
93 "Four\tFour\n"
94 "One\tOne\n"
95 "Three\tThree\n"
96 "Two\tTwo\n"
97 "feature\tcomponent\n";
98
99 static const CHAR file_dat[] = "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n"
100 "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n"
101 "File\tFile\n"
102 "five.txt\tFive\tfive.txt\t1000\t\t\t0\t5\n"
103 "four.txt\tFour\tfour.txt\t1000\t\t\t0\t4\n"
104 "one.txt\tOne\tone.txt\t1000\t\t\t0\t1\n"
105 "three.txt\tThree\tthree.txt\t1000\t\t\t0\t3\n"
106 "two.txt\tTwo\ttwo.txt\t1000\t\t\t0\t2\n"
107 "file\tcomponent\tfilename\t100\t\t\t8192\t1\n";
108
109 static const CHAR install_exec_seq_dat[] = "Action\tCondition\tSequence\n"
110 "s72\tS255\tI2\n"
111 "InstallExecuteSequence\tAction\n"
112 "AllocateRegistrySpace\tNOT Installed\t1550\n"
113 "CostFinalize\t\t1000\n"
114 "CostInitialize\t\t800\n"
115 "FileCost\t\t900\n"
116 "InstallFiles\t\t4000\n"
117 "RegisterProduct\t\t6100\n"
118 "PublishProduct\t\t6400\n"
119 "InstallFinalize\t\t6600\n"
120 "InstallInitialize\t\t1500\n"
121 "InstallValidate\t\t1400\n"
122 "LaunchConditions\t\t100\n"
123 "WriteRegistryValues\tSourceDir And SOURCEDIR\t5000";
124
125 static const CHAR media_dat[] = "DiskId\tLastSequence\tDiskPrompt\tCabinet\tVolumeLabel\tSource\n"
126 "i2\ti4\tL64\tS255\tS32\tS72\n"
127 "Media\tDiskId\n"
128 "1\t5\t\t\tDISK1\t\n";
129
130 static const CHAR property_dat[] = "Property\tValue\n"
131 "s72\tl0\n"
132 "Property\tProperty\n"
133 "DefaultUIFont\tDlgFont8\n"
134 "HASUIRUN\t0\n"
135 "INSTALLLEVEL\t3\n"
136 "InstallMode\tTypical\n"
137 "Manufacturer\tWine\n"
138 "PIDTemplate\t12345<###-%%%%%%%>@@@@@\n"
139 "ProductCode\t{F1C3AF50-8B56-4A69-A00C-00773FE42F30}\n"
140 "ProductID\tnone\n"
141 "ProductLanguage\t1033\n"
142 "ProductName\tMSITEST\n"
143 "ProductVersion\t1.1.1\n"
144 "PROMPTROLLBACKCOST\tP\n"
145 "Setup\tSetup\n"
146 "UpgradeCode\t{CE067E8D-2E1A-4367-B734-4EB2BDAD6565}";
147
148 static const CHAR registry_dat[] = "Registry\tRoot\tKey\tName\tValue\tComponent_\n"
149 "s72\ti2\tl255\tL255\tL0\ts72\n"
150 "Registry\tRegistry\n"
151 "Apples\t2\tSOFTWARE\\Wine\\msitest\tName\timaname\tOne\n"
152 "Oranges\t2\tSOFTWARE\\Wine\\msitest\tnumber\t#314\tTwo\n"
153 "regdata\t2\tSOFTWARE\\Wine\\msitest\tblah\tbad\tdangler\n"
154 "OrderTest\t2\tSOFTWARE\\Wine\\msitest\tOrderTestName\tOrderTestValue\tcomponent";
155
156 typedef struct _msi_table
157 {
158 const CHAR *filename;
159 const CHAR *data;
160 int size;
161 } msi_table;
162
163 #define ADD_TABLE(x) {#x".idt", x##_dat, sizeof(x##_dat)}
164
165 static const msi_table tables[] =
166 {
167 ADD_TABLE(component),
168 ADD_TABLE(directory),
169 ADD_TABLE(feature),
170 ADD_TABLE(feature_comp),
171 ADD_TABLE(file),
172 ADD_TABLE(install_exec_seq),
173 ADD_TABLE(media),
174 ADD_TABLE(property),
175 ADD_TABLE(registry)
176 };
177
178 typedef struct _msi_summary_info
179 {
180 UINT property;
181 UINT datatype;
182 INT iValue;
183 FILETIME *pftValue;
184 const CHAR *szValue;
185 } msi_summary_info;
186
187 #define ADD_INFO_I2(property, iValue) {property, VT_I2, iValue, NULL, NULL}
188 #define ADD_INFO_I4(property, iValue) {property, VT_I4, iValue, NULL, NULL}
189 #define ADD_INFO_LPSTR(property, szValue) {property, VT_LPSTR, 0, NULL, szValue}
190 #define ADD_INFO_FILETIME(property, pftValue) {property, VT_FILETIME, 0, pftValue, NULL}
191
192 static const msi_summary_info summary_info[] =
193 {
194 ADD_INFO_LPSTR(PID_TEMPLATE, ";1033"),
195 ADD_INFO_LPSTR(PID_REVNUMBER, "{004757CA-5092-49c2-AD20-28E1CE0DF5F2}"),
196 ADD_INFO_I4(PID_PAGECOUNT, 100),
197 ADD_INFO_I4(PID_WORDCOUNT, 0),
198 ADD_INFO_FILETIME(PID_CREATE_DTM, &systemtime),
199 ADD_INFO_FILETIME(PID_LASTPRINTED, &systemtime)
200 };
201
202 /*
203 * Database Helpers
204 */
205
206 static void write_file(const CHAR *filename, const char *data, int data_size)
207 {
208 DWORD size;
209
210 HANDLE hf = CreateFile(filename, GENERIC_WRITE, 0, NULL,
211 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
212
213 WriteFile(hf, data, data_size, &size, NULL);
214 CloseHandle(hf);
215 }
216
217 static void write_msi_summary_info(MSIHANDLE db, const msi_summary_info *info, int num_info)
218 {
219 MSIHANDLE summary;
220 UINT r;
221 int j;
222
223 r = MsiGetSummaryInformationA(db, NULL, num_info, &summary);
224 ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
225
226 /* import summary information into the stream */
227 for (j = 0; j < num_info; j++)
228 {
229 const msi_summary_info *entry = &info[j];
230
231 r = MsiSummaryInfoSetPropertyA(summary, entry->property, entry->datatype,
232 entry->iValue, entry->pftValue, entry->szValue);
233 ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
234 }
235
236 /* write the summary changes back to the stream */
237 r = MsiSummaryInfoPersist(summary);
238 ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
239
240 MsiCloseHandle(summary);
241 }
242
243 static void create_database(const CHAR *name, const msi_table *tables, int num_tables,
244 const msi_summary_info *info, int num_info)
245 {
246 MSIHANDLE db;
247 UINT r;
248 int j;
249
250 r = MsiOpenDatabaseA(name, MSIDBOPEN_CREATE, &db);
251 ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
252
253 /* import the tables into the database */
254 for (j = 0; j < num_tables; j++)
255 {
256 const msi_table *table = &tables[j];
257
258 write_file(table->filename, table->data, (table->size - 1) * sizeof(char));
259
260 r = MsiDatabaseImportA(db, CURR_DIR, table->filename);
261 ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
262
263 DeleteFileA(table->filename);
264 }
265
266 write_msi_summary_info(db, info, num_info);
267
268 r = MsiDatabaseCommit(db);
269 ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
270
271 MsiCloseHandle(db);
272 }
273
274 static BOOL create_package(LPWSTR path)
275 {
276 DWORD len;
277
278 /* Prepare package */
279 create_database(msifile, tables,
280 sizeof(tables) / sizeof(msi_table), summary_info,
281 sizeof(summary_info) / sizeof(msi_summary_info));
282
283 len = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
284 CURR_DIR, -1, path, MAX_PATH);
285 ok(len, "MultiByteToWideChar returned error %d\n", GetLastError());
286 if (!len)
287 return FALSE;
288
289 /* lstrcatW does not work on win95 */
290 path[len - 1] = '\\';
291 memcpy(&path[len], szMsifile, sizeof(szMsifile));
292 return TRUE;
293 }
294
295 /*
296 * Installation helpers
297 */
298
299 static char PROG_FILES_DIR[MAX_PATH];
300
301 static BOOL get_program_files_dir(LPSTR buf)
302 {
303 HKEY hkey;
304 DWORD type = REG_EXPAND_SZ, size;
305
306 if (RegOpenKey(HKEY_LOCAL_MACHINE,
307 "Software\\Microsoft\\Windows\\CurrentVersion", &hkey))
308 return FALSE;
309
310 size = MAX_PATH;
311 if (RegQueryValueEx(hkey, "ProgramFilesDir (x86)", 0, &type, (LPBYTE)buf, &size) &&
312 RegQueryValueEx(hkey, "ProgramFilesDir", 0, &type, (LPBYTE)buf, &size))
313 return FALSE;
314
315 RegCloseKey(hkey);
316 return TRUE;
317 }
318
319 static void create_file(const CHAR *name, DWORD size)
320 {
321 HANDLE file;
322 DWORD written, left;
323
324 file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
325 ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n", name);
326 WriteFile(file, name, strlen(name), &written, NULL);
327 WriteFile(file, "\n", strlen("\n"), &written, NULL);
328
329 left = size - lstrlen(name) - 1;
330
331 SetFilePointer(file, left, NULL, FILE_CURRENT);
332 SetEndOfFile(file);
333
334 CloseHandle(file);
335 }
336
337 static void create_test_files(void)
338 {
339 CreateDirectoryA("msitest", NULL);
340 create_file("msitest\\one.txt", 100);
341 CreateDirectoryA("msitest\\first", NULL);
342 create_file("msitest\\first\\two.txt", 100);
343 CreateDirectoryA("msitest\\second", NULL);
344 create_file("msitest\\second\\three.txt", 100);
345 CreateDirectoryA("msitest\\cabout",NULL);
346 create_file("msitest\\cabout\\four.txt", 100);
347 CreateDirectoryA("msitest\\cabout\\new",NULL);
348 create_file("msitest\\cabout\\new\\five.txt", 100);
349 create_file("msitest\\filename", 100);
350 }
351
352 static BOOL delete_pf(const CHAR *rel_path, BOOL is_file)
353 {
354 CHAR path[MAX_PATH];
355
356 lstrcpyA(path, PROG_FILES_DIR);
357 lstrcatA(path, "\\");
358 lstrcatA(path, rel_path);
359
360 if (is_file)
361 return DeleteFileA(path);
362 else
363 return RemoveDirectoryA(path);
364 }
365
366 static void delete_test_files(void)
367 {
368 DeleteFileA(msifile);
369 DeleteFileA("msitest\\cabout\\new\\five.txt");
370 DeleteFileA("msitest\\cabout\\four.txt");
371 DeleteFileA("msitest\\second\\three.txt");
372 DeleteFileA("msitest\\first\\two.txt");
373 DeleteFileA("msitest\\one.txt");
374 DeleteFileA("msitest\\filename");
375 RemoveDirectoryA("msitest\\cabout\\new");
376 RemoveDirectoryA("msitest\\cabout");
377 RemoveDirectoryA("msitest\\second");
378 RemoveDirectoryA("msitest\\first");
379 RemoveDirectoryA("msitest");
380 }
381
382 /*
383 * Automation helpers and tests
384 */
385
386 /* ok-like statement which takes two unicode strings or one unicode and one ANSI string as arguments */
387 static CHAR string1[MAX_PATH], string2[MAX_PATH];
388
389 /* lstrcmpW is not supported on Win9x */
390 static int strcmp_ww(const WCHAR* str1, const WCHAR* str2)
391 {
392 CHAR str1A[MAX_PATH], str2A[MAX_PATH];
393
394 WideCharToMultiByte(CP_ACP, 0, str1, -1, str1A, MAX_PATH, NULL, NULL);
395 WideCharToMultiByte(CP_ACP, 0, str2, -1, str2A, MAX_PATH, NULL, NULL);
396
397 return lstrcmpA(str1A, str2A);
398 }
399
400 #define ok_w2(format, szString1, szString2) \
401 \
402 do { \
403 WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
404 WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
405 if (lstrcmpA(string1, string2) != 0) \
406 ok(0, format, string1, string2); \
407 } while(0);
408
409 #define ok_w2n(format, szString1, szString2, len) \
410 \
411 if (memcmp(szString1, szString2, len * sizeof(WCHAR)) != 0) \
412 { \
413 WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
414 WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
415 ok(0, format, string1, string2); \
416 }
417
418 #define ok_aw(format, aString, wString) \
419 \
420 WideCharToMultiByte(CP_ACP, 0, wString, -1, string1, MAX_PATH, NULL, NULL); \
421 if (lstrcmpA(string1, aString) != 0) \
422 ok(0, format, string1, aString); \
423
424 #define ok_awplus(format, extra, aString, wString) \
425 \
426 WideCharToMultiByte(CP_ACP, 0, wString, -1, string1, MAX_PATH, NULL, NULL); \
427 if (lstrcmpA(string1, aString) != 0) \
428 ok(0, format, extra, string1, aString); \
429
430 /* exception checker */
431 static WCHAR szSource[] = {'M','s','i',' ','A','P','I',' ','E','r','r','o','r',0};
432
433 #define ok_exception(hr, szDescription) \
434 if (hr == DISP_E_EXCEPTION) \
435 { \
436 /* Compare wtype, source, and destination */ \
437 ok(excepinfo.wCode == 1000, "Exception info was %d, expected 1000\n", excepinfo.wCode); \
438 \
439 ok(excepinfo.bstrSource != NULL, "Exception source was NULL\n"); \
440 if (excepinfo.bstrSource) \
441 ok_w2("Exception source was \"%s\" but expected to be \"%s\"\n", excepinfo.bstrSource, szSource); \
442 \
443 ok(excepinfo.bstrDescription != NULL, "Exception description was NULL\n"); \
444 if (excepinfo.bstrDescription) \
445 ok_w2("Exception description was \"%s\" but expected to be \"%s\"\n", excepinfo.bstrDescription, szDescription); \
446 \
447 SysFreeString(excepinfo.bstrSource); \
448 SysFreeString(excepinfo.bstrDescription); \
449 SysFreeString(excepinfo.bstrHelpFile); \
450 }
451
452 static DISPID get_dispid( IDispatch *disp, const char *name )
453 {
454 LPOLESTR str;
455 UINT len;
456 DISPID id = -1;
457 HRESULT r;
458
459 len = MultiByteToWideChar(CP_ACP, 0, name, -1, NULL, 0 );
460 str = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR) );
461 if (str)
462 {
463 len = MultiByteToWideChar(CP_ACP, 0, name, -1, str, len );
464 r = IDispatch_GetIDsOfNames( disp, &IID_NULL, &str, 1, 0, &id );
465 HeapFree(GetProcessHeap(), 0, str);
466 if (r != S_OK)
467 return -1;
468 }
469
470 return id;
471 }
472
473 static void test_dispid(void)
474 {
475 DISPID dispid;
476
477 dispid = get_dispid(pInstaller, "CreateRecord");
478 ok(dispid == 1, "Expected 1, got %d\n", dispid);
479 dispid = get_dispid(pInstaller, "OpenPackage");
480 ok(dispid == 2, "Expected 2, got %d\n", dispid);
481 dispid = get_dispid(pInstaller, "OpenProduct");
482 ok(dispid == 3, "Expected 3, got %d\n", dispid);
483 dispid = get_dispid(pInstaller, "OpenDatabase");
484 ok(dispid == 4, "Expected 4, got %d\n", dispid);
485 dispid = get_dispid(pInstaller, "SummaryInformation");
486 ok(dispid == 5, "Expected 5, got %d\n", dispid);
487 dispid = get_dispid( pInstaller, "UILevel" );
488 ok(dispid == 6, "Expected 6, got %d\n", dispid);
489 dispid = get_dispid(pInstaller, "EnableLog");
490 ok(dispid == 7, "Expected 7, got %d\n", dispid);
491 dispid = get_dispid(pInstaller, "InstallProduct");
492 ok(dispid == 8, "Expected 8, got %d\n", dispid);
493 dispid = get_dispid(pInstaller, "Version");
494 ok(dispid == 9, "Expected 9, got %d\n", dispid);
495 dispid = get_dispid(pInstaller, "LastErrorRecord");
496 ok(dispid == 10, "Expected 10, got %d\n", dispid);
497 dispid = get_dispid(pInstaller, "RegistryValue");
498 ok(dispid == 11, "Expected 11, got %d\n", dispid);
499 dispid = get_dispid(pInstaller, "Environment");
500 ok(dispid == 12, "Expected 12, got %d\n", dispid);
501 dispid = get_dispid(pInstaller, "FileAttributes");
502 ok(dispid == 13, "Expected 13, got %d\n", dispid);
503 dispid = get_dispid(pInstaller, "FileSize");
504 ok(dispid == 15, "Expected 15, got %d\n", dispid);
505 dispid = get_dispid(pInstaller, "FileVersion");
506 ok(dispid == 16, "Expected 16, got %d\n", dispid);
507 dispid = get_dispid(pInstaller, "ProductState");
508 ok(dispid == 17, "Expected 17, got %d\n", dispid);
509 dispid = get_dispid(pInstaller, "ProductInfo");
510 ok(dispid == 18, "Expected 18, got %d\n", dispid);
511 todo_wine
512 {
513 dispid = get_dispid(pInstaller, "ConfigureProduct");
514 ok(dispid == 19, "Expected 19, got %d\n", dispid);
515 dispid = get_dispid(pInstaller, "ReinstallProduct");
516 ok(dispid == 20 , "Expected 20, got %d\n", dispid);
517 dispid = get_dispid(pInstaller, "CollectUserInfo");
518 ok(dispid == 21, "Expected 21, got %d\n", dispid);
519 dispid = get_dispid(pInstaller, "ApplyPatch");
520 ok(dispid == 22, "Expected 22, got %d\n", dispid);
521 dispid = get_dispid(pInstaller, "FeatureParent");
522 ok(dispid == 23, "Expected 23, got %d\n", dispid);
523 dispid = get_dispid(pInstaller, "FeatureState");
524 ok(dispid == 24, "Expected 24, got %d\n", dispid);
525 dispid = get_dispid(pInstaller, "UseFeature");
526 ok(dispid == 25, "Expected 25, got %d\n", dispid);
527 dispid = get_dispid(pInstaller, "FeatureUsageCount");
528 ok(dispid == 26, "Expected 26, got %d\n", dispid);
529 dispid = get_dispid(pInstaller, "FeatureUsageDate");
530 ok(dispid == 27, "Expected 27, got %d\n", dispid);
531 dispid = get_dispid(pInstaller, "ConfigureFeature");
532 ok(dispid == 28, "Expected 28, got %d\n", dispid);
533 dispid = get_dispid(pInstaller, "ReinstallFeature");
534 ok(dispid == 29, "Expected 29, got %d\n", dispid);
535 dispid = get_dispid(pInstaller, "ProvideComponent");
536 ok(dispid == 30, "Expected 30, got %d\n", dispid);
537 dispid = get_dispid(pInstaller, "ComponentPath");
538 ok(dispid == 31, "Expected 31, got %d\n", dispid);
539 dispid = get_dispid(pInstaller, "ProvideQualifiedComponent");
540 ok(dispid == 32, "Expected 32, got %d\n", dispid);
541 dispid = get_dispid(pInstaller, "QualifierDescription");
542 ok(dispid == 33, "Expected 33, got %d\n", dispid);
543 dispid = get_dispid(pInstaller, "ComponentQualifiers");
544 ok(dispid == 34, "Expected 34, got %d\n", dispid);
545 }
546 dispid = get_dispid(pInstaller, "Products");
547 ok(dispid == 35, "Expected 35, got %d\n", dispid);
548 todo_wine
549 {
550 dispid = get_dispid(pInstaller, "Features");
551 ok(dispid == 36, "Expected 36, got %d\n", dispid);
552 dispid = get_dispid(pInstaller, "Components");
553 ok(dispid == 37, "Expected 37, got %d\n", dispid);
554 dispid = get_dispid(pInstaller, "ComponentClients");
555 ok(dispid == 38, "Expected 38, got %d\n", dispid);
556 dispid = get_dispid(pInstaller, "Patches");
557 ok(dispid == 39, "Expected 39, got %d\n", dispid);
558 }
559 dispid = get_dispid(pInstaller, "RelatedProducts");
560 ok(dispid == 40, "Expected 40, got %d\n", dispid);
561 todo_wine
562 {
563 dispid = get_dispid(pInstaller, "PatchInfo");
564 ok(dispid == 41, "Expected 41, got %d\n", dispid);
565 dispid = get_dispid(pInstaller, "PatchTransforms");
566 ok(dispid == 42, "Expected 42, got %d\n", dispid);
567 dispid = get_dispid(pInstaller, "AddSource");
568 ok(dispid == 43, "Expected 43, got %d\n", dispid);
569 dispid = get_dispid(pInstaller, "ClearSourceList");
570 ok(dispid == 44, "Expected 44, got %d\n", dispid);
571 dispid = get_dispid(pInstaller, "ForceSourceListResolution");
572 ok(dispid == 45, "Expected 45, got %d\n", dispid);
573 dispid = get_dispid(pInstaller, "ShortcutTarget");
574 ok(dispid == 46, "Expected 46, got %d\n", dispid);
575 dispid = get_dispid(pInstaller, "FileHash");
576 ok(dispid == 47, "Expected 47, got %d\n", dispid);
577 dispid = get_dispid(pInstaller, "FileSignatureInfo");
578 ok(dispid == 48, "Expected 48, got %d\n", dispid);
579 }
580 dispid = get_dispid(pInstaller, "RemovePatches");
581 ok(dispid == 49 || dispid == -1, "Expected 49 or -1, got %d\n", dispid);
582 dispid = get_dispid(pInstaller, "ApplyMultiplePatches");
583 ok(dispid == 51 || dispid == -1, "Expected 51 or -1, got %d\n", dispid);
584 dispid = get_dispid(pInstaller, "ProductsEx");
585 ok(dispid == 52 || dispid == -1, "Expected 52 or -1, got %d\n", dispid);
586 dispid = get_dispid(pInstaller, "PatchesEx");
587 ok(dispid == 55 || dispid == -1, "Expected 55 or -1, got %d\n", dispid);
588 dispid = get_dispid(pInstaller, "ExtractPatchXMLData");
589 ok(dispid == 57 || dispid == -1, "Expected 57 or -1, got %d\n", dispid);
590 dispid = get_dispid( pInstaller, "ProductElevated" );
591 ok(dispid == 59 || dispid == -1, "Expected 59 or -1, got %d\n", dispid);
592 dispid = get_dispid( pInstaller, "ProvideAssembly" );
593 ok(dispid == 60 || dispid == -1, "Expected 60 or -1, got %d\n", dispid);
594 dispid = get_dispid( pInstaller, "ProductInfoFromScript" );
595 ok(dispid == 61 || dispid == -1, "Expected 61 or -1, got %d\n", dispid);
596 dispid = get_dispid( pInstaller, "AdvertiseProduct" );
597 ok(dispid == 62 || dispid == -1, "Expected 62 or -1, got %d\n", dispid);
598 dispid = get_dispid( pInstaller, "CreateAdvertiseScript" );
599 ok(dispid == 63 || dispid == -1, "Expected 63 or -1, got %d\n", dispid);
600 dispid = get_dispid( pInstaller, "PatchFiles" );
601 ok(dispid == 65 || dispid == -1, "Expected 65 or -1, got %d\n", dispid);
602 }
603
604 /* Test basic IDispatch functions */
605 static void test_dispatch(void)
606 {
607 static WCHAR szOpenPackage[] = { 'O','p','e','n','P','a','c','k','a','g','e',0 };
608 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};
609 static WCHAR szProductState[] = { 'P','r','o','d','u','c','t','S','t','a','t','e',0 };
610 HRESULT hr;
611 DISPID dispid;
612 OLECHAR *name;
613 VARIANT varresult;
614 VARIANTARG vararg[3];
615 WCHAR path[MAX_PATH];
616 DISPPARAMS dispparams = {NULL, NULL, 0, 0};
617
618 /* Test getting ID of a function name that does not exist */
619 name = (WCHAR *)szMsifile;
620 hr = IDispatch_GetIDsOfNames(pInstaller, &IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &dispid);
621 ok(hr == DISP_E_UNKNOWNNAME, "IDispatch::GetIDsOfNames returned 0x%08x\n", hr);
622
623 /* Test invoking this function */
624 hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, NULL, NULL, NULL, NULL);
625 ok(hr == DISP_E_MEMBERNOTFOUND, "IDispatch::Invoke returned 0x%08x\n", hr);
626
627 /* Test getting ID of a function name that does exist */
628 name = szOpenPackage;
629 hr = IDispatch_GetIDsOfNames(pInstaller, &IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &dispid);
630 ok(hr == S_OK, "IDispatch::GetIDsOfNames returned 0x%08x\n", hr);
631
632 /* Test invoking this function (without parameters passed) */
633 if (0) /* All of these crash MSI on Windows XP */
634 {
635 hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, NULL, NULL, NULL, NULL);
636 hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, NULL, NULL, &excepinfo, NULL);
637 VariantInit(&varresult);
638 hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, NULL, &varresult, &excepinfo, NULL);
639 }
640
641 /* Try with NULL params */
642 hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
643 ok(hr == DISP_E_TYPEMISMATCH, "IDispatch::Invoke returned 0x%08x\n", hr);
644
645 /* Try one empty parameter */
646 dispparams.rgvarg = vararg;
647 dispparams.cArgs = 1;
648 VariantInit(&vararg[0]);
649 hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
650 ok(hr == DISP_E_TYPEMISMATCH, "IDispatch::Invoke returned 0x%08x\n", hr);
651
652 /* Try two empty parameters */
653 dispparams.cArgs = 2;
654 VariantInit(&vararg[0]);
655 VariantInit(&vararg[1]);
656 hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
657 ok(hr == DISP_E_TYPEMISMATCH, "IDispatch::Invoke returned 0x%08x\n", hr);
658
659 /* Try one parameter, the required BSTR. Second parameter is optional.
660 * NOTE: The specified package does not exist, which is why the call fails.
661 */
662 dispparams.cArgs = 1;
663 VariantInit(&vararg[0]);
664 V_VT(&vararg[0]) = VT_BSTR;
665 V_BSTR(&vararg[0]) = SysAllocString(szMsifile);
666 hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
667 ok(hr == DISP_E_EXCEPTION, "IDispatch::Invoke returned 0x%08x\n", hr);
668 ok_exception(hr, szOpenPackageException);
669 VariantClear(&vararg[0]);
670
671 /* Provide the required BSTR and an empty second parameter.
672 * NOTE: The specified package does not exist, which is why the call fails.
673 */
674 dispparams.cArgs = 2;
675 VariantInit(&vararg[1]);
676 V_VT(&vararg[1]) = VT_BSTR;
677 V_BSTR(&vararg[1]) = SysAllocString(szMsifile);
678 VariantInit(&vararg[0]);
679 hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
680 ok(hr == DISP_E_EXCEPTION, "IDispatch::Invoke returned 0x%08x\n", hr);
681 ok_exception(hr, szOpenPackageException);
682 VariantClear(&vararg[1]);
683
684 /* Provide the required BSTR and two empty parameters.
685 * NOTE: The specified package does not exist, which is why the call fails.
686 */
687 dispparams.cArgs = 3;
688 VariantInit(&vararg[2]);
689 V_VT(&vararg[2]) = VT_BSTR;
690 V_BSTR(&vararg[2]) = SysAllocString(szMsifile);
691 VariantInit(&vararg[1]);
692 VariantInit(&vararg[0]);
693 hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
694 ok(hr == DISP_E_EXCEPTION, "IDispatch::Invoke returned 0x%08x\n", hr);
695 ok_exception(hr, szOpenPackageException);
696 VariantClear(&vararg[2]);
697
698 /* Provide the required BSTR and a second parameter with the wrong type. */
699 dispparams.cArgs = 2;
700 VariantInit(&vararg[1]);
701 V_VT(&vararg[1]) = VT_BSTR;
702 V_BSTR(&vararg[1]) = SysAllocString(szMsifile);
703 VariantInit(&vararg[0]);
704 V_VT(&vararg[0]) = VT_BSTR;
705 V_BSTR(&vararg[0]) = SysAllocString(szMsifile);
706 hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
707 ok(hr == DISP_E_TYPEMISMATCH, "IDispatch::Invoke returned 0x%08x\n", hr);
708 VariantClear(&vararg[0]);
709 VariantClear(&vararg[1]);
710
711 /* Create a proper installer package. */
712 create_package(path);
713
714 /* Try one parameter, the required BSTR. Second parameter is optional.
715 * Proper installer package exists. Path to the package is relative.
716 */
717 dispparams.cArgs = 1;
718 VariantInit(&vararg[0]);
719 V_VT(&vararg[0]) = VT_BSTR;
720 V_BSTR(&vararg[0]) = SysAllocString(szMsifile);
721 hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
722 todo_wine ok(hr == DISP_E_EXCEPTION, "IDispatch::Invoke returned 0x%08x\n", hr);
723 ok_exception(hr, szOpenPackageException);
724 VariantClear(&vararg[0]);
725 if (hr != DISP_E_EXCEPTION)
726 VariantClear(&varresult);
727
728 /* Try one parameter, the required BSTR. Second parameter is optional.
729 * Proper installer package exists. Path to the package is absolute.
730 */
731 dispparams.cArgs = 1;
732 VariantInit(&vararg[0]);
733 V_VT(&vararg[0]) = VT_BSTR;
734 V_BSTR(&vararg[0]) = SysAllocString(path);
735 hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
736 ok(hr == S_OK, "IDispatch::Invoke returned 0x%08x\n", hr);
737 VariantClear(&vararg[0]);
738 VariantClear(&varresult);
739
740 /* Provide the required BSTR and an empty second parameter. Proper
741 * installation package exists.
742 */
743 dispparams.cArgs = 2;
744 VariantInit(&vararg[1]);
745 V_VT(&vararg[1]) = VT_BSTR;
746 V_BSTR(&vararg[1]) = SysAllocString(path);
747 VariantInit(&vararg[0]);
748 hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
749 ok(hr == S_OK, "IDispatch::Invoke returned 0x%08x\n", hr);
750 VariantClear(&vararg[1]);
751 VariantClear(&varresult);
752
753 /* Provide the required BSTR and two empty parameters. Proper
754 * installation package exists.
755 */
756 dispparams.cArgs = 3;
757 VariantInit(&vararg[2]);
758 V_VT(&vararg[2]) = VT_BSTR;
759 V_BSTR(&vararg[2]) = SysAllocString(path);
760 VariantInit(&vararg[1]);
761 VariantInit(&vararg[0]);
762 hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
763 ok(hr == S_OK, "IDispatch::Invoke returned 0x%08x\n", hr);
764 VariantClear(&vararg[2]);
765 VariantClear(&varresult);
766
767 /* Provide the required BSTR and a second parameter with the wrong type. */
768 dispparams.cArgs = 2;
769 VariantInit(&vararg[1]);
770 V_VT(&vararg[1]) = VT_BSTR;
771 V_BSTR(&vararg[1]) = SysAllocString(path);
772 VariantInit(&vararg[0]);
773 V_VT(&vararg[0]) = VT_BSTR;
774 V_BSTR(&vararg[0]) = SysAllocString(path);
775 hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
776 ok(hr == DISP_E_TYPEMISMATCH, "IDispatch::Invoke returned 0x%08x\n", hr);
777 VariantClear(&vararg[0]);
778 VariantClear(&vararg[1]);
779
780 /* Provide the required BSTR and a second parameter that can be coerced to
781 * VT_I4.
782 */
783 dispparams.cArgs = 2;
784 VariantInit(&vararg[1]);
785 V_VT(&vararg[1]) = VT_BSTR;
786 V_BSTR(&vararg[1]) = SysAllocString(path);
787 VariantInit(&vararg[0]);
788 V_VT(&vararg[0]) = VT_I2;
789 V_BSTR(&vararg[0]) = 0;
790 hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
791 ok(hr == S_OK, "IDispatch::Invoke returned 0x%08x\n", hr);
792 VariantClear(&vararg[1]);
793 VariantClear(&varresult);
794
795 DeleteFileW(path);
796
797 /* Test invoking a method as a DISPATCH_PROPERTYGET or DISPATCH_PROPERTYPUT */
798 VariantInit(&vararg[0]);
799 hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYGET, &dispparams, &varresult, &excepinfo, NULL);
800 ok(hr == DISP_E_MEMBERNOTFOUND, "IDispatch::Invoke returned 0x%08x\n", hr);
801
802 VariantInit(&vararg[0]);
803 hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYPUT, &dispparams, &varresult, &excepinfo, NULL);
804 ok(hr == DISP_E_MEMBERNOTFOUND, "IDispatch::Invoke returned 0x%08x\n", hr);
805
806 /* Test invoking a read-only property as DISPATCH_PROPERTYPUT or as a DISPATCH_METHOD */
807 name = szProductState;
808 hr = IDispatch_GetIDsOfNames(pInstaller, &IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &dispid);
809 ok(hr == S_OK, "IDispatch::GetIDsOfNames returned 0x%08x\n", hr);
810
811 dispparams.rgvarg = NULL;
812 dispparams.cArgs = 0;
813 hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYPUT, &dispparams, &varresult, &excepinfo, NULL);
814 ok(hr == DISP_E_MEMBERNOTFOUND, "IDispatch::Invoke returned 0x%08x\n", hr);
815
816 dispparams.rgvarg = NULL;
817 dispparams.cArgs = 0;
818 hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL);
819 ok(hr == DISP_E_MEMBERNOTFOUND, "IDispatch::Invoke returned 0x%08x\n", hr);
820 }
821
822 /* invocation helper function */
823 static int _invoke_todo_vtResult = 0;
824
825 static HRESULT invoke(IDispatch *pDispatch, LPCSTR szName, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, VARTYPE vtResult)
826 {
827 OLECHAR *name = NULL;
828 DISPID dispid;
829 HRESULT hr;
830 UINT i;
831 UINT len;
832
833 memset(pVarResult, 0, sizeof(VARIANT));
834 VariantInit(pVarResult);
835
836 len = MultiByteToWideChar(CP_ACP, 0, szName, -1, NULL, 0 );
837 name = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR) );
838 if (!name) return E_FAIL;
839 len = MultiByteToWideChar(CP_ACP, 0, szName, -1, name, len );
840 hr = IDispatch_GetIDsOfNames(pDispatch, &IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &dispid);
841 HeapFree(GetProcessHeap(), 0, name);
842 ok(hr == S_OK, "IDispatch::GetIDsOfNames returned 0x%08x\n", hr);
843 if (!hr == S_OK) return hr;
844
845 memset(&excepinfo, 0, sizeof(excepinfo));
846 hr = IDispatch_Invoke(pDispatch, dispid, &IID_NULL, LOCALE_NEUTRAL, wFlags, pDispParams, pVarResult, &excepinfo, NULL);
847
848 if (hr == S_OK)
849 {
850 if (_invoke_todo_vtResult) todo_wine
851 ok(V_VT(pVarResult) == vtResult, "Variant result type is %d, expected %d\n", V_VT(pVarResult), vtResult);
852 else
853 ok(V_VT(pVarResult) == vtResult, "Variant result type is %d, expected %d\n", V_VT(pVarResult), vtResult);
854 if (vtResult != VT_EMPTY)
855 {
856 hr = VariantChangeTypeEx(pVarResult, pVarResult, LOCALE_NEUTRAL, 0, vtResult);
857 ok(hr == S_OK, "VariantChangeTypeEx returned 0x%08x\n", hr);
858 }
859 }
860
861 for (i=0; i<pDispParams->cArgs; i++)
862 VariantClear(&pDispParams->rgvarg[i]);
863
864 return hr;
865 }
866
867 /* Object_Property helper functions */
868
869 static HRESULT Installer_CreateRecord(int count, IDispatch **pRecord)
870 {
871 VARIANT varresult;
872 VARIANTARG vararg[1];
873 DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
874 HRESULT hr;
875
876 VariantInit(&vararg[0]);
877 V_VT(&vararg[0]) = VT_I4;
878 V_I4(&vararg[0]) = count;
879
880 hr = invoke(pInstaller, "CreateRecord", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH);
881 *pRecord = V_DISPATCH(&varresult);
882 return hr;
883 }
884
885 static HRESULT Installer_RegistryValue(HKEY hkey, LPCWSTR szKey, VARIANT vValue, VARIANT *pVarResult, VARTYPE vtExpect)
886 {
887 VARIANTARG vararg[3];
888 DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
889
890 VariantInit(&vararg[2]);
891 V_VT(&vararg[2]) = VT_I4;
892 V_I4(&vararg[2]) = (INT_PTR)hkey;
893 VariantInit(&vararg[1]);
894 V_VT(&vararg[1]) = VT_BSTR;
895 V_BSTR(&vararg[1]) = SysAllocString(szKey);
896 VariantInit(&vararg[0]);
897 VariantCopy(&vararg[0], &vValue);
898 VariantClear(&vValue);
899
900 return invoke(pInstaller, "RegistryValue", DISPATCH_METHOD, &dispparams, pVarResult, vtExpect);
901 }
902
903 static HRESULT Installer_RegistryValueE(HKEY hkey, LPCWSTR szKey, BOOL *pBool)
904 {
905 VARIANT varresult;
906 VARIANTARG vararg;
907 HRESULT hr;
908
909 VariantInit(&vararg);
910 V_VT(&vararg) = VT_EMPTY;
911 hr = Installer_RegistryValue(hkey, szKey, vararg, &varresult, VT_BOOL);
912 *pBool = V_BOOL(&varresult);
913 VariantClear(&varresult);
914 return hr;
915 }
916
917 static HRESULT Installer_RegistryValueW(HKEY hkey, LPCWSTR szKey, LPCWSTR szValue, LPWSTR szString)
918 {
919 VARIANT varresult;
920 VARIANTARG vararg;
921 HRESULT hr;
922
923 VariantInit(&vararg);
924 V_VT(&vararg) = VT_BSTR;
925 V_BSTR(&vararg) = SysAllocString(szValue);
926
927 hr = Installer_RegistryValue(hkey, szKey, vararg, &varresult, VT_BSTR);
928 if (V_BSTR(&varresult))
929 /* lstrcpyW is not implemented on Win95 (lstrlenW is though) */
930 memcpy(szString, V_BSTR(&varresult), (lstrlenW(V_BSTR(&varresult)) + 1) * sizeof(WCHAR));
931 VariantClear(&varresult);
932 return hr;
933 }
934
935 static HRESULT Installer_RegistryValueI(HKEY hkey, LPCWSTR szKey, int iValue, LPWSTR szString, VARTYPE vtResult)
936 {
937 VARIANT varresult;
938 VARIANTARG vararg;
939 HRESULT hr;
940
941 VariantInit(&vararg);
942 V_VT(&vararg) = VT_I4;
943 V_I4(&vararg) = iValue;
944
945 hr = Installer_RegistryValue(hkey, szKey, vararg, &varresult, vtResult);
946 if (SUCCEEDED(hr) && vtResult == VT_BSTR)
947 memcpy(szString, V_BSTR(&varresult), (lstrlenW(V_BSTR(&varresult)) + 1) * sizeof(WCHAR));
948 VariantClear(&varresult);
949 return hr;
950 }
951
952 static HRESULT Installer_OpenPackage(LPCWSTR szPackagePath, int options, IDispatch **pSession)
953 {
954 VARIANT varresult;
955 VARIANTARG vararg[2];
956 DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
957 HRESULT hr;
958
959 VariantInit(&vararg[1]);
960 V_VT(&vararg[1]) = VT_BSTR;
961 V_BSTR(&vararg[1]) = SysAllocString(szPackagePath);
962 VariantInit(&vararg[0]);
963 V_VT(&vararg[0]) = VT_I4;
964 V_I4(&vararg[0]) = options;
965
966 hr = invoke(pInstaller, "OpenPackage", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH);
967 *pSession = V_DISPATCH(&varresult);
968 return hr;
969 }
970
971 static HRESULT Installer_OpenDatabase(LPCWSTR szDatabasePath, int openmode, IDispatch **pDatabase)
972 {
973 VARIANT varresult;
974 VARIANTARG vararg[2];
975 DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
976 HRESULT hr;
977
978 VariantInit(&vararg[1]);
979 V_VT(&vararg[1]) = VT_BSTR;
980 V_BSTR(&vararg[1]) = SysAllocString(szDatabasePath);
981 VariantInit(&vararg[0]);
982 V_VT(&vararg[0]) = VT_I4;
983 V_I4(&vararg[0]) = openmode;
984
985 hr = invoke(pInstaller, "OpenDatabase", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH);
986 *pDatabase = V_DISPATCH(&varresult);
987 return hr;
988 }
989
990 static HRESULT Installer_InstallProduct(LPCWSTR szPackagePath, LPCWSTR szPropertyValues)
991 {
992 VARIANT varresult;
993 VARIANTARG vararg[2];
994 DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
995
996 VariantInit(&vararg[1]);
997 V_VT(&vararg[1]) = VT_BSTR;
998 V_BSTR(&vararg[1]) = SysAllocString(szPackagePath);
999 VariantInit(&vararg[0]);
1000 V_VT(&vararg[0]) = VT_BSTR;
1001 V_BSTR(&vararg[0]) = SysAllocString(szPropertyValues);
1002
1003 return invoke(pInstaller, "InstallProduct", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY);
1004 }
1005
1006 static HRESULT Installer_ProductState(LPCWSTR szProduct, int *pInstallState)
1007 {
1008 VARIANT varresult;
1009 VARIANTARG vararg[1];
1010 DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1011 HRESULT hr;
1012
1013 VariantInit(&vararg[0]);
1014 V_VT(&vararg[0]) = VT_BSTR;
1015 V_BSTR(&vararg[0]) = SysAllocString(szProduct);
1016
1017 hr = invoke(pInstaller, "ProductState", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1018 *pInstallState = V_I4(&varresult);
1019 VariantClear(&varresult);
1020 return hr;
1021 }
1022
1023 static HRESULT Installer_ProductInfo(LPCWSTR szProduct, LPCWSTR szAttribute, LPWSTR szString)
1024 {
1025 VARIANT varresult;
1026 VARIANTARG vararg[2];
1027 DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1028 HRESULT hr;
1029
1030 VariantInit(&vararg[1]);
1031 V_VT(&vararg[1]) = VT_BSTR;
1032 V_BSTR(&vararg[1]) = SysAllocString(szProduct);
1033 VariantInit(&vararg[0]);
1034 V_VT(&vararg[0]) = VT_BSTR;
1035 V_BSTR(&vararg[0]) = SysAllocString(szAttribute);
1036
1037 hr = invoke(pInstaller, "ProductInfo", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR);
1038 if (V_BSTR(&varresult))
1039 memcpy(szString, V_BSTR(&varresult), (lstrlenW(V_BSTR(&varresult)) + 1) * sizeof(WCHAR));
1040 VariantClear(&varresult);
1041 return hr;
1042 }
1043
1044 static HRESULT Installer_Products(IDispatch **pStringList)
1045 {
1046 VARIANT varresult;
1047 DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1048 HRESULT hr;
1049
1050 hr = invoke(pInstaller, "Products", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH);
1051 *pStringList = V_DISPATCH(&varresult);
1052 return hr;
1053 }
1054
1055 static HRESULT Installer_RelatedProducts(LPCWSTR szProduct, IDispatch **pStringList)
1056 {
1057 VARIANT varresult;
1058 VARIANTARG vararg[1];
1059 DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1060 HRESULT hr;
1061
1062 VariantInit(&vararg[0]);
1063 V_VT(&vararg[0]) = VT_BSTR;
1064 V_BSTR(&vararg[0]) = SysAllocString(szProduct);
1065
1066 hr = invoke(pInstaller, "RelatedProducts", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH);
1067 *pStringList = V_DISPATCH(&varresult);
1068 return hr;
1069 }
1070
1071 static HRESULT Installer_VersionGet(LPWSTR szVersion)
1072 {
1073 VARIANT varresult;
1074 DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1075 HRESULT hr;
1076
1077 hr = invoke(pInstaller, "Version", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR);
1078 if (V_BSTR(&varresult))
1079 memcpy(szVersion, V_BSTR(&varresult), (lstrlenW(V_BSTR(&varresult)) + 1) * sizeof(WCHAR));
1080 VariantClear(&varresult);
1081 return hr;
1082 }
1083
1084 static HRESULT Session_Installer(IDispatch *pSession, IDispatch **pInst)
1085 {
1086 VARIANT varresult;
1087 DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1088 HRESULT hr;
1089
1090 hr = invoke(pSession, "Installer", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH);
1091 *pInst = V_DISPATCH(&varresult);
1092 return hr;
1093 }
1094
1095 static HRESULT Session_PropertyGet(IDispatch *pSession, LPCWSTR szName, LPWSTR szReturn)
1096 {
1097 VARIANT varresult;
1098 VARIANTARG vararg[1];
1099 DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1100 HRESULT hr;
1101
1102 VariantInit(&vararg[0]);
1103 V_VT(&vararg[0]) = VT_BSTR;
1104 V_BSTR(&vararg[0]) = SysAllocString(szName);
1105
1106 hr = invoke(pSession, "Property", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR);
1107 if (V_BSTR(&varresult))
1108 memcpy(szReturn, V_BSTR(&varresult), (lstrlenW(V_BSTR(&varresult)) + 1) * sizeof(WCHAR));
1109 VariantClear(&varresult);
1110 return hr;
1111 }
1112
1113 static HRESULT Session_PropertyPut(IDispatch *pSession, LPCWSTR szName, LPCWSTR szValue)
1114 {
1115 VARIANT varresult;
1116 VARIANTARG vararg[2];
1117 DISPID dispid = DISPID_PROPERTYPUT;
1118 DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};
1119
1120 VariantInit(&vararg[1]);
1121 V_VT(&vararg[1]) = VT_BSTR;
1122 V_BSTR(&vararg[1]) = SysAllocString(szName);
1123 VariantInit(&vararg[0]);
1124 V_VT(&vararg[0]) = VT_BSTR;
1125 V_BSTR(&vararg[0]) = SysAllocString(szValue);
1126
1127 return invoke(pSession, "Property", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
1128 }
1129
1130 static HRESULT Session_LanguageGet(IDispatch *pSession, UINT *pLangId)
1131 {
1132 VARIANT varresult;
1133 DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1134 HRESULT hr;
1135
1136 hr = invoke(pSession, "Language", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1137 *pLangId = V_I4(&varresult);
1138 VariantClear(&varresult);
1139 return hr;
1140 }
1141
1142 static HRESULT Session_ModeGet(IDispatch *pSession, int iFlag, BOOL *pMode)
1143 {
1144 VARIANT varresult;
1145 VARIANTARG vararg[1];
1146 DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1147 HRESULT hr;
1148
1149 VariantInit(&vararg[0]);
1150 V_VT(&vararg[0]) = VT_I4;
1151 V_I4(&vararg[0]) = iFlag;
1152
1153 hr = invoke(pSession, "Mode", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BOOL);
1154 *pMode = V_BOOL(&varresult);
1155 VariantClear(&varresult);
1156 return hr;
1157 }
1158
1159 static HRESULT Session_ModePut(IDispatch *pSession, int iFlag, BOOL bMode)
1160 {
1161 VARIANT varresult;
1162 VARIANTARG vararg[2];
1163 DISPID dispid = DISPID_PROPERTYPUT;
1164 DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};
1165
1166 VariantInit(&vararg[1]);
1167 V_VT(&vararg[1]) = VT_I4;
1168 V_I4(&vararg[1]) = iFlag;
1169 VariantInit(&vararg[0]);
1170 V_VT(&vararg[0]) = VT_BOOL;
1171 V_BOOL(&vararg[0]) = bMode;
1172
1173 return invoke(pSession, "Mode", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
1174 }
1175
1176 static HRESULT Session_Database(IDispatch *pSession, IDispatch **pDatabase)
1177 {
1178 VARIANT varresult;
1179 DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1180 HRESULT hr;
1181
1182 hr = invoke(pSession, "Database", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH);
1183 *pDatabase = V_DISPATCH(&varresult);
1184 return hr;
1185 }
1186
1187 static HRESULT Session_DoAction(IDispatch *pSession, LPCWSTR szAction, int *iReturn)
1188 {
1189 VARIANT varresult;
1190 VARIANTARG vararg[1];
1191 DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1192 HRESULT hr;
1193
1194 VariantInit(&vararg[0]);
1195 V_VT(&vararg[0]) = VT_BSTR;
1196 V_BSTR(&vararg[0]) = SysAllocString(szAction);
1197
1198 hr = invoke(pSession, "DoAction", DISPATCH_METHOD, &dispparams, &varresult, VT_I4);
1199 *iReturn = V_I4(&varresult);
1200 VariantClear(&varresult);
1201 return hr;
1202 }
1203
1204 static HRESULT Session_EvaluateCondition(IDispatch *pSession, LPCWSTR szCondition, int *iReturn)
1205 {
1206 VARIANT varresult;
1207 VARIANTARG vararg[1];
1208 DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1209 HRESULT hr;
1210
1211 VariantInit(&vararg[0]);
1212 V_VT(&vararg[0]) = VT_BSTR;
1213 V_BSTR(&vararg[0]) = SysAllocString(szCondition);
1214
1215 hr = invoke(pSession, "EvaluateCondition", DISPATCH_METHOD, &dispparams, &varresult, VT_I4);
1216 *iReturn = V_I4(&varresult);
1217 VariantClear(&varresult);
1218 return hr;
1219 }
1220
1221 static HRESULT Session_Message(IDispatch *pSession, LONG kind, IDispatch *record, int *ret)
1222 {
1223 VARIANT varresult;
1224 VARIANTARG vararg[2];
1225 DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1226 HRESULT hr;
1227
1228 VariantInit(&varresult);
1229 V_VT(vararg) = VT_DISPATCH;
1230 V_DISPATCH(vararg) = record;
1231 V_VT(vararg+1) = VT_I4;
1232 V_I4(vararg+1) = kind;
1233
1234 hr = invoke(pSession, "Message", DISPATCH_METHOD, &dispparams, &varresult, VT_I4);
1235
1236 ok(V_VT(&varresult) == VT_I4, "V_VT(varresult) = %d\n", V_VT(&varresult));
1237 *ret = V_I4(&varresult);
1238
1239 return hr;
1240 }
1241
1242 static HRESULT Session_SetInstallLevel(IDispatch *pSession, LONG iInstallLevel)
1243 {
1244 VARIANT varresult;
1245 VARIANTARG vararg[1];
1246 DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1247
1248 VariantInit(&vararg[0]);
1249 V_VT(&vararg[0]) = VT_I4;
1250 V_I4(&vararg[0]) = iInstallLevel;
1251
1252 return invoke(pSession, "SetInstallLevel", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY);
1253 }
1254
1255 static HRESULT Session_FeatureCurrentState(IDispatch *pSession, LPCWSTR szName, int *pState)
1256 {
1257 VARIANT varresult;
1258 VARIANTARG vararg[1];
1259 DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1260 HRESULT hr;
1261
1262 VariantInit(&vararg[0]);
1263 V_VT(&vararg[0]) = VT_BSTR;
1264 V_BSTR(&vararg[0]) = SysAllocString(szName);
1265
1266 hr = invoke(pSession, "FeatureCurrentState", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1267 *pState = V_I4(&varresult);
1268 VariantClear(&varresult);
1269 return hr;
1270 }
1271
1272 static HRESULT Session_FeatureRequestStateGet(IDispatch *pSession, LPCWSTR szName, int *pState)
1273 {
1274 VARIANT varresult;
1275 VARIANTARG vararg[1];
1276 DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1277 HRESULT hr;
1278
1279 VariantInit(&vararg[0]);
1280 V_VT(&vararg[0]) = VT_BSTR;
1281 V_BSTR(&vararg[0]) = SysAllocString(szName);
1282
1283 hr = invoke(pSession, "FeatureRequestState", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1284 *pState = V_I4(&varresult);
1285 VariantClear(&varresult);
1286 return hr;
1287 }
1288
1289 static HRESULT Session_FeatureRequestStatePut(IDispatch *pSession, LPCWSTR szName, int iState)
1290 {
1291 VARIANT varresult;
1292 VARIANTARG vararg[2];
1293 DISPID dispid = DISPID_PROPERTYPUT;
1294 DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};
1295
1296 VariantInit(&vararg[1]);
1297 V_VT(&vararg[1]) = VT_BSTR;
1298 V_BSTR(&vararg[1]) = SysAllocString(szName);
1299 VariantInit(&vararg[0]);
1300 V_VT(&vararg[0]) = VT_I4;
1301 V_I4(&vararg[0]) = iState;
1302
1303 return invoke(pSession, "FeatureRequestState", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
1304 }
1305
1306 static HRESULT Database_OpenView(IDispatch *pDatabase, LPCWSTR szSql, IDispatch **pView)
1307 {
1308 VARIANT varresult;
1309 VARIANTARG vararg[1];
1310 DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1311 HRESULT hr;
1312
1313 VariantInit(&vararg[0]);
1314 V_VT(&vararg[0]) = VT_BSTR;
1315 V_BSTR(&vararg[0]) = SysAllocString(szSql);
1316
1317 hr = invoke(pDatabase, "OpenView", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH);
1318 *pView = V_DISPATCH(&varresult);
1319 return hr;
1320 }
1321
1322 static HRESULT Database_SummaryInformation(IDispatch *pDatabase, int iUpdateCount, IDispatch **pSummaryInfo)
1323 {
1324 VARIANT varresult;
1325 VARIANTARG vararg[1];
1326 DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1327 HRESULT hr;
1328
1329 VariantInit(&vararg[0]);
1330 V_VT(&vararg[0]) = VT_I4;
1331 V_I4(&vararg[0]) = iUpdateCount;
1332
1333 hr = invoke(pDatabase, "SummaryInformation", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH);
1334 *pSummaryInfo = V_DISPATCH(&varresult);
1335 return hr;
1336 }
1337
1338 static HRESULT View_Execute(IDispatch *pView, IDispatch *pRecord)
1339 {
1340 VARIANT varresult;
1341 VARIANTARG vararg[1];
1342 DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1343
1344 VariantInit(&vararg[0]);
1345 V_VT(&vararg[0]) = VT_DISPATCH;
1346 V_DISPATCH(&vararg[0]) = pRecord;
1347
1348 return invoke(pView, "Execute", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY);
1349 }
1350
1351 static HRESULT View_Fetch(IDispatch *pView, IDispatch **ppRecord)
1352 {
1353 VARIANT varresult;
1354 DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1355 HRESULT hr = invoke(pView, "Fetch", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH);
1356 *ppRecord = V_DISPATCH(&varresult);
1357 return hr;
1358 }
1359
1360 static HRESULT View_Modify(IDispatch *pView, int iMode, IDispatch *pRecord)
1361 {
1362 VARIANT varresult;
1363 VARIANTARG vararg[2];
1364 DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1365
1366 VariantInit(&vararg[1]);
1367 V_VT(&vararg[1]) = VT_I4;
1368 V_I4(&vararg[1]) = iMode;
1369 VariantInit(&vararg[0]);
1370 V_VT(&vararg[0]) = VT_DISPATCH;
1371 V_DISPATCH(&vararg[0]) = pRecord;
1372 if (pRecord)
1373 IDispatch_AddRef(pRecord); /* VariantClear in invoke will call IDispatch_Release */
1374
1375 return invoke(pView, "Modify", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY);
1376 }
1377
1378 static HRESULT View_Close(IDispatch *pView)
1379 {
1380 VARIANT varresult;
1381 DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1382 return invoke(pView, "Close", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY);
1383 }
1384
1385 static HRESULT Record_FieldCountGet(IDispatch *pRecord, int *pFieldCount)
1386 {
1387 VARIANT varresult;
1388 DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1389 HRESULT hr = invoke(pRecord, "FieldCount", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1390 *pFieldCount = V_I4(&varresult);
1391 VariantClear(&varresult);
1392 return hr;
1393 }
1394
1395 static HRESULT Record_StringDataGet(IDispatch *pRecord, int iField, LPWSTR szString)
1396 {
1397 VARIANT varresult;
1398 VARIANTARG vararg[1];
1399 DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1400 HRESULT hr;
1401
1402 VariantInit(&vararg[0]);
1403 V_VT(&vararg[0]) = VT_I4;
1404 V_I4(&vararg[0]) = iField;
1405
1406 hr = invoke(pRecord, "StringData", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR);
1407 if (V_BSTR(&varresult))
1408 memcpy(szString, V_BSTR(&varresult), (lstrlenW(V_BSTR(&varresult)) + 1) * sizeof(WCHAR));
1409 VariantClear(&varresult);
1410 return hr;
1411 }
1412
1413 static HRESULT Record_StringDataPut(IDispatch *pRecord, int iField, LPCWSTR szString)
1414 {
1415 VARIANT varresult;
1416 VARIANTARG vararg[2];
1417 DISPID dispid = DISPID_PROPERTYPUT;
1418 DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};
1419
1420 VariantInit(&vararg[1]);
1421 V_VT(&vararg[1]) = VT_I4;
1422 V_I4(&vararg[1]) = iField;
1423 VariantInit(&vararg[0]);
1424 V_VT(&vararg[0]) = VT_BSTR;
1425 V_BSTR(&vararg[0]) = SysAllocString(szString);
1426
1427 return invoke(pRecord, "StringData", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
1428 }
1429
1430 static HRESULT Record_IntegerDataGet(IDispatch *pRecord, int iField, int *pValue)
1431 {
1432 VARIANT varresult;
1433 VARIANTARG vararg[1];
1434 DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1435 HRESULT hr;
1436
1437 VariantInit(&vararg[0]);
1438 V_VT(&vararg[0]) = VT_I4;
1439 V_I4(&vararg[0]) = iField;
1440
1441 hr = invoke(pRecord, "IntegerData", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1442 *pValue = V_I4(&varresult);
1443 VariantClear(&varresult);
1444 return hr;
1445 }
1446
1447 static HRESULT Record_IntegerDataPut(IDispatch *pRecord, int iField, int iValue)
1448 {
1449 VARIANT varresult;
1450 VARIANTARG vararg[2];
1451 DISPID dispid = DISPID_PROPERTYPUT;
1452 DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};
1453
1454 VariantInit(&vararg[1]);
1455 V_VT(&vararg[1]) = VT_I4;
1456 V_I4(&vararg[1]) = iField;
1457 VariantInit(&vararg[0]);
1458 V_VT(&vararg[0]) = VT_I4;
1459 V_I4(&vararg[0]) = iValue;
1460
1461 return invoke(pRecord, "IntegerData", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
1462 }
1463
1464 static HRESULT StringList__NewEnum(IDispatch *pList, IUnknown **ppEnumVARIANT)
1465 {
1466 VARIANT varresult;
1467 DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1468 HRESULT hr = invoke(pList, "_NewEnum", DISPATCH_METHOD, &dispparams, &varresult, VT_UNKNOWN);
1469 *ppEnumVARIANT = V_UNKNOWN(&varresult);
1470 return hr;
1471 }
1472
1473 static HRESULT StringList_Item(IDispatch *pStringList, int iIndex, LPWSTR szString)
1474 {
1475 VARIANT varresult;
1476 VARIANTARG vararg[1];
1477 DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1478 HRESULT hr;
1479
1480 VariantInit(&vararg[0]);
1481 V_VT(&vararg[0]) = VT_I4;
1482 V_I4(&vararg[0]) = iIndex;
1483
1484 hr = invoke(pStringList, "Item", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR);
1485 if (V_BSTR(&varresult))
1486 memcpy(szString, V_BSTR(&varresult), (lstrlenW(V_BSTR(&varresult)) + 1) * sizeof(WCHAR));
1487 VariantClear(&varresult);
1488 return hr;
1489 }
1490
1491 static HRESULT StringList_Count(IDispatch *pStringList, int *pCount)
1492 {
1493 VARIANT varresult;
1494 DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1495 HRESULT hr = invoke(pStringList, "Count", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1496 *pCount = V_I4(&varresult);
1497 VariantClear(&varresult);
1498 return hr;
1499 }
1500
1501 static HRESULT SummaryInfo_PropertyGet(IDispatch *pSummaryInfo, int pid, VARIANT *pVarResult, VARTYPE vtExpect)
1502 {
1503 VARIANTARG vararg[1];
1504 DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
1505
1506 VariantInit(&vararg[0]);
1507 V_VT(&vararg[0]) = VT_I4;
1508 V_I4(&vararg[0]) = pid;
1509 return invoke(pSummaryInfo, "Property", DISPATCH_PROPERTYGET, &dispparams, pVarResult, vtExpect);
1510 }
1511
1512 static HRESULT SummaryInfo_PropertyPut(IDispatch *pSummaryInfo, int pid, VARIANT *pVariant)
1513 {
1514 VARIANT varresult;
1515 VARIANTARG vararg[2];
1516 DISPID dispid = DISPID_PROPERTYPUT;
1517 DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1};
1518
1519 VariantInit(&vararg[1]);
1520 V_VT(&vararg[1]) = VT_I4;
1521 V_I4(&vararg[1]) = pid;
1522 VariantInit(&vararg[0]);
1523 VariantCopyInd(vararg, pVariant);
1524
1525 return invoke(pSummaryInfo, "Property", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY);
1526 }
1527
1528 static HRESULT SummaryInfo_PropertyCountGet(IDispatch *pSummaryInfo, int *pCount)
1529 {
1530 VARIANT varresult;
1531 DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1532 HRESULT hr;
1533
1534 hr = invoke(pSummaryInfo, "PropertyCount", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4);
1535 *pCount = V_I4(&varresult);
1536 VariantClear(&varresult);
1537 return hr;
1538 }
1539
1540 /* Test the various objects */
1541
1542 #define TEST_SUMMARYINFO_PROPERTIES_MODIFIED 4
1543
1544 static void test_SummaryInfo(IDispatch *pSummaryInfo, const msi_summary_info *info, int num_info, BOOL readonly)
1545 {
1546 static const WCHAR szPropertyException[] = { 'P','r','o','p','e','r','t','y',',','P','i','d',0 };
1547 static const WCHAR szTitle[] = { 'T','i','t','l','e',0 };
1548 VARIANT varresult, var;
1549 SYSTEMTIME st;
1550 HRESULT hr;
1551 int j;
1552
1553 /* SummaryInfo::PropertyCount */
1554 hr = SummaryInfo_PropertyCountGet(pSummaryInfo, &j);
1555 ok(hr == S_OK, "SummaryInfo_PropertyCount failed, hresult 0x%08x\n", hr);
1556 ok(j == num_info, "SummaryInfo_PropertyCount returned %d, expected %d\n", j, num_info);
1557
1558 /* SummaryInfo::Property, get for properties we have set */
1559 for (j = 0; j < num_info; j++)
1560 {
1561 const msi_summary_info *entry = &info[j];
1562
1563 int vt = entry->datatype;
1564 if (vt == VT_LPSTR) vt = VT_BSTR;
1565 else if (vt == VT_FILETIME) vt = VT_DATE;
1566 else if (vt == VT_I2) vt = VT_I4;
1567
1568 hr = SummaryInfo_PropertyGet(pSummaryInfo, entry->property, &varresult, vt);
1569 ok(hr == S_OK, "SummaryInfo_Property (pid %d) failed, hresult 0x%08x\n", entry->property, hr);
1570 if (V_VT(&varresult) != vt)
1571 skip("Skipping property tests due to type mismatch\n");
1572 else if (vt == VT_I4)
1573 ok(V_I4(&varresult) == entry->iValue, "SummaryInfo_Property (pid %d) I4 result expected to be %d, but was %d\n",
1574 entry->property, entry->iValue, V_I4(&varresult));
1575 else if (vt == VT_DATE)
1576 {
1577 FILETIME ft;
1578 DATE d;
1579
1580 FileTimeToLocalFileTime(entry->pftValue, &ft);
1581 FileTimeToSystemTime(&ft, &st);
1582 SystemTimeToVariantTime(&st, &d);
1583 ok(d == V_DATE(&varresult), "SummaryInfo_Property (pid %d) DATE result expected to be %lf, but was %lf\n", entry->property, d, V_DATE(&varresult));
1584 }
1585 else if (vt == VT_BSTR)
1586 {
1587 ok_awplus("SummaryInfo_Property (pid %d) BSTR result expected to be %s, but was %s\n", entry->property, entry->szValue, V_BSTR(&varresult));
1588 }
1589 else
1590 skip("SummaryInfo_Property (pid %d) unhandled result type %d\n", entry->property, vt);
1591
1592 VariantClear(&varresult);
1593 }
1594
1595 /* SummaryInfo::Property, get; invalid arguments */
1596
1597 /* Invalid pids */
1598 hr = SummaryInfo_PropertyGet(pSummaryInfo, -1, &varresult, VT_EMPTY);
1599 ok(hr == DISP_E_EXCEPTION, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1600 ok_exception(hr, szPropertyException);
1601
1602 hr = SummaryInfo_PropertyGet(pSummaryInfo, 1000, &varresult, VT_EMPTY);
1603 ok(hr == DISP_E_EXCEPTION, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1604 ok_exception(hr, szPropertyException);
1605
1606 /* Unsupported pids */
1607 hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_DICTIONARY, &varresult, VT_EMPTY);
1608 ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1609
1610 hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_THUMBNAIL, &varresult, VT_EMPTY);
1611 ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1612
1613 /* Pids we have not set, one for each type */
1614 hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_CODEPAGE, &varresult, VT_EMPTY);
1615 ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1616
1617 hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_TITLE, &varresult, VT_EMPTY);
1618 ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1619
1620 hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_EDITTIME, &varresult, VT_EMPTY);
1621 ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1622
1623 hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_CHARCOUNT, &varresult, VT_EMPTY);
1624 ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1625
1626 if (!readonly)
1627 {
1628 /* SummaryInfo::Property, put; one for each type */
1629
1630 /* VT_I2 */
1631 VariantInit(&var);
1632 V_VT(&var) = VT_I2;
1633 V_I2(&var) = 1;
1634 hr = SummaryInfo_PropertyPut(pSummaryInfo, PID_CODEPAGE, &var);
1635 ok(hr == S_OK, "SummaryInfo_PropertyPut failed, hresult 0x%08x\n", hr);
1636
1637 hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_CODEPAGE, &varresult, VT_I4 /* NOT VT_I2 */);
1638 ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1639 ok(V_I2(&var) == V_I2(&varresult), "SummaryInfo_PropertyGet expected %d, but returned %d\n", V_I2(&var), V_I2(&varresult));
1640 VariantClear(&varresult);
1641 VariantClear(&var);
1642
1643 /* VT_BSTR */
1644 V_VT(&var) = VT_BSTR;
1645 V_BSTR(&var) = SysAllocString(szTitle);
1646 hr = SummaryInfo_PropertyPut(pSummaryInfo, PID_TITLE, &var);
1647 ok(hr == S_OK, "SummaryInfo_PropertyPut failed, hresult 0x%08x\n", hr);
1648
1649 hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_TITLE, &varresult, V_VT(&var));
1650 ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1651 ok_w2("SummaryInfo_PropertyGet expected %s, but returned %s\n", V_BSTR(&var), V_BSTR(&varresult));
1652 VariantClear(&varresult);
1653 VariantClear(&var);
1654
1655 /* VT_DATE */
1656 V_VT(&var) = VT_DATE;
1657 FileTimeToSystemTime(&systemtime, &st);
1658 SystemTimeToVariantTime(&st, &V_DATE(&var));
1659 hr = SummaryInfo_PropertyPut(pSummaryInfo, PID_LASTSAVE_DTM, &var);
1660 ok(hr == S_OK, "SummaryInfo_PropertyPut failed, hresult 0x%08x\n", hr);
1661
1662 hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_LASTSAVE_DTM, &varresult, V_VT(&var));
1663 ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1664 ok(V_DATE(&var) == V_DATE(&varresult), "SummaryInfo_PropertyGet expected %lf, but returned %lf\n", V_DATE(&var), V_DATE(&varresult));
1665 VariantClear(&varresult);
1666 VariantClear(&var);
1667
1668 /* VT_I4 */
1669 V_VT(&var) = VT_I4;
1670 V_I4(&var) = 1000;
1671 hr = SummaryInfo_PropertyPut(pSummaryInfo, PID_CHARCOUNT, &var);
1672 ok(hr == S_OK, "SummaryInfo_PropertyPut failed, hresult 0x%08x\n", hr);
1673
1674 hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_CHARCOUNT, &varresult, V_VT(&var));
1675 ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr);
1676 ok(V_I4(&var) == V_I4(&varresult), "SummaryInfo_PropertyGet expected %d, but returned %d\n", V_I4(&var), V_I4(&varresult));
1677 VariantClear(&varresult);
1678 VariantClear(&var);
1679
1680 /* SummaryInfo::PropertyCount */
1681 hr = SummaryInfo_PropertyCountGet(pSummaryInfo, &j);
1682 ok(hr == S_OK, "SummaryInfo_PropertyCount failed, hresult 0x%08x\n", hr);
1683 ok(j == num_info+4, "SummaryInfo_PropertyCount returned %d, expected %d\n", j, num_info);
1684 }
1685 }
1686
1687 static void test_Database(IDispatch *pDatabase, BOOL readonly)
1688 {
1689 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 };
1690 static WCHAR szThree[] = { 'T','h','r','e','e',0 };
1691 static WCHAR szTwo[] = { 'T','w','o',0 };
1692 static WCHAR szStringDataField[] = { 'S','t','r','i','n','g','D','a','t','a',',','F','i','e','l','d',0 };
1693 static WCHAR szModifyModeRecord[] = { 'M','o','d','i','f','y',',','M','o','d','e',',','R','e','c','o','r','d',0 };
1694 IDispatch *pView = NULL, *pSummaryInfo = NULL;
1695 HRESULT hr;
1696
1697 hr = Database_OpenView(pDatabase, szSql, &pView);
1698 ok(hr == S_OK, "Database_OpenView failed, hresult 0x%08x\n", hr);
1699 if (hr == S_OK)
1700 {
1701 IDispatch *pRecord = NULL;
1702 WCHAR szString[MAX_PATH];
1703
1704 /* View::Execute */
1705 hr = View_Execute(pView, NULL);
1706 ok(hr == S_OK, "View_Execute failed, hresult 0x%08x\n", hr);
1707
1708 /* View::Fetch */
1709 hr = View_Fetch(pView, &pRecord);
1710 ok(hr == S_OK, "View_Fetch failed, hresult 0x%08x\n", hr);
1711 ok(pRecord != NULL, "View_Fetch should not have returned NULL record\n");
1712 if (pRecord)
1713 {
1714 /* Record::StringDataGet */
1715 memset(szString, 0, sizeof(szString));
1716 hr = Record_StringDataGet(pRecord, 1, szString);
1717 ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr);
1718 ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szThree);
1719
1720 /* Record::StringDataPut with correct index */
1721 hr = Record_StringDataPut(pRecord, 1, szTwo);
1722 ok(hr == S_OK, "Record_StringDataPut failed, hresult 0x%08x\n", hr);
1723
1724 /* Record::StringDataGet */
1725 memset(szString, 0, sizeof(szString));
1726 hr = Record_StringDataGet(pRecord, 1, szString);
1727 ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr);
1728 ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szTwo);
1729
1730 /* Record::StringDataPut with incorrect index */
1731 hr = Record_StringDataPut(pRecord, -1, szString);
1732 ok(hr == DISP_E_EXCEPTION, "Record_StringDataPut failed, hresult 0x%08x\n", hr);
1733 ok_exception(hr, szStringDataField);
1734
1735 /* View::Modify with incorrect parameters */
1736 hr = View_Modify(pView, -5, NULL);
1737 ok(hr == DISP_E_EXCEPTION, "View_Modify failed, hresult 0x%08x\n", hr);
1738 ok_exception(hr, szModifyModeRecord);
1739
1740 hr = View_Modify(pView, -5, pRecord);
1741 ok(hr == DISP_E_EXCEPTION, "View_Modify failed, hresult 0x%08x\n", hr);
1742 ok_exception(hr, szModifyModeRecord);
1743
1744 hr = View_Modify(pView, MSIMODIFY_REFRESH, NULL);
1745 ok(hr == DISP_E_EXCEPTION, "View_Modify failed, hresult 0x%08x\n", hr);
1746 ok_exception(hr, szModifyModeRecord);
1747
1748 hr = View_Modify(pView, MSIMODIFY_REFRESH, pRecord);
1749 ok(hr == S_OK, "View_Modify failed, hresult 0x%08x\n", hr);
1750
1751 /* Record::StringDataGet, confirm that the record is back to its unmodified value */
1752 memset(szString, 0, sizeof(szString));
1753 hr = Record_StringDataGet(pRecord, 1, szString);
1754 ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr);
1755 todo_wine ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szThree);
1756
1757 IDispatch_Release(pRecord);
1758 }
1759
1760 /* View::Fetch */
1761 hr = View_Fetch(pView, &pRecord);
1762 ok(hr == S_OK, "View_Fetch failed, hresult 0x%08x\n", hr);
1763 ok(pRecord != NULL, "View_Fetch should not have returned NULL record\n");
1764 if (pRecord)
1765 {
1766 /* Record::StringDataGet */
1767 memset(szString, 0, sizeof(szString));
1768 hr = Record_StringDataGet(pRecord, 1, szString);
1769 ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr);
1770 ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szTwo);
1771
1772 IDispatch_Release(pRecord);
1773 }
1774
1775 /* View::Fetch */
1776 hr = View_Fetch(pView, &pRecord);
1777 ok(hr == S_OK, "View_Fetch failed, hresult 0x%08x\n", hr);
1778 ok(pRecord == NULL, "View_Fetch should have returned NULL record\n");
1779 if (pRecord)
1780 IDispatch_Release(pRecord);
1781
1782 /* View::Close */
1783 hr = View_Close(pView);
1784 ok(hr == S_OK, "View_Close failed, hresult 0x%08x\n", hr);
1785
1786 IDispatch_Release(pView);
1787 }
1788
1789 /* Database::SummaryInformation */
1790 hr = Database_SummaryInformation(pDatabase, TEST_SUMMARYINFO_PROPERTIES_MODIFIED, &pSummaryInfo);
1791 ok(hr == S_OK, "Database_SummaryInformation failed, hresult 0x%08x\n", hr);
1792 ok(pSummaryInfo != NULL, "Database_SummaryInformation should not have returned NULL record\n");
1793 if (pSummaryInfo)
1794 {
1795 test_SummaryInfo(pSummaryInfo, summary_info, sizeof(summary_info)/sizeof(msi_summary_info), readonly);
1796 IDispatch_Release(pSummaryInfo);
1797 }
1798 }
1799
1800 static void test_Session(IDispatch *pSession)
1801 {
1802 static WCHAR szProductName[] = { 'P','r','o','d','u','c','t','N','a','m','e',0 };
1803 static WCHAR szOne[] = { 'O','n','e',0 };
1804 static WCHAR szOneStateFalse[] = { '!','O','n','e','>','0',0 };
1805 static WCHAR szOneStateTrue[] = { '!','O','n','e','=','-','1',0 };
1806 static WCHAR szOneActionFalse[] = { '$','O','n','e','=','-','1',0 };
1807 static WCHAR szOneActionTrue[] = { '$','O','n','e','>','0',0 };
1808 static WCHAR szCostInitialize[] = { 'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0 };
1809 static WCHAR szEmpty[] = { 0 };
1810 static WCHAR szEquals[] = { '=',0 };
1811 static WCHAR szPropertyName[] = { 'P','r','o','p','e','r','t','y',',','N','a','m','e',0 };
1812 static WCHAR szModeFlag[] = { 'M','o','d','e',',','F','l','a','g',0 };
1813 WCHAR stringw[MAX_PATH];
1814 CHAR string[MAX_PATH];
1815 UINT len;
1816 BOOL bool;
1817 int myint;
1818 IDispatch *pDatabase = NULL, *pInst = NULL, *record = NULL;
1819 ULONG refs_before, refs_after;
1820 HRESULT hr;
1821
1822 /* Session::Installer */
1823 hr = Session_Installer(pSession, &pInst);
1824 ok(hr == S_OK, "Session_Installer failed, hresult 0x%08x\n", hr);
1825 ok(pInst != NULL, "Session_Installer returned NULL IDispatch pointer\n");
1826 ok(pInst == pInstaller, "Session_Installer does not match Installer instance from CoCreateInstance\n");
1827 refs_before = IDispatch_AddRef(pInst);
1828
1829 hr = Session_Installer(pSession, &pInst);
1830 ok(hr == S_OK, "Session_Installer failed, hresult 0x%08x\n", hr);
1831 ok(pInst != NULL, "Session_Installer returned NULL IDispatch pointer\n");
1832 ok(pInst == pInstaller, "Session_Installer does not match Installer instance from CoCreateInstance\n");
1833 refs_after = IDispatch_Release(pInst);
1834 ok(refs_before == refs_after, "got %u and %u\n", refs_before, refs_after);
1835
1836 /* Session::Property, get */
1837 memset(stringw, 0, sizeof(stringw));
1838 hr = Session_PropertyGet(pSession, szProductName, stringw);
1839 ok(hr == S_OK, "Session_PropertyGet failed, hresult 0x%08x\n", hr);
1840 if (strcmp_ww(stringw, szMSITEST) != 0)
1841 {
1842 len = WideCharToMultiByte(CP_ACP, 0, stringw, -1, string, MAX_PATH, NULL, NULL);
1843 ok(len, "WideCharToMultiByteChar returned error %d\n", GetLastError());
1844 ok(0, "Property \"ProductName\" expected to be \"MSITEST\" but was \"%s\"\n", string);
1845 }
1846
1847 /* Session::Property, put */
1848 hr = Session_PropertyPut(pSession, szProductName, szProductName);
1849 ok(hr == S_OK, "Session_PropertyPut failed, hresult 0x%08x\n", hr);
1850 memset(stringw, 0, sizeof(stringw));
1851 hr = Session_PropertyGet(pSession, szProductName, stringw);
1852 ok(hr == S_OK, "Session_PropertyGet failed, hresult 0x%08x\n", hr);
1853 if (strcmp_ww(stringw, szProductName) != 0)
1854 {
1855 len = WideCharToMultiByte(CP_ACP, 0, stringw, -1, string, MAX_PATH, NULL, NULL);
1856 ok(len, "WideCharToMultiByteChar returned error %d\n", GetLastError());
1857 ok(0, "Property \"ProductName\" expected to be \"ProductName\" but was \"%s\"\n", string);
1858 }
1859
1860 /* Try putting a property using empty property identifier */
1861 hr = Session_PropertyPut(pSession, szEmpty, szProductName);
1862 ok(hr == DISP_E_EXCEPTION, "Session_PropertyPut failed, hresult 0x%08x\n", hr);
1863 ok_exception(hr, szPropertyName);
1864
1865 /* Try putting a property using illegal property identifier */
1866 hr = Session_PropertyPut(pSession, szEquals, szProductName);
1867 ok(hr == S_OK, "Session_PropertyPut failed, hresult 0x%08x\n", hr);
1868
1869 /* Session::Language, get */
1870 hr = Session_LanguageGet(pSession, &len);
1871 ok(hr == S_OK, "Session_LanguageGet failed, hresult 0x%08x\n", hr);
1872 /* Not sure how to check the language is correct */
1873
1874 /* Session::Mode, get */
1875 hr = Session_ModeGet(pSession, MSIRUNMODE_REBOOTATEND, &bool);
1876 ok(hr == S_OK, "Session_ModeGet failed, hresult 0x%08x\n", hr);
1877 ok(!bool, "Reboot at end session mode is %d\n", bool);
1878
1879 hr = Session_ModeGet(pSession, MSIRUNMODE_MAINTENANCE, &bool);
1880 ok(hr == S_OK, "Session_ModeGet failed, hresult 0x%08x\n", hr);
1881 ok(!bool, "Maintenance mode is %d\n", bool);
1882
1883 /* Session::Mode, put */
1884 hr = Session_ModePut(pSession, MSIRUNMODE_REBOOTATEND, TRUE);
1885 ok(hr == S_OK, "Session_ModePut failed, hresult 0x%08x\n", hr);
1886 hr = Session_ModeGet(pSession, MSIRUNMODE_REBOOTATEND, &bool);
1887 ok(hr == S_OK, "Session_ModeGet failed, hresult 0x%08x\n", hr);
1888 ok(bool, "Reboot at end session mode is %d, expected 1\n", bool);
1889 hr = Session_ModePut(pSession, MSIRUNMODE_REBOOTATEND, FALSE); /* set it again so we don't reboot */
1890 ok(hr == S_OK, "Session_ModePut failed, hresult 0x%08x\n", hr);
1891
1892 hr = Session_ModePut(pSession, MSIRUNMODE_REBOOTNOW, TRUE);
1893 todo_wine ok(hr == S_OK, "Session_ModePut failed, hresult 0x%08x\n", hr);
1894 if (hr == DISP_E_EXCEPTION) ok_exception(hr, szModeFlag);
1895
1896 hr = Session_ModeGet(pSession, MSIRUNMODE_REBOOTNOW, &bool);
1897 ok(hr == S_OK, "Session_ModeGet failed, hresult 0x%08x\n", hr);
1898 ok(bool, "Reboot now mode is %d, expected 1\n", bool);
1899
1900 hr = Session_ModePut(pSession, MSIRUNMODE_REBOOTNOW, FALSE); /* set it again so we don't reboot */
1901 todo_wine ok(hr == S_OK, "Session_ModePut failed, hresult 0x%08x\n", hr);
1902 if (hr == DISP_E_EXCEPTION) ok_exception(hr, szModeFlag);
1903
1904 hr = Session_ModePut(pSession, MSIRUNMODE_MAINTENANCE, TRUE);
1905 ok(hr == DISP_E_EXCEPTION, "Session_ModePut failed, hresult 0x%08x\n", hr);
1906 ok_exception(hr, szModeFlag);
1907
1908 /* Session::Database, get */
1909 hr = Session_Database(pSession, &pDatabase);
1910 ok(hr == S_OK, "Session_Database failed, hresult 0x%08x\n", hr);
1911 if (hr == S_OK)
1912 {
1913 test_Database(pDatabase, TRUE);
1914 IDispatch_Release(pDatabase);
1915 }
1916
1917 /* Session::EvaluateCondition */
1918 hr = Session_EvaluateCondition(pSession, NULL, &myint);
1919 ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1920 ok(myint == MSICONDITION_NONE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1921
1922 hr = Session_EvaluateCondition(pSession, szEmpty, &myint);
1923 ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1924 ok(myint == MSICONDITION_NONE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1925
1926 hr = Session_EvaluateCondition(pSession, szEquals, &myint);
1927 ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1928 ok(myint == MSICONDITION_ERROR, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1929
1930 /* Session::DoAction(CostInitialize) must occur before the next statements */
1931 hr = Session_DoAction(pSession, szCostInitialize, &myint);
1932 ok(hr == S_OK, "Session_DoAction failed, hresult 0x%08x\n", hr);
1933 ok(myint == IDOK, "DoAction(CostInitialize) returned %d, %d expected\n", myint, IDOK);
1934
1935 /* Session::SetInstallLevel */
1936 hr = Session_SetInstallLevel(pSession, INSTALLLEVEL_MINIMUM);
1937 ok(hr == S_OK, "Session_SetInstallLevel failed, hresult 0x%08x\n", hr);
1938
1939 /* Session::FeatureCurrentState, get */
1940 hr = Session_FeatureCurrentState(pSession, szOne, &myint);
1941 ok(hr == S_OK, "Session_FeatureCurrentState failed, hresult 0x%08x\n", hr);
1942 ok(myint == INSTALLSTATE_UNKNOWN, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1943
1944 /* Session::Message */
1945 hr = Installer_CreateRecord(0, &record);
1946 ok(hr == S_OK, "Installer_CreateRecord failed: %08x\n", hr);
1947 hr = Session_Message(pSession, INSTALLMESSAGE_INFO, record, &myint);
1948 ok(hr == S_OK, "Session_Message failed: %08x\n", hr);
1949 ok(myint == 0, "Session_Message returned %x\n", myint);
1950
1951 /* Session::EvaluateCondition */
1952 hr = Session_EvaluateCondition(pSession, szOneStateFalse, &myint);
1953 ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1954 ok(myint == MSICONDITION_FALSE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1955
1956 hr = Session_EvaluateCondition(pSession, szOneStateTrue, &myint);
1957 ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1958 ok(myint == MSICONDITION_TRUE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1959
1960 /* Session::FeatureRequestState, put */
1961 hr = Session_FeatureRequestStatePut(pSession, szOne, INSTALLSTATE_ADVERTISED);
1962 ok(hr == S_OK, "Session_FeatureRequestStatePut failed, hresult 0x%08x\n", hr);
1963 hr = Session_FeatureRequestStateGet(pSession, szOne, &myint);
1964 ok(hr == S_OK, "Session_FeatureRequestStateGet failed, hresult 0x%08x\n", hr);
1965 ok(myint == INSTALLSTATE_ADVERTISED, "Feature request state was %d but expected %d\n", myint, INSTALLSTATE_ADVERTISED);
1966
1967 /* Session::EvaluateCondition */
1968 hr = Session_EvaluateCondition(pSession, szOneActionFalse, &myint);
1969 ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1970 ok(myint == MSICONDITION_FALSE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1971
1972 hr = Session_EvaluateCondition(pSession, szOneActionTrue, &myint);
1973 ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr);
1974 ok(myint == MSICONDITION_TRUE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN);
1975 }
1976
1977 /* delete key and all its subkeys */
1978 static DWORD delete_key( HKEY hkey )
1979 {
1980 char name[MAX_PATH];
1981 DWORD ret;
1982
1983 while (!(ret = RegEnumKeyA(hkey, 0, name, sizeof(name))))
1984 {
1985 HKEY tmp;
1986 if (!(ret = RegOpenKeyExA( hkey, name, 0, KEY_ENUMERATE_SUB_KEYS, &tmp )))
1987 {
1988 ret = delete_key( tmp );
1989 RegCloseKey( tmp );
1990 }
1991 if (ret) break;
1992 }
1993 if (ret != ERROR_NO_MORE_ITEMS) return ret;
1994 RegDeleteKeyA( hkey, "" );
1995 return 0;
1996 }
1997
1998 static void test_Installer_RegistryValue(void)
1999 {
2000 static const DWORD qw[2] = { 0x12345678, 0x87654321 };
2001 static const WCHAR szKey[] = { 'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\','T','e','s','t',0 };
2002 static const WCHAR szOne[] = { 'O','n','e',0 };
2003 static const WCHAR szTwo[] = { 'T','w','o',0 };
2004 static const WCHAR szThree[] = { 'T','h','r','e','e',0 };
2005 static const WCHAR szREG_BINARY[] = { '(','R','E','G','_','B','I','N','A','R','Y',')',0 };
2006 static const WCHAR szFour[] = { 'F','o','u','r',0 };
2007 static const WCHAR szExpand[] = { '%','M','S','I','T','E','S','T','%',0 };
2008 static const WCHAR szFive[] = { 'F','i','v','e',0,'H','i',0,0 };
2009 static const WCHAR szFiveHi[] = { 'F','i','v','e','\n','H','i',0 };
2010 static const WCHAR szSix[] = { 'S','i','x',0 };
2011 static const WCHAR szREG_[] = { '(','R','E','G','_',']',0 };
2012 static const WCHAR szSeven[] = { 'S','e','v','e','n',0 };
2013 static const WCHAR szEight[] = { 'E','i','g','h','t',0 };
2014 static const WCHAR szBlank[] = { 0 };
2015 VARIANT varresult;
2016 VARIANTARG vararg;
2017 WCHAR szString[MAX_PATH];
2018 HKEY hkey, hkey_sub;
2019 HKEY curr_user = (HKEY)1;
2020 HRESULT hr;
2021 BOOL bRet;
2022 LONG lRet;
2023
2024 /* Delete keys */
2025 SetLastError(0xdeadbeef);
2026 lRet = RegOpenKeyW( HKEY_CURRENT_USER, szKey, &hkey );
2027 if (!lRet && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
2028 {
2029 win_skip("Needed W-functions are not implemented\n");
2030 return;
2031 }
2032 if (!lRet)
2033 delete_key( hkey );
2034
2035 /* Does our key exist? Shouldn't; check with all three possible value parameter types */
2036 hr = Installer_RegistryValueE(curr_user, szKey, &bRet);
2037 ok(hr == S_OK, "Installer_RegistryValueE failed, hresult 0x%08x\n", hr);
2038 ok(!bRet, "Registry key expected to not exist, but Installer_RegistryValue claims it does\n");
2039
2040 memset(szString, 0, sizeof(szString));
2041 hr = Installer_RegistryValueW(curr_user, szKey, NULL, szString);
2042 ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2043
2044 memset(szString, 0, sizeof(szString));
2045 hr = Installer_RegistryValueI(curr_user, szKey, 0, szString, VT_BSTR);
2046 ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2047
2048 /* Create key */
2049 ok(!RegCreateKeyW( HKEY_CURRENT_USER, szKey, &hkey ), "RegCreateKeyW failed\n");
2050
2051 ok(!RegSetValueExW(hkey,szOne,0,REG_SZ, (const BYTE *)szOne, sizeof(szOne)),
2052 "RegSetValueExW failed\n");
2053 ok(!RegSetValueExW(hkey,szTwo,0,REG_DWORD, (const BYTE *)qw, 4),
2054 "RegSetValueExW failed\n");
2055 ok(!RegSetValueExW(hkey,szThree,0,REG_BINARY, (const BYTE *)qw, 4),
2056 "RegSetValueExW failed\n");
2057 ok(SetEnvironmentVariableA("MSITEST", "Four"), "SetEnvironmentVariableA failed %d\n", GetLastError());
2058 ok(!RegSetValueExW(hkey,szFour,0,REG_EXPAND_SZ, (const BYTE *)szExpand, sizeof(szExpand)),
2059 "RegSetValueExW failed\n");
2060 ok(!RegSetValueExW(hkey,szFive,0,REG_MULTI_SZ, (const BYTE *)szFive, sizeof(szFive)),
2061 "RegSetValueExW failed\n");
2062 ok(!RegSetValueExW(hkey,szSix,0,REG_QWORD, (const BYTE *)qw, 8),
2063 "RegSetValueExW failed\n");
2064 ok(!RegSetValueExW(hkey,szSeven,0,REG_NONE, NULL, 0),
2065 "RegSetValueExW failed\n");
2066
2067 ok(!RegSetValueExW(hkey,NULL,0,REG_SZ, (const BYTE *)szOne, sizeof(szOne)),
2068 "RegSetValueExW failed\n");
2069
2070 ok(!RegCreateKeyW( hkey, szEight, &hkey_sub ), "RegCreateKeyW failed\n");
2071
2072 /* Does our key exist? It should, and make sure we retrieve the correct default value */
2073 bRet = FALSE;
2074 hr = Installer_RegistryValueE(curr_user, szKey, &bRet);
2075 ok(hr == S_OK, "Installer_RegistryValueE failed, hresult 0x%08x\n", hr);
2076 ok(bRet, "Registry key expected to exist, but Installer_RegistryValue claims it does not\n");
2077
2078 memset(szString, 0, sizeof(szString));
2079 hr = Installer_RegistryValueW(curr_user, szKey, NULL, szString);
2080 ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2081 ok_w2("Default registry value \"%s\" does not match expected \"%s\"\n", szString, szOne);
2082
2083 /* Ask for the value of a nonexistent key */
2084 memset(szString, 0, sizeof(szString));
2085 hr = Installer_RegistryValueW(curr_user, szKey, szExpand, szString);
2086 ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2087
2088 /* Get values of keys */
2089 memset(szString, 0, sizeof(szString));
2090 hr = Installer_RegistryValueW(curr_user, szKey, szOne, szString);
2091 ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2092 ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szOne);
2093
2094 VariantInit(&vararg);
2095 V_VT(&vararg) = VT_BSTR;
2096 V_BSTR(&vararg) = SysAllocString(szTwo);
2097 hr = Installer_RegistryValue(curr_user, szKey, vararg, &varresult, VT_I4);
2098 ok(hr == S_OK, "Installer_RegistryValue failed, hresult 0x%08x\n", hr);
2099 ok(V_I4(&varresult) == 305419896, "Registry value %d does not match expected value\n", V_I4(&varresult));
2100 VariantClear(&varresult);
2101
2102 memset(szString, 0, sizeof(szString));
2103 hr = Installer_RegistryValueW(curr_user, szKey, szThree, szString);
2104 ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2105 ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szREG_BINARY);
2106
2107 memset(szString, 0, sizeof(szString));
2108 hr = Installer_RegistryValueW(curr_user, szKey, szFour, szString);
2109 ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2110 ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szFour);
2111
2112 /* Vista does not NULL-terminate this case */
2113 memset(szString, 0, sizeof(szString));
2114 hr = Installer_RegistryValueW(curr_user, szKey, szFive, szString);
2115 ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2116 ok_w2n("Registry value \"%s\" does not match expected \"%s\"\n",
2117 szString, szFiveHi, lstrlenW(szFiveHi));
2118
2119 memset(szString, 0, sizeof(szString));
2120 hr = Installer_RegistryValueW(curr_user, szKey, szSix, szString);
2121 ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
2122 ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szREG_);
2123
2124 VariantInit(&vararg);
2125 V_VT(&vararg) = VT_BSTR;
2126 V_BSTR(&vararg) = SysAllocString(szSeven);
2127 hr = Installer_RegistryValue(curr_user, szKey, vararg, &varresult, VT_EMPTY);
2128 ok(hr == S_OK, "Installer_RegistryValue failed, hresult 0x%08x\n", hr);
2129
2130 /* Get string class name for the key */
2131 memset(szString, 0, sizeof(szString));
2132 hr = Installer_RegistryValueI(curr_user, szKey, 0, szString, VT_BSTR);
2133 ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2134 ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szBlank);
2135
2136 /* Get name of a value by positive number (RegEnumValue like), valid index */
2137 memset(szString, 0, sizeof(szString));
2138 hr = Installer_RegistryValueI(curr_user, szKey, 2, szString, VT_BSTR);
2139 ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2140 /* RegEnumValue order seems different on wine */
2141 todo_wine ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szTwo);
2142
2143 /* Get name of a value by positive number (RegEnumValue like), invalid index */
2144 memset(szString, 0, sizeof(szString));
2145 hr = Installer_RegistryValueI(curr_user, szKey, 10, szString, VT_EMPTY);
2146 ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2147
2148 /* Get name of a subkey by negative number (RegEnumValue like), valid index */
2149 memset(szString, 0, sizeof(szString));
2150 hr = Installer_RegistryValueI(curr_user, szKey, -1, szString, VT_BSTR);
2151 ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2152 ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szEight);
2153
2154 /* Get name of a subkey by negative number (RegEnumValue like), invalid index */
2155 memset(szString, 0, sizeof(szString));
2156 hr = Installer_RegistryValueI(curr_user, szKey, -10, szString, VT_EMPTY);
2157 ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
2158
2159 /* clean up */
2160 delete_key(hkey);
2161 }
2162
2163 static void test_Installer_Products(BOOL bProductInstalled)
2164 {
2165 WCHAR szString[MAX_PATH];
2166 HRESULT hr;
2167 int idx;
2168 IUnknown *pUnk = NULL;
2169 IEnumVARIANT *pEnum = NULL;
2170 VARIANT var;
2171 ULONG celt;
2172 int iCount, iValue;
2173 IDispatch *pStringList = NULL;
2174 BOOL bProductFound = FALSE;
2175
2176 /* Installer::Products */
2177 hr = Installer_Products(&pStringList);
2178 ok(hr == S_OK, "Installer_Products failed, hresult 0x%08x\n", hr);
2179 if (hr == S_OK)
2180 {
2181 /* StringList::_NewEnum */
2182 hr = StringList__NewEnum(pStringList, &pUnk);
2183 ok(hr == S_OK, "StringList_NewEnum failed, hresult 0x%08x\n", hr);
2184 if (hr == S_OK)
2185 {
2186 hr = IUnknown_QueryInterface(pUnk, &IID_IEnumVARIANT, (void **)&pEnum);
2187 ok (hr == S_OK, "IUnknown::QueryInterface returned 0x%08x\n", hr);
2188 }
2189 if (!pEnum)
2190 skip("IEnumVARIANT tests\n");
2191
2192 /* StringList::Count */
2193 hr = StringList_Count(pStringList, &iCount);
2194 ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr);
2195
2196 for (idx=0; idx<iCount; idx++)
2197 {
2198 /* StringList::Item */
2199 memset(szString, 0, sizeof(szString));
2200 hr = StringList_Item(pStringList, idx, szString);
2201 ok(hr == S_OK, "StringList_Item failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);
2202
2203 if (hr == S_OK)
2204 {
2205 /* Installer::ProductState */
2206 hr = Installer_ProductState(szString, &iValue);
2207 ok(hr == S_OK, "Installer_ProductState failed, hresult 0x%08x\n", hr);
2208 if (hr == S_OK)
2209 ok(iValue == INSTALLSTATE_DEFAULT || iValue == INSTALLSTATE_ADVERTISED, "Installer_ProductState returned %d, expected %d or %d\n", iValue, INSTALLSTATE_DEFAULT, INSTALLSTATE_ADVERTISED);
2210
2211 /* Not found our product code yet? Check */
2212 if (!bProductFound && !strcmp_ww(szString, szProductCode))
2213 bProductFound = TRUE;
2214
2215 /* IEnumVARIANT::Next */
2216 if (pEnum)
2217 {
2218 hr = IEnumVARIANT_Next(pEnum, 1, &var, &celt);
2219 ok(hr == S_OK, "IEnumVARIANT_Next failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);
2220 ok(celt == 1, "%d items were retrieved, expected 1\n", celt);
2221 ok(V_VT(&var) == VT_BSTR, "IEnumVARIANT_Next returned variant of type %d, expected %d\n", V_VT(&var), VT_BSTR);
2222 ok_w2("%s returned by StringList_Item does not match %s returned by IEnumVARIANT_Next\n", szString, V_BSTR(&var));
2223 VariantClear(&var);
2224 }
2225 }
2226 }
2227
2228 if (bProductInstalled)
2229 {
2230 ok(bProductInstalled == bProductFound, "Product expected to %s installed but product code was %s\n",
2231 bProductInstalled ? "be" : "not be",
2232 bProductFound ? "found" : "not found");
2233 }
2234
2235 if (pEnum)
2236 {
2237 IEnumVARIANT *pEnum2 = NULL;
2238
2239 if (0) /* Crashes on Windows XP */
2240 {
2241 /* IEnumVARIANT::Clone, NULL pointer */
2242 hr = IEnumVARIANT_Clone(pEnum, NULL);
2243 }
2244
2245 /* IEnumVARIANT::Clone */
2246 hr = IEnumVARIANT_Clone(pEnum, &pEnum2);
2247 ok(hr == S_OK, "IEnumVARIANT_Clone failed, hresult 0x%08x\n", hr);
2248 if (hr == S_OK)
2249 {
2250 /* IEnumVARIANT::Clone is supposed to save the position, but it actually just goes back to the beginning */
2251
2252 /* IEnumVARIANT::Next of the clone */
2253 if (iCount)
2254 {
2255 hr = IEnumVARIANT_Next(pEnum2, 1, &var, &celt);
2256 ok(hr == S_OK, "IEnumVARIANT_Next failed, hresult 0x%08x\n", hr);
2257 ok(celt == 1, "%d items were retrieved, expected 0\n", celt);
2258 ok(V_VT(&var) == VT_BSTR, "IEnumVARIANT_Next returned variant of type %d, expected %d\n", V_VT(&var), VT_BSTR);
2259 VariantClear(&var);
2260 }
2261 else
2262 skip("IEnumVARIANT::Next of clone will not return success with 0 products\n");
2263
2264 IEnumVARIANT_Release(pEnum2);
2265 }
2266
2267 /* IEnumVARIANT::Skip should fail */
2268 hr = IEnumVARIANT_Skip(pEnum, 1);
2269 ok(hr == S_FALSE, "IEnumVARIANT_Skip failed, hresult 0x%08x\n", hr);
2270
2271 /* IEnumVARIANT::Next, NULL variant pointer */
2272 hr = IEnumVARIANT_Next(pEnum, 1, NULL, &celt);
2273 ok(hr == S_FALSE, "IEnumVARIANT_Next failed, hresult 0x%08x\n", hr);
2274 ok(celt == 0, "%d items were retrieved, expected 0\n", celt);
2275
2276 /* IEnumVARIANT::Next, should not return any more items */
2277 hr = IEnumVARIANT_Next(pEnum, 1, &var, &celt);
2278 ok(hr == S_FALSE, "IEnumVARIANT_Next failed, hresult 0x%08x\n", hr);
2279 ok(celt == 0, "%d items were retrieved, expected 0\n", celt);
2280 VariantClear(&var);
2281
2282 /* IEnumVARIANT::Reset */
2283 hr = IEnumVARIANT_Reset(pEnum);
2284 ok(hr == S_OK, "IEnumVARIANT_Reset failed, hresult 0x%08x\n", hr);
2285
2286 if (iCount)
2287 {
2288 /* IEnumVARIANT::Skip to the last product */
2289 hr = IEnumVARIANT_Skip(pEnum, iCount-1);
2290 ok(hr == S_OK, "IEnumVARIANT_Skip failed, hresult 0x%08x\n", hr);
2291
2292 /* IEnumVARIANT::Next should match the very last retrieved value, also makes sure it works with
2293 * NULL celt pointer. */
2294 hr = IEnumVARIANT_Next(pEnum, 1, &var, NULL);
2295 ok(hr == S_OK, "IEnumVARIANT_Next failed (idx %d, count %d), hresult 0x%08x\n", idx, iCount, hr);
2296 ok(V_VT(&var) == VT_BSTR, "IEnumVARIANT_Next returned variant of type %d, expected %d\n", V_VT(&var), VT_BSTR);
2297 ok_w2("%s returned by StringList_Item does not match %s returned by IEnumVARIANT_Next\n", szString, V_BSTR(&var));
2298 VariantClear(&var);
2299 }
2300 else
2301 skip("IEnumVARIANT::Skip impossible for 0 products\n");
2302 }
2303
2304 /* StringList::Item using an invalid index */
2305 memset(szString, 0, sizeof(szString));
2306 hr = StringList_Item(pStringList, iCount, szString);
2307 ok(hr == DISP_E_BADINDEX, "StringList_Item for an invalid index did not return DISP_E_BADINDEX, hresult 0x%08x\n", hr);
2308
2309 if (pEnum) IEnumVARIANT_Release(pEnum);
2310 if (pUnk) IUnknown_Release(pUnk);
2311 IDispatch_Release(pStringList);
2312 }
2313 }
2314
2315 /* Delete a registry subkey, including all its subkeys (RegDeleteKey does not work on keys with subkeys without
2316 * deleting the subkeys first) */
2317 static UINT delete_registry_key(HKEY hkeyParent, LPCSTR subkey)
2318 {
2319 UINT ret;
2320 CHAR *string = NULL;
2321 HKEY hkey;
2322 DWORD dwSize;
2323
2324 ret = RegOpenKey(hkeyParent, subkey, &hkey);
2325 if (ret != ERROR_SUCCESS) return ret;
2326 ret = RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL);
2327 if (ret != ERROR_SUCCESS) return ret;
2328 if (!(string = HeapAlloc(GetProcessHeap(), 0, ++dwSize))) return ERROR_NOT_ENOUGH_MEMORY;
2329
2330 while (RegEnumKeyA(hkey, 0, string, dwSize) == ERROR_SUCCESS)
2331 delete_registry_key(hkey, string);
2332
2333 RegCloseKey(hkey);
2334 HeapFree(GetProcessHeap(), 0, string);
2335 RegDeleteKeyA(hkeyParent, subkey);
2336 return ERROR_SUCCESS;
2337 }
2338
2339 /* Find a specific registry subkey at any depth within the given key and subkey and return its parent key. */
2340 static UINT find_registry_key(HKEY hkeyParent, LPCSTR subkey, LPCSTR findkey, HKEY *phkey)
2341 {
2342 UINT ret;
2343 CHAR *string = NULL;
2344 int idx = 0;
2345 HKEY hkey;
2346 DWORD dwSize;
2347 BOOL found = FALSE;
2348
2349 *phkey = 0;
2350
2351 ret = RegOpenKey(hkeyParent, subkey, &hkey);
2352 if (ret != ERROR_SUCCESS) return ret;
2353 ret = RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL);
2354 if (ret != ERROR_SUCCESS) return ret;
2355 if (!(string = HeapAlloc(GetProcessHeap(), 0, ++dwSize))) return ERROR_NOT_ENOUGH_MEMORY;
2356
2357 while (!found &&
2358 RegEnumKeyA(hkey, idx++, string, dwSize) == ERROR_SUCCESS)
2359 {
2360 if (!strcmp(string, findkey))
2361 {
2362 *phkey = hkey;
2363 found = TRUE;
2364 }
2365 else if (find_registry_key(hkey, string, findkey, phkey) == ERROR_SUCCESS) found = TRUE;
2366 }
2367
2368 if (*phkey != hkey) RegCloseKey(hkey);
2369 HeapFree(GetProcessHeap(), 0, string);
2370 return (found ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND);
2371 }
2372
2373 static void test_Installer_InstallProduct(void)
2374 {
2375 HRESULT hr;
2376 CHAR path[MAX_PATH];
2377 WCHAR szString[MAX_PATH];
2378 LONG res;
2379 HKEY hkey;
2380 DWORD num, size, type;
2381 int iValue, iCount;
2382 IDispatch *pStringList = NULL;
2383
2384 create_test_files();
2385
2386 /* Installer::InstallProduct */
2387 hr = Installer_InstallProduct(szMsifile, NULL);
2388 if (hr == DISP_E_EXCEPTION)
2389 {
2390 skip("Installer object not supported.\n");
2391 delete_test_files();
2392 return;
2393 }
2394 ok(hr == S_OK, "Installer_InstallProduct failed, hresult 0x%08x\n", hr);
2395
2396 /* Installer::ProductState for our product code, which has been installed */
2397 hr = Installer_ProductState(szProductCode, &iValue);
2398 ok(hr == S_OK, "Installer_ProductState failed, hresult 0x%08x\n", hr);
2399 ok(iValue == INSTALLSTATE_DEFAULT, "Installer_ProductState returned %d, expected %d\n", iValue, INSTALLSTATE_DEFAULT);
2400
2401 /* Installer::ProductInfo for our product code */
2402
2403 /* NULL attribute */
2404 memset(szString, 0, sizeof(szString));
2405 hr = Installer_ProductInfo(szProductCode, NULL, szString);
2406 ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2407 ok_exception(hr, szProductInfoException);
2408
2409 /* Nonexistent attribute */
2410 memset(szString, 0, sizeof(szString));
2411 hr = Installer_ProductInfo(szProductCode, szMsifile, szString);
2412 ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2413 ok_exception(hr, szProductInfoException);
2414
2415 /* Package name */
2416 memset(szString, 0, sizeof(szString));
2417 hr = Installer_ProductInfo(szProductCode, WINE_INSTALLPROPERTY_PACKAGENAMEW, szString);
2418 ok(hr == S_OK, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2419 todo_wine ok_w2("Installer_ProductInfo returned %s but expected %s\n", szString, szMsifile);
2420
2421 /* Product name */
2422 memset(szString, 0, sizeof(szString));
2423 hr = Installer_ProductInfo(szProductCode, WINE_INSTALLPROPERTY_PRODUCTNAMEW, szString);
2424 ok(hr == S_OK, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2425 todo_wine ok_w2("Installer_ProductInfo returned %s but expected %s\n", szString, szMSITEST);
2426
2427 /* Installer::Products */
2428 test_Installer_Products(TRUE);
2429
2430 /* Installer::RelatedProducts for our upgrade code */
2431 hr = Installer_RelatedProducts(szUpgradeCode, &pStringList);
2432 ok(hr == S_OK, "Installer_RelatedProducts failed, hresult 0x%08x\n", hr);
2433 if (hr == S_OK)
2434 {
2435 /* StringList::Count */
2436 hr = StringList_Count(pStringList, &iCount);
2437 ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr);
2438 ok(iCount == 1, "Expected one related product but found %d\n", iCount);
2439
2440 /* StringList::Item */
2441 memset(szString, 0, sizeof(szString));
2442 hr = StringList_Item(pStringList, 0, szString);
2443 ok(hr == S_OK, "StringList_Item failed (idx 0, count %d), hresult 0x%08x\n", iCount, hr);
2444 ok_w2("StringList_Item returned %s but expected %s\n", szString, szProductCode);
2445
2446 IDispatch_Release(pStringList);
2447 }
2448
2449 /* Check & clean up installed files & registry keys */
2450 ok(delete_pf("msitest\\cabout\\new\\five.txt", TRUE), "File not installed\n");
2451 ok(delete_pf("msitest\\cabout\\new", FALSE), "File not installed\n");
2452 ok(delete_pf("msitest\\cabout\\four.txt", TRUE), "File not installed\n");
2453 ok(delete_pf("msitest\\cabout", FALSE), "File not installed\n");
2454 ok(delete_pf("msitest\\changed\\three.txt", TRUE), "File not installed\n");
2455 ok(delete_pf("msitest\\changed", FALSE), "File not installed\n");
2456 ok(delete_pf("msitest\\first\\two.txt", TRUE), "File not installed\n");
2457 ok(delete_pf("msitest\\first", FALSE), "File not installed\n");
2458 ok(delete_pf("msitest\\one.txt", TRUE), "File not installed\n");
2459 ok(delete_pf("msitest\\filename", TRUE), "File not installed\n");
2460 ok(delete_pf("msitest", FALSE), "File not installed\n");
2461
2462 res = RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine\\msitest", &hkey);
2463 ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2464
2465 size = MAX_PATH;
2466 type = REG_SZ;
2467 res = RegQueryValueExA(hkey, "Name", NULL, &type, (LPBYTE)path, &size);
2468 ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2469 ok(!lstrcmpA(path, "imaname"), "Expected imaname, got %s\n", path);
2470
2471 size = MAX_PATH;
2472 type = REG_SZ;
2473 res = RegQueryValueExA(hkey, "blah", NULL, &type, (LPBYTE)path, &size);
2474 ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
2475
2476 size = sizeof(num);
2477 type = REG_DWORD;
2478 res = RegQueryValueExA(hkey, "number", NULL, &type, (LPBYTE)&num, &size);
2479 ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2480 ok(num == 314, "Expected 314, got %d\n", num);
2481
2482 size = MAX_PATH;
2483 type = REG_SZ;
2484 res = RegQueryValueExA(hkey, "OrderTestName", NULL, &type, (LPBYTE)path, &size);
2485 ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2486 ok(!lstrcmpA(path, "OrderTestValue"), "Expected imaname, got %s\n", path);
2487
2488 RegCloseKey(hkey);
2489
2490 res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine\\msitest");
2491 ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2492
2493 /* Remove registry keys written by RegisterProduct standard action */
2494 res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{F1C3AF50-8B56-4A69-A00C-00773FE42F30}");
2495 ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2496
2497 res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UpgradeCodes\\D8E760ECA1E276347B43E42BDBDA5656");
2498 ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2499
2500 res = find_registry_key(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData", "05FA3C1F65B896A40AC00077F34EF203", &hkey);
2501 ok(res == ERROR_SUCCESS ||
2502 broken(res == ERROR_FILE_NOT_FOUND), /* win9x */
2503 "Expected ERROR_SUCCESS, got %d\n", res);
2504 if (res == ERROR_SUCCESS)
2505 {
2506 res = delete_registry_key(hkey, "05FA3C1F65B896A40AC00077F34EF203");
2507 ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2508 RegCloseKey(hkey);
2509
2510 res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\Products\\05FA3C1F65B896A40AC00077F34EF203");
2511 ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
2512 }
2513 else
2514 {
2515 /* win9x defaults to a per-machine install. */
2516 RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\Products\\05FA3C1F65B896A40AC00077F34EF203");
2517 }
2518
2519 /* Remove registry keys written by PublishProduct standard action */
2520 res = RegOpenKey(HKEY_CURRENT_USER, "SOFTWARE\\Microsoft\\Installer", &hkey);
2521 ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2522
2523 res = delete_registry_key(hkey, "Products\\05FA3C1F65B896A40AC00077F34EF203");
2524 ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2525
2526 res = RegDeleteKeyA(hkey, "UpgradeCodes\\D8E760ECA1E276347B43E42BDBDA5656");
2527 ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
2528
2529 RegCloseKey(hkey);
2530
2531 /* Delete installation files we installed */
2532 delete_test_files();
2533 }
2534
2535 static void test_Installer(void)
2536 {
2537 static WCHAR szCreateRecordException[] = { 'C','r','e','a','t','e','R','e','c','o','r','d',',','C','o','u','n','t',0 };
2538 static WCHAR szIntegerDataException[] = { 'I','n','t','e','g','e','r','D','a','t','a',',','F','i','e','l','d',0 };
2539 WCHAR szPath[MAX_PATH];
2540 HRESULT hr;
2541 IDispatch *pSession = NULL, *pDatabase = NULL, *pRecord = NULL, *pStringList = NULL;
2542 int iValue, iCount;
2543
2544 if (!pInstaller) return;
2545
2546 /* Installer::CreateRecord */
2547
2548 /* Test for error */
2549 hr = Installer_CreateRecord(-1, &pRecord);
2550 ok(hr == DISP_E_EXCEPTION, "Installer_CreateRecord failed, hresult 0x%08x\n", hr);
2551 ok_exception(hr, szCreateRecordException);
2552
2553 /* Test for success */
2554 hr = Installer_CreateRecord(1, &pRecord);
2555 ok(hr == S_OK, "Installer_CreateRecord failed, hresult 0x%08x\n", hr);
2556 ok(pRecord != NULL, "Installer_CreateRecord should not have returned NULL record\n");
2557 if (pRecord)
2558 {
2559 /* Record::FieldCountGet */
2560 hr = Record_FieldCountGet(pRecord, &iValue);
2561 ok(hr == S_OK, "Record_FiledCountGet failed, hresult 0x%08x\n", hr);
2562 ok(iValue == 1, "Record_FieldCountGet result was %d but expected 1\n", iValue);
2563
2564 /* Record::IntegerDataGet */
2565 hr = Record_IntegerDataGet(pRecord, 1, &iValue);
2566 ok(hr == S_OK, "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
2567 ok(iValue == MSI_NULL_INTEGER, "Record_IntegerDataGet result was %d but expected %d\n", iValue, MSI_NULL_INTEGER);
2568
2569 /* Record::IntegerDataGet, bad index */
2570 hr = Record_IntegerDataGet(pRecord, 10, &iValue);
2571 ok(hr == S_OK, "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
2572 ok(iValue == MSI_NULL_INTEGER, "Record_IntegerDataGet result was %d but expected %d\n", iValue, MSI_NULL_INTEGER);
2573
2574 /* Record::IntegerDataPut */
2575 hr = Record_IntegerDataPut(pRecord, 1, 100);
2576 ok(hr == S_OK, "Record_IntegerDataPut failed, hresult 0x%08x\n", hr);
2577
2578 /* Record::IntegerDataPut, bad index */
2579 hr = Record_IntegerDataPut(pRecord, 10, 100);
2580 ok(hr == DISP_E_EXCEPTION, "Record_IntegerDataPut failed, hresult 0x%08x\n", hr);
2581 ok_exception(hr, szIntegerDataException);
2582
2583 /* Record::IntegerDataGet */
2584 hr = Record_IntegerDataGet(pRecord, 1, &iValue);
2585 ok(hr == S_OK, "Record_IntegerDataGet failed, hresult 0x%08x\n", hr);
2586 ok(iValue == 100, "Record_IntegerDataGet result was %d but expected 100\n", iValue);
2587
2588 IDispatch_Release(pRecord);
2589 }
2590
2591 create_package(szPath);
2592
2593 /* Installer::OpenPackage */
2594 hr = Installer_OpenPackage(szPath, 0, &pSession);
2595 ok(hr == S_OK, "Installer_OpenPackage failed, hresult 0x%08x\n", hr);
2596 if (hr == S_OK)
2597 {
2598 test_Session(pSession);
2599 IDispatch_Release(pSession);
2600 }
2601
2602 /* Installer::OpenDatabase */
2603 hr = Installer_OpenDatabase(szPath, (INT_PTR)MSIDBOPEN_TRANSACT, &pDatabase);
2604 ok(hr == S_OK, "Installer_OpenDatabase failed, hresult 0x%08x\n", hr);
2605 if (hr == S_OK)
2606 {
2607 test_Database(pDatabase, FALSE);
2608 IDispatch_Release(pDatabase);
2609 }
2610
2611 /* Installer::RegistryValue */
2612 test_Installer_RegistryValue();
2613
2614 /* Installer::ProductState for our product code, which should not be installed */
2615 hr = Installer_ProductState(szProductCode, &iValue);
2616 ok(hr == S_OK, "Installer_ProductState failed, hresult 0x%08x\n", hr);
2617 ok(iValue == INSTALLSTATE_UNKNOWN, "Installer_ProductState returned %d, expected %d\n", iValue, INSTALLSTATE_UNKNOWN);
2618
2619 /* Installer::ProductInfo for our product code, which should not be installed */
2620
2621 /* Package name */
2622 memset(szPath, 0, sizeof(szPath));
2623 hr = Installer_ProductInfo(szProductCode, WINE_INSTALLPROPERTY_PACKAGENAMEW, szPath);
2624 ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2625 ok_exception(hr, szProductInfoException);
2626
2627 /* NULL attribute and NULL product code */
2628 memset(szPath, 0, sizeof(szPath));
2629 hr = Installer_ProductInfo(NULL, NULL, szPath);
2630 ok(hr == DISP_E_EXCEPTION, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
2631 ok_exception(hr, szProductInfoException);
2632
2633 /* Installer::Products */
2634 test_Installer_Products(FALSE);
2635
2636 /* Installer::RelatedProducts for our upgrade code, should not find anything */
2637 hr = Installer_RelatedProducts(szUpgradeCode, &pStringList);
2638 ok(hr == S_OK, "Installer_RelatedProducts failed, hresult 0x%08x\n", hr);
2639 if (hr == S_OK)
2640 {
2641 /* StringList::Count */
2642 hr = StringList_Count(pStringList, &iCount);
2643 ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr);
2644 ok(!iCount, "Expected no related products but found %d\n", iCount);
2645
2646 IDispatch_Release(pStringList);
2647 }
2648
2649 /* Installer::Version */
2650 memset(szPath, 0, sizeof(szPath));
2651 hr = Installer_VersionGet(szPath);
2652 ok(hr == S_OK, "Installer_VersionGet failed, hresult 0x%08x\n", hr);
2653
2654 /* Installer::InstallProduct and other tests that depend on our product being installed */
2655 test_Installer_InstallProduct();
2656 }
2657
2658 START_TEST(automation)
2659 {
2660 DWORD len;
2661 char temp_path[MAX_PATH], prev_path[MAX_PATH];
2662 HRESULT hr;
2663 CLSID clsid;
2664 IUnknown *pUnk;
2665
2666 GetSystemTimeAsFileTime(&systemtime);
2667
2668 GetCurrentDirectoryA(MAX_PATH, prev_path);
2669 GetTempPath(MAX_PATH, temp_path);
2670 SetCurrentDirectoryA(temp_path);
2671
2672 lstrcpyA(CURR_DIR, temp_path);
2673 len = lstrlenA(CURR_DIR);
2674
2675 if(len && (CURR_DIR[len - 1] == '\\'))
2676 CURR_DIR[len - 1] = 0;
2677
2678 get_program_files_dir(PROG_FILES_DIR);
2679
2680 hr = OleInitialize(NULL);
2681 ok (hr == S_OK, "OleInitialize returned 0x%08x\n", hr);
2682 hr = CLSIDFromProgID(szProgId, &clsid);
2683 ok (hr == S_OK, "CLSIDFromProgID returned 0x%08x\n", hr);
2684 hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&pUnk);
2685 ok(hr == S_OK, "CoCreateInstance returned 0x%08x\n", hr);
2686
2687 if (pUnk)
2688 {
2689 hr = IUnknown_QueryInterface(pUnk, &IID_IDispatch, (void **)&pInstaller);
2690 ok (hr == S_OK, "IUnknown::QueryInterface returned 0x%08x\n", hr);
2691
2692 test_dispid();
2693 test_dispatch();
2694 test_Installer();
2695
2696 IDispatch_Release(pInstaller);
2697 IUnknown_Release(pUnk);
2698 }
2699
2700 OleUninitialize();
2701
2702 SetCurrentDirectoryA(prev_path);
2703 }