[RAPPS] Stopped GCC whining (fixed GCC build)
[reactos.git] / 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 #include <atlbase.h>
10 #include <atlcom.h>
11 #include <strsafe.h>
12 #include <ndk/rtlfuncs.h>
13
14 #define NDEBUG
15 #include <debug.h>
16 #include <shellutils.h>
17
18 /* Test IShellLink::SetPath with environment-variables, existing, non-existing, ...*/
19 typedef struct
20 {
21 PCWSTR pathIn;
22 HRESULT hrSetPath;
23
24 /* Test 1 - hrGetPathX = IShellLink::GetPath(pathOutX, ... , flagsX); */
25 PCWSTR pathOut1;
26 DWORD flags1;
27 HRESULT hrGetPath1;
28 BOOL expandPathOut1;
29
30 /* Test 2 */
31 PCWSTR pathOut2;
32 DWORD flags2;
33 HRESULT hrGetPath2;
34 BOOL expandPathOut2;
35 } TEST_SHELL_LINK_DEF;
36
37 static TEST_SHELL_LINK_DEF linkTestList[] =
38 {
39 {
40 L"%comspec%", S_OK,
41 L"%comspec%", SLGP_SHORTPATH, S_OK, TRUE,
42 L"%comspec%", SLGP_RAWPATH, S_OK, FALSE
43 },
44 {
45 L"%anyvar%", E_INVALIDARG,
46 L"", SLGP_SHORTPATH, S_FALSE, FALSE,
47 L"", SLGP_RAWPATH, S_FALSE, FALSE
48 },
49 {
50 L"%anyvar%%comspec%", S_OK,
51 L"c:\\%anyvar%%comspec%", SLGP_SHORTPATH, S_OK, TRUE,
52 L"%anyvar%%comspec%", SLGP_RAWPATH, S_OK, FALSE
53 },
54 {
55 L"%temp%", S_OK,
56 L"%temp%", SLGP_SHORTPATH, S_OK, TRUE,
57 L"%temp%", SLGP_RAWPATH, S_OK, FALSE
58 },
59 {
60 L"%shell%", S_OK,
61 L"%systemroot%\\system32\\%shell%", SLGP_SHORTPATH, S_OK, TRUE,
62 L"%shell%", SLGP_RAWPATH, S_OK, FALSE
63 },
64 {
65 L"u:\\anypath\\%anyvar%", S_OK,
66 L"u:\\anypath\\%anyvar%", SLGP_SHORTPATH, S_OK, TRUE,
67 L"u:\\anypath\\%anyvar%", SLGP_RAWPATH, S_OK, FALSE
68 },
69 {
70 L"c:\\temp", S_OK,
71 L"c:\\temp", SLGP_SHORTPATH, S_OK, FALSE,
72 L"c:\\temp", SLGP_RAWPATH, S_OK, FALSE
73 },
74 {
75 L"cmd.exe", S_OK,
76 L"%comspec%", SLGP_SHORTPATH, S_OK, TRUE,
77 L"%comspec%", SLGP_RAWPATH, S_OK, TRUE
78 },
79 {
80 L"%systemroot%\\non-existent-file", S_OK,
81 L"%systemroot%\\non-existent-file", SLGP_SHORTPATH, S_OK, TRUE,
82 L"%systemroot%\\non-existent-file", SLGP_RAWPATH, S_OK, FALSE
83 },
84 {
85 L"c:\\non-existent-path\\non-existent-file", S_OK,
86 L"c:\\non-existent-path\\non-existent-file", SLGP_SHORTPATH, S_OK, FALSE,
87 L"c:\\non-existent-path\\non-existent-file", SLGP_RAWPATH, S_OK, FALSE
88 },
89 {
90 L"non-existent-file", E_INVALIDARG,
91 L"", SLGP_SHORTPATH, S_FALSE, FALSE,
92 L"", SLGP_RAWPATH, S_FALSE, FALSE
93 },
94 };
95
96 static
97 VOID
98 test_checklinkpath(UINT i, TEST_SHELL_LINK_DEF* testDef)
99 {
100 static WCHAR evVar[MAX_PATH];
101
102 HRESULT hr, expectedHr;
103 WCHAR wPathOut[MAX_PATH];
104 BOOL expandPathOut;
105 PCWSTR expectedPathOut;
106 CComPtr<IShellLinkW> psl;
107 UINT i1;
108 DWORD flags;
109
110 hr = CoCreateInstance(CLSID_ShellLink,
111 NULL,
112 CLSCTX_INPROC_SERVER,
113 IID_PPV_ARG(IShellLinkW, &psl));
114 ok(hr == S_OK, "CoCreateInstance, hr = 0x%lx\n", hr);
115 if (FAILED(hr))
116 {
117 skip("Could not instantiate CShellLink\n");
118 return;
119 }
120
121 hr = psl->SetPath(testDef->pathIn);
122 ok(hr == testDef->hrSetPath, "IShellLink::SetPath(%d), got hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrSetPath);
123
124 expectedPathOut = NULL;
125 for (i1 = 0; i1 <= 1; i1++)
126 {
127 if (i1 == 1) /* Usually SLGP_RAWPATH */
128 {
129 flags = testDef->flags1;
130 expandPathOut = testDef->expandPathOut1;
131 expectedPathOut = testDef->pathOut1;
132 expectedHr = testDef->hrGetPath1;
133 }
134 else /* Usually SLGP_SHORTPATH */
135 {
136 flags = testDef->flags2;
137 expandPathOut = testDef->expandPathOut2;
138 expectedPathOut = testDef->pathOut2;
139 expectedHr = testDef->hrGetPath2;
140 }
141
142 /* Patch some variables */
143 if (expandPathOut)
144 {
145 ExpandEnvironmentStringsW(expectedPathOut, evVar, _countof(evVar));
146 DPRINT("** %S **\n",evVar);
147 expectedPathOut = evVar;
148 }
149
150 hr = psl->GetPath(wPathOut, _countof(wPathOut), NULL, flags);
151 ok(hr == expectedHr,
152 "IShellLink::GetPath(%d), flags 0x%lx, got hr = 0x%lx, expected 0x%lx\n",
153 i, flags, hr, expectedHr);
154 ok(wcsicmp(wPathOut, expectedPathOut) == 0,
155 "IShellLink::GetPath(%d), flags 0x%lx, in %S, got %S, expected %S\n",
156 i, flags, testDef->pathIn, wPathOut, expectedPathOut);
157 }
158 }
159
160 static
161 VOID
162 TestShellLink(void)
163 {
164 UINT i;
165
166 /* Needed for test */
167 SetEnvironmentVariableW(L"shell", L"cmd.exe");
168
169 for (i = 0; i < _countof(linkTestList); ++i)
170 {
171 DPRINT("IShellLink-Test(%d): %S\n", i, linkTestList[i].pathIn);
172 test_checklinkpath(i, &linkTestList[i]);
173 }
174
175 SetEnvironmentVariableW(L"shell",NULL);
176 }
177
178 static
179 VOID
180 TestDescription(void)
181 {
182 HRESULT hr;
183 CComPtr<IShellLinkW> psl;
184 WCHAR buffer[64];
185 PCWSTR testDescription = L"This is a test description";
186
187 /* Test SetDescription */
188 hr = CoCreateInstance(CLSID_ShellLink,
189 NULL,
190 CLSCTX_INPROC_SERVER,
191 IID_PPV_ARG(IShellLinkW, &psl));
192 ok(hr == S_OK, "CoCreateInstance, hr = 0x%lx\n", hr);
193 if (FAILED(hr))
194 {
195 skip("Could not instantiate CShellLink\n");
196 return;
197 }
198
199 memset(buffer, 0x55, sizeof(buffer));
200 hr = psl->GetDescription(buffer, RTL_NUMBER_OF(buffer));
201 ok(hr == S_OK, "IShellLink::GetDescription returned hr = 0x%lx\n", hr);
202 ok(buffer[0] == 0, "buffer[0] = %x\n", buffer[0]);
203 ok(buffer[1] == 0x5555, "buffer[1] = %x\n", buffer[1]);
204
205 hr = psl->SetDescription(testDescription);
206 ok(hr == S_OK, "IShellLink::SetDescription returned hr = 0x%lx\n", hr);
207
208 memset(buffer, 0x55, sizeof(buffer));
209 hr = psl->GetDescription(buffer, RTL_NUMBER_OF(buffer));
210 ok(hr == S_OK, "IShellLink::GetDescription returned hr = 0x%lx\n", hr);
211 ok(buffer[wcslen(testDescription)] == 0, "buffer[n] = %x\n", buffer[wcslen(testDescription)]);
212 ok(buffer[wcslen(testDescription) + 1] == 0x5555, "buffer[n+1] = %x\n", buffer[wcslen(testDescription) + 1]);
213 ok(!wcscmp(buffer, testDescription), "buffer = '%ls'\n", buffer);
214
215 hr = psl->SetDescription(NULL);
216 ok(hr == S_OK, "IShellLink::SetDescription returned hr = 0x%lx\n", hr);
217
218 memset(buffer, 0x55, sizeof(buffer));
219 hr = psl->GetDescription(buffer, RTL_NUMBER_OF(buffer));
220 ok(hr == S_OK, "IShellLink::GetDescription returned hr = 0x%lx\n", hr);
221 ok(buffer[0] == 0, "buffer[0] = %x\n", buffer[0]);
222 ok(buffer[1] == 0x5555, "buffer[1] = %x\n", buffer[1]);
223 }
224
225
226 /* Test IShellLink::Get/SetIconLocation and IExtractIcon::GetIconLocation */
227 typedef struct
228 {
229 PCWSTR FilePath;
230
231 /* Expected results */
232 HRESULT hrDefIcon; // Return value for GIL_DEFAULTICON
233 HRESULT hrForShrt; // Return value for GIL_FORSHORTCUT
234 /* Return values for GIL_FORSHELL */
235 HRESULT hrForShell;
236 PCWSTR IconPath;
237 UINT Flags;
238 } TEST_SHELL_ICON;
239
240 static TEST_SHELL_ICON ShIconTests[] =
241 {
242 /* Executable with icons */
243 {L"%SystemRoot%\\system32\\cmd.exe", S_FALSE, E_INVALIDARG,
244 S_OK, L"%SystemRoot%\\system32\\cmd.exe", GIL_NOTFILENAME | GIL_PERINSTANCE},
245
246 /* Executable without icon */
247 {L"%SystemRoot%\\system32\\autochk.exe", S_FALSE, E_INVALIDARG,
248 S_OK, L"%SystemRoot%\\system32\\autochk.exe", GIL_NOTFILENAME | GIL_PERINSTANCE},
249
250 /* Existing file */
251 {L"%SystemRoot%\\system32\\shell32.dll", S_FALSE, E_INVALIDARG,
252 S_OK, L"*", GIL_NOTFILENAME | GIL_PERCLASS},
253
254 /* Non-existing files */
255 {L"%SystemRoot%\\non-existent-file.sdf", S_FALSE, E_INVALIDARG,
256 S_OK, L"*", GIL_NOTFILENAME | GIL_PERCLASS},
257 {L"c:\\non-existent-path\\non-existent-file.sdf", S_FALSE, E_INVALIDARG,
258 S_OK, L"*", GIL_NOTFILENAME | GIL_PERCLASS},
259 };
260
261 static
262 VOID
263 test_iconlocation(UINT i, TEST_SHELL_ICON* testDef)
264 {
265 HRESULT hr;
266 CComPtr<IShellLinkW> psl;
267 CComPtr<IExtractIconW> pei;
268 INT iIcon;
269 UINT wFlags;
270 PCWSTR pszExplorer = L"%SystemRoot%\\explorer.exe";
271 WCHAR szPath[MAX_PATH];
272 WCHAR szPath2[MAX_PATH];
273
274 hr = CoCreateInstance(CLSID_ShellLink,
275 NULL,
276 CLSCTX_INPROC_SERVER,
277 IID_PPV_ARG(IShellLinkW, &psl));
278 ok(hr == S_OK, "CoCreateInstance, hr = 0x%lx\n", hr);
279 if (FAILED(hr))
280 {
281 skip("Could not instantiate CShellLink\n");
282 return;
283 }
284
285 /* Set the path to a file */
286 ExpandEnvironmentStringsW(testDef->FilePath, szPath, _countof(szPath));
287 hr = psl->SetPath(szPath);
288 ok(hr == S_OK, "IShellLink::SetPath failed, hr = 0x%lx\n", hr);
289
290 /*
291 * This test shows that this does not imply that the icon is automatically
292 * set and be retrieved naively by a call to IShellLink::GetIconLocation.
293 */
294 iIcon = 0xdeadbeef;
295 wcscpy(szPath, L"garbage");
296 hr = psl->GetIconLocation(szPath, _countof(szPath), &iIcon);
297 ok(hr == S_OK, "IShellLink::GetIconLocation(%d) failed, hr = 0x%lx\n", i, hr);
298 ok(*szPath == L'\0', "IShellLink::GetIconLocation(%d) returned '%S'\n", i, szPath);
299 ok(iIcon == 0, "IShellLink::GetIconLocation(%d) returned %d, expected %d\n", i, iIcon, 0);
300
301 /* Try to grab the IExtractIconW interface */
302 hr = psl->QueryInterface(IID_PPV_ARG(IExtractIconW, &pei));
303 ok(hr == S_OK, "IShellLink::QueryInterface(IExtractIconW)(%d) failed, hr = 0x%lx\n", i, hr);
304 if (!pei)
305 {
306 win_skip("No IExtractIconW interface\n");
307 return;
308 }
309
310 iIcon = wFlags = 0xdeadbeef;
311 wcscpy(szPath, L"garbage");
312 hr = pei->GetIconLocation(GIL_DEFAULTICON, szPath, _countof(szPath), &iIcon, &wFlags);
313 ok(hr == testDef->hrDefIcon, "IExtractIcon::GetIconLocation(%d) returned hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrDefIcon);
314 ok(*szPath == L'\0', "IExtractIcon::GetIconLocation(%d) returned '%S'\n", i, szPath);
315 // ok(iIcon == 0, "IExtractIcon::GetIconLocation(%d) returned %d\n", i, iIcon);
316
317 iIcon = wFlags = 0xdeadbeef;
318 wcscpy(szPath, L"garbage");
319 hr = pei->GetIconLocation(GIL_FORSHORTCUT, szPath, _countof(szPath), &iIcon, &wFlags);
320 ok(hr == testDef->hrForShrt, "IExtractIcon::GetIconLocation(%d) returned hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrForShrt);
321 // Here, both szPath and iIcon are untouched...
322
323 iIcon = wFlags = 0xdeadbeef;
324 wcscpy(szPath, L"garbage");
325 hr = pei->GetIconLocation(GIL_FORSHELL, szPath, _countof(szPath), &iIcon, &wFlags);
326 ok(hr == testDef->hrForShell, "IExtractIcon::GetIconLocation(%d) returned hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrForShell);
327 ok(wFlags == testDef->Flags, "IExtractIcon::GetIconLocation(%d) returned wFlags = 0x%x, expected 0x%x\n", i, wFlags, testDef->Flags);
328 /*
329 * Actually, even if wFlags specifies GIL_NOTFILENAME, a correct file name is returned
330 * for executables only (at least...), otherwise we can get an asterix '*'.
331 */
332 ExpandEnvironmentStringsW(testDef->IconPath, szPath2, _countof(szPath2));
333 ok(_wcsicmp(szPath2, szPath) == 0, "IExtractIcon::GetIconLocation(%d) returned '%S', expected '%S'\n", i, szPath, szPath2);
334
335 // ok(*szPath == L'\0', "IExtractIcon::GetIconLocation returned '%S'\n", szPath);
336 // ok(iIcon == 0, "IExtractIcon::GetIconLocation returned %d\n", iIcon);
337 // ok(FALSE, "hr = 0x%lx, szPath = '%S', iIcon = %d, wFlags = %d\n", hr, szPath, iIcon, wFlags);
338
339
340 /*
341 * Now we test what happens when we explicitly set an icon to the shortcut.
342 * Note that actually, SetIconLocation() does not verify whether the file
343 * really exists.
344 */
345 hr = psl->SetIconLocation(pszExplorer, 1);
346 ok(hr == S_OK, "IShellLink::SetIconLocation(%d) failed, hr = 0x%lx\n", i, hr);
347
348 /*
349 * First, we call IShellLink::GetIconLocation. We retrieve
350 * exactly what we specified with SetIconLocation.
351 */
352 iIcon = 0xdeadbeef;
353 wcscpy(szPath, L"garbage");
354 hr = psl->GetIconLocation(szPath, _countof(szPath), &iIcon);
355 ok(hr == S_OK, "IShellLink::GetIconLocation(%d) failed, hr = 0x%lx\n", i, hr);
356 ok(wcscmp(szPath, pszExplorer) == 0, "IShellLink::GetIconLocation(%d) returned '%S', expected '%S'\n", i, szPath, pszExplorer);
357 ok(iIcon == 1, "IShellLink::GetIconLocation(%d) returned %d, expected %d\n", i, iIcon, 1);
358
359 /*
360 * Now we test what happens with IExtractIcon::GetIconLocation.
361 * We see that it retrieves the icon of the shortcut's underlying file.
362 */
363 iIcon = wFlags = 0xdeadbeef;
364 wcscpy(szPath, L"garbage");
365 hr = pei->GetIconLocation(GIL_DEFAULTICON, szPath, _countof(szPath), &iIcon, &wFlags);
366 ok(hr == testDef->hrDefIcon, "IExtractIcon::GetIconLocation(%d) returned hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrDefIcon);
367 ok(*szPath == L'\0', "IExtractIcon::GetIconLocation(%d) returned '%S'\n", i, szPath);
368 // ok(iIcon == 0, "IExtractIcon::GetIconLocation(%d) returned %d\n", i, iIcon);
369
370 iIcon = wFlags = 0xdeadbeef;
371 wcscpy(szPath, L"garbage");
372 hr = pei->GetIconLocation(GIL_FORSHORTCUT, szPath, _countof(szPath), &iIcon, &wFlags);
373 ok(hr == testDef->hrForShrt, "IExtractIcon::GetIconLocation(%d) returned hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrForShrt);
374 // Here, both szPath and iIcon are untouched...
375
376 iIcon = wFlags = 0xdeadbeef;
377 wcscpy(szPath, L"garbage");
378 hr = pei->GetIconLocation(GIL_FORSHELL, szPath, _countof(szPath), &iIcon, &wFlags);
379 ok(hr == testDef->hrForShell, "IExtractIcon::GetIconLocation(%d) returned hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrForShell);
380 ok(wFlags == testDef->Flags, "IExtractIcon::GetIconLocation(%d) returned wFlags = 0x%x, expected 0x%x\n", i, wFlags, testDef->Flags);
381 /*
382 * Actually, even if wFlags specifies GIL_NOTFILENAME, a correct file name is returned
383 * for executables only (at least...), otherwise we can get an asterix '*'.
384 */
385 ExpandEnvironmentStringsW(testDef->IconPath, szPath2, _countof(szPath2));
386 ok(_wcsicmp(szPath2, szPath) == 0, "IExtractIcon::GetIconLocation(%d) returned '%S', expected '%S'\n", i, szPath, szPath2);
387
388 // ok(*szPath == L'\0', "IExtractIcon::GetIconLocation returned '%S'\n", szPath);
389 // ok(iIcon == 0, "IExtractIcon::GetIconLocation returned %d\n", iIcon);
390 // ok(FALSE, "hr = 0x%lx, szPath = '%S', iIcon = %d, wFlags = %d\n", hr, szPath, iIcon, wFlags);
391 }
392
393 static
394 VOID
395 TestIconLocation(void)
396 {
397 UINT i;
398
399 for (i = 0; i < _countof(ShIconTests); ++i)
400 {
401 test_iconlocation(i, &ShIconTests[i]);
402 }
403 }
404
405
406 START_TEST(CShellLink)
407 {
408 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
409
410 TestShellLink();
411 TestDescription();
412 TestIconLocation();
413
414 CoUninitialize();
415 }