Reactos
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{
18 PCWSTR pathIn;
19 HRESULT hrSetPath;
20
21 /* Test 1 - hrGetPathX = IShellLink::GetPath(pathOutX, ... , flagsX); */
22 PCWSTR pathOut1;
23 DWORD flags1;
24 HRESULT hrGetPath1;
25 BOOL expandPathOut1;
26
27 /* Test 2 */
28 PCWSTR pathOut2;
29 DWORD flags2;
30 HRESULT hrGetPath2;
31 BOOL expandPathOut2;
32} TEST_SHELL_LINK_DEF;
33
34static TEST_SHELL_LINK_DEF linkTestList_WS03[] =
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 TEST_SHELL_LINK_DEF linkTestList_Vista[] =
94{
95 {
96 L"%comspec%", S_OK,
97 L"%comspec%", SLGP_SHORTPATH, S_OK, TRUE,
98 L"%comspec%", SLGP_RAWPATH, S_OK, FALSE
99 },
100 {
101 L"%anyvar%", S_OK,
102 L"%USERPROFILE%\\Desktop\\%anyvar%", SLGP_SHORTPATH, S_OK, TRUE,
103 L"%USERPROFILE%\\Desktop\\%anyvar%", SLGP_RAWPATH, S_OK, TRUE
104 },
105 {
106 L"%anyvar%%comspec%", E_INVALIDARG,
107 L"", SLGP_SHORTPATH, S_FALSE, TRUE,
108 L"%anyvar%%comspec%", SLGP_RAWPATH, S_OK, FALSE
109 },
110 {
111 L"%temp%", S_OK,
112 L"%temp%", SLGP_SHORTPATH, S_OK, TRUE,
113 L"%temp%", SLGP_RAWPATH, S_OK, FALSE
114 },
115 {
116 L"%shell%", S_OK,
117 L"%systemroot%\\system32\\%shell%", SLGP_SHORTPATH, S_OK, TRUE,
118 L"%shell%", SLGP_RAWPATH, S_OK, FALSE
119 },
120 {
121 L"u:\\anypath\\%anyvar%", S_OK,
122 L"u:\\anypath\\%anyvar%", SLGP_SHORTPATH, S_OK, TRUE,
123 L"u:\\anypath\\%anyvar%", SLGP_RAWPATH, S_OK, FALSE
124 },
125 {
126 L"c:\\temp", S_OK,
127 L"c:\\temp", SLGP_SHORTPATH, S_OK, FALSE,
128 L"c:\\temp", SLGP_RAWPATH, S_OK, FALSE
129 },
130 {
131 L"cmd.exe", S_OK,
132 L"%comspec%", SLGP_SHORTPATH, S_OK, TRUE,
133 L"%comspec%", SLGP_RAWPATH, S_OK, TRUE
134 },
135 {
136 L"%systemroot%\\non-existent-file", S_OK,
137 L"%systemroot%\\non-existent-file", SLGP_SHORTPATH, S_OK, TRUE,
138 L"%systemroot%\\non-existent-file", SLGP_RAWPATH, S_OK, FALSE
139 },
140 {
141 L"c:\\non-existent-path\\non-existent-file", S_OK,
142 L"c:\\non-existent-path\\non-existent-file", SLGP_SHORTPATH, S_OK, FALSE,
143 L"c:\\non-existent-path\\non-existent-file", SLGP_RAWPATH, S_OK, FALSE
144 },
145 {
146 L"non-existent-file", S_OK,
147 L"%USERPROFILE%\\Desktop\\non-existent-file", SLGP_SHORTPATH, S_OK, TRUE,
148 L"%USERPROFILE%\\Desktop\\non-existent-file", SLGP_RAWPATH, S_OK, TRUE
149 },
150};
151
152static
153VOID
154test_checklinkpath(UINT i, TEST_SHELL_LINK_DEF* testDef)
155{
156static WCHAR evVar[MAX_PATH];
157
158 HRESULT hr, expectedHr;
159 WCHAR wPathOut[MAX_PATH];
160 BOOL expandPathOut;
161 PCWSTR expectedPathOut;
162 CComPtr<IShellLinkW> psl;
163 UINT i1;
164 DWORD flags;
165
166 hr = CoCreateInstance(CLSID_ShellLink,
167 NULL,
168 CLSCTX_INPROC_SERVER,
169 IID_PPV_ARG(IShellLinkW, &psl));
170 ok(hr == S_OK, "CoCreateInstance, hr = 0x%lx\n", hr);
171 if (FAILED(hr))
172 {
173 skip("Could not instantiate CShellLink\n");
174 return;
175 }
176
177 hr = psl->SetPath(testDef->pathIn);
178 ok(hr == testDef->hrSetPath, "IShellLink::SetPath(%d), got hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrSetPath);
179
180 expectedPathOut = NULL;
181 for (i1 = 0; i1 <= 1; i1++)
182 {
183 if (i1 == 0) /* Usually SLGP_SHORTPATH */
184 {
185 flags = testDef->flags1;
186 expandPathOut = testDef->expandPathOut1;
187 expectedPathOut = testDef->pathOut1;
188 expectedHr = testDef->hrGetPath1;
189 }
190 else // if (i1 == 1) /* Usually SLGP_RAWPATH */
191 {
192 flags = testDef->flags2;
193 expandPathOut = testDef->expandPathOut2;
194 expectedPathOut = testDef->pathOut2;
195 expectedHr = testDef->hrGetPath2;
196 }
197
198 /* Patch some variables */
199 if (expandPathOut)
200 {
201 ExpandEnvironmentStringsW(expectedPathOut, evVar, _countof(evVar));
202 DPRINT("** %S **\n",evVar);
203 expectedPathOut = evVar;
204 }
205
206 hr = psl->GetPath(wPathOut, _countof(wPathOut), NULL, flags);
207 ok(hr == expectedHr,
208 "IShellLink::GetPath(%d), flags 0x%lx, got hr = 0x%lx, expected 0x%lx\n",
209 i, flags, hr, expectedHr);
210 ok(_wcsicmp(wPathOut, expectedPathOut) == 0,
211 "IShellLink::GetPath(%d), flags 0x%lx, in %S, got %S, expected %S\n",
212 i, flags, testDef->pathIn, wPathOut, expectedPathOut);
213 }
214}
215
216static
217VOID
218TestShellLink(void)
219{
220 UINT i, TestListCnt;
221 TEST_SHELL_LINK_DEF* Test;
222
223 /* Needed for test */
224 SetEnvironmentVariableW(L"shell", L"cmd.exe");
225
226 if (GetNTVersion() <= _WIN32_WINNT_WS03)
227 {
228 Test = linkTestList_WS03;
229 TestListCnt = _countof(linkTestList_WS03);
230 }
231 else
232 {
233 Test = linkTestList_Vista;
234 TestListCnt = _countof(linkTestList_Vista);
235 }
236
237 for (i = 0; i < TestListCnt; ++i)
238 {
239 DPRINT("IShellLink-Test(%d): %S\n", i, Test[i].pathIn);
240 test_checklinkpath(i, &Test[i]);
241 }
242
243 SetEnvironmentVariableW(L"shell",NULL);
244}
245
246static
247VOID
248TestDescription(void)
249{
250 HRESULT hr;
251 CComPtr<IShellLinkW> psl;
252 WCHAR buffer[64];
253 PCWSTR testDescription = L"This is a test description";
254
255 /* Test SetDescription */
256 hr = CoCreateInstance(CLSID_ShellLink,
257 NULL,
258 CLSCTX_INPROC_SERVER,
259 IID_PPV_ARG(IShellLinkW, &psl));
260 ok(hr == S_OK, "CoCreateInstance, hr = 0x%lx\n", hr);
261 if (FAILED(hr))
262 {
263 skip("Could not instantiate CShellLink\n");
264 return;
265 }
266
267 memset(buffer, 0x55, sizeof(buffer));
268 hr = psl->GetDescription(buffer, RTL_NUMBER_OF(buffer));
269 ok(hr == S_OK, "IShellLink::GetDescription returned hr = 0x%lx\n", hr);
270 ok(buffer[0] == 0, "buffer[0] = %x\n", buffer[0]);
271 ok(buffer[1] == 0x5555, "buffer[1] = %x\n", buffer[1]);
272
273 hr = psl->SetDescription(testDescription);
274 ok(hr == S_OK, "IShellLink::SetDescription returned hr = 0x%lx\n", hr);
275
276 memset(buffer, 0x55, sizeof(buffer));
277 hr = psl->GetDescription(buffer, RTL_NUMBER_OF(buffer));
278 ok(hr == S_OK, "IShellLink::GetDescription returned hr = 0x%lx\n", hr);
279 ok(buffer[wcslen(testDescription)] == 0, "buffer[n] = %x\n", buffer[wcslen(testDescription)]);
280 ok(buffer[wcslen(testDescription) + 1] == 0x5555, "buffer[n+1] = %x\n", buffer[wcslen(testDescription) + 1]);
281 ok(!wcscmp(buffer, testDescription), "buffer = '%ls'\n", buffer);
282
283 hr = psl->SetDescription(NULL);
284 ok(hr == S_OK, "IShellLink::SetDescription returned hr = 0x%lx\n", hr);
285
286 memset(buffer, 0x55, sizeof(buffer));
287 hr = psl->GetDescription(buffer, RTL_NUMBER_OF(buffer));
288 ok(hr == S_OK, "IShellLink::GetDescription returned hr = 0x%lx\n", hr);
289 ok(buffer[0] == 0, "buffer[0] = %x\n", buffer[0]);
290 ok(buffer[1] == 0x5555, "buffer[1] = %x\n", buffer[1]);
291}
292
293
294/* Test IShellLink::Get/SetIconLocation and IExtractIcon::GetIconLocation */
295typedef struct
296{
297 PCWSTR FilePath;
298
299 /* Expected results */
300 HRESULT hrDefIcon; // Return value for GIL_DEFAULTICON
301 HRESULT hrForShrt; // Return value for GIL_FORSHORTCUT
302 /* Return values for GIL_FORSHELL */
303 HRESULT hrForShell;
304 PCWSTR IconPath;
305 UINT Flags;
306} TEST_SHELL_ICON;
307
308static TEST_SHELL_ICON ShIconTests[] =
309{
310 /* Executable with icons */
311 {L"%SystemRoot%\\system32\\cmd.exe", S_FALSE, E_INVALIDARG,
312 S_OK, L"%SystemRoot%\\system32\\cmd.exe", GIL_NOTFILENAME | GIL_PERINSTANCE},
313
314 /* Executable without icon */
315 {L"%SystemRoot%\\system32\\autochk.exe", S_FALSE, E_INVALIDARG,
316 S_OK, L"%SystemRoot%\\system32\\autochk.exe", GIL_NOTFILENAME | GIL_PERINSTANCE},
317
318 /* Existing file */
319 {L"%SystemRoot%\\system32\\shell32.dll", S_FALSE, E_INVALIDARG,
320 S_OK, L"*", GIL_NOTFILENAME | GIL_PERCLASS},
321
322 /* Non-existing files */
323 {L"%SystemRoot%\\non-existent-file.sdf", S_FALSE, E_INVALIDARG,
324 S_OK, L"*", GIL_NOTFILENAME | GIL_PERCLASS},
325 {L"c:\\non-existent-path\\non-existent-file.sdf", S_FALSE, E_INVALIDARG,
326 S_OK, L"*", GIL_NOTFILENAME | GIL_PERCLASS},
327};
328
329static
330VOID
331test_iconlocation(UINT i, TEST_SHELL_ICON* testDef)
332{
333 HRESULT hr;
334 CComPtr<IShellLinkW> psl;
335 CComPtr<IExtractIconW> pei;
336 INT iIcon;
337 UINT wFlags;
338 PCWSTR pszExplorer = L"%SystemRoot%\\explorer.exe";
339 WCHAR szPath[MAX_PATH];
340 WCHAR szPath2[MAX_PATH];
341
342 hr = CoCreateInstance(CLSID_ShellLink,
343 NULL,
344 CLSCTX_INPROC_SERVER,
345 IID_PPV_ARG(IShellLinkW, &psl));
346 ok(hr == S_OK, "CoCreateInstance, hr = 0x%lx\n", hr);
347 if (FAILED(hr))
348 {
349 skip("Could not instantiate CShellLink\n");
350 return;
351 }
352
353 /* Set the path to a file */
354 ExpandEnvironmentStringsW(testDef->FilePath, szPath, _countof(szPath));
355 hr = psl->SetPath(szPath);
356 ok(hr == S_OK, "IShellLink::SetPath failed, hr = 0x%lx\n", hr);
357
358 /*
359 * This test shows that this does not imply that the icon is automatically
360 * set and be retrieved naively by a call to IShellLink::GetIconLocation.
361 */
362 iIcon = 0xdeadbeef;
363 wcscpy(szPath, L"garbage");
364 hr = psl->GetIconLocation(szPath, _countof(szPath), &iIcon);
365 ok(hr == S_OK, "IShellLink::GetIconLocation(%d) failed, hr = 0x%lx\n", i, hr);
366 ok(*szPath == L'\0', "IShellLink::GetIconLocation(%d) returned '%S'\n", i, szPath);
367 ok(iIcon == 0, "IShellLink::GetIconLocation(%d) returned %d, expected %d\n", i, iIcon, 0);
368
369 /* Try to grab the IExtractIconW interface */
370 hr = psl->QueryInterface(IID_PPV_ARG(IExtractIconW, &pei));
371 ok(hr == S_OK, "IShellLink::QueryInterface(IExtractIconW)(%d) failed, hr = 0x%lx\n", i, hr);
372 if (!pei)
373 {
374 win_skip("No IExtractIconW interface\n");
375 return;
376 }
377
378 iIcon = wFlags = 0xdeadbeef;
379 wcscpy(szPath, L"garbage");
380 hr = pei->GetIconLocation(GIL_DEFAULTICON, szPath, _countof(szPath), &iIcon, &wFlags);
381 ok(hr == testDef->hrDefIcon, "IExtractIcon::GetIconLocation(%d) returned hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrDefIcon);
382 ok(*szPath == L'\0', "IExtractIcon::GetIconLocation(%d) returned '%S'\n", i, szPath);
383 // ok(iIcon == 0, "IExtractIcon::GetIconLocation(%d) returned %d\n", i, iIcon);
384
385 iIcon = wFlags = 0xdeadbeef;
386 wcscpy(szPath, L"garbage");
387 hr = pei->GetIconLocation(GIL_FORSHORTCUT, szPath, _countof(szPath), &iIcon, &wFlags);
388 ok(hr == testDef->hrForShrt, "IExtractIcon::GetIconLocation(%d) returned hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrForShrt);
389 // Here, both szPath and iIcon are untouched...
390
391 iIcon = wFlags = 0xdeadbeef;
392 wcscpy(szPath, L"garbage");
393 hr = pei->GetIconLocation(GIL_FORSHELL, szPath, _countof(szPath), &iIcon, &wFlags);
394 ok(hr == testDef->hrForShell, "IExtractIcon::GetIconLocation(%d) returned hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrForShell);
395 ok(wFlags == testDef->Flags, "IExtractIcon::GetIconLocation(%d) returned wFlags = 0x%x, expected 0x%x\n", i, wFlags, testDef->Flags);
396 /*
397 * Actually, even if wFlags specifies GIL_NOTFILENAME, a correct file name is returned
398 * for executables only (at least...), otherwise we can get an asterix '*'.
399 */
400 ExpandEnvironmentStringsW(testDef->IconPath, szPath2, _countof(szPath2));
401 ok(_wcsicmp(szPath2, szPath) == 0, "IExtractIcon::GetIconLocation(%d) returned '%S', expected '%S'\n", i, szPath, szPath2);
402
403 // ok(*szPath == L'\0', "IExtractIcon::GetIconLocation returned '%S'\n", szPath);
404 // ok(iIcon == 0, "IExtractIcon::GetIconLocation returned %d\n", iIcon);
405 // ok(FALSE, "hr = 0x%lx, szPath = '%S', iIcon = %d, wFlags = %d\n", hr, szPath, iIcon, wFlags);
406
407
408 /*
409 * Now we test what happens when we explicitly set an icon to the shortcut.
410 * Note that actually, SetIconLocation() does not verify whether the file
411 * really exists.
412 */
413 hr = psl->SetIconLocation(pszExplorer, 1);
414 ok(hr == S_OK, "IShellLink::SetIconLocation(%d) failed, hr = 0x%lx\n", i, hr);
415
416 /*
417 * First, we call IShellLink::GetIconLocation. We retrieve
418 * exactly what we specified with SetIconLocation.
419 */
420 iIcon = 0xdeadbeef;
421 wcscpy(szPath, L"garbage");
422 hr = psl->GetIconLocation(szPath, _countof(szPath), &iIcon);
423 ok(hr == S_OK, "IShellLink::GetIconLocation(%d) failed, hr = 0x%lx\n", i, hr);
424 ok(wcscmp(szPath, pszExplorer) == 0, "IShellLink::GetIconLocation(%d) returned '%S', expected '%S'\n", i, szPath, pszExplorer);
425 ok(iIcon == 1, "IShellLink::GetIconLocation(%d) returned %d, expected %d\n", i, iIcon, 1);
426
427 /*
428 * Now we test what happens with IExtractIcon::GetIconLocation.
429 * We see that it retrieves the icon of the shortcut's underlying file.
430 */
431 iIcon = wFlags = 0xdeadbeef;
432 wcscpy(szPath, L"garbage");
433 hr = pei->GetIconLocation(GIL_DEFAULTICON, szPath, _countof(szPath), &iIcon, &wFlags);
434 ok(hr == testDef->hrDefIcon, "IExtractIcon::GetIconLocation(%d) returned hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrDefIcon);
435 ok(*szPath == L'\0', "IExtractIcon::GetIconLocation(%d) returned '%S'\n", i, szPath);
436 // ok(iIcon == 0, "IExtractIcon::GetIconLocation(%d) returned %d\n", i, iIcon);
437
438 iIcon = wFlags = 0xdeadbeef;
439 wcscpy(szPath, L"garbage");
440 hr = pei->GetIconLocation(GIL_FORSHORTCUT, szPath, _countof(szPath), &iIcon, &wFlags);
441 ok(hr == testDef->hrForShrt, "IExtractIcon::GetIconLocation(%d) returned hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrForShrt);
442 // Here, both szPath and iIcon are untouched...
443
444 iIcon = wFlags = 0xdeadbeef;
445 wcscpy(szPath, L"garbage");
446 hr = pei->GetIconLocation(GIL_FORSHELL, szPath, _countof(szPath), &iIcon, &wFlags);
447 ok(hr == testDef->hrForShell, "IExtractIcon::GetIconLocation(%d) returned hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrForShell);
448 ok(wFlags == testDef->Flags, "IExtractIcon::GetIconLocation(%d) returned wFlags = 0x%x, expected 0x%x\n", i, wFlags, testDef->Flags);
449 /*
450 * Actually, even if wFlags specifies GIL_NOTFILENAME, a correct file name is returned
451 * for executables only (at least...), otherwise we can get an asterix '*'.
452 */
453 ExpandEnvironmentStringsW(testDef->IconPath, szPath2, _countof(szPath2));
454 ok(_wcsicmp(szPath2, szPath) == 0, "IExtractIcon::GetIconLocation(%d) returned '%S', expected '%S'\n", i, szPath, szPath2);
455
456 // ok(*szPath == L'\0', "IExtractIcon::GetIconLocation returned '%S'\n", szPath);
457 // ok(iIcon == 0, "IExtractIcon::GetIconLocation returned %d\n", iIcon);
458 // ok(FALSE, "hr = 0x%lx, szPath = '%S', iIcon = %d, wFlags = %d\n", hr, szPath, iIcon, wFlags);
459}
460
461static
462VOID
463TestIconLocation(void)
464{
465 UINT i;
466
467 for (i = 0; i < _countof(ShIconTests); ++i)
468 {
469 test_iconlocation(i, &ShIconTests[i]);
470 }
471}
472
473
474START_TEST(CShellLink)
475{
476 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
477
478 TestShellLink();
479 TestDescription();
480 TestIconLocation();
481
482 CoUninitialize();
483}