2 * Copyright 2011 Jacek Caban for CodeWeavers
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.
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.
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
19 #define WIN32_NO_STATUS
30 #include <wine/test.h>
32 DEFINE_GUID(GUID_NULL
,0,0,0,0,0,0,0,0,0,0,0);
34 #define EXPECT_HR(hr,hr_exp) \
35 ok(hr == hr_exp, "got 0x%08x, expected 0x%08x\n", hr, hr_exp)
37 static void test_wshshell(void)
39 static const WCHAR notepadW
[] = {'n','o','t','e','p','a','d','.','e','x','e',0};
40 static const WCHAR desktopW
[] = {'D','e','s','k','t','o','p',0};
41 static const WCHAR lnk1W
[] = {'f','i','l','e','.','l','n','k',0};
42 static const WCHAR pathW
[] = {'%','P','A','T','H','%',0};
43 static const WCHAR sysW
[] = {'S','Y','S','T','E','M',0};
44 static const WCHAR path2W
[] = {'P','A','T','H',0};
45 static const WCHAR dummydirW
[] = {'d','e','a','d','p','a','r','r','o','t',0};
46 static const WCHAR emptyW
[] = {'e','m','p','t','y',0};
52 IDispatch
*disp
, *shortcut
;
53 IUnknown
*shell
, *unk
;
54 IFolderCollection
*folders
;
61 VARIANT arg
, res
, arg2
;
66 hr
= CoCreateInstance(&CLSID_WshShell
, NULL
, CLSCTX_INPROC_SERVER
|CLSCTX_INPROC_HANDLER
,
67 &IID_IDispatch
, (void**)&disp
);
68 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
70 hr
= IDispatch_QueryInterface(disp
, &IID_IWshShell3
, (void**)&shell
);
72 IDispatch_Release(disp
);
74 hr
= IDispatch_QueryInterface(disp
, &IID_IDispatchEx
, (void**)&dispex
);
75 EXPECT_HR(hr
, E_NOINTERFACE
);
77 hr
= IUnknown_QueryInterface(shell
, &IID_IWshShell3
, (void**)&sh3
);
80 hr
= IWshShell3_QueryInterface(sh3
, &IID_IObjectWithSite
, (void**)&unk
);
81 ok(hr
== E_NOINTERFACE
, "got 0x%08x\n", hr
);
83 hr
= IWshShell3_QueryInterface(sh3
, &IID_IWshShell
, (void**)&unk
);
84 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
85 IUnknown_Release(unk
);
87 hr
= IWshShell3_QueryInterface(sh3
, &IID_IWshShell2
, (void**)&unk
);
88 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
89 IUnknown_Release(unk
);
91 hr
= IWshShell3_get_SpecialFolders(sh3
, &coll
);
94 hr
= IWshCollection_QueryInterface(coll
, &IID_IFolderCollection
, (void**)&folders
);
95 EXPECT_HR(hr
, E_NOINTERFACE
);
97 hr
= IWshCollection_QueryInterface(coll
, &IID_IDispatch
, (void**)&disp
);
100 hr
= IDispatch_GetTypeInfo(disp
, 0, 0, &ti
);
103 hr
= ITypeInfo_GetTypeAttr(ti
, &tattr
);
105 ok(IsEqualIID(&tattr
->guid
, &IID_IWshCollection
), "got wrong type guid\n");
106 ITypeInfo_ReleaseTypeAttr(ti
, tattr
);
108 /* try to call Item() with normal IDispatch procedure */
109 str
= SysAllocString(desktopW
);
110 V_VT(&arg
) = VT_BSTR
;
113 dp
.rgdispidNamedArgs
= NULL
;
116 hr
= IDispatch_Invoke(disp
, DISPID_VALUE
, &IID_NULL
, 1033, DISPATCH_PROPERTYGET
, &dp
, &res
, &ei
, &err
);
117 EXPECT_HR(hr
, DISP_E_MEMBERNOTFOUND
);
119 /* try Item() directly, it returns directory path apparently */
120 V_VT(&res
) = VT_EMPTY
;
121 hr
= IWshCollection_Item(coll
, &arg
, &res
);
123 ok(V_VT(&res
) == VT_BSTR
, "got res type %d\n", V_VT(&res
));
127 /* CreateShortcut() */
128 str
= SysAllocString(lnk1W
);
129 hr
= IWshShell3_CreateShortcut(sh3
, str
, &shortcut
);
132 hr
= IDispatch_QueryInterface(shortcut
, &IID_IWshShortcut
, (void**)&shcut
);
135 hr
= IWshShortcut_get_Arguments(shcut
, NULL
);
136 ok(hr
== E_POINTER
, "got 0x%08x\n", hr
);
138 hr
= IWshShortcut_get_IconLocation(shcut
, NULL
);
139 ok(hr
== E_POINTER
, "got 0x%08x\n", hr
);
141 IWshShortcut_Release(shcut
);
142 IDispatch_Release(shortcut
);
144 /* ExpandEnvironmentStrings */
145 hr
= IWshShell3_ExpandEnvironmentStrings(sh3
, NULL
, NULL
);
146 ok(hr
== E_POINTER
, "got 0x%08x\n", hr
);
148 str
= SysAllocString(pathW
);
149 hr
= IWshShell3_ExpandEnvironmentStrings(sh3
, str
, NULL
);
150 ok(hr
== E_POINTER
, "got 0x%08x\n", hr
);
153 V_VT(&arg
) = VT_BSTR
;
154 V_BSTR(&arg
) = SysAllocString(sysW
);
155 hr
= IWshShell3_get_Environment(sh3
, &arg
, &env
);
156 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
159 hr
= IWshEnvironment_get_Item(env
, NULL
, NULL
);
160 ok(hr
== E_POINTER
, "got 0x%08x\n", hr
);
163 hr
= IWshEnvironment_get_Item(env
, NULL
, &ret
);
164 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
165 ok(ret
&& !*ret
, "got %p\n", ret
);
168 /* invalid var name */
169 str
= SysAllocString(lnk1W
);
170 hr
= IWshEnvironment_get_Item(env
, str
, NULL
);
171 ok(hr
== E_POINTER
, "got 0x%08x\n", hr
);
174 hr
= IWshEnvironment_get_Item(env
, str
, &ret
);
175 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
176 ok(ret
&& *ret
== 0, "got %s\n", wine_dbgstr_w(ret
));
181 str
= SysAllocString(path2W
);
182 hr
= IWshEnvironment_get_Item(env
, str
, &ret
);
183 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
184 ok(ret
&& *ret
!= 0, "got %s\n", wine_dbgstr_w(ret
));
188 IWshEnvironment_Release(env
);
192 V_VT(&arg2
) = VT_ERROR
;
193 V_ERROR(&arg2
) = DISP_E_PARAMNOTFOUND
;
195 str
= SysAllocString(notepadW
);
196 hr
= IWshShell3_Run(sh3
, str
, &arg
, &arg2
, NULL
);
197 ok(hr
== E_POINTER
, "got 0x%08x\n", hr
);
200 hr
= IWshShell3_Run(sh3
, str
, NULL
, &arg2
, &retval
);
201 ok(hr
== E_POINTER
, "got 0x%08x\n", hr
);
202 ok(retval
== 10, "got %u\n", retval
);
205 hr
= IWshShell3_Run(sh3
, str
, &arg
, NULL
, &retval
);
206 ok(hr
== E_POINTER
, "got 0x%08x\n", hr
);
207 ok(retval
== 10, "got %u\n", retval
);
210 V_VT(&arg2
) = VT_ERROR
;
212 hr
= IWshShell3_Run(sh3
, str
, &arg
, &arg2
, &retval
);
213 ok(hr
== DISP_E_TYPEMISMATCH
, "got 0x%08x\n", hr
);
214 ok(retval
== 10, "got %u\n", retval
);
218 /* current directory */
219 if (0) /* crashes on native */
220 hr
= IWshShell3_get_CurrentDirectory(sh3
, NULL
);
223 hr
= IWshShell3_get_CurrentDirectory(sh3
, &str
);
224 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
225 ok(str
&& str
[0] != 0, "got empty string\n");
228 hr
= IWshShell3_put_CurrentDirectory(sh3
, NULL
);
229 ok(hr
== E_INVALIDARG
||
230 broken(hr
== HRESULT_FROM_WIN32(ERROR_NOACCESS
)), "got 0x%08x\n", hr
);
232 str
= SysAllocString(emptyW
);
233 hr
= IWshShell3_put_CurrentDirectory(sh3
, str
);
234 ok(hr
== HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
), "got 0x%08x\n", hr
);
237 str
= SysAllocString(dummydirW
);
238 hr
= IWshShell3_put_CurrentDirectory(sh3
, str
);
239 ok(hr
== HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
), "got 0x%08x\n", hr
);
243 hr
= IWshShell3_Exec(sh3
, NULL
, NULL
);
244 ok(hr
== E_POINTER
, "got 0x%08x\n", hr
);
246 hr
= IWshShell3_Exec(sh3
, NULL
, &shexec
);
247 ok(hr
== DISP_E_EXCEPTION
, "got 0x%08x\n", hr
);
249 str
= SysAllocString(emptyW
);
250 hr
= IWshShell3_Exec(sh3
, str
, &shexec
);
251 ok(hr
== HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
), "got 0x%08x\n", hr
);
254 IWshCollection_Release(coll
);
255 IDispatch_Release(disp
);
256 IWshShell3_Release(sh3
);
257 IUnknown_Release(shell
);
260 /* delete key and all its subkeys */
261 static DWORD
delete_key(HKEY hkey
)
266 while (!(ret
= RegEnumKeyA(hkey
, 0, name
, sizeof(name
)))) {
268 if (!(ret
= RegOpenKeyExA(hkey
, name
, 0, KEY_ENUMERATE_SUB_KEYS
, &tmp
))) {
269 ret
= delete_key(tmp
);
274 if (ret
!= ERROR_NO_MORE_ITEMS
) return ret
;
275 RegDeleteKeyA(hkey
, "");
279 static void test_registry(void)
281 static const WCHAR keypathW
[] = {'H','K','E','Y','_','C','U','R','R','E','N','T','_','U','S','E','R','\\',
282 'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\','T','e','s','t','\\',0};
283 static const WCHAR regsz2W
[] = {'r','e','g','s','z','2',0};
284 static const WCHAR regszW
[] = {'r','e','g','s','z',0};
285 static const WCHAR regdwordW
[] = {'r','e','g','d','w','o','r','d',0};
286 static const WCHAR regbinaryW
[] = {'r','e','g','b','i','n','a','r','y',0};
287 static const WCHAR regmultiszW
[] = {'r','e','g','m','u','l','t','i','s','z',0};
289 static const WCHAR regsz1W
[] = {'H','K','E','Y','_','C','U','R','R','E','N','T','_','U','S','E','R','\\',
290 'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\','T','e','s','t','\\','r','e','g','s','z','1',0};
291 static const WCHAR foobarW
[] = {'f','o','o','b','a','r',0};
292 static const WCHAR fooW
[] = {'f','o','o',0};
293 static const WCHAR brokenW
[] = {'H','K','E','Y','_','b','r','o','k','e','n','_','k','e','y',0};
294 static const WCHAR broken2W
[] = {'H','K','E','Y','_','C','U','R','R','E','N','T','_','U','S','E','R','a',0};
295 WCHAR pathW
[MAX_PATH
];
307 hr
= CoCreateInstance(&CLSID_WshShell
, NULL
, CLSCTX_INPROC_SERVER
|CLSCTX_INPROC_HANDLER
,
308 &IID_IWshShell3
, (void**)&sh3
);
309 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
312 V_VT(&value
) = VT_I2
;
313 hr
= IWshShell3_RegRead(sh3
, NULL
, &value
);
314 ok(hr
== E_POINTER
, "got 0x%08x\n", hr
);
315 ok(V_VT(&value
) == VT_I2
, "got %d\n", V_VT(&value
));
317 name
= SysAllocString(brokenW
);
318 hr
= IWshShell3_RegRead(sh3
, name
, NULL
);
319 ok(hr
== E_POINTER
, "got 0x%08x\n", hr
);
320 V_VT(&value
) = VT_I2
;
321 hr
= IWshShell3_RegRead(sh3
, name
, &value
);
322 ok(hr
== HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND
), "got 0x%08x\n", hr
);
323 ok(V_VT(&value
) == VT_I2
, "got %d\n", V_VT(&value
));
326 name
= SysAllocString(broken2W
);
327 V_VT(&value
) = VT_I2
;
328 hr
= IWshShell3_RegRead(sh3
, name
, &value
);
329 ok(hr
== HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND
), "got 0x%08x\n", hr
);
330 ok(V_VT(&value
) == VT_I2
, "got %d\n", V_VT(&value
));
333 ret
= RegCreateKeyA(HKEY_CURRENT_USER
, "Software\\Wine\\Test", &root
);
334 ok(ret
== 0, "got %d\n", ret
);
336 ret
= RegSetValueExA(root
, "regsz", 0, REG_SZ
, (const BYTE
*)"foobar", 7);
337 ok(ret
== 0, "got %d\n", ret
);
339 ret
= RegSetValueExA(root
, "regsz2", 0, REG_SZ
, (const BYTE
*)"foobar\0f", 9);
340 ok(ret
== 0, "got %d\n", ret
);
342 ret
= RegSetValueExA(root
, "regmultisz", 0, REG_MULTI_SZ
, (const BYTE
*)"foo\0bar\0", 9);
343 ok(ret
== 0, "got %d\n", ret
);
346 ret
= RegSetValueExA(root
, "regdword", 0, REG_DWORD
, (const BYTE
*)&dwvalue
, sizeof(dwvalue
));
347 ok(ret
== 0, "got %d\n", ret
);
350 ret
= RegSetValueExA(root
, "regbinary", 0, REG_BINARY
, (const BYTE
*)&dwvalue
, sizeof(dwvalue
));
351 ok(ret
== 0, "got %d\n", ret
);
354 lstrcpyW(pathW
, keypathW
);
355 lstrcatW(pathW
, regszW
);
356 name
= SysAllocString(pathW
);
358 hr
= IWshShell3_RegRead(sh3
, name
, &value
);
359 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
360 ok(V_VT(&value
) == VT_BSTR
, "got %d\n", V_VT(&value
));
361 ok(!lstrcmpW(V_BSTR(&value
), foobarW
), "got %s\n", wine_dbgstr_w(V_BSTR(&value
)));
362 VariantClear(&value
);
365 /* REG_SZ with embedded NULL */
366 lstrcpyW(pathW
, keypathW
);
367 lstrcatW(pathW
, regsz2W
);
368 name
= SysAllocString(pathW
);
370 hr
= IWshShell3_RegRead(sh3
, name
, &value
);
371 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
372 ok(V_VT(&value
) == VT_BSTR
, "got %d\n", V_VT(&value
));
373 ok(SysStringLen(V_BSTR(&value
)) == 6, "len %d\n", SysStringLen(V_BSTR(&value
)));
374 VariantClear(&value
);
378 lstrcpyW(pathW
, keypathW
);
379 lstrcatW(pathW
, regdwordW
);
380 name
= SysAllocString(pathW
);
382 hr
= IWshShell3_RegRead(sh3
, name
, &value
);
383 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
384 ok(V_VT(&value
) == VT_I4
, "got %d\n", V_VT(&value
));
385 ok(V_I4(&value
) == 10, "got %d\n", V_I4(&value
));
389 lstrcpyW(pathW
, keypathW
);
390 lstrcatW(pathW
, regbinaryW
);
391 name
= SysAllocString(pathW
);
393 hr
= IWshShell3_RegRead(sh3
, name
, &value
);
394 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
395 ok(V_VT(&value
) == (VT_ARRAY
|VT_VARIANT
), "got 0x%x\n", V_VT(&value
));
396 dim
= SafeArrayGetDim(V_ARRAY(&value
));
397 ok(dim
== 1, "got %u\n", dim
);
399 hr
= SafeArrayGetLBound(V_ARRAY(&value
), 1, &bound
);
400 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
401 ok(bound
== 0, "got %u\n", bound
);
403 hr
= SafeArrayGetUBound(V_ARRAY(&value
), 1, &bound
);
404 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
405 ok(bound
== 3, "got %u\n", bound
);
407 hr
= SafeArrayGetVartype(V_ARRAY(&value
), &vartype
);
408 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
409 ok(vartype
== VT_VARIANT
, "got %d\n", vartype
);
412 hr
= SafeArrayGetElement(V_ARRAY(&value
), &bound
, &v
);
413 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
414 ok(V_VT(&v
) == VT_UI1
, "got %d\n", V_VT(&v
));
415 ok(V_UI1(&v
) == 11, "got %u\n", V_UI1(&v
));
417 VariantClear(&value
);
421 lstrcpyW(pathW
, keypathW
);
422 lstrcatW(pathW
, regmultiszW
);
423 name
= SysAllocString(pathW
);
425 hr
= IWshShell3_RegRead(sh3
, name
, &value
);
426 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
427 ok(V_VT(&value
) == (VT_ARRAY
|VT_VARIANT
), "got 0x%x\n", V_VT(&value
));
430 dim
= SafeArrayGetDim(V_ARRAY(&value
));
431 ok(dim
== 1, "got %u\n", dim
);
433 hr
= SafeArrayGetLBound(V_ARRAY(&value
), 1, &bound
);
434 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
435 ok(bound
== 0, "got %u\n", bound
);
437 hr
= SafeArrayGetUBound(V_ARRAY(&value
), 1, &bound
);
438 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
439 ok(bound
== 1, "got %u\n", bound
);
441 hr
= SafeArrayGetVartype(V_ARRAY(&value
), &vartype
);
442 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
443 ok(vartype
== VT_VARIANT
, "got %d\n", vartype
);
446 hr
= SafeArrayGetElement(V_ARRAY(&value
), &bound
, &v
);
447 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
448 ok(V_VT(&v
) == VT_BSTR
, "got %d\n", V_VT(&v
));
449 ok(!lstrcmpW(V_BSTR(&v
), fooW
), "got %s\n", wine_dbgstr_w(V_BSTR(&v
)));
451 VariantClear(&value
);
453 name
= SysAllocString(regsz1W
);
454 V_VT(&value
) = VT_I2
;
455 hr
= IWshShell3_RegRead(sh3
, name
, &value
);
456 ok(hr
== HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
), "got 0x%08x\n", hr
);
457 ok(V_VT(&value
) == VT_I2
, "got %d\n", V_VT(&value
));
458 VariantClear(&value
);
464 ret
= RegCreateKeyA(HKEY_CURRENT_USER
, "Software\\Wine\\Test", &root
);
465 ok(ret
== 0, "got %d\n", ret
);
467 hr
= IWshShell3_RegWrite(sh3
, NULL
, NULL
, NULL
);
468 ok(hr
== E_POINTER
, "got 0x%08x\n", hr
);
470 lstrcpyW(pathW
, keypathW
);
471 lstrcatW(pathW
, regszW
);
472 name
= SysAllocString(pathW
);
474 hr
= IWshShell3_RegWrite(sh3
, name
, NULL
, NULL
);
475 ok(hr
== E_POINTER
, "got 0x%08x\n", hr
);
478 hr
= IWshShell3_RegWrite(sh3
, name
, &value
, NULL
);
479 ok(hr
== E_POINTER
, "got 0x%08x\n", hr
);
481 hr
= IWshShell3_RegWrite(sh3
, name
, &value
, &value
);
482 ok(hr
== E_INVALIDARG
, "got 0x%08x\n", hr
);
484 /* type is optional */
486 V_ERROR(&v
) = DISP_E_PARAMNOTFOUND
;
487 hr
= IWshShell3_RegWrite(sh3
, name
, &value
, &v
);
488 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
490 /* default type is REG_SZ */
491 V_VT(&value
) = VT_I4
;
493 hr
= IWshShell3_RegWrite(sh3
, name
, &value
, &v
);
494 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
497 ret
= RegQueryValueExA(root
, "regsz", 0, &type
, NULL
, NULL
);
498 ok(ret
== ERROR_SUCCESS
, "got %d\n", ret
);
499 ok(type
== REG_SZ
, "got %d\n", type
);
501 ret
= RegDeleteValueA(root
, "regsz");
502 ok(ret
== ERROR_SUCCESS
, "got %d\n", ret
);
503 V_VT(&value
) = VT_BSTR
;
504 V_BSTR(&value
) = SysAllocString(regszW
);
505 hr
= IWshShell3_RegWrite(sh3
, name
, &value
, &v
);
506 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
507 VariantClear(&value
);
510 ret
= RegQueryValueExA(root
, "regsz", 0, &type
, NULL
, NULL
);
511 ok(ret
== ERROR_SUCCESS
, "got %d\n", ret
);
512 ok(type
== REG_SZ
, "got %d\n", type
);
514 ret
= RegDeleteValueA(root
, "regsz");
515 ok(ret
== ERROR_SUCCESS
, "got %d\n", ret
);
516 V_VT(&value
) = VT_R4
;
518 hr
= IWshShell3_RegWrite(sh3
, name
, &value
, &v
);
519 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
520 VariantClear(&value
);
523 ret
= RegQueryValueExA(root
, "regsz", 0, &type
, NULL
, NULL
);
524 ok(ret
== ERROR_SUCCESS
, "got %d\n", ret
);
525 ok(type
== REG_SZ
, "got %d\n", type
);
527 ret
= RegDeleteValueA(root
, "regsz");
528 ok(ret
== ERROR_SUCCESS
, "got %d\n", ret
);
529 V_VT(&value
) = VT_R4
;
533 hr
= IWshShell3_RegWrite(sh3
, name
, &value
, &v
);
534 ok(hr
== E_INVALIDARG
, "got 0x%08x\n", hr
);
535 VariantClear(&value
);
540 IWshShell3_Release(sh3
);
550 hr
= CoCreateInstance(&CLSID_WshShell
, NULL
, CLSCTX_INPROC_SERVER
|CLSCTX_INPROC_HANDLER
,
551 &IID_IUnknown
, (void**)&unk
);
553 win_skip("Could not create WshShell object: %08x\n", hr
);
556 IUnknown_Release(unk
);