shelllnk.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 162k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. /*++
  2. Copyright (c) 1990-1998,  Microsoft Corporation  All rights reserved.
  3. Module Name:
  4.     shelllnk.cpp
  5. Abstract:
  6.     This module implements the shell link object
  7. Revision History:
  8.     07/20/98    arulk    Created from C source for this object.
  9. --*/
  10. #include "shellprv.h"
  11. #include <shlobjp.h>
  12. #include "shelllnk.h"
  13. extern "C" {
  14. #include "vdate.h"      // For VDATEINPUTBUF
  15. #include "ids.h"        // For String Resource identifiers
  16. #include "pif.h"        // For manipulating PIF files
  17. #include "trayp.h"      // For  WMTRAY_* messages 
  18. #include "views.h"      // For FSIDM_OPENPRN
  19. #include "fstreex.h"    // For SHGetClassFlags ..
  20. #include "os.h"         // For Win32MoveFile ...
  21. #include "lnktrack.h"   // For FindInFolder
  22. #include "util.h"       // For GetMenuIndexForCanonicalVerb
  23. #include "uemapp.h"
  24. #include <filterr.h>
  25. #include "folder.h"
  26. #include "clsobj.h"     // For CFolderShortcut_CreateInstance
  27. #ifdef WINNT
  28. #include "tracker.h"
  29. #endif
  30. }
  31. // BUGBUG:(seanf) This is sleazy - This fn is defined in shlobj.h, but only if urlmon.h
  32. // was included first. Rather than monkey with the include order in
  33. // shellprv.h, we'll duplicate the prototype here, where SOFTDISTINFO
  34. // is now defined.
  35. SHDOCAPI_(DWORD) SoftwareUpdateMessageBox( HWND hWnd,
  36.                                            LPCWSTR szDistUnit,
  37.                                            DWORD dwFlags,
  38.                                            LPSOFTDISTINFO psdi );
  39. // The following strings are used to support the shell link set path hack that
  40. // allows us to bless links for Darwin without exposing stuff from IShellLinkDataList
  41. #define DARWINGUID_TAG TEXT("::{9db1186e-40df-11d1-aa8c-00c04fb67863}:")
  42. #define LOGO3GUID_TAG  TEXT("::{9db1186f-40df-11d1-aa8c-00c04fb67863}:")
  43. // #define TEST_EXTRA_DATA
  44. // this is to make sure the link file format is extensible
  45. #ifdef TEST_EXTRA_DATA
  46. BOOL bTestExtra = FALSE;
  47. #endif // TEST_EXTRA_DATA
  48. #define TF_DEBUGLINKCODE 0x00800000
  49. EXTERN_C BOOL IsFolderShortcut(LPCTSTR pszName);
  50. //-------------------------------------------------------------------------------------
  51. CShellLink::CShellLink()
  52. {
  53.     _cRef = 1;
  54. #ifdef WINNT
  55.     if (g_bRunOnNT5)
  56.         _ptracker = new CTracker( (IShellLink*) this );
  57. #endif
  58.     _ResetPersistData();
  59. }
  60. //-------------------------------------------------------------------------------------
  61. CShellLink::~CShellLink()
  62. {
  63. }
  64. STDMETHODIMP CShellLink::QueryInterface(REFIID riid, void **ppvObj)
  65. {
  66.     static const QITAB qit[] = {
  67.         QITABENT(CShellLink, IShellLinkA),
  68.         QITABENT(CShellLink, IShellLinkW),
  69.         QITABENT(CShellLink, IPersistFile),
  70.         QITABENT(CShellLink, IPersistStream),
  71.         QITABENT(CShellLink, IShellExtInit),
  72.         QITABENTMULTI(CShellLink, IContextMenu, IContextMenu3),
  73.         QITABENTMULTI(CShellLink, IContextMenu2, IContextMenu3),
  74.         QITABENT(CShellLink, IContextMenu3),
  75.         QITABENT(CShellLink, IDropTarget),
  76.         QITABENT(CShellLink, IExtractIconA),
  77.         QITABENT(CShellLink, IExtractIconW),
  78.         QITABENT(CShellLink, IShellLinkDataList),
  79.         QITABENT(CShellLink, IQueryInfo),
  80.         QITABENT(CShellLink, IPersistPropertyBag),
  81.         QITABENT(CShellLink, IObjectWithSite),
  82.         QITABENT(CShellLink, IServiceProvider),
  83.         QITABENT(CShellLink, IFilter),
  84.         { 0 },
  85.     };
  86.     HRESULT hr = QISearch(this, qit, riid, ppvObj);
  87. #ifdef WINNT
  88.     // ISLTracker is a private test interface, and isn't implemented
  89.     // in CShellLink
  90.     if (FAILED(hr) && (IID_ISLTracker == riid))
  91.     {
  92.         *ppvObj = _ptracker;
  93.         AddRef();
  94.         hr = S_OK;
  95.     }
  96. #endif
  97.     return hr;
  98. }
  99. STDMETHODIMP_(ULONG) CShellLink::AddRef()
  100. {
  101.     return InterlockedIncrement(&_cRef);
  102. }
  103. void CShellLink::_ResetPersistData()
  104. {
  105.     if (_pidl)
  106.     {
  107.         ILFree(_pidl);
  108.         _pidl = NULL;
  109.     }
  110.     if (_pli)
  111.     {
  112.         LocalFree((HLOCAL)_pli);
  113.         _pli = NULL;
  114.     }
  115.     Str_SetPtr(&_pszName, NULL);
  116.     Str_SetPtr(&_pszRelPath, NULL);
  117.     Str_SetPtr(&_pszWorkingDir, NULL);
  118.     Str_SetPtr(&_pszArgs, NULL);
  119.     Str_SetPtr(&_pszIconLocation, NULL);
  120.     if (_pExtraData)
  121.     {
  122.         SHFreeDataBlockList(_pExtraData);
  123.         _pExtraData = NULL;
  124.     }
  125. #ifdef WINNT
  126.     if (_ptracker)
  127.         _ptracker->InitNew();
  128. #endif
  129.     // init data members.  all others are zero inited
  130.     memset(&_sld, 0, SIZEOF(_sld));
  131.     _sld.iShowCmd = SW_SHOWNORMAL;
  132. }
  133. //-------------------------------------------------------------------------------------
  134. STDMETHODIMP_(ULONG) CShellLink::Release()
  135. {
  136.     if (InterlockedDecrement(&_cRef))
  137.        return _cRef;
  138.     _ResetPersistData();        // free all data
  139.     Str_SetPtr(&_pszCurFile, NULL);
  140.     Str_SetPtr(&_pszRelSource, NULL);
  141.     if (_pcmTarget)
  142.         _pcmTarget->Release();
  143.     if (_pdtSrc)
  144.         _pdtSrc->Release();
  145.     if (_pxi)
  146.         _pxi->Release();
  147.     if (_pxiA)
  148.         _pxiA->Release();
  149. #ifdef WINNT
  150.     if (_ptracker)
  151.         delete _ptracker;
  152. #endif
  153.     delete this;
  154.     return 0;
  155. }
  156. #if 0
  157. void DumpPLI(PCLINKINFO pli)
  158. {
  159.     LPCTSTR p;
  160.     if (!pli)
  161.         return;
  162.     DebugMsg(DM_TRACE, TEXT("DumpPLI:"));
  163.     if (GetLinkInfoData(pli, LIDT_VOLUME_SERIAL_NUMBER, &p))
  164.         DebugMsg(DM_TRACE, TEXT("tSerial #t%d"), *p);
  165.     if (GetLinkInfoData(pli, LIDT_DRIVE_TYPE, &p))
  166.         DebugMsg(DM_TRACE, TEXT("tDrive Typet%d"), *p);
  167.     if (GetLinkInfoData(pli, LIDT_VOLUME_LABEL, &p))
  168.         DebugMsg(DM_TRACE, TEXT("tLabelt%s"), p);
  169.     if (GetLinkInfoData(pli, LIDT_LOCAL_BASE_PATH, &p))
  170.         DebugMsg(DM_TRACE, TEXT("tBase Patht%s"), p);
  171.     if (GetLinkInfoData(pli, LIDT_NET_RESOURCE, &p))
  172.         DebugMsg(DM_TRACE, TEXT("tNet Rest%s"), p);
  173.     if (GetLinkInfoData(pli, LIDT_COMMON_PATH_SUFFIX, &p))
  174.         DebugMsg(DM_TRACE, TEXT("tPath Sufixt%s"), p);
  175. }
  176. #else
  177. #define DumpPLI(p)
  178. #endif
  179. // Compare _sld to a WIN32_FIND_DATA
  180. BOOL CShellLink::IsEqualFindData(const WIN32_FIND_DATA *pfd)
  181. {
  182.     return (pfd->dwFileAttributes == _sld.dwFileAttributes)                       &&
  183.            (CompareFileTime(&pfd->ftCreationTime, &_sld.ftCreationTime) == 0)     &&
  184.            (CompareFileTime(&pfd->ftLastWriteTime, &_sld.ftLastWriteTime) == 0)   &&
  185.            (pfd->nFileSizeLow == _sld.nFileSizeLow);
  186. }
  187. BOOL CShellLink::SetFindData(const WIN32_FIND_DATA *pfd)
  188. {
  189.     if (!IsEqualFindData(pfd))
  190.     {
  191.         _sld.dwFileAttributes = pfd->dwFileAttributes;
  192.         _sld.ftCreationTime = pfd->ftCreationTime;
  193.         _sld.ftLastAccessTime = pfd->ftLastAccessTime;
  194.         _sld.ftLastWriteTime = pfd->ftLastWriteTime;
  195.         _sld.nFileSizeLow = pfd->nFileSizeLow;
  196.         _bDirty = TRUE;
  197.         return TRUE;
  198.     }
  199.     return FALSE;
  200. }
  201. PLINKINFO CopyLinkInfo(PCLINKINFO pcliSrc)
  202. {
  203.     ASSERT(pcliSrc);
  204.     DWORD dwSize = *(UNALIGNED DWORD *)pcliSrc; // size of this thing
  205.     PLINKINFO pli = (PLINKINFO)LocalAlloc(LPTR, dwSize);      // make a copy
  206.     if (pli)
  207.         CopyMemory(pli, pcliSrc, dwSize);
  208.     return  pli;
  209. }
  210. // Creates LinkInfo for a CShellLink instance that does not yet have one
  211. // create the LinkInfo from the pidl (assumed to be to a path) for this link
  212. //
  213. // returns:
  214. //
  215. //      success, pointer to the LINKINFO
  216. //      NULL     this link does not have LINKINFO
  217. PLINKINFO CShellLink::GetLinkInfo()
  218. {
  219.     PLINKINFO pliNew;
  220.     TCHAR szPath[MAX_PATH];
  221.     if ((_pidl == NULL) && (_sld.dwFlags & SLDF_HAS_RELPATH))
  222.     {
  223.         TCHAR szTmp[MAX_PATH];
  224.         _ResolveRelative(szTmp);
  225.     }
  226.     if (!_pidl || !SHGetPathFromIDList(_pidl, szPath))
  227.     {
  228.         DebugMsg(DM_TRACE, TEXT("GetLinkInfo called for non file link"));
  229.         return NULL;
  230.     }
  231.     if (_pli)
  232.     {
  233.         LocalFree((HLOCAL)_pli);
  234.         _pli = NULL;
  235.     }
  236.     // this bit disables LINKINFO tracking on a per link basis, this is set
  237.     // externally by admins to make links more "transparent"
  238.     if (!(_sld.dwFlags & SLDF_FORCE_NO_LINKINFO))
  239.     {
  240.         if (CreateLinkInfo(szPath, &pliNew))
  241.         {
  242.             _pli = CopyLinkInfo(pliNew);
  243.             _bDirty = TRUE;
  244.             DestroyLinkInfo(pliNew);
  245.         }
  246.     }
  247.     return _pli;
  248. }
  249. void PathGetRelative(LPTSTR pszPath, LPCTSTR pszFrom, DWORD dwAttrFrom, LPCTSTR pszRel)
  250. {
  251.     TCHAR szRoot[MAX_PATH];
  252.     lstrcpy(szRoot, pszFrom);
  253.     if (!(dwAttrFrom & FILE_ATTRIBUTE_DIRECTORY))
  254.         PathRemoveFileSpec(szRoot);
  255.     ASSERT(PathIsRelative(pszRel));
  256.     PathCombine(pszPath, szRoot, pszRel);
  257. }
  258. //
  259. // update the working dir to match changes being made to the link target
  260. //
  261. void CShellLink::UpdateWorkingDir(LPCITEMIDLIST pidlNew)
  262. {
  263.     TCHAR szOld[MAX_PATH], szNew[MAX_PATH], szPath[MAX_PATH];
  264.     if (_pszWorkingDir == NULL ||
  265.         _pszWorkingDir[0] == 0 ||
  266.         StrChr(_pszWorkingDir, TEXT('%')) ||
  267.         _pidl == NULL ||
  268.         !SHGetPathFromIDList(_pidl, szOld) ||
  269.         !SHGetPathFromIDList(pidlNew, szNew) ||
  270.         (lstrcmpi(szOld, szNew) == 0))
  271.         return;
  272.     if (PathRelativePathTo(szPath, szOld, _sld.dwFileAttributes, _pszWorkingDir, FILE_ATTRIBUTE_DIRECTORY))
  273.     {
  274.         PathGetRelative(szOld, szNew, GetFileAttributes(szNew), szPath);        // get result is szOld
  275.         if (PathIsDirectory(szOld))
  276.         {
  277.             DebugMsg(DM_TRACE, TEXT("working dir updated to %s"), szOld);
  278.             Str_SetPtr(&_pszWorkingDir, szOld);
  279.             _bDirty = TRUE;
  280.         }
  281.     }
  282. }
  283. // set the pidl either based on a new pidl or a path
  284. // this will set the dirty flag if this info is different from the current
  285. //
  286. // in:
  287. //      pidlNew         if non-null, use as new PIDL for link
  288. //      pszPath         if non-null, create a pidl for this and set it
  289. //      pfdNew          find data for this file (if we already have it)
  290. //
  291. // returns:
  292. //      TRUE            successfully set the pidl (or it was unchanged)
  293. //      FALSE           memory failure or pszPath does not exist
  294. BOOL CShellLink::SetPIDLPath(LPCITEMIDLIST pidlNew, LPCTSTR pszPath, const WIN32_FIND_DATA *pfdNew)
  295. {
  296.     LPITEMIDLIST pidlCreated = NULL;
  297.     if (pszPath && !pidlNew)
  298.     {
  299.         TCHAR szPath[MAX_PATH];
  300.         LPITEMIDLIST pidlDesktop;
  301.         
  302.         // the path is the same as the current pidl, short circuit the disk hits
  303.         if (_pidl && SHGetPathFromIDList(_pidl, szPath) && !lstrcmpi(szPath, pszPath))
  304.             return TRUE;
  305.         pidlDesktop = SHCloneSpecialIDList(NULL, CSIDL_DESKTOPDIRECTORY, TRUE);
  306.         pidlCreated = ILCreateFromPath(pszPath);
  307.         // before we just used to create the pidl from path
  308.         // now we check if pidl is immediate child of a desktop
  309.         // and if it is then we use ILFindLast else we use the
  310.         // fully created pidl
  311.         // this is done because the items on the desktop have pidls
  312.         // relative to the desktop, not the full path
  313.         // if we did not do this and we updated the pidl of e.g. 
  314.         // shortcut (on desktop) to an item (on desktop), the new pidl
  315.         // would be a full path pidl and it would be different from the
  316.         // one of the item it points to which is a bug
  317.         if (pidlDesktop)
  318.         {
  319.             if (pidlCreated && ILIsParent(pidlDesktop, pidlCreated, TRUE))
  320.             {
  321.                 LPITEMIDLIST pidlTmp = pidlCreated;
  322.                 pidlCreated = ILClone(ILFindLastID(pidlCreated));
  323.                 ILFree(pidlTmp);
  324.             }
  325.             ILFree(pidlDesktop);
  326.         }        
  327.         if (pidlCreated == NULL)
  328.         {
  329.             TCHAR achPath[MAX_PATH];
  330.             DebugMsg(DM_TRACE, TEXT("Failed to create pidl for link (trying simple PIDL)"));
  331.             lstrcpy(achPath, pszPath);
  332.             PathResolve(achPath, NULL, PRF_TRYPROGRAMEXTENSIONS);
  333.             pidlCreated = SHSimpleIDListFromPath(achPath);
  334.         }
  335.         if (!pidlCreated)
  336.         {
  337.             DebugMsg(DM_TRACE, TEXT("Failed to create pidl for link"));
  338.             return FALSE;
  339.         }
  340.         pidlNew = pidlCreated;
  341.     }
  342.     if (!pidlNew)
  343.     {
  344.         if (_pidl)
  345.         {
  346.             ILFree(_pidl);
  347.             _pidl = NULL;
  348.             _bDirty = TRUE;
  349.         }
  350.         if (_pli)
  351.         {
  352.             LocalFree((HLOCAL)_pli);
  353.             _pli = NULL;
  354.         }
  355.         memset((void *)&_sld, 0, SIZEOF(_sld));
  356.     }
  357.     else
  358.     {
  359.         // NOTE: this can result in an asser in the fs compare items if the 2 pidls
  360.         // are both simple.  but since this is just an optimization to avoid resetting
  361.         // the pidl if it is the same that can be ignored.
  362.         if (!_pidl || !ILIsEqual(_pidl, pidlNew))
  363.         {
  364.             /* Yes.  Save updated path IDL. */
  365.             LPITEMIDLIST pidlClone = ILClone(pidlNew);
  366.             if (pidlClone)
  367.             {
  368.                 UpdateWorkingDir(pidlClone);
  369.                 if (_pidl)
  370.                     ILFree(_pidl);
  371.                 _pidl = pidlClone;
  372.                 // we are modifing _pidl.  We should never have a EXP_SPECIAL_FOLDER_SIG section
  373.                 // when we modify the _pidl, otherwise the two will be out of ssync
  374.                 ASSERT( NULL == SHFindDataBlock(_pExtraData, EXP_SPECIAL_FOLDER_SIG) );
  375.                 GetLinkInfo();      // construct the LinkInfo (pli)
  376.                 DumpPLI(_pli);
  377.             }
  378.             else
  379.             {
  380.                 DebugMsg(DM_TRACE, TEXT("SetPIDLPath ILClone failed"));
  381.                 return FALSE;
  382.             }
  383.             _bDirty = TRUE;
  384.         }
  385.         if (pfdNew)
  386.             SetFindData(pfdNew); // Win9x only
  387.     }
  388.     if (pidlCreated)
  389.         ILFree(pidlCreated);
  390.     return TRUE;
  391. }
  392. // sees if this link might have a relative path
  393. //
  394. //
  395. // out:
  396. //      pszPath returned new path
  397. //
  398. // returns:
  399. //      TRUE    we found a relative path and it exists
  400. //      FALSE   outa luck.
  401. //
  402. BOOL CShellLink::_ResolveRelative(LPTSTR pszPath)
  403. {
  404.     LPCTSTR pszPathRel;
  405.     TCHAR szRoot[MAX_PATH];
  406.     // pszRelSource overrides pszCurFile
  407.     pszPathRel = _pszRelSource ? _pszRelSource : _pszCurFile;
  408.     if (pszPathRel == NULL || _pszRelPath == NULL)
  409.         return FALSE;
  410.     lstrcpy(szRoot, pszPathRel);
  411.     PathRemoveFileSpec(szRoot);         // pszfrom is a file (not a directory)
  412.     PathCombine(pszPath, szRoot, _pszRelPath);
  413.     if (PathFileExistsAndAttributes(pszPath, NULL))
  414.     {
  415.         DebugMsg(DM_TRACE, TEXT("_ResolveRelative() returning %s"), pszPath);
  416.         return SetPIDLPath(NULL, pszPath, NULL);
  417.     }
  418.     return FALSE;
  419. }
  420. void CShellLink::GetFindData(WIN32_FIND_DATA *pfd)
  421. {
  422.     TCHAR szPath[MAX_PATH];
  423.     pfd->dwFileAttributes = _sld.dwFileAttributes;
  424.     pfd->ftCreationTime = _sld.ftCreationTime;
  425.     pfd->ftLastAccessTime = _sld.ftLastAccessTime;
  426.     pfd->ftLastWriteTime = _sld.ftLastWriteTime;
  427.     pfd->nFileSizeLow = _sld.nFileSizeLow;
  428.     pfd->nFileSizeHigh = 0;
  429.     SHGetPathFromIDList(_pidl, szPath);
  430.     // no one should call this on a pidl without a path
  431.     ASSERT(szPath[0]);
  432.     lstrcpy(pfd->cFileName, PathFindFileName(szPath));
  433. }
  434. STDMETHODIMP CShellLink::GetPath(LPWSTR pszFile, int cchMaxPath, WIN32_FIND_DATAW *pfd, DWORD fFlags)
  435. {
  436.     TCHAR szPath[MAX_PATH];
  437.     VDATEINPUTBUF(pszFile, TCHAR, cchMaxPath);
  438.     DumpPLI(_pli);
  439.     // For darwin enabled links, we do NOT want to have to go and call         
  440.     // ParseDarwinID here because that could possible force the app to install.
  441.     // So, instead we return the path to the icon as the path for darwin enable
  442.     // shortcuts. This allows the icon to be correct and since the darwin icon 
  443.     // will always be an .exe, ensuring that the context menu will be correct. 
  444.     if (_sld.dwFlags & SLDF_HAS_DARWINID)                                 
  445.     {                                                                          
  446.         if (_pszIconLocation && _pszIconLocation[0])                 
  447.         {                                                                      
  448.             SHExpandEnvironmentStrings(_pszIconLocation, szPath, ARRAYSIZE(szPath));
  449.             SHTCharToUnicode(szPath, pszFile, cchMaxPath);                             
  450.             return S_OK;                                                       
  451.         }                                                                      
  452.         else                                                                   
  453.         {                                                                      
  454.             // we should never have a darwin link that does not have           
  455.             // an icon path.                                                   
  456.             ASSERT(FALSE);                                                     
  457.             return S_FALSE;                                                    
  458.         }                                                                      
  459.     }                                                                                                                                                   
  460.     if ((_pidl == NULL) && (_sld.dwFlags & SLDF_HAS_RELPATH))
  461.     {
  462.         TCHAR szTmp[MAX_PATH];
  463.         _ResolveRelative(szTmp);
  464.     }
  465.     if (!_pidl || !SHGetPathFromIDListEx(_pidl, szPath, (fFlags & SLGP_SHORTPATH) ? GPFIDL_ALTNAME : 0))
  466.         szPath[0] = 0;
  467.     if ((_sld.dwFlags & SLDF_HAS_EXP_SZ) && (fFlags & SLGP_RAWPATH))
  468.     {
  469.         // Special case where we grab the Target name from
  470.         // the extra data section of the link rather than from
  471.         // the pidl.  We do this after we grab the name from the pidl
  472.         // so that if we fail, then there is still some hope that a
  473.         // name can be returned.
  474.         LPEXP_SZ_LINK pszl = (LPEXP_SZ_LINK)SHFindDataBlock(_pExtraData, EXP_SZ_LINK_SIG);
  475.         if (pszl)
  476.         {
  477.             SHUnicodeToTChar(pszl->swzTarget, szPath, ARRAYSIZE(szPath));
  478.             DebugMsg( DM_TRACE, TEXT("CShellLink::GetPath() %s (from xtra data)"), szPath );
  479.         }
  480.     }
  481.     if (pszFile)
  482.     {
  483.         SHTCharToUnicode(szPath, pszFile, cchMaxPath);
  484.     }
  485.     if (pfd)
  486.     {
  487.         memset(pfd, 0, SIZEOF(*pfd));
  488.         if (szPath[0])
  489.         {
  490.             pfd->dwFileAttributes = _sld.dwFileAttributes;
  491.             pfd->ftCreationTime = _sld.ftCreationTime;
  492.             pfd->ftLastAccessTime = _sld.ftLastAccessTime;
  493.             pfd->ftLastWriteTime = _sld.ftLastWriteTime;
  494.             pfd->nFileSizeLow = _sld.nFileSizeLow;
  495.             SHTCharToUnicode(PathFindFileName(szPath), pfd->cFileName, ARRAYSIZE(pfd->cFileName));
  496.         }
  497.     }
  498.     return (_pidl != NULL && szPath[0] == 0) ? S_FALSE : S_OK;
  499. }
  500. STDMETHODIMP CShellLink::GetIDList(LPITEMIDLIST *ppidl)
  501. {
  502.     if (_pidl)
  503.         return SHILClone(_pidl, ppidl);
  504.     *ppidl = NULL;
  505.     return S_FALSE;     // success but empty
  506. }
  507. #ifdef DEBUG
  508. #define DumpTimes(ftCreate, ftAccessed, ftWrite) 
  509.     DebugMsg(DM_TRACE, TEXT("create   %8x%8x"), ftCreate.dwLowDateTime,   ftCreate.dwHighDateTime);     
  510.     DebugMsg(DM_TRACE, TEXT("accessed %8x%8x"), ftAccessed.dwLowDateTime, ftAccessed.dwHighDateTime);   
  511.     DebugMsg(DM_TRACE, TEXT("write    %8x%8x"), ftWrite.dwLowDateTime,    ftWrite.dwHighDateTime);
  512. #else
  513. #define DumpTimes(ftCreate, ftAccessed, ftWrite)
  514. #endif
  515. void CheckAndFixNullCreateTime(LPCTSTR pszFile, FILETIME *pftCreationTime, const FILETIME *pftLastWriteTime)
  516. {
  517.     if (IsNullTime(pftCreationTime))
  518.     {
  519.         HANDLE hfile = CreateFile(pszFile, GENERIC_READ | GENERIC_WRITE,
  520.                                    FILE_SHARE_READ | FILE_SHARE_WRITE,
  521.                                    NULL, OPEN_EXISTING, 0, NULL);
  522.         DebugMsg(DM_TRACE, TEXT("NULL create time"));
  523.         // this file has a bogus create time, set it to the last accessed time
  524.         if (hfile != INVALID_HANDLE_VALUE)
  525.         {
  526.             DebugMsg(DM_TRACE, TEXT("pfd times"));
  527.             DebugMsg(DM_TRACE, TEXT("create   %8x%8x"), pftCreationTime->dwLowDateTime, pftCreationTime->dwHighDateTime);
  528.             if (SetFileTime(hfile, pftLastWriteTime, NULL, NULL))
  529.             {
  530.                 // BUGBUG: get the time back to make sure we match the precision of the file system
  531.                 *pftCreationTime = *pftLastWriteTime;     // patch this up
  532. #ifdef DEBUG
  533.                 {
  534.                     FILETIME ftCreate, ftAccessed, ftWrite;
  535.                     GetFileTime((HANDLE)hfile, &ftCreate, &ftAccessed, &ftWrite);
  536.                     AssertMsg(CompareFileTime(&ftCreate, pftCreationTime) == 0, TEXT("create times don't match"));
  537.                     DumpTimes(ftCreate, ftAccessed, ftWrite);
  538.                 }
  539. #endif
  540.             }
  541.             else
  542.             {
  543.                 DebugMsg(DM_TRACE, TEXT("unable to set create time"));
  544.             }
  545.             CloseHandle(hfile);
  546.         }
  547.     }
  548. }
  549. // Compare _sld to a BY_HANDLE_FILE_INFORMATION
  550. BOOL CShellLink::IsEqualFileInfo(const BY_HANDLE_FILE_INFORMATION *pFileInfo)
  551. {
  552.     return (pFileInfo->dwFileAttributes == _sld.dwFileAttributes)                       &&
  553.            (CompareFileTime(&pFileInfo->ftCreationTime, &_sld.ftCreationTime) == 0)     &&
  554.            (CompareFileTime(&pFileInfo->ftLastWriteTime, &_sld.ftLastWriteTime) == 0)   &&
  555.            (pFileInfo->nFileSizeLow == _sld.nFileSizeLow);
  556. }
  557. //
  558. // Query the file/directory at pszPath for it's file attributes
  559. // and CTracker information.  Use this info to update sld
  560. // and ptracker.
  561. //
  562. BOOL CShellLink::QueryAndSetFindData(LPCTSTR pszPath)
  563. {
  564. #ifdef WINNT
  565.     // Open the file or directory.  We have to set FILE_FLAG_BACKUP_SEMANTICS
  566.     // to get CreateFile to give us directory handles.
  567.     BOOL bRes = FALSE;
  568.     HANDLE hFile = CreateFile(pszPath, FILE_READ_ATTRIBUTES,
  569.                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  570.                         NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL);
  571.     if (INVALID_HANDLE_VALUE != hFile)
  572.     {
  573.         // Get the file attributes
  574.         BY_HANDLE_FILE_INFORMATION FileInfo;
  575.         if (GetFileInformationByHandle(hFile, &FileInfo))
  576.         {
  577.             bRes = TRUE;
  578.             // If this file doesn't have a create time for some reason, set it to be the
  579.             // current last-write time.
  580.             CheckAndFixNullCreateTime(pszPath, &FileInfo.ftCreationTime, &FileInfo.ftLastWriteTime);
  581.             // If the attributes are different that what we have cached, then keep
  582.             // the new values.
  583.             if (!IsEqualFileInfo(&FileInfo))
  584.             {
  585.                 _sld.dwFileAttributes = FileInfo.dwFileAttributes;
  586.                 _sld.ftCreationTime = FileInfo.ftCreationTime;
  587.                 _sld.ftLastAccessTime = FileInfo.ftLastAccessTime;
  588.                 _sld.ftLastWriteTime = FileInfo.ftLastWriteTime;
  589.                 _sld.nFileSizeLow = FileInfo.nFileSizeLow;
  590.                 _bDirty = TRUE;
  591.             }
  592.             
  593.             // save the Object IDs as well.
  594.             if (_ptracker)
  595.             {
  596.                 if (SUCCEEDED(_ptracker->InitFromHandle( hFile, pszPath)))
  597.                 {
  598.                     if (_ptracker->IsDirty())
  599.                         _bDirty = TRUE;
  600.                 }
  601.                 else
  602.                 {
  603.                     //Save space in the .lnk file
  604.                     _ptracker->InitNew();
  605.                 }
  606.             }
  607.         }
  608.         else
  609.         {
  610.             DebugMsg( DM_ERROR, TEXT("Couldn't get file info by handle (%d)"), GetLastError() );
  611.         }
  612.         CloseHandle(hFile);
  613.     }
  614.     else
  615.     {
  616.         DebugMsg( DM_TRACE, TEXT("QueryAndSetFindData couldn't open "%s" (%08X)"), pszPath, GetLastError() );
  617.     }
  618.     return bRes;
  619. #else // #ifdef WINNT
  620.     // Win95 does not support CreateFile() on folders!
  621.     WIN32_FIND_DATA fd;
  622.     HANDLE hFind = FindFirstFile(pszPath, &fd);
  623.     if (hFind == INVALID_HANDLE_VALUE)
  624.         return FALSE;
  625.     FindClose(hFind);
  626.     CheckAndFixNullCreateTime(pszPath, &fd.ftCreationTime, &fd.ftLastWriteTime);
  627.     SetFindData(&fd);
  628.     return TRUE;
  629. #endif // #ifdef WINNT ... #else
  630. }
  631. STDMETHODIMP CShellLink::SetIDList(LPCITEMIDLIST pidlnew)
  632. {
  633.     HRESULT hr = S_OK;
  634.     TCHAR szPath[MAX_PATH];
  635.     if (pidlnew != _pidl)
  636.         SetPIDLPath(pidlnew, NULL, NULL);
  637.     // is this a pidl to a file?
  638.     if (_pidl && SHGetPathFromIDList(_pidl, szPath))
  639.     {
  640.         // DebugMsg(DM_TRACE, "ShellLink::SetIDList(%s)", szPath);
  641.         if (PathIsRoot(szPath))
  642.         {
  643.             memset(&_sld, 0, SIZEOF(_sld));
  644.             _sld.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
  645.         }
  646.         else
  647.         {
  648.             if( !QueryAndSetFindData(szPath ) )
  649.                 hr = S_FALSE;
  650.         }
  651.     }
  652.     return hr;
  653. }
  654. BOOL DifferentStrings(LPCTSTR psz1, LPCTSTR psz2)
  655. {
  656.     if (psz1 && psz2)
  657.         return lstrcmp(psz1, psz2);
  658.     else
  659.         return (!psz1 && psz2) || (psz1 && !psz2);
  660. }
  661. // NOTE: NULL string ptr is valid argument for this function
  662. HRESULT CShellLink::_SetField(LPTSTR *ppszField, LPCWSTR pszValueW)
  663. {
  664.     TCHAR szValue[INFOTIPSIZE], *pszValue;
  665.     if (pszValueW)
  666.     {
  667.         SHUnicodeToTChar(pszValueW, szValue, ARRAYSIZE(szValue));
  668.         pszValue = szValue;
  669.     }
  670.     else
  671.         pszValue = NULL;
  672.     if (DifferentStrings(*ppszField, pszValue))
  673.         _bDirty = TRUE;
  674.     Str_SetPtr(ppszField, pszValue);
  675.     return S_OK;
  676. }
  677. HRESULT CShellLink::_SetField(LPTSTR *ppszField, LPCSTR pszValueA)
  678. {
  679.     TCHAR szValue[INFOTIPSIZE], *pszValue;
  680.     if (pszValueA)
  681.     {
  682.         SHAnsiToTChar(pszValueA, szValue, ARRAYSIZE(szValue));
  683.         pszValue = szValue;
  684.     }
  685.     else
  686.         pszValue = NULL;
  687.     if (DifferentStrings(*ppszField, pszValue))
  688.         _bDirty = TRUE;
  689.     Str_SetPtr(ppszField, pszValue);
  690.     return S_OK;
  691. }
  692. HRESULT CShellLink::_GetField(LPCTSTR pszField, LPWSTR pszValue, int cchValue)
  693. {
  694.     if (pszField == NULL)
  695.         *pszValue = 0;
  696.     else
  697.     {
  698.         SHTCharToUnicode(pszField, pszValue, cchValue);
  699.     }
  700.     return S_OK;
  701. }
  702. HRESULT CShellLink::_GetField(LPCTSTR pszField, LPSTR pszValue, int cchValue)
  703. {
  704.     if (pszField == NULL)
  705.         *pszValue = 0;
  706.     else
  707.     {
  708.         SHTCharToAnsi(pszField, pszValue, cchValue);
  709.     }
  710.     return S_OK;
  711. }
  712. //  order is important
  713. const int c_rgcsidlUserFolders[] = {
  714.     CSIDL_MYPICTURES | TEST_SUBFOLDER,
  715.     CSIDL_PERSONAL | TEST_SUBFOLDER,
  716.     CSIDL_DESKTOPDIRECTORY | TEST_SUBFOLDER,
  717.     CSIDL_COMMON_DESKTOPDIRECTORY | TEST_SUBFOLDER,
  718. };
  719. STDAPI_(void) SHMakeDescription(LPCITEMIDLIST pidlDesc, int ids, LPTSTR pszDesc, UINT cch)
  720. {
  721.     LPCITEMIDLIST pidlName = pidlDesc;
  722.     TCHAR szPath[MAX_PATH], szFormat[64];
  723.     DWORD gdn;
  724.     ASSERT(pidlDesc);
  725.     
  726.     //
  727.     //  we want to only show the INFOLDER name for 
  728.     //  folders the user sees often.  so in the desktop
  729.     //  or mydocs or mypics we just show that name.
  730.     //  otherwise show the whole path.
  731.     //
  732.     //  NOTE - there can be some weirdness if you start making
  733.     //  shortcuts to special folders off the desktop
  734.     //  specifically if you make a shortcut to mydocs the comment
  735.     //  ends up being %USERPROFILE%, but this is a rare enough 
  736.     //  case that i dont think we need to worry too much.
  737.     //
  738.     SHGetNameAndFlags(pidlDesc, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath), NULL);
  739.     int csidl = GetSpecialFolderID(szPath, c_rgcsidlUserFolders, ARRAYSIZE(c_rgcsidlUserFolders));
  740.     if (-1 != csidl)
  741.     {
  742.         gdn = SHGDN_INFOLDER   | SHGDN_FORADDRESSBAR;
  743.         switch (csidl)
  744.         {
  745.         case CSIDL_DESKTOPDIRECTORY:
  746.         case CSIDL_COMMON_DESKTOPDIRECTORY:
  747.             {
  748.                 ULONG cb;
  749.                 if (csidl == GetSpecialFolderParentIDAndOffset(pidlDesc, &cb))
  750.                 {
  751.                     //  reorient based off the desktop.
  752.                     pidlName = (LPCITEMIDLIST)(((BYTE *)pidlDesc) + cb);
  753.                 }
  754.             }
  755.             break;
  756.         case CSIDL_PERSONAL:
  757.             if (SUCCEEDED(GetMyDocumentsDisplayName(szPath, ARRAYSIZE(szPath))))
  758.                 pidlName = NULL;
  759.             break;
  760.         default:
  761.             break;
  762.         }
  763.     }
  764.     else
  765.         gdn = SHGDN_FORPARSING | SHGDN_FORADDRESSBAR;
  766.     if (pidlName)
  767.         SHGetNameAndFlags(pidlName, gdn, szPath, ARRAYSIZE(szPath), NULL);
  768. #if 0       //  if we ever want to handle frienly URL comments
  769.     if (UrlIs(pszPath, URLIS_URL))
  770.     {
  771.         DWORD cchPath = SIZECHARS(szPath);
  772.         if (FAILED(UrlCombine(pszPath, TEXT(""), szPath, &cchPath, 0)))
  773.         {
  774.             // if the URL is too big, then just use the hostname...
  775.             cchPath = SIZECHARS(szPath);
  776.             UrlCombine(pszPath, TEXT("/"), szPath, &cchPath, 0);
  777.         }
  778.     }
  779. #endif
  780.     if (ids != -1)
  781.     {
  782.         LoadString(HINST_THISDLL, ids, szFormat, ARRAYSIZE(szFormat));
  783.         wnsprintf(pszDesc, cch, szFormat, szPath);
  784.     }
  785.     else
  786.         StrCpyN(pszDesc, szPath, cch);
  787. }
  788. void _MakeDescription(LPCITEMIDLIST pidlTo, LPTSTR pszDesc, UINT cch)
  789. {
  790.     LPITEMIDLIST pidlParent = ILCloneParent(pidlTo);
  791.     if (pidlParent)
  792.     {
  793.         SHMakeDescription(pidlParent, IDS_LOCATION, pszDesc, cch);
  794.         ILFree(pidlParent);
  795.     }
  796.     else
  797.         *pszDesc = 0;
  798. }
  799. STDMETHODIMP CShellLink::GetDescription(LPWSTR pszDesc, int cchMax)
  800. {
  801.     return _GetField(_pszName, pszDesc, cchMax);
  802. }
  803. STDMETHODIMP CShellLink::GetDescription(LPSTR pszDesc, int cchMax)
  804. {
  805.     return _GetField(_pszName, pszDesc, cchMax);
  806. }
  807. STDMETHODIMP CShellLink::SetDescription(LPCWSTR pszDesc)
  808. {
  809.     return _SetField(&_pszName, pszDesc);
  810. }
  811. STDMETHODIMP CShellLink::SetDescription(LPCSTR pszDesc)
  812. {
  813.     return _SetField(&_pszName, pszDesc);
  814. }
  815. STDMETHODIMP CShellLink::GetWorkingDirectory(LPWSTR pszDir, int cchDir)
  816. {
  817.     return _GetField(_pszWorkingDir, pszDir, cchDir);
  818. }
  819. STDMETHODIMP CShellLink::GetWorkingDirectory(LPSTR pszDir, int cchMaxPath)
  820. {
  821.     return _GetField(_pszWorkingDir, pszDir, cchMaxPath);
  822. }
  823. STDMETHODIMP CShellLink::SetWorkingDirectory(LPCWSTR pszWorkingDir)
  824. {
  825.     return _SetField(&_pszWorkingDir, pszWorkingDir);
  826. }
  827. STDMETHODIMP CShellLink::SetWorkingDirectory(LPCSTR pszDir)
  828. {
  829.     return _SetField(&_pszWorkingDir, pszDir);
  830. }
  831. STDMETHODIMP CShellLink::GetArguments(LPWSTR pszArgs, int cchArgs)
  832. {
  833.     return _GetField(_pszArgs, pszArgs, cchArgs);
  834. }
  835. STDMETHODIMP CShellLink::GetArguments(LPSTR pszArgs, int cchMaxPath)
  836. {
  837.     return _GetField(_pszArgs, pszArgs, cchMaxPath);
  838. }
  839. STDMETHODIMP CShellLink::SetArguments(LPCWSTR pszArgs)
  840. {
  841.     return _SetField(&_pszArgs, pszArgs);
  842. }
  843. STDMETHODIMP CShellLink::SetArguments(LPCSTR pszArgs)
  844. {
  845.     return _SetField(&_pszArgs, pszArgs);
  846. }
  847. STDMETHODIMP CShellLink::GetHotkey(WORD *pwHotkey)
  848. {
  849.     *pwHotkey = _sld.wHotkey;
  850.     return S_OK;
  851. }
  852. STDMETHODIMP CShellLink::SetHotkey(WORD wHotkey)
  853. {
  854.     if (_sld.wHotkey != wHotkey)
  855.     {
  856.         _bDirty = TRUE;
  857.         _sld.wHotkey = wHotkey;
  858.     }
  859.     return S_OK;
  860. }
  861. STDMETHODIMP CShellLink::GetShowCmd(int *piShowCmd)
  862. {
  863.     *piShowCmd = _sld.iShowCmd;
  864.     return S_OK;
  865. }
  866. STDMETHODIMP CShellLink::SetShowCmd(int iShowCmd)
  867. {
  868.     if (_sld.iShowCmd != iShowCmd)
  869.         _bDirty = TRUE;
  870.     _sld.iShowCmd = iShowCmd;
  871.     return S_OK;
  872. }
  873. // IShellLinkW::GetIconLocation
  874. STDMETHODIMP CShellLink::GetIconLocation(LPWSTR pszIconPath, int cchIconPath, int *piIcon)
  875. {
  876.     VDATEINPUTBUF(pszIconPath, TCHAR, cchIconPath);
  877.     BOOL fFound = FALSE;
  878.     if (_sld.dwFlags & SLDF_HAS_EXP_ICON_SZ)
  879.     {
  880.         LPEXP_SZ_LINK pszl = (LPEXP_SZ_LINK)SHFindDataBlock(_pExtraData, EXP_SZ_ICON_SIG);
  881.         if (pszl)
  882.         {
  883.             SHExpandEnvironmentStringsW(pszl->swzTarget, pszIconPath, cchIconPath);
  884.             fFound = (pszIconPath[0] != 0);
  885.         }
  886.     }
  887.     if (!fFound)
  888.     {
  889.         _GetField(_pszIconLocation, pszIconPath, cchIconPath);
  890.     }
  891.     *piIcon = _sld.iIcon;
  892.     return S_OK;
  893. }
  894. // IShellLinkA::GetIconLocation
  895. STDMETHODIMP CShellLink::GetIconLocation(LPSTR pszPath, int cchMaxPath, int *piIcon)
  896. {
  897.     WCHAR szPath[MAX_PATH];
  898.     HRESULT hr = GetIconLocation(szPath, ARRAYSIZE(szPath), piIcon);
  899.     if (SUCCEEDED(hr))
  900.         SHUnicodeToAnsi(szPath, pszPath, cchMaxPath);
  901.     return hr;
  902. }
  903. // IShellLinkW::SetIconLocation
  904. // NOTE: 
  905. //      pszIconPath may be NULL
  906. STDMETHODIMP CShellLink::SetIconLocation(LPCWSTR pszIconPath, int iIcon)
  907. {
  908.     TCHAR szIconPath[MAX_PATH];
  909.     if (pszIconPath)
  910.         SHUnicodeToTChar(pszIconPath, szIconPath, ARRAYSIZE(szIconPath));
  911.     if (pszIconPath)
  912.     {
  913.         HANDLE  hToken;
  914.         TCHAR   szIconPathEnc[MAX_PATH];
  915.         if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_IMPERSONATE, TRUE, &hToken) == FALSE)
  916.         {
  917.             hToken = NULL;
  918.         }
  919.         if (PathUnExpandEnvStringsForUser(hToken, szIconPath, szIconPathEnc, ARRAYSIZE(szIconPathEnc)) != 0)
  920.         {
  921.             EXP_SZ_LINK expLink;
  922.             // mark that link has expandable strings, and add them
  923.             _sld.dwFlags |= SLDF_HAS_EXP_ICON_SZ; // should this be unique for icons?
  924.             LPEXP_SZ_LINK lpNew = (LPEXP_SZ_LINK)SHFindDataBlock(_pExtraData, EXP_SZ_ICON_SIG);
  925.             if (!lpNew) 
  926.             {
  927.                 lpNew = &expLink;
  928.                 expLink.cbSize = 0;
  929.                 expLink.dwSignature = EXP_SZ_ICON_SIG;
  930.             }
  931.             // store both A and W version (for no good reason!)
  932.             SHTCharToAnsi(szIconPathEnc, lpNew->szTarget, ARRAYSIZE(lpNew->szTarget));
  933.             SHTCharToUnicode(szIconPathEnc, lpNew->swzTarget, ARRAYSIZE(lpNew->swzTarget));
  934.             // See if this is a new entry that we need to add
  935.             if (lpNew->cbSize == 0)
  936.             {
  937.                 lpNew->cbSize = SIZEOF(*lpNew);
  938.                 _AddExtraDataSection((DATABLOCK_HEADER *)lpNew);
  939.             }
  940.         }
  941.         else 
  942.         {
  943.             _sld.dwFlags &= ~SLDF_HAS_EXP_ICON_SZ;
  944.             _RemoveExtraDataSection(EXP_SZ_ICON_SIG);
  945.         }
  946.         if (hToken != NULL)
  947.         {
  948.             CloseHandle(hToken);
  949.         }
  950.     }
  951.     _SetField(&_pszIconLocation, pszIconPath);
  952.     if (_sld.iIcon != iIcon)
  953.     {
  954.         _sld.iIcon = iIcon;
  955.         _bDirty = TRUE;
  956.     }
  957.     if ((_sld.dwFlags & SLDF_HAS_DARWINID) && pszIconPath)
  958.     {
  959.         // NOTE: The comment below is for darwin as it shipped in win98/IE4.01,
  960.         // and is fixed in the > NT5 versions of the shell's darwin implementation.
  961.         //
  962.         // for darwin enalbed links, we make the path point to the
  963.         // icon location (which is must be of the type (ie same ext) as the real
  964.         // destination. So, if I want a darwin link to readme.txt, the shell
  965.         // needs the icon to be icon1.txt, which is lame!!. This ensures
  966.         // that the context menu will be correct and allows us to return
  967.         // from CShellLink::GetPath & CShellLink::GetIDList without faulting the 
  968.         // application in because we lie to people and tell them that we 
  969.         // really point to our icon, which is the same type as the real target,
  970.         // thus making our context menu be correct.
  971.         SetPIDLPath(NULL, szIconPath, NULL);
  972.     }
  973.     return S_OK;
  974. }
  975. // IShellLinkA::SetIconLocation
  976. STDMETHODIMP CShellLink::SetIconLocation(LPCSTR pszPath, int iIcon)
  977. {
  978.     WCHAR szPath[MAX_PATH];
  979.     LPWSTR pszPathW;
  980.     if (pszPath)
  981.     {
  982.         SHAnsiToUnicode(pszPath, szPath, ARRAYSIZE(szPath));
  983.         pszPathW = szPath;
  984.     }
  985.     else
  986.         pszPathW = NULL;
  987.     return SetIconLocation(pszPathW, iIcon);
  988. }
  989. // set the relative path, this is used before a link is saved so we know what
  990. // we should use to store the link relative to as well as before the link is resolved
  991. // so we know the new path to use with the saved relative path.
  992. //
  993. // in:
  994. //      pszPathRel      path to make link target relative to, must be a path to
  995. //                      a file, not a directory.
  996. //
  997. //      dwReserved      must be 0
  998. //
  999. // returns:
  1000. //      S_OK            relative path is set
  1001. //
  1002. STDMETHODIMP CShellLink::SetRelativePath(LPCWSTR pszPathRel, DWORD dwRes)
  1003. {
  1004.     if (dwRes != 0)
  1005.         return E_INVALIDARG;
  1006.     return _SetField(&_pszRelSource, pszPathRel);
  1007. }
  1008. STDMETHODIMP CShellLink::SetRelativePath(LPCSTR pszPathRel, DWORD dwRes)
  1009. {
  1010.     if (dwRes != 0)
  1011.         return E_INVALIDARG;
  1012.     return _SetField(&_pszRelSource, pszPathRel);
  1013. }
  1014. //
  1015. //  If SLR_UPDATE isn't set, check IPersistFile::IsDirty after
  1016. //  calling this to see if the link info has changed.
  1017. //
  1018. STDMETHODIMP CShellLink::Resolve(HWND hwnd, DWORD fFlags)
  1019. {
  1020.     return ResolveLink(hwnd, fFlags, 0);
  1021. }
  1022. //    converts version in text format (a,b,c,d) into two dwords (a,b), (c,d)
  1023. //    The printed version number is of format a.b.d (but, we don't care)
  1024. //    NOTE: Stolen from ineturlmondownloadhelpers.cxx
  1025. HRESULT GetVersionFromString(TCHAR *szBuf, DWORD *pdwFileVersionMS, DWORD *pdwFileVersionLS)
  1026. {
  1027.     const TCHAR *pch = szBuf;
  1028.     TCHAR ch;
  1029.     USHORT n = 0;
  1030.     USHORT a = 0;
  1031.     USHORT b = 0;
  1032.     USHORT c = 0;
  1033.     USHORT d = 0;
  1034.     enum HAVE { HAVE_NONE, HAVE_A, HAVE_B, HAVE_C, HAVE_D } have = HAVE_NONE;
  1035.     *pdwFileVersionMS = 0;
  1036.     *pdwFileVersionLS = 0;
  1037.     if (!pch)            // default to zero if none provided
  1038.         return S_OK;
  1039.     if (lstrcmp(pch, TEXT("-1,-1,-1,-1")) == 0) {
  1040.         *pdwFileVersionMS = 0xffffffff;
  1041.         *pdwFileVersionLS = 0xffffffff;
  1042.         return S_OK;
  1043.     }
  1044.     for (ch = *pch++;;ch = *pch++) {
  1045.         if ((ch == ',') || (ch == '')) {
  1046.             switch (have) {
  1047.             case HAVE_NONE:
  1048.                 a = n;
  1049.                 have = HAVE_A;
  1050.                 break;
  1051.             case HAVE_A:
  1052.                 b = n;
  1053.                 have = HAVE_B;
  1054.                 break;
  1055.             case HAVE_B:
  1056.                 c = n;
  1057.                 have = HAVE_C;
  1058.                 break;
  1059.             case HAVE_C:
  1060.                 d = n;
  1061.                 have = HAVE_D;
  1062.                 break;
  1063.             case HAVE_D:
  1064.                 return E_INVALIDARG; // invalid arg
  1065.             }
  1066.             if (ch == '') {
  1067.                 // all done convert a,b,c,d into two dwords of version
  1068.                 *pdwFileVersionMS = ((a << 16)|b);
  1069.                 *pdwFileVersionLS = ((c << 16)|d);
  1070.                 return S_OK;
  1071.             }
  1072.             n = 0; // reset
  1073.         } else if ( (ch < '0') || (ch > '9'))
  1074.             return E_INVALIDARG;    // invalid arg
  1075.         else
  1076.             n = n*10 + (ch - '0');
  1077.     } /* end forever */
  1078.     // NEVERREACHED
  1079. }
  1080. //  Purpose:    A ResolveLink-time check to see if the link has
  1081. //              Logo3 application channel
  1082. //
  1083. //  Inputs:     [LPCTSTR] - pszLogo3ID - the id/keyname for
  1084. //                          our Logo3 software.
  1085. //
  1086. //  Outputs:    [BOOL]
  1087. //                  -   TRUE if our peek at the registry
  1088. //                      indicates we have an ad to show
  1089. //                  -   FALSE indicates no new version
  1090. //                      to advertise.
  1091. //
  1092. //  Algorithm:  Check the software update registry info for the
  1093. //              ID embedded in the link. This is a sleazy hack
  1094. //              to avoid loading shdocvw and urlmon, which are
  1095. //              the normal code path for this check.
  1096. //              NOTE: The version checking logic is stolen from
  1097. //              shellshdocvwsftupmb.cpp
  1098. HRESULT GetLogo3SoftwareUpdateInfo( LPCTSTR pszLogo3ID, LPSOFTDISTINFO psdi )
  1099. {    
  1100.     HRESULT     hr = S_OK;
  1101.     HKEY        hkeyDistInfo = 0;
  1102.     HKEY        hkeyAvail = 0;
  1103.     HKEY        hkeyAdvertisedVersion = 0;
  1104.     DWORD       lResult = 0;
  1105.     DWORD       dwSize = 0;
  1106.     DWORD       dwType;
  1107.     TCHAR       szBuffer[MAX_PATH];
  1108.     TCHAR       szVersionBuf[MAX_PATH];
  1109.     DWORD       dwLen = 0;
  1110.     DWORD       dwCurAdvMS = 0;
  1111.     DWORD       dwCurAdvLS = 0;
  1112.     wsprintf(szBuffer,
  1113.              TEXT("Software\Microsoft\Windows\CurrentVersion\Uninstall\%s"),
  1114.              pszLogo3ID);
  1115.     if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szBuffer, 0, KEY_READ,
  1116.                      &hkeyDistInfo) != ERROR_SUCCESS) {
  1117.         hr = E_FAIL;
  1118.         goto Exit;
  1119.     }
  1120.     
  1121.     if (RegOpenKeyEx(hkeyDistInfo, TEXT("AvailableVersion"), 0, KEY_READ,
  1122.                      &hkeyAvail) != ERROR_SUCCESS) {
  1123.         hr = E_FAIL;
  1124.         goto Exit;
  1125.     }
  1126.     dwSize = sizeof(DWORD);
  1127.     if (SHQueryValueEx(hkeyAvail, TEXT("Precache"), 0, &dwType,
  1128.                         (unsigned char *)&lResult, &dwSize) == ERROR_SUCCESS) {
  1129.         // Precached value was the code download HR
  1130.         if ( lResult == S_OK )
  1131.             psdi->dwFlags = SOFTDIST_FLAG_USAGE_PRECACHE;
  1132.     }
  1133.     dwSize = MAX_PATH;
  1134.     if (SHQueryValueEx(hkeyAvail, TEXT("AdvertisedVersion"), NULL, &dwType, 
  1135.                         (unsigned char *)szVersionBuf, &dwSize) == ERROR_SUCCESS)
  1136.     {
  1137.         GetVersionFromString(szVersionBuf, &psdi->dwAdvertisedVersionMS, &psdi->dwAdvertisedVersionLS);
  1138.        // Get the AdState, if any
  1139.         dwSize = sizeof(DWORD);
  1140.         SHQueryValueEx(hkeyAvail, TEXT("AdState"), NULL, NULL, (LPBYTE)&psdi->dwAdState, &dwSize);
  1141.     }
  1142.  
  1143.     dwSize = MAX_PATH;
  1144.     if (SHQueryValueEx(hkeyAvail, NULL, NULL, &dwType,
  1145.                         (unsigned char *)szVersionBuf, &dwSize) != ERROR_SUCCESS) {
  1146.         hr = S_FALSE;
  1147.         goto Exit;
  1148.     }
  1149.     if ( FAILED(GetVersionFromString(szVersionBuf, &psdi->dwUpdateVersionMS, &psdi->dwUpdateVersionLS))){
  1150.         hr = S_FALSE;
  1151.         goto Exit;
  1152.     }
  1153.  
  1154.     dwLen = sizeof(DWORD);
  1155.     if (SHQueryValueEx(hkeyDistInfo, TEXT("VersionMajor"), 0, &dwType,
  1156.                         (LPBYTE)&psdi->dwInstalledVersionMS, &dwLen) != ERROR_SUCCESS) {
  1157.         hr = S_FALSE;
  1158.         goto Exit;
  1159.     }
  1160.     dwLen = sizeof(DWORD);
  1161.     if (SHQueryValueEx(hkeyDistInfo, TEXT("VersionMinor"), 0, &dwType,
  1162.                         (LPBYTE)&psdi->dwInstalledVersionLS, &dwLen) != ERROR_SUCCESS) {
  1163.         hr = S_FALSE;
  1164.         goto Exit;
  1165.     }
  1166.         // BUGBUG: SeanF needs to review new logic
  1167.     if (psdi->dwUpdateVersionMS > psdi->dwInstalledVersionMS ||
  1168.         (psdi->dwUpdateVersionMS == psdi->dwInstalledVersionMS &&
  1169.          psdi->dwUpdateVersionLS > psdi->dwInstalledVersionLS)) {
  1170.         hr = S_OK;
  1171.     } else {
  1172.         hr = S_FALSE;
  1173.     }
  1174. Exit:
  1175.     if (hkeyAdvertisedVersion) {
  1176.         RegCloseKey(hkeyAdvertisedVersion);
  1177.     }
  1178.     if (hkeyAvail) {
  1179.         RegCloseKey(hkeyAvail);
  1180.     }
  1181.     if (hkeyDistInfo) {
  1182.         RegCloseKey(hkeyDistInfo);
  1183.     }
  1184.     return hr;
  1185. }
  1186. //  Purpose:    A ResolveLink-time check to see if the link has
  1187. //              Logo3 application channel
  1188. //
  1189. //  Inputs:     [LPCTSTR] - pszLogo3ID - the id/keyname for
  1190. //                          our Logo3 software.
  1191. //
  1192. //  Outputs:    [BOOL]
  1193. //                  -   TRUE if our peek at the registry
  1194. //                      indicates we have an ad to show
  1195. //                  -   FALSE indicates no new version
  1196. //                      to advertise.
  1197. //
  1198. //  Algorithm:  Check the software update registry info for the
  1199. //              ID embedded in the link. This is a sleazy hack
  1200. //              to avoid loading shdocvw and urlmon, which are
  1201. //              the normal code path for this check.
  1202. //              The version checking logic is stolen from
  1203. BOOL FLogo3RegPeek( LPCTSTR pszLogo3ID )
  1204. {
  1205.     BOOL            bHaveAd = FALSE;
  1206.     HRESULT         hr;
  1207.     SOFTDISTINFO    sdi = { 0 };
  1208.     DWORD           dwAdStateNew = SOFTDIST_ADSTATE_NONE;
  1209.     hr = GetLogo3SoftwareUpdateInfo( pszLogo3ID, &sdi );
  1210.  
  1211.     // we need an HREF to work properly. The title and abstract are negotiable.
  1212.     if ( SUCCEEDED(hr) )
  1213.     {
  1214.         // see if this is an update the user already knows about.
  1215.         // If it is, then skip the dialog.
  1216.         if (  (sdi.dwUpdateVersionMS >= sdi.dwInstalledVersionMS ||
  1217.                 (sdi.dwUpdateVersionMS == sdi.dwInstalledVersionMS &&
  1218.                  sdi.dwUpdateVersionLS >= sdi.dwInstalledVersionLS))    && 
  1219.               (sdi.dwUpdateVersionMS >= sdi.dwAdvertisedVersionMS ||
  1220.                 (sdi.dwUpdateVersionMS == sdi.dwAdvertisedVersionMS &&
  1221.                  sdi.dwUpdateVersionLS >= sdi.dwAdvertisedVersionLS)) )
  1222.         { 
  1223.             if ( hr == S_OK ) // new version
  1224.             {
  1225.                 // we have a pending update, either on the net, or downloaded
  1226.                 if ( sdi.dwFlags & SOFTDIST_FLAG_USAGE_PRECACHE )
  1227.                 {
  1228.                     dwAdStateNew = SOFTDIST_ADSTATE_DOWNLOADED;
  1229.                 }
  1230.                 else
  1231.                 {
  1232.                     dwAdStateNew = SOFTDIST_ADSTATE_AVAILABLE;
  1233.                 }
  1234.             }
  1235.             else if ( sdi.dwUpdateVersionMS == sdi.dwInstalledVersionMS &&
  1236.                       sdi.dwUpdateVersionLS == sdi.dwInstalledVersionLS )
  1237.             {
  1238.                 // if installed version matches advertised, then we autoinstalled already
  1239.                 // BUGBUG: If the user gets gets channel notification, then runs out
  1240.                 // to the store and buys the new version, then installs it, we'll
  1241.                 // mistake this for an auto-install.
  1242.                 dwAdStateNew = SOFTDIST_ADSTATE_INSTALLED;
  1243.             }
  1244.             // only show the dialog if we've haven't been in this ad state before for
  1245.             // this update version
  1246.             if ( dwAdStateNew > sdi.dwAdState )
  1247.             {
  1248.                 bHaveAd = TRUE;
  1249.             }
  1250.         } // if update is a newer version than advertised
  1251.     }
  1252.     return bHaveAd;
  1253. }
  1254. //  Purpose:    A ResolveLink-time check to see if the link has
  1255. //              Logo3 application channel
  1256. //
  1257. //  Inputs:     [HWND] hwnd
  1258. //                  -   The parent window (which could be the desktop).
  1259. //              [DWORD] fFlags
  1260. //                  -   Flags from the SLR_FLAGS enumeration.
  1261. //
  1262. //  Outputs:    [HRESULT]
  1263. //                  -   S_OK    The user wants to pursue the
  1264. //                              software update.
  1265. //                      S_FALSE No software update, or the user
  1266. //                              doesn't want it now.
  1267. //
  1268. //  Algorithm:  Check the software update registry info for the
  1269. //              ID embedded in the link. If there's a new version
  1270. //              advertised, prompt the user with shdocvw's message
  1271. //              box. If the mb says update, tell the caller we
  1272. //              don't want the link target, as we're headed to the
  1273. //              link update page.
  1274. HRESULT CShellLink::CheckLogo3Link(HWND hwnd,DWORD fFlags )
  1275. {
  1276.     HRESULT hr = S_FALSE; // default to no update.
  1277.     LPEXP_DARWIN_LINK pdl = (LPEXP_DARWIN_LINK)SHFindDataBlock(_pExtraData, EXP_LOGO3_ID_SIG);
  1278.     if (pdl)
  1279.     {
  1280.         TCHAR szLogo3ID[MAX_PATH];
  1281.         WCHAR szwLogo3ID[MAX_PATH];
  1282.         int   cchBlessData;
  1283.         TCHAR *pch;
  1284.         WCHAR *pwch;
  1285. #ifdef UNICODE
  1286.         pch = pdl->szwDarwinID;
  1287. #else
  1288.         pch = pdl->szDarwinID;
  1289. #endif
  1290.         // Ideally, we support multiple, semi-colon delmited IDs, for now
  1291.         // just grab the first one.
  1292.         for ( pwch = pdl->szwDarwinID, cchBlessData = 0;
  1293.               *pch != ';' && *pch != '' && cchBlessData < MAX_PATH;
  1294.               pch++, pwch++, cchBlessData++ )
  1295.         {
  1296.             szLogo3ID[cchBlessData] = *pch;
  1297.             szwLogo3ID[cchBlessData] = *pwch;
  1298.         }
  1299.         // and terminate
  1300.         szLogo3ID[cchBlessData] = '';
  1301.         szwLogo3ID[cchBlessData] = L'';
  1302.         
  1303.         // Before well haul in shdocvw, we'll sneak a peak at our Logo3 reg goo 
  1304.         if (!(fFlags & SLR_NO_UI) && FLogo3RegPeek(szLogo3ID))
  1305.         {
  1306.             // stuff stolen from shdocvwutil.cpp's CheckSoftwareUpdateUI
  1307.             BOOL fLaunchUpdate = FALSE;
  1308.             int nRes;
  1309.             SOFTDISTINFO sdi = { 0 };
  1310.             sdi.cbSize = sizeof(SOFTDISTINFO);
  1311.             nRes = SoftwareUpdateMessageBox( hwnd, szwLogo3ID, 0, &sdi );
  1312.             if (nRes != IDABORT)
  1313.             {
  1314.                 if (nRes == IDYES)
  1315.                 {
  1316.                     // BUGBUG: This differ's from Shdocvw in that we don't
  1317.                     // have the cool internal navigation stuff to play with.
  1318.                     // Originally, this was done with ShellExecEx. This failed
  1319.                     // because the http hook wasn't 100% reliable on Win95.
  1320.                     //ShellExecuteW(NULL, NULL, sdi.szHREF, NULL, NULL, 0);
  1321.                     hr = HlinkNavigateString(NULL, sdi.szHREF);
  1322.                 } // if user wants update
  1323.                 if (sdi.szTitle != NULL)
  1324.                     SHFree( sdi.szTitle );
  1325.                 if (sdi.szAbstract != NULL)
  1326.                     SHFree( sdi.szAbstract );
  1327.                 if (sdi.szHREF != NULL)
  1328.                     SHFree( sdi.szHREF );
  1329.     
  1330.                 fLaunchUpdate = nRes == IDYES && SUCCEEDED(hr);
  1331.             } // if no message box abort ( error )
  1332.             if ( fLaunchUpdate )
  1333.                 hr = S_OK;
  1334.         } // if showing UI is okay
  1335.     } // if Logo3 ID retreived.
  1336.     return hr;
  1337. }
  1338. void CShellLink::UpdateDirPIDL(LPTSTR pszPath)
  1339. {
  1340.     TCHAR  szPathSrc[MAX_PATH];
  1341.     LPTSTR psz;
  1342.     ASSERT(_sld.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);       
  1343.     if (pszPath)
  1344.     {
  1345.         psz = pszPath;
  1346.     }
  1347.     else
  1348.     {
  1349.         GetPath(szPathSrc, ARRAYSIZE(szPathSrc), NULL, 0);
  1350.         psz = szPathSrc;
  1351.     }
  1352.     
  1353.     if (_pidl)
  1354.     {
  1355.         ILFree(_pidl);
  1356.         _pidl = NULL;
  1357.     }
  1358.     SetPIDLPath(NULL, psz, NULL);
  1359. }
  1360. BOOL PathExistsOrMediaInserted(HWND hwnd, LPCTSTR pszPath)
  1361. {
  1362.     if (PathFileExistsAndAttributes(pszPath, NULL))
  1363.         return TRUE;
  1364.     // Give the user a chance to insert the disk.
  1365.     if (hwnd && SUCCEEDED(SHPathPrepareForWrite(hwnd, NULL, pszPath, SHPPFW_IGNOREFILENAME)) && PathFileExistsAndAttributes(pszPath, NULL))
  1366.         return TRUE;
  1367.     return FALSE;
  1368. }
  1369. //
  1370. // updates then resolves LinkInfo associated with a CShellLink instance
  1371. // if the resolve results in a new path updates the pidl to the new path
  1372. //
  1373. // in:
  1374. //      hwnd    to post resolve UI on (if dwFlags indicates UI)
  1375. //      dwFlags to be passed to ResolveLinkInfo
  1376. //
  1377. // returns:
  1378. //      FALSE   we failed the update, either UI cancel or memory failure
  1379. //      TRUE    we have a valid pli and pidl read to be used OR
  1380. //              we should search for this path using the link search code
  1381. //          2   the drive that the shortcut refers to is gone, but we should
  1382. //              give the tracker a try(in case the domain server knows where it went)
  1383. BOOL CShellLink::UpdateAndResolveLinkInfo(HWND hwnd, DWORD dwFlags)
  1384. {
  1385.     TCHAR szResolvedPath[MAX_PATH];
  1386.     if (SHRestricted(REST_LINKRESOLVEIGNORELINKINFO))
  1387.     {
  1388.         if (SHGetPathFromIDList(_pidl, szResolvedPath))
  1389.         {
  1390.             if (PathFileExistsAndAttributes(szResolvedPath, NULL))
  1391.                 return TRUE;
  1392.             else if (!PathIsUNC(szResolvedPath) && IsDisconnectedNetDrive(DRIVEID(szResolvedPath)))
  1393.             {
  1394.                 TCHAR szDrive[4];
  1395.                 szDrive[0] = szResolvedPath[0];
  1396.                 szDrive[1] = TEXT(':');
  1397.                 szDrive[2] = 0;
  1398.                 WNetRestoreConnection(hwnd, szDrive);
  1399.             }
  1400.         }
  1401.         return TRUE;
  1402.     }
  1403.     if (_pli)
  1404.     {
  1405.         DWORD dwOutFlags;
  1406.         BOOL bResolved = FALSE;
  1407.         ASSERT(! (dwFlags & RLI_IFL_UPDATE));
  1408.         if (ResolveLinkInfo(_pli, szResolvedPath, dwFlags, hwnd,
  1409.                                  &dwOutFlags, NULL))
  1410.         {
  1411.             ASSERT(! (dwOutFlags & RLI_OFL_UPDATED));
  1412.             bResolved = TRUE;
  1413.             // we have to hit the disk again to set the new path
  1414.             // DebugMsg(DM_TRACE, "Resolved LinkInfo -> %s %4x", szResolvedPath, dwOutFlags);
  1415.             if (PathFileExistsAndAttributes(szResolvedPath, NULL))
  1416.                 return SetPIDLPath(NULL, szResolvedPath, NULL);
  1417.             else
  1418.                 DebugMsg(DM_TRACE, TEXT("Link referent %s not found."), szResolvedPath);
  1419.         }
  1420.         else if (GetLastError() == ERROR_CANCELLED)
  1421.         {
  1422.             DebugMsg(DM_TRACE, TEXT("ResolveLinkInfo() failed, user canceled."));
  1423.             return FALSE;
  1424.         }
  1425.         DebugMsg(DM_TRACE, TEXT("ResolveLinkInfo() failed."));
  1426.         // Resolve failed, or resolve succeeded but the file was not found.
  1427.         // Try PIDL path.
  1428.         SHGetPathFromIDList(_pidl, szResolvedPath);
  1429.         ASSERT(szResolvedPath[0]);
  1430.         if (PathExistsOrMediaInserted(hwnd, szResolvedPath) || _ResolveRelative(szResolvedPath))
  1431.         {
  1432.             // this can happen when linkinfo can't find the original drive
  1433.             // serial # on the device. could be that the drive was
  1434.             // dblspaced or some disk utility wacked on it.
  1435.             DebugMsg(DM_TRACE, TEXT("Link referent %s found on different volume but same path.  LinkInfo will be updated."), szResolvedPath);
  1436.             /* Update LinkInfo to refer to PIDL path. */
  1437.             GetLinkInfo();
  1438.             return TRUE;
  1439.         }
  1440.         if (bResolved)
  1441.             return TRUE;        // please search for this
  1442. #ifdef WINNT
  1443.         //Give ptracker a chance before giving up
  1444.         return 2;
  1445. #else
  1446.         // BUGBUG: UI goes here
  1447.         // 1) if it is a floppy ask to insert
  1448.         // 2) if on unshared media tell them that
  1449.         // 3) net problems, tell 'em
  1450.         if (dwFlags & RLI_IFL_ALLOW_UI)
  1451.         {
  1452.             LPCTSTR pszName = _pszCurFile ? (LPCTSTR)PathFindFileName(_pszCurFile) : c_szNULL;
  1453.             ShellMessageBox(HINST_THISDLL, hwnd,
  1454.                             MAKEINTRESOURCE(IDS_LINKUNAVAILABLE),
  1455.                             MAKEINTRESOURCE(IDS_LINKERROR),
  1456.                             MB_OK | MB_ICONEXCLAMATION, pszName);
  1457.         }
  1458.         return FALSE;
  1459. #endif
  1460.     }
  1461.     else
  1462.         return TRUE;            // search for this
  1463. }
  1464. // returns:
  1465. //      S_OK    this resolution was taken care of
  1466. //      S_FALSE or FAILED(hr)   - did not happen, plese do regular stuff
  1467. HRESULT CShellLink::_SelfResolve(HWND hwnd, DWORD fFlags)
  1468. {
  1469.     HRESULT hr = S_FALSE;   // no, we did not handle it
  1470.     IShellFolder* psf;
  1471.     LPCITEMIDLIST pidlChild;
  1472.     if (_pidl && SUCCEEDED(SHBindToIDListParent(_pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlChild)))
  1473.     {
  1474.         IResolveShellLink* pResLink = NULL;
  1475.         // 2 ways to get the link resolve object
  1476.         // 1. ask the folder for the resolver for the item
  1477.         if (FAILED(psf->GetUIObjectOf(NULL, 1, &pidlChild, IID_IResolveShellLink, NULL, (void **)&pResLink)))
  1478.         {
  1479.             // 2. bind to the object directly and ask it (CreateViewObject)
  1480.             IShellFolder *psfItem;
  1481.             if (SUCCEEDED(psf->BindToObject(pidlChild, NULL, IID_PPV_ARG(IShellFolder, &psfItem))))
  1482.             {
  1483.                 psfItem->CreateViewObject(NULL, IID_PPV_ARG(IResolveShellLink, &pResLink));
  1484.                 psfItem->Release();
  1485.             }
  1486.         }
  1487.         if (pResLink)
  1488.         {
  1489.             hr = pResLink->ResolveShellLink((IUnknown*)(IShellLink*) this, hwnd, fFlags);
  1490.             pResLink->Release();
  1491.         }
  1492.         psf->Release();
  1493.     }
  1494.     return hr;
  1495. }
  1496. //
  1497. //  Purpose:    Provide the implementation for
  1498. //              IShellLink::Resolve and IShellLinkTracker::Resolve
  1499. //
  1500. //  Inputs:     [HWND] hwnd
  1501. //                  -   The parent window (which could be the desktop).
  1502. //              [DWORD] fFlags
  1503. //                  -   Flags from the SLR_FLAGS enumeration.
  1504. //              [DWORD] dwTracker
  1505. //                  -   Restrict CTracker::Resolve from the
  1506. //                      TrkMendRestrictions enumeration
  1507. //
  1508. //  Outputs:    [HRESULT]
  1509. //                  -   S_OK    resolution was successful
  1510. //                      S_FALSE user canceled
  1511. //
  1512. //  Algorithm:  Look for the link target and update the link path and IDList.
  1513. //              Check IPersistFile::IsDirty after calling this to see if the
  1514. //              link info has changed as a result.
  1515. //
  1516. HRESULT CShellLink::ResolveLink(HWND hwnd, DWORD fFlags, DWORD dwTracker)
  1517. {
  1518.     HRESULT hres = S_OK;
  1519.     TCHAR szPath[MAX_PATH];
  1520.     DWORD dwResolveFlags;
  1521. #ifdef WINNT
  1522.     DWORD fifFlags  = 0;
  1523. #endif
  1524.     // Check to see if the PIDL knows how to resolve itself (delegate to the folder)
  1525.     if (_SelfResolve(hwnd, fFlags) == S_OK)
  1526.         return S_OK;
  1527.         
  1528.     // Check to see if this link is a Logo3 link
  1529.     if (_sld.dwFlags & SLDF_HAS_LOGO3ID &&
  1530.         !SHRestricted(REST_NOLOGO3CHANNELNOTIFY))
  1531.     {
  1532.         if ( CheckLogo3Link(hwnd, fFlags) == S_OK )
  1533.             return S_FALSE;
  1534.     }
  1535.     // check to see if this is a Darwin link
  1536.     if (_sld.dwFlags & SLDF_HAS_DARWINID)
  1537.     {
  1538.         if (!(fFlags & SLR_INVOKE_MSI))
  1539.         {
  1540.             // we only envoke darwin if they are passing the correct SLR_INVOKE_MSI
  1541.             // flag. This prevents bonehead apps from going and calling resolve and
  1542.             // faulting in a bunch of darwin apps.
  1543.             return S_OK;
  1544.         }
  1545.         LPEXP_DARWIN_LINK pdl = (LPEXP_DARWIN_LINK)SHFindDataBlock(_pExtraData, EXP_DARWIN_ID_SIG);
  1546.         if (pdl && IsDarwinEnabled())
  1547.         {
  1548.             // Special case darwin links
  1549.             TCHAR szDarwinCommand[MAX_PATH];
  1550.             TCHAR szDarwinID[MAX_PATH];
  1551.             SHUnicodeToTChar(pdl->szwDarwinID, szDarwinID, ARRAYSIZE(szDarwinID));
  1552.             DebugMsg(DM_TRACE, TEXT("CShellLink::ResolveLink() %s (Darwin ID)"), szDarwinID);
  1553.             HRESULT hres = ParseDarwinID(szDarwinID, szDarwinCommand , SIZECHARS(szDarwinCommand));
  1554.             if (FAILED(hres) || 
  1555.                 HRESULT_CODE(hres) == ERROR_SUCCESS_REBOOT_REQUIRED || 
  1556.                 HRESULT_CODE(hres) == ERROR_SUCCESS_REBOOT_INITIATED)
  1557.             {
  1558.                 switch (HRESULT_CODE(hres))
  1559.                 {
  1560.                 case ERROR_INSTALL_USEREXIT:
  1561.                     break;  // User pressed cancel. They don't need UI.
  1562.                 case ERROR_SUCCESS_REBOOT_INITIATED:
  1563.                 case ERROR_SUCCESS_REBOOT_REQUIRED:
  1564.                     break;  // If we need to reboot, then don't launch...
  1565.                 default:
  1566.                     if (!(fFlags & SLR_NO_UI))
  1567.                     {
  1568.                         TCHAR szTemp[MAX_PATH];
  1569.                         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
  1570.                                     NULL, HRESULT_CODE(hres), 0, szTemp, ARRAYSIZE(szTemp), NULL);
  1571.                         ShellMessageBox(HINST_THISDLL,
  1572.                                     hwnd,
  1573.                                     szTemp,
  1574.                                     MAKEINTRESOURCE(IDS_LINKERROR),
  1575.                                     MB_OK | MB_ICONSTOP, NULL, NULL);
  1576.                     }
  1577.                     break;
  1578.                 }
  1579.                 return E_FAIL;
  1580.             }
  1581.             // We want to fire an event for the product code, not the path. Do this here since we've got the product code.
  1582.             TCHAR szProductCode[MAX_PATH];
  1583.             szProductCode[0] = TEXT('');
  1584.             MsiDecomposeDescriptor(szDarwinID, szProductCode, NULL, NULL, NULL);
  1585.             if (szProductCode[0] != TEXT(''))
  1586.             {
  1587.                 UEMFireEvent(&UEMIID_SHELL, UEME_RUNPATH, UEMF_XEVENT, -1, (LPARAM)szProductCode);
  1588.             }
  1589.             SetPIDLPath(NULL, szDarwinCommand, NULL);
  1590.             return S_OK;
  1591.         }
  1592.     }
  1593.     // check to see whether this link has expandable environment strings
  1594.     if (_sld.dwFlags & SLDF_HAS_EXP_SZ)
  1595.     {
  1596.         // yep, so create a new pidl that points to expanded path
  1597.         if (_GetExpPath(szPath, SIZECHARS(szPath)))
  1598.         {
  1599.             // The best kind of pidl we can have is one created by ILCreateFromPath.
  1600.             // It's even better than any pidl we might have stored in the .lnk file.
  1601.             LPITEMIDLIST pidlTemp = ILCreateFromPath( szPath );
  1602.             if ( pidlTemp )
  1603.             {
  1604.                 if (_pidl)
  1605.                     ILFree(_pidl);
  1606.                 _pidl = pidlTemp;
  1607.                 // we are modifing _pidl.  We should never have a EXP_SPECIAL_FOLDER_SIG section
  1608.                 // when we modify the _pidl, otherwise the two will be out of ssync
  1609.                 ASSERT( NULL == SHFindDataBlock(_pExtraData, EXP_SPECIAL_FOLDER_SIG) );
  1610.             }
  1611.             else
  1612.             {
  1613.                 // The target file is no longer valid so we should dump the EXP_SZ section before
  1614.                 // we continue.  Note that we don't set bDirty here, that is only set later if
  1615.                 // we actually resolve this link to a new path or pidl.  The result is we'll only
  1616.                 // save this modification if a new target is found and accepted by the user.
  1617.                 _sld.dwFlags &= ~SLDF_HAS_EXP_SZ;
  1618.                 // The second best kind of pidl we can have is the pdil stored in the link file.
  1619.                 // This pidl is most likely equivalent to the simple pidl we would otherwise create
  1620.                 // below and we know that either pidl will be invalid.  We simply need a pidl that
  1621.                 // is of the correct class.  If no pidl is originally stored with the link then the
  1622.                 // shell creates and stores the pidl the first time the link is resolved.
  1623.                 if (!_pidl)
  1624.                 {
  1625.                     // If we don't have our second choice, we settle for our third choice which is to
  1626.                     // create a simple pidl.  The simple pidl is good enough to allow some link resolution
  1627.                     // to occur which is better than the silent failure we get otherwise.  We should
  1628.                     // still have Link Info and Find Data to use in resolving the pidl.
  1629.                     _pidl = SHSimpleIDListFromPath(szPath);
  1630.                     // we are modifing _pidl.  We should never have a EXP_SPECIAL_FOLDER_SIG section
  1631.                     // when we modify the _pidl, otherwise the two will be out of ssync
  1632.                     ASSERT( NULL == SHFindDataBlock(_pExtraData, EXP_SPECIAL_FOLDER_SIG) );
  1633.                 }
  1634.             }
  1635.         } 
  1636.         else 
  1637.         {
  1638.             DebugMsg(DM_TRACE, TEXT("ResolveLink called on EXP_SZ link without EXP_SZ section!"));
  1639.             _sld.dwFlags &= ~SLDF_HAS_EXP_SZ;
  1640.         }
  1641.     }
  1642.     if (_pidl == NULL)
  1643.         return E_FAIL;
  1644.     // ensure that this is a link to a file, if not validate it
  1645.     if (!SHGetPathFromIDList(_pidl, szPath))
  1646.     {
  1647.         // validate the non file system target first
  1648.         ULONG dwAttrib = SFGAO_VALIDATE;     // to check for existance
  1649.         if (FAILED(SHGetNameAndFlags(_pidl, SHGDN_NORMAL, szPath, ARRAYSIZE(szPath), &dwAttrib)))
  1650.         {
  1651.             if (!(fFlags & SLR_NO_UI))
  1652.             {
  1653.                 ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_CANTFINDORIGINAL), NULL,
  1654.                             MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND, szPath);
  1655.             }
  1656.             return E_FAIL;
  1657.         }
  1658.         return S_OK;
  1659.     }
  1660.     // At this point, szPath contains the path+filename to the target
  1661.     dwResolveFlags = (RLI_IFL_CONNECT | RLI_IFL_TEMPORARY);
  1662.     if (!PathIsRoot(szPath))
  1663.     {
  1664.         dwResolveFlags |= RLI_IFL_LOCAL_SEARCH;
  1665.         // if we are trying to avoid link info's we can bail if we 
  1666.         // the file already exists..
  1667.         // Is the link dirty?
  1668.         if (QueryAndSetFindData(szPath))
  1669.         {
  1670.             // No
  1671.             // Is this the 'real desktop' or 'desktop folder' case AND
  1672.             // does the caller want us to update the link?
  1673.             if ((_sld.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
  1674.                 (!ILIsEmpty(_pidl) && ILIsEmpty(_ILNext(_pidl))) &&
  1675.                 (!(fFlags & SLR_NOUPDATE)))
  1676.             {
  1677.                 // Yes, so update to disambiguate the two cases.
  1678.                 UpdateDirPIDL(szPath);
  1679.             }
  1680.             // Nothing else needs to be done because nothing else is out of date.
  1681.             return S_OK;
  1682.         }
  1683.     }
  1684.     if (!(fFlags & SLR_NOLINKINFO))
  1685.     {
  1686.         if (!(fFlags & SLR_NO_UI))
  1687.             dwResolveFlags |= RLI_IFL_ALLOW_UI;
  1688.         switch (UpdateAndResolveLinkInfo(hwnd, dwResolveFlags))
  1689.         {
  1690.          //
  1691.         //  No chance of finding the file - give up now.
  1692.         //
  1693.         case FALSE:
  1694.             DebugMsg(DM_TRACE, TEXT("UpdateAndResolveLinkInfo() failed"));
  1695.             return S_FALSE; // they canceled or this failed
  1696. #ifdef WINNT
  1697.         //
  1698.         //  We have no chance of finding the file, but let the tracker try.
  1699.         //
  1700.         case 2:
  1701.             fifFlags |= FIF_NODRIVE;        // don't try ourselves
  1702.             break;
  1703. #endif
  1704.         //
  1705.         //  Maybe we can find it after all.
  1706.         //
  1707.         case TRUE:
  1708.             break;
  1709.            
  1710.         }
  1711.         // UpdateAndResolveLinkInfo() may have changed the path
  1712.         SHGetPathFromIDList(_pidl, szPath);
  1713.     }
  1714.     if (PathIsRoot(szPath))
  1715.     {
  1716.         // DebugMsg(DM_TRACE, "ShellLink::Resolve() root path %s", szPath);
  1717.         // should be golden
  1718.     }
  1719.     else
  1720.     {
  1721.         // the above code did the retry logic (UI) if needed
  1722.         if (!QueryAndSetFindData(szPath))
  1723.         {
  1724.             int id;
  1725.             BOOL fFound = FALSE;
  1726.             // this thing is a link that is broken
  1727.             id = GetLastError();
  1728.             DebugMsg(DM_TRACE, TEXT("ShellLink::Resolve() file not found %s(%d)"), szPath, id);
  1729.             // Some error codes we will try to recover from by trying to get the network
  1730.             // to restore the connection.
  1731.             if (id == ERROR_BAD_NETPATH)
  1732.             {
  1733.                 TCHAR szDrive[4];
  1734.                 szDrive[0] = szPath[0];
  1735.                 szDrive[1] = TEXT(':');
  1736.                 szDrive[2] = 0;
  1737.                 if (WNetRestoreConnection(hwnd, szDrive) == WN_SUCCESS)
  1738.                 {
  1739.                     fFound = QueryAndSetFindData(szPath);
  1740.                     if( fFound && (_sld.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
  1741.                         UpdateDirPIDL(szPath );
  1742.                 }
  1743.             }
  1744.             if (!fFound)
  1745.             {
  1746.                 WIN32_FIND_DATA fd;
  1747.                 GetFindData(&fd);  // sld => fd
  1748.                 // Find the file
  1749.                 id = FindInFolder( hwnd, fFlags, szPath, &fd, _pszCurFile
  1750. #ifdef WINNT
  1751.                                    , _ptracker, dwTracker, fifFlags
  1752. #endif
  1753.                                    );
  1754.                 
  1755.                 switch (id) 
  1756.                 {
  1757.                     case IDOK:
  1758.                         DebugMsg(DM_TRACE, TEXT("ShellLink::Resolve() resolved to %s"), fd.cFileName);
  1759.                         // fd.cFileName comes back fully qualified
  1760. #ifdef WINNT
  1761.                         QueryAndSetFindData(fd.cFileName);
  1762.                         SetPIDLPath(NULL, fd.cFileName, NULL);
  1763. #else
  1764.                         SetPIDLPath(NULL, fd.cFileName, &fd);
  1765. #endif
  1766.                         ASSERT(_bDirty);       // should be dirty now
  1767.                         break;
  1768.                     
  1769.                     default:
  1770.                         ASSERT(!_bDirty);      // should not be dirty now
  1771.                         hres = S_FALSE;
  1772.                 }
  1773.             }
  1774.         }
  1775.     }
  1776.     //
  1777.     // if the link is dirty update it.
  1778.     //
  1779.     if (_bDirty && (fFlags & SLR_UPDATE))
  1780.         Save((LPCOLESTR)NULL, TRUE);
  1781.     return hres;
  1782. }   // ResolveLink
  1783. // This will just add a section to the end of the extra data -- it does
  1784. // not check to see if the section already exists, etc.
  1785. void CShellLink::_AddExtraDataSection(DATABLOCK_HEADER *peh)
  1786. {
  1787.     if (SHAddDataBlock(&_pExtraData, peh))
  1788.     {
  1789.         _bDirty = TRUE;
  1790.     }
  1791. }
  1792. // This will remove the extra data section with the given signature.
  1793. void CShellLink::_RemoveExtraDataSection(DWORD dwSig)
  1794. {
  1795.     if (SHRemoveDataBlock(&_pExtraData, dwSig))
  1796.     {
  1797.         _bDirty = TRUE;
  1798.     }
  1799. }
  1800. // currently this function is used for NT shell32 builds only
  1801. void * CShellLink::_ReadExtraDataSection(DWORD dwSig)
  1802. {
  1803.     DATABLOCK_HEADER *pdb;
  1804.     CopyDataBlock(dwSig, (void **)&pdb);
  1805.     return (void *)pdb;
  1806. }
  1807. // Darwin and Logo3 blessings share the same structure
  1808. HRESULT CShellLink::BlessLink(LPCTSTR *ppszPath, DWORD dwSignature)
  1809. {
  1810.     EXP_DARWIN_LINK expLink;
  1811.     LPEXP_DARWIN_LINK lpNew;
  1812.     TCHAR szBlessID[MAX_PATH];
  1813.     int   cchBlessData;
  1814.     TCHAR *pch;
  1815.     // Copy the blessing data and advance *ppszPath to the end of the data.
  1816.     for ( pch = szBlessID, cchBlessData = 0;
  1817.           **ppszPath != ':' && **ppszPath != '' && cchBlessData < MAX_PATH;
  1818.           pch++, (*ppszPath)++, cchBlessData++ )
  1819.         *pch = **ppszPath;
  1820.     // Terminate the blessing data
  1821.     *pch = 0;
  1822.     
  1823.     // Set the magic flag
  1824.     if (dwSignature == EXP_DARWIN_ID_SIG)
  1825.         _sld.dwFlags |= SLDF_HAS_DARWINID;
  1826.     else if (dwSignature == EXP_LOGO3_ID_SIG)
  1827.         _sld.dwFlags |= SLDF_HAS_LOGO3ID;
  1828.     else
  1829.     {
  1830.         TraceMsg(TF_WARNING, "BlessLink was passed a bad data block signature.");
  1831.         return E_INVALIDARG;
  1832.     }
  1833.     // locate the old block, if it's there
  1834.     lpNew = (LPEXP_DARWIN_LINK)SHFindDataBlock(_pExtraData, dwSignature);
  1835.     // if not, use our stack var
  1836.     if (!lpNew)
  1837.     {
  1838.         lpNew = &expLink;
  1839.         expLink.dbh.cbSize = 0;
  1840.         expLink.dbh.dwSignature = dwSignature;
  1841.     }
  1842.     SHTCharToAnsi(szBlessID, lpNew->szDarwinID, ARRAYSIZE(lpNew->szDarwinID));
  1843.     SHTCharToUnicode(szBlessID, lpNew->szwDarwinID, ARRAYSIZE(lpNew->szwDarwinID));
  1844.     // See if this is a new entry that we need to add
  1845.     if (lpNew->dbh.cbSize == 0)
  1846.     {
  1847.         lpNew->dbh.cbSize = SIZEOF(*lpNew);
  1848.         _AddExtraDataSection((DATABLOCK_HEADER *)lpNew);
  1849.     }
  1850.     return S_OK;
  1851. }
  1852. HRESULT CShellLink::CheckForLinkBlessing(LPCTSTR *ppszPathIn)
  1853. {
  1854.     HRESULT hr = S_FALSE; // default to no-error, no blessing
  1855.     while (SUCCEEDED(hr) && (*ppszPathIn)[0] == ':' && (*ppszPathIn)[1] == ':' )
  1856.     {
  1857.         // identify type of link blessing and perform
  1858.         if (StrCmpNI(*ppszPathIn, DARWINGUID_TAG, ARRAYSIZE(DARWINGUID_TAG) - 1) == 0)
  1859.         {
  1860.             *ppszPathIn = *ppszPathIn + ARRAYSIZE(DARWINGUID_TAG) - 1;
  1861.             hr = BlessLink(ppszPathIn, EXP_DARWIN_ID_SIG);
  1862.         }
  1863.         else if (StrCmpNI(*ppszPathIn, LOGO3GUID_TAG, ARRAYSIZE(LOGO3GUID_TAG) - 1) == 0)
  1864.         {
  1865.             HRESULT hrBless;
  1866.             *ppszPathIn = *ppszPathIn + ARRAYSIZE(LOGO3GUID_TAG) - 1;
  1867.             hrBless = BlessLink(ppszPathIn, EXP_LOGO3_ID_SIG);
  1868.             // if the blessing failed, report the error, otherwise keep the
  1869.             // default hr == S_FALSE or the result of the Darwin blessing.
  1870.             if ( FAILED(hrBless) )
  1871.                 hr = hrBless;
  1872.         }
  1873.         else
  1874.             break;
  1875.     }
  1876.         
  1877.     return hr;
  1878. }
  1879. STDMETHODIMP CShellLink::SetPath(LPCWSTR pszPathW)
  1880. {
  1881.     HRESULT hr;
  1882.     TCHAR szPath[MAX_PATH];
  1883.     LPCTSTR pszPath;
  1884.     // NOTE: all the other Set* functions allow NULL pointer to be passed in, but this
  1885.     // one does not because it would AV. 
  1886.     if (!pszPathW)
  1887.     {
  1888.         return E_INVALIDARG;
  1889.     }
  1890.     else if (_sld.dwFlags & SLDF_HAS_DARWINID)
  1891.     {
  1892.         return S_FALSE; // a darwin link already, then we dont allow the path to change
  1893.     }
  1894.     SHUnicodeToTChar(pszPathW, szPath, ARRAYSIZE(szPath));
  1895.     pszPath = szPath;
  1896. // TODO: Remove OLD_DARWIN stuff once we have transitioned Darwin to
  1897. //       the new link blessing syntax.
  1898. #define OLD_DARWIN
  1899. #ifdef OLD_DARWIN
  1900.     int iLength = lstrlen(pszPath);
  1901.     // we check to see if the path is enclosed in []'s. If so,
  1902.     // this means that we are creating a DARWIN link. We therefore
  1903.     // set SLDF_HAS_DARWINID and store the darwinID (the string
  1904.     // inside the []'s) in the extra data segment.
  1905.     if ((pszPath[0] == TEXT('[')) && (pszPath[iLength - 1] == TEXT(']')))
  1906.     {
  1907.         // we have a path that is enclosed in []'s,
  1908.         // so this must be a Darwin link.
  1909.         EXP_DARWIN_LINK expLink;
  1910.         TCHAR szDarwinID[MAX_PATH];
  1911.         // strip off the []'s
  1912.         lstrcpy(szDarwinID, &pszPath[1]);
  1913.         szDarwinID[iLength - 2] = 0;
  1914.         _sld.dwFlags |= SLDF_HAS_DARWINID;
  1915.         LPEXP_DARWIN_LINK pedl = (LPEXP_DARWIN_LINK)SHFindDataBlock(_pExtraData, EXP_DARWIN_ID_SIG);
  1916.         if (!pedl)
  1917.         {
  1918.             pedl = &expLink;
  1919.             expLink.dbh.cbSize = 0;
  1920.             expLink.dbh.dwSignature = EXP_DARWIN_ID_SIG;
  1921.         }
  1922.         SHTCharToAnsi(szDarwinID, pedl->szDarwinID, ARRAYSIZE(pedl->szDarwinID));
  1923.         SHTCharToUnicode(szDarwinID, pedl->szwDarwinID, ARRAYSIZE(pedl->szwDarwinID));
  1924.         // See if this is a new entry that we need to add
  1925.         if (pedl->dbh.cbSize == 0)
  1926.         {
  1927.             pedl->dbh.cbSize = SIZEOF(*pedl);
  1928.             _AddExtraDataSection((DATABLOCK_HEADER *)pedl);
  1929.         }
  1930.         // For darwin links, we ignore the path and pidl for now. We would
  1931.         // normally call SetPIDLPath and SetIDList but we skip these
  1932.         // steps for darwin links because all SetPIDLPath does is set the pidl
  1933.         // and all SetIDList does is set fd (the WIN32_FIND_DATA)
  1934.         // for the target, and we dont have a target since we are a darwin link.
  1935.         hr = S_OK;
  1936.     }
  1937.     else
  1938. #endif // OLD_DARWIN
  1939.     {
  1940.         // Check for ::<guid>:<data>: prefix, which signals us to bless the
  1941.         // the lnk with extra data. NOTE: we pass the &pszPath here so that this fn can 
  1942.         // advance the string pointer past the ::<guid>:<data>: sections and point to 
  1943.         // the path, if there is one.
  1944.         hr = CheckForLinkBlessing(&pszPath);
  1945.         if (S_OK != hr)
  1946.         {
  1947.             // Check to see if the target has any expandable environment strings
  1948.             // in it.  If so, set the appropriate information in the CShellLink
  1949.             // data.
  1950.             TCHAR szExpPath[MAX_PATH];
  1951.             SHExpandEnvironmentStrings(pszPath, szExpPath, ARRAYSIZE(szExpPath));
  1952.             if (lstrcmp(szExpPath, pszPath)) 
  1953.             {
  1954.                 EXP_SZ_LINK expLink;
  1955.                 // mark that link has expandable strings, and add them
  1956.                 _sld.dwFlags |= SLDF_HAS_EXP_SZ;
  1957.                 LPEXP_SZ_LINK pel = (LPEXP_SZ_LINK)SHFindDataBlock(_pExtraData, EXP_SZ_LINK_SIG);
  1958.                 if (!pel) 
  1959.                 {
  1960.                     pel = &expLink;
  1961.                     expLink.cbSize = 0;
  1962.                     expLink.dwSignature = EXP_SZ_LINK_SIG;
  1963.                 }
  1964.                 // store both A and W version (for no good reason!)
  1965.                 SHTCharToAnsi(pszPath, pel->szTarget, ARRAYSIZE(pel->szTarget));
  1966.                 SHTCharToUnicode(pszPath, pel->swzTarget, ARRAYSIZE(pel->swzTarget));
  1967.                 // See if this is a new entry that we need to add
  1968.                 if (pel->cbSize == 0)
  1969.                 {
  1970.                     pel->cbSize = SIZEOF(*pel);
  1971.                     _AddExtraDataSection((DATABLOCK_HEADER *)pel);
  1972.                 }
  1973.                 SetPIDLPath(NULL, szExpPath, NULL);
  1974.             }
  1975.             else 
  1976.             {
  1977.                 _sld.dwFlags &= (~SLDF_HAS_EXP_SZ);
  1978.                 _RemoveExtraDataSection(EXP_SZ_LINK_SIG);
  1979.                 SetPIDLPath(NULL, pszPath, NULL);
  1980.             }
  1981.             hr = SetIDList(_pidl);
  1982.         }
  1983.     }
  1984.     return hr;
  1985. }
  1986. STDMETHODIMP CShellLink::GetClassID(CLSID *pClassID)
  1987. {
  1988.     *pClassID = CLSID_ShellLink;
  1989.     return S_OK;
  1990. }
  1991. STDMETHODIMP CShellLink::IsDirty()
  1992. {
  1993.     return _bDirty ? S_OK : S_FALSE;
  1994. }
  1995. HRESULT LinkInfo_LoadFromStream(IStream *pstm, PLINKINFO *ppli)
  1996. {
  1997.     DWORD dwSize;
  1998.     ULONG cbBytesRead;
  1999.     HRESULT hres;
  2000.     if (*ppli)
  2001.     {
  2002.         LocalFree((HLOCAL)*ppli);
  2003.         *ppli = NULL;
  2004.     }
  2005.     hres = pstm->Read(&dwSize, SIZEOF(dwSize), &cbBytesRead);     // size of data
  2006.     if (SUCCEEDED(hres) && (cbBytesRead == SIZEOF(dwSize)))
  2007.     {
  2008.         if (dwSize >= SIZEOF(dwSize))   // must be at least this big
  2009.         {
  2010.             /* Yes.  Read remainder of LinkInfo into local memory. */
  2011.             PLINKINFO pli = (PLINKINFO)LocalAlloc(LPTR, dwSize);
  2012.             if (pli)
  2013.             {
  2014.                 *(DWORD *)pli = dwSize;         // Copy size
  2015.                 dwSize -= SIZEOF(dwSize);       // Read remainder of LinkInfo
  2016.                 hres = pstm->Read(((DWORD *)pli) + 1, dwSize, &cbBytesRead);
  2017.                 if (SUCCEEDED(hres) && (cbBytesRead == dwSize))
  2018.                    *ppli = pli; // LinkInfo read successfully
  2019.                 else
  2020.                    LocalFree((HLOCAL)pli);
  2021.             }
  2022.         }
  2023.     }
  2024.     return hres;
  2025. }
  2026. //Decodes the CSIDL_ relative target pidl
  2027. void CShellLink::_DecodeSpecialFolder()
  2028. {
  2029. #ifdef DEBUG
  2030.     if ( _pszCurFile )
  2031.     {
  2032.         TraceMsg( TF_DEBUGLINKCODE, "Enter _DecodeSpecialFolder (%s):", _pszCurFile );
  2033.     }
  2034.     else
  2035.     {
  2036.         TraceMsg( TF_DEBUGLINKCODE, "Enter _DecodeSpecialFolder (no file name):" );
  2037.     }
  2038. #endif
  2039.     LPEXP_SPECIAL_FOLDER pData = (LPEXP_SPECIAL_FOLDER)SHFindDataBlock(_pExtraData, EXP_SPECIAL_FOLDER_SIG);
  2040.     if (pData)
  2041.     {
  2042.         TraceMsg( TF_DEBUGLINKCODE, "  EXP_SPECIAL_FOLDER_SIG found, CSIDL=%d, cbOffset=0x%08x", pData->idSpecialFolder, pData->cbOffset );
  2043.         LPITEMIDLIST pidlFolder = SHCloneSpecialIDList(NULL, pData->idSpecialFolder, FALSE);
  2044.         if (pidlFolder)
  2045.         {
  2046.             // BUGBUG: In theory the pData->cbOffset should always match the _pidl for this shortcut so the
  2047.             // following _ILSkip should be 100% safe.  The reason this should be safe is because we delete
  2048.             // the EXP_SPECIAL_FOLDER_SIG section as soon as we use it and we don't write it back until we
  2049.             // save the link, at which time we write it using the updated _pidl.  However, somehow we are
  2050.             // seeing a lot of _pidls that don't match the pData->cbOffset so somebody someplace is directly
  2051.             // mucking with the _pidl without updating the EXP_SPECIAL_FOLDER_SIG section.
  2052.             ASSERT(IS_VALID_PIDL(_pidl));
  2053.             LPITEMIDLIST pidlTarget = _ILSkip(_pidl, pData->cbOffset);
  2054.             LPITEMIDLIST pidlSanityCheck = _pidl;
  2055.             while ( !ILIsEmpty(pidlSanityCheck) && (pidlSanityCheck < pidlTarget) )
  2056.             {
  2057.                 // We go one step at a time until pidlSanityCheck == pidlTarget.  If we reach the end
  2058.                 // of pidlSanityCheck, or if we go past pidlTarget, before this condition is met then
  2059.                 // we have an invalid pData->cbOffset.
  2060.                 pidlSanityCheck = _ILNext(pidlSanityCheck);
  2061.             }
  2062.             if ( pidlSanityCheck == pidlTarget )
  2063.             {
  2064.                 LPITEMIDLIST pidlNew = ILCombine(pidlFolder, pidlTarget);
  2065.                 if (pidlNew)
  2066.                 {
  2067.                     UpdateWorkingDir(pidlNew);
  2068.                     ILFree(_pidl);
  2069.                     _pidl = pidlNew;
  2070.                     TraceMsg( TF_DEBUGLINKCODE, "  _DecodeSpecialFolder _pidl update successful." );
  2071.                 }
  2072.             }
  2073.             else
  2074.             {
  2075.                 // ToddB: If you hit this error in a debugger contact ShellHot
  2076.                 TraceMsg( TF_ERROR, "  A bogus pidl offset was found in _DecodeSpecialFolder, this should not happen!" );
  2077.             }
  2078.             ILFree(pidlFolder);
  2079.         }
  2080.         // Note: We must always remove this if we have one. because the offset
  2081.         // will get trashed if some other link tracking
  2082.         // mechanism replace pidl on us.
  2083.         _RemoveExtraDataSection(EXP_SPECIAL_FOLDER_SIG);
  2084.         ASSERT( NULL==SHFindDataBlock(_pExtraData, EXP_SPECIAL_FOLDER_SIG) );
  2085.         TraceMsg( TF_DEBUGLINKCODE, "  EXP_SPECIAL_FOLDER_SIG removed" );
  2086.     }
  2087.     TraceMsg( TF_DEBUGLINKCODE, "Exit _DecodeSpecialFolder" );
  2088. }
  2089. STDMETHODIMP CShellLink::Load(IStream *pstm)
  2090. {
  2091.     ULONG cbBytes;
  2092.     DWORD cbSize;
  2093.     TraceMsg( TF_DEBUGLINKCODE, "Loading link from stream." );
  2094.     _ResetPersistData();        // clear out our state
  2095.     HRESULT hres = pstm->Read(&cbSize, SIZEOF(cbSize), &cbBytes);
  2096.     if (SUCCEEDED(hres))
  2097.     {
  2098.         if (cbBytes == SIZEOF(cbSize))
  2099.         {
  2100.             if (cbSize == SIZEOF(_sld))
  2101.             {
  2102.                 hres = pstm->Read((LPBYTE)&_sld + SIZEOF(cbSize), SIZEOF(_sld) - SIZEOF(cbSize), &cbBytes);
  2103.                 if (SUCCEEDED(hres) && cbBytes == (SIZEOF(_sld) - SIZEOF(cbSize)) && IsEqualGUID(_sld.clsid, CLSID_ShellLink))
  2104.                 {
  2105.                     _sld.cbSize = SIZEOF(_sld);
  2106.                     switch (_sld.iShowCmd) 
  2107.                     {
  2108.                         case SW_SHOWNORMAL:
  2109.                         case SW_SHOWMINNOACTIVE:
  2110.                         case SW_SHOWMAXIMIZED:
  2111.                         break;
  2112.                         default:
  2113.                             DebugMsg(DM_TRACE, TEXT("Shortcut Load, mapping bogus ShowCmd: %d"), _sld.iShowCmd);
  2114.                             _sld.iShowCmd = SW_SHOWNORMAL;
  2115.                         break;
  2116.                     }
  2117.                     // save so we can generate notify on save
  2118.                     _wOldHotkey = _sld.wHotkey;   
  2119.                     // read all of the members
  2120.                     if (_sld.dwFlags & SLDF_HAS_ID_LIST)
  2121.                     {
  2122.                         TraceMsg( TF_DEBUGLINKCODE, "  CShellLink: Loading pidl..." );
  2123.                         hres = ILLoadFromStream(pstm, &_pidl);
  2124.                         // success means we read the correct amount of data from the stream, not that the pidl is valid.
  2125.                         if (SUCCEEDED(hres))
  2126.                         {
  2127.                             if (_pli && (_sld.dwFlags & SLDF_FORCE_NO_LINKINFO))
  2128.                             {
  2129.                                 DebugMsg( DM_TRACE, TEXT("labotimizing link"));
  2130.                                 LocalFree((HLOCAL)_pli);
  2131.                                 _pli = NULL;
  2132.                             }
  2133.                             // Check for a valid pidl.  File corruption can cause pidls to become bad which will cause
  2134.                             // explorer to AV unless we catch it here.  Also, people have been known to write invalid
  2135.                             // pidls into link files from time to time.
  2136.                             if ( !SHIsValidPidl(_pidl) )
  2137.                             {
  2138.                                 // In theory this will only happen due to file corruption, but I've seen this too
  2139.                                 // often not to suspect that we might be doing something wrong.
  2140.                                 TraceMsg( TF_WARNING, "CShellLink::Load: Corrupted pidl read from link file." );
  2141.                                 // turn off the flag, which we know is on to start with
  2142.                                 _sld.dwFlags ^= SLDF_HAS_ID_LIST;
  2143.                                 // free the memory.  I call SHFree and not ILFree to avoid getting debug assert messages.
  2144.                                 // We already know this is an invalid pidl.
  2145.                                 SHFree(_pidl);
  2146.                                 // drop the result
  2147.                                 _pidl = NULL;