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 ILFree(fPIDL); 176 fPIDL = NULL; 177 GlobalFree(fPersistState); 178 fPersistState = NULL; 179 hResult = punk->QueryInterface(IID_PPV_ARG(ITravelLogClient, &travelLogClient)); 180 if (FAILED_UNEXPECTEDLY(hResult)) 181 return hResult; 182 hResult = punk->QueryInterface(IID_PPV_ARG(IPersistHistory, &persistHistory)); 183 if (FAILED_UNEXPECTEDLY(hResult)) 184 return hResult; 185 hResult = CreateStreamOnHGlobal(NULL, FALSE, &globalStream); 186 if (FAILED_UNEXPECTEDLY(hResult)) 187 return hResult; 188 hResult = persistHistory->SaveHistory(globalStream); 189 if (FAILED_UNEXPECTEDLY(hResult)) 190 return hResult; 191 hResult = travelLogClient->GetWindowData(globalStream, &windowData); 192 if (FAILED_UNEXPECTEDLY(hResult)) 193 return hResult; 194 fPIDL = windowData.pidl; 195 // TODO: Properly free the windowData 196 hResult = GetHGlobalFromStream(globalStream, &fPersistState); 197 if (FAILED_UNEXPECTEDLY(hResult)) 198 return hResult; 199 200 GetToolTipText(punk, wch); 201 TRACE("Updated entry display name is now: %S\n", wch); 202 203 return S_OK; 204 } 205 206 HRESULT STDMETHODCALLTYPE CTravelEntry::GetPidl(LPITEMIDLIST *ppidl) 207 { 208 if (ppidl == NULL) 209 return E_POINTER; 210 *ppidl = ILClone(fPIDL); 211 if (*ppidl == NULL) 212 return E_OUTOFMEMORY; 213 214 TRACE("CTravelEntry::GetPidl returning ppidl=%p\n", *ppidl); 215 216 return S_OK; 217 } 218 219 CTravelLog::CTravelLog() 220 { 221 fListHead = NULL; 222 fListTail = NULL; 223 fCurrentEntry = NULL; 224 fMaximumSize = 0; 225 fCurrentSize = 0; 226 fEntryCount = 0; 227 TRACE("CTravelLog created\n"); 228 } 229 230 CTravelLog::~CTravelLog() 231 { 232 CTravelEntry *anEntry; 233 CTravelEntry *next; 234 235 anEntry = fListHead; 236 while (anEntry != NULL) 237 { 238 next = anEntry->fNextEntry; 239 anEntry->Release(); 240 anEntry = next; 241 } 242 TRACE("CTravelLog destroyed\n"); 243 } 244 245 HRESULT CTravelLog::Initialize() 246 { 247 FIXME("CTravelLog::Initialize using hardcoded fMaximumSize.\n"); 248 fMaximumSize = 1024 * 1024; // TODO: change to read this from registry 249 // Software\Microsoft\Windows\CurrentVersion\Explorer\TravelLog 250 // MaxSize 251 return S_OK; 252 } 253 254 HRESULT CTravelLog::FindRelativeEntry(int _offset, CTravelEntry **foundEntry) 255 { 256 CTravelEntry *curEntry; 257 int offset = _offset; 258 259 if (foundEntry == NULL) 260 return E_INVALIDARG; 261 262 *foundEntry = NULL; 263 curEntry = fCurrentEntry; 264 if (offset < 0) 265 { 266 while (offset < 0 && curEntry != NULL) 267 { 268 curEntry = curEntry->fPreviousEntry; 269 offset++; 270 } 271 } 272 else 273 { 274 while (offset > 0 && curEntry != NULL) 275 { 276 curEntry = curEntry->fNextEntry; 277 offset--; 278 } 279 } 280 if (curEntry == NULL) 281 return E_INVALIDARG; 282 283 *foundEntry = curEntry; 284 285 TRACE("CTravelLog::FindRelativeEntry for offset %d, returning %p\n", offset, *foundEntry); 286 287 return S_OK; 288 } 289 290 void CTravelLog::DeleteChain(CTravelEntry *startHere) 291 { 292 CTravelEntry *saveNext; 293 long itemSize; 294 295 TRACE("CTravelLog::DeleteChain deleting chain starting at %p\n", startHere); 296 297 long startEntryCount = fEntryCount; 298 299 if (startHere->fPreviousEntry != NULL) 300 { 301 startHere->fPreviousEntry->fNextEntry = NULL; 302 fListTail = startHere->fPreviousEntry; 303 } 304 else 305 { 306 fListHead = NULL; 307 fListTail = NULL; 308 } 309 while (startHere != NULL) 310 { 311 saveNext = startHere->fNextEntry; 312 itemSize = startHere->GetSize(); 313 fCurrentSize -= itemSize; 314 startHere->Release(); 315 startHere = saveNext; 316 fEntryCount--; 317 } 318 319 TRACE("CTravelLog::DeleteChain chain of %d items deleted\n", startEntryCount - fEntryCount); 320 } 321 322 void CTravelLog::AppendEntry(CTravelEntry *afterEntry, CTravelEntry *newEntry) 323 { 324 if (afterEntry == NULL) 325 { 326 TRACE("CTravelLog::AppendEntry appending %p after NULL. Resetting head and tail\n", newEntry); 327 fListHead = newEntry; 328 fListTail = newEntry; 329 } 330 else 331 { 332 TRACE("CTravelLog::AppendEntry appending %p after %p\n", newEntry, afterEntry); 333 newEntry->fNextEntry = afterEntry->fNextEntry; 334 afterEntry->fNextEntry = newEntry; 335 newEntry->fPreviousEntry = afterEntry; 336 if (newEntry->fNextEntry == NULL) 337 fListTail = newEntry; 338 else 339 newEntry->fNextEntry->fPreviousEntry = newEntry; 340 } 341 fEntryCount++; 342 } 343 344 HRESULT STDMETHODCALLTYPE CTravelLog::AddEntry(IUnknown *punk, BOOL fIsLocalAnchor) 345 { 346 CComObject<CTravelEntry> *newEntry; 347 long itemSize; 348 349 TRACE("CTravelLog::AddEntry for IUnknown punk=%p, fIsLocalAnchor=%s\n", punk, fIsLocalAnchor ? "TRUE" : "FALSE"); 350 351 if (punk == NULL) 352 return E_INVALIDARG; 353 ATLTRY (newEntry = new CComObject<CTravelEntry>); 354 if (newEntry == NULL) 355 return E_OUTOFMEMORY; 356 newEntry->AddRef(); 357 if (fCurrentEntry != NULL && fCurrentEntry->fNextEntry != NULL) 358 DeleteChain(fCurrentEntry->fNextEntry); 359 AppendEntry(fCurrentEntry, newEntry); 360 itemSize = newEntry->GetSize(); 361 fCurrentSize += itemSize; 362 fCurrentEntry = newEntry; 363 return S_OK; 364 } 365 366 HRESULT STDMETHODCALLTYPE CTravelLog::UpdateEntry(IUnknown *punk, BOOL fIsLocalAnchor) 367 { 368 if (punk == NULL) 369 return E_INVALIDARG; 370 if (fCurrentEntry == NULL) 371 return E_UNEXPECTED; 372 return fCurrentEntry->Update(punk, fIsLocalAnchor); 373 } 374 375 HRESULT STDMETHODCALLTYPE CTravelLog::UpdateExternal(IUnknown *punk, IUnknown *punkHLBrowseContext) 376 { 377 return E_NOTIMPL; 378 } 379 380 HRESULT STDMETHODCALLTYPE CTravelLog::Travel(IUnknown *punk, int iOffset) 381 { 382 CTravelEntry *destinationEntry; 383 HRESULT hResult; 384 385 TRACE("CTravelLog::Travel for IUnknown punk=%p at offset=%d\n", punk, iOffset); 386 387 hResult = FindRelativeEntry(iOffset, &destinationEntry); 388 if (FAILED_UNEXPECTEDLY(hResult)) 389 return hResult; 390 fCurrentEntry = destinationEntry; 391 hResult = destinationEntry->Invoke(punk); 392 if (FAILED_UNEXPECTEDLY(hResult)) 393 return hResult; 394 return S_OK; 395 } 396 397 HRESULT STDMETHODCALLTYPE CTravelLog::GetTravelEntry(IUnknown *punk, int iOffset, ITravelEntry **ppte) 398 { 399 CTravelEntry *destinationEntry; 400 HRESULT hResult; 401 402 hResult = FindRelativeEntry(iOffset, &destinationEntry); 403 if (FAILED_UNEXPECTEDLY(hResult)) 404 return hResult; 405 hResult = destinationEntry->QueryInterface(IID_PPV_ARG(ITravelEntry, ppte)); 406 if (FAILED_UNEXPECTEDLY(hResult)) 407 return hResult; 408 409 TRACE("CTravelLog::GetTravelEntry for IUnknown punk=%p at offset=%d returning %p\n", punk, iOffset, *ppte); 410 411 return hResult; 412 } 413 414 HRESULT STDMETHODCALLTYPE CTravelLog::FindTravelEntry(IUnknown *punk, LPCITEMIDLIST pidl, ITravelEntry **ppte) 415 { 416 if (ppte == NULL) 417 return E_POINTER; 418 if (punk == NULL || pidl == NULL) 419 return E_INVALIDARG; 420 421 UNIMPLEMENTED; 422 423 return E_NOTIMPL; 424 } 425 426 HRESULT STDMETHODCALLTYPE CTravelLog::GetToolTipText(IUnknown *punk, int iOffset, int idsTemplate, LPWSTR pwzText, DWORD cchText) 427 { 428 CTravelEntry *destinationEntry; 429 wchar_t tempString[MAX_PATH]; 430 wchar_t templateString[200]; 431 HRESULT hResult; 432 433 if (pwzText == NULL) 434 return E_POINTER; 435 if (punk == NULL || cchText == 0) 436 return E_INVALIDARG; 437 hResult = FindRelativeEntry(iOffset, &destinationEntry); 438 if (FAILED_UNEXPECTEDLY(hResult)) 439 return hResult; 440 hResult = destinationEntry->GetToolTipText(punk, tempString); 441 if (FAILED_UNEXPECTEDLY(hResult)) 442 return hResult; 443 if (iOffset < 0) 444 { 445 wcscpy(templateString, L"Back to %s"); 446 } 447 else 448 { 449 wcscpy(templateString, L"Forward to %s"); 450 } 451 _snwprintf(pwzText, cchText, templateString, tempString); 452 453 TRACE("CTravelLog::GetToolTipText for IUnknown punk=%p at offset=%d returning L\"%S\"\n", punk, iOffset, pwzText); 454 455 return S_OK; 456 } 457 458 static void FixAmpersands(wchar_t *buffer) 459 { 460 wchar_t tempBuffer[MAX_PATH * 2]; 461 wchar_t ch; 462 wchar_t *srcPtr; 463 wchar_t *dstPtr; 464 465 srcPtr = buffer; 466 dstPtr = tempBuffer; 467 while (*srcPtr != 0) 468 { 469 ch = *srcPtr++; 470 *dstPtr++ = ch; 471 if (ch == '&') 472 *dstPtr++ = '&'; 473 } 474 *dstPtr = 0; 475 wcscpy(buffer, tempBuffer); 476 } 477 478 HRESULT STDMETHODCALLTYPE CTravelLog::InsertMenuEntries(IUnknown *punk, HMENU hmenu, 479 int nPos, int idFirst, int idLast, DWORD dwFlags) 480 { 481 CTravelEntry *currentItem; 482 MENUITEMINFO menuItemInfo; 483 wchar_t itemTextBuffer[MAX_PATH * 2]; 484 HRESULT hResult; 485 486 TRACE("CTravelLog::InsertMenuEntries for IUnknown punk=%p, nPos=%d, idFirst=%d, idLast=%d\n", punk); 487 488 // TLMENUF_BACK - include back entries 489 // TLMENUF_INCLUDECURRENT - include current entry, if TLMENUF_CHECKCURRENT then check the entry 490 // TLMENUF_FORE - include next entries 491 // if fore+back, list from oldest to newest 492 // if back, list from newest to oldest 493 // if fore, list from newest to oldest 494 495 // don't forget to patch ampersands before adding to menu 496 if (punk == NULL) 497 return E_INVALIDARG; 498 if (idLast <= idFirst) 499 return E_INVALIDARG; 500 menuItemInfo.cbSize = sizeof(menuItemInfo); 501 menuItemInfo.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STATE | MIIM_STRING; 502 menuItemInfo.fType = MFT_STRING; 503 menuItemInfo.wID = idFirst; 504 menuItemInfo.fState = MFS_ENABLED; 505 menuItemInfo.dwTypeData = itemTextBuffer; 506 if ((dwFlags & TLMENUF_BACK) != 0) 507 { 508 if ((dwFlags & TLMENUF_FORE) != 0) 509 { 510 currentItem = fCurrentEntry; 511 if (currentItem != NULL) 512 { 513 while (currentItem->fPreviousEntry != NULL) 514 currentItem = currentItem->fPreviousEntry; 515 } 516 while (currentItem != fCurrentEntry) 517 { 518 hResult = currentItem->GetToolTipText(punk, itemTextBuffer); 519 if (SUCCEEDED(hResult)) 520 { 521 FixAmpersands(itemTextBuffer); 522 TRACE("CTravelLog::InsertMenuEntries adding entry L\"%S\"/L\"%S\" with id %d\n", itemTextBuffer, menuItemInfo.dwTypeData, menuItemInfo.wID); 523 if (InsertMenuItem(hmenu, nPos, TRUE, &menuItemInfo)) 524 { 525 nPos++; 526 menuItemInfo.wID++; 527 } 528 } 529 currentItem = currentItem->fNextEntry; 530 } 531 } 532 else 533 { 534 currentItem = fCurrentEntry; 535 if (currentItem != NULL) 536 currentItem = currentItem->fPreviousEntry; 537 while (currentItem != NULL) 538 { 539 hResult = currentItem->GetToolTipText(punk, itemTextBuffer); 540 if (SUCCEEDED(hResult)) 541 { 542 FixAmpersands(itemTextBuffer); 543 TRACE("CTravelLog::InsertMenuEntries adding entry L\"%S\"/L\"%S\" with id %d\n", itemTextBuffer, menuItemInfo.dwTypeData, menuItemInfo.wID); 544 if (InsertMenuItem(hmenu, nPos, TRUE, &menuItemInfo)) 545 { 546 nPos++; 547 menuItemInfo.wID++; 548 } 549 } 550 currentItem = currentItem->fPreviousEntry; 551 } 552 } 553 } 554 if ((dwFlags & TLMENUF_INCLUDECURRENT) != 0) 555 { 556 if (fCurrentEntry != NULL) 557 { 558 hResult = fCurrentEntry->GetToolTipText(punk, itemTextBuffer); 559 if (SUCCEEDED(hResult)) 560 { 561 FixAmpersands(itemTextBuffer); 562 if ((dwFlags & TLMENUF_CHECKCURRENT) != 0) 563 menuItemInfo.fState |= MFS_CHECKED; 564 TRACE("CTravelLog::InsertMenuEntries adding entry L\"%S\"/L\"%S\" with id %d\n", itemTextBuffer, menuItemInfo.dwTypeData, menuItemInfo.wID); 565 if (InsertMenuItem(hmenu, nPos, TRUE, &menuItemInfo)) 566 { 567 nPos++; 568 menuItemInfo.wID++; 569 } 570 menuItemInfo.fState &= ~MFS_CHECKED; 571 } 572 } 573 } 574 if ((dwFlags & TLMENUF_FORE) != 0) 575 { 576 currentItem = fCurrentEntry; 577 if (currentItem != NULL) 578 currentItem = currentItem->fNextEntry; 579 while (currentItem != NULL) 580 { 581 hResult = currentItem->GetToolTipText(punk, itemTextBuffer); 582 if (SUCCEEDED(hResult)) 583 { 584 FixAmpersands(itemTextBuffer); 585 TRACE("CTravelLog::InsertMenuEntries adding entry L\"%S\"/L\"%S\" with id %d\n", itemTextBuffer, menuItemInfo.dwTypeData, menuItemInfo.wID); 586 if (InsertMenuItem(hmenu, nPos, TRUE, &menuItemInfo)) 587 { 588 nPos++; 589 menuItemInfo.wID++; 590 } 591 } 592 currentItem = currentItem->fNextEntry; 593 } 594 } 595 return S_OK; 596 } 597 598 HRESULT STDMETHODCALLTYPE CTravelLog::Clone(ITravelLog **pptl) 599 { 600 if (pptl == NULL) 601 return E_POINTER; 602 *pptl = NULL; 603 // duplicate the log 604 UNIMPLEMENTED; 605 return E_NOTIMPL; 606 } 607 608 DWORD STDMETHODCALLTYPE CTravelLog::CountEntries(IUnknown *punk) 609 { 610 if (punk == NULL) 611 return E_INVALIDARG; 612 return fEntryCount; 613 } 614 615 HRESULT STDMETHODCALLTYPE CTravelLog::Revert() 616 { 617 // remove the current entry? 618 UNIMPLEMENTED; 619 return E_NOTIMPL; 620 } 621 622 HRESULT CreateTravelLog(REFIID riid, void **ppv) 623 { 624 CComObject<CTravelLog> *theTravelLog; 625 HRESULT hResult; 626 627 if (ppv == NULL) 628 return E_POINTER; 629 *ppv = NULL; 630 ATLTRY (theTravelLog = new CComObject<CTravelLog>); 631 if (theTravelLog == NULL) 632 return E_OUTOFMEMORY; 633 hResult = theTravelLog->QueryInterface(riid, reinterpret_cast<void **>(ppv)); 634 if (FAILED_UNEXPECTEDLY(hResult)) 635 { 636 delete theTravelLog; 637 return hResult; 638 } 639 hResult = theTravelLog->Initialize(); 640 if (FAILED_UNEXPECTEDLY(hResult)) 641 return hResult; 642 return S_OK; 643 } 644