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