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 } 413