ae3155931001bb026829433eaca29476f3d0d8e7
[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"c:\\non-existent-path\\non-existent-file", S_OK,
81 L"c:\\non-existent-path\\non-existent-file", SLGP_SHORTPATH, S_OK, FALSE,
82 L"c:\\non-existent-path\\non-existent-file", SLGP_RAWPATH, S_OK, FALSE
83 },
84 {
85 L"non-existent-file", E_INVALIDARG,
86 L"", SLGP_SHORTPATH, S_FALSE, FALSE,
87 L"", SLGP_RAWPATH, S_FALSE, FALSE
88 },
89 };
90
91 static
92 VOID
93 test_checklinkpath(UINT i, TEST_SHELL_LINK_DEF* testDef)
94 {
95 static WCHAR evVar[MAX_PATH];
96
97 HRESULT hr, expectedHr;
98 WCHAR wPathOut[MAX_PATH];
99 BOOL expandPathOut;
100 PCWSTR expectedPathOut;
101 IShellLinkW *psl;
102 UINT i1;
103 DWORD flags;
104
105 hr = CoCreateInstance(CLSID_ShellLink,
106 NULL,
107 CLSCTX_INPROC_SERVER,
108 IID_PPV_ARG(IShellLinkW, &psl));
109 ok(hr == S_OK, "CoCreateInstance, hr = 0x%lx\n", hr);
110 if (FAILED(hr))
111 {
112 skip("Could not instantiate CShellLink\n");
113 return;
114 }
115
116 hr = psl->SetPath(testDef->pathIn);
117 ok(hr == testDef->hrSetPath, "IShellLink::SetPath(%d), got hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrSetPath);
118
119 expectedPathOut = NULL;
120 for (i1 = 0; i1 <= 1; i1++)
121 {
122 if (i1 == 1) /* Usually SLGP_RAWPATH */
123 {
124 flags = testDef->flags1;
125 expandPathOut = testDef->expandPathOut1;
126 expectedPathOut = testDef->pathOut1;
127 expectedHr = testDef->hrGetPath1;
128 }
129 else /* Usually SLGP_SHORTPATH */
130 {
131 flags = testDef->flags2;
132 expandPathOut = testDef->expandPathOut2;
133 expectedPathOut = testDef->pathOut2;
134 expectedHr = testDef->hrGetPath2;
135 }
136
137 /* Patch some variables */
138 if (expandPathOut)
139 {
140 ExpandEnvironmentStringsW(expectedPathOut, evVar, _countof(evVar));
141 DPRINT("** %S **\n",evVar);
142 expectedPathOut = evVar;
143 }
144
145 hr = psl->GetPath(wPathOut, _countof(wPathOut), NULL, flags);
146 ok(hr == expectedHr,
147 "IShellLink::GetPath(%d), flags 0x%lx, got hr = 0x%lx, expected 0x%lx\n",
148 i, flags, hr, expectedHr);
149 ok(wcsicmp(wPathOut, expectedPathOut) == 0,
150 "IShellLink::GetPath(%d), flags 0x%lx, in %S, got %S, expected %S\n",
151 i, flags, testDef->pathIn, wPathOut, expectedPathOut);
152 }
153
154 psl->Release();
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 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 psl->Release();
222 }
223
224
225 /* Test IShellLink::Get/SetIconLocation and IExtractIcon::GetIconLocation */
226 typedef struct
227 {
228 PCWSTR FilePath;
229
230 /* Expected results */
231 HRESULT hrDefIcon; // Return value for GIL_DEFAULTICON
232 HRESULT hrForShrt; // Return value for GIL_FORSHORTCUT
233 /* Return values for GIL_FORSHELL */
234 HRESULT hrForShell;
235 PCWSTR IconPath;
236 UINT Flags;
237 } TEST_SHELL_ICON;
238
239 static TEST_SHELL_ICON ShIconTests[] =
240 {
241 /* Executable with icons */
242 {L"%SystemRoot%\\system32\\cmd.exe", S_FALSE, E_INVALIDARG,
243 S_OK, L"%SystemRoot%\\system32\\cmd.exe", GIL_NOTFILENAME | GIL_PERINSTANCE},
244
245 /* Executable without icon */
246 {L"%SystemRoot%\\system32\\autochk.exe", S_FALSE, E_INVALIDARG,
247 S_OK, L"%SystemRoot%\\system32\\autochk.exe", GIL_NOTFILENAME | GIL_PERINSTANCE},
248
249 /* Existing file */
250 {L"%SystemRoot%\\system32\\shell32.dll", S_FALSE, E_INVALIDARG,
251 S_OK, L"%SystemRoot%\\system32\\shell32.dll", GIL_NOTFILENAME | GIL_PERCLASS},
252
253 /* Non-existing file */
254 {L"c:\\non-existent-path\\non-existent-file.sdf", S_FALSE, E_INVALIDARG,
255 S_OK, L"c:\\non-existent-path\\non-existent-file.sdf", GIL_NOTFILENAME | GIL_PERCLASS},
256 };
257
258 static
259 VOID
260 test_iconlocation(UINT i, TEST_SHELL_ICON* testDef)
261 {
262 HRESULT hr;
263 IShellLinkW *psl;
264 IExtractIconW *pei;
265 INT iIcon;
266 UINT wFlags;
267 WCHAR szPath[MAX_PATH];
268
269 hr = CoCreateInstance(CLSID_ShellLink,
270 NULL,
271 CLSCTX_INPROC_SERVER,
272 IID_PPV_ARG(IShellLinkW, &psl));
273 ok(hr == S_OK, "CoCreateInstance, hr = 0x%lx\n", hr);
274 if (FAILED(hr))
275 {
276 skip("Could not instantiate CShellLink\n");
277 return;
278 }
279
280 /* Set the path to a file */
281 ExpandEnvironmentStringsW(testDef->FilePath, szPath, _countof(szPath));
282 hr = psl->SetPath(szPath);
283 ok(hr == S_OK, "IShellLink::SetPath failed, hr = 0x%lx\n", hr);
284
285 /*
286 * This test shows that this does not imply that the icon is automatically
287 * set and be retrieved naively by a call to IShellLink::GetIconLocation.
288 */
289 iIcon = 0xdeadbeef;
290 wcscpy(szPath, L"garbage");
291 hr = psl->GetIconLocation(szPath, _countof(szPath), &iIcon);
292 ok(hr == S_OK, "IShellLink::GetIconLocation(%d) failed, hr = 0x%lx\n", i, hr);
293 ok(*szPath == L'\0', "IShellLink::GetIconLocation(%d) returned '%S'\n", i, szPath);
294 ok(iIcon == 0, "IShellLink::GetIconLocation(%d) returned %d\n", i, iIcon);
295
296 /* Try to grab the IExtractIconW interface */
297 hr = psl->QueryInterface(IID_PPV_ARG(IExtractIconW, &pei));
298 ok(hr == S_OK, "IShellLink::QueryInterface(IExtractIconW)(%d) failed, hr = 0x%lx\n", i, hr);
299 if (!pei)
300 {
301 win_skip("No IExtractIconW interface\n");
302 psl->Release();
303 return;
304 }
305
306 iIcon = wFlags = 0xdeadbeef;
307 wcscpy(szPath, L"garbage");
308 hr = pei->GetIconLocation(GIL_DEFAULTICON, szPath, _countof(szPath), &iIcon, &wFlags);
309 ok(hr == testDef->hrDefIcon, "IShellLink::GetIconLocation(%d) returned hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrDefIcon);
310 ok(*szPath == L'\0', "IShellLink::GetIconLocation(%d) returned '%S'\n", i, szPath);
311 // ok(iIcon == 0, "IShellLink::GetIconLocation(%d) returned %d\n", i, iIcon);
312
313 iIcon = wFlags = 0xdeadbeef;
314 wcscpy(szPath, L"garbage");
315 hr = pei->GetIconLocation(GIL_FORSHORTCUT, szPath, _countof(szPath), &iIcon, &wFlags);
316 ok(hr == testDef->hrForShrt, "IShellLink::GetIconLocation(%d) returned hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrForShrt);
317 // Here, both szPath and iIcon are untouched...
318
319 iIcon = wFlags = 0xdeadbeef;
320 wcscpy(szPath, L"garbage");
321 hr = pei->GetIconLocation(GIL_FORSHELL, szPath, _countof(szPath), &iIcon, &wFlags);
322 ok(hr == testDef->hrForShell, "IShellLink::GetIconLocation(%d) returned hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrForShell);
323 ok(wFlags == testDef->Flags, "IShellLink::GetIconLocation(%d) returned wFlags = 0x%lx, expected 0x%lx\n", i, wFlags, testDef->Flags);
324 // ok(*szPath == L'\0', "IShellLink::GetIconLocation returned '%S'\n", szPath);
325 // ok(iIcon == 0, "IShellLink::GetIconLocation returned %d\n", iIcon);
326 // ok(FALSE, "hr = 0x%lx, szPath = '%S', iIcon = %d, wFlags = %d\n", hr, szPath, iIcon, wFlags);
327
328 /* Release the interfaces */
329 pei->Release();
330 psl->Release();
331 }
332
333 static
334 VOID
335 TestIconLocation(void)
336 {
337 UINT i;
338
339 for (i = 0; i < _countof(ShIconTests); ++i)
340 {
341 test_iconlocation(i, &ShIconTests[i]);
342 }
343 }
344
345 START_TEST(CShellLink)
346 {
347 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
348
349 TestShellLink();
350 TestDescription();
351 TestIconLocation();
352
353 CoUninitialize();
354 }