home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / mfc / src / oledoc1.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-06-16  |  21.3 KB  |  880 lines

  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992-1998 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Microsoft Foundation Classes Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10.  
  11. #include "stdafx.h"
  12.  
  13. #ifdef AFX_OLE_SEG
  14. #pragma code_seg(AFX_OLE_SEG)
  15. #endif
  16.  
  17. #ifdef _DEBUG
  18. #undef THIS_FILE
  19. static char THIS_FILE[] = __FILE__;
  20. #endif
  21.  
  22. #define new DEBUG_NEW
  23.  
  24. /////////////////////////////////////////////////////////////////////////////
  25. // COleDocument - enables both server and client
  26.  
  27. COleDocument::COleDocument()
  28. {
  29.     ASSERT(m_viewList.IsEmpty());
  30.     ASSERT(m_docItemList.IsEmpty());
  31.  
  32. #ifdef _DEBUG
  33.     // check for common mistake of not initializing OLE libraries before
  34.     //  creating an OLE document.
  35.     LPMALLOC lpMalloc = NULL;
  36.     if (::CoGetMalloc(MEMCTX_TASK, &lpMalloc) != S_OK)
  37.     {
  38.         TRACE0("Warning: CoGetMalloc(MEMCTX_TASK, ...) failed --\n");
  39.         TRACE0("\tperhaps AfxOleInit() has not been called.\n");
  40.     }
  41.     RELEASE(lpMalloc);
  42. #endif
  43.  
  44.     m_dwNextItemNumber = 1; // item number for first item in document
  45.     m_bLastVisible = FALSE;
  46.  
  47.     m_bRemember = TRUE;
  48.     m_bSameAsLoad = TRUE;
  49.     m_lpRootStg = NULL;
  50.     m_ptd = NULL;       // default to screen target device
  51.     m_bCompoundFile = FALSE;
  52.  
  53.     AfxOleLockApp();
  54. }
  55.  
  56. COleDocument::~COleDocument()
  57. {
  58.     ASSERT_VALID(this);
  59.  
  60. #ifdef _DEBUG
  61.     if (!m_docItemList.IsEmpty())
  62.         TRACE1("Warning: destroying COleDocument with %d doc items.\n",
  63.             m_docItemList.GetCount());
  64. #endif
  65.  
  66.     // remove all doc-items from the list before shutting down the storage
  67.     POSITION pos = GetStartPosition();
  68.     while (pos != NULL)
  69.     {
  70.         CDocItem* pItem = GetNextItem(pos);
  71.         ASSERT(pItem != NULL);
  72.         delete pItem;
  73.     }
  74.  
  75.     // release the hold on the document storage
  76.     RELEASE(m_lpRootStg);
  77.     CoTaskMemFree(m_ptd);
  78.  
  79.     AfxOleUnlockApp();
  80. }
  81.  
  82. /////////////////////////////////////////////////////////////////////////////
  83. // DocItem management
  84.  
  85. void COleDocument::AddItem(CDocItem* pItem)
  86. {
  87.     // don't do an ASSERT_VALID until after we've added it !
  88.     ASSERT_KINDOF(CDocItem, pItem);
  89.  
  90.     ASSERT(pItem->m_pDocument == NULL);     // not yet initialized
  91.     m_docItemList.AddTail(pItem);
  92.     pItem->m_pDocument = this;
  93.  
  94.     ASSERT_VALID(pItem);    // now it must be valid
  95. }
  96.  
  97. void COleDocument::RemoveItem(CDocItem* pItem)
  98. {
  99.     ASSERT_VALID(pItem);    // must be valid before detach
  100.     ASSERT_KINDOF(CDocItem, pItem);
  101.     ASSERT(pItem->m_pDocument == this);     // formerly attached
  102.  
  103.     ASSERT(m_docItemList.Find(pItem) != NULL);  // must be in list
  104.     m_docItemList.RemoveAt(m_docItemList.Find(pItem));
  105.     ASSERT(m_docItemList.Find(pItem) == NULL);  // must not be in list now
  106.     pItem->m_pDocument = NULL;
  107. }
  108.  
  109. POSITION COleDocument::GetStartPosition() const
  110. {
  111.     ASSERT_VALID(this);
  112.     return m_docItemList.GetHeadPosition();
  113. }
  114.  
  115. CDocItem* COleDocument::GetNextItem(POSITION& pos) const
  116. {
  117.     // handle special case of !pos -- makes enumeration code smaller
  118.     if (pos == NULL)
  119.         return NULL;
  120.  
  121.     // otherwise get next item from list
  122.     ASSERT_VALID(this);
  123.     CDocItem* pItem = (CDocItem*)m_docItemList.GetNext(pos);
  124.     ASSERT(pItem != NULL);
  125.     ASSERT_KINDOF(CDocItem, pItem);
  126.     ASSERT(pItem->m_pDocument == this);     // must be ours
  127.     return pItem;
  128. }
  129.  
  130. CDocItem*
  131. COleDocument::GetNextItemOfKind(POSITION& pos, CRuntimeClass* pClass) const
  132. {
  133.     while (pos != NULL)
  134.     {
  135.         CDocItem* pItem = GetNextItem(pos);
  136.         ASSERT_VALID(pItem);
  137.         if (pItem->IsKindOf(pClass))
  138.             return pItem;
  139.     }
  140.     return NULL;    // no suitable item found
  141. }
  142.  
  143. COleClientItem* COleDocument::GetNextClientItem(POSITION& pos) const
  144. {
  145.     COleClientItem *pItem =
  146.         (COleClientItem*)GetNextItemOfKind(pos, RUNTIME_CLASS(COleClientItem));
  147.     return pItem;
  148. }
  149.  
  150. COleServerItem* COleDocument::GetNextServerItem(POSITION& pos) const
  151. {
  152.     COleServerItem *pItem =
  153.         (COleServerItem*)GetNextItemOfKind(pos, RUNTIME_CLASS(COleServerItem));
  154.     return pItem;
  155. }
  156.  
  157. void COleDocument::DeleteContents()
  158. {
  159.     // deletes all COleClientItem objects in the doc item list
  160.     //  (Note: doesn't touch server items or other docitems)
  161.  
  162.     POSITION pos = GetStartPosition();
  163.     COleClientItem* pItem;
  164.     while ((pItem = GetNextClientItem(pos)) != NULL)
  165.     {
  166.         if (pItem->m_lpObject != NULL)
  167.         {
  168.             pItem->Release(OLECLOSE_NOSAVE);    // release OLE object
  169.             RemoveItem(pItem);  // disconnect from document
  170.             pItem->InternalRelease();   // may 'delete pItem'
  171.         }
  172.     }
  173. }
  174.  
  175. void COleDocument::SetPathName(LPCTSTR lpszPathName, BOOL bAddToMRU)
  176. {
  177.     USES_CONVERSION;
  178.  
  179.     CDocument::SetPathName(lpszPathName, bAddToMRU);
  180.  
  181.     // update all of the objects' host names
  182.     POSITION pos = GetStartPosition();
  183.     COleClientItem* pItem;
  184.     while ((pItem = GetNextClientItem(pos)) != NULL)
  185.     {
  186.         // update that item's host names
  187.         pItem->m_lpObject->SetHostNames(T2COLE(AfxGetAppName()),
  188.             T2COLE(m_strTitle));
  189.     }
  190. }
  191.  
  192. void COleDocument::Serialize(CArchive& ar)
  193. {
  194.     ASSERT_VALID(this);
  195.  
  196.     // serialize all items in the doc item list
  197.     if (ar.IsStoring())
  198.     {
  199.         DWORD dwCount = 0;
  200.         POSITION pos = GetStartPosition();
  201.         while (pos != NULL)
  202.         {
  203.             CDocItem* pDocItem = GetNextItem(pos);
  204.             ASSERT_VALID(pDocItem);
  205.  
  206.             // only count non-blank ones
  207.             if (!pDocItem->IsBlank())
  208.                 ++dwCount;
  209.         }
  210.         ar << dwCount;  // write count of objects
  211.  
  212.         // serialize all the items in the list
  213.         pos = GetStartPosition();
  214.         while (pos != NULL)
  215.         {
  216.             CDocItem* pDocItem = GetNextItem(pos);
  217.             ASSERT_VALID(pDocItem);
  218.  
  219.             // only write non-blank ones
  220.             if (!pDocItem->IsBlank())
  221.                 ar << pDocItem;
  222.         }
  223.     }
  224.     else
  225.     {
  226.         // read number of items in the file
  227.         DWORD dwCount;
  228.         ar >> dwCount;
  229.  
  230.         // read all of them into the list
  231.         while (dwCount--)
  232.         {
  233.             CDocItem* pDocItem;
  234.             ar >> pDocItem;     // as they are serialized, they are added!
  235.         }
  236.     }
  237. }
  238.  
  239. void COleDocument::CommitItems(BOOL bSuccess)
  240. {
  241.     // special 'Commit' phase for COleClientItem items
  242.     POSITION pos = GetStartPosition();
  243.     COleClientItem* pItem;
  244.     while ((pItem = GetNextClientItem(pos)) != NULL)
  245.     {
  246.         // calling CommitItem with FALSE causes the object to revert
  247.         //  to the original storage.  Calling CommitItem TRUE causes
  248.         //  the item to adopt the new storage created in the Serialize
  249.         //  function.
  250.         pItem->CommitItem(bSuccess);
  251.     }
  252. }
  253.  
  254. BOOL COleDocument::HasBlankItems() const
  255. {
  256.     ASSERT_VALID(this);
  257.  
  258.     POSITION pos = GetStartPosition();
  259.     while (pos != NULL)
  260.     {
  261.         CDocItem* pDocItem = GetNextItem(pos);
  262.         ASSERT_VALID(pDocItem);
  263.         if (pDocItem->IsBlank())
  264.             return TRUE;    // blank item found
  265.     }
  266.     return FALSE;   // no items found that were blank
  267. }
  268.  
  269. void COleDocument::UpdateModifiedFlag()
  270. {
  271.     ASSERT_VALID(this);
  272.  
  273.     POSITION pos = GetStartPosition();
  274.     COleClientItem* pItem;
  275.     while ((pItem = GetNextClientItem(pos)) != NULL)
  276.     {
  277.         if (pItem->IsModified())
  278.         {
  279.             SetModifiedFlag();
  280.             break;
  281.         }
  282.     }
  283. }
  284.  
  285. void COleDocument::PreCloseFrame(CFrameWnd* pFrameArg)
  286. {
  287.     ASSERT_VALID(this);
  288.     ASSERT_VALID(pFrameArg);
  289.  
  290.     // turn off redraw so the user doesn't see the deactivation happening
  291.     BOOL bSetRedraw = FALSE;
  292.     if (pFrameArg->GetStyle() & WS_VISIBLE)
  293.     {
  294.         pFrameArg->SendMessage(WM_SETREDRAW, (WPARAM)FALSE);
  295.         bSetRedraw = TRUE;
  296.     }
  297.  
  298.     // deactivate any inplace active items on this frame
  299.     COleClientItem* pItem = GetInPlaceActiveItem(pFrameArg);
  300.     if (pItem != NULL)
  301.     {
  302.         pItem->Deactivate();
  303.         pItem->Close(OLECLOSE_NOSAVE);
  304.     }
  305.  
  306.     // turn redraw back on
  307.     if (bSetRedraw)
  308.         pFrameArg->SendMessage(WM_SETREDRAW, (WPARAM)TRUE);
  309.  
  310.     // should not have any inplace active items
  311.     ASSERT(GetInPlaceActiveItem(pFrameArg) == NULL);
  312. }
  313.  
  314. BOOL COleDocument::SaveModified()
  315. {
  316.     // determine if necessary to discard changes
  317.     if (::InSendMessage())
  318.     {
  319.         POSITION pos = GetStartPosition();
  320.         COleClientItem* pItem;
  321.         while ((pItem = GetNextClientItem(pos)) != NULL)
  322.         {
  323.             ASSERT(pItem->m_lpObject != NULL);
  324.             SCODE sc = pItem->m_lpObject->IsUpToDate();
  325.             if (sc != OLE_E_NOTRUNNING && FAILED(sc))
  326.             {
  327.                 // inside inter-app SendMessage limits the user's choices
  328.                 CString name = m_strPathName;
  329.                 if (name.IsEmpty())
  330.                     VERIFY(name.LoadString(AFX_IDS_UNTITLED));
  331.  
  332.                 CString prompt;
  333.                 AfxFormatString1(prompt, AFX_IDP_ASK_TO_DISCARD, name);
  334.                 return AfxMessageBox(prompt, MB_OKCANCEL|MB_DEFBUTTON2,
  335.                     AFX_IDP_ASK_TO_DISCARD) == IDOK;
  336.             }
  337.         }
  338.     }
  339.  
  340.     // sometimes items change without a notification, so we have to
  341.     //  update the document's modified flag before calling
  342.     //  CDocument::SaveModified.
  343.     UpdateModifiedFlag();
  344.  
  345.     return CDocument::SaveModified();
  346. }
  347.  
  348. void COleDocument::OnShowViews(BOOL /*bVisible*/)
  349. {
  350.     // no default implementation
  351. }
  352.  
  353. void COleDocument::OnIdle()
  354. {
  355.     ASSERT_VALID(this);
  356.  
  357.     // determine if any visible views are on this document
  358.     BOOL bVisible = FALSE;
  359.     POSITION pos = GetFirstViewPosition();
  360.     while (pos != NULL)
  361.     {
  362.         CView* pView = GetNextView(pos);
  363.         ASSERT_VALID(pView);
  364.         CFrameWnd* pFrameWnd = pView->GetParentFrame();
  365.         ASSERT_VALID(pFrameWnd);
  366.         if (pFrameWnd->GetStyle() & WS_VISIBLE)
  367.         {
  368.             bVisible = TRUE;
  369.             break;
  370.         }
  371.     }
  372.  
  373.     // when state has changed, call OnShowViews
  374.     if (bVisible != m_bLastVisible)
  375.     {
  376.         OnShowViews(bVisible);
  377.         m_bLastVisible = bVisible;
  378.     }
  379. }
  380.  
  381. /////////////////////////////////////////////////////////////////////////////
  382. // COleDocument -> window mapping
  383.  
  384. CFrameWnd* COleDocument::GetFirstFrame()
  385. {
  386.     ASSERT_VALID(this);
  387.  
  388.     // get position of first view in the document
  389.     POSITION pos = GetFirstViewPosition();
  390.  
  391.     // get view at that position
  392.     CView* pView = GetNextView(pos);
  393.     if (pView == NULL)
  394.         return NULL;
  395.     ASSERT_VALID(pView);
  396.  
  397.     // return the first frame window that is a parent of that view
  398.     CFrameWnd* pFrameWnd = (CFrameWnd*)pView->GetParentFrame();
  399.     ASSERT_VALID(pFrameWnd);
  400.     ASSERT_KINDOF(CFrameWnd, pFrameWnd);
  401.     return pFrameWnd;
  402. }
  403.  
  404. /////////////////////////////////////////////////////////////////////////////
  405. // COleDocument helpers
  406.  
  407. LPMONIKER COleDocument::GetMoniker(OLEGETMONIKER /*nAssign*/)
  408. {
  409.     USES_CONVERSION;
  410.  
  411.     ASSERT_VALID(this);
  412.  
  413.     // no moniker for untitled documents
  414.     if (m_strPathName.IsEmpty())
  415.         return NULL;
  416.  
  417.     // return file moniker based on current path name
  418.     LPMONIKER lpMoniker;
  419.     CreateFileMoniker(T2COLE(m_strPathName), &lpMoniker);
  420.     return lpMoniker;
  421. }
  422.  
  423. LPOLEITEMCONTAINER COleDocument::GetContainer()
  424. {
  425.     // COleDocument doesn't support IOleClientSite::GetContainer
  426.  
  427.     return NULL;
  428. }
  429.  
  430. /////////////////////////////////////////////////////////////////////////////
  431. // 'Compound File' enabling in COleDocument
  432.  
  433. BOOL COleDocument::OnNewDocument()
  434. {
  435.     // call base class, which destroys all items
  436.     if (!CDocument::OnNewDocument())
  437.         return FALSE;
  438.  
  439.     // for file-based compound files, need to create temporary file
  440.     if (m_bCompoundFile && !m_bEmbedded)
  441.     {
  442.         // abort changes to the current docfile
  443.         RELEASE(m_lpRootStg);
  444.  
  445.         // create new temporary docfile
  446.         LPSTORAGE lpStorage;
  447.         SCODE sc = ::StgCreateDocfile(NULL, STGM_DELETEONRELEASE|
  448.             STGM_READWRITE|STGM_TRANSACTED|STGM_SHARE_EXCLUSIVE|STGM_CREATE,
  449.             0, &lpStorage);
  450.         if (sc != S_OK)
  451.             return FALSE;
  452.  
  453.         ASSERT(lpStorage != NULL);
  454.         m_lpRootStg = lpStorage;
  455.     }
  456.  
  457.     return TRUE;
  458. }
  459.  
  460. BOOL COleDocument::OnOpenDocument(LPCTSTR lpszPathName)
  461. {
  462.     USES_CONVERSION;
  463.  
  464.     ASSERT(lpszPathName == NULL || AfxIsValidString(lpszPathName));
  465.  
  466.     // just use default implementation if 'docfile' not enabled
  467.     if (!m_bCompoundFile && m_lpRootStg == NULL)
  468.     {
  469.         ASSERT(lpszPathName != NULL);
  470.         return CDocument::OnOpenDocument(lpszPathName);
  471.     }
  472.  
  473.     if (IsModified())
  474.         TRACE0("Warning: OnOpenDocument replaces an unsaved document.\n");
  475.  
  476.     // abort changes to current docfile
  477.     if (lpszPathName != NULL)
  478.     {
  479.         DeleteContents();
  480.         RELEASE(m_lpRootStg);
  481.     }
  482.     SetModifiedFlag();  // dirty during de-serialize
  483.  
  484.     BOOL bResult = FALSE;
  485.     TRY
  486.     {
  487.         if (m_lpRootStg == NULL)
  488.         {
  489.             LPCOLESTR lpsz = T2COLE(lpszPathName);
  490.  
  491.             // use STGM_CONVERT if necessary
  492.             SCODE sc;
  493.             LPSTORAGE lpStorage = NULL;
  494.             if (StgIsStorageFile(lpsz) == S_FALSE)
  495.             {
  496.                 // convert existing storage file
  497.                 sc = StgCreateDocfile(lpsz, STGM_READWRITE|
  498.                     STGM_TRANSACTED|STGM_SHARE_DENY_WRITE|STGM_CONVERT,
  499.                     0, &lpStorage);
  500.                 if (FAILED(sc) || lpStorage == NULL)
  501.                     sc = StgCreateDocfile(lpsz, STGM_READ|
  502.                         STGM_TRANSACTED|STGM_CONVERT, 0, &lpStorage);
  503.             }
  504.             else
  505.             {
  506.                 // open new storage file
  507.                 sc = StgOpenStorage(lpsz, NULL,
  508.                     STGM_READWRITE|STGM_TRANSACTED|STGM_SHARE_DENY_WRITE,
  509.                     0, 0, &lpStorage);
  510.                 if (FAILED(sc) || lpStorage == NULL)
  511.                     sc = StgOpenStorage(lpsz, NULL,
  512.                         STGM_READ|STGM_TRANSACTED, 0, 0, &lpStorage);
  513.             }
  514.             if (FAILED(sc))
  515.                 AfxThrowOleException(sc);
  516.  
  517.             ASSERT(lpStorage != NULL);
  518.             m_lpRootStg = lpStorage;
  519.         }
  520.  
  521.         // use helper to read document from storage
  522.         LoadFromStorage();
  523.  
  524.         SetModifiedFlag(FALSE); // start off with unmodified
  525.         bResult = TRUE;
  526.     }
  527.     CATCH_ALL(e)
  528.     {
  529.         DeleteContents();   // removed failed contents
  530.         RELEASE(m_lpRootStg);
  531.  
  532.         // if not file-based load, return exceptions to the caller
  533.         if (lpszPathName == NULL)
  534.         {
  535.             THROW_LAST();
  536.             ASSERT(FALSE);  // not reached
  537.         }
  538.  
  539.         TRY
  540.         {
  541.             ReportSaveLoadException(lpszPathName, e,
  542.                 FALSE, AFX_IDP_FAILED_TO_OPEN_DOC);
  543.         }
  544.         END_TRY
  545.         DELETE_EXCEPTION(e);
  546.     }
  547.     END_CATCH_ALL
  548.  
  549.     return bResult;
  550. }
  551.  
  552. BOOL COleDocument::OnSaveDocument(LPCTSTR lpszPathName)
  553.     // lpszPathName must be fully qualified
  554. {
  555.     USES_CONVERSION;
  556.  
  557.     ASSERT(lpszPathName == NULL || AfxIsValidString(lpszPathName));
  558.  
  559.     // use default implementation if 'docfile' not enabled
  560.     if (!m_bCompoundFile && m_lpRootStg == NULL)
  561.     {
  562.         ASSERT(lpszPathName != NULL);
  563.         return CDocument::OnSaveDocument(lpszPathName);
  564.     }
  565.  
  566.     LPSTORAGE lpOrigStg = NULL;
  567.     if (lpszPathName != NULL)
  568.         m_bSameAsLoad = AfxComparePath(m_strPathName, lpszPathName);
  569.  
  570.     BOOL bResult = FALSE;
  571.     TRY
  572.     {
  573.         // open new root storage if necessary
  574.         if (lpszPathName != NULL && !m_bSameAsLoad)
  575.         {
  576.             // temporarily detach current storage
  577.             lpOrigStg = m_lpRootStg;
  578.             m_lpRootStg = NULL;
  579.  
  580.             LPSTORAGE lpStorage;
  581.             SCODE sc = ::StgCreateDocfile(T2COLE(lpszPathName),
  582.                 STGM_READWRITE|STGM_TRANSACTED|STGM_SHARE_DENY_WRITE|STGM_CREATE,
  583.                 0, &lpStorage);
  584.             if (sc != S_OK)
  585.                 AfxThrowOleException(sc);
  586.  
  587.             ASSERT(lpStorage != NULL);
  588.             m_lpRootStg = lpStorage;
  589.         }
  590.         ASSERT(m_lpRootStg != NULL);
  591.  
  592.         // use helper to save to root storage
  593.         SaveToStorage();
  594.  
  595.         if (lpszPathName != NULL)
  596.         {
  597.             // commit each of the items
  598.             CommitItems(m_bRemember && !m_bSameAsLoad);
  599.  
  600.             // mark document as clean if remembering the storage
  601.             if (m_bRemember)
  602.                 SetModifiedFlag(FALSE);
  603.  
  604.             // remember correct storage or release save copy as storage
  605.             if (!m_bSameAsLoad)
  606.             {
  607.                 if (m_bRemember)
  608.                 {
  609.                     // Save As case -- m_stgRoot is new storage, forget old storage
  610.                     lpOrigStg->Release();
  611.                 }
  612.                 else
  613.                 {
  614.                     // Save Copy As case -- m_stgRoot should hook up to m_stgOrig.
  615.                     m_lpRootStg->Release();
  616.                     m_lpRootStg = lpOrigStg;
  617.                 }
  618.             }
  619.         }
  620.  
  621.         bResult = TRUE;
  622.     }
  623.     CATCH_ALL(e)
  624.     {
  625.         if (lpOrigStg != NULL)
  626.         {
  627.             // save as failed: abort new storage, and re-attach original
  628.             RELEASE(m_lpRootStg);
  629.             m_lpRootStg = lpOrigStg;
  630.         }
  631.  
  632.         if (lpszPathName == NULL)
  633.         {
  634.             THROW_LAST();
  635.             ASSERT(FALSE);  // not reached
  636.         }
  637.  
  638.         TRY
  639.         {
  640.             ReportSaveLoadException(lpszPathName, e,
  641.                 TRUE, AFX_IDP_FAILED_TO_SAVE_DOC);
  642.         }
  643.         END_TRY
  644.         DELETE_EXCEPTION(e);
  645.     }
  646.     END_CATCH_ALL
  647.  
  648.     // cleanup
  649.     m_bSameAsLoad = TRUE;
  650.     m_bRemember = TRUE;
  651.  
  652.     return bResult;
  653. }
  654.  
  655. void COleDocument::OnCloseDocument()
  656. {
  657.     // close the document without deleting the memory
  658.     BOOL bAutoDelete = m_bAutoDelete;
  659.     m_bAutoDelete = FALSE;
  660.     CDocument::OnCloseDocument();
  661.  
  662.     // release storage since document has been closed
  663.     RELEASE(m_lpRootStg);
  664.  
  665.     // delete the document if necessary
  666.     if (bAutoDelete)
  667.         delete this;
  668. }
  669.  
  670. /////////////////////////////////////////////////////////////////////////////
  671. // Helpers for saving to IStorage based files
  672. //  (these are used in the 'docfile' implementation as well as for servers)
  673.  
  674. void COleDocument::SaveToStorage(CObject* pObject)
  675. {
  676.     ASSERT(m_lpRootStg != NULL);
  677.  
  678.     // create Contents stream
  679.     COleStreamFile file;
  680.     CFileException fe;
  681.     if (!file.CreateStream(m_lpRootStg, _T("Contents"),
  682.             CFile::modeReadWrite|CFile::shareExclusive|CFile::modeCreate, &fe))
  683.     {
  684.         if (fe.m_cause == CFileException::fileNotFound)
  685.             AfxThrowArchiveException(CArchiveException::badSchema);
  686.         else
  687.             AfxThrowFileException(fe.m_cause, fe.m_lOsError);
  688.     }
  689.  
  690.     // save to Contents stream
  691.     CArchive saveArchive(&file, CArchive::store | CArchive::bNoFlushOnDelete);
  692.     saveArchive.m_pDocument = this;
  693.     saveArchive.m_bForceFlat = FALSE;
  694.  
  695.     TRY
  696.     {
  697.         // save the contents
  698.         if (pObject != NULL)
  699.             pObject->Serialize(saveArchive);
  700.         else
  701.             Serialize(saveArchive);
  702.         saveArchive.Close();
  703.         file.Close();
  704.  
  705.         // commit the root storage
  706.         SCODE sc = m_lpRootStg->Commit(STGC_ONLYIFCURRENT);
  707.         if (sc != S_OK)
  708.             AfxThrowOleException(sc);
  709.     }
  710.     CATCH_ALL(e)
  711.     {
  712.         file.Abort();   // will not throw an exception
  713.         CommitItems(FALSE); // abort save in progress
  714.         NO_CPP_EXCEPTION(saveArchive.Abort());
  715.         THROW_LAST();
  716.     }
  717.     END_CATCH_ALL
  718. }
  719.  
  720. void COleDocument::LoadFromStorage()
  721. {
  722.     ASSERT(m_lpRootStg != NULL);
  723.  
  724.     // open Contents stream
  725.     COleStreamFile file;
  726.     CFileException fe;
  727.     if (!file.OpenStream(m_lpRootStg, _T("Contents"),
  728.             CFile::modeRead|CFile::shareExclusive, &fe) &&
  729.         !file.CreateStream(m_lpRootStg, _T("Contents"),
  730.             CFile::modeRead|CFile::shareExclusive|CFile::modeCreate, &fe))
  731.     {
  732.         if (fe.m_cause == CFileException::fileNotFound)
  733.             AfxThrowArchiveException(CArchiveException::badSchema);
  734.         else
  735.             AfxThrowFileException(fe.m_cause, fe.m_lOsError);
  736.     }
  737.  
  738.     // load it with CArchive (loads from Contents stream)
  739.     CArchive loadArchive(&file, CArchive::load | CArchive::bNoFlushOnDelete);
  740.     loadArchive.m_pDocument = this;
  741.     loadArchive.m_bForceFlat = FALSE;
  742.  
  743.     TRY
  744.     {
  745.         if (file.GetLength() != 0)
  746.             Serialize(loadArchive);     // load main contents
  747.         loadArchive.Close();
  748.         file.Close();
  749.     }
  750.     CATCH_ALL(e)
  751.     {
  752.         file.Abort();   // will not throw an exception
  753.         DeleteContents();   // removed failed contents
  754.         NO_CPP_EXCEPTION(loadArchive.Abort());
  755.         THROW_LAST();
  756.     }
  757.     END_CATCH_ALL
  758. }
  759.  
  760. /////////////////////////////////////////////////////////////////////////////
  761. // COleDocument diagnostics
  762.  
  763. #ifdef _DEBUG
  764. void COleDocument::AssertValid() const
  765. {
  766.     CDocument::AssertValid();
  767.  
  768.     ASSERT(m_ptd == NULL || AfxIsValidAddress(m_ptd, (size_t)m_ptd->tdSize, FALSE));
  769.     ASSERT_VALID(&m_docItemList);
  770.     ASSERT(!m_bEmbedded || m_strPathName.IsEmpty());
  771. }
  772.  
  773. void COleDocument::Dump(CDumpContext& dc) const
  774. {
  775.     CDocument::Dump(dc);
  776.  
  777.     dc << "with " << m_docItemList.GetCount() << " doc items";
  778.     dc << "\nm_dwNextItemNumber = " << m_dwNextItemNumber;
  779.     dc << "\nm_bLastVisible = " << m_bLastVisible;
  780.     dc << "\nm_bEmbedded = " << m_bEmbedded;
  781.     dc << "\nm_lpRootStg = " << m_lpRootStg;
  782.     dc << "\nm_bSameAsLoad = " << m_bSameAsLoad;
  783.     dc << "\nm_bRemember = " << m_bRemember;
  784.     dc << "\nm_ptd = " << m_ptd;
  785.  
  786.     dc << "\n";
  787. }
  788. #endif //_DEBUG
  789.  
  790. /////////////////////////////////////////////////////////////////////////////
  791. // CDocItem
  792.  
  793. CDocItem::CDocItem()
  794. {
  795.     m_pDocument = NULL;
  796. }
  797.  
  798. CDocItem::~CDocItem()
  799. {
  800.     ASSERT(m_pDocument == NULL);    // must be detached from document
  801. }
  802.  
  803. void CDocItem::Serialize(CArchive& ar)
  804. {
  805.     if (ar.IsStoring())
  806.     {
  807.         ASSERT_VALID(m_pDocument);
  808.         // nothing to do, there is no data
  809.     }
  810.     else
  811.     {
  812.         // if no document connected yet, attach it from the archive
  813.         if (m_pDocument == NULL)
  814.         {
  815.             COleDocument* pContainerDoc = (COleDocument*)ar.m_pDocument;
  816.             ASSERT_VALID(pContainerDoc);
  817.             ASSERT_KINDOF(COleDocument, pContainerDoc);
  818.             pContainerDoc->AddItem(this);
  819.             ASSERT(pContainerDoc == m_pDocument);
  820.         }
  821.     }
  822.     // perform ASSERT_VALID at the end because COleServerItem::AssertValid
  823.     // checks the validity of the m_pDocument pointer
  824.     ASSERT_VALID(this);
  825. }
  826.  
  827. BOOL CDocItem::IsBlank() const
  828. {
  829.     // by default, a CDocItem is not blank. COleClientItem is sometimes blank!
  830.     //  (a COleServerItem is blank by default)
  831.     return FALSE;
  832. }
  833.  
  834. /////////////////////////////////////////////////////////////////////////////
  835. // CDocItem diagnostics
  836.  
  837. #ifdef _DEBUG
  838. void CDocItem::AssertValid() const
  839. {
  840.     CObject::AssertValid();
  841.     if (m_pDocument != NULL)
  842.         m_pDocument->AssertValid();
  843. }
  844.  
  845. void CDocItem::Dump(CDumpContext& dc) const
  846. {
  847.     CCmdTarget::Dump(dc);
  848.  
  849.     dc << "m_pDocument = " << (void*)m_pDocument;
  850.     dc << "\n";
  851. }
  852. #endif //_DEBUG
  853.  
  854. /////////////////////////////////////////////////////////////////////////////
  855. // Inline function declarations expanded out-of-line
  856.  
  857. #ifndef _AFX_ENABLE_INLINES
  858.  
  859. // expand inlines for OLE general APIs
  860. static char _szAfxOleInl[] = "afxole.inl";
  861. #undef THIS_FILE
  862. #define THIS_FILE _szAfxOleInl
  863. #define _AFXOLE_INLINE
  864. #include "afxole.inl"
  865.  
  866. #endif //!_AFX_ENABLE_INLINES
  867.  
  868. #ifdef AFX_INIT_SEG
  869. #pragma code_seg(AFX_INIT_SEG)
  870. #endif
  871.  
  872. IMPLEMENT_SERIAL(CDocItem, CCmdTarget, 0)
  873. IMPLEMENT_DYNAMIC(COleDocument, CDocument)
  874.  
  875. // These IMPLEMENT_DYNAMICs here for .OBJ granularity reasons.
  876. IMPLEMENT_DYNAMIC(COleClientItem, CDocItem)
  877. IMPLEMENT_DYNAMIC(COleServerItem, CDocItem)
  878.  
  879. /////////////////////////////////////////////////////////////////////////////
  880.