[BROWSEUI]
[reactos.git] / reactos / dll / win32 / browseui / travellog.cpp
1 /*
2 * ReactOS Explorer
3 *
4 * Copyright 2009 Andrew Hill <ash77 at domain reactos.org>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 /*
22 Implements a class that keeps track of a PIDL history and allows
23 navigation forward and backward. This really should be in shdocvw, but it
24 is not registered for external instantiation, and the entire IBrowserService
25 hierarchy that normally spans browseui and shdocvw are collapsed into one
26 hierarchy in browseui, so I am moving it to browseui for now. If someone
27 decides to refactor code later, it wouldn't be difficult to move it.
28
29 TODO:
30 ****Does original travel log update the current item in the Travel method before or after calling ITravelEntry::Invoke?
31 ****Change to load maximum size from registry
32 ****Add code to track current size
33 ****Fix InsertMenuEntries to not exceed limit of menu item ids provided. Perhaps the method should try to be intelligent and if there are
34 too many items, center around the current item? Could cause dispatch problems...
35 ****Move tool tip text templates to resources
36 **Simplify code in InsertMenuEntries
37 Implement UpdateExternal
38 Implement FindTravelEntry
39 Implement Clone
40 Implement Revert
41
42 */
43
44 #include "precomp.h"
45
46 class CTravelEntry :
47 public CComObjectRootEx<CComMultiThreadModelNoCS>,
48 public ITravelEntry
49 {
50 public:
51 CTravelEntry *fNextEntry;
52 CTravelEntry *fPreviousEntry;
53 private:
54 LPITEMIDLIST fPIDL;
55 HGLOBAL fPersistState;
56 public:
57 CTravelEntry();
58 ~CTravelEntry();
59 HRESULT GetToolTipText(IUnknown *punk, LPWSTR pwzText) const;
60 long GetSize() const;
61
62 // *** ITravelEntry methods ***
63 virtual HRESULT STDMETHODCALLTYPE Invoke(IUnknown *punk);
64 virtual HRESULT STDMETHODCALLTYPE Update(IUnknown *punk, BOOL fIsLocalAnchor);
65 virtual HRESULT STDMETHODCALLTYPE GetPidl(LPITEMIDLIST *ppidl);
66
67 BEGIN_COM_MAP(CTravelEntry)
68 COM_INTERFACE_ENTRY_IID(IID_ITravelEntry, ITravelEntry)
69 END_COM_MAP()
70 };
71
72 class CTravelLog :
73 public CComObjectRootEx<CComMultiThreadModelNoCS>,
74 public ITravelLog
75 {
76 private:
77 CTravelEntry *fListHead;
78 CTravelEntry *fListTail;
79 CTravelEntry *fCurrentEntry;
80 long fMaximumSize;
81 long fCurrentSize;
82 unsigned long fEntryCount;
83 public:
84 CTravelLog();
85 ~CTravelLog();
86 HRESULT Initialize();
87 HRESULT FindRelativeEntry(int offset, CTravelEntry **foundEntry);
88 void DeleteChain(CTravelEntry *startHere);
89 void AppendEntry(CTravelEntry *afterEntry, CTravelEntry *newEntry);
90 public:
91
92 // *** ITravelLog methods ***
93 virtual HRESULT STDMETHODCALLTYPE AddEntry(IUnknown *punk, BOOL fIsLocalAnchor);
94 virtual HRESULT STDMETHODCALLTYPE UpdateEntry(IUnknown *punk, BOOL fIsLocalAnchor);
95 virtual HRESULT STDMETHODCALLTYPE UpdateExternal(IUnknown *punk, IUnknown *punkHLBrowseContext);
96 virtual HRESULT STDMETHODCALLTYPE Travel(IUnknown *punk, int iOffset);
97 virtual HRESULT STDMETHODCALLTYPE GetTravelEntry(IUnknown *punk, int iOffset, ITravelEntry **ppte);
98 virtual HRESULT STDMETHODCALLTYPE FindTravelEntry(IUnknown *punk, LPCITEMIDLIST pidl, ITravelEntry **ppte);
99 virtual HRESULT STDMETHODCALLTYPE GetToolTipText(IUnknown *punk, int iOffset, int idsTemplate, LPWSTR pwzText, DWORD cchText);
100 virtual HRESULT STDMETHODCALLTYPE InsertMenuEntries(IUnknown *punk, HMENU hmenu, int nPos, int idFirst, int idLast, DWORD dwFlags);
101 virtual HRESULT STDMETHODCALLTYPE Clone(ITravelLog **pptl);
102 virtual DWORD STDMETHODCALLTYPE CountEntries(IUnknown *punk);
103 virtual HRESULT STDMETHODCALLTYPE Revert();
104
105 BEGIN_COM_MAP(CTravelLog)
106 COM_INTERFACE_ENTRY_IID(IID_ITravelLog, ITravelLog)
107 END_COM_MAP()
108 };
109
110 CTravelEntry::CTravelEntry()
111 {
112 fNextEntry = NULL;
113 fPreviousEntry = NULL;
114 fPIDL = NULL;
115 fPersistState = NULL;
116 }
117
118 CTravelEntry::~CTravelEntry()
119 {
120 ILFree(fPIDL);
121 GlobalFree(fPersistState);
122 }
123
124 HRESULT CTravelEntry::GetToolTipText(IUnknown *punk, LPWSTR pwzText) const
125 {
126 HRESULT hResult;
127
128 hResult = ILGetDisplayNameEx(NULL, fPIDL, pwzText, ILGDN_NORMAL) ? S_OK : S_FALSE;
129 if (FAILED_UNEXPECTEDLY(hResult))
130 return hResult;
131
132 return S_OK;
133 }
134
135 long CTravelEntry::GetSize() const
136 {
137 return 0;
138 }
139
140 HRESULT STDMETHODCALLTYPE CTravelEntry::Invoke(IUnknown *punk)
141 {
142 CComPtr<IPersistHistory> persistHistory;
143 CComPtr<IStream> globalStream;
144 HRESULT hResult;
145
146 TRACE("CTravelEntry::Invoke for IUnknown punk=%p\n", punk);
147
148 hResult = punk->QueryInterface(IID_PPV_ARG(IPersistHistory, &persistHistory));
149 if (FAILED_UNEXPECTEDLY(hResult))
150 return hResult;
151 hResult = CreateStreamOnHGlobal(fPersistState, FALSE, &globalStream);
152 if (FAILED_UNEXPECTEDLY(hResult))
153 return hResult;
154 hResult = persistHistory->LoadHistory(globalStream, NULL);
155 if (FAILED_UNEXPECTEDLY(hResult))
156 return hResult;
157 return S_OK;
158 }
159
160 HRESULT STDMETHODCALLTYPE CTravelEntry::Update(IUnknown *punk, BOOL fIsLocalAnchor)
161 {
162 CComPtr<ITravelLogClient> travelLogClient;
163 CComPtr<IPersistHistory> persistHistory;
164 CComPtr<IStream> globalStream;
165 WINDOWDATA windowData;
166 HRESULT hResult;
167
168 TRACE("CTravelEntry::Update for IUnknown punk=%p, fIsLocalAnchor=%s\n", punk, fIsLocalAnchor ? "TRUE" : "FALSE");
169
170
171 WCHAR wch[MAX_PATH * 2];
172 GetToolTipText(punk, wch);
173 TRACE("Updating entry with display name: %S\n", wch);
174
175 ZeroMemory(&windowData, sizeof(WINDOWDATA));
176 ILFree(fPIDL);
177 fPIDL = NULL;
178 GlobalFree(fPersistState);
179 fPersistState = NULL;
180 hResult = punk->QueryInterface(IID_PPV_ARG(ITravelLogClient, &travelLogClient));
181 if (FAILED_UNEXPECTEDLY(hResult))
182 return hResult;
183 hResult = punk->QueryInterface(IID_PPV_ARG(IPersistHistory, &persistHistory));
184 if (FAILED_UNEXPECTEDLY(hResult))
185 return hResult;
186 hResult = CreateStreamOnHGlobal(NULL, FALSE, &globalStream);
187 if (FAILED_UNEXPECTEDLY(hResult))
188 return hResult;
189 hResult = persistHistory->SaveHistory(globalStream);
190 if (FAILED_UNEXPECTEDLY(hResult))
191 return hResult;
192 hResult = travelLogClient->GetWindowData(globalStream, &windowData);
193 if (FAILED_UNEXPECTEDLY(hResult))
194 return hResult;
195 fPIDL = windowData.pidl;
196 // TODO: Properly free the windowData
197 hResult = GetHGlobalFromStream(globalStream, &fPersistState);
198 if (FAILED_UNEXPECTEDLY(hResult))
199 return hResult;
200
201 GetToolTipText(punk, wch);
202 TRACE("Updated entry display name is now: %S\n", wch);
203
204 return S_OK;
205 }
206
207 HRESULT STDMETHODCALLTYPE CTravelEntry::GetPidl(LPITEMIDLIST *ppidl)
208 {
209 if (ppidl == NULL)
210 return E_POINTER;
211 *ppidl = ILClone(fPIDL);
212 if (*ppidl == NULL)
213 return E_OUTOFMEMORY;
214
215 TRACE("CTravelEntry::GetPidl returning ppidl=%p\n", *ppidl);
216
217 return S_OK;
218 }
219
220 CTravelLog::CTravelLog()
221 {
222 fListHead = NULL;
223 fListTail = NULL;
224 fCurrentEntry = NULL;
225 fMaximumSize = 0;
226 fCurrentSize = 0;
227 fEntryCount = 0;
228 TRACE("CTravelLog created\n");
229 }
230
231 CTravelLog::~CTravelLog()
232 {
233 CTravelEntry *anEntry;
234 CTravelEntry *next;
235
236 anEntry = fListHead;
237 while (anEntry != NULL)
238 {
239 next = anEntry->fNextEntry;
240 anEntry->Release();
241 anEntry = next;
242 }
243 TRACE("CTravelLog destroyed\n");
244 }
245
246 HRESULT CTravelLog::Initialize()
247 {
248 FIXME("CTravelLog::Initialize using hardcoded fMaximumSize.\n");
249 fMaximumSize = 1024 * 1024; // TODO: change to read this from registry
250 // Software\Microsoft\Windows\CurrentVersion\Explorer\TravelLog
251 // MaxSize
252 return S_OK;
253 }
254
255 HRESULT CTravelLog::FindRelativeEntry(int _offset, CTravelEntry **foundEntry)
256 {
257 CTravelEntry *curEntry;
258 int offset = _offset;
259
260 if (foundEntry == NULL)
261 return E_INVALIDARG;
262
263 *foundEntry = NULL;
264 curEntry = fCurrentEntry;
265 if (offset < 0)
266 {
267 while (offset < 0 && curEntry != NULL)
268 {
269 curEntry = curEntry->fPreviousEntry;
270 offset++;
271 }
272 }
273 else
274 {
275 while (offset > 0 && curEntry != NULL)
276 {
277 curEntry = curEntry->fNextEntry;
278 offset--;
279 }
280 }
281 if (curEntry == NULL)
282 return E_INVALIDARG;
283
284 *foundEntry = curEntry;
285
286 TRACE("CTravelLog::FindRelativeEntry for offset %d, returning %p\n", offset, *foundEntry);
287
288 return S_OK;
289 }
290
291 void CTravelLog::DeleteChain(CTravelEntry *startHere)
292 {
293 CTravelEntry *saveNext;
294 long itemSize;
295
296 TRACE("CTravelLog::DeleteChain deleting chain starting at %p\n", startHere);
297
298 long startEntryCount = fEntryCount;
299
300 if (startHere->fPreviousEntry != NULL)
301 {
302 startHere->fPreviousEntry->fNextEntry = NULL;
303 fListTail = startHere->fPreviousEntry;
304 }
305 else
306 {
307 fListHead = NULL;
308 fListTail = NULL;
309 }
310 while (startHere != NULL)
311 {
312 saveNext = startHere->fNextEntry;
313 itemSize = startHere->GetSize();
314 fCurrentSize -= itemSize;
315 startHere->Release();
316 startHere = saveNext;
317 fEntryCount--;
318 }
319
320 TRACE("CTravelLog::DeleteChain chain of %d items deleted\n", startEntryCount - fEntryCount);
321 }
322
323 void CTravelLog::AppendEntry(CTravelEntry *afterEntry, CTravelEntry *newEntry)
324 {
325 if (afterEntry == NULL)
326 {
327 TRACE("CTravelLog::AppendEntry appending %p after NULL. Resetting head and tail\n", newEntry);
328 fListHead = newEntry;
329 fListTail = newEntry;
330 }
331 else
332 {
333 TRACE("CTravelLog::AppendEntry appending %p after %p\n", newEntry, afterEntry);
334 newEntry->fNextEntry = afterEntry->fNextEntry;
335 afterEntry->fNextEntry = newEntry;
336 newEntry->fPreviousEntry = afterEntry;
337 if (newEntry->fNextEntry == NULL)
338 fListTail = newEntry;
339 else
340 newEntry->fNextEntry->fPreviousEntry = newEntry;
341 }
342 fEntryCount++;
343 }
344
345 HRESULT STDMETHODCALLTYPE CTravelLog::AddEntry(IUnknown *punk, BOOL fIsLocalAnchor)
346 {
347 CComObject<CTravelEntry> *newEntry;
348 long itemSize;
349
350 TRACE("CTravelLog::AddEntry for IUnknown punk=%p, fIsLocalAnchor=%s\n", punk, fIsLocalAnchor ? "TRUE" : "FALSE");
351
352 if (punk == NULL)
353 return E_INVALIDARG;
354 ATLTRY (newEntry = new CComObject<CTravelEntry>);
355 if (newEntry == NULL)
356 return E_OUTOFMEMORY;
357 newEntry->AddRef();
358 if (fCurrentEntry != NULL && fCurrentEntry->fNextEntry != NULL)
359 DeleteChain(fCurrentEntry->fNextEntry);
360 AppendEntry(fCurrentEntry, newEntry);
361 itemSize = newEntry->GetSize();
362 fCurrentSize += itemSize;
363 fCurrentEntry = newEntry;
364 return S_OK;
365 }
366
367 HRESULT STDMETHODCALLTYPE CTravelLog::UpdateEntry(IUnknown *punk, BOOL fIsLocalAnchor)
368 {
369 if (punk == NULL)
370 return E_INVALIDARG;
371 if (fCurrentEntry == NULL)
372 return E_UNEXPECTED;
373 return fCurrentEntry->Update(punk, fIsLocalAnchor);
374 }
375
376 HRESULT STDMETHODCALLTYPE CTravelLog::UpdateExternal(IUnknown *punk, IUnknown *punkHLBrowseContext)
377 {
378 return E_NOTIMPL;
379 }
380
381 HRESULT STDMETHODCALLTYPE CTravelLog::Travel(IUnknown *punk, int iOffset)
382 {
383 CTravelEntry *destinationEntry;
384 HRESULT hResult;
385
386 TRACE("CTravelLog::Travel for IUnknown punk=%p at offset=%d\n", punk, iOffset);
387
388 hResult = FindRelativeEntry(iOffset, &destinationEntry);
389 if (FAILED_UNEXPECTEDLY(hResult))
390 return hResult;
391 fCurrentEntry = destinationEntry;
392 hResult = destinationEntry->Invoke(punk);
393 if (FAILED_UNEXPECTEDLY(hResult))
394 return hResult;
395 return S_OK;
396 }
397
398 HRESULT STDMETHODCALLTYPE CTravelLog::GetTravelEntry(IUnknown *punk, int iOffset, ITravelEntry **ppte)
399 {
400 CTravelEntry *destinationEntry;
401 HRESULT hResult;
402
403 hResult = FindRelativeEntry(iOffset, &destinationEntry);
404 if (FAILED_UNEXPECTEDLY(hResult))
405 return hResult;
406 hResult = destinationEntry->QueryInterface(IID_PPV_ARG(ITravelEntry, ppte));
407 if (FAILED_UNEXPECTEDLY(hResult))
408 return hResult;
409
410 TRACE("CTravelLog::GetTravelEntry for IUnknown punk=%p at offset=%d returning %p\n", punk, iOffset, *ppte);
411
412 return hResult;
413 }
414
415 HRESULT STDMETHODCALLTYPE CTravelLog::FindTravelEntry(IUnknown *punk, LPCITEMIDLIST pidl, ITravelEntry **ppte)
416 {
417 if (ppte == NULL)
418 return E_POINTER;
419 if (punk == NULL || pidl == NULL)
420 return E_INVALIDARG;
421
422 UNIMPLEMENTED;
423
424 return E_NOTIMPL;
425 }
426
427 HRESULT STDMETHODCALLTYPE CTravelLog::GetToolTipText(IUnknown *punk, int iOffset, int idsTemplate, LPWSTR pwzText, DWORD cchText)
428 {
429 CTravelEntry *destinationEntry;
430 wchar_t tempString[MAX_PATH];
431 wchar_t templateString[200];
432 HRESULT hResult;
433
434 if (pwzText == NULL)
435 return E_POINTER;
436 if (punk == NULL || cchText == 0)
437 return E_INVALIDARG;
438 hResult = FindRelativeEntry(iOffset, &destinationEntry);
439 if (FAILED_UNEXPECTEDLY(hResult))
440 return hResult;
441 hResult = destinationEntry->GetToolTipText(punk, tempString);
442 if (FAILED_UNEXPECTEDLY(hResult))
443 return hResult;
444 if (iOffset < 0)
445 {
446 if(LoadStringW(_AtlBaseModule.GetResourceInstance(),
447 IDS_BACK, templateString, sizeof(templateString) / sizeof(wchar_t)) == 0)
448 return HRESULT_FROM_WIN32(GetLastError());
449 }
450 else
451 {
452 if(LoadStringW(_AtlBaseModule.GetResourceInstance(),
453 IDS_FORWARD, templateString, sizeof(templateString) / sizeof(wchar_t)) == 0)
454 return HRESULT_FROM_WIN32(GetLastError());
455 }
456 _snwprintf(pwzText, cchText, templateString, tempString);
457
458 TRACE("CTravelLog::GetToolTipText for IUnknown punk=%p at offset=%d returning L\"%S\"\n", punk, iOffset, pwzText);
459
460 return S_OK;
461 }
462
463 static void FixAmpersands(wchar_t *buffer)
464 {
465 wchar_t tempBuffer[MAX_PATH * 2];
466 wchar_t ch;
467 wchar_t *srcPtr;
468 wchar_t *dstPtr;
469
470 srcPtr = buffer;
471 dstPtr = tempBuffer;
472 while (*srcPtr != 0)
473 {
474 ch = *srcPtr++;
475 *dstPtr++ = ch;
476 if (ch == '&')
477 *dstPtr++ = '&';
478 }
479 *dstPtr = 0;
480 wcscpy(buffer, tempBuffer);
481 }
482
483 HRESULT STDMETHODCALLTYPE CTravelLog::InsertMenuEntries(IUnknown *punk, HMENU hmenu,
484 int nPos, int idFirst, int idLast, DWORD dwFlags)
485 {
486 CTravelEntry *currentItem;
487 MENUITEMINFO menuItemInfo;
488 wchar_t itemTextBuffer[MAX_PATH * 2];
489 HRESULT hResult;
490
491 TRACE("CTravelLog::InsertMenuEntries for IUnknown punk=%p, nPos=%d, idFirst=%d, idLast=%d\n", punk);
492
493 // TLMENUF_BACK - include back entries
494 // TLMENUF_INCLUDECURRENT - include current entry, if TLMENUF_CHECKCURRENT then check the entry
495 // TLMENUF_FORE - include next entries
496 // if fore+back, list from oldest to newest
497 // if back, list from newest to oldest
498 // if fore, list from newest to oldest
499
500 // don't forget to patch ampersands before adding to menu
501 if (punk == NULL)
502 return E_INVALIDARG;
503 if (idLast <= idFirst)
504 return E_INVALIDARG;
505 menuItemInfo.cbSize = sizeof(menuItemInfo);
506 menuItemInfo.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STATE | MIIM_STRING;
507 menuItemInfo.fType = MFT_STRING;
508 menuItemInfo.wID = idFirst;
509 menuItemInfo.fState = MFS_ENABLED;
510 menuItemInfo.dwTypeData = itemTextBuffer;
511 if ((dwFlags & TLMENUF_BACK) != 0)
512 {
513 if ((dwFlags & TLMENUF_FORE) != 0)
514 {
515 currentItem = fCurrentEntry;
516 if (currentItem != NULL)
517 {
518 while (currentItem->fPreviousEntry != NULL)
519 currentItem = currentItem->fPreviousEntry;
520 }
521 while (currentItem != fCurrentEntry)
522 {
523 hResult = currentItem->GetToolTipText(punk, itemTextBuffer);
524 if (SUCCEEDED(hResult))
525 {
526 FixAmpersands(itemTextBuffer);
527 TRACE("CTravelLog::InsertMenuEntries adding entry L\"%S\"/L\"%S\" with id %d\n", itemTextBuffer, menuItemInfo.dwTypeData, menuItemInfo.wID);
528 if (InsertMenuItem(hmenu, nPos, TRUE, &menuItemInfo))
529 {
530 nPos++;
531 menuItemInfo.wID++;
532 }
533 }
534 currentItem = currentItem->fNextEntry;
535 }
536 }
537 else
538 {
539 currentItem = fCurrentEntry;
540 if (currentItem != NULL)
541 currentItem = currentItem->fPreviousEntry;
542 while (currentItem != NULL)
543 {
544 hResult = currentItem->GetToolTipText(punk, itemTextBuffer);
545 if (SUCCEEDED(hResult))
546 {
547 FixAmpersands(itemTextBuffer);
548 TRACE("CTravelLog::InsertMenuEntries adding entry L\"%S\"/L\"%S\" with id %d\n", itemTextBuffer, menuItemInfo.dwTypeData, menuItemInfo.wID);
549 if (InsertMenuItem(hmenu, nPos, TRUE, &menuItemInfo))
550 {
551 nPos++;
552 menuItemInfo.wID++;
553 }
554 }
555 currentItem = currentItem->fPreviousEntry;
556 }
557 }
558 }
559 if ((dwFlags & TLMENUF_INCLUDECURRENT) != 0)
560 {
561 if (fCurrentEntry != NULL)
562 {
563 hResult = fCurrentEntry->GetToolTipText(punk, itemTextBuffer);
564 if (SUCCEEDED(hResult))
565 {
566 FixAmpersands(itemTextBuffer);
567 if ((dwFlags & TLMENUF_CHECKCURRENT) != 0)
568 menuItemInfo.fState |= MFS_CHECKED;
569 TRACE("CTravelLog::InsertMenuEntries adding entry L\"%S\"/L\"%S\" with id %d\n", itemTextBuffer, menuItemInfo.dwTypeData, menuItemInfo.wID);
570 if (InsertMenuItem(hmenu, nPos, TRUE, &menuItemInfo))
571 {
572 nPos++;
573 menuItemInfo.wID++;
574 }
575 menuItemInfo.fState &= ~MFS_CHECKED;
576 }
577 }
578 }
579 if ((dwFlags & TLMENUF_FORE) != 0)
580 {
581 currentItem = fCurrentEntry;
582 if (currentItem != NULL)
583 currentItem = currentItem->fNextEntry;
584 while (currentItem != NULL)
585 {
586 hResult = currentItem->GetToolTipText(punk, itemTextBuffer);
587 if (SUCCEEDED(hResult))
588 {
589 FixAmpersands(itemTextBuffer);
590 TRACE("CTravelLog::InsertMenuEntries adding entry L\"%S\"/L\"%S\" with id %d\n", itemTextBuffer, menuItemInfo.dwTypeData, menuItemInfo.wID);
591 if (InsertMenuItem(hmenu, nPos, TRUE, &menuItemInfo))
592 {
593 nPos++;
594 menuItemInfo.wID++;
595 }
596 }
597 currentItem = currentItem->fNextEntry;
598 }
599 }
600 return S_OK;
601 }
602
603 HRESULT STDMETHODCALLTYPE CTravelLog::Clone(ITravelLog **pptl)
604 {
605 if (pptl == NULL)
606 return E_POINTER;
607 *pptl = NULL;
608 // duplicate the log
609 UNIMPLEMENTED;
610 return E_NOTIMPL;
611 }
612
613 DWORD STDMETHODCALLTYPE CTravelLog::CountEntries(IUnknown *punk)
614 {
615 if (punk == NULL)
616 return E_INVALIDARG;
617 return fEntryCount;
618 }
619
620 HRESULT STDMETHODCALLTYPE CTravelLog::Revert()
621 {
622 // remove the current entry?
623 UNIMPLEMENTED;
624 return E_NOTIMPL;
625 }
626
627 HRESULT CTravelLog_CreateInstance(REFIID riid, void **ppv)
628 {
629 return ShellObjectCreatorInit<CTravelLog>(riid, ppv);
630 }