1 /* 2 * 3 * Copyright 1997 Marcus Meissner 4 * Copyright 1998 Juergen Schmied 5 * Copyright 2005 Mike McCormack 6 * Copyright 2009 Andrew Hill 7 * Copyright 2013 Dominik Hornung 8 * Copyright 2017 Hermes Belusca-Maito 9 * Copyright 2018-2021 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 10 * 11 * This library is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU Lesser General Public 13 * License as published by the Free Software Foundation; either 14 * version 2.1 of the License, or (at your option) any later version. 15 * 16 * This library is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 * Lesser General Public License for more details. 20 * 21 * You should have received a copy of the GNU Lesser General Public 22 * License along with this library; if not, write to the Free Software 23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 24 * 25 * NOTES 26 * Nearly complete information about the binary formats 27 * of .lnk files available at http://www.wotsit.org 28 * 29 * You can use winedump to examine the contents of a link file: 30 * winedump lnk sc.lnk 31 * 32 * MSI advertised shortcuts are totally undocumented. They provide an 33 * icon for a program that is not yet installed, and invoke MSI to 34 * install the program when the shortcut is clicked on. They are 35 * created by passing a special string to SetPath, and the information 36 * in that string is parsed an stored. 37 */ 38 /* 39 * In the following is listed more documentation about the Shell Link file format, 40 * as well as its interface. 41 * 42 * General introduction about "Shell Links" (MSDN): 43 * https://msdn.microsoft.com/en-us/library/windows/desktop/bb776891(v=vs.85).aspx 44 * 45 * 46 * Details of the file format: 47 * 48 * - Official MSDN documentation "[MS-SHLLINK]: Shell Link (.LNK) Binary File Format": 49 * https://msdn.microsoft.com/en-us/library/dd871305.aspx 50 * 51 * - Forensics: 52 * http://forensicswiki.org/wiki/LNK 53 * http://computerforensics.parsonage.co.uk/downloads/TheMeaningofLIFE.pdf 54 * https://ithreats.files.wordpress.com/2009/05/lnk_the_windows_shortcut_file_format.pdf 55 * https://github.com/libyal/liblnk/blob/master/documentation/Windows%20Shortcut%20File%20(LNK)%20format.asciidoc 56 * 57 * - List of possible shell link header flags (SHELL_LINK_DATA_FLAGS enumeration): 58 * https://msdn.microsoft.com/en-us/library/windows/desktop/bb762540(v=vs.85).aspx 59 * https://msdn.microsoft.com/en-us/library/dd891314.aspx 60 * 61 * 62 * In addition to storing its target by using a PIDL, a shell link file also 63 * stores metadata to make the shell able to track the link target, in situations 64 * where the link target is moved amongst local or network directories, or moved 65 * to different volumes. For this, two structures are used: 66 * 67 * - The first and oldest one (from NewShell/WinNT4) is the "LinkInfo" structure, 68 * stored in a serialized manner at the beginning of the shell link file: 69 * https://msdn.microsoft.com/en-us/library/dd871404.aspx 70 * The official API for manipulating this is located in LINKINFO.DLL . 71 * 72 * - The second, more recent one, is an extra binary block appended to the 73 * extra-data list of the shell link file: this is the "TrackerDataBlock": 74 * https://msdn.microsoft.com/en-us/library/dd891376.aspx 75 * Its purpose is for link tracking, and works in coordination with the 76 * "Distributed Link Tracking" service ('TrkWks' client, 'TrkSvr' server). 77 * See a detailed explanation at: 78 * http://www.serverwatch.com/tutorials/article.php/1476701/Searching-for-the-Missing-Link-Distributed-Link-Tracking.htm 79 * 80 * 81 * MSI installations most of the time create so-called "advertised shortcuts". 82 * They provide an icon for a program that may not be installed yet, and invoke 83 * MSI to install the program when the shortcut is opened (resolved). 84 * The philosophy of this approach is explained in detail inside the MSDN article 85 * "Application Resiliency: Unlock the Hidden Features of Windows Installer" 86 * (by Michael Sanford), here: 87 * https://msdn.microsoft.com/en-us/library/aa302344.aspx 88 * 89 * This functionality is implemented by adding a binary "Darwin" data block 90 * of type "EXP_DARWIN_LINK", signature EXP_DARWIN_ID_SIG == 0xA0000006, 91 * to the shell link file: 92 * https://msdn.microsoft.com/en-us/library/dd871369.aspx 93 * or, this could be done more simply by specifying a special link target path 94 * with the IShellLink::SetPath() function. Defining the following GUID: 95 * SHELL32_AdvtShortcutComponent = "::{9db1186e-40df-11d1-aa8c-00c04fb67863}:" 96 * setting a target of the form: 97 * "::{SHELL32_AdvtShortcutComponent}:<MSI_App_ID>" 98 * would automatically create the necessary binary block. 99 * 100 * With that, the target of the shortcut now becomes the MSI data. The latter 101 * is parsed from MSI and retrieved by the shell that then can run the program. 102 * 103 * This MSI functionality, dubbed "link blessing", actually originates from an 104 * older technology introduced in Internet Explorer 3 (and now obsolete since 105 * Internet Explorer 7), called "MS Internet Component Download (MSICD)", see 106 * this MSDN introductory article: 107 * https://msdn.microsoft.com/en-us/library/aa741198(v=vs.85).aspx 108 * and leveraged in Internet Explorer 4 with "Software Update Channels", see: 109 * https://msdn.microsoft.com/en-us/library/aa740931(v=vs.85).aspx 110 * Applications supporting this technology could present shell links having 111 * a special target, see subsection "Modifying the Shortcut" in the article: 112 * https://msdn.microsoft.com/en-us/library/aa741201(v=vs.85).aspx#pub_shor 113 * 114 * Similarly as for the MSI shortcuts, these MSICD shortcuts are created by 115 * specifying a special link target path with the IShellLink::SetPath() function, 116 * defining the following GUID: 117 * SHELL32_AdvtShortcutProduct = "::{9db1186f-40df-11d1-aa8c-00c04fb67863}:" 118 * and setting a target of the form: 119 * "::{SHELL32_AdvtShortcutProduct}:<AppName>::<Path>" . 120 * A tool, called "blesslnk.exe", was also provided for automatizing the process; 121 * its ReadMe can be found in the (now outdated) MS "Internet Client SDK" (INetSDK, 122 * for MS Windows 95 and NT), whose contents can be read at: 123 * http://www.msfn.org/board/topic/145352-new-windows-lnk-vulnerability/?page=4#comment-944223 124 * The MS INetSDK can be found at: 125 * https://web.archive.org/web/20100924000013/http://support.microsoft.com/kb/177877 126 * 127 * Internally the shell link target of these MSICD shortcuts is converted into 128 * a binary data block of a type similar to Darwin / "EXP_DARWIN_LINK", but with 129 * a different signature EXP_LOGO3_ID_SIG == 0xA0000007 . Such shell links are 130 * called "Logo3" shortcuts. They were evoked in this user comment in "The Old 131 * New Thing" blog: 132 * https://blogs.msdn.microsoft.com/oldnewthing/20121210-00/?p=5883#comment-1025083 133 * 134 * The shell exports the API 'SoftwareUpdateMessageBox' (in shdocvw.dll) that 135 * displays a message when an update for an application supporting this 136 * technology is available. 137 * 138 */ 139 140 #include "precomp.h" 141 142 #include <appmgmt.h> 143 144 WINE_DEFAULT_DEBUG_CHANNEL(shell); 145 146 /* 147 * Allows to define whether or not Windows-compatible behaviour 148 * should be adopted when setting and retrieving icon location paths. 149 * See CShellLink::SetIconLocation(LPCWSTR pszIconPath, INT iIcon) 150 * for more details. 151 */ 152 #define ICON_LINK_WINDOWS_COMPAT 153 154 #define SHLINK_LOCAL 0 155 #define SHLINK_REMOTE 1 156 157 /* link file formats */ 158 159 #include "pshpack1.h" 160 161 struct LOCATION_INFO 162 { 163 DWORD dwTotalSize; 164 DWORD dwHeaderSize; 165 DWORD dwFlags; 166 DWORD dwVolTableOfs; 167 DWORD dwLocalPathOfs; 168 DWORD dwNetworkVolTableOfs; 169 DWORD dwFinalPathOfs; 170 }; 171 172 struct LOCAL_VOLUME_INFO 173 { 174 DWORD dwSize; 175 DWORD dwType; 176 DWORD dwVolSerial; 177 DWORD dwVolLabelOfs; 178 }; 179 180 struct volume_info 181 { 182 DWORD type; 183 DWORD serial; 184 WCHAR label[12]; /* assume 8.3 */ 185 }; 186 187 #include "poppack.h" 188 189 /* IShellLink Implementation */ 190 191 static HRESULT ShellLink_UpdatePath(LPCWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath); 192 193 /* strdup on the process heap */ 194 static LPWSTR __inline HEAP_strdupAtoW(HANDLE heap, DWORD flags, LPCSTR str) 195 { 196 INT len; 197 LPWSTR p; 198 199 assert(str); 200 201 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0); 202 p = (LPWSTR)HeapAlloc(heap, flags, len * sizeof(WCHAR)); 203 if (!p) 204 return p; 205 MultiByteToWideChar(CP_ACP, 0, str, -1, p, len); 206 return p; 207 } 208 209 static LPWSTR __inline strdupW(LPCWSTR src) 210 { 211 LPWSTR dest; 212 if (!src) return NULL; 213 dest = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(src) + 1) * sizeof(WCHAR)); 214 if (dest) 215 wcscpy(dest, src); 216 return dest; 217 } 218 219 // TODO: Use it for constructor & destructor too 220 VOID CShellLink::Reset() 221 { 222 ILFree(m_pPidl); 223 m_pPidl = NULL; 224 225 HeapFree(GetProcessHeap(), 0, m_sPath); 226 m_sPath = NULL; 227 ZeroMemory(&volume, sizeof(volume)); 228 229 HeapFree(GetProcessHeap(), 0, m_sDescription); 230 m_sDescription = NULL; 231 HeapFree(GetProcessHeap(), 0, m_sPathRel); 232 m_sPathRel = NULL; 233 HeapFree(GetProcessHeap(), 0, m_sWorkDir); 234 m_sWorkDir = NULL; 235 HeapFree(GetProcessHeap(), 0, m_sArgs); 236 m_sArgs = NULL; 237 HeapFree(GetProcessHeap(), 0, m_sIcoPath); 238 m_sIcoPath = NULL; 239 240 m_bRunAs = FALSE; 241 m_bDirty = FALSE; 242 243 if (m_pDBList) 244 SHFreeDataBlockList(m_pDBList); 245 m_pDBList = NULL; 246 247 /**/sProduct = sComponent = NULL;/**/ 248 } 249 250 CShellLink::CShellLink() 251 { 252 m_Header.dwSize = sizeof(m_Header); 253 m_Header.clsid = CLSID_ShellLink; 254 m_Header.dwFlags = 0; 255 256 m_Header.dwFileAttributes = 0; 257 ZeroMemory(&m_Header.ftCreationTime, sizeof(m_Header.ftCreationTime)); 258 ZeroMemory(&m_Header.ftLastAccessTime, sizeof(m_Header.ftLastAccessTime)); 259 ZeroMemory(&m_Header.ftLastWriteTime, sizeof(m_Header.ftLastWriteTime)); 260 m_Header.nFileSizeLow = 0; 261 262 m_Header.nIconIndex = 0; 263 m_Header.nShowCommand = SW_SHOWNORMAL; 264 m_Header.wHotKey = 0; 265 266 m_pPidl = NULL; 267 268 m_sPath = NULL; 269 ZeroMemory(&volume, sizeof(volume)); 270 271 m_sDescription = NULL; 272 m_sPathRel = NULL; 273 m_sWorkDir = NULL; 274 m_sArgs = NULL; 275 m_sIcoPath = NULL; 276 m_bRunAs = FALSE; 277 m_bDirty = FALSE; 278 m_pDBList = NULL; 279 m_bInInit = FALSE; 280 m_hIcon = NULL; 281 m_idCmdFirst = 0; 282 283 m_sLinkPath = NULL; 284 285 /**/sProduct = sComponent = NULL;/**/ 286 } 287 288 CShellLink::~CShellLink() 289 { 290 TRACE("-- destroying IShellLink(%p)\n", this); 291 292 ILFree(m_pPidl); 293 294 HeapFree(GetProcessHeap(), 0, m_sPath); 295 296 HeapFree(GetProcessHeap(), 0, m_sDescription); 297 HeapFree(GetProcessHeap(), 0, m_sPathRel); 298 HeapFree(GetProcessHeap(), 0, m_sWorkDir); 299 HeapFree(GetProcessHeap(), 0, m_sArgs); 300 HeapFree(GetProcessHeap(), 0, m_sIcoPath); 301 HeapFree(GetProcessHeap(), 0, m_sLinkPath); 302 SHFreeDataBlockList(m_pDBList); 303 } 304 305 HRESULT STDMETHODCALLTYPE CShellLink::GetClassID(CLSID *pclsid) 306 { 307 TRACE("%p %p\n", this, pclsid); 308 309 if (pclsid == NULL) 310 return E_POINTER; 311 *pclsid = CLSID_ShellLink; 312 return S_OK; 313 } 314 315 /************************************************************************ 316 * IPersistStream_IsDirty (IPersistStream) 317 */ 318 HRESULT STDMETHODCALLTYPE CShellLink::IsDirty() 319 { 320 TRACE("(%p)\n", this); 321 return (m_bDirty ? S_OK : S_FALSE); 322 } 323 324 HRESULT STDMETHODCALLTYPE CShellLink::Load(LPCOLESTR pszFileName, DWORD dwMode) 325 { 326 TRACE("(%p, %s, %x)\n", this, debugstr_w(pszFileName), dwMode); 327 328 if (dwMode == 0) 329 dwMode = STGM_READ | STGM_SHARE_DENY_WRITE; 330 331 CComPtr<IStream> stm; 332 HRESULT hr = SHCreateStreamOnFileW(pszFileName, dwMode, &stm); 333 if (SUCCEEDED(hr)) 334 { 335 HeapFree(GetProcessHeap(), 0, m_sLinkPath); 336 m_sLinkPath = strdupW(pszFileName); 337 hr = Load(stm); 338 ShellLink_UpdatePath(m_sPathRel, pszFileName, m_sWorkDir, &m_sPath); 339 m_bDirty = FALSE; 340 } 341 TRACE("-- returning hr %08x\n", hr); 342 return hr; 343 } 344 345 HRESULT STDMETHODCALLTYPE CShellLink::Save(LPCOLESTR pszFileName, BOOL fRemember) 346 { 347 BOOL bAlreadyExists; 348 WCHAR szFullPath[MAX_PATH]; 349 350 TRACE("(%p)->(%s)\n", this, debugstr_w(pszFileName)); 351 352 if (!pszFileName) 353 return E_FAIL; 354 355 bAlreadyExists = PathFileExistsW(pszFileName); 356 357 CComPtr<IStream> stm; 358 HRESULT hr = SHCreateStreamOnFileW(pszFileName, STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, &stm); 359 if (SUCCEEDED(hr)) 360 { 361 hr = Save(stm, FALSE); 362 363 if (SUCCEEDED(hr)) 364 { 365 GetFullPathNameW(pszFileName, _countof(szFullPath), szFullPath, NULL); 366 if (bAlreadyExists) 367 SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATHW, szFullPath, NULL); 368 else 369 SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, szFullPath, NULL); 370 371 if (m_sLinkPath) 372 HeapFree(GetProcessHeap(), 0, m_sLinkPath); 373 374 m_sLinkPath = strdupW(pszFileName); 375 m_bDirty = FALSE; 376 } 377 else 378 { 379 DeleteFileW(pszFileName); 380 WARN("Failed to create shortcut %s\n", debugstr_w(pszFileName)); 381 } 382 } 383 384 return hr; 385 } 386 387 HRESULT STDMETHODCALLTYPE CShellLink::SaveCompleted(LPCOLESTR pszFileName) 388 { 389 FIXME("(%p)->(%s)\n", this, debugstr_w(pszFileName)); 390 return S_OK; 391 } 392 393 HRESULT STDMETHODCALLTYPE CShellLink::GetCurFile(LPOLESTR *ppszFileName) 394 { 395 *ppszFileName = NULL; 396 397 if (!m_sLinkPath) 398 { 399 /* IPersistFile::GetCurFile called before IPersistFile::Save */ 400 return S_FALSE; 401 } 402 403 *ppszFileName = (LPOLESTR)CoTaskMemAlloc((wcslen(m_sLinkPath) + 1) * sizeof(WCHAR)); 404 if (!*ppszFileName) 405 { 406 /* out of memory */ 407 return E_OUTOFMEMORY; 408 } 409 410 /* copy last saved filename */ 411 wcscpy(*ppszFileName, m_sLinkPath); 412 413 return S_OK; 414 } 415 416 static HRESULT Stream_LoadString(IStream* stm, BOOL unicode, LPWSTR *pstr) 417 { 418 TRACE("%p\n", stm); 419 420 USHORT len; 421 DWORD count = 0; 422 HRESULT hr = stm->Read(&len, sizeof(len), &count); 423 if (FAILED(hr) || count != sizeof(len)) 424 return E_FAIL; 425 426 if (unicode) 427 len *= sizeof(WCHAR); 428 429 TRACE("reading %d\n", len); 430 LPSTR temp = (LPSTR)HeapAlloc(GetProcessHeap(), 0, len + sizeof(WCHAR)); 431 if (!temp) 432 return E_OUTOFMEMORY; 433 count = 0; 434 hr = stm->Read(temp, len, &count); 435 if (FAILED(hr) || count != len) 436 { 437 HeapFree(GetProcessHeap(), 0, temp); 438 return E_FAIL; 439 } 440 441 TRACE("read %s\n", debugstr_an(temp, len)); 442 443 /* convert to unicode if necessary */ 444 LPWSTR str; 445 if (!unicode) 446 { 447 count = MultiByteToWideChar(CP_ACP, 0, temp, len, NULL, 0); 448 str = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (count + 1) * sizeof(WCHAR)); 449 if (!str) 450 { 451 HeapFree(GetProcessHeap(), 0, temp); 452 return E_OUTOFMEMORY; 453 } 454 MultiByteToWideChar(CP_ACP, 0, temp, len, str, count); 455 HeapFree(GetProcessHeap(), 0, temp); 456 } 457 else 458 { 459 count /= sizeof(WCHAR); 460 str = (LPWSTR)temp; 461 } 462 str[count] = 0; 463 464 *pstr = str; 465 466 return S_OK; 467 } 468 469 470 /* 471 * NOTE: The following 5 functions are part of LINKINFO.DLL 472 */ 473 static BOOL ShellLink_GetVolumeInfo(LPCWSTR path, CShellLink::volume_info *volume) 474 { 475 WCHAR drive[4] = { path[0], ':', '\\', 0 }; 476 477 volume->type = GetDriveTypeW(drive); 478 BOOL bRet = GetVolumeInformationW(drive, volume->label, _countof(volume->label), &volume->serial, NULL, NULL, NULL, 0); 479 TRACE("ret = %d type %d serial %08x name %s\n", bRet, 480 volume->type, volume->serial, debugstr_w(volume->label)); 481 return bRet; 482 } 483 484 static HRESULT Stream_ReadChunk(IStream* stm, LPVOID *data) 485 { 486 struct sized_chunk 487 { 488 DWORD size; 489 unsigned char data[1]; 490 } *chunk; 491 492 TRACE("%p\n", stm); 493 494 DWORD size; 495 ULONG count; 496 HRESULT hr = stm->Read(&size, sizeof(size), &count); 497 if (FAILED(hr) || count != sizeof(size)) 498 return E_FAIL; 499 500 chunk = static_cast<sized_chunk *>(HeapAlloc(GetProcessHeap(), 0, size)); 501 if (!chunk) 502 return E_OUTOFMEMORY; 503 504 chunk->size = size; 505 hr = stm->Read(chunk->data, size - sizeof(size), &count); 506 if (FAILED(hr) || count != (size - sizeof(size))) 507 { 508 HeapFree(GetProcessHeap(), 0, chunk); 509 return E_FAIL; 510 } 511 512 TRACE("Read %d bytes\n", chunk->size); 513 514 *data = chunk; 515 516 return S_OK; 517 } 518 519 static BOOL Stream_LoadVolume(LOCAL_VOLUME_INFO *vol, CShellLink::volume_info *volume) 520 { 521 volume->serial = vol->dwVolSerial; 522 volume->type = vol->dwType; 523 524 if (!vol->dwVolLabelOfs) 525 return FALSE; 526 if (vol->dwSize <= vol->dwVolLabelOfs) 527 return FALSE; 528 INT len = vol->dwSize - vol->dwVolLabelOfs; 529 530 LPSTR label = (LPSTR)vol; 531 label += vol->dwVolLabelOfs; 532 MultiByteToWideChar(CP_ACP, 0, label, len, volume->label, _countof(volume->label)); 533 534 return TRUE; 535 } 536 537 static LPWSTR Stream_LoadPath(LPCSTR p, DWORD maxlen) 538 { 539 UINT len = 0; 540 541 while (len < maxlen && p[len]) 542 len++; 543 544 UINT wlen = MultiByteToWideChar(CP_ACP, 0, p, len, NULL, 0); 545 LPWSTR path = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wlen + 1) * sizeof(WCHAR)); 546 if (!path) 547 return NULL; 548 MultiByteToWideChar(CP_ACP, 0, p, len, path, wlen); 549 path[wlen] = 0; 550 551 return path; 552 } 553 554 static HRESULT Stream_LoadLocation(IStream *stm, 555 CShellLink::volume_info *volume, LPWSTR *path) 556 { 557 char *p = NULL; 558 HRESULT hr = Stream_ReadChunk(stm, (LPVOID*) &p); 559 if (FAILED(hr)) 560 return hr; 561 562 LOCATION_INFO *loc = reinterpret_cast<LOCATION_INFO *>(p); 563 if (loc->dwTotalSize < sizeof(LOCATION_INFO)) 564 { 565 HeapFree(GetProcessHeap(), 0, p); 566 return E_FAIL; 567 } 568 569 /* if there's valid local volume information, load it */ 570 if (loc->dwVolTableOfs && 571 ((loc->dwVolTableOfs + sizeof(LOCAL_VOLUME_INFO)) <= loc->dwTotalSize)) 572 { 573 LOCAL_VOLUME_INFO *volume_info; 574 575 volume_info = (LOCAL_VOLUME_INFO*) &p[loc->dwVolTableOfs]; 576 Stream_LoadVolume(volume_info, volume); 577 } 578 579 /* if there's a local path, load it */ 580 DWORD n = loc->dwLocalPathOfs; 581 if (n && n < loc->dwTotalSize) 582 *path = Stream_LoadPath(&p[n], loc->dwTotalSize - n); 583 584 TRACE("type %d serial %08x name %s path %s\n", volume->type, 585 volume->serial, debugstr_w(volume->label), debugstr_w(*path)); 586 587 HeapFree(GetProcessHeap(), 0, p); 588 return S_OK; 589 } 590 591 592 /* 593 * The format of the advertised shortcut info is: 594 * 595 * Offset Description 596 * ------ ----------- 597 * 0 Length of the block (4 bytes, usually 0x314) 598 * 4 tag (dword) 599 * 8 string data in ASCII 600 * 8+0x104 string data in UNICODE 601 * 602 * In the original Win32 implementation the buffers are not initialized 603 * to zero, so data trailing the string is random garbage. 604 */ 605 HRESULT CShellLink::GetAdvertiseInfo(LPWSTR *str, DWORD dwSig) 606 { 607 LPEXP_DARWIN_LINK pInfo; 608 609 *str = NULL; 610 611 pInfo = (LPEXP_DARWIN_LINK)SHFindDataBlock(m_pDBList, dwSig); 612 if (!pInfo) 613 return E_FAIL; 614 615 /* Make sure that the size of the structure is valid */ 616 if (pInfo->dbh.cbSize != sizeof(*pInfo)) 617 { 618 ERR("Ooops. This structure is not as expected...\n"); 619 return E_FAIL; 620 } 621 622 TRACE("dwSig %08x string = '%s'\n", pInfo->dbh.dwSignature, debugstr_w(pInfo->szwDarwinID)); 623 624 *str = pInfo->szwDarwinID; 625 return S_OK; 626 } 627 628 /************************************************************************ 629 * IPersistStream_Load (IPersistStream) 630 */ 631 HRESULT STDMETHODCALLTYPE CShellLink::Load(IStream *stm) 632 { 633 TRACE("%p %p\n", this, stm); 634 635 if (!stm) 636 return STG_E_INVALIDPOINTER; 637 638 /* Free all the old stuff */ 639 Reset(); 640 641 ULONG dwBytesRead = 0; 642 HRESULT hr = stm->Read(&m_Header, sizeof(m_Header), &dwBytesRead); 643 if (FAILED(hr)) 644 return hr; 645 646 if (dwBytesRead != sizeof(m_Header)) 647 return E_FAIL; 648 if (m_Header.dwSize != sizeof(m_Header)) 649 return E_FAIL; 650 if (!IsEqualIID(m_Header.clsid, CLSID_ShellLink)) 651 return E_FAIL; 652 653 /* Load the new data in order */ 654 655 if (TRACE_ON(shell)) 656 { 657 SYSTEMTIME stCreationTime; 658 SYSTEMTIME stLastAccessTime; 659 SYSTEMTIME stLastWriteTime; 660 WCHAR sTemp[MAX_PATH]; 661 662 FileTimeToSystemTime(&m_Header.ftCreationTime, &stCreationTime); 663 FileTimeToSystemTime(&m_Header.ftLastAccessTime, &stLastAccessTime); 664 FileTimeToSystemTime(&m_Header.ftLastWriteTime, &stLastWriteTime); 665 666 GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &stCreationTime, 667 NULL, sTemp, _countof(sTemp)); 668 TRACE("-- stCreationTime: %s\n", debugstr_w(sTemp)); 669 GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &stLastAccessTime, 670 NULL, sTemp, _countof(sTemp)); 671 TRACE("-- stLastAccessTime: %s\n", debugstr_w(sTemp)); 672 GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &stLastWriteTime, 673 NULL, sTemp, _countof(sTemp)); 674 TRACE("-- stLastWriteTime: %s\n", debugstr_w(sTemp)); 675 } 676 677 /* load all the new stuff */ 678 if (m_Header.dwFlags & SLDF_HAS_ID_LIST) 679 { 680 hr = ILLoadFromStream(stm, &m_pPidl); 681 if (FAILED(hr)) 682 return hr; 683 } 684 pdump(m_pPidl); 685 686 /* Load the location information... */ 687 if (m_Header.dwFlags & SLDF_HAS_LINK_INFO) 688 { 689 hr = Stream_LoadLocation(stm, &volume, &m_sPath); 690 if (FAILED(hr)) 691 return hr; 692 } 693 /* ... but if it is required not to use it, clear it */ 694 if (m_Header.dwFlags & SLDF_FORCE_NO_LINKINFO) 695 { 696 HeapFree(GetProcessHeap(), 0, m_sPath); 697 m_sPath = NULL; 698 ZeroMemory(&volume, sizeof(volume)); 699 } 700 701 BOOL unicode = !!(m_Header.dwFlags & SLDF_UNICODE); 702 703 if (m_Header.dwFlags & SLDF_HAS_NAME) 704 { 705 hr = Stream_LoadString(stm, unicode, &m_sDescription); 706 if (FAILED(hr)) 707 return hr; 708 TRACE("Description -> %s\n", debugstr_w(m_sDescription)); 709 } 710 711 if (m_Header.dwFlags & SLDF_HAS_RELPATH) 712 { 713 hr = Stream_LoadString(stm, unicode, &m_sPathRel); 714 if (FAILED(hr)) 715 return hr; 716 TRACE("Relative Path-> %s\n", debugstr_w(m_sPathRel)); 717 } 718 719 if (m_Header.dwFlags & SLDF_HAS_WORKINGDIR) 720 { 721 hr = Stream_LoadString(stm, unicode, &m_sWorkDir); 722 if (FAILED(hr)) 723 return hr; 724 PathRemoveBackslash(m_sWorkDir); 725 TRACE("Working Dir -> %s\n", debugstr_w(m_sWorkDir)); 726 } 727 728 if (m_Header.dwFlags & SLDF_HAS_ARGS) 729 { 730 hr = Stream_LoadString(stm, unicode, &m_sArgs); 731 if (FAILED(hr)) 732 return hr; 733 TRACE("Arguments -> %s\n", debugstr_w(m_sArgs)); 734 } 735 736 if (m_Header.dwFlags & SLDF_HAS_ICONLOCATION) 737 { 738 hr = Stream_LoadString(stm, unicode, &m_sIcoPath); 739 if (FAILED(hr)) 740 return hr; 741 TRACE("Icon file -> %s\n", debugstr_w(m_sIcoPath)); 742 } 743 744 /* Now load the optional data block list */ 745 hr = SHReadDataBlockList(stm, &m_pDBList); 746 if (FAILED(hr)) // FIXME: Should we fail? 747 return hr; 748 749 if (TRACE_ON(shell)) 750 { 751 #if (NTDDI_VERSION < NTDDI_LONGHORN) 752 if (m_Header.dwFlags & SLDF_HAS_LOGO3ID) 753 { 754 hr = GetAdvertiseInfo(&sProduct, EXP_LOGO3_ID_SIG); 755 if (SUCCEEDED(hr)) 756 TRACE("Product -> %s\n", debugstr_w(sProduct)); 757 } 758 #endif 759 if (m_Header.dwFlags & SLDF_HAS_DARWINID) 760 { 761 hr = GetAdvertiseInfo(&sComponent, EXP_DARWIN_ID_SIG); 762 if (SUCCEEDED(hr)) 763 TRACE("Component -> %s\n", debugstr_w(sComponent)); 764 } 765 } 766 767 if (m_Header.dwFlags & SLDF_RUNAS_USER) 768 m_bRunAs = TRUE; 769 else 770 m_bRunAs = FALSE; 771 772 TRACE("OK\n"); 773 774 pdump(m_pPidl); 775 776 return S_OK; 777 } 778 779 /************************************************************************ 780 * Stream_WriteString 781 * 782 * Helper function for IPersistStream_Save. Writes a unicode string 783 * with terminating nul byte to a stream, preceded by the its length. 784 */ 785 static HRESULT Stream_WriteString(IStream* stm, LPCWSTR str) 786 { 787 SIZE_T length; 788 USHORT len; 789 DWORD count; 790 791 length = wcslen(str) + 1; 792 if (length > MAXUSHORT) 793 { 794 return E_INVALIDARG; 795 } 796 797 len = (USHORT)length; 798 HRESULT hr = stm->Write(&len, sizeof(len), &count); 799 if (FAILED(hr)) 800 return hr; 801 802 length *= sizeof(WCHAR); 803 804 hr = stm->Write(str, (ULONG)length, &count); 805 if (FAILED(hr)) 806 return hr; 807 808 return S_OK; 809 } 810 811 /************************************************************************ 812 * Stream_WriteLocationInfo 813 * 814 * Writes the location info to a stream 815 * 816 * FIXME: One day we might want to write the network volume information 817 * and the final path. 818 * Figure out how Windows deals with unicode paths here. 819 */ 820 static HRESULT Stream_WriteLocationInfo(IStream* stm, LPCWSTR path, 821 CShellLink::volume_info *volume) 822 { 823 LOCAL_VOLUME_INFO *vol; 824 LOCATION_INFO *loc; 825 826 TRACE("%p %s %p\n", stm, debugstr_w(path), volume); 827 828 /* figure out the size of everything */ 829 DWORD label_size = WideCharToMultiByte(CP_ACP, 0, volume->label, -1, 830 NULL, 0, NULL, NULL); 831 DWORD path_size = WideCharToMultiByte(CP_ACP, 0, path, -1, 832 NULL, 0, NULL, NULL); 833 DWORD volume_info_size = sizeof(*vol) + label_size; 834 DWORD final_path_size = 1; 835 DWORD total_size = sizeof(*loc) + volume_info_size + path_size + final_path_size; 836 837 /* create pointers to everything */ 838 loc = static_cast<LOCATION_INFO *>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, total_size)); 839 vol = (LOCAL_VOLUME_INFO*) &loc[1]; 840 LPSTR szLabel = (LPSTR) &vol[1]; 841 LPSTR szPath = &szLabel[label_size]; 842 LPSTR szFinalPath = &szPath[path_size]; 843 844 /* fill in the location information header */ 845 loc->dwTotalSize = total_size; 846 loc->dwHeaderSize = sizeof(*loc); 847 loc->dwFlags = 1; 848 loc->dwVolTableOfs = sizeof(*loc); 849 loc->dwLocalPathOfs = sizeof(*loc) + volume_info_size; 850 loc->dwNetworkVolTableOfs = 0; 851 loc->dwFinalPathOfs = sizeof(*loc) + volume_info_size + path_size; 852 853 /* fill in the volume information */ 854 vol->dwSize = volume_info_size; 855 vol->dwType = volume->type; 856 vol->dwVolSerial = volume->serial; 857 vol->dwVolLabelOfs = sizeof(*vol); 858 859 /* copy in the strings */ 860 WideCharToMultiByte(CP_ACP, 0, volume->label, -1, 861 szLabel, label_size, NULL, NULL); 862 WideCharToMultiByte(CP_ACP, 0, path, -1, 863 szPath, path_size, NULL, NULL); 864 *szFinalPath = 0; 865 866 ULONG count = 0; 867 HRESULT hr = stm->Write(loc, total_size, &count); 868 HeapFree(GetProcessHeap(), 0, loc); 869 870 return hr; 871 } 872 873 /************************************************************************ 874 * IPersistStream_Save (IPersistStream) 875 * 876 * FIXME: makes assumptions about byte order 877 */ 878 HRESULT STDMETHODCALLTYPE CShellLink::Save(IStream *stm, BOOL fClearDirty) 879 { 880 TRACE("%p %p %x\n", this, stm, fClearDirty); 881 882 m_Header.dwSize = sizeof(m_Header); 883 m_Header.clsid = CLSID_ShellLink; 884 885 /* 886 * Reset the flags: keep only the flags related to data blocks as they were 887 * already set in accordance by the different mutator member functions. 888 * The other flags will be determined now by the presence or absence of data. 889 */ 890 m_Header.dwFlags &= (SLDF_RUN_WITH_SHIMLAYER | SLDF_RUNAS_USER | 891 SLDF_RUN_IN_SEPARATE | SLDF_HAS_DARWINID | 892 #if (NTDDI_VERSION < NTDDI_LONGHORN) 893 SLDF_HAS_LOGO3ID | 894 #endif 895 SLDF_HAS_EXP_ICON_SZ | SLDF_HAS_EXP_SZ); 896 // TODO: When we will support Vista+ functionality, add other flags to this list. 897 898 /* The stored strings are in UNICODE */ 899 m_Header.dwFlags |= SLDF_UNICODE; 900 901 if (m_pPidl) 902 m_Header.dwFlags |= SLDF_HAS_ID_LIST; 903 if (m_sPath) 904 m_Header.dwFlags |= SLDF_HAS_LINK_INFO; 905 if (m_sDescription && *m_sDescription) 906 m_Header.dwFlags |= SLDF_HAS_NAME; 907 if (m_sPathRel && *m_sPathRel) 908 m_Header.dwFlags |= SLDF_HAS_RELPATH; 909 if (m_sWorkDir && *m_sWorkDir) 910 m_Header.dwFlags |= SLDF_HAS_WORKINGDIR; 911 if (m_sArgs && *m_sArgs) 912 m_Header.dwFlags |= SLDF_HAS_ARGS; 913 if (m_sIcoPath && *m_sIcoPath) 914 m_Header.dwFlags |= SLDF_HAS_ICONLOCATION; 915 if (m_bRunAs) 916 m_Header.dwFlags |= SLDF_RUNAS_USER; 917 918 /* Write the shortcut header */ 919 ULONG count; 920 HRESULT hr = stm->Write(&m_Header, sizeof(m_Header), &count); 921 if (FAILED(hr)) 922 { 923 ERR("Write failed\n"); 924 return hr; 925 } 926 927 /* Save the data in order */ 928 929 if (m_pPidl) 930 { 931 hr = ILSaveToStream(stm, m_pPidl); 932 if (FAILED(hr)) 933 { 934 ERR("Failed to write PIDL\n"); 935 return hr; 936 } 937 } 938 939 if (m_sPath) 940 { 941 hr = Stream_WriteLocationInfo(stm, m_sPath, &volume); 942 if (FAILED(hr)) 943 return hr; 944 } 945 946 if (m_Header.dwFlags & SLDF_HAS_NAME) 947 { 948 hr = Stream_WriteString(stm, m_sDescription); 949 if (FAILED(hr)) 950 return hr; 951 } 952 953 if (m_Header.dwFlags & SLDF_HAS_RELPATH) 954 { 955 hr = Stream_WriteString(stm, m_sPathRel); 956 if (FAILED(hr)) 957 return hr; 958 } 959 960 if (m_Header.dwFlags & SLDF_HAS_WORKINGDIR) 961 { 962 hr = Stream_WriteString(stm, m_sWorkDir); 963 if (FAILED(hr)) 964 return hr; 965 } 966 967 if (m_Header.dwFlags & SLDF_HAS_ARGS) 968 { 969 hr = Stream_WriteString(stm, m_sArgs); 970 if (FAILED(hr)) 971 return hr; 972 } 973 974 if (m_Header.dwFlags & SLDF_HAS_ICONLOCATION) 975 { 976 hr = Stream_WriteString(stm, m_sIcoPath); 977 if (FAILED(hr)) 978 return hr; 979 } 980 981 /* 982 * Now save the data block list. 983 * 984 * NOTE that both advertised Product and Component are already saved 985 * inside Logo3 and Darwin data blocks in the m_pDBList list, and the 986 * m_Header.dwFlags is suitably initialized. 987 */ 988 hr = SHWriteDataBlockList(stm, m_pDBList); 989 if (FAILED(hr)) 990 return hr; 991 992 /* Clear the dirty bit if requested */ 993 if (fClearDirty) 994 m_bDirty = FALSE; 995 996 return hr; 997 } 998 999 /************************************************************************ 1000 * IPersistStream_GetSizeMax (IPersistStream) 1001 */ 1002 HRESULT STDMETHODCALLTYPE CShellLink::GetSizeMax(ULARGE_INTEGER *pcbSize) 1003 { 1004 TRACE("(%p)\n", this); 1005 return E_NOTIMPL; 1006 } 1007 1008 static BOOL SHELL_ExistsFileW(LPCWSTR path) 1009 { 1010 if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(path)) 1011 return FALSE; 1012 1013 return TRUE; 1014 } 1015 1016 /************************************************************************** 1017 * ShellLink_UpdatePath 1018 * update absolute path in sPath using relative path in sPathRel 1019 */ 1020 static HRESULT ShellLink_UpdatePath(LPCWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath) 1021 { 1022 if (!path || !psPath) 1023 return E_INVALIDARG; 1024 1025 if (!*psPath && sPathRel) 1026 { 1027 WCHAR buffer[2*MAX_PATH], abs_path[2*MAX_PATH]; 1028 LPWSTR final = NULL; 1029 1030 /* first try if [directory of link file] + [relative path] finds an existing file */ 1031 1032 GetFullPathNameW(path, MAX_PATH * 2, buffer, &final); 1033 if (!final) 1034 final = buffer; 1035 wcscpy(final, sPathRel); 1036 1037 *abs_path = '\0'; 1038 1039 if (SHELL_ExistsFileW(buffer)) 1040 { 1041 if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final)) 1042 wcscpy(abs_path, buffer); 1043 } 1044 else 1045 { 1046 /* try if [working directory] + [relative path] finds an existing file */ 1047 if (sWorkDir) 1048 { 1049 wcscpy(buffer, sWorkDir); 1050 wcscpy(PathAddBackslashW(buffer), sPathRel); 1051 1052 if (SHELL_ExistsFileW(buffer)) 1053 if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final)) 1054 wcscpy(abs_path, buffer); 1055 } 1056 } 1057 1058 /* FIXME: This is even not enough - not all shell links can be resolved using this algorithm. */ 1059 if (!*abs_path) 1060 wcscpy(abs_path, sPathRel); 1061 1062 *psPath = strdupW(abs_path); 1063 if (!*psPath) 1064 return E_OUTOFMEMORY; 1065 } 1066 1067 return S_OK; 1068 } 1069 1070 HRESULT STDMETHODCALLTYPE CShellLink::GetPath(LPSTR pszFile, INT cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD fFlags) 1071 { 1072 HRESULT hr; 1073 LPWSTR pszFileW; 1074 WIN32_FIND_DATAW wfd; 1075 1076 TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n", 1077 this, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(m_sPath)); 1078 1079 /* Allocate a temporary UNICODE buffer */ 1080 pszFileW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, cchMaxPath * sizeof(WCHAR)); 1081 if (!pszFileW) 1082 return E_OUTOFMEMORY; 1083 1084 /* Call the UNICODE function */ 1085 hr = GetPath(pszFileW, cchMaxPath, &wfd, fFlags); 1086 1087 /* Convert the file path back to ANSI */ 1088 WideCharToMultiByte(CP_ACP, 0, pszFileW, -1, 1089 pszFile, cchMaxPath, NULL, NULL); 1090 1091 /* Free the temporary buffer */ 1092 HeapFree(GetProcessHeap(), 0, pszFileW); 1093 1094 if (pfd) 1095 { 1096 ZeroMemory(pfd, sizeof(*pfd)); 1097 1098 /* Copy the file data if a file path was returned */ 1099 if (*pszFile) 1100 { 1101 DWORD len; 1102 1103 /* Copy the fixed part */ 1104 CopyMemory(pfd, &wfd, FIELD_OFFSET(WIN32_FIND_DATAA, cFileName)); 1105 1106 /* Convert the file names to ANSI */ 1107 len = lstrlenW(wfd.cFileName); 1108 WideCharToMultiByte(CP_ACP, 0, wfd.cFileName, len + 1, 1109 pfd->cFileName, sizeof(pfd->cFileName), NULL, NULL); 1110 len = lstrlenW(wfd.cAlternateFileName); 1111 WideCharToMultiByte(CP_ACP, 0, wfd.cAlternateFileName, len + 1, 1112 pfd->cAlternateFileName, sizeof(pfd->cAlternateFileName), NULL, NULL); 1113 } 1114 } 1115 1116 return hr; 1117 } 1118 1119 HRESULT STDMETHODCALLTYPE CShellLink::GetIDList(PIDLIST_ABSOLUTE *ppidl) 1120 { 1121 TRACE("(%p)->(ppidl=%p)\n", this, ppidl); 1122 1123 if (!m_pPidl) 1124 { 1125 *ppidl = NULL; 1126 return S_FALSE; 1127 } 1128 1129 *ppidl = ILClone(m_pPidl); 1130 return S_OK; 1131 } 1132 1133 HRESULT STDMETHODCALLTYPE CShellLink::SetIDList(PCIDLIST_ABSOLUTE pidl) 1134 { 1135 TRACE("(%p)->(pidl=%p)\n", this, pidl); 1136 return SetTargetFromPIDLOrPath(pidl, NULL); 1137 } 1138 1139 HRESULT STDMETHODCALLTYPE CShellLink::GetDescription(LPSTR pszName, INT cchMaxName) 1140 { 1141 TRACE("(%p)->(%p len=%u)\n", this, pszName, cchMaxName); 1142 1143 if (cchMaxName) 1144 *pszName = 0; 1145 1146 if (m_sDescription) 1147 WideCharToMultiByte(CP_ACP, 0, m_sDescription, -1, 1148 pszName, cchMaxName, NULL, NULL); 1149 1150 return S_OK; 1151 } 1152 1153 HRESULT STDMETHODCALLTYPE CShellLink::SetDescription(LPCSTR pszName) 1154 { 1155 TRACE("(%p)->(pName=%s)\n", this, pszName); 1156 1157 HeapFree(GetProcessHeap(), 0, m_sDescription); 1158 m_sDescription = NULL; 1159 1160 if (pszName) 1161 { 1162 m_sDescription = HEAP_strdupAtoW(GetProcessHeap(), 0, pszName); 1163 if (!m_sDescription) 1164 return E_OUTOFMEMORY; 1165 } 1166 m_bDirty = TRUE; 1167 1168 return S_OK; 1169 } 1170 1171 HRESULT STDMETHODCALLTYPE CShellLink::GetWorkingDirectory(LPSTR pszDir, INT cchMaxPath) 1172 { 1173 TRACE("(%p)->(%p len=%u)\n", this, pszDir, cchMaxPath); 1174 1175 if (cchMaxPath) 1176 *pszDir = 0; 1177 1178 if (m_sWorkDir) 1179 WideCharToMultiByte(CP_ACP, 0, m_sWorkDir, -1, 1180 pszDir, cchMaxPath, NULL, NULL); 1181 1182 return S_OK; 1183 } 1184 1185 HRESULT STDMETHODCALLTYPE CShellLink::SetWorkingDirectory(LPCSTR pszDir) 1186 { 1187 TRACE("(%p)->(dir=%s)\n", this, pszDir); 1188 1189 HeapFree(GetProcessHeap(), 0, m_sWorkDir); 1190 m_sWorkDir = NULL; 1191 1192 if (pszDir) 1193 { 1194 m_sWorkDir = HEAP_strdupAtoW(GetProcessHeap(), 0, pszDir); 1195 if (!m_sWorkDir) 1196 return E_OUTOFMEMORY; 1197 } 1198 m_bDirty = TRUE; 1199 1200 return S_OK; 1201 } 1202 1203 HRESULT STDMETHODCALLTYPE CShellLink::GetArguments(LPSTR pszArgs, INT cchMaxPath) 1204 { 1205 TRACE("(%p)->(%p len=%u)\n", this, pszArgs, cchMaxPath); 1206 1207 if (cchMaxPath) 1208 *pszArgs = 0; 1209 1210 if (m_sArgs) 1211 WideCharToMultiByte(CP_ACP, 0, m_sArgs, -1, 1212 pszArgs, cchMaxPath, NULL, NULL); 1213 1214 return S_OK; 1215 } 1216 1217 HRESULT STDMETHODCALLTYPE CShellLink::SetArguments(LPCSTR pszArgs) 1218 { 1219 TRACE("(%p)->(args=%s)\n", this, pszArgs); 1220 1221 HeapFree(GetProcessHeap(), 0, m_sArgs); 1222 m_sArgs = NULL; 1223 1224 if (pszArgs) 1225 { 1226 m_sArgs = HEAP_strdupAtoW(GetProcessHeap(), 0, pszArgs); 1227 if (!m_sArgs) 1228 return E_OUTOFMEMORY; 1229 } 1230 m_bDirty = TRUE; 1231 1232 return S_OK; 1233 } 1234 1235 HRESULT STDMETHODCALLTYPE CShellLink::GetHotkey(WORD *pwHotkey) 1236 { 1237 TRACE("(%p)->(%p)(0x%08x)\n", this, pwHotkey, m_Header.wHotKey); 1238 *pwHotkey = m_Header.wHotKey; 1239 return S_OK; 1240 } 1241 1242 HRESULT STDMETHODCALLTYPE CShellLink::SetHotkey(WORD wHotkey) 1243 { 1244 TRACE("(%p)->(hotkey=%x)\n", this, wHotkey); 1245 1246 m_Header.wHotKey = wHotkey; 1247 m_bDirty = TRUE; 1248 1249 return S_OK; 1250 } 1251 1252 HRESULT STDMETHODCALLTYPE CShellLink::GetShowCmd(INT *piShowCmd) 1253 { 1254 TRACE("(%p)->(%p) %d\n", this, piShowCmd, m_Header.nShowCommand); 1255 *piShowCmd = m_Header.nShowCommand; 1256 return S_OK; 1257 } 1258 1259 HRESULT STDMETHODCALLTYPE CShellLink::SetShowCmd(INT iShowCmd) 1260 { 1261 TRACE("(%p) %d\n", this, iShowCmd); 1262 1263 m_Header.nShowCommand = iShowCmd; 1264 m_bDirty = TRUE; 1265 1266 return S_OK; 1267 } 1268 1269 HRESULT STDMETHODCALLTYPE CShellLink::GetIconLocation(LPSTR pszIconPath, INT cchIconPath, INT *piIcon) 1270 { 1271 HRESULT hr; 1272 LPWSTR pszIconPathW; 1273 1274 TRACE("(%p)->(%p len=%u iicon=%p)\n", this, pszIconPath, cchIconPath, piIcon); 1275 1276 /* Allocate a temporary UNICODE buffer */ 1277 pszIconPathW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, cchIconPath * sizeof(WCHAR)); 1278 if (!pszIconPathW) 1279 return E_OUTOFMEMORY; 1280 1281 /* Call the UNICODE function */ 1282 hr = GetIconLocation(pszIconPathW, cchIconPath, piIcon); 1283 1284 /* Convert the file path back to ANSI */ 1285 WideCharToMultiByte(CP_ACP, 0, pszIconPathW, -1, 1286 pszIconPath, cchIconPath, NULL, NULL); 1287 1288 /* Free the temporary buffer */ 1289 HeapFree(GetProcessHeap(), 0, pszIconPathW); 1290 1291 return hr; 1292 } 1293 1294 HRESULT STDMETHODCALLTYPE CShellLink::GetIconLocation(UINT uFlags, PSTR pszIconFile, UINT cchMax, int *piIndex, UINT *pwFlags) 1295 { 1296 HRESULT hr; 1297 LPWSTR pszIconFileW; 1298 1299 TRACE("(%p)->(%u %p len=%u piIndex=%p pwFlags=%p)\n", this, uFlags, pszIconFile, cchMax, piIndex, pwFlags); 1300 1301 /* Allocate a temporary UNICODE buffer */ 1302 pszIconFileW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, cchMax * sizeof(WCHAR)); 1303 if (!pszIconFileW) 1304 return E_OUTOFMEMORY; 1305 1306 /* Call the UNICODE function */ 1307 hr = GetIconLocation(uFlags, pszIconFileW, cchMax, piIndex, pwFlags); 1308 1309 /* Convert the file path back to ANSI */ 1310 WideCharToMultiByte(CP_ACP, 0, pszIconFileW, -1, 1311 pszIconFile, cchMax, NULL, NULL); 1312 1313 /* Free the temporary buffer */ 1314 HeapFree(GetProcessHeap(), 0, pszIconFileW); 1315 1316 return hr; 1317 } 1318 1319 HRESULT STDMETHODCALLTYPE CShellLink::Extract(PCSTR pszFile, UINT nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIconSize) 1320 { 1321 TRACE("(%p)->(path=%s iicon=%u)\n", this, pszFile, nIconIndex); 1322 1323 LPWSTR str = NULL; 1324 if (pszFile) 1325 { 1326 str = HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile); 1327 if (!str) 1328 return E_OUTOFMEMORY; 1329 } 1330 1331 HRESULT hr = Extract(str, nIconIndex, phiconLarge, phiconSmall, nIconSize); 1332 1333 if (str) 1334 HeapFree(GetProcessHeap(), 0, str); 1335 1336 return hr; 1337 } 1338 1339 HRESULT STDMETHODCALLTYPE CShellLink::SetIconLocation(LPCSTR pszIconPath, INT iIcon) 1340 { 1341 TRACE("(%p)->(path=%s iicon=%u)\n", this, pszIconPath, iIcon); 1342 1343 LPWSTR str = NULL; 1344 if (pszIconPath) 1345 { 1346 str = HEAP_strdupAtoW(GetProcessHeap(), 0, pszIconPath); 1347 if (!str) 1348 return E_OUTOFMEMORY; 1349 } 1350 1351 HRESULT hr = SetIconLocation(str, iIcon); 1352 1353 if (str) 1354 HeapFree(GetProcessHeap(), 0, str); 1355 1356 return hr; 1357 } 1358 1359 HRESULT STDMETHODCALLTYPE CShellLink::SetRelativePath(LPCSTR pszPathRel, DWORD dwReserved) 1360 { 1361 TRACE("(%p)->(path=%s %x)\n", this, pszPathRel, dwReserved); 1362 1363 HeapFree(GetProcessHeap(), 0, m_sPathRel); 1364 m_sPathRel = NULL; 1365 1366 if (pszPathRel) 1367 { 1368 m_sPathRel = HEAP_strdupAtoW(GetProcessHeap(), 0, pszPathRel); 1369 m_bDirty = TRUE; 1370 } 1371 1372 return ShellLink_UpdatePath(m_sPathRel, m_sPath, m_sWorkDir, &m_sPath); 1373 } 1374 1375 static LPWSTR 1376 shelllink_get_msi_component_path(LPWSTR component) 1377 { 1378 DWORD Result, sz = 0; 1379 1380 Result = CommandLineFromMsiDescriptor(component, NULL, &sz); 1381 if (Result != ERROR_SUCCESS) 1382 return NULL; 1383 1384 sz++; 1385 LPWSTR path = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, sz * sizeof(WCHAR)); 1386 Result = CommandLineFromMsiDescriptor(component, path, &sz); 1387 if (Result != ERROR_SUCCESS) 1388 { 1389 HeapFree(GetProcessHeap(), 0, path); 1390 path = NULL; 1391 } 1392 1393 TRACE("returning %s\n", debugstr_w(path)); 1394 1395 return path; 1396 } 1397 1398 HRESULT STDMETHODCALLTYPE CShellLink::Resolve(HWND hwnd, DWORD fFlags) 1399 { 1400 HRESULT hr = S_OK; 1401 BOOL bSuccess; 1402 1403 TRACE("(%p)->(hwnd=%p flags=%x)\n", this, hwnd, fFlags); 1404 1405 /* FIXME: use IResolveShellLink interface? */ 1406 1407 // FIXME: See InvokeCommand(). 1408 1409 #if (NTDDI_VERSION < NTDDI_LONGHORN) 1410 // NOTE: For Logo3 (EXP_LOGO3_ID_SIG), check also for SHRestricted(REST_NOLOGO3CHANNELNOTIFY) 1411 if (m_Header.dwFlags & SLDF_HAS_LOGO3ID) 1412 { 1413 FIXME("Logo3 links are not supported yet!\n"); 1414 return E_FAIL; 1415 } 1416 #endif 1417 1418 /* Resolve Darwin (MSI) target */ 1419 if (m_Header.dwFlags & SLDF_HAS_DARWINID) 1420 { 1421 LPWSTR component = NULL; 1422 hr = GetAdvertiseInfo(&component, EXP_DARWIN_ID_SIG); 1423 if (FAILED(hr)) 1424 return E_FAIL; 1425 1426 /* Clear the cached path */ 1427 HeapFree(GetProcessHeap(), 0, m_sPath); 1428 m_sPath = shelllink_get_msi_component_path(component); 1429 if (!m_sPath) 1430 return E_FAIL; 1431 } 1432 1433 if (!m_sPath && m_pPidl) 1434 { 1435 WCHAR buffer[MAX_PATH]; 1436 1437 bSuccess = SHGetPathFromIDListW(m_pPidl, buffer); 1438 if (bSuccess && *buffer) 1439 { 1440 m_sPath = strdupW(buffer); 1441 if (!m_sPath) 1442 return E_OUTOFMEMORY; 1443 1444 m_bDirty = TRUE; 1445 } 1446 else 1447 { 1448 hr = S_OK; /* don't report an error occurred while just caching information */ 1449 } 1450 } 1451 1452 // FIXME: Strange to do that here... 1453 if (!m_sIcoPath && m_sPath) 1454 { 1455 m_sIcoPath = strdupW(m_sPath); 1456 if (!m_sIcoPath) 1457 return E_OUTOFMEMORY; 1458 1459 m_Header.nIconIndex = 0; 1460 1461 m_bDirty = TRUE; 1462 } 1463 1464 return hr; 1465 } 1466 1467 HRESULT STDMETHODCALLTYPE CShellLink::SetPath(LPCSTR pszFile) 1468 { 1469 TRACE("(%p)->(path=%s)\n", this, pszFile); 1470 1471 if (!pszFile) 1472 return E_INVALIDARG; 1473 1474 LPWSTR str = HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile); 1475 if (!str) 1476 return E_OUTOFMEMORY; 1477 1478 HRESULT hr = SetPath(str); 1479 HeapFree(GetProcessHeap(), 0, str); 1480 1481 return hr; 1482 } 1483 1484 HRESULT STDMETHODCALLTYPE CShellLink::GetPath(LPWSTR pszFile, INT cchMaxPath, WIN32_FIND_DATAW *pfd, DWORD fFlags) 1485 { 1486 WCHAR buffer[MAX_PATH]; 1487 1488 TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n", 1489 this, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(m_sPath)); 1490 1491 if (cchMaxPath) 1492 *pszFile = 0; 1493 // FIXME: What if cchMaxPath == 0 , or pszFile == NULL ?? 1494 1495 // FIXME: What about Darwin?? 1496 1497 /* 1498 * Retrieve the path to the target from the PIDL (if we have one). 1499 * NOTE: Do NOT use the cached path (m_sPath from link info). 1500 */ 1501 if (m_pPidl && SHGetPathFromIDListW(m_pPidl, buffer)) 1502 { 1503 if (fFlags & SLGP_SHORTPATH) 1504 GetShortPathNameW(buffer, buffer, _countof(buffer)); 1505 // FIXME: Add support for SLGP_UNCPRIORITY 1506 } 1507 else 1508 { 1509 *buffer = 0; 1510 } 1511 1512 /* If we have a FindData structure, initialize it */ 1513 if (pfd) 1514 { 1515 ZeroMemory(pfd, sizeof(*pfd)); 1516 1517 /* Copy the file data if the target is a file path */ 1518 if (*buffer) 1519 { 1520 pfd->dwFileAttributes = m_Header.dwFileAttributes; 1521 pfd->ftCreationTime = m_Header.ftCreationTime; 1522 pfd->ftLastAccessTime = m_Header.ftLastAccessTime; 1523 pfd->ftLastWriteTime = m_Header.ftLastWriteTime; 1524 pfd->nFileSizeHigh = 0; 1525 pfd->nFileSizeLow = m_Header.nFileSizeLow; 1526 1527 /* 1528 * Build temporarily a short path in pfd->cFileName (of size MAX_PATH), 1529 * then extract and store the short file name in pfd->cAlternateFileName. 1530 */ 1531 GetShortPathNameW(buffer, pfd->cFileName, _countof(pfd->cFileName)); 1532 lstrcpynW(pfd->cAlternateFileName, 1533 PathFindFileNameW(pfd->cFileName), 1534 _countof(pfd->cAlternateFileName)); 1535 1536 /* Now extract and store the long file name in pfd->cFileName */ 1537 lstrcpynW(pfd->cFileName, 1538 PathFindFileNameW(buffer), 1539 _countof(pfd->cFileName)); 1540 } 1541 } 1542 1543 /* Finally check if we have a raw path the user actually wants to retrieve */ 1544 if ((fFlags & SLGP_RAWPATH) && (m_Header.dwFlags & SLDF_HAS_EXP_SZ)) 1545 { 1546 /* Search for a target environment block */ 1547 LPEXP_SZ_LINK pInfo; 1548 pInfo = (LPEXP_SZ_LINK)SHFindDataBlock(m_pDBList, EXP_SZ_LINK_SIG); 1549 if (pInfo && (pInfo->cbSize == sizeof(*pInfo))) 1550 lstrcpynW(buffer, pInfo->szwTarget, cchMaxPath); 1551 } 1552 1553 /* For diagnostics purposes only... */ 1554 // NOTE: SLGP_UNCPRIORITY is unsupported 1555 fFlags &= ~(SLGP_RAWPATH | SLGP_SHORTPATH); 1556 if (fFlags) FIXME("(%p): Unsupported flags %lu\n", this, fFlags); 1557 1558 /* Copy the data back to the user */ 1559 if (*buffer) 1560 lstrcpynW(pszFile, buffer, cchMaxPath); 1561 1562 return (*buffer ? S_OK : S_FALSE); 1563 } 1564 1565 HRESULT STDMETHODCALLTYPE CShellLink::GetDescription(LPWSTR pszName, INT cchMaxName) 1566 { 1567 TRACE("(%p)->(%p len=%u)\n", this, pszName, cchMaxName); 1568 1569 *pszName = 0; 1570 if (m_sDescription) 1571 lstrcpynW(pszName, m_sDescription, cchMaxName); 1572 1573 return S_OK; 1574 } 1575 1576 HRESULT STDMETHODCALLTYPE CShellLink::SetDescription(LPCWSTR pszName) 1577 { 1578 TRACE("(%p)->(desc=%s)\n", this, debugstr_w(pszName)); 1579 1580 HeapFree(GetProcessHeap(), 0, m_sDescription); 1581 m_sDescription = NULL; 1582 1583 if (pszName) 1584 { 1585 m_sDescription = strdupW(pszName); 1586 if (!m_sDescription) 1587 return E_OUTOFMEMORY; 1588 } 1589 m_bDirty = TRUE; 1590 1591 return S_OK; 1592 } 1593 1594 HRESULT STDMETHODCALLTYPE CShellLink::GetWorkingDirectory(LPWSTR pszDir, INT cchMaxPath) 1595 { 1596 TRACE("(%p)->(%p len %u)\n", this, pszDir, cchMaxPath); 1597 1598 if (cchMaxPath) 1599 *pszDir = 0; 1600 1601 if (m_sWorkDir) 1602 lstrcpynW(pszDir, m_sWorkDir, cchMaxPath); 1603 1604 return S_OK; 1605 } 1606 1607 HRESULT STDMETHODCALLTYPE CShellLink::SetWorkingDirectory(LPCWSTR pszDir) 1608 { 1609 TRACE("(%p)->(dir=%s)\n", this, debugstr_w(pszDir)); 1610 1611 HeapFree(GetProcessHeap(), 0, m_sWorkDir); 1612 m_sWorkDir = NULL; 1613 1614 if (pszDir) 1615 { 1616 m_sWorkDir = strdupW(pszDir); 1617 if (!m_sWorkDir) 1618 return E_OUTOFMEMORY; 1619 } 1620 m_bDirty = TRUE; 1621 1622 return S_OK; 1623 } 1624 1625 HRESULT STDMETHODCALLTYPE CShellLink::GetArguments(LPWSTR pszArgs, INT cchMaxPath) 1626 { 1627 TRACE("(%p)->(%p len=%u)\n", this, pszArgs, cchMaxPath); 1628 1629 if (cchMaxPath) 1630 *pszArgs = 0; 1631 1632 if (m_sArgs) 1633 lstrcpynW(pszArgs, m_sArgs, cchMaxPath); 1634 1635 return S_OK; 1636 } 1637 1638 HRESULT STDMETHODCALLTYPE CShellLink::SetArguments(LPCWSTR pszArgs) 1639 { 1640 TRACE("(%p)->(args=%s)\n", this, debugstr_w(pszArgs)); 1641 1642 HeapFree(GetProcessHeap(), 0, m_sArgs); 1643 m_sArgs = NULL; 1644 1645 if (pszArgs) 1646 { 1647 m_sArgs = strdupW(pszArgs); 1648 if (!m_sArgs) 1649 return E_OUTOFMEMORY; 1650 } 1651 m_bDirty = TRUE; 1652 1653 return S_OK; 1654 } 1655 1656 HRESULT STDMETHODCALLTYPE CShellLink::GetIconLocation(LPWSTR pszIconPath, INT cchIconPath, INT *piIcon) 1657 { 1658 TRACE("(%p)->(%p len=%u iicon=%p)\n", this, pszIconPath, cchIconPath, piIcon); 1659 1660 if (cchIconPath) 1661 *pszIconPath = 0; 1662 1663 *piIcon = 0; 1664 1665 /* Update the original icon path location */ 1666 if (m_Header.dwFlags & SLDF_HAS_EXP_ICON_SZ) 1667 { 1668 WCHAR szPath[MAX_PATH]; 1669 1670 /* Search for an icon environment block */ 1671 LPEXP_SZ_LINK pInfo; 1672 pInfo = (LPEXP_SZ_LINK)SHFindDataBlock(m_pDBList, EXP_SZ_ICON_SIG); 1673 if (pInfo && (pInfo->cbSize == sizeof(*pInfo))) 1674 { 1675 SHExpandEnvironmentStringsW(pInfo->szwTarget, szPath, _countof(szPath)); 1676 1677 m_Header.dwFlags &= ~SLDF_HAS_ICONLOCATION; 1678 HeapFree(GetProcessHeap(), 0, m_sIcoPath); 1679 1680 m_sIcoPath = strdupW(szPath); 1681 if (!m_sIcoPath) 1682 return E_OUTOFMEMORY; 1683 1684 m_Header.dwFlags |= SLDF_HAS_ICONLOCATION; 1685 1686 m_bDirty = TRUE; 1687 } 1688 } 1689 1690 *piIcon = m_Header.nIconIndex; 1691 1692 if (m_sIcoPath) 1693 lstrcpynW(pszIconPath, m_sIcoPath, cchIconPath); 1694 1695 return S_OK; 1696 } 1697 1698 static HRESULT SHELL_PidlGetIconLocationW(PCIDLIST_ABSOLUTE pidl, 1699 UINT uFlags, PWSTR pszIconFile, UINT cchMax, int *piIndex, UINT *pwFlags) 1700 { 1701 LPCITEMIDLIST pidlLast; 1702 CComPtr<IShellFolder> psf; 1703 1704 HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast); 1705 if (FAILED_UNEXPECTEDLY(hr)) 1706 return hr; 1707 1708 CComPtr<IExtractIconW> pei; 1709 hr = psf->GetUIObjectOf(0, 1, &pidlLast, IID_NULL_PPV_ARG(IExtractIconW, &pei)); 1710 if (FAILED_UNEXPECTEDLY(hr)) 1711 return hr; 1712 1713 hr = pei->GetIconLocation(uFlags, pszIconFile, cchMax, piIndex, pwFlags); 1714 if (FAILED_UNEXPECTEDLY(hr)) 1715 return hr; 1716 1717 return S_OK; 1718 } 1719 1720 HRESULT STDMETHODCALLTYPE CShellLink::GetIconLocation(UINT uFlags, PWSTR pszIconFile, UINT cchMax, int *piIndex, UINT *pwFlags) 1721 { 1722 HRESULT hr; 1723 1724 pszIconFile[0] = UNICODE_NULL; 1725 1726 /* 1727 * It is possible for a shell link to point to another shell link, 1728 * and in particular there is the possibility to point to itself. 1729 * Now, suppose we ask such a link to retrieve its associated icon. 1730 * This function would be called, and due to COM would be called again 1731 * recursively. To solve this issue, we forbid calling GetIconLocation() 1732 * with GIL_FORSHORTCUT set in uFlags, as done by Windows (shown by tests). 1733 */ 1734 if (uFlags & GIL_FORSHORTCUT) 1735 return E_INVALIDARG; 1736 1737 /* 1738 * Now, we set GIL_FORSHORTCUT so that: i) we allow the icon extractor 1739 * of the target to give us a suited icon, and ii) we protect ourselves 1740 * against recursive call. 1741 */ 1742 uFlags |= GIL_FORSHORTCUT; 1743 1744 if (uFlags & GIL_DEFAULTICON) 1745 return S_FALSE; 1746 1747 hr = GetIconLocation(pszIconFile, cchMax, piIndex); 1748 if (FAILED(hr) || pszIconFile[0] == UNICODE_NULL) 1749 { 1750 hr = SHELL_PidlGetIconLocationW(m_pPidl, uFlags, pszIconFile, cchMax, piIndex, pwFlags); 1751 } 1752 else 1753 { 1754 *pwFlags = GIL_NOTFILENAME | GIL_PERCLASS; 1755 } 1756 1757 return hr; 1758 } 1759 1760 HRESULT STDMETHODCALLTYPE 1761 CShellLink::Extract(PCWSTR pszFile, UINT nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIconSize) 1762 { 1763 HRESULT hr = NOERROR; 1764 UINT cxyLarge = LOWORD(nIconSize), cxySmall = HIWORD(nIconSize); 1765 1766 if (phiconLarge) 1767 { 1768 *phiconLarge = NULL; 1769 PrivateExtractIconsW(pszFile, nIconIndex, cxyLarge, cxyLarge, phiconLarge, NULL, 1, 0); 1770 1771 if (*phiconLarge == NULL) 1772 hr = S_FALSE; 1773 } 1774 1775 if (phiconSmall) 1776 { 1777 *phiconSmall = NULL; 1778 PrivateExtractIconsW(pszFile, nIconIndex, cxySmall, cxySmall, phiconSmall, NULL, 1, 0); 1779 1780 if (*phiconSmall == NULL) 1781 hr = S_FALSE; 1782 } 1783 1784 if (hr == S_FALSE) 1785 { 1786 if (phiconLarge && *phiconLarge) 1787 { 1788 DestroyIcon(*phiconLarge); 1789 *phiconLarge = NULL; 1790 } 1791 if (phiconSmall && *phiconSmall) 1792 { 1793 DestroyIcon(*phiconSmall); 1794 *phiconSmall = NULL; 1795 } 1796 } 1797 1798 return hr; 1799 } 1800 1801 #if 0 1802 /* Extends the functionality of PathUnExpandEnvStringsW */ 1803 BOOL PathFullyUnExpandEnvStringsW( 1804 _In_ LPCWSTR pszPath, 1805 _Out_ LPWSTR pszBuf, 1806 _In_ UINT cchBuf) 1807 { 1808 BOOL Ret = FALSE; // Set to TRUE as soon as PathUnExpandEnvStrings starts unexpanding. 1809 BOOL res; 1810 LPCWSTR p; 1811 1812 // *pszBuf = L'\0'; 1813 while (*pszPath && cchBuf > 0) 1814 { 1815 /* Attempt unexpanding the path */ 1816 res = PathUnExpandEnvStringsW(pszPath, pszBuf, cchBuf); 1817 if (!res) 1818 { 1819 /* The unexpansion failed. Try to find a path delimiter. */ 1820 p = wcspbrk(pszPath, L" /\\:*?\"<>|%"); 1821 if (!p) /* None found, we will copy the remaining path */ 1822 p = pszPath + wcslen(pszPath); 1823 else /* Found one, we will copy the delimiter and skip it */ 1824 ++p; 1825 /* If we overflow, we cannot unexpand more, so return FALSE */ 1826 if (p - pszPath >= cchBuf) 1827 return FALSE; // *pszBuf = L'\0'; 1828 1829 /* Copy the untouched portion of path up to the delimiter, included */ 1830 wcsncpy(pszBuf, pszPath, p - pszPath); 1831 pszBuf[p - pszPath] = L'\0'; // NULL-terminate 1832 1833 /* Advance the pointers and decrease the remaining buffer size */ 1834 cchBuf -= (p - pszPath); 1835 pszBuf += (p - pszPath); 1836 pszPath += (p - pszPath); 1837 } 1838 else 1839 { 1840 /* 1841 * The unexpansion succeeded. Skip the unexpanded part by trying 1842 * to find where the original path and the unexpanded string 1843 * become different. 1844 * NOTE: An alternative(?) would be to stop also at the last 1845 * path delimiter encountered in the loop (i.e. would be the 1846 * first path delimiter in the strings). 1847 */ 1848 LPWSTR q; 1849 1850 /* 1851 * The algorithm starts at the end of the strings and loops back 1852 * while the characters are equal, until it finds a discrepancy. 1853 */ 1854 p = pszPath + wcslen(pszPath); 1855 q = pszBuf + wcslen(pszBuf); // This wcslen should be < cchBuf 1856 while ((*p == *q) && (p > pszPath) && (q > pszBuf)) 1857 { 1858 --p; --q; 1859 } 1860 /* Skip discrepancy */ 1861 ++p; ++q; 1862 1863 /* Advance the pointers and decrease the remaining buffer size */ 1864 cchBuf -= (q - pszBuf); 1865 pszBuf = q; 1866 pszPath = p; 1867 1868 Ret = TRUE; 1869 } 1870 } 1871 1872 return Ret; 1873 } 1874 #endif 1875 1876 HRESULT STDMETHODCALLTYPE CShellLink::SetIconLocation(LPCWSTR pszIconPath, INT iIcon) 1877 { 1878 HRESULT hr = E_FAIL; 1879 WCHAR szIconPath[MAX_PATH]; 1880 1881 TRACE("(%p)->(path=%s iicon=%u)\n", this, debugstr_w(pszIconPath), iIcon); 1882 1883 if (pszIconPath) 1884 { 1885 /* 1886 * Check whether the user-given file path contains unexpanded 1887 * environment variables. If so, create a target environment block. 1888 * Note that in this block we will store the user-given path. 1889 * It will contain the unexpanded environment variables, but 1890 * it can also contain already expanded path that the user does 1891 * not want to see them unexpanded (e.g. so that they always 1892 * refer to the same place even if the would-be corresponding 1893 * environment variable could change). 1894 */ 1895 #ifdef ICON_LINK_WINDOWS_COMPAT 1896 /* Try to fully unexpand the icon path */ 1897 // if (PathFullyUnExpandEnvStringsW(pszIconPath, szIconPath, _countof(szIconPath))) 1898 BOOL bSuccess = PathUnExpandEnvStringsW(pszIconPath, szIconPath, _countof(szIconPath)); 1899 if (bSuccess && wcscmp(pszIconPath, szIconPath) != 0) 1900 #else 1901 /* 1902 * In some situations, described in http://stackoverflow.com/questions/2976489/ishelllinkseticonlocation-translates-my-icon-path-into-program-files-which-i 1903 * the result of PathUnExpandEnvStringsW() could be wrong, and instead 1904 * one would have to store the actual provided icon location path, while 1905 * creating an icon environment block ONLY if that path already contains 1906 * environment variables. This is what the present case is trying to implement. 1907 */ 1908 SHExpandEnvironmentStringsW(pszIconPath, szIconPath, _countof(szIconPath)); 1909 if (wcscmp(pszIconPath, szIconPath) != 0) 1910 #endif 1911 { 1912 /* 1913 * The user-given file path contains unexpanded environment 1914 * variables, so we need an icon environment block. 1915 */ 1916 EXP_SZ_LINK buffer; 1917 LPEXP_SZ_LINK pInfo; 1918 1919 #ifdef ICON_LINK_WINDOWS_COMPAT 1920 /* Make pszIconPath point to the unexpanded path */ 1921 LPCWSTR pszOrgIconPath = pszIconPath; 1922 pszIconPath = szIconPath; 1923 #endif 1924 pInfo = (LPEXP_SZ_LINK)SHFindDataBlock(m_pDBList, EXP_SZ_ICON_SIG); 1925 if (pInfo) 1926 { 1927 /* Make sure that the size of the structure is valid */ 1928 if (pInfo->cbSize != sizeof(*pInfo)) 1929 { 1930 ERR("Ooops. This structure is not as expected...\n"); 1931 1932 /* Invalid structure, remove it altogether */ 1933 m_Header.dwFlags &= ~SLDF_HAS_EXP_ICON_SZ; 1934 RemoveDataBlock(EXP_SZ_ICON_SIG); 1935 1936 /* Reset the pointer and go use the static buffer */ 1937 pInfo = NULL; 1938 } 1939 } 1940 if (!pInfo) 1941 { 1942 /* Use the static buffer */ 1943 pInfo = &buffer; 1944 buffer.cbSize = sizeof(buffer); 1945 buffer.dwSignature = EXP_SZ_ICON_SIG; 1946 } 1947 1948 lstrcpynW(pInfo->szwTarget, pszIconPath, _countof(pInfo->szwTarget)); 1949 WideCharToMultiByte(CP_ACP, 0, pszIconPath, -1, 1950 pInfo->szTarget, _countof(pInfo->szTarget), NULL, NULL); 1951 1952 hr = S_OK; 1953 if (pInfo == &buffer) 1954 hr = AddDataBlock(pInfo); 1955 if (hr == S_OK) 1956 m_Header.dwFlags |= SLDF_HAS_EXP_ICON_SZ; 1957 1958 #ifdef ICON_LINK_WINDOWS_COMPAT 1959 /* Set pszIconPath back to the original one */ 1960 pszIconPath = pszOrgIconPath; 1961 #else 1962 /* Now, make pszIconPath point to the expanded path */ 1963 pszIconPath = szIconPath; 1964 #endif 1965 } 1966 else 1967 { 1968 /* 1969 * The user-given file path does not contain unexpanded environment 1970 * variables, so we need to remove any icon environment block. 1971 */ 1972 m_Header.dwFlags &= ~SLDF_HAS_EXP_ICON_SZ; 1973 RemoveDataBlock(EXP_SZ_ICON_SIG); 1974 1975 /* pszIconPath points to the user path */ 1976 } 1977 } 1978 1979 #ifdef ICON_LINK_WINDOWS_COMPAT 1980 /* Store the original icon path location (may contain unexpanded environment strings) */ 1981 #endif 1982 if (pszIconPath) 1983 { 1984 m_Header.dwFlags &= ~SLDF_HAS_ICONLOCATION; 1985 HeapFree(GetProcessHeap(), 0, m_sIcoPath); 1986 1987 m_sIcoPath = strdupW(pszIconPath); 1988 if (!m_sIcoPath) 1989 return E_OUTOFMEMORY; 1990 1991 m_Header.dwFlags |= SLDF_HAS_ICONLOCATION; 1992 } 1993 1994 hr = S_OK; 1995 1996 m_Header.nIconIndex = iIcon; 1997 m_bDirty = TRUE; 1998 1999 return hr; 2000 } 2001 2002 HRESULT STDMETHODCALLTYPE CShellLink::SetRelativePath(LPCWSTR pszPathRel, DWORD dwReserved) 2003 { 2004 TRACE("(%p)->(path=%s %x)\n", this, debugstr_w(pszPathRel), dwReserved); 2005 2006 HeapFree(GetProcessHeap(), 0, m_sPathRel); 2007 m_sPathRel = NULL; 2008 2009 if (pszPathRel) 2010 { 2011 m_sPathRel = strdupW(pszPathRel); 2012 if (!m_sPathRel) 2013 return E_OUTOFMEMORY; 2014 } 2015 m_bDirty = TRUE; 2016 2017 return ShellLink_UpdatePath(m_sPathRel, m_sPath, m_sWorkDir, &m_sPath); 2018 } 2019 2020 static LPWSTR GetAdvertisedArg(LPCWSTR str) 2021 { 2022 if (!str) 2023 return NULL; 2024 2025 LPCWSTR p = wcschr(str, L':'); 2026 if (!p) 2027 return NULL; 2028 2029 DWORD len = p - str; 2030 LPWSTR ret = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * (len + 1)); 2031 if (!ret) 2032 return ret; 2033 2034 memcpy(ret, str, sizeof(WCHAR)*len); 2035 ret[len] = 0; 2036 return ret; 2037 } 2038 2039 HRESULT CShellLink::WriteAdvertiseInfo(LPCWSTR string, DWORD dwSig) 2040 { 2041 EXP_DARWIN_LINK buffer; 2042 LPEXP_DARWIN_LINK pInfo; 2043 2044 if ( (dwSig != EXP_DARWIN_ID_SIG) 2045 #if (NTDDI_VERSION < NTDDI_LONGHORN) 2046 && (dwSig != EXP_LOGO3_ID_SIG) 2047 #endif 2048 ) 2049 { 2050 return E_INVALIDARG; 2051 } 2052 2053 if (!string) 2054 return S_FALSE; 2055 2056 pInfo = (LPEXP_DARWIN_LINK)SHFindDataBlock(m_pDBList, dwSig); 2057 if (pInfo) 2058 { 2059 /* Make sure that the size of the structure is valid */ 2060 if (pInfo->dbh.cbSize != sizeof(*pInfo)) 2061 { 2062 ERR("Ooops. This structure is not as expected...\n"); 2063 2064 /* Invalid structure, remove it altogether */ 2065 if (dwSig == EXP_DARWIN_ID_SIG) 2066 m_Header.dwFlags &= ~SLDF_HAS_DARWINID; 2067 #if (NTDDI_VERSION < NTDDI_LONGHORN) 2068 else if (dwSig == EXP_LOGO3_ID_SIG) 2069 m_Header.dwFlags &= ~SLDF_HAS_LOGO3ID; 2070 #endif 2071 RemoveDataBlock(dwSig); 2072 2073 /* Reset the pointer and go use the static buffer */ 2074 pInfo = NULL; 2075 } 2076 } 2077 if (!pInfo) 2078 { 2079 /* Use the static buffer */ 2080 pInfo = &buffer; 2081 buffer.dbh.cbSize = sizeof(buffer); 2082 buffer.dbh.dwSignature = dwSig; 2083 } 2084 2085 lstrcpynW(pInfo->szwDarwinID, string, _countof(pInfo->szwDarwinID)); 2086 WideCharToMultiByte(CP_ACP, 0, string, -1, 2087 pInfo->szDarwinID, _countof(pInfo->szDarwinID), NULL, NULL); 2088 2089 HRESULT hr = S_OK; 2090 if (pInfo == &buffer) 2091 hr = AddDataBlock(pInfo); 2092 if (hr == S_OK) 2093 { 2094 if (dwSig == EXP_DARWIN_ID_SIG) 2095 m_Header.dwFlags |= SLDF_HAS_DARWINID; 2096 #if (NTDDI_VERSION < NTDDI_LONGHORN) 2097 else if (dwSig == EXP_LOGO3_ID_SIG) 2098 m_Header.dwFlags |= SLDF_HAS_LOGO3ID; 2099 #endif 2100 } 2101 2102 return hr; 2103 } 2104 2105 HRESULT CShellLink::SetAdvertiseInfo(LPCWSTR str) 2106 { 2107 HRESULT hr; 2108 LPCWSTR szComponent = NULL, szProduct = NULL, p; 2109 INT len; 2110 GUID guid; 2111 WCHAR szGuid[38+1]; 2112 2113 /**/sProduct = sComponent = NULL;/**/ 2114 2115 while (str[0]) 2116 { 2117 /* each segment must start with two colons */ 2118 if (str[0] != ':' || str[1] != ':') 2119 return E_FAIL; 2120 2121 /* the last segment is just two colons */ 2122 if (!str[2]) 2123 break; 2124 str += 2; 2125 2126 /* there must be a colon straight after a guid */ 2127 p = wcschr(str, L':'); 2128 if (!p) 2129 return E_FAIL; 2130 len = p - str; 2131 if (len != 38) 2132 return E_FAIL; 2133 2134 /* get the guid, and check if it's validly formatted */ 2135 memcpy(szGuid, str, sizeof(WCHAR)*len); 2136 szGuid[len] = 0; 2137 2138 hr = CLSIDFromString(szGuid, &guid); 2139 if (hr != S_OK) 2140 return hr; 2141 str = p + 1; 2142 2143 /* match it up to a guid that we care about */ 2144 if (IsEqualGUID(guid, SHELL32_AdvtShortcutComponent) && !szComponent) 2145 szComponent = str; /* Darwin */ 2146 else if (IsEqualGUID(guid, SHELL32_AdvtShortcutProduct) && !szProduct) 2147 szProduct = str; /* Logo3 */ 2148 else 2149 return E_FAIL; 2150 2151 /* skip to the next field */ 2152 str = wcschr(str, L':'); 2153 if (!str) 2154 return E_FAIL; 2155 } 2156 2157 /* we have to have a component for an advertised shortcut */ 2158 if (!szComponent) 2159 return E_FAIL; 2160 2161 szComponent = GetAdvertisedArg(szComponent); 2162 szProduct = GetAdvertisedArg(szProduct); 2163 2164 hr = WriteAdvertiseInfo(szComponent, EXP_DARWIN_ID_SIG); 2165 // if (FAILED(hr)) 2166 // return hr; 2167 #if (NTDDI_VERSION < NTDDI_LONGHORN) 2168 hr = WriteAdvertiseInfo(szProduct, EXP_LOGO3_ID_SIG); 2169 // if (FAILED(hr)) 2170 // return hr; 2171 #endif 2172 2173 HeapFree(GetProcessHeap(), 0, (PVOID)szComponent); 2174 HeapFree(GetProcessHeap(), 0, (PVOID)szProduct); 2175 2176 if (TRACE_ON(shell)) 2177 { 2178 GetAdvertiseInfo(&sComponent, EXP_DARWIN_ID_SIG); 2179 TRACE("Component = %s\n", debugstr_w(sComponent)); 2180 #if (NTDDI_VERSION < NTDDI_LONGHORN) 2181 GetAdvertiseInfo(&sProduct, EXP_LOGO3_ID_SIG); 2182 TRACE("Product = %s\n", debugstr_w(sProduct)); 2183 #endif 2184 } 2185 2186 return S_OK; 2187 } 2188 2189 HRESULT CShellLink::SetTargetFromPIDLOrPath(LPCITEMIDLIST pidl, LPCWSTR pszFile) 2190 { 2191 HRESULT hr = S_OK; 2192 LPITEMIDLIST pidlNew = NULL; 2193 WCHAR szPath[MAX_PATH]; 2194 2195 /* 2196 * Not both 'pidl' and 'pszFile' should be set. 2197 * But either one or both can be NULL. 2198 */ 2199 if (pidl && pszFile) 2200 return E_FAIL; 2201 2202 if (pidl) 2203 { 2204 /* Clone the PIDL */ 2205 pidlNew = ILClone(pidl); 2206 if (!pidlNew) 2207 return E_FAIL; 2208 } 2209 else if (pszFile) 2210 { 2211 /* Build a PIDL for this path target */ 2212 hr = SHILCreateFromPathW(pszFile, &pidlNew, NULL); 2213 if (FAILED(hr)) 2214 { 2215 /* This failed, try to resolve the path, then create a simple PIDL */ 2216 2217 StringCchCopyW(szPath, _countof(szPath), pszFile); 2218 PathResolveW(szPath, NULL, PRF_TRYPROGRAMEXTENSIONS); 2219 2220 if (PathIsFileSpecW(szPath)) 2221 { 2222 hr = E_INVALIDARG; 2223 szPath[0] = 0; 2224 } 2225 else 2226 { 2227 hr = S_OK; 2228 pidlNew = SHSimpleIDListFromPathW(szPath); 2229 // NOTE: Don't make it failed here even if pidlNew was NULL. 2230 // We don't fail on purpose even if SHSimpleIDListFromPathW returns NULL. 2231 // This behaviour has been verified with tests. 2232 } 2233 } 2234 } 2235 // else if (!pidl && !pszFile) { pidlNew = NULL; hr = S_OK; } 2236 2237 ILFree(m_pPidl); 2238 m_pPidl = pidlNew; 2239 2240 if (!pszFile) 2241 { 2242 if (SHGetPathFromIDListW(pidlNew, szPath)) 2243 pszFile = szPath; 2244 } 2245 2246 // TODO: Fully update link info, tracker, file attribs... 2247 2248 // if (pszFile) 2249 if (!pszFile) 2250 { 2251 *szPath = L'\0'; 2252 pszFile = szPath; 2253 } 2254 2255 /* Update the cached path (for link info) */ 2256 ShellLink_GetVolumeInfo(pszFile, &volume); 2257 2258 if (m_sPath) 2259 HeapFree(GetProcessHeap(), 0, m_sPath); 2260 2261 m_sPath = strdupW(pszFile); 2262 if (!m_sPath) 2263 return E_OUTOFMEMORY; 2264 2265 m_bDirty = TRUE; 2266 return hr; 2267 } 2268 2269 HRESULT STDMETHODCALLTYPE CShellLink::SetPath(LPCWSTR pszFile) 2270 { 2271 LPWSTR unquoted = NULL; 2272 HRESULT hr = S_OK; 2273 2274 TRACE("(%p)->(path=%s)\n", this, debugstr_w(pszFile)); 2275 2276 if (!pszFile) 2277 return E_INVALIDARG; 2278 2279 /* 2280 * Allow upgrading Logo3 shortcuts (m_Header.dwFlags & SLDF_HAS_LOGO3ID), 2281 * but forbid upgrading Darwin ones. 2282 */ 2283 if (m_Header.dwFlags & SLDF_HAS_DARWINID) 2284 return S_FALSE; 2285 2286 /* quotes at the ends of the string are stripped */ 2287 SIZE_T len = wcslen(pszFile); 2288 if (pszFile[0] == L'"' && pszFile[len-1] == L'"') 2289 { 2290 unquoted = strdupW(pszFile); 2291 PathUnquoteSpacesW(unquoted); 2292 pszFile = unquoted; 2293 } 2294 2295 /* any other quote marks are invalid */ 2296 if (wcschr(pszFile, L'"')) 2297 { 2298 hr = S_FALSE; 2299 goto end; 2300 } 2301 2302 /* Clear the cached path */ 2303 HeapFree(GetProcessHeap(), 0, m_sPath); 2304 m_sPath = NULL; 2305 2306 /* Check for an advertised target (Logo3 or Darwin) */ 2307 if (SetAdvertiseInfo(pszFile) != S_OK) 2308 { 2309 /* This is not an advertised target, but a regular path */ 2310 WCHAR szPath[MAX_PATH]; 2311 2312 /* 2313 * Check whether the user-given file path contains unexpanded 2314 * environment variables. If so, create a target environment block. 2315 * Note that in this block we will store the user-given path. 2316 * It will contain the unexpanded environment variables, but 2317 * it can also contain already expanded path that the user does 2318 * not want to see them unexpanded (e.g. so that they always 2319 * refer to the same place even if the would-be corresponding 2320 * environment variable could change). 2321 */ 2322 if (*pszFile) 2323 SHExpandEnvironmentStringsW(pszFile, szPath, _countof(szPath)); 2324 else 2325 *szPath = L'\0'; 2326 2327 if (*pszFile && (wcscmp(pszFile, szPath) != 0)) 2328 { 2329 /* 2330 * The user-given file path contains unexpanded environment 2331 * variables, so we need a target environment block. 2332 */ 2333 EXP_SZ_LINK buffer; 2334 LPEXP_SZ_LINK pInfo; 2335 2336 pInfo = (LPEXP_SZ_LINK)SHFindDataBlock(m_pDBList, EXP_SZ_LINK_SIG); 2337 if (pInfo) 2338 { 2339 /* Make sure that the size of the structure is valid */ 2340 if (pInfo->cbSize != sizeof(*pInfo)) 2341 { 2342 ERR("Ooops. This structure is not as expected...\n"); 2343 2344 /* Invalid structure, remove it altogether */ 2345 m_Header.dwFlags &= ~SLDF_HAS_EXP_SZ; 2346 RemoveDataBlock(EXP_SZ_LINK_SIG); 2347 2348 /* Reset the pointer and go use the static buffer */ 2349 pInfo = NULL; 2350 } 2351 } 2352 if (!pInfo) 2353 { 2354 /* Use the static buffer */ 2355 pInfo = &buffer; 2356 buffer.cbSize = sizeof(buffer); 2357 buffer.dwSignature = EXP_SZ_LINK_SIG; 2358 } 2359 2360 lstrcpynW(pInfo->szwTarget, pszFile, _countof(pInfo->szwTarget)); 2361 WideCharToMultiByte(CP_ACP, 0, pszFile, -1, 2362 pInfo->szTarget, _countof(pInfo->szTarget), NULL, NULL); 2363 2364 hr = S_OK; 2365 if (pInfo == &buffer) 2366 hr = AddDataBlock(pInfo); 2367 if (hr == S_OK) 2368 m_Header.dwFlags |= SLDF_HAS_EXP_SZ; 2369 2370 /* Now, make pszFile point to the expanded path */ 2371 pszFile = szPath; 2372 } 2373 else 2374 { 2375 /* 2376 * The user-given file path does not contain unexpanded environment 2377 * variables, so we need to remove any target environment block. 2378 */ 2379 m_Header.dwFlags &= ~SLDF_HAS_EXP_SZ; 2380 RemoveDataBlock(EXP_SZ_LINK_SIG); 2381 2382 /* pszFile points to the user path */ 2383 } 2384 2385 /* Set the target */ 2386 hr = SetTargetFromPIDLOrPath(NULL, pszFile); 2387 } 2388 2389 m_bDirty = TRUE; 2390 2391 end: 2392 HeapFree(GetProcessHeap(), 0, unquoted); 2393 return hr; 2394 } 2395 2396 HRESULT STDMETHODCALLTYPE CShellLink::AddDataBlock(void* pDataBlock) 2397 { 2398 if (SHAddDataBlock(&m_pDBList, (DATABLOCK_HEADER*)pDataBlock)) 2399 { 2400 m_bDirty = TRUE; 2401 return S_OK; 2402 } 2403 return S_FALSE; 2404 } 2405 2406 HRESULT STDMETHODCALLTYPE CShellLink::CopyDataBlock(DWORD dwSig, void** ppDataBlock) 2407 { 2408 DATABLOCK_HEADER* pBlock; 2409 PVOID pDataBlock; 2410 2411 TRACE("%p %08x %p\n", this, dwSig, ppDataBlock); 2412 2413 *ppDataBlock = NULL; 2414 2415 pBlock = SHFindDataBlock(m_pDBList, dwSig); 2416 if (!pBlock) 2417 { 2418 ERR("unknown datablock %08x (not found)\n", dwSig); 2419 return E_FAIL; 2420 } 2421 2422 pDataBlock = LocalAlloc(LMEM_ZEROINIT, pBlock->cbSize); 2423 if (!pDataBlock) 2424 return E_OUTOFMEMORY; 2425 2426 CopyMemory(pDataBlock, pBlock, pBlock->cbSize); 2427 2428 *ppDataBlock = pDataBlock; 2429 return S_OK; 2430 } 2431 2432 HRESULT STDMETHODCALLTYPE CShellLink::RemoveDataBlock(DWORD dwSig) 2433 { 2434 if (SHRemoveDataBlock(&m_pDBList, dwSig)) 2435 { 2436 m_bDirty = TRUE; 2437 return S_OK; 2438 } 2439 return S_FALSE; 2440 } 2441 2442 HRESULT STDMETHODCALLTYPE CShellLink::GetFlags(DWORD *pdwFlags) 2443 { 2444 TRACE("%p %p\n", this, pdwFlags); 2445 *pdwFlags = m_Header.dwFlags; 2446 return S_OK; 2447 } 2448 2449 HRESULT STDMETHODCALLTYPE CShellLink::SetFlags(DWORD dwFlags) 2450 { 2451 #if 0 // FIXME! 2452 m_Header.dwFlags = dwFlags; 2453 m_bDirty = TRUE; 2454 return S_OK; 2455 #else 2456 FIXME("\n"); 2457 return E_NOTIMPL; 2458 #endif 2459 } 2460 2461 /************************************************************************** 2462 * CShellLink implementation of IShellExtInit::Initialize() 2463 * 2464 * Loads the shelllink from the dataobject the shell is pointing to. 2465 */ 2466 HRESULT STDMETHODCALLTYPE CShellLink::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID) 2467 { 2468 TRACE("%p %p %p %p\n", this, pidlFolder, pdtobj, hkeyProgID); 2469 2470 if (!pdtobj) 2471 return E_FAIL; 2472 2473 FORMATETC format; 2474 format.cfFormat = CF_HDROP; 2475 format.ptd = NULL; 2476 format.dwAspect = DVASPECT_CONTENT; 2477 format.lindex = -1; 2478 format.tymed = TYMED_HGLOBAL; 2479 2480 STGMEDIUM stgm; 2481 HRESULT hr = pdtobj->GetData(&format, &stgm); 2482 if (FAILED(hr)) 2483 return hr; 2484 2485 UINT count = DragQueryFileW((HDROP)stgm.hGlobal, -1, NULL, 0); 2486 if (count == 1) 2487 { 2488 count = DragQueryFileW((HDROP)stgm.hGlobal, 0, NULL, 0); 2489 count++; 2490 LPWSTR path = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, count * sizeof(WCHAR)); 2491 if (path) 2492 { 2493 count = DragQueryFileW((HDROP)stgm.hGlobal, 0, path, count); 2494 hr = Load(path, 0); 2495 HeapFree(GetProcessHeap(), 0, path); 2496 } 2497 } 2498 ReleaseStgMedium(&stgm); 2499 2500 return S_OK; 2501 } 2502 2503 HRESULT STDMETHODCALLTYPE CShellLink::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) 2504 { 2505 INT id = 0; 2506 2507 m_idCmdFirst = idCmdFirst; 2508 2509 TRACE("%p %p %u %u %u %u\n", this, 2510 hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags); 2511 2512 if (!hMenu) 2513 return E_INVALIDARG; 2514 2515 CStringW strOpen(MAKEINTRESOURCEW(IDS_OPEN_VERB)); 2516 CStringW strOpenFileLoc(MAKEINTRESOURCEW(IDS_OPENFILELOCATION)); 2517 2518 MENUITEMINFOW mii; 2519 ZeroMemory(&mii, sizeof(mii)); 2520 mii.cbSize = sizeof(mii); 2521 mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE; 2522 mii.dwTypeData = strOpen.GetBuffer(); 2523 mii.cch = wcslen(mii.dwTypeData); 2524 mii.wID = idCmdFirst + id++; 2525 mii.fState = MFS_DEFAULT | MFS_ENABLED; 2526 mii.fType = MFT_STRING; 2527 if (!InsertMenuItemW(hMenu, indexMenu++, TRUE, &mii)) 2528 return E_FAIL; 2529 2530 mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE; 2531 mii.dwTypeData = strOpenFileLoc.GetBuffer(); 2532 mii.cch = wcslen(mii.dwTypeData); 2533 mii.wID = idCmdFirst + id++; 2534 mii.fState = MFS_ENABLED; 2535 mii.fType = MFT_STRING; 2536 if (!InsertMenuItemW(hMenu, indexMenu++, TRUE, &mii)) 2537 return E_FAIL; 2538 2539 UNREFERENCED_PARAMETER(indexMenu); 2540 2541 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, id); 2542 } 2543 2544 HRESULT CShellLink::DoOpenFileLocation() 2545 { 2546 WCHAR szParams[MAX_PATH + 64]; 2547 StringCbPrintfW(szParams, sizeof(szParams), L"/select,%s", m_sPath); 2548 2549 INT_PTR ret; 2550 ret = reinterpret_cast<INT_PTR>(ShellExecuteW(NULL, NULL, L"explorer.exe", szParams, 2551 NULL, m_Header.nShowCommand)); 2552 if (ret <= 32) 2553 { 2554 ERR("ret: %08lX\n", ret); 2555 return E_FAIL; 2556 } 2557 2558 return S_OK; 2559 } 2560 2561 HRESULT STDMETHODCALLTYPE CShellLink::InvokeCommand(LPCMINVOKECOMMANDINFO lpici) 2562 { 2563 TRACE("%p %p\n", this, lpici); 2564 2565 if (lpici->cbSize < sizeof(CMINVOKECOMMANDINFO)) 2566 return E_INVALIDARG; 2567 2568 // NOTE: We could use lpici->hwnd (certainly in case lpici->fMask doesn't contain CMIC_MASK_FLAG_NO_UI) 2569 // as the parent window handle... ? 2570 /* FIXME: get using interface set from IObjectWithSite?? */ 2571 // NOTE: We might need an extended version of Resolve that provides us with paths... 2572 HRESULT hr = Resolve(lpici->hwnd, 0); 2573 if (FAILED(hr)) 2574 { 2575 TRACE("failed to resolve component with error 0x%08x\n", hr); 2576 return hr; 2577 } 2578 2579 UINT idCmd = LOWORD(lpici->lpVerb); 2580 TRACE("idCmd: %d\n", idCmd); 2581 2582 switch (idCmd) 2583 { 2584 case IDCMD_OPEN: 2585 return DoOpen(lpici); 2586 case IDCMD_OPENFILELOCATION: 2587 return DoOpenFileLocation(); 2588 default: 2589 return E_NOTIMPL; 2590 } 2591 } 2592 2593 HRESULT CShellLink::DoOpen(LPCMINVOKECOMMANDINFO lpici) 2594 { 2595 HRESULT hr; 2596 LPWSTR args = NULL; 2597 LPWSTR path = strdupW(m_sPath); 2598 2599 if ( lpici->cbSize == sizeof(CMINVOKECOMMANDINFOEX) && 2600 (lpici->fMask & CMIC_MASK_UNICODE) ) 2601 { 2602 LPCMINVOKECOMMANDINFOEX iciex = (LPCMINVOKECOMMANDINFOEX)lpici; 2603 SIZE_T len = 2; 2604 2605 if (m_sArgs) 2606 len += wcslen(m_sArgs); 2607 if (iciex->lpParametersW) 2608 len += wcslen(iciex->lpParametersW); 2609 2610 args = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 2611 *args = 0; 2612 if (m_sArgs) 2613 wcscat(args, m_sArgs); 2614 if (iciex->lpParametersW) 2615 { 2616 wcscat(args, L" "); 2617 wcscat(args, iciex->lpParametersW); 2618 } 2619 } 2620 else if (m_sArgs != NULL) 2621 { 2622 args = strdupW(m_sArgs); 2623 } 2624 2625 SHELLEXECUTEINFOW sei; 2626 ZeroMemory(&sei, sizeof(sei)); 2627 sei.cbSize = sizeof(sei); 2628 sei.fMask = SEE_MASK_HASLINKNAME | SEE_MASK_UNICODE | 2629 (lpici->fMask & (SEE_MASK_NOASYNC | SEE_MASK_ASYNCOK | SEE_MASK_FLAG_NO_UI)); 2630 sei.lpFile = path; 2631 sei.lpClass = m_sLinkPath; 2632 sei.nShow = m_Header.nShowCommand; 2633 sei.lpDirectory = m_sWorkDir; 2634 sei.lpParameters = args; 2635 sei.lpVerb = L"open"; 2636 2637 // HACK for ShellExecuteExW 2638 if (m_sPath && wcsstr(m_sPath, L".cpl")) 2639 sei.lpVerb = L"cplopen"; 2640 2641 if (ShellExecuteExW(&sei)) 2642 hr = S_OK; 2643 else 2644 hr = E_FAIL; 2645 2646 HeapFree(GetProcessHeap(), 0, args); 2647 HeapFree(GetProcessHeap(), 0, path); 2648 2649 return hr; 2650 } 2651 2652 HRESULT STDMETHODCALLTYPE CShellLink::GetCommandString(UINT_PTR idCmd, UINT uType, UINT* pwReserved, LPSTR pszName, UINT cchMax) 2653 { 2654 FIXME("%p %lu %u %p %p %u\n", this, idCmd, uType, pwReserved, pszName, cchMax); 2655 return E_NOTIMPL; 2656 } 2657 2658 INT_PTR CALLBACK ExtendedShortcutProc(HWND hwndDlg, UINT uMsg, 2659 WPARAM wParam, LPARAM lParam) 2660 { 2661 switch(uMsg) 2662 { 2663 case WM_INITDIALOG: 2664 if (lParam) 2665 { 2666 HWND hDlgCtrl = GetDlgItem(hwndDlg, IDC_SHORTEX_RUN_DIFFERENT); 2667 SendMessage(hDlgCtrl, BM_SETCHECK, BST_CHECKED, 0); 2668 } 2669 return TRUE; 2670 case WM_COMMAND: 2671 { 2672 HWND hDlgCtrl = GetDlgItem(hwndDlg, IDC_SHORTEX_RUN_DIFFERENT); 2673 if (LOWORD(wParam) == IDOK) 2674 { 2675 if (SendMessage(hDlgCtrl, BM_GETCHECK, 0, 0) == BST_CHECKED) 2676 EndDialog(hwndDlg, 1); 2677 else 2678 EndDialog(hwndDlg, 0); 2679 } 2680 else if (LOWORD(wParam) == IDCANCEL) 2681 { 2682 EndDialog(hwndDlg, -1); 2683 } 2684 else if (LOWORD(wParam) == IDC_SHORTEX_RUN_DIFFERENT) 2685 { 2686 if (SendMessage(hDlgCtrl, BM_GETCHECK, 0, 0) == BST_CHECKED) 2687 SendMessage(hDlgCtrl, BM_SETCHECK, BST_UNCHECKED, 0); 2688 else 2689 SendMessage(hDlgCtrl, BM_SETCHECK, BST_CHECKED, 0); 2690 } 2691 } 2692 } 2693 return FALSE; 2694 } 2695 2696 /************************************************************************** 2697 * SH_GetTargetTypeByPath 2698 * 2699 * Function to get target type by passing full path to it 2700 */ 2701 LPWSTR SH_GetTargetTypeByPath(LPCWSTR lpcwFullPath) 2702 { 2703 LPCWSTR pwszExt; 2704 static WCHAR wszBuf[MAX_PATH]; 2705 2706 /* Get file information */ 2707 SHFILEINFOW fi; 2708 if (!SHGetFileInfoW(lpcwFullPath, 0, &fi, sizeof(fi), SHGFI_TYPENAME | SHGFI_USEFILEATTRIBUTES)) 2709 { 2710 ERR("SHGetFileInfoW failed for %ls (%lu)\n", lpcwFullPath, GetLastError()); 2711 fi.szTypeName[0] = L'\0'; 2712 fi.hIcon = NULL; 2713 } 2714 2715 pwszExt = PathFindExtensionW(lpcwFullPath); 2716 if (pwszExt[0]) 2717 { 2718 if (!fi.szTypeName[0]) 2719 { 2720 /* The file type is unknown, so default to string "FileExtension File" */ 2721 size_t cchRemaining = 0; 2722 LPWSTR pwszEnd = NULL; 2723 2724 StringCchPrintfExW(wszBuf, _countof(wszBuf), &pwszEnd, &cchRemaining, 0, L"%s ", pwszExt + 1); 2725 } 2726 else 2727 { 2728 /* Update file type */ 2729 StringCbPrintfW(wszBuf, sizeof(wszBuf), L"%s (%s)", fi.szTypeName, pwszExt); 2730 } 2731 } 2732 2733 return wszBuf; 2734 } 2735 2736 BOOL CShellLink::OnInitDialog(HWND hwndDlg, HWND hwndFocus, LPARAM lParam) 2737 { 2738 TRACE("CShellLink::OnInitDialog(hwnd %p hwndFocus %p lParam %p)\n", hwndDlg, hwndFocus, lParam); 2739 2740 TRACE("m_sArgs: %S sComponent: %S m_sDescription: %S m_sIcoPath: %S m_sPath: %S m_sPathRel: %S sProduct: %S m_sWorkDir: %S\n", m_sArgs, sComponent, m_sDescription, 2741 m_sIcoPath, m_sPath, m_sPathRel, sProduct, m_sWorkDir); 2742 2743 m_bInInit = TRUE; 2744 2745 /* Get file information */ 2746 // FIXME! FIXME! Shouldn't we use m_sIcoPath, m_Header.nIconIndex instead??? 2747 SHFILEINFOW fi; 2748 if (!SHGetFileInfoW(m_sLinkPath, 0, &fi, sizeof(fi), SHGFI_TYPENAME | SHGFI_ICON)) 2749 { 2750 ERR("SHGetFileInfoW failed for %ls (%lu)\n", m_sLinkPath, GetLastError()); 2751 fi.szTypeName[0] = L'\0'; 2752 fi.hIcon = NULL; 2753 } 2754 2755 if (fi.hIcon) 2756 { 2757 if (m_hIcon) 2758 DestroyIcon(m_hIcon); 2759 m_hIcon = fi.hIcon; 2760 SendDlgItemMessageW(hwndDlg, IDC_SHORTCUT_ICON, STM_SETICON, (WPARAM)m_hIcon, 0); 2761 } 2762 else 2763 ERR("ExtractIconW failed %ls %u\n", m_sIcoPath, m_Header.nIconIndex); 2764 2765 /* Target type */ 2766 if (m_sPath) 2767 SetDlgItemTextW(hwndDlg, IDC_SHORTCUT_TYPE_EDIT, SH_GetTargetTypeByPath(m_sPath)); 2768 2769 /* Target location */ 2770 if (m_sPath) 2771 { 2772 WCHAR target[MAX_PATH]; 2773 StringCchCopyW(target, _countof(target), m_sPath); 2774 PathRemoveFileSpecW(target); 2775 SetDlgItemTextW(hwndDlg, IDC_SHORTCUT_LOCATION_EDIT, PathFindFileNameW(target)); 2776 } 2777 2778 /* Target path */ 2779 if (m_sPath) 2780 { 2781 WCHAR newpath[2*MAX_PATH] = L"\0"; 2782 if (wcschr(m_sPath, ' ')) 2783 StringCchPrintfExW(newpath, _countof(newpath), NULL, NULL, 0, L"\"%ls\"", m_sPath); 2784 else 2785 StringCchCopyExW(newpath, _countof(newpath), m_sPath, NULL, NULL, 0); 2786 2787 if (m_sArgs && m_sArgs[0]) 2788 { 2789 StringCchCatW(newpath, _countof(newpath), L" "); 2790 StringCchCatW(newpath, _countof(newpath), m_sArgs); 2791 } 2792 SetDlgItemTextW(hwndDlg, IDC_SHORTCUT_TARGET_TEXT, newpath); 2793 } 2794 2795 /* Working dir */ 2796 if (m_sWorkDir) 2797 SetDlgItemTextW(hwndDlg, IDC_SHORTCUT_START_IN_EDIT, m_sWorkDir); 2798 2799 /* Description */ 2800 if (m_sDescription) 2801 SetDlgItemTextW(hwndDlg, IDC_SHORTCUT_COMMENT_EDIT, m_sDescription); 2802 2803 /* auto-completion */ 2804 SHAutoComplete(GetDlgItem(hwndDlg, IDC_SHORTCUT_TARGET_TEXT), SHACF_DEFAULT); 2805 SHAutoComplete(GetDlgItem(hwndDlg, IDC_SHORTCUT_START_IN_EDIT), SHACF_DEFAULT); 2806 2807 m_bInInit = FALSE; 2808 2809 return TRUE; 2810 } 2811 2812 void CShellLink::OnCommand(HWND hwndDlg, int id, HWND hwndCtl, UINT codeNotify) 2813 { 2814 switch (id) 2815 { 2816 case IDC_SHORTCUT_FIND: 2817 SHOpenFolderAndSelectItems(m_pPidl, 0, NULL, 0); 2818 /// 2819 /// FIXME 2820 /// open target directory 2821 /// 2822 return; 2823 2824 case IDC_SHORTCUT_CHANGE_ICON: 2825 { 2826 WCHAR wszPath[MAX_PATH] = L""; 2827 2828 if (m_sIcoPath) 2829 wcscpy(wszPath, m_sIcoPath); 2830 else 2831 FindExecutableW(m_sPath, NULL, wszPath); 2832 2833 INT IconIndex = m_Header.nIconIndex; 2834 if (PickIconDlg(hwndDlg, wszPath, _countof(wszPath), &IconIndex)) 2835 { 2836 SetIconLocation(wszPath, IconIndex); 2837 PropSheet_Changed(GetParent(hwndDlg), hwndDlg); 2838 2839 HICON hIconLarge = CreateShortcutIcon(wszPath, IconIndex); 2840 if (hIconLarge) 2841 { 2842 if (m_hIcon) 2843 DestroyIcon(m_hIcon); 2844 m_hIcon = hIconLarge; 2845 SendDlgItemMessageW(hwndDlg, IDC_SHORTCUT_ICON, STM_SETICON, (WPARAM)m_hIcon, 0); 2846 } 2847 } 2848 return; 2849 } 2850 2851 case IDC_SHORTCUT_ADVANCED: 2852 { 2853 INT_PTR result = DialogBoxParamW(shell32_hInstance, MAKEINTRESOURCEW(IDD_SHORTCUT_EXTENDED_PROPERTIES), hwndDlg, ExtendedShortcutProc, (LPARAM)m_bRunAs); 2854 if (result == 1 || result == 0) 2855 { 2856 if (m_bRunAs != result) 2857 { 2858 PropSheet_Changed(GetParent(hwndDlg), hwndDlg); 2859 } 2860 2861 m_bRunAs = result; 2862 } 2863 return; 2864 } 2865 } 2866 if (codeNotify == EN_CHANGE) 2867 { 2868 if (!m_bInInit) 2869 PropSheet_Changed(GetParent(hwndDlg), hwndDlg); 2870 } 2871 } 2872 2873 LRESULT CShellLink::OnNotify(HWND hwndDlg, int idFrom, LPNMHDR pnmhdr) 2874 { 2875 WCHAR wszBuf[MAX_PATH]; 2876 LPPSHNOTIFY lppsn = (LPPSHNOTIFY)pnmhdr; 2877 2878 if (lppsn->hdr.code == PSN_APPLY) 2879 { 2880 /* set working directory */ 2881 GetDlgItemTextW(hwndDlg, IDC_SHORTCUT_START_IN_EDIT, wszBuf, _countof(wszBuf)); 2882 SetWorkingDirectory(wszBuf); 2883 2884 /* set link destination */ 2885 GetDlgItemTextW(hwndDlg, IDC_SHORTCUT_TARGET_TEXT, wszBuf, _countof(wszBuf)); 2886 LPWSTR lpszArgs = NULL; 2887 LPWSTR unquoted = strdupW(wszBuf); 2888 StrTrimW(unquoted, L" "); 2889 2890 if (!PathFileExistsW(unquoted)) 2891 { 2892 lpszArgs = PathGetArgsW(unquoted); 2893 PathRemoveArgsW(unquoted); 2894 StrTrimW(lpszArgs, L" "); 2895 } 2896 if (unquoted[0] == '"' && unquoted[wcslen(unquoted) - 1] == '"') 2897 PathUnquoteSpacesW(unquoted); 2898 2899 WCHAR *pwszExt = PathFindExtensionW(unquoted); 2900 if (!wcsicmp(pwszExt, L".lnk")) 2901 { 2902 // FIXME load localized error msg 2903 MessageBoxW(hwndDlg, L"You cannot create a link to a shortcut", L"Error", MB_ICONERROR); 2904 SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE); 2905 return TRUE; 2906 } 2907 2908 if (!PathFileExistsW(unquoted)) 2909 { 2910 // FIXME load localized error msg 2911 MessageBoxW(hwndDlg, L"The specified file name in the target box is invalid", L"Error", MB_ICONERROR); 2912 SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE); 2913 return TRUE; 2914 } 2915 2916 SetPath(unquoted); 2917 if (lpszArgs) 2918 SetArguments(lpszArgs); 2919 else 2920 SetArguments(L"\0"); 2921 2922 HeapFree(GetProcessHeap(), 0, unquoted); 2923 2924 TRACE("This %p m_sLinkPath %S\n", this, m_sLinkPath); 2925 Save(m_sLinkPath, TRUE); 2926 SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATHW, m_sLinkPath, NULL); 2927 SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); 2928 return TRUE; 2929 } 2930 return FALSE; 2931 } 2932 2933 void CShellLink::OnDestroy(HWND hwndDlg) 2934 { 2935 if (m_hIcon) 2936 { 2937 DestroyIcon(m_hIcon); 2938 m_hIcon = NULL; 2939 } 2940 } 2941 2942 /************************************************************************** 2943 * SH_ShellLinkDlgProc 2944 * 2945 * dialog proc of the shortcut property dialog 2946 */ 2947 2948 INT_PTR CALLBACK 2949 CShellLink::SH_ShellLinkDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) 2950 { 2951 LPPROPSHEETPAGEW ppsp; 2952 CShellLink *pThis = reinterpret_cast<CShellLink *>(GetWindowLongPtr(hwndDlg, DWLP_USER)); 2953 2954 switch (uMsg) 2955 { 2956 case WM_INITDIALOG: 2957 ppsp = (LPPROPSHEETPAGEW)lParam; 2958 if (ppsp == NULL) 2959 break; 2960 2961 pThis = reinterpret_cast<CShellLink *>(ppsp->lParam); 2962 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pThis); 2963 return pThis->OnInitDialog(hwndDlg, (HWND)(wParam), lParam); 2964 2965 case WM_NOTIFY: 2966 return pThis->OnNotify(hwndDlg, (int)wParam, (NMHDR *)lParam); 2967 2968 case WM_COMMAND: 2969 pThis->OnCommand(hwndDlg, LOWORD(wParam), (HWND)lParam, HIWORD(wParam)); 2970 break; 2971 2972 case WM_DESTROY: 2973 pThis->OnDestroy(hwndDlg); 2974 break; 2975 2976 default: 2977 break; 2978 } 2979 2980 return FALSE; 2981 } 2982 2983 /************************************************************************** 2984 * ShellLink_IShellPropSheetExt interface 2985 */ 2986 2987 HRESULT STDMETHODCALLTYPE CShellLink::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam) 2988 { 2989 HPROPSHEETPAGE hPage = SH_CreatePropertySheetPage(IDD_SHORTCUT_PROPERTIES, SH_ShellLinkDlgProc, (LPARAM)this, NULL); 2990 if (hPage == NULL) 2991 { 2992 ERR("failed to create property sheet page\n"); 2993 return E_FAIL; 2994 } 2995 2996 if (!pfnAddPage(hPage, lParam)) 2997 return E_FAIL; 2998 2999 return S_OK; 3000 } 3001 3002 HRESULT STDMETHODCALLTYPE CShellLink::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplacePage, LPARAM lParam) 3003 { 3004 TRACE("(%p) (uPageID %u, pfnReplacePage %p lParam %p\n", this, uPageID, pfnReplacePage, lParam); 3005 return E_NOTIMPL; 3006 } 3007 3008 HRESULT STDMETHODCALLTYPE CShellLink::SetSite(IUnknown *punk) 3009 { 3010 TRACE("%p %p\n", this, punk); 3011 3012 m_site = punk; 3013 3014 return S_OK; 3015 } 3016 3017 HRESULT STDMETHODCALLTYPE CShellLink::GetSite(REFIID iid, void ** ppvSite) 3018 { 3019 TRACE("%p %s %p\n", this, debugstr_guid(&iid), ppvSite); 3020 3021 if (m_site == NULL) 3022 return E_FAIL; 3023 3024 return m_site->QueryInterface(iid, ppvSite); 3025 } 3026 3027 HRESULT STDMETHODCALLTYPE CShellLink::DragEnter(IDataObject *pDataObject, 3028 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) 3029 { 3030 TRACE("(%p)->(DataObject=%p)\n", this, pDataObject); 3031 3032 if (*pdwEffect == DROPEFFECT_NONE) 3033 return S_OK; 3034 3035 LPCITEMIDLIST pidlLast; 3036 CComPtr<IShellFolder> psf; 3037 3038 HRESULT hr = SHBindToParent(m_pPidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast); 3039 3040 if (SUCCEEDED(hr)) 3041 { 3042 hr = psf->GetUIObjectOf(0, 1, &pidlLast, IID_NULL_PPV_ARG(IDropTarget, &m_DropTarget)); 3043 3044 if (SUCCEEDED(hr)) 3045 hr = m_DropTarget->DragEnter(pDataObject, dwKeyState, pt, pdwEffect); 3046 else 3047 *pdwEffect = DROPEFFECT_NONE; 3048 } 3049 else 3050 *pdwEffect = DROPEFFECT_NONE; 3051 3052 return S_OK; 3053 } 3054 3055 HRESULT STDMETHODCALLTYPE CShellLink::DragOver(DWORD dwKeyState, POINTL pt, 3056 DWORD *pdwEffect) 3057 { 3058 TRACE("(%p)\n", this); 3059 HRESULT hr = S_OK; 3060 if (m_DropTarget) 3061 hr = m_DropTarget->DragOver(dwKeyState, pt, pdwEffect); 3062 return hr; 3063 } 3064 3065 HRESULT STDMETHODCALLTYPE CShellLink::DragLeave() 3066 { 3067 TRACE("(%p)\n", this); 3068 HRESULT hr = S_OK; 3069 if (m_DropTarget) 3070 { 3071 hr = m_DropTarget->DragLeave(); 3072 m_DropTarget.Release(); 3073 } 3074 3075 return hr; 3076 } 3077 3078 HRESULT STDMETHODCALLTYPE CShellLink::Drop(IDataObject *pDataObject, 3079 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) 3080 { 3081 TRACE("(%p)\n", this); 3082 HRESULT hr = S_OK; 3083 if (m_DropTarget) 3084 hr = m_DropTarget->Drop(pDataObject, dwKeyState, pt, pdwEffect); 3085 3086 return hr; 3087 } 3088 3089 /************************************************************************** 3090 * IShellLink_ConstructFromFile 3091 */ 3092 HRESULT WINAPI IShellLink_ConstructFromPath(WCHAR *path, REFIID riid, LPVOID *ppv) 3093 { 3094 CComPtr<IPersistFile> ppf; 3095 HRESULT hr = CShellLink::_CreatorClass::CreateInstance(NULL, IID_PPV_ARG(IPersistFile, &ppf)); 3096 if (FAILED(hr)) 3097 return hr; 3098 3099 hr = ppf->Load(path, 0); 3100 if (FAILED(hr)) 3101 return hr; 3102 3103 return ppf->QueryInterface(riid, ppv); 3104 } 3105 3106 HRESULT WINAPI IShellLink_ConstructFromFile(IShellFolder * psf, LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppv) 3107 { 3108 WCHAR path[MAX_PATH]; 3109 if (!ILGetDisplayNameExW(psf, pidl, path, 0)) 3110 return E_FAIL; 3111 3112 return IShellLink_ConstructFromPath(path, riid, ppv); 3113 } 3114 3115 HICON CShellLink::CreateShortcutIcon(LPCWSTR wszIconPath, INT IconIndex) 3116 { 3117 const INT cx = GetSystemMetrics(SM_CXICON), cy = GetSystemMetrics(SM_CYICON); 3118 const COLORREF crMask = GetSysColor(COLOR_3DFACE); 3119 HDC hDC; 3120 HIMAGELIST himl = ImageList_Create(cx, cy, ILC_COLOR32 | ILC_MASK, 1, 1); 3121 HICON hIcon = NULL, hNewIcon = NULL; 3122 HICON hShortcut = (HICON)LoadImageW(shell32_hInstance, MAKEINTRESOURCE(IDI_SHELL_SHORTCUT), 3123 IMAGE_ICON, cx, cy, 0); 3124 3125 ::ExtractIconExW(wszIconPath, IconIndex, &hIcon, NULL, 1); 3126 if (!hIcon || !hShortcut || !himl) 3127 goto cleanup; 3128 3129 hDC = CreateCompatibleDC(NULL); 3130 if (hDC) 3131 { 3132 // create 32bpp bitmap 3133 BITMAPINFO bi; 3134 ZeroMemory(&bi, sizeof(bi)); 3135 bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 3136 bi.bmiHeader.biWidth = cx; 3137 bi.bmiHeader.biHeight = cy; 3138 bi.bmiHeader.biPlanes = 1; 3139 bi.bmiHeader.biBitCount = 32; 3140 LPVOID pvBits; 3141 HBITMAP hbm = CreateDIBSection(hDC, &bi, DIB_RGB_COLORS, &pvBits, NULL, 0); 3142 if (hbm) 3143 { 3144 // draw the icon image 3145 HGDIOBJ hbmOld = SelectObject(hDC, hbm); 3146 { 3147 HBRUSH hbr = CreateSolidBrush(crMask); 3148 RECT rc = { 0, 0, cx, cy }; 3149 FillRect(hDC, &rc, hbr); 3150 DeleteObject(hbr); 3151 3152 DrawIconEx(hDC, 0, 0, hIcon, cx, cy, 0, NULL, DI_NORMAL); 3153 DrawIconEx(hDC, 0, 0, hShortcut, cx, cy, 0, NULL, DI_NORMAL); 3154 } 3155 SelectObject(hDC, hbmOld); 3156 3157 INT iAdded = ImageList_AddMasked(himl, hbm, crMask); 3158 hNewIcon = ImageList_GetIcon(himl, iAdded, ILD_NORMAL | ILD_TRANSPARENT); 3159 3160 DeleteObject(hbm); 3161 } 3162 DeleteDC(hDC); 3163 } 3164 3165 cleanup: 3166 if (hIcon) 3167 DestroyIcon(hIcon); 3168 if (hShortcut) 3169 DestroyIcon(hShortcut); 3170 if (himl) 3171 ImageList_Destroy(himl); 3172 3173 return hNewIcon; 3174 } 3175