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

Windows Kernel

Development Platform:

Visual C++

  1. #include "priv.h"
  2. #include <hlink.h>
  3. #include "iface.h"
  4. #include "resource.h"
  5. #include <mluisupp.h>
  6. STDAPI SafeGetItemObject(IShellView *psv, UINT uItem, REFIID riid, void **ppv);
  7. class CTravelEntry : public ITravelEntry
  8. {
  9. public:
  10.     CTravelEntry(BOOL fIsLocalAnchor);
  11.     // *** IUnknown
  12.     STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj);
  13.     STDMETHODIMP_(ULONG) AddRef();
  14.     STDMETHODIMP_(ULONG) Release();
  15.     // *** ITravelEntry specific methods
  16.     STDMETHODIMP Update(IUnknown *punk, BOOL fIsLocalAnchor);
  17.     STDMETHODIMP Invoke(IUnknown *punk);
  18.     STDMETHODIMP GetPidl(LPITEMIDLIST *ppidl);
  19.     static HRESULT CreateTravelEntry(IBrowserService *pbs, BOOL fIsLocalAnchor, CTravelEntry **ppte);
  20.     void SetPrev(CTravelEntry *ptePrev);
  21.     void SetNext(CTravelEntry *pteNext);
  22.     CTravelEntry *GetPrev() {return _ptePrev;}
  23.     CTravelEntry *GetNext() {return _pteNext;}
  24.     void RemoveSelf();
  25.     BOOL CanInvoke(IUnknown *punk, BOOL fAllowLocalAnchor);
  26.     HRESULT GetIndexBrowser(IUnknown *punkIn, IShellBrowser **ppsbOut);
  27.     DWORD Size();
  28.     DWORD ListSize();
  29.     HRESULT Clone(CTravelEntry **ppte);
  30.     HRESULT UpdateExternal(IUnknown *punk, IUnknown *punkHLBrowseContext);
  31.     HRESULT UpdateSelf(IUnknown *punk) 
  32.         {return Update(punk, (_type == TET_LOCALANCHOR));}
  33.     BOOL IsExternal(void)
  34.         { return (_type==TET_EXTERNALNAV); }
  35.     HRESULT GetDisplayName(LPTSTR psz, DWORD cch, DWORD dwFlags);
  36.     BOOL IsEqual(LPCITEMIDLIST pidl)
  37.         {return ILIsEqual(pidl, _pidl);}
  38.     BOOL IsLocalAnchor(void)
  39.         { return (_type==TET_LOCALANCHOR);}
  40. #ifdef DEBUG
  41.     void TransferToThreadMemlist(DWORD id);
  42. #endif
  43. protected:
  44.     CTravelEntry(void);
  45.     HRESULT _InvokeExternal(IUnknown *punk);
  46.     HRESULT _UpdateTravelLog(IUnknown *punk, BOOL fIsLocalAnchor);
  47.     LONG _cRef;
  48.     ~CTravelEntry();
  49.     void _Reset(void);
  50.     enum {
  51.         TET_EMPTY   = 0,
  52.         TET_DEFAULT = 1,
  53.         TET_LOCALANCHOR,
  54.         TET_EXTERNALNAV
  55.     };
  56.     DWORD _type;            //  flags for our own sake...
  57.     LPITEMIDLIST _pidl;            //  pidl of the entry
  58.     HGLOBAL _hGlobalData;       //  the stream data saved by the entry
  59.     DWORD _bid;             //  the BrowserIndex for frame specific navigation
  60.     DWORD _dwCookie;     //  if _hGlobalData is NULL the cookie should be set
  61.     WCHAR *_pwzTitle;
  62.     
  63.     IHlink *_phl;
  64.     IHlinkBrowseContext *_phlbc;
  65.     CTravelEntry *_ptePrev;
  66.     CTravelEntry *_pteNext;
  67. };
  68. CTravelEntry::CTravelEntry(BOOL fIsLocalAnchor) : _cRef(1)
  69. {
  70.     //these should always be allocated
  71.     //  thus they will always start 0
  72.     if (fIsLocalAnchor)
  73.         _type = TET_LOCALANCHOR;
  74.     else
  75.         ASSERT(!_type);
  76.     ASSERT(!_pwzTitle);
  77.     ASSERT(!_pidl);
  78.     ASSERT(!_hGlobalData);
  79.     ASSERT(!_bid);
  80.     ASSERT(!_dwCookie);
  81.     ASSERT(!_ptePrev);
  82.     ASSERT(!_pteNext);
  83.     ASSERT(!_phl);
  84.     ASSERT(!_phlbc);
  85.     TraceMsg(TF_TRAVELLOG, "TE[%X] created _type = %x", this, _type);
  86. }
  87. CTravelEntry::CTravelEntry(void) :_cRef(1)
  88. {
  89.     ASSERT(!_type);
  90.     ASSERT(!_pwzTitle);
  91.     ASSERT(!_pidl);
  92.     ASSERT(!_hGlobalData);
  93.     ASSERT(!_bid);
  94.     ASSERT(!_dwCookie);
  95.     ASSERT(!_ptePrev);
  96.     ASSERT(!_pteNext);
  97.     ASSERT(!_phl);
  98.     ASSERT(!_phlbc);
  99.     TraceMsg(TF_TRAVELLOG, "TE[%X] created", this, _type);
  100. }
  101. HGLOBAL CloneHGlobal(HGLOBAL hGlobalIn)
  102. {
  103.     DWORD dwSize = (DWORD)GlobalSize(hGlobalIn);
  104.     HGLOBAL hGlobalOut = GlobalAlloc(GlobalFlags(hGlobalIn), dwSize);
  105.     HGLOBAL hGlobalResult = NULL;
  106.     if (NULL != hGlobalOut)
  107.     {
  108.         LPVOID pIn= GlobalLock(hGlobalIn);
  109.         if (NULL != pIn)
  110.         {
  111.             LPVOID pOut= GlobalLock(hGlobalOut);
  112.             if (NULL != pOut)
  113.             {
  114.                 memcpy(pOut, pIn, dwSize);
  115.                 GlobalUnlock(hGlobalOut);
  116.                 hGlobalResult = hGlobalOut;
  117.             }
  118.             GlobalUnlock(hGlobalIn);
  119.         }
  120.         if (!hGlobalResult)
  121.         {
  122.             GlobalFree(hGlobalOut);
  123.         }
  124.     }
  125.     return hGlobalResult;
  126. }
  127. HRESULT 
  128. CTravelEntry::Clone(CTravelEntry **ppte)
  129. {
  130.     //  dont ever clone an external entry
  131.     if (_type == TET_EXTERNALNAV)
  132.         return E_FAIL;
  133.     CTravelEntry *pte = new CTravelEntry();
  134.     HRESULT hr = S_OK;
  135.     if (pte)
  136.     {
  137.         pte->_type = _type;
  138.         pte->_bid = _bid;
  139.         pte->_dwCookie = _dwCookie;
  140.         if (_pwzTitle)
  141.         {
  142.             pte->_pwzTitle = StrDup(_pwzTitle);
  143.             if (!pte->_pwzTitle)
  144.             {
  145.                 hr = E_OUTOFMEMORY;
  146.             }
  147.         }
  148.         if (_pidl)
  149.         {
  150.             pte->_pidl = ILClone(_pidl);
  151.             if (!pte->_pidl)
  152.                 hr = E_OUTOFMEMORY;
  153.         }
  154.         else
  155.             pte->_pidl = NULL;
  156.         if (_hGlobalData)
  157.         {
  158.             pte->_hGlobalData = CloneHGlobal(_hGlobalData);
  159.             if (NULL == pte->_hGlobalData)
  160.             {
  161.                 hr = E_OUTOFMEMORY;
  162.             }
  163.         }
  164.         else
  165.         {
  166.             ASSERT(NULL == pte->_hGlobalData);
  167.         }
  168.     }
  169.     else 
  170.         hr = E_OUTOFMEMORY;
  171.     if (FAILED(hr) && pte)
  172.     {
  173.         pte->Release();
  174.         *ppte = NULL;
  175.     }
  176.     else
  177.         *ppte = pte;
  178.     TraceMsg(TF_TRAVELLOG, "TE[%X] Clone hr = %x", this, hr);
  179.     return hr;
  180. }
  181. CTravelEntry::~CTravelEntry()
  182. {
  183.     ILFree(_pidl);
  184.     if (_hGlobalData)
  185.     {
  186.         GlobalFree(_hGlobalData);
  187.     }
  188.     if (_pwzTitle)
  189.     {
  190.         LocalFree(_pwzTitle);
  191.     }
  192.     if (_pteNext)
  193.     {
  194.         _pteNext->Release();
  195.     }
  196.     ATOMICRELEASE(_phl);
  197.     ATOMICRELEASE(_phlbc);
  198.     TraceMsg(TF_TRAVELLOG, "TE[%X] destroyed ", this);
  199. }
  200. #ifdef DEBUG
  201. void CTravelEntry::TransferToThreadMemlist(DWORD id)
  202. {
  203.     //must call this on every allocated thing ....
  204.     transfer_to_thread_memlist( id, this);
  205.     // i dont think that i have to catch pidls or pstms because they
  206.     //are not part of the debug allocator
  207. }
  208. #endif
  209. HRESULT CTravelEntry::QueryInterface(REFIID riid, void **ppvObj)
  210. {
  211.     static const QITAB qit[] = { 
  212.         QITABENT(CTravelEntry, ITravelEntry), // IID_ITravelEntry
  213.         { 0 }, 
  214.     };
  215.     return QISearch(this, qit, riid, ppvObj);
  216. }
  217. ULONG CTravelEntry::AddRef()
  218. {
  219.     return InterlockedIncrement(&_cRef);
  220. }
  221. ULONG CTravelEntry::Release()
  222. {
  223.     if (InterlockedDecrement(&_cRef))
  224.         return _cRef;
  225.     delete this;
  226.     return 0;
  227. }
  228. HRESULT CTravelEntry::GetIndexBrowser(IUnknown *punk, IShellBrowser **ppsb)
  229. {
  230.     HRESULT hr = E_FAIL;
  231.     IBrowserService *pbs;
  232.     ASSERT(ppsb);
  233.     *ppsb = NULL;
  234.     if (SUCCEEDED(punk->QueryInterface(IID_IBrowserService, (void **)&pbs)))
  235.     {
  236.         IUnknown *punkReal;
  237.         if (SUCCEEDED(pbs->GetBrowserByIndex(_bid, &punkReal)))
  238.         {
  239.             hr = punkReal->QueryInterface(IID_IShellBrowser, (void **)ppsb);
  240.             punkReal->Release();
  241.         }
  242.         pbs->Release();
  243.     }
  244.     TraceMsg(TF_TRAVELLOG, "TE[%X]::GetIndexBrowser _bid = %X, hr = %X", this, _bid, hr);
  245.     return hr;
  246. }
  247. BOOL CTravelEntry::CanInvoke(IUnknown *punk, BOOL fAllowLocalAnchor)
  248. {
  249.     IShellBrowser *psb;
  250.     BOOL fRet = IsLocalAnchor() ? fAllowLocalAnchor : TRUE;
  251.     fRet = fRet && SUCCEEDED(GetIndexBrowser(punk, &psb)) ;
  252.     ATOMICRELEASE(psb);
  253.     return fRet;
  254. }
  255. DWORD CTravelEntry::Size()
  256. {
  257.     DWORD cbSize = SIZEOF(*this);
  258.     if (_pidl)
  259.         cbSize += ILGetSize(_pidl);
  260.     if (_hGlobalData)
  261.     {
  262.         cbSize += (DWORD)GlobalSize(_hGlobalData);
  263.     }
  264.     if (_pwzTitle)
  265.     {
  266.         cbSize += (DWORD)LocalSize(_pwzTitle);
  267.     }
  268.     return cbSize;
  269. }
  270. DWORD CTravelEntry::ListSize()
  271. {
  272.     CTravelEntry *pte = GetNext();
  273.     DWORD cb = Size();
  274.     while (pte)
  275.     {
  276.         cb += pte->Size();
  277.         pte = pte->GetNext();
  278.     }
  279.     return cb;
  280. }
  281. void CTravelEntry::_Reset()
  282. {
  283.     Pidl_Set(&_pidl, NULL);
  284.     if (NULL != _hGlobalData)
  285.     {
  286.         GlobalFree(_hGlobalData);
  287.         _hGlobalData = NULL;
  288.     }
  289.     ATOMICRELEASE(_phl);
  290.     ATOMICRELEASE(_phlbc);
  291.     _bid = 0;
  292.     _type = TET_EMPTY;
  293.     _dwCookie = 0;
  294.     if (_pwzTitle)
  295.     {
  296.         LocalFree(_pwzTitle);
  297.         _pwzTitle = NULL;
  298.     }
  299.     TraceMsg(TF_TRAVELLOG, "TE[%X]::_Reset", this);
  300. }
  301. HRESULT CTravelEntry::_UpdateTravelLog(IUnknown *punk, BOOL fIsLocalAnchor)
  302. {
  303.     IBrowserService *pbs;
  304.     HRESULT hr = E_FAIL;
  305.     //  we need to update here
  306.     if (SUCCEEDED(punk->QueryInterface(IID_IBrowserService, (void **)&pbs)))
  307.     {
  308.         ITravelLog *ptl;
  309.         if (SUCCEEDED(pbs->GetTravelLog(&ptl)))
  310.         {
  311.             hr = ptl->UpdateEntry(punk, fIsLocalAnchor);
  312.             ptl->Release();
  313.         }
  314.         pbs->Release();
  315.     }
  316.     return hr;
  317. }
  318. HRESULT CTravelEntry::_InvokeExternal(IUnknown *punk)
  319. {
  320.     HRESULT hr = E_FAIL;
  321.     ASSERT(_phl);
  322.     ASSERT(_phlbc);
  323.     
  324.     TraceMsg(TF_TRAVELLOG, "TE[%X]::InvokeExternal entered on _bid = %X, _phl = %X, _phlbc = %X", this, _bid, _phl, _phlbc);
  325.     // set the size and position of the browser frame window, so that the
  326.     // external target can sync up its frame window to those coordinates
  327.     HLBWINFO hlbwi;
  328.     hlbwi.cbSize = sizeof(hlbwi);
  329.     hlbwi.grfHLBWIF = 0;
  330.     IOleWindow *pow;
  331.     HWND hwnd = NULL;
  332.     if (SUCCEEDED(punk->QueryInterface(IID_IOleWindow, (void **)&pow)))
  333.     {
  334.         pow->GetWindow(&hwnd);
  335.         pow->Release();
  336.     }
  337.     if (hwnd) 
  338.     {
  339.         WINDOWPLACEMENT wp = {0};
  340.         wp.length = sizeof(WINDOWPLACEMENT);
  341.         GetWindowPlacement(hwnd, &wp);
  342.         hlbwi.grfHLBWIF = HLBWIF_HASFRAMEWNDINFO;
  343.         hlbwi.rcFramePos = wp.rcNormalPosition;
  344.         if (wp.showCmd == SW_SHOWMAXIMIZED)
  345.             hlbwi.grfHLBWIF |= HLBWIF_FRAMEWNDMAXIMIZED;
  346.     }
  347.     _phlbc->SetBrowseWindowInfo(&hlbwi);
  348.     //
  349.     //  right now we always now we are going back, but later on
  350.     //  maybe we should ask the browser whether this is back or forward
  351.     //
  352.     hr = _phl->Navigate(HLNF_NAVIGATINGBACK, NULL, NULL, _phlbc);
  353.     
  354.     IServiceProvider *psp; 
  355.     if (SUCCEEDED(punk->QueryInterface(IID_IServiceProvider, (void **)&psp)))
  356.     {
  357.         IWebBrowser2 *pwb;
  358.         ASSERT(psp);
  359.         if (SUCCEEDED(psp->QueryService(SID_SWebBrowserApp, IID_IWebBrowser2, (void **)&pwb)))
  360.         {
  361.             ASSERT(pwb);
  362.             pwb->put_Visible(FALSE);
  363.             pwb->Release();
  364.         }
  365.         psp->Release();
  366.     }
  367.     _UpdateTravelLog(punk, FALSE);
  368.     TraceMsg(TF_TRAVELLOG, "TE[%X]::InvokeExternal exited hr = %X", this, hr);
  369.     return hr;
  370. }
  371. HRESULT CTravelEntry::Invoke(IUnknown *punk)
  372. {
  373.     IPersistHistory *pph = NULL;
  374.     HRESULT hr = E_FAIL;
  375.     IShellBrowser *psb = NULL;
  376.     TraceMsg(TF_TRAVELLOG, "TE[%X]::Invoke entered on _bid = %X", this, _bid);
  377.     TraceMsgW(TF_TRAVELLOG, "TE[%X]::Invoke title '%s'", this, _pwzTitle);
  378.     if (_type == TET_EXTERNALNAV)
  379.     {
  380.         hr = _InvokeExternal(punk);
  381.         goto Quit;
  382.     }
  383.     if (FAILED(GetIndexBrowser(punk, &psb)))
  384.         goto Quit;
  385.     hr = psb->QueryInterface(IID_IPersistHistory, (void **)&pph);
  386.     if (SUCCEEDED(hr))
  387.     {
  388.         ASSERT(pph);
  389.         if (_type == TET_LOCALANCHOR)
  390.         {
  391.             hr = pph->SetPositionCookie(_dwCookie);
  392.         }
  393.         else
  394.         {
  395.             //  we need to clone it
  396.             ASSERT(_hGlobalData);
  397.             
  398.             HGLOBAL hGlobal = CloneHGlobal(_hGlobalData);
  399.             if (NULL != hGlobal)
  400.             {
  401.                 IStream *pstm;
  402.                 
  403.                 hr = CreateStreamOnHGlobal(hGlobal, TRUE, &pstm);
  404.                 if (SUCCEEDED(hr))
  405.                 {
  406.                     hr = pph->LoadHistory(pstm, NULL);
  407.                     pstm->Release();
  408.                 }
  409.                 else
  410.                 {
  411.                     GlobalFree(hGlobal);
  412.                 }
  413.             }
  414.             else
  415.             {
  416.                 hr = E_OUTOFMEMORY;
  417.             }
  418.         }
  419.         pph->Release();
  420.     }
  421. Quit:
  422.     SAFERELEASE(psb);
  423.     TraceMsg(TF_TRAVELLOG, "TE[%X]::Invoke exited on _bid = %X, hr = %X", this, _bid, hr);
  424.     return hr;
  425. }
  426. HRESULT CTravelEntry::UpdateExternal(IUnknown *punk, IUnknown *punkHLBrowseContext)
  427. {
  428.     TraceMsg(TF_TRAVELLOG, "TE[%X]::UpdateExternal entered on punk = %X, punkhlbc = %X", this, punk, punkHLBrowseContext);
  429.     _Reset();
  430.     ASSERT(punkHLBrowseContext);
  431.     punkHLBrowseContext->QueryInterface(IID_IHlinkBrowseContext, (void **)&_phlbc);
  432.     ASSERT(_phlbc);
  433.     _type = TET_EXTERNALNAV;
  434.     HRESULT hr = E_FAIL;
  435.     //
  436.     //  right now we only support externals being previous.  we never actually navigate
  437.     //  to another app.  we handle everything in pane ourselves.
  438.     //  so theoretically we never need to worry about HLID_NEXT
  439.     _phlbc->GetHlink((ULONG) HLID_PREVIOUS, &_phl);
  440.     
  441.     IBrowserService *pbs;
  442.     punk->QueryInterface(IID_IBrowserService, (void **)&pbs);
  443.     if (pbs && _phl) 
  444.     {
  445.         _bid = pbs->GetBrowserIndex();
  446.         WCHAR *pwszTarget;
  447.         hr = _phl->GetStringReference(HLINKGETREF_ABSOLUTE, &pwszTarget, NULL);
  448.         if (SUCCEEDED(hr))
  449.         {
  450.             TCHAR szName[MAX_URL_STRING];
  451.             StrCpyN(szName, pwszTarget, ARRAYSIZE(szName));
  452.             OleFree(pwszTarget);
  453.             // create pidl
  454.             hr = IECreateFromPath(szName, &_pidl);
  455.         }
  456.     }
  457.     ATOMICRELEASE(pbs);
  458.     TraceMsg(TF_TRAVELLOG, "TE[%X]::UpdateExternal exited _bid = %X, hr = %X", this, _bid, hr);
  459.     return hr;
  460. }
  461. HRESULT CTravelEntry::Update(IUnknown *punk, BOOL fIsLocalAnchor)
  462. {
  463.     ASSERT(punk);
  464.     
  465.     //  this means that we went back to an external app, 
  466.     //  and now we are going forward again.  we dont persist 
  467.     //  any state info about them that would be different.
  468.     if (_type == TET_EXTERNALNAV)
  469.     {
  470.         TraceMsg(TF_TRAVELLOG, "TE[%X]::Update NOOP on external entry", this);
  471.         return S_OK;
  472.     }
  473.     IBrowserService *pbs;
  474.     IShellBrowser *psb = NULL;
  475.     IShellView *psv = NULL;
  476.     HRESULT hr = punk->QueryInterface(IID_IBrowserService, (void **)&pbs);
  477.     TraceMsg(TF_TRAVELLOG, "TE[%X]::Update entered on pbs = %X", this, pbs);
  478.     pbs->QueryInterface(IID_IShellBrowser, (void **)&psb);
  479.     psb->QueryActiveShellView(&psv);
  480.     _Reset();
  481.     if (SUCCEEDED(hr))
  482.     {
  483.         if (SUCCEEDED(pbs->GetPidl(&_pidl)))
  484.         {
  485.             IPersistHistory *pph;
  486.             pbs->QueryInterface(IID_IPersistHistory, (void **)&pph);
  487.             ASSERT(_pidl);
  488.             ASSERT(pph);
  489.             _bid = pbs->GetBrowserIndex();
  490.             if (psv)
  491.             {
  492.                 //  pick up the title as a display name 
  493.                 //  for menus and the like
  494.                 WCHAR wzTitle[MAX_PATH];
  495.                 
  496.                 pbs->GetTitle(psv, wzTitle, SIZECHARS(wzTitle));
  497.                 _pwzTitle = StrDup(wzTitle);
  498.                 
  499.                 TraceMsgW(TF_TRAVELLOG, "TE[%X]::Update title '%s'", this, _pwzTitle);
  500.             }
  501.             if (fIsLocalAnchor)
  502.             {
  503.                 //
  504.                 //  persist a cookie
  505.                 _type = TET_LOCALANCHOR;
  506.                 hr = pph->GetPositionCookie(&_dwCookie);
  507.             }
  508.             else
  509.             {
  510.                 //
  511.                 //  persist a stream
  512.                 ASSERT(!_hGlobalData);
  513.                 IStream *pstm;
  514.                 hr = CreateStreamOnHGlobal(NULL, FALSE, &pstm);
  515.                 
  516.                 if (SUCCEEDED(hr))
  517.                 {
  518.                     ASSERT(pph);
  519.                     _type = TET_DEFAULT;
  520.                     
  521.                     hr = pph->SaveHistory(pstm);
  522.                     STATSTG stg;
  523.                     HRESULT hrStat = pstm->Stat(&stg, STATFLAG_NONAME);
  524.                     hr = GetHGlobalFromStream(pstm, &_hGlobalData);
  525.                     pstm->Release();
  526.                     //  This little exercise here is to shrink the memory block we get from
  527.                     //  the OLE API which allocates blocks in chunks of 8KB.  Typical stream
  528.                     //  sizes are only a few hundred bytes.
  529.                     
  530.                     if (SUCCEEDED(hrStat))
  531.                     {
  532.                         HGLOBAL hGlobalTemp = GlobalReAlloc(_hGlobalData, stg.cbSize.LowPart, GMEM_MOVEABLE);
  533.                         if (NULL != hGlobalTemp)
  534.                         {
  535.                             _hGlobalData = hGlobalTemp;
  536.                         }
  537.                     }
  538.                 }
  539.             }
  540.             pph->Release();
  541.         }
  542.         else //_pidl == NULL
  543.             hr = E_OUTOFMEMORY;
  544.     }
  545.     if (FAILED(hr))
  546.         _Reset();
  547.     SAFERELEASE(psb);
  548.     SAFERELEASE(psv);
  549.     SAFERELEASE(pbs);
  550.     TraceMsg(TF_TRAVELLOG, "TE[%X]::Update exited on _bid = %X, hr = %X", this, _bid, hr);
  551.     return hr;
  552. }
  553. HRESULT CTravelEntry::GetPidl(LPITEMIDLIST * ppidl)
  554. {
  555.     if (EVAL(ppidl))
  556.     {
  557.         *ppidl = ILClone(_pidl);
  558.         if (*ppidl)
  559.             return S_OK;
  560.     }
  561.     return E_FAIL;
  562. }
  563. void CTravelEntry::SetNext(CTravelEntry *pteNext)
  564. {
  565.     if (_pteNext)
  566.         _pteNext->Release();
  567.     _pteNext = pteNext;
  568.     if (_pteNext) 
  569.     {
  570.         _pteNext->_ptePrev = this;
  571.     }
  572. }
  573. // the only time we prepend the link is to the top of the entire chain (bottom of the stack)
  574. //
  575. // the topmost element has a ref count to it that we must then assume
  576. void CTravelEntry::SetPrev(CTravelEntry *ptePrev)
  577. {
  578.     ASSERT(!_ptePrev);
  579.     _ptePrev = ptePrev;
  580.     if (_ptePrev)
  581.         _ptePrev->SetNext(this);
  582. }
  583. //
  584. //  this is for removing from the middle of the list...
  585. //
  586. void CTravelEntry::RemoveSelf()
  587. {
  588.     if (_pteNext)
  589.         _pteNext->_ptePrev = _ptePrev;
  590.     // remove yourself from the list
  591.     if (_ptePrev) 
  592.     {
  593.         // after this point, we may be destroyed so can't touch any more member vars
  594.         _ptePrev->_pteNext = _pteNext;
  595.     }
  596.     _ptePrev = NULL;
  597.     _pteNext = NULL;
  598.     // we lose a reference now because we're gone from _ptePrev's _pteNext
  599.     // (or if we were the top of the list, we're also nuked)
  600.     Release();
  601. }
  602. HRESULT GetUnescapedUrlIfAppropriate(LPCITEMIDLIST pidl, LPTSTR pszUrl, DWORD cch)
  603. {
  604.     TCHAR szUrl[MAX_URL_STRING];
  605.     // The SHGDN_NORMAL display name will be the pretty name (Web Page title) unless
  606.     // it's an FTP URL or the web page didn't set a title.
  607.     if (SUCCEEDED(IEGetDisplayName(pidl, szUrl, SHGDN_NORMAL)) &&
  608.         UrlIs(szUrl, URLIS_URL))
  609.     {
  610.         // NT #279192, If an URL is escaped, it normally contains three types of
  611.         // escaped chars.
  612.         // 1) Seperating type chars ('#' for frag, '?' for params, etc.)
  613.         // 2) DBCS chars,
  614.         // 3) Data (a bitmap in the url by escaping the binary bytes)
  615.         // Since #2 is very common, we want to try to unescape it so it has meaning
  616.         // to the user.  UnEscaping isn't safe if the user can copy or modify the data
  617.         // because they could loose data when it's reparsed.  One thing we need to
  618.         // do for #2 to work is for it to be in ANSI when unescaped.  This is needed
  619.         // or the DBCS lead and trail bytes will be in unicode as [0x<LeadByte> 0x00]
  620.         // [0x<TrailByte> 0x00].  Being in ANSI could cause a problem if the the string normally
  621.         // crosses code pages, but that is uncommon or non-existent in the IsURLChild()
  622.         // case.
  623.         CHAR szUrlAnsi[MAX_URL_STRING];
  624.         SHTCharToAnsi(szUrl, szUrlAnsi, ARRAYSIZE(szUrlAnsi));
  625.         UrlUnescapeA(szUrlAnsi, NULL, NULL, URL_UNESCAPE_INPLACE|URL_UNESCAPE_HIGH_ANSI_ONLY);
  626.         SHAnsiToTChar(szUrlAnsi, pszUrl, cch);
  627.     }
  628.     else
  629.     {
  630.         StrCpyN(pszUrl, szUrl, cch);    // Truncate if needed
  631.     }
  632.     return S_OK;
  633. }
  634. #define TEGDN_FORSYSTEM     0x00000001
  635. HRESULT CTravelEntry::GetDisplayName(LPTSTR psz, DWORD cch, DWORD dwFlags)
  636. {
  637.     if (!psz || !cch)
  638.         return E_INVALIDARG;
  639.     psz[0] = 0;
  640.     if ((NULL != _pwzTitle) && (*_pwzTitle != 0))
  641.     {
  642.         StrCpyNW(psz, _pwzTitle, cch);
  643.     }
  644.     else if (_pidl)
  645.     {
  646.         GetUnescapedUrlIfAppropriate(_pidl, psz, cch);
  647.     }
  648.     if (dwFlags & TEGDN_FORSYSTEM)
  649.     {
  650.         if (!SHIsDisplayable(psz, g_fRunOnFE, g_bRunOnNT5))
  651.         {
  652.             // Display name isn't system-displayable.  Just use the path/url instead.
  653.             SHTitleFromPidl(_pidl, psz, cch, FALSE);
  654.         }
  655.     }
  656.     return psz[0] ? S_OK : E_FAIL;
  657. }
  658. class CTravelLog : public ITravelLog
  659. {
  660. public:
  661.     // *** IUnknown
  662.     STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj);
  663.     STDMETHODIMP_(ULONG) AddRef() ;
  664.     STDMETHODIMP_(ULONG) Release();
  665.     // *** ITravelLog specific methods
  666.     STDMETHODIMP AddEntry(IUnknown *punk, BOOL fIsLocalAnchor);
  667.     STDMETHODIMP UpdateEntry(IUnknown *punk, BOOL fIsLocalAnchor);
  668.     STDMETHODIMP UpdateExternal(IUnknown *punk, IUnknown *punkHLBrowseContext);
  669.     STDMETHODIMP Travel(IUnknown *punk, int iOffset);
  670.     STDMETHODIMP GetTravelEntry(IUnknown *punk, int iOffset, ITravelEntry **ppte);
  671.     STDMETHODIMP FindTravelEntry(IUnknown *punk, LPCITEMIDLIST pidl, ITravelEntry **ppte);
  672.     STDMETHODIMP GetToolTipText(IUnknown *punk, int iOffset, int idsTemplate, LPWSTR pwzText, DWORD cchText);
  673.     STDMETHODIMP InsertMenuEntries(IUnknown *punk, HMENU hmenu, int nPos, int idFirst, int idLast, DWORD dwFlags);
  674.     STDMETHODIMP Clone(ITravelLog **pptl);
  675.     STDMETHODIMP_(DWORD) CountEntries(IUnknown *punk);
  676.     STDMETHODIMP Revert(void);
  677.     CTravelLog();
  678. #ifdef DEBUG
  679.     void TransferToThreadMemlist(DWORD id);
  680. #endif
  681. protected:
  682.     ~CTravelLog();
  683.     HRESULT _FindEntryByOffset(IUnknown *punk, int iOffset, CTravelEntry **ppte);
  684.     void _Prune(void);
  685.     LONG _cRef;
  686.     DWORD _cbMaxSize;
  687.     DWORD _cbTotalSize;
  688.     CTravelEntry *_pteCurrent;  //pteCurrent
  689.     CTravelEntry *_pteUpdate;
  690.     CTravelEntry *_pteRoot;
  691. };
  692. CTravelLog::CTravelLog() : _cRef(1) 
  693. {
  694.     ASSERT(!_pteCurrent);
  695.     ASSERT(!_pteUpdate);
  696.     ASSERT(!_pteRoot);
  697.     DWORD dwType, dwSize = SIZEOF(_cbMaxSize), dwDefault = 1024 * 1024;
  698.     
  699.     SHRegGetUSValue(TEXT("Software\Microsoft\Windows\CurrentVersion\Explorer\TravelLog"), TEXT("MaxSize"), &dwType, (LPVOID)&_cbMaxSize, &dwSize, FALSE, (void *)&dwDefault, SIZEOF(dwDefault));
  700.     TraceMsg(TF_TRAVELLOG, "TL[%X] created", this);
  701. }
  702. CTravelLog::~CTravelLog()
  703. {
  704.     //DestroyList by releasing the root
  705.     SAFERELEASE(_pteRoot);
  706.     TraceMsg(TF_TRAVELLOG, "TL[%X] destroyed ", this);
  707. }
  708. HRESULT CTravelLog::QueryInterface(REFIID riid, void **ppvObj)
  709. {
  710.     static const QITAB qit[] = { 
  711.         QITABENT(CTravelLog, ITravelLog), // IID_ITravelLog
  712.         { 0 }, 
  713.     };
  714.     return QISearch(this, qit, riid, ppvObj);
  715. }
  716. ULONG CTravelLog::AddRef()
  717. {
  718.     return InterlockedIncrement(&_cRef);
  719. }
  720. ULONG CTravelLog::Release()
  721. {
  722.     if (InterlockedDecrement(&_cRef))
  723.         return _cRef;
  724.     delete this;
  725.     return 0;
  726. }
  727. HRESULT CTravelLog::AddEntry(IUnknown *punk, BOOL fIsLocalAnchor)
  728. {
  729.     ASSERT(punk);
  730.     if(SHRestricted2W(REST_NoNavButtons, NULL, 0))
  731.     {
  732.         return S_FALSE;
  733.     }
  734.     TraceMsg(TF_TRAVELLOG, "TL[%X]::AddEntry punk = %X, IsLocal = %s", this, punk, fIsLocalAnchor ? "TRUE" : "FALSE");
  735.     CTravelEntry *pte = new CTravelEntry(fIsLocalAnchor);
  736.     if (pte)
  737.     {
  738.         //replace the current with the new
  739.         if (_pteCurrent)
  740.         {
  741.             CTravelEntry *pteNext = _pteCurrent->GetNext();
  742.             if (pteNext)
  743.             {
  744.                 _cbTotalSize -= pteNext->ListSize();
  745.             }
  746.             //  the list keeps its own ref count, and only needs
  747.             //  to be modified when passed outside of the list
  748.             //  setnext will release the current next if necessary
  749.             //  this will also set pte->prev = pteCurrent
  750.             _pteCurrent->SetNext(pte);
  751.         }
  752.         else
  753.             _pteRoot = pte;
  754.         _cbTotalSize += pte->Size();
  755.         _pteCurrent = pte;
  756.         ASSERT(_cbTotalSize == _pteRoot->ListSize());
  757.     }
  758.     TraceMsg(TF_TRAVELLOG, "TL[%X]::AddEntry punk = %X, IsLocal = %d, pte = %X", this, punk, fIsLocalAnchor, pte);
  759.     return pte ? S_OK : E_OUTOFMEMORY;
  760. }
  761. void CTravelLog::_Prune(void)
  762. {
  763.     // BUGBUGTODO need an increment or something
  764.     ASSERT(_cbTotalSize == _pteRoot->ListSize());
  765.     while (_cbTotalSize > _cbMaxSize && _pteRoot != _pteCurrent)
  766.     {
  767.         CTravelEntry *pte = _pteRoot;
  768.         _pteRoot = _pteRoot->GetNext();
  769.         _cbTotalSize -= pte->Size();
  770.         pte->RemoveSelf();
  771.         ASSERT(_cbTotalSize == _pteRoot->ListSize());
  772.     }
  773. }
  774. HRESULT CTravelLog::UpdateEntry(IUnknown *punk, BOOL fIsLocalAnchor)
  775. {
  776.     CTravelEntry *pte = _pteUpdate ? _pteUpdate : _pteCurrent;
  777.     //  this can happen under weird stress conditions, evidently
  778.     if (!pte)
  779.         return E_FAIL;
  780.     _cbTotalSize -= pte->Size();
  781.     HRESULT hr = pte->Update(punk, fIsLocalAnchor);
  782.     _cbTotalSize += pte->Size();
  783.     ASSERT(_cbTotalSize == _pteRoot->ListSize());
  784.     // Debug prints need to be before _Prune() since pte can get freed by _Prune() resulting
  785.     // in a crash if pte->Size() is called
  786.     TraceMsg(TF_TRAVELLOG, "TL[%X]::UpdateEntry pte->Size() = %d", this, pte->Size());
  787.     TraceMsg(TF_TRAVELLOG, "TL[%X]::UpdateEntry punk = %X, IsLocal = %d, hr = %X", this, punk, fIsLocalAnchor, hr);
  788.     
  789.     _Prune();
  790.     _pteUpdate = NULL;
  791.     return hr;
  792. }
  793. HRESULT CTravelLog::UpdateExternal(IUnknown *punk, IUnknown *punkHLBrowseContext)
  794. {
  795.     CTravelEntry *pte = _pteUpdate ? _pteUpdate : _pteCurrent;
  796.     ASSERT(punk);
  797.     ASSERT(pte);
  798.     ASSERT(punkHLBrowseContext);
  799.     if (pte)
  800.         return pte->UpdateExternal(punk, punkHLBrowseContext);
  801.     return E_FAIL;
  802. }
  803. HRESULT CTravelLog::Travel(IUnknown *punk, int iOffset)
  804. {
  805.     ASSERT(punk);
  806.     HRESULT hr = E_FAIL;
  807.     CTravelEntry *pte;
  808.     TraceMsg(TF_TRAVELLOG, "TL[%X]::Travel entered with punk = %X, iOffset = %d", this, punk, iOffset);
  809.     if (SUCCEEDED(_FindEntryByOffset(punk, iOffset, &pte)))
  810.     {
  811.         // we will update where we are before we move away...
  812.         //  but external navigates dont go through the normal activation
  813.         //  so we dont want to setup the external to be updated
  814.         //  _pteUpdate is also what allows us to Revert().
  815.         if (!_pteCurrent->IsExternal() && !_pteUpdate)
  816.             _pteUpdate = _pteCurrent;
  817.         _pteCurrent = pte;
  818.         hr = _pteCurrent->Invoke(punk);
  819.         //
  820.         //  if the entry bails with an error, then we need to reset ourself
  821.         //  to what we were.  right now, the only place this should happen
  822.         //  is if an Abort was returned from SetPositionCookie
  823.         //  because somebody aborted during before navigate.
  824.         //  but i think that any error means that we can legitimately Revert().
  825.         //
  826.         if (FAILED(hr))
  827.         {
  828.             Revert();
  829.         }
  830.     }
  831.     TraceMsg(TF_TRAVELLOG, "TL[%X]::Travel exited with hr = %X", this, hr);
  832.     return hr;
  833. }
  834. HRESULT CTravelLog::_FindEntryByOffset(IUnknown *punk, int iOffset, CTravelEntry **ppte)
  835. {
  836.     CTravelEntry *pte = _pteCurrent;
  837.     BOOL fAllowLocalAnchor = TRUE;
  838.     if (iOffset < 0)
  839.     {
  840.         while (iOffset && pte)
  841.         {
  842.             pte = pte->GetPrev();
  843.             if (pte && pte->CanInvoke(punk, fAllowLocalAnchor))
  844.             {
  845.                 iOffset++;
  846.                 fAllowLocalAnchor = fAllowLocalAnchor && pte->IsLocalAnchor();
  847.             }
  848.         }
  849.     }
  850.     else if (iOffset > 0)
  851.     {
  852.         while (iOffset && pte)
  853.         {
  854.             pte = pte->GetNext();
  855.             if (pte && pte->CanInvoke(punk, fAllowLocalAnchor))
  856.             {
  857.                 iOffset--;
  858.                 fAllowLocalAnchor = fAllowLocalAnchor && pte->IsLocalAnchor();
  859.             }
  860.         }
  861.     }
  862.     if (pte)
  863.     {
  864.         *ppte = pte;
  865.         return S_OK;
  866.     }
  867.     return E_FAIL;
  868. }
  869. HRESULT CTravelLog::GetTravelEntry(IUnknown *punk, int iOffset, ITravelEntry **ppte)
  870. {
  871.     HRESULT hr;
  872.     BOOL fCheckExternal = FALSE;
  873.     if (iOffset == TLOG_BACKEXTERNAL) 
  874.     {
  875.         iOffset = TLOG_BACK;
  876.         fCheckExternal = TRUE;
  877.     }
  878.     if (iOffset == 0)
  879.     {
  880.         //  BUGBUGCOMPAT - going back and fore between external apps is dangerous - zekel 24-JUN-97
  881.         //  we always fail if the current is external
  882.         //  this is because word will attempt to navigate us to 
  883.         //  the same url instead of FORE when the user selects
  884.         //  it from the drop down.
  885.         if (_pteCurrent && _pteCurrent->IsExternal())
  886.         {
  887.             hr = E_FAIL;
  888.             ASSERT(!_pteCurrent->GetPrev());
  889.             TraceMsg(TF_TRAVELLOG, "TL[%X]::GetTravelEntry current is External", this);
  890.             goto Quit;
  891.         }
  892.     }
  893.     CTravelEntry *pte;
  894.     hr = _FindEntryByOffset(punk, iOffset, &pte);
  895.     //
  896.     // If TLOG_BACKEXTERNAL is specified, we return S_OK only if the previous
  897.     // entry is external.
  898.     //
  899.     if (fCheckExternal && SUCCEEDED(hr)) {
  900.         if (!pte->IsExternal()) {
  901.             hr = E_FAIL;
  902.         }
  903.         TraceMsg(TF_TRAVELLOG, "TL[%X]::GetTravelEntry(BACKEX)", this);
  904.     }
  905.     if (ppte && SUCCEEDED(hr)) {
  906.         hr = pte->QueryInterface(IID_ITravelEntry, (void **) ppte);
  907.     }
  908. Quit:
  909.     TraceMsg(TF_TRAVELLOG, "TL[%X]::GetTravelEntry iOffset = %d, hr = %X", this, iOffset, hr);
  910.     return hr;
  911. }
  912. HRESULT CTravelLog::FindTravelEntry(IUnknown *punk, LPCITEMIDLIST pidl, ITravelEntry **ppte)
  913. {
  914.     CTravelEntry *pte = _pteRoot;
  915.     BOOL fAllowLocalAnchor = TRUE;
  916.     
  917.     while (pte)
  918.     {
  919.         if (pte->CanInvoke(punk, fAllowLocalAnchor) && pte->IsEqual(pidl))
  920.             break;
  921.         fAllowLocalAnchor = fAllowLocalAnchor && pte->IsLocalAnchor();
  922.         pte = pte->GetNext();
  923.     }
  924.     if (pte)
  925.     {
  926.         return pte->QueryInterface(IID_ITravelEntry, (void **)ppte);
  927.     }
  928.     *ppte =  NULL;
  929.     return E_FAIL;
  930. }
  931. HRESULT CTravelLog::Clone(ITravelLog **pptl)
  932. {
  933.     CTravelLog *ptl = new CTravelLog();
  934.     HRESULT hr = S_OK;
  935.     if (ptl && _pteCurrent)
  936.     {
  937.         // first set the current pointer
  938.         hr = _pteCurrent->Clone(&ptl->_pteCurrent);
  939.         if (SUCCEEDED(hr))
  940.         {
  941.             ptl->_cbTotalSize = _cbTotalSize;
  942.             
  943.             CTravelEntry *pteSrc;
  944.             CTravelEntry *pteClone, *pteDst = ptl->_pteCurrent;
  945.             
  946.             //  then we need to loop forward and set each
  947.             for (pteSrc = _pteCurrent->GetNext(), pteDst = ptl->_pteCurrent;
  948.                 pteSrc; pteSrc = pteSrc->GetNext())
  949.             {
  950.                 ASSERT(pteDst);
  951.                 if (FAILED(pteSrc->Clone(&pteClone)))
  952.                     break;
  953.                 ASSERT(pteClone);
  954.                 pteDst->SetNext(pteClone);
  955.                 pteDst = pteClone;
  956.             }
  957.                 
  958.             //then loop back and set them all
  959.             for (pteSrc = _pteCurrent->GetPrev(), pteDst = ptl->_pteCurrent;
  960.                 pteSrc; pteSrc = pteSrc->GetPrev())
  961.             {
  962.                 ASSERT(pteDst);
  963.                 if (FAILED(pteSrc->Clone(&pteClone)))
  964.                     break;
  965.                 ASSERT(pteClone);
  966.                 pteDst->SetPrev(pteClone);
  967.                 pteDst = pteClone;
  968.             }   
  969.             //  the root is the furthest back we could go
  970.             ptl->_pteRoot = pteDst;
  971.         }
  972.     }
  973.     else 
  974.         hr = E_OUTOFMEMORY;
  975.     if (SUCCEEDED(hr))
  976.     {
  977.         ptl->QueryInterface(IID_ITravelLog, (void **) pptl);
  978.     }
  979.     else 
  980.     {
  981.         *pptl = NULL;
  982.     }
  983.     
  984.     if (ptl) 
  985.         ptl->Release();
  986.     TraceMsg(TF_TRAVELLOG, "TL[%X]::Clone hr = %x, ptlClone = %X", this, hr, ptl);
  987.     return hr;
  988. }
  989. // HACKHACK: 3rd parameter used to be idsTemplate, which we would use to grab the
  990. // string template.  However, since there's no way the caller can specify the hinst
  991. // of the module in which to look for this resource, this broke in the shdocvw /
  992. // browseui split (callers would pass offsets into browseui.dll; we'd look for them in
  993. // shdocvw.dll).  My solution is is to ignore this parameter entirely and assume that:
  994. //
  995. //  if iOffset is negative, the caller wants the "back to" text
  996. //  else, the caller wants the "forward to" text
  997. //
  998. // tjgreen 14-july-98.
  999. //
  1000. HRESULT CTravelLog::GetToolTipText(IUnknown *punk, int iOffset, int, LPWSTR pwzText, DWORD cchText)
  1001. {
  1002.     TCHAR szName[MAX_URL_STRING];
  1003.     TCHAR szTemplate[80];
  1004.     TraceMsg(TF_TRAVELLOG, "TL[%X]::ToolTip entering iOffset = %d, ptlClone = %X", this, iOffset);
  1005.     ASSERT(pwzText);
  1006.     ASSERT(cchText);
  1007.     *pwzText = 0;
  1008.     CTravelEntry *pte;
  1009.     HRESULT hr = _FindEntryByOffset(punk, iOffset, &pte);
  1010.     if (SUCCEEDED(hr))
  1011.     {
  1012.         ASSERT(pte);
  1013.         pte->GetDisplayName(szName, SIZECHARS(szName), 0);
  1014.         int idsTemplate = (iOffset < 0) ? IDS_NAVIGATEBACKTO : IDS_NAVIGATEFORWARDTO;
  1015.         if (MLLoadString(idsTemplate, szTemplate, ARRAYSIZE(szTemplate))) {
  1016.             DWORD cchTemplateLen = lstrlen(szTemplate);
  1017.             DWORD cchLen = cchTemplateLen + lstrlen(szName);
  1018.             if (cchLen > cchText) {
  1019.                 // so that we don't overflow the pwzText buffer
  1020.                 szName[cchText - cchTemplateLen - 1] = 0;
  1021.             }
  1022.             wnsprintf(pwzText, cchText, szTemplate, szName);
  1023.         }
  1024.         else
  1025.             hr = E_UNEXPECTED;
  1026.     }
  1027.     TraceMsg(TF_TRAVELLOG, "TL[%X]::ToolTip exiting hr = %X, pwzText = %ls", this, hr, pwzText);
  1028.     return hr;
  1029. }
  1030. HRESULT CTravelLog::InsertMenuEntries(IUnknown *punk, HMENU hmenu, int iIns, int idFirst, int idLast, DWORD dwFlags)
  1031. {
  1032.     ASSERT(idLast >= idFirst);
  1033.     ASSERT(hmenu);
  1034.     ASSERT(punk);
  1035.     int cItemsBack = idLast - idFirst + 1;
  1036.     int cItemsFore = 0;
  1037.     
  1038.     CTravelEntry *pte;
  1039.     LONG cAdded = 0;
  1040.     TCHAR szName[40];
  1041.     DWORD cchName = SIZECHARS(szName);
  1042.     UINT uFlags = MF_STRING | MF_ENABLED | MF_BYPOSITION;
  1043.     TraceMsg(TF_TRAVELLOG, "TL[%X]::InsertMenuEntries entered on punk = %X, hmenu = %X, iIns = %d, idRange = %d-%d, flags = %X", this, punk, hmenu, iIns, idFirst, idLast, dwFlags);
  1044.     ASSERT(cItemsFore >= 0);
  1045.     ASSERT(cItemsBack >= 0);
  1046.     if (IsFlagSet(dwFlags, TLMENUF_INCLUDECURRENT))
  1047.         cItemsBack--;
  1048.     if (IsFlagSet(dwFlags, TLMENUF_BACKANDFORTH))
  1049.     {
  1050.         cItemsFore = cItemsBack / 2;
  1051.         cItemsBack = cItemsBack - cItemsFore;
  1052.     }
  1053.     else if (IsFlagSet(dwFlags, TLMENUF_FORE))
  1054.     {
  1055.         cItemsFore = cItemsBack;
  1056.         cItemsBack = 0;
  1057.     }
  1058.     while (cItemsFore)
  1059.     {
  1060.         if (SUCCEEDED(_FindEntryByOffset(punk, cItemsFore, &pte)))
  1061.         {
  1062.             pte->GetDisplayName(szName, cchName, TEGDN_FORSYSTEM);
  1063.             ASSERT(*szName);
  1064.             FixAmpersands(szName, ARRAYSIZE(szName));
  1065.             InsertMenu(hmenu, iIns, uFlags, idLast, szName);
  1066.             cAdded++;
  1067.             TraceMsg(TF_TRAVELLOG, "TL[%X]::IME Fore id = %d, szName = %s", this, idLast, szName);
  1068.         }
  1069.         
  1070.         cItemsFore--;
  1071.         idLast--;
  1072.     }
  1073.     if (IsFlagSet(dwFlags, TLMENUF_INCLUDECURRENT))
  1074.     {
  1075.         // clear the name
  1076.         *szName = 0;
  1077.         //have to get the title from the actual pbs
  1078.         IBrowserService *pbs ;
  1079.         WCHAR wzTitle[MAX_PATH];
  1080.         LPITEMIDLIST pidl = NULL;
  1081.         if (SUCCEEDED(punk->QueryInterface(IID_IBrowserService, (void **)&pbs)))
  1082.         {
  1083.             pbs->GetPidl(&pidl);
  1084.             if (SUCCEEDED(pbs->GetTitle(NULL, wzTitle, SIZECHARS(wzTitle))))
  1085.             {
  1086.                 StrCpyN(szName, wzTitle, cchName);
  1087.             }
  1088.             else if (pidl)
  1089.             {
  1090.                 GetUnescapedUrlIfAppropriate(pidl, szName, ARRAYSIZE(szName));
  1091.             }
  1092.             pbs->Release();
  1093.         }
  1094.         if (!SHIsDisplayable(szName, g_fRunOnFE, g_bRunOnNT5) && pidl)
  1095.         {
  1096.             // Display name isn't system-displayable.  Just use the path/url instead.
  1097.             SHTitleFromPidl(pidl, szName, ARRAYSIZE(szName), FALSE);
  1098.         }
  1099.         if (!(*szName))
  1100.             TraceMsg(TF_ERROR, "CTravelLog::InsertMenuEntries -- failed to find title for current entry");
  1101.         ILFree(pidl);
  1102.         FixAmpersands(szName, ARRAYSIZE(szName));
  1103.         InsertMenu(hmenu, iIns, uFlags | (IsFlagSet(dwFlags, TLMENUF_CHECKCURRENT) ? MF_CHECKED : 0), idLast, szName);
  1104.         cAdded++;
  1105.         TraceMsg(TF_TRAVELLOG, "TL[%X]::IME Current id = %d, szName = %s", this, idLast, szName);
  1106.         idLast--;
  1107.     }
  1108.     
  1109.     if (IsFlagSet(dwFlags, TLMENUF_BACKANDFORTH))
  1110.     {
  1111.         //  we need to reverse the order of insertion for back
  1112.         //  when both directions are displayed
  1113.         int i;
  1114.         for (i = 1; i <= cItemsBack; i++, idLast--)
  1115.         {
  1116.             if (SUCCEEDED(_FindEntryByOffset(punk, -i, &pte)))
  1117.             {
  1118.                 pte->GetDisplayName(szName, cchName, TEGDN_FORSYSTEM);
  1119.                 ASSERT(*szName);
  1120.                 FixAmpersands(szName, ARRAYSIZE(szName));
  1121.                 InsertMenu(hmenu, iIns, uFlags, idLast, szName);
  1122.                 cAdded++;
  1123.                 TraceMsg(TF_TRAVELLOG, "TL[%X]::IME Back id = %d, szName = %s", this, idLast, szName);
  1124.             }
  1125.         }
  1126.     }
  1127.     else while (cItemsBack)
  1128.     {
  1129.         if (SUCCEEDED(_FindEntryByOffset(punk, -cItemsBack, &pte)))
  1130.         {
  1131.             pte->GetDisplayName(szName, cchName, TEGDN_FORSYSTEM);
  1132.             ASSERT(*szName);
  1133.             FixAmpersands(szName, ARRAYSIZE(szName));
  1134.             InsertMenu(hmenu, iIns, uFlags, idLast, szName);
  1135.             cAdded++;
  1136.             TraceMsg(TF_TRAVELLOG, "TL[%X]::IME Back id = %d, szName = %s", this, idLast, szName);
  1137.         }
  1138.         cItemsBack--;
  1139.         idLast--;
  1140.     }
  1141.     TraceMsg(TF_TRAVELLOG, "TL[%X]::InsertMenuEntries exiting added = %d", this, cAdded);
  1142.     return cAdded ? S_OK : S_FALSE;
  1143. }
  1144. DWORD CTravelLog::CountEntries(IUnknown *punk)
  1145. {
  1146.     CTravelEntry *pte = _pteRoot;
  1147.     DWORD dw = 0;
  1148.     BOOL fAllowLocalAnchor = TRUE;
  1149.     while (pte)
  1150.     {
  1151.         if (pte->CanInvoke(punk, fAllowLocalAnchor))
  1152.             dw++;
  1153.         fAllowLocalAnchor = fAllowLocalAnchor && pte->IsLocalAnchor();
  1154.         pte = pte->GetNext();
  1155.     }
  1156.     TraceMsg(TF_TRAVELLOG, "TL[%X]::CountEntries count = %d", this, dw);
  1157.     return dw;
  1158. }
  1159. HRESULT CTravelLog::Revert(void)
  1160. {
  1161.     // this function should only be called when
  1162.     //  we have travelled, and we stop the travel before finishing
  1163.     if (_pteUpdate)
  1164.     {
  1165.         // trade them back
  1166.         _pteCurrent = _pteUpdate;
  1167.         _pteUpdate = NULL;
  1168.         return S_OK;
  1169.     }
  1170.     return E_FAIL;
  1171. }
  1172. #ifdef DEBUG
  1173. void CTravelLog::TransferToThreadMemlist(DWORD id)
  1174. {
  1175.     //must call this on every allocated thing ....
  1176.     transfer_to_thread_memlist(id, this);
  1177.     CTravelEntry *pte = _pteRoot;
  1178.     while (pte)
  1179.     {
  1180.         pte->TransferToThreadMemlist(id);
  1181.         pte = pte->GetNext();
  1182.     }
  1183. }
  1184. #endif
  1185. #ifdef TRAVELDOCS
  1186. GetNewDocument()
  1187. {
  1188.     new CTraveledDocument();
  1189.     ptd->Init();
  1190.     DPA_Add(ptd);
  1191.     DPA_Sort();
  1192. }
  1193. // really need to use ILIsEqual() instead of this
  1194. int ILCompareFastButWrong(LPITEMIDLIST pidl1, LPITEMIDLIST pidl2)
  1195. {
  1196.     int iret;
  1197.     DWORD cb1 = ILGetSize(ptd1->_pidl); 
  1198.     DWORD cb2 = ILGetSize(ptd2->_pidl);
  1199.     iret = cb1 - cb2;
  1200.     if (0 == iret)
  1201.         iret = memcmp(pidl1, cb1, pidl2, cb2);
  1202.     return iret;
  1203. }
  1204. static int CTraveledDocument::Compare(PTRAVELEDDOCUMENT ptd1, PTRAVELEDDOCUMENT ptd2)
  1205. {
  1206.     int iret;
  1207.     
  1208.     iret = ptd1->_type - ptd2->_type;
  1209.     if (0 = iret)
  1210.     {
  1211.         iret = ptd1->_hash - ptd2->_hash;
  1212.         if (0 == iret)
  1213.         {
  1214.             switch (ptd1->_type)
  1215.             {
  1216.             case TDOC_PIDL:
  1217.                 iret = ILCompareFastButWrong(ptd1->_pidl, ptd2->_pidl);
  1218.                 break;
  1219.             case TDOC_URL:
  1220.                 iret = UrlCompare((LPTSTR)ptd1->_strUrl, (LPTSTR)ptd2->_strUrl, FALSE);
  1221.                 break;
  1222.             default:
  1223.                 ASSERT(FALSE);
  1224.             }
  1225.         }
  1226.     }
  1227.     return iret;
  1228. }
  1229. #endif //TRAVELDOCS
  1230. HRESULT CreateTravelLog(ITravelLog **pptl)
  1231. {
  1232.     HRESULT hres;
  1233.     CTravelLog *ptl =  new CTravelLog();
  1234.     if (ptl)
  1235.     {
  1236.         hres = ptl->QueryInterface(IID_ITravelLog, (void **)pptl);
  1237.         ptl->Release();
  1238.     }
  1239.     else
  1240.     {
  1241.         *pptl = NULL;
  1242.         hres = E_OUTOFMEMORY;
  1243.     }
  1244.     return hres;
  1245. }
  1246. SHDOCAPI_(void) TLTransferToThreadMemlist(ITravelLog *ptl, DWORD id)
  1247. {
  1248. #ifdef DEBUG
  1249.     CTravelLog *ptlReal = (CTravelLog *)ptl;
  1250.     ptlReal->TransferToThreadMemlist(id);
  1251. #endif
  1252. }
  1253.