[HEADERS] Move some helpers from undocshell.h to shellutils.h as they didn't cover...
[reactos.git] / modules / rostests / apitests / shell32 / CShellLink.cpp
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, ...*/
16 typedef 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
34 static TEST_SHELL_LINK_DEF linkTestList[] =
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
93 static
94 VOID
95 test_checklinkpath(UINT i, TEST_SHELL_LINK_DEF* testDef)
96 {
97 static 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
157 static
158 VOID
159 TestShellLink(void)
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);
169 test_checklinkpath(i, &linkTestList[i]);
170 }
171
172 SetEnvironmentVariableW(L"shell",NULL);
173 }
174
175 static
176 VOID
177 TestDescription(void)
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 */
224 typedef struct
225 {
226 PCWSTR FilePath;
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 */
232 HRESULT hrForShell;
233 PCWSTR IconPath;
234 UINT Flags;
235 } TEST_SHELL_ICON;
236
237 static TEST_SHELL_ICON ShIconTests[] =
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
258 static
259 VOID
260 test_iconlocation(UINT i, TEST_SHELL_ICON* testDef)
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";
268 WCHAR szPath[MAX_PATH];
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 */
283 ExpandEnvironmentStringsW(testDef->FilePath, szPath, _countof(szPath));
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 */
299 hr = psl->QueryInterface(IID_PPV_ARG(IExtractIconW, &pei));
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
390 static
391 VOID
392 TestIconLocation(void)
393 {
394 UINT i;
395
396 for (i = 0; i < _countof(ShIconTests); ++i)
397 {
398 test_iconlocation(i, &ShIconTests[i]);
399 }
400 }
401
402
403 START_TEST(CShellLink)
404 {
405 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
406
407 TestShellLink();
408 TestDescription();
409 TestIconLocation();
410
411 CoUninitialize();
412 }