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