ReactOS 0.4.15-dev-7918-g2a2556c
CShellLink.cpp
Go to the documentation of this file.
1/*
2 * PROJECT: ReactOS api tests
3 * LICENSE: LGPLv2.1+ - See COPYING.LIB in the top level directory
4 * PURPOSE: Test for CShellLink
5 * PROGRAMMER: Andreas Maier
6 */
7
8#include "shelltest.h"
9
10#define NDEBUG
11#include <debug.h>
12#include <stdio.h>
13#include <shellutils.h>
14
15/* Test IShellLink::SetPath with environment-variables, existing, non-existing, ...*/
16typedef struct
17{
20
21 /* Test 1 - hrGetPathX = IShellLink::GetPath(pathOutX, ... , flagsX); */
26
27 /* Test 2 */
33
35{
36 {
37 L"%comspec%", S_OK,
38 L"%comspec%", SLGP_SHORTPATH, S_OK, TRUE,
39 L"%comspec%", SLGP_RAWPATH, S_OK, FALSE
40 },
41 {
42 L"%anyvar%", E_INVALIDARG,
43 L"", SLGP_SHORTPATH, S_FALSE, FALSE,
44 L"", SLGP_RAWPATH, S_FALSE, FALSE
45 },
46 {
47 L"%anyvar%%comspec%", S_OK,
48 L"c:\\%anyvar%%comspec%", SLGP_SHORTPATH, S_OK, TRUE,
49 L"%anyvar%%comspec%", SLGP_RAWPATH, S_OK, FALSE
50 },
51 {
52 L"%temp%", S_OK,
53 L"%temp%", SLGP_SHORTPATH, S_OK, TRUE,
54 L"%temp%", SLGP_RAWPATH, S_OK, FALSE
55 },
56 {
57 L"%shell%", S_OK,
58 L"%systemroot%\\system32\\%shell%", SLGP_SHORTPATH, S_OK, TRUE,
59 L"%shell%", SLGP_RAWPATH, S_OK, FALSE
60 },
61 {
62 L"u:\\anypath\\%anyvar%", S_OK,
63 L"u:\\anypath\\%anyvar%", SLGP_SHORTPATH, S_OK, TRUE,
64 L"u:\\anypath\\%anyvar%", SLGP_RAWPATH, S_OK, FALSE
65 },
66 {
67 L"c:\\temp", S_OK,
68 L"c:\\temp", SLGP_SHORTPATH, S_OK, FALSE,
69 L"c:\\temp", SLGP_RAWPATH, S_OK, FALSE
70 },
71 {
72 L"cmd.exe", S_OK,
73 L"%comspec%", SLGP_SHORTPATH, S_OK, TRUE,
74 L"%comspec%", SLGP_RAWPATH, S_OK, TRUE
75 },
76 {
77 L"%systemroot%\\non-existent-file", S_OK,
78 L"%systemroot%\\non-existent-file", SLGP_SHORTPATH, S_OK, TRUE,
79 L"%systemroot%\\non-existent-file", SLGP_RAWPATH, S_OK, FALSE
80 },
81 {
82 L"c:\\non-existent-path\\non-existent-file", S_OK,
83 L"c:\\non-existent-path\\non-existent-file", SLGP_SHORTPATH, S_OK, FALSE,
84 L"c:\\non-existent-path\\non-existent-file", SLGP_RAWPATH, S_OK, FALSE
85 },
86 {
87 L"non-existent-file", E_INVALIDARG,
88 L"", SLGP_SHORTPATH, S_FALSE, FALSE,
89 L"", SLGP_RAWPATH, S_FALSE, FALSE
90 },
91};
92
93static
94VOID
96{
97static WCHAR evVar[MAX_PATH];
98
99 HRESULT hr, expectedHr;
100 WCHAR wPathOut[MAX_PATH];
101 BOOL expandPathOut;
102 PCWSTR expectedPathOut;
103 CComPtr<IShellLinkW> psl;
104 UINT i1;
105 DWORD flags;
106
107 hr = CoCreateInstance(CLSID_ShellLink,
108 NULL,
109 CLSCTX_INPROC_SERVER,
110 IID_PPV_ARG(IShellLinkW, &psl));
111 ok(hr == S_OK, "CoCreateInstance, hr = 0x%lx\n", hr);
112 if (FAILED(hr))
113 {
114 skip("Could not instantiate CShellLink\n");
115 return;
116 }
117
118 hr = psl->SetPath(testDef->pathIn);
119 ok(hr == testDef->hrSetPath, "IShellLink::SetPath(%d), got hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrSetPath);
120
121 expectedPathOut = NULL;
122 for (i1 = 0; i1 <= 1; i1++)
123 {
124 if (i1 == 0) /* Usually SLGP_SHORTPATH */
125 {
126 flags = testDef->flags1;
127 expandPathOut = testDef->expandPathOut1;
128 expectedPathOut = testDef->pathOut1;
129 expectedHr = testDef->hrGetPath1;
130 }
131 else // if (i1 == 1) /* Usually SLGP_RAWPATH */
132 {
133 flags = testDef->flags2;
134 expandPathOut = testDef->expandPathOut2;
135 expectedPathOut = testDef->pathOut2;
136 expectedHr = testDef->hrGetPath2;
137 }
138
139 /* Patch some variables */
140 if (expandPathOut)
141 {
142 ExpandEnvironmentStringsW(expectedPathOut, evVar, _countof(evVar));
143 DPRINT("** %S **\n",evVar);
144 expectedPathOut = evVar;
145 }
146
147 hr = psl->GetPath(wPathOut, _countof(wPathOut), NULL, flags);
148 ok(hr == expectedHr,
149 "IShellLink::GetPath(%d), flags 0x%lx, got hr = 0x%lx, expected 0x%lx\n",
150 i, flags, hr, expectedHr);
151 ok(wcsicmp(wPathOut, expectedPathOut) == 0,
152 "IShellLink::GetPath(%d), flags 0x%lx, in %S, got %S, expected %S\n",
153 i, flags, testDef->pathIn, wPathOut, expectedPathOut);
154 }
155}
156
157static
158VOID
160{
161 UINT i;
162
163 /* Needed for test */
164 SetEnvironmentVariableW(L"shell", L"cmd.exe");
165
166 for (i = 0; i < _countof(linkTestList); ++i)
167 {
168 DPRINT("IShellLink-Test(%d): %S\n", i, linkTestList[i].pathIn);
170 }
171
173}
174
175static
176VOID
178{
179 HRESULT hr;
180 CComPtr<IShellLinkW> psl;
181 WCHAR buffer[64];
182 PCWSTR testDescription = L"This is a test description";
183
184 /* Test SetDescription */
185 hr = CoCreateInstance(CLSID_ShellLink,
186 NULL,
187 CLSCTX_INPROC_SERVER,
188 IID_PPV_ARG(IShellLinkW, &psl));
189 ok(hr == S_OK, "CoCreateInstance, hr = 0x%lx\n", hr);
190 if (FAILED(hr))
191 {
192 skip("Could not instantiate CShellLink\n");
193 return;
194 }
195
196 memset(buffer, 0x55, sizeof(buffer));
197 hr = psl->GetDescription(buffer, RTL_NUMBER_OF(buffer));
198 ok(hr == S_OK, "IShellLink::GetDescription returned hr = 0x%lx\n", hr);
199 ok(buffer[0] == 0, "buffer[0] = %x\n", buffer[0]);
200 ok(buffer[1] == 0x5555, "buffer[1] = %x\n", buffer[1]);
201
202 hr = psl->SetDescription(testDescription);
203 ok(hr == S_OK, "IShellLink::SetDescription returned hr = 0x%lx\n", hr);
204
205 memset(buffer, 0x55, sizeof(buffer));
206 hr = psl->GetDescription(buffer, RTL_NUMBER_OF(buffer));
207 ok(hr == S_OK, "IShellLink::GetDescription returned hr = 0x%lx\n", hr);
208 ok(buffer[wcslen(testDescription)] == 0, "buffer[n] = %x\n", buffer[wcslen(testDescription)]);
209 ok(buffer[wcslen(testDescription) + 1] == 0x5555, "buffer[n+1] = %x\n", buffer[wcslen(testDescription) + 1]);
210 ok(!wcscmp(buffer, testDescription), "buffer = '%ls'\n", buffer);
211
212 hr = psl->SetDescription(NULL);
213 ok(hr == S_OK, "IShellLink::SetDescription returned hr = 0x%lx\n", hr);
214
215 memset(buffer, 0x55, sizeof(buffer));
216 hr = psl->GetDescription(buffer, RTL_NUMBER_OF(buffer));
217 ok(hr == S_OK, "IShellLink::GetDescription returned hr = 0x%lx\n", hr);
218 ok(buffer[0] == 0, "buffer[0] = %x\n", buffer[0]);
219 ok(buffer[1] == 0x5555, "buffer[1] = %x\n", buffer[1]);
220}
221
222
223/* Test IShellLink::Get/SetIconLocation and IExtractIcon::GetIconLocation */
224typedef struct
225{
227
228 /* Expected results */
229 HRESULT hrDefIcon; // Return value for GIL_DEFAULTICON
230 HRESULT hrForShrt; // Return value for GIL_FORSHORTCUT
231 /* Return values for GIL_FORSHELL */
236
238{
239 /* Executable with icons */
240 {L"%SystemRoot%\\system32\\cmd.exe", S_FALSE, E_INVALIDARG,
241 S_OK, L"%SystemRoot%\\system32\\cmd.exe", GIL_NOTFILENAME | GIL_PERINSTANCE},
242
243 /* Executable without icon */
244 {L"%SystemRoot%\\system32\\autochk.exe", S_FALSE, E_INVALIDARG,
245 S_OK, L"%SystemRoot%\\system32\\autochk.exe", GIL_NOTFILENAME | GIL_PERINSTANCE},
246
247 /* Existing file */
248 {L"%SystemRoot%\\system32\\shell32.dll", S_FALSE, E_INVALIDARG,
249 S_OK, L"*", GIL_NOTFILENAME | GIL_PERCLASS},
250
251 /* Non-existing files */
252 {L"%SystemRoot%\\non-existent-file.sdf", S_FALSE, E_INVALIDARG,
253 S_OK, L"*", GIL_NOTFILENAME | GIL_PERCLASS},
254 {L"c:\\non-existent-path\\non-existent-file.sdf", S_FALSE, E_INVALIDARG,
255 S_OK, L"*", GIL_NOTFILENAME | GIL_PERCLASS},
256};
257
258static
259VOID
261{
262 HRESULT hr;
263 CComPtr<IShellLinkW> psl;
264 CComPtr<IExtractIconW> pei;
265 INT iIcon;
266 UINT wFlags;
267 PCWSTR pszExplorer = L"%SystemRoot%\\explorer.exe";
269 WCHAR szPath2[MAX_PATH];
270
271 hr = CoCreateInstance(CLSID_ShellLink,
272 NULL,
273 CLSCTX_INPROC_SERVER,
274 IID_PPV_ARG(IShellLinkW, &psl));
275 ok(hr == S_OK, "CoCreateInstance, hr = 0x%lx\n", hr);
276 if (FAILED(hr))
277 {
278 skip("Could not instantiate CShellLink\n");
279 return;
280 }
281
282 /* Set the path to a file */
284 hr = psl->SetPath(szPath);
285 ok(hr == S_OK, "IShellLink::SetPath failed, hr = 0x%lx\n", hr);
286
287 /*
288 * This test shows that this does not imply that the icon is automatically
289 * set and be retrieved naively by a call to IShellLink::GetIconLocation.
290 */
291 iIcon = 0xdeadbeef;
292 wcscpy(szPath, L"garbage");
293 hr = psl->GetIconLocation(szPath, _countof(szPath), &iIcon);
294 ok(hr == S_OK, "IShellLink::GetIconLocation(%d) failed, hr = 0x%lx\n", i, hr);
295 ok(*szPath == L'\0', "IShellLink::GetIconLocation(%d) returned '%S'\n", i, szPath);
296 ok(iIcon == 0, "IShellLink::GetIconLocation(%d) returned %d, expected %d\n", i, iIcon, 0);
297
298 /* Try to grab the IExtractIconW interface */
300 ok(hr == S_OK, "IShellLink::QueryInterface(IExtractIconW)(%d) failed, hr = 0x%lx\n", i, hr);
301 if (!pei)
302 {
303 win_skip("No IExtractIconW interface\n");
304 return;
305 }
306
307 iIcon = wFlags = 0xdeadbeef;
308 wcscpy(szPath, L"garbage");
309 hr = pei->GetIconLocation(GIL_DEFAULTICON, szPath, _countof(szPath), &iIcon, &wFlags);
310 ok(hr == testDef->hrDefIcon, "IExtractIcon::GetIconLocation(%d) returned hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrDefIcon);
311 ok(*szPath == L'\0', "IExtractIcon::GetIconLocation(%d) returned '%S'\n", i, szPath);
312 // ok(iIcon == 0, "IExtractIcon::GetIconLocation(%d) returned %d\n", i, iIcon);
313
314 iIcon = wFlags = 0xdeadbeef;
315 wcscpy(szPath, L"garbage");
316 hr = pei->GetIconLocation(GIL_FORSHORTCUT, szPath, _countof(szPath), &iIcon, &wFlags);
317 ok(hr == testDef->hrForShrt, "IExtractIcon::GetIconLocation(%d) returned hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrForShrt);
318 // Here, both szPath and iIcon are untouched...
319
320 iIcon = wFlags = 0xdeadbeef;
321 wcscpy(szPath, L"garbage");
322 hr = pei->GetIconLocation(GIL_FORSHELL, szPath, _countof(szPath), &iIcon, &wFlags);
323 ok(hr == testDef->hrForShell, "IExtractIcon::GetIconLocation(%d) returned hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrForShell);
324 ok(wFlags == testDef->Flags, "IExtractIcon::GetIconLocation(%d) returned wFlags = 0x%x, expected 0x%x\n", i, wFlags, testDef->Flags);
325 /*
326 * Actually, even if wFlags specifies GIL_NOTFILENAME, a correct file name is returned
327 * for executables only (at least...), otherwise we can get an asterix '*'.
328 */
329 ExpandEnvironmentStringsW(testDef->IconPath, szPath2, _countof(szPath2));
330 ok(_wcsicmp(szPath2, szPath) == 0, "IExtractIcon::GetIconLocation(%d) returned '%S', expected '%S'\n", i, szPath, szPath2);
331
332 // ok(*szPath == L'\0', "IExtractIcon::GetIconLocation returned '%S'\n", szPath);
333 // ok(iIcon == 0, "IExtractIcon::GetIconLocation returned %d\n", iIcon);
334 // ok(FALSE, "hr = 0x%lx, szPath = '%S', iIcon = %d, wFlags = %d\n", hr, szPath, iIcon, wFlags);
335
336
337 /*
338 * Now we test what happens when we explicitly set an icon to the shortcut.
339 * Note that actually, SetIconLocation() does not verify whether the file
340 * really exists.
341 */
342 hr = psl->SetIconLocation(pszExplorer, 1);
343 ok(hr == S_OK, "IShellLink::SetIconLocation(%d) failed, hr = 0x%lx\n", i, hr);
344
345 /*
346 * First, we call IShellLink::GetIconLocation. We retrieve
347 * exactly what we specified with SetIconLocation.
348 */
349 iIcon = 0xdeadbeef;
350 wcscpy(szPath, L"garbage");
351 hr = psl->GetIconLocation(szPath, _countof(szPath), &iIcon);
352 ok(hr == S_OK, "IShellLink::GetIconLocation(%d) failed, hr = 0x%lx\n", i, hr);
353 ok(wcscmp(szPath, pszExplorer) == 0, "IShellLink::GetIconLocation(%d) returned '%S', expected '%S'\n", i, szPath, pszExplorer);
354 ok(iIcon == 1, "IShellLink::GetIconLocation(%d) returned %d, expected %d\n", i, iIcon, 1);
355
356 /*
357 * Now we test what happens with IExtractIcon::GetIconLocation.
358 * We see that it retrieves the icon of the shortcut's underlying file.
359 */
360 iIcon = wFlags = 0xdeadbeef;
361 wcscpy(szPath, L"garbage");
362 hr = pei->GetIconLocation(GIL_DEFAULTICON, szPath, _countof(szPath), &iIcon, &wFlags);
363 ok(hr == testDef->hrDefIcon, "IExtractIcon::GetIconLocation(%d) returned hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrDefIcon);
364 ok(*szPath == L'\0', "IExtractIcon::GetIconLocation(%d) returned '%S'\n", i, szPath);
365 // ok(iIcon == 0, "IExtractIcon::GetIconLocation(%d) returned %d\n", i, iIcon);
366
367 iIcon = wFlags = 0xdeadbeef;
368 wcscpy(szPath, L"garbage");
369 hr = pei->GetIconLocation(GIL_FORSHORTCUT, szPath, _countof(szPath), &iIcon, &wFlags);
370 ok(hr == testDef->hrForShrt, "IExtractIcon::GetIconLocation(%d) returned hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrForShrt);
371 // Here, both szPath and iIcon are untouched...
372
373 iIcon = wFlags = 0xdeadbeef;
374 wcscpy(szPath, L"garbage");
375 hr = pei->GetIconLocation(GIL_FORSHELL, szPath, _countof(szPath), &iIcon, &wFlags);
376 ok(hr == testDef->hrForShell, "IExtractIcon::GetIconLocation(%d) returned hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrForShell);
377 ok(wFlags == testDef->Flags, "IExtractIcon::GetIconLocation(%d) returned wFlags = 0x%x, expected 0x%x\n", i, wFlags, testDef->Flags);
378 /*
379 * Actually, even if wFlags specifies GIL_NOTFILENAME, a correct file name is returned
380 * for executables only (at least...), otherwise we can get an asterix '*'.
381 */
382 ExpandEnvironmentStringsW(testDef->IconPath, szPath2, _countof(szPath2));
383 ok(_wcsicmp(szPath2, szPath) == 0, "IExtractIcon::GetIconLocation(%d) returned '%S', expected '%S'\n", i, szPath, szPath2);
384
385 // ok(*szPath == L'\0', "IExtractIcon::GetIconLocation returned '%S'\n", szPath);
386 // ok(iIcon == 0, "IExtractIcon::GetIconLocation returned %d\n", iIcon);
387 // ok(FALSE, "hr = 0x%lx, szPath = '%S', iIcon = %d, wFlags = %d\n", hr, szPath, iIcon, wFlags);
388}
389
390static
391VOID
393{
394 UINT i;
395
396 for (i = 0; i < _countof(ShIconTests); ++i)
397 {
399 }
400}
401
402
404{
406
410
412}
#define RTL_NUMBER_OF(x)
Definition: RtlRegistry.c:12
#define ok(value,...)
Definition: atltest.h:57
#define skip(...)
Definition: atltest.h:64
#define START_TEST(x)
Definition: atltest.h:75
#define E_INVALIDARG
Definition: ddrawi.h:101
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
#define MAX_PATH
Definition: compat.h:34
#define wcsicmp
Definition: compat.h:15
BOOL WINAPI DECLSPEC_HOTPATCH SetEnvironmentVariableW(IN LPCWSTR lpName, IN LPCWSTR lpValue)
Definition: environ.c:259
DWORD WINAPI ExpandEnvironmentStringsW(IN LPCWSTR lpSrc, IN LPWSTR lpDst, IN DWORD nSize)
Definition: environ.c:519
HRESULT WINAPI DECLSPEC_HOTPATCH CoCreateInstance(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID iid, LPVOID *ppv)
Definition: compobj.c:3325
HRESULT WINAPI DECLSPEC_HOTPATCH CoInitializeEx(LPVOID lpReserved, DWORD dwCoInit)
Definition: compobj.c:2002
void WINAPI DECLSPEC_HOTPATCH CoUninitialize(void)
Definition: compobj.c:2067
unsigned int BOOL
Definition: ntddk_ex.h:94
unsigned long DWORD
Definition: ntddk_ex.h:95
GLuint buffer
Definition: glext.h:5915
GLbitfield flags
Definition: glext.h:7161
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
_CRTIMP size_t __cdecl wcslen(_In_z_ const wchar_t *_Str)
HRESULT QueryInterface([in] REFIID riid, [out, iid_is(riid)] void **ppvObject)
#define S_OK
Definition: intsafe.h:52
#define FAILED(hr)
Definition: intsafe.h:51
LPCWSTR szPath
Definition: env.c:37
unsigned int UINT
Definition: ndis.h:50
#define L(x)
Definition: ntvdm.h:50
@ COINIT_APARTMENTTHREADED
Definition: objbase.h:278
_Check_return_ _CRTIMP int __cdecl _wcsicmp(_In_z_ const wchar_t *_Str1, _In_z_ const wchar_t *_Str2)
_CRTIMP wchar_t *__cdecl wcscpy(_Out_writes_z_(_String_length_(_Source)+1) wchar_t *_Dest, _In_z_ const wchar_t *_Source)
_Check_return_ _CRTIMP int __cdecl wcscmp(_In_z_ const wchar_t *_Str1, _In_z_ const wchar_t *_Str2)
#define win_skip
Definition: test.h:160
#define memset(x, y, z)
Definition: compat.h:39
HRESULT hr
Definition: shlfolder.c:183
#define DPRINT
Definition: sndvol32.h:71
#define _countof(array)
Definition: sndvol32.h:68
HRESULT hrForShell
Definition: CShellLink.cpp:232
const uint16_t * PCWSTR
Definition: typedefs.h:57
int32_t INT
Definition: typedefs.h:58
_In_ DWORD _Out_ _In_ WORD wFlags
Definition: wincon.h:531
#define S_FALSE
Definition: winerror.h:2357
#define IID_PPV_ARG(Itype, ppType)
__wchar_t WCHAR
Definition: xmlstorage.h:180