1 /*
2  * Copyright 2011 Jacek Caban for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #define COBJMACROS
20 #define CONST_VTABLE
21 
22 #include <initguid.h>
23 #include <ole2.h>
24 #include <dispex.h>
25 
26 #include "wshom.h"
27 #include "wine/test.h"
28 
29 DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
30 
31 #define EXPECT_HR(hr,hr_exp) \
32     ok(hr == hr_exp, "got 0x%08x, expected 0x%08x\n", hr, hr_exp)
33 
34 #define test_provideclassinfo(a, b) _test_provideclassinfo((IDispatch*)a, b, __LINE__)
35 static void _test_provideclassinfo(IDispatch *disp, const GUID *guid, int line)
36 {
37     IProvideClassInfo *classinfo;
38     TYPEATTR *attr;
39     ITypeInfo *ti;
40     HRESULT hr;
41 
42     hr = IDispatch_QueryInterface(disp, &IID_IProvideClassInfo, (void **)&classinfo);
43     ok_(__FILE__,line) (hr == S_OK, "Failed to get IProvideClassInfo, %#x.\n", hr);
44 
45     hr = IProvideClassInfo_GetClassInfo(classinfo, &ti);
46     ok_(__FILE__,line) (hr == S_OK, "GetClassInfo() failed, %#x.\n", hr);
47 
48     hr = ITypeInfo_GetTypeAttr(ti, &attr);
49     ok_(__FILE__,line) (hr == S_OK, "GetTypeAttr() failed, %#x.\n", hr);
50 
51     ok_(__FILE__,line) (IsEqualGUID(&attr->guid, guid), "Unexpected typeinfo %s, expected %s\n", wine_dbgstr_guid(&attr->guid),
52         wine_dbgstr_guid(guid));
53 
54     IProvideClassInfo_Release(classinfo);
55     ITypeInfo_ReleaseTypeAttr(ti, attr);
56     ITypeInfo_Release(ti);
57 }
58 
59 #define CHECK_BSTR_LENGTH(str) check_bstr_length(str, __LINE__)
60 static void check_bstr_length(BSTR str, int line)
61 {
62     ok_(__FILE__, line)(SysStringLen(str) == lstrlenW(str), "Unexpected string length %u vs %u.\n",
63             SysStringLen(str), lstrlenW(str));
64 }
65 
66 static void test_wshshell(void)
67 {
68     static const WCHAR notepadW[] = {'n','o','t','e','p','a','d','.','e','x','e',0};
69     static const WCHAR desktopW[] = {'D','e','s','k','t','o','p',0};
70     static const WCHAR lnk1W[] = {'f','i','l','e','.','l','n','k',0};
71     static const WCHAR pathW[] = {'%','P','A','T','H','%',0};
72     static const WCHAR sysW[] = {'S','Y','S','T','E','M',0};
73     static const WCHAR path2W[] = {'P','A','T','H',0};
74     static const WCHAR dummydirW[] = {'d','e','a','d','p','a','r','r','o','t',0};
75     static const WCHAR emptyW[] = {'e','m','p','t','y',0};
76     static const WCHAR cmdexeW[] = {'\\','c','m','d','.','e','x','e',0};
77     static const WCHAR testdirW[] = {'w','s','h','o','m',' ','t','e','s','t',' ','d','i','r',0};
78     static const WCHAR paramsW[] =
79         {' ','/','c',' ','r','d',' ','/','s',' ','/','q',' ','c',':','\\','n','o','s','u','c','h','d','i','r',0};
80     static const WCHAR cmdW[] =
81         {'c','m','d','.','e','x','e',' ','/','c',' ','r','d',' ','/','s',' ','/','q',' ','c',':','\\',
82          'n','o','s','u','c','h','d','i','r',0};
83     static const WCHAR cmd2W[] =
84         {'"','c','m','d','.','e','x','e',' ','"',' ','/','c',' ','r','d',' ','/','s',' ','/','q',' ','c',':','\\',
85          'n','o','s','u','c','h','d','i','r',0};
86     WCHAR path[MAX_PATH], path2[MAX_PATH], buf[MAX_PATH];
87     IWshEnvironment *env;
88     IWshExec *shexec;
89     IWshShell3 *sh3;
90     IDispatchEx *dispex;
91     IWshCollection *coll;
92     IDispatch *disp, *shortcut;
93     IUnknown *shell, *unk;
94     IFolderCollection *folders;
95     IWshShortcut *shcut;
96     ITypeInfo *ti;
97     HRESULT hr;
98     TYPEATTR *tattr;
99     DISPPARAMS dp;
100     EXCEPINFO ei;
101     VARIANT arg, res, arg2;
102     BSTR str, ret;
103     DWORD retval, attrs;
104     UINT err;
105 
106     hr = CoCreateInstance(&CLSID_WshShell, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
107             &IID_IDispatch, (void**)&disp);
108     ok(hr == S_OK, "got 0x%08x\n", hr);
109 
110     hr = IDispatch_QueryInterface(disp, &IID_IWshShell3, (void**)&shell);
111     EXPECT_HR(hr, S_OK);
112     test_provideclassinfo(disp, &IID_IWshShell3);
113 
114     hr = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
115     EXPECT_HR(hr, E_NOINTERFACE);
116     IDispatch_Release(disp);
117 
118     hr = IUnknown_QueryInterface(shell, &IID_IWshShell3, (void**)&sh3);
119     EXPECT_HR(hr, S_OK);
120 
121     hr = IWshShell3_QueryInterface(sh3, &IID_IObjectWithSite, (void**)&unk);
122     ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
123 
124     hr = IWshShell3_QueryInterface(sh3, &IID_IWshShell, (void**)&unk);
125     ok(hr == S_OK, "got 0x%08x\n", hr);
126     IUnknown_Release(unk);
127 
128     hr = IWshShell3_QueryInterface(sh3, &IID_IWshShell2, (void**)&unk);
129     ok(hr == S_OK, "got 0x%08x\n", hr);
130     IUnknown_Release(unk);
131 
132     hr = IWshShell3_get_SpecialFolders(sh3, &coll);
133     EXPECT_HR(hr, S_OK);
134     test_provideclassinfo(coll, &IID_IWshCollection);
135 
136     hr = IWshCollection_QueryInterface(coll, &IID_IFolderCollection, (void**)&folders);
137     EXPECT_HR(hr, E_NOINTERFACE);
138 
139     hr = IWshCollection_QueryInterface(coll, &IID_IDispatch, (void**)&disp);
140     EXPECT_HR(hr, S_OK);
141 
142     hr = IDispatch_GetTypeInfo(disp, 0, 0, &ti);
143     EXPECT_HR(hr, S_OK);
144 
145     hr = ITypeInfo_GetTypeAttr(ti, &tattr);
146     EXPECT_HR(hr, S_OK);
147     ok(IsEqualIID(&tattr->guid, &IID_IWshCollection), "got wrong type guid\n");
148     ITypeInfo_ReleaseTypeAttr(ti, tattr);
149 
150     /* try to call Item() with normal IDispatch procedure */
151     str = SysAllocString(desktopW);
152     V_VT(&arg) = VT_BSTR;
153     V_BSTR(&arg) = str;
154     dp.rgvarg = &arg;
155     dp.rgdispidNamedArgs = NULL;
156     dp.cArgs = 1;
157     dp.cNamedArgs = 0;
158     hr = IDispatch_Invoke(disp, DISPID_VALUE, &IID_NULL, 1033, DISPATCH_PROPERTYGET, &dp, &res, &ei, &err);
159     EXPECT_HR(hr, DISP_E_MEMBERNOTFOUND);
160 
161     /* try Item() directly, it returns directory path apparently */
162     V_VT(&res) = VT_EMPTY;
163     hr = IWshCollection_Item(coll, &arg, &res);
164     EXPECT_HR(hr, S_OK);
165     ok(V_VT(&res) == VT_BSTR, "got res type %d\n", V_VT(&res));
166     CHECK_BSTR_LENGTH(V_BSTR(&res));
167     SysFreeString(str);
168     VariantClear(&res);
169 
170     /* CreateShortcut() */
171     str = SysAllocString(lnk1W);
172     hr = IWshShell3_CreateShortcut(sh3, str, &shortcut);
173     EXPECT_HR(hr, S_OK);
174     SysFreeString(str);
175     hr = IDispatch_QueryInterface(shortcut, &IID_IWshShortcut, (void**)&shcut);
176     EXPECT_HR(hr, S_OK);
177     test_provideclassinfo(shortcut, &IID_IWshShortcut);
178 
179     hr = IWshShortcut_get_Arguments(shcut, NULL);
180     ok(hr == E_POINTER, "got 0x%08x\n", hr);
181 
182     hr = IWshShortcut_get_IconLocation(shcut, NULL);
183     ok(hr == E_POINTER, "got 0x%08x\n", hr);
184 
185     IWshShortcut_Release(shcut);
186     IDispatch_Release(shortcut);
187 
188     /* ExpandEnvironmentStrings */
189     hr = IWshShell3_ExpandEnvironmentStrings(sh3, NULL, NULL);
190     ok(hr == E_POINTER, "got 0x%08x\n", hr);
191 
192     str = SysAllocString(pathW);
193     hr = IWshShell3_ExpandEnvironmentStrings(sh3, str, NULL);
194     ok(hr == E_POINTER, "got 0x%08x\n", hr);
195     SysFreeString(str);
196 
197     V_VT(&arg) = VT_BSTR;
198     V_BSTR(&arg) = SysAllocString(sysW);
199     hr = IWshShell3_get_Environment(sh3, &arg, &env);
200     ok(hr == S_OK, "got 0x%08x\n", hr);
201     VariantClear(&arg);
202 
203     hr = IWshEnvironment_get_Item(env, NULL, NULL);
204     ok(hr == E_POINTER, "got 0x%08x\n", hr);
205     test_provideclassinfo(env, &IID_IWshEnvironment);
206 
207     ret = (BSTR)0x1;
208     hr = IWshEnvironment_get_Item(env, NULL, &ret);
209     ok(hr == S_OK, "got 0x%08x\n", hr);
210     ok(ret && !*ret, "got %p\n", ret);
211     SysFreeString(ret);
212 
213     /* invalid var name */
214     str = SysAllocString(lnk1W);
215     hr = IWshEnvironment_get_Item(env, str, NULL);
216     ok(hr == E_POINTER, "got 0x%08x\n", hr);
217 
218     ret = NULL;
219     hr = IWshEnvironment_get_Item(env, str, &ret);
220     ok(hr == S_OK, "got 0x%08x\n", hr);
221     ok(ret && *ret == 0, "got %s\n", wine_dbgstr_w(ret));
222     CHECK_BSTR_LENGTH(ret);
223     SysFreeString(ret);
224     SysFreeString(str);
225 
226     /* valid name */
227     str = SysAllocString(path2W);
228     hr = IWshEnvironment_get_Item(env, str, &ret);
229     ok(hr == S_OK, "got 0x%08x\n", hr);
230     ok(ret && *ret != 0, "got %s\n", wine_dbgstr_w(ret));
231     CHECK_BSTR_LENGTH(ret);
232     SysFreeString(ret);
233     SysFreeString(str);
234 
235     IWshEnvironment_Release(env);
236 
237     V_VT(&arg) = VT_I2;
238     V_I2(&arg) = 0;
239     V_VT(&arg2) = VT_ERROR;
240     V_ERROR(&arg2) = DISP_E_PARAMNOTFOUND;
241 
242     str = SysAllocString(notepadW);
243     hr = IWshShell3_Run(sh3, str, &arg, &arg2, NULL);
244     ok(hr == E_POINTER, "got 0x%08x\n", hr);
245 
246     retval = 10;
247     hr = IWshShell3_Run(sh3, str, NULL, &arg2, &retval);
248     ok(hr == E_POINTER, "got 0x%08x\n", hr);
249     ok(retval == 10, "got %u\n", retval);
250 
251     retval = 10;
252     hr = IWshShell3_Run(sh3, str, &arg, NULL, &retval);
253     ok(hr == E_POINTER, "got 0x%08x\n", hr);
254     ok(retval == 10, "got %u\n", retval);
255 
256     retval = 10;
257     V_VT(&arg2) = VT_ERROR;
258     V_ERROR(&arg2) = 0;
259     hr = IWshShell3_Run(sh3, str, &arg, &arg2, &retval);
260     ok(hr == DISP_E_TYPEMISMATCH, "got 0x%08x\n", hr);
261     ok(retval == 10, "got %u\n", retval);
262     SysFreeString(str);
263 
264     V_VT(&arg2) = VT_BOOL;
265     V_BOOL(&arg2) = VARIANT_TRUE;
266 
267     retval = 0xdeadbeef;
268     str = SysAllocString(cmdW);
269     hr = IWshShell3_Run(sh3, str, &arg, &arg2, &retval);
270     ok(hr == S_OK, "got 0x%08x\n", hr);
271     todo_wine ok(retval == ERROR_FILE_NOT_FOUND, "got %u\n", retval);
272     SysFreeString(str);
273 
274     retval = 0xdeadbeef;
275     str = SysAllocString(cmd2W);
276     hr = IWshShell3_Run(sh3, str, &arg, &arg2, &retval);
277     ok(hr == S_OK, "got 0x%08x\n", hr);
278     todo_wine ok(retval == ERROR_FILE_NOT_FOUND, "got %u\n", retval);
279     SysFreeString(str);
280 
281     GetSystemDirectoryW(path, ARRAY_SIZE(path));
282     lstrcatW(path, cmdexeW);
283     attrs = GetFileAttributesW(path);
284     ok(attrs != INVALID_FILE_ATTRIBUTES, "cmd.exe not found\n");
285 
286     /* copy cmd.exe to a path with spaces */
287     GetTempPathW(ARRAY_SIZE(path2), path2);
288     lstrcatW(path2, testdirW);
289     CreateDirectoryW(path2, NULL);
290     lstrcatW(path2, cmdexeW);
291     CopyFileW(path, path2, FALSE);
292 
293     buf[0] = '"';
294     lstrcpyW(buf + 1, path2);
295     buf[lstrlenW(buf)] = '"';
296     lstrcpyW(buf + lstrlenW(path2) + 2, paramsW);
297 
298     retval = 0xdeadbeef;
299     str = SysAllocString(buf);
300     hr = IWshShell3_Run(sh3, str, &arg, &arg2, &retval);
301     ok(hr == S_OK, "got 0x%08x\n", hr);
302     todo_wine ok(retval == ERROR_FILE_NOT_FOUND, "got %u\n", retval);
303     SysFreeString(str);
304 
305     DeleteFileW(path2);
306     path2[lstrlenW(path2) - lstrlenW(cmdexeW)] = 0;
307     RemoveDirectoryW(path2);
308 
309     /* current directory */
310     if (0) /* crashes on native */
311         hr = IWshShell3_get_CurrentDirectory(sh3, NULL);
312 
313     str = NULL;
314     hr = IWshShell3_get_CurrentDirectory(sh3, &str);
315     ok(hr == S_OK, "got 0x%08x\n", hr);
316     ok(str && str[0] != 0, "got empty string\n");
317     CHECK_BSTR_LENGTH(str);
318     SysFreeString(str);
319 
320     hr = IWshShell3_put_CurrentDirectory(sh3, NULL);
321     ok(hr == E_INVALIDARG ||
322        broken(hr == HRESULT_FROM_WIN32(ERROR_NOACCESS)), "got 0x%08x\n", hr);
323 
324     str = SysAllocString(emptyW);
325     hr = IWshShell3_put_CurrentDirectory(sh3, str);
326     ok(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "got 0x%08x\n", hr);
327     SysFreeString(str);
328 
329     str = SysAllocString(dummydirW);
330     hr = IWshShell3_put_CurrentDirectory(sh3, str);
331     ok(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "got 0x%08x\n", hr);
332     SysFreeString(str);
333 
334     /* Exec */
335     hr = IWshShell3_Exec(sh3, NULL, NULL);
336     ok(hr == E_POINTER, "got 0x%08x\n", hr);
337 
338     hr = IWshShell3_Exec(sh3, NULL, &shexec);
339     ok(hr == DISP_E_EXCEPTION, "got 0x%08x\n", hr);
340 
341     str = SysAllocString(emptyW);
342     hr = IWshShell3_Exec(sh3, str, &shexec);
343     ok(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "got 0x%08x\n", hr);
344     SysFreeString(str);
345 
346     IWshCollection_Release(coll);
347     IDispatch_Release(disp);
348     IWshShell3_Release(sh3);
349     IUnknown_Release(shell);
350 }
351 
352 /* delete key and all its subkeys */
353 static DWORD delete_key(HKEY hkey)
354 {
355     char name[MAX_PATH];
356     DWORD ret;
357 
358     while (!(ret = RegEnumKeyA(hkey, 0, name, sizeof(name)))) {
359         HKEY tmp;
360         if (!(ret = RegOpenKeyExA(hkey, name, 0, KEY_ENUMERATE_SUB_KEYS, &tmp))) {
361             ret = delete_key(tmp);
362             RegCloseKey(tmp);
363         }
364         if (ret) break;
365     }
366     if (ret != ERROR_NO_MORE_ITEMS) return ret;
367     RegDeleteKeyA(hkey, "");
368     return 0;
369 }
370 
371 static void test_registry(void)
372 {
373     static const WCHAR keypathW[] = {'H','K','E','Y','_','C','U','R','R','E','N','T','_','U','S','E','R','\\',
374         'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\','T','e','s','t','\\',0};
375     static const WCHAR regsz2W[] = {'r','e','g','s','z','2',0};
376     static const WCHAR regszW[] = {'r','e','g','s','z',0};
377     static const WCHAR regdwordW[] = {'r','e','g','d','w','o','r','d',0};
378     static const WCHAR regbinaryW[] = {'r','e','g','b','i','n','a','r','y',0};
379     static const WCHAR regmultiszW[] = {'r','e','g','m','u','l','t','i','s','z',0};
380 
381     static const WCHAR regsz1W[] = {'H','K','E','Y','_','C','U','R','R','E','N','T','_','U','S','E','R','\\',
382         'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\','T','e','s','t','\\','r','e','g','s','z','1',0};
383     static const WCHAR foobarW[] = {'f','o','o','b','a','r',0};
384     static const WCHAR fooW[] = {'f','o','o',0};
385     static const WCHAR brokenW[] = {'H','K','E','Y','_','b','r','o','k','e','n','_','k','e','y',0};
386     static const WCHAR broken2W[] = {'H','K','E','Y','_','C','U','R','R','E','N','T','_','U','S','E','R','a',0};
387     WCHAR pathW[MAX_PATH];
388     DWORD dwvalue, type;
389     VARIANT value, v;
390     IWshShell3 *sh3;
391     VARTYPE vartype;
392     LONG bound;
393     HRESULT hr;
394     BSTR name;
395     HKEY root;
396     LONG ret;
397     UINT dim;
398 
399     hr = CoCreateInstance(&CLSID_WshShell, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
400             &IID_IWshShell3, (void**)&sh3);
401     ok(hr == S_OK, "got 0x%08x\n", hr);
402 
403     /* RegRead() */
404     V_VT(&value) = VT_I2;
405     hr = IWshShell3_RegRead(sh3, NULL, &value);
406     ok(hr == E_POINTER, "got 0x%08x\n", hr);
407     ok(V_VT(&value) == VT_I2, "got %d\n", V_VT(&value));
408 
409     name = SysAllocString(brokenW);
410     hr = IWshShell3_RegRead(sh3, name, NULL);
411     ok(hr == E_POINTER, "got 0x%08x\n", hr);
412     V_VT(&value) = VT_I2;
413     hr = IWshShell3_RegRead(sh3, name, &value);
414     ok(hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), "got 0x%08x\n", hr);
415     ok(V_VT(&value) == VT_I2, "got %d\n", V_VT(&value));
416     SysFreeString(name);
417 
418     name = SysAllocString(broken2W);
419     V_VT(&value) = VT_I2;
420     hr = IWshShell3_RegRead(sh3, name, &value);
421     ok(hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), "got 0x%08x\n", hr);
422     ok(V_VT(&value) == VT_I2, "got %d\n", V_VT(&value));
423     SysFreeString(name);
424 
425     ret = RegCreateKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Test", &root);
426     ok(ret == 0, "got %d\n", ret);
427 
428     ret = RegSetValueExA(root, "regsz", 0, REG_SZ, (const BYTE*)"foobar", 7);
429     ok(ret == 0, "got %d\n", ret);
430 
431     ret = RegSetValueExA(root, "regsz2", 0, REG_SZ, (const BYTE*)"foobar\0f", 9);
432     ok(ret == 0, "got %d\n", ret);
433 
434     ret = RegSetValueExA(root, "regmultisz", 0, REG_MULTI_SZ, (const BYTE*)"foo\0bar\0", 9);
435     ok(ret == 0, "got %d\n", ret);
436 
437     dwvalue = 10;
438     ret = RegSetValueExA(root, "regdword", 0, REG_DWORD, (const BYTE*)&dwvalue, sizeof(dwvalue));
439     ok(ret == 0, "got %d\n", ret);
440 
441     dwvalue = 11;
442     ret = RegSetValueExA(root, "regbinary", 0, REG_BINARY, (const BYTE*)&dwvalue, sizeof(dwvalue));
443     ok(ret == 0, "got %d\n", ret);
444 
445     /* REG_SZ */
446     lstrcpyW(pathW, keypathW);
447     lstrcatW(pathW, regszW);
448     name = SysAllocString(pathW);
449     VariantInit(&value);
450     hr = IWshShell3_RegRead(sh3, name, &value);
451     ok(hr == S_OK, "got 0x%08x\n", hr);
452     ok(V_VT(&value) == VT_BSTR, "got %d\n", V_VT(&value));
453     ok(!lstrcmpW(V_BSTR(&value), foobarW), "got %s\n", wine_dbgstr_w(V_BSTR(&value)));
454     CHECK_BSTR_LENGTH(V_BSTR(&value));
455     VariantClear(&value);
456     SysFreeString(name);
457 
458     /* REG_SZ with embedded NULL */
459     lstrcpyW(pathW, keypathW);
460     lstrcatW(pathW, regsz2W);
461     name = SysAllocString(pathW);
462     VariantInit(&value);
463     hr = IWshShell3_RegRead(sh3, name, &value);
464     ok(hr == S_OK, "got 0x%08x\n", hr);
465     ok(V_VT(&value) == VT_BSTR, "got %d\n", V_VT(&value));
466     ok(SysStringLen(V_BSTR(&value)) == 6, "len %d\n", SysStringLen(V_BSTR(&value)));
467     CHECK_BSTR_LENGTH(V_BSTR(&value));
468     VariantClear(&value);
469     SysFreeString(name);
470 
471     /* REG_DWORD */
472     lstrcpyW(pathW, keypathW);
473     lstrcatW(pathW, regdwordW);
474     name = SysAllocString(pathW);
475     VariantInit(&value);
476     hr = IWshShell3_RegRead(sh3, name, &value);
477     ok(hr == S_OK, "got 0x%08x\n", hr);
478     ok(V_VT(&value) == VT_I4, "got %d\n", V_VT(&value));
479     ok(V_I4(&value) == 10, "got %d\n", V_I4(&value));
480     SysFreeString(name);
481 
482     /* REG_BINARY */
483     lstrcpyW(pathW, keypathW);
484     lstrcatW(pathW, regbinaryW);
485     name = SysAllocString(pathW);
486     VariantInit(&value);
487     hr = IWshShell3_RegRead(sh3, name, &value);
488     ok(hr == S_OK, "got 0x%08x\n", hr);
489     ok(V_VT(&value) == (VT_ARRAY|VT_VARIANT), "got 0x%x\n", V_VT(&value));
490     dim = SafeArrayGetDim(V_ARRAY(&value));
491     ok(dim == 1, "got %u\n", dim);
492 
493     hr = SafeArrayGetLBound(V_ARRAY(&value), 1, &bound);
494     ok(hr == S_OK, "got 0x%08x\n", hr);
495     ok(bound == 0, "got %u\n", bound);
496 
497     hr = SafeArrayGetUBound(V_ARRAY(&value), 1, &bound);
498     ok(hr == S_OK, "got 0x%08x\n", hr);
499     ok(bound == 3, "got %u\n", bound);
500 
501     hr = SafeArrayGetVartype(V_ARRAY(&value), &vartype);
502     ok(hr == S_OK, "got 0x%08x\n", hr);
503     ok(vartype == VT_VARIANT, "got %d\n", vartype);
504 
505     bound = 0;
506     hr = SafeArrayGetElement(V_ARRAY(&value), &bound, &v);
507     ok(hr == S_OK, "got 0x%08x\n", hr);
508     ok(V_VT(&v) == VT_UI1, "got %d\n", V_VT(&v));
509     ok(V_UI1(&v) == 11, "got %u\n", V_UI1(&v));
510     VariantClear(&v);
511     VariantClear(&value);
512     SysFreeString(name);
513 
514     /* REG_MULTI_SZ */
515     lstrcpyW(pathW, keypathW);
516     lstrcatW(pathW, regmultiszW);
517     name = SysAllocString(pathW);
518     VariantInit(&value);
519     hr = IWshShell3_RegRead(sh3, name, &value);
520     ok(hr == S_OK, "got 0x%08x\n", hr);
521     ok(V_VT(&value) == (VT_ARRAY|VT_VARIANT), "got 0x%x\n", V_VT(&value));
522     SysFreeString(name);
523 
524     dim = SafeArrayGetDim(V_ARRAY(&value));
525     ok(dim == 1, "got %u\n", dim);
526 
527     hr = SafeArrayGetLBound(V_ARRAY(&value), 1, &bound);
528     ok(hr == S_OK, "got 0x%08x\n", hr);
529     ok(bound == 0, "got %u\n", bound);
530 
531     hr = SafeArrayGetUBound(V_ARRAY(&value), 1, &bound);
532     ok(hr == S_OK, "got 0x%08x\n", hr);
533     ok(bound == 1, "got %u\n", bound);
534 
535     hr = SafeArrayGetVartype(V_ARRAY(&value), &vartype);
536     ok(hr == S_OK, "got 0x%08x\n", hr);
537     ok(vartype == VT_VARIANT, "got %d\n", vartype);
538 
539     bound = 0;
540     hr = SafeArrayGetElement(V_ARRAY(&value), &bound, &v);
541     ok(hr == S_OK, "got 0x%08x\n", hr);
542     ok(V_VT(&v) == VT_BSTR, "got %d\n", V_VT(&v));
543     ok(!lstrcmpW(V_BSTR(&v), fooW), "got %s\n", wine_dbgstr_w(V_BSTR(&v)));
544     CHECK_BSTR_LENGTH(V_BSTR(&v));
545     VariantClear(&v);
546     VariantClear(&value);
547 
548     name = SysAllocString(regsz1W);
549     V_VT(&value) = VT_I2;
550     hr = IWshShell3_RegRead(sh3, name, &value);
551     ok(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "got 0x%08x\n", hr);
552     ok(V_VT(&value) == VT_I2, "got %d\n", V_VT(&value));
553     VariantClear(&value);
554     SysFreeString(name);
555 
556     delete_key(root);
557 
558     /* RegWrite() */
559     ret = RegCreateKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Test", &root);
560     ok(ret == 0, "got %d\n", ret);
561 
562     hr = IWshShell3_RegWrite(sh3, NULL, NULL, NULL);
563     ok(hr == E_POINTER, "got 0x%08x\n", hr);
564 
565     lstrcpyW(pathW, keypathW);
566     lstrcatW(pathW, regszW);
567     name = SysAllocString(pathW);
568 
569     hr = IWshShell3_RegWrite(sh3, name, NULL, NULL);
570     ok(hr == E_POINTER, "got 0x%08x\n", hr);
571 
572     VariantInit(&value);
573     hr = IWshShell3_RegWrite(sh3, name, &value, NULL);
574     ok(hr == E_POINTER, "got 0x%08x\n", hr);
575 
576     hr = IWshShell3_RegWrite(sh3, name, &value, &value);
577     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
578 
579     /* type is optional */
580     V_VT(&v) = VT_ERROR;
581     V_ERROR(&v) = DISP_E_PARAMNOTFOUND;
582     hr = IWshShell3_RegWrite(sh3, name, &value, &v);
583     ok(hr == S_OK, "got 0x%08x\n", hr);
584 
585     /* default type is REG_SZ */
586     V_VT(&value) = VT_I4;
587     V_I4(&value) = 12;
588     hr = IWshShell3_RegWrite(sh3, name, &value, &v);
589     ok(hr == S_OK, "got 0x%08x\n", hr);
590 
591     type = REG_NONE;
592     ret = RegQueryValueExA(root, "regsz", 0, &type, NULL, NULL);
593     ok(ret == ERROR_SUCCESS, "got %d\n", ret);
594     ok(type == REG_SZ, "got %d\n", type);
595 
596     ret = RegDeleteValueA(root, "regsz");
597     ok(ret == ERROR_SUCCESS, "got %d\n", ret);
598     V_VT(&value) = VT_BSTR;
599     V_BSTR(&value) = SysAllocString(regszW);
600     hr = IWshShell3_RegWrite(sh3, name, &value, &v);
601     ok(hr == S_OK, "got 0x%08x\n", hr);
602     VariantClear(&value);
603 
604     type = REG_NONE;
605     ret = RegQueryValueExA(root, "regsz", 0, &type, NULL, NULL);
606     ok(ret == ERROR_SUCCESS, "got %d\n", ret);
607     ok(type == REG_SZ, "got %d\n", type);
608 
609     ret = RegDeleteValueA(root, "regsz");
610     ok(ret == ERROR_SUCCESS, "got %d\n", ret);
611     V_VT(&value) = VT_R4;
612     V_R4(&value) = 1.2;
613     hr = IWshShell3_RegWrite(sh3, name, &value, &v);
614     ok(hr == S_OK, "got 0x%08x\n", hr);
615     VariantClear(&value);
616 
617     type = REG_NONE;
618     ret = RegQueryValueExA(root, "regsz", 0, &type, NULL, NULL);
619     ok(ret == ERROR_SUCCESS, "got %d\n", ret);
620     ok(type == REG_SZ, "got %d\n", type);
621 
622     ret = RegDeleteValueA(root, "regsz");
623     ok(ret == ERROR_SUCCESS, "got %d\n", ret);
624     V_VT(&value) = VT_R4;
625     V_R4(&value) = 1.2;
626     V_VT(&v) = VT_I2;
627     V_I2(&v) = 1;
628     hr = IWshShell3_RegWrite(sh3, name, &value, &v);
629     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
630     VariantClear(&value);
631 
632     SysFreeString(name);
633 
634     delete_key(root);
635     IWshShell3_Release(sh3);
636 }
637 
638 static void test_popup(void)
639 {
640     static const WCHAR textW[] = {'T','e','x','t',0};
641     VARIANT timeout, type, title, optional;
642     IWshShell *sh;
643     int button;
644     HRESULT hr;
645     BSTR text;
646 
647     hr = CoCreateInstance(&CLSID_WshShell, NULL, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER,
648             &IID_IWshShell, (void **)&sh);
649     ok(hr == S_OK, "Failed to create WshShell object, hr %#x.\n", hr);
650 
651     button = 123;
652     text = SysAllocString(textW);
653 
654     hr = IWshShell_Popup(sh, NULL, NULL, NULL, NULL, &button);
655     ok(hr == E_POINTER, "Unexpected retval %#x.\n", hr);
656     ok(button == 123, "Unexpected button id %d.\n", button);
657 
658     hr = IWshShell_Popup(sh, text, NULL, NULL, NULL, &button);
659     ok(hr == E_POINTER, "Unexpected retval %#x.\n", hr);
660     ok(button == 123, "Unexpected button id %d.\n", button);
661 
662     V_VT(&optional) = VT_ERROR;
663     V_ERROR(&optional) = DISP_E_PARAMNOTFOUND;
664 
665     V_VT(&timeout) = VT_I2;
666     V_I2(&timeout) = 1;
667 
668     V_VT(&type) = VT_I2;
669     V_I2(&type) = 1;
670 
671     V_VT(&title) = VT_BSTR;
672     V_BSTR(&title) = NULL;
673 
674     hr = IWshShell_Popup(sh, text, &timeout, &optional, &type, &button);
675     ok(hr == S_OK, "Unexpected retval %#x.\n", hr);
676     ok(button == -1, "Unexpected button id %d.\n", button);
677 
678     hr = IWshShell_Popup(sh, text, &timeout, &title, &optional, &button);
679     ok(hr == S_OK, "Unexpected retval %#x.\n", hr);
680     ok(button == -1, "Unexpected button id %d.\n", button);
681 
682     SysFreeString(text);
683     IWshShell_Release(sh);
684 }
685 
686 START_TEST(wshom)
687 {
688     IUnknown *unk;
689     HRESULT hr;
690 
691     CoInitialize(NULL);
692 
693     hr = CoCreateInstance(&CLSID_WshShell, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
694             &IID_IUnknown, (void**)&unk);
695     if (FAILED(hr)) {
696         win_skip("Could not create WshShell object: %08x\n", hr);
697         return;
698     }
699     IUnknown_Release(unk);
700 
701     test_wshshell();
702     test_registry();
703     test_popup();
704 
705     CoUninitialize();
706 }
707