SAMPLE: Corrections for Inside OLE 2 Sample Code

ID: Q113255

The information in this article applies to:

SUMMARY

The sample code for the book "Inside OLE 2," by Kraig Brockschmidt (Microsoft Press) was originally written to be compatible with OLE 2.0. This code has been updated to be compatible with OLE 2.01, and also to fix some problems in the original code. This article describes the changes that were made in updating the code.

MORE INFORMATION

The following file is available for download from the Microsoft Software Library:

 ~ INOLE2.EXE (size: 2107743 bytes) 

For more information about downloading files from the Microsoft Software Library, please see the following article in the Microsoft Knowledge Base:

   ARTICLE-ID: Q119591
   TITLE     : How to Obtain Microsoft Support Files from
               Online Services

NOTE: This archive contains subdirectories, be sure to use the -d command line switch during extraction.

Below is a quick list of the problems that required changes:

Updated Sample Code Available

The INOLE2 Software Library sample contains a complete source tree for updated "Inside OLE 2" sample code. All of the changes described in this article have already been made to the INOLE2 code.

The easiest way to update all of the "Inside OLE 2" samples is to get a completely new source tree from INOLE2, and then rebuild the samples from scratch under OLE 2.01. This article can then be used as a reference, explaining what changes were made and why there were necessary.

BOOKUI.DLL

If the original source code to the Inside OLE 2 samples is updated according to this article, and the samples are then rebuilt to be compatible with OLE 2.01, it will also be necessary to rebuild an OLE 2.01 version of the OLE2UI library for the samples to use. To do so, build a library called BOOKUI.DLL according to the instructions in the OLE 2.01 SDK.

Use this new DLL to replace the INOLE2\BUILD\BOOKUI.DLL file included with the book.

If the samples are rebuilt from the INOLE2 Software Library sample, it is not necessary to manually rebuild BOOKUI.DLL, because INOLE2 already includes an updated version of BOOKUI.DLL.

1. Compiler Errors on IViewObject::Draw()

Problem:

The prototype for IViewObject::Draw() changed between OLE 2.0 and OLE 2.01. In 2.0 the lprcBounds and lprcWBounds parameters were prototyped as "const LPRECTL"; in 2.01 they are prototyped as "LPCRECTL". This change causes the HCosmo (Chapter 11) and Polyline (Chapters 11 and 16) sample applications to fail during compilation under 2.01.

Solution:

To correct this error, update all references to IViewObject::Draw() in the Inside OLE 2 sample code by changing const LPRECTL to LPCRECTL.

This change will be necessary in the following files:

   interfac\iviewobj.cpp
   interfac\iviewobj.h

   chap11\hcosmo\hcosmo.h
   chap11\hcosmo\iviewobj.cpp

   chap11\polyline\iviewobj.cpp
   chap11\polyline\polyline.h

   chap16\polyline\iviewobj.cpp
   chap16\polyline\polyline.h

2. Compiler Errors on OleUIAddVerbMenu()

Problem:

The prototype for OleUIAddVerbMenu() changed between OLE 2.0 and OLE 2.01. The OLE2UI library version OLE 2.01 added an additional parameter to OleUIAddVerbMenu(). This new parameter is an integer value called "idVerbMax", and it indicates the largest number the container allows for a verb menu item identifier. The idVerbMax parameter immediately follows the idVerbMin parameter.

The Patron sample applications were written for the OLE 2.0 version of the user interface library, which did not include this new parameter. Consequently, these samples do not compile with the OLE 2.01 header files. This difference affects the versions of Patron found in Chapters 9, 12, 13, 14, and 15.

Solution:

To correct this problem, perform the following two steps:

1. Build an OLE 2.01 version of BOOKUI.DLL, as described in problem 1

   above.

2. Update all calls to OleUIAddVerbMenu() in Patron by passing "ID_VERBMAX"
   (already defined in RESOURCE.H) as the idVerbMax parameter. For example,
   in the function CPage::FQueryObjectSelected(), change the following

      OleUIAddVerbMenu(NULL, NULL, hMenu, MENUPOS_OBJECT
          , IDM_VERBMIN, FALSE, 0, &hMenuTemp);

   to read as follows:

      OleUIAddVerbMenu(NULL, NULL, hMenu, MENUPOS_OBJECT
          , IDM_VERBMIN, IDM_VERBMAX, FALSE, 0, &hMenuTemp);

   This change will be necessary in the functions
   CPage::FQueryObjectSelected() and CTenant::AddVerbMenu() in each of the
   following files:

      chap09\patron\page.cpp
      chap09\patron\tenant.cpp

      chap12\patron\page.cpp
      chap12\patron\tenant.cpp

      chap13\patron\page.cpp
      chap13\patron\tenant.cpp

      chap14\patron\page.cpp
      chap14\patron\tenant.cpp

      chap15\patron\page.cpp
      chap15\patron\tenant.cpp

3. Compiler Errors on OleStdGetObjectDescriptorFromOleObject()

Problem:

The prototype for OleStdGetObjectDescriptorFromOleObject() changed between OLE 2.0 and OLE 2.01. Version 2.01 of the OLE2UI library added an additional parameter to OleStdGetObjectDescriptorFromOleObject(). This new parameter, "lpSizelHim", is an LPSIZEL value and points to a structure that indicates the dimensions of the object. The lpSizelHim parameter was added to the end of the existing parameter list.

The Patron sample applications were written for the OLE 2.0 version of the user interface library, which did not include this new parameter. Consequently, these samples do not compile with the OLE 2.01 header files. This difference affects the versions of Patron in Chapters 9, 12, 13, 14, and 15.

Solution:

To correct this problem, perform the following two steps:

1. Build an OLE 2.01 version of BOOKUI.DLL, as described in problem 1

   above.

2a. To quickly solve the problem, update all calls to
    OleStdGetObjectDescriptorFromOleObject() in Patron by passing NULL as
    the lpSizelHim parameter.

2b. For a more complete solution to the problem, first declare a local
    variable:

       SIZEL   szl;

    Then replace the code:

       stm.hGlobal=OleStdGetObjectDescriptorDataFromOleObject
           (m_pIOleObject, NULL, m_fe.dwAspect, ptl);

    with the code:

       SETSIZEL(szl, (10*(m_rcl.right-m_rcl.left))
           , (10 * (m_rcl.bottom-m_rcl.top)));

       stm.hGlobal=OleStdGetObjectDescriptorDataFromOleObject
           (m_pIOleObject, NULL, m_fe.dwAspect, ptl, &szl);

    This code correctly computes the size of the object and then stores
    those extents into the object descriptor.

These changes will be necessary in the function CTenant::CopyEmbeddedObject() in each of the following files:

   chap09\patron\tenant.cpp
   chap12\patron\tenant.cpp
   chap13\patron\tenant.cpp
   chap14\patron\tenant.cpp
   chap15\patron\tenant.cpp

These changes will also be necessary in the function CTenant::CopyLinkedObject(), found in TENANT.CPP in Chapters 12, 13, 14, and 15.

4. Compiler Errors on IDS_CLOSE

Problem:

The OLE2UI.H header file shipped with OLE 2.01 defines a symbol IDS_CLOSE. This symbol causes a conflict with a symbol of the same name defined in the Cosmo samples.

This conflict affects the versions of Cosmo in Chapters 10, 13, 14, and 16. The files affected are COSMO.RC and RESOURCE.H.

Solution:

To correct the problem, rename IDS_CLOSE in RESOURCE.H to IDS_CLOSE2, and change the single occurrence of IDS_CLOSE in COSMO.RC to IDS_CLOSE2 in each chapter. These changes are necessary in the following files:

   chap10\cosmo\cosmo.rc
   chap10\cosmo\resource.h

   chap13\cosmo\cosmo.rc
   chap13\cosmo\resource.h

   chap14\cosmo\cosmo.rc
   chap14\cosmo\resource.h

   chap16\cosmo\cosmo.rc
   chap16\cosmo\resource.h

5. Linker Fails with CLASSLIB Sample

Problem:

There is an error in the FILES.LST file in the CLASSLIB directory of the sample code. The first entry in this file, "cstrtable.obj", is a valid long filename under Windows NT, but the file created during compilation is actually "cstrtabl.obj" (no "e" on table). This causes no problems with a 16-bit compiler, because the extra "e" is ignored. However, it will cause an error with a 32-bit compiler.

Solution:

To solve the problem, remove the "e", thus changing "cstrtable.obj" to "cstrtabl.obj".

6. Compiler Errors Referencing GETICON.H

Problem:

In OLE 2.0, container applications must include the file GETICON.H, which is supplied with the OLE Software Development Kit (SDK) version 2.0. In OLE 2.01, applications no longer need to include GETICON.H, because the information in that file has been moved to other header files. Because it is no longer needed, GETICON.H is not included with the OLE 2.01 SDK.

The Patron sample applications of Chapters 14 and 15 were written for OLE 2.0, so they include GETICON.H. Because this file is no longer available, these versions of Patron fail to compile with the OLE 2.01 SDK.

Solution:

To correct the problem, comment out or delete the line "#include <geticon.h>" in TENANT.CPP. This change is necessary in the following files:

   chap14\patron\tenant.cpp
   chap15\patron\tenant.cpp

7. Incorrect Prototypes for LibMain() and WEP()

Problem:

The LibMain() and WEP() functions in all of the DLL samples are prototyped incorrectly. These prototypes cause errors when using some compilers (for example, Borland C++ 4.0); they do not cause problems with other compilers (for example, Microsoft Visual C++ versions 1.0 and 1.5).

Solution:

The DLL samples in Inside OLE 2 implement the LibMain() and WEP() functions as follows:

   HANDLE FAR PASCAL LibMain(HANDLE hInstance, WORD wDataSeg
       , WORD cbHeapSize, LPSTR lpCmdLine)
       {
       ...

       return hInstance;
       }

   void FAR PASCAL WEP(int bSystemExit)
       {
       return;
       }

To match the Windows SDK specifications, both of these functions should return an int:

   int FAR PASCAL LibMain(HANDLE hInstance, WORD wDataSeg
       , WORD cbHeapSize, LPSTR lpCmdLine)
       {
       ...

       return (int)hInstance;
       }

   int FAR PASCAL WEP(int bSystemExit)
       {
       return 0;
       }

To solve this problem, make the changes above to all occurrences of LibMain() and WEP() in the Inside OLE 2 sample code.

8. Patron GP Faults During Activate As

Summary:

The Patron sample applications from Chapters 14 and 15 support the OLE 2 Convert dialog box. Choosing Activate As from this dialog box sometimes causes a GP fault.

Problem:

The problem lies in the way that the CTenant::Close() function uses the fReopen flag. When Close() determines that there are no references to the tenant IStorage, it normally resets the internal state of the tenant to default values. As part of this process, it sets the member m_pIStorage to NULL. However, if fReopen is TRUE, this assignment to NULL is skipped.

This can lead to situations where m_pIStorage is non-NULL but the storage itself has been destroyed. Thus m_pIStorage is an invalid pointer, and trying to call through it causes a GP fault.

To correct this problem, ensure that the m_pIStorage variable is set to NULL any time its reference count is 0 (zero). This correction involves removing a condition in the CTenant::Close() function, which is found in the Patron source file TENANT.CPP.

Solution:

To correct this problem, edit the code to CTenant::Close() as follows:

Where you see the lines

   if (!fReopen)
       m_pIStorage=NULL;

remove the "if" statement, leaving:

   m_pIStorage=NULL;

The fReopen flag was used in an attempt to provide some optimization when performing Activate As, but this flag is not necessary. The updated Inside OLE 2 sample code referenced above has removed the flag entirely. This affects the TENANT.CPP, TENANT.H, and PAGE.CPP source files of the PATRON samples in Chapters 14 and 15.

9. Patron's Convert To Fails

Summary:

The Patron sample applications from Chapters 14 and 15 support the OLE 2 Convert dialog box. When Convert To is chosen from this dialog box, PATRON fails to perform the conversion properly.

Problem:

The problem is that CPage::FConvertObject() releases the page's IStorage pointer without committing it. Because Patron uses transacted storage, this discards all changes, including the conversion that just happened.

The code in error (listed on Page 817 of Inside OLE 2) is as follows:

   if ((CF_SELECTCONVERTTO & ct.dwFlags)
       && !IsEqualCLSID(ct.clsid, ct.clsidNew))
       {
       LPSTORAGE   pIStorage;

       //This should be the only close necessary.
       m_pTenantCur->RectGet(&rcl, FALSE);
       m_pTenantCur->StorageGet(&pIStorage);
       m_pTenantCur->Close(FALSE, FALSE);

       hr=OleStdDoConvert(pIStorage, ct.clsidNew);
       pIStorage->Release();

This code does not commit the changes to the storage affected by OleStdDoConvert before releasing pIStorage.

Solution:

To correct this problem, include a call to pIStorage->Commit() before releasing the storage:

   if ((CF_SELECTCONVERTTO & ct.dwFlags)
       && !IsEqualCLSID(ct.clsid, ct.clsidNew))
       {
       LPSTORAGE   pIStorage;

       //This should be the only close necessary.
       m_pTenantCur->RectGet(&rcl, FALSE);
       m_pTenantCur->StorageGet(&pIStorage);
       m_pTenantCur->Close(FALSE, FALSE);

       hr=OleStdDoConvert(pIStorage, ct.clsidNew);

       pIStorage->Commit(STGC_ONLYIFCURRENT);
       pIStorage->Release();

   ...

10. CLASSLIB String Allocations Fail with Some Compilers

Summary:

The Inside OLE 2 sample code includes a CLASSLIB library. Compiling the CStringTable class from this library may cause string space allocation failures with some compilers.

Problem:

The problem is an uninitialized local variable "cchUsed" in the function CStringTable::FInit(). This routine is found in the CLASSLIB source file SCSTRTABL.CPP.

Solution:

Initialize cchUsed to zero, for example, change the line

   UINT        cchUsed;

to read as follows:

   UINT        cchUsed=0;

11. CImpIPolyline::WriteToFile() Returns an Incorrect Value

Summary:

The Polyline sample DLL in Chapter 4 contains a function, CImpIPolyline::WriteToFile(), which returns an incorrect value.

Problem:

If WriteToFile()'s write operation succeeds, it should return NOERROR. Instead, it returns POLYLINE_E_WRITEFAILURE.

Solution:

In the code for the CImpIPolyline::WriteToFile() function, change the lines that read

   return (m_pObj->m_fDirty) ?
       NOERROR : ResultFromScode(POLYLINE_E_WRITEFAILURE);

to read as follows:

   return (!m_pObj->m_fDirty) ?
       NOERROR : ResultFromScode(POLYLINE_E_WRITEFAILURE);

Make this change to the Chapter 4 version of Polyline. CImpIPolyline::WriteToFile() is in the Polyline source file IPOLYLIN.CPP.

12. CImpIOleObject::GetClientSite() Returns an Incorrect Value

Summary:

The Cosmo sample applications in Chapters 10, 13, 14 and 16 contain a function, CImpIOleObject::GetClientSite(), that returns an incorrect value.

Problem:

When GetClientSite() encounters no errors, it should return NOERROR. Instead, it returns E_NOTIMPL.

Solution:

In the code for CImpIOleObject::GetClientSite(), change the line that reads

   return ResultFromScode(E_NOTIMPL);

to read as follows:

   return NOERROR;

Make this change to the versions of Cosmo in Chapters 10, 13, 14 and 16. CImpIOleObject::GetClientSite() is in the Cosmo source file IOLEOBJ.CPP.

13. Polyline Registers a Format with an Uninitialized String

Summary:

The Polyline sample DLLs in Chapters 5, 6, 11 and 16 attempt to register a clipboard format using an uninitialized string. As a result, data formats do not get properly transferred during clipboard, drag & drop, and compound document operations.

Problem:

The offending line is:

   m_cf=RegisterClipboardFormat(PSZ(IDS_STORAGEFORMAT));

At constructor time, the stringtable referenced by the PSZ macro (this macro tries to access the member CPolyine::m_pST) has not been initialized, so m_cf is not set properly.

Solution:

To correct the error, the line of code listed above that initializes m_cf must be moved into the function CPolyline::FInit(). Specifically, move the line of code to occur after the code that initializes the stringtable:

   if (!m_pST->FInit(IDS_POLYLINEMIN, IDS_POLYLINEMAX))
       return FALSE;

   m_cf=RegisterClipboardFormat(PSZ(IDS_STORAGEFORMAT));

Make this one-line change to the versions of Polyline in Chapters 5, 11, and 16. CPolyline::FInit() is in the Polyline source file POLYLINE.CPP.

Correcting this error in the Chapter 6 version of Polyline requires an extra step. All lines initializing the FORMATETC arrays m_rgfeGet[0] and m_rgfeSet[0] in which m_cf is stored must also be moved into CPolyline::FInit(). The most convenient way to make this change is to simply move all the FORMATETC initialization to the end of FInit(). Note that Polyline does not use the FORMATETC arrays in Chapters 11 and 16, so this extra step is not necessary in those chapters.

14. DataTran Reference Counting Problem

Summary:

The DataTran data transfer object in Chapter 7 has a reference-counting problem in the function CImpIDataObj::GetData(). As a result, storage elements in the STGMEDIUM returned by GetData() may become invalid while they are still in use.

Problem:

GetData() incorrectly fails to call AddRef() on any IStorage or IStream pointer contained in the STGMEDIUM it returns.

Solution:

To correct this error, add the following lines of code to GetData(), immediately after the line "*pSTM = pRen->stm;":

   /*
    * Must remember to AddRef any other objects
    * in the STGMEDIUM:  storages and streams.
    */ 
   if (TYMED_ISTORAGE==pSTM->tymed)
       pSTM->pstg->AddRef();

   if (TYMED_ISTREAM==pSTM->tymed)
       pSTM->pstm->AddRef();

GetData() is in the DataTran source file IDATAOBJ.CPP.

15. Component Cosmo Fails to Release Polyline Object

Summary:

The Component Cosmo samples in Chapter 6, 7, 8, 11 and 16 fail to fully clean up their allocations when closing a document. As a result, files may not be saved completely and unused code is left in memory.

Problem:

In the function CCosmoDoc::~CCosmoDoc(), CoCosmo obtains an IDataObject pointer on the Polyline object it maintains in the document. It then fails to release this pointer, resulting in the problems listed above.

Solution:

In the code for CCosmoDoc::~CCosmoDoc(), add a call to IDataObject::Release().

Change the lines

   if (SUCCEEDED(hr))
       pIDataObject->DUnadvise(m_dwConn);

to read as follows:

   if (SUCCEEDED(hr))
       {
       pIDataObject->DUnadvise(m_dwConn);
       pIDataObject->Release();
       }

CCosmoDoc::~CCosmoDoc() is in the CoCosmo source file DOCUMENT.CPP.

16. Cosmo Fails to Load When Used with HCosmo Handler

Summary:

The Cosmo sample applications in Chapter 10, 13, 14, or 16, when they are being used in conjunction with the custom object handler HCosmo from Chapter 11, fail to load and activate a Cosmo object. This problem occurs when a container application is reloading a Cosmo object from a saved file, and also when a container application is reactivating a Cosmo object after the object has been first created and then deactivated.

Problem:

The problem actually lies in HCosmo. HCosmo incorrectly keeps the object stream open in the object's IStorage. It does this so that it can perform low-memory saves without having to reopen the stream.

However, when a process has a stream open and that stream has a particular IStorage as its parent, the OLE 2 implementation of STORAGE.DLL does not allow the process to open the stream a second time from the same parent IStorage. Therefore, when the Cosmo application receives the IStorage pointer from HCosmo, and attempts to open the same object stream that HCosmo already has open, the attempt to open the stream fails.

This problem shows up in HCosmo's implementation of IOleObject::DoVerb(). HCosmo delegates the DoVerb() call to the default handler, and the default handler attempts to launch Cosmo. As described above, Cosmo cannot open the object stream, so it returns an error of STG_E_READFAULT to the default handler, which in turn returns STG_E_READFAULT to HCosmo.

As noted above, HCosmo keeps the object stream open so that it can perform low-memory saves without having to reopen the stream. However, this is not necessary. Because HCosmo never makes changes to the object, it never has to save anything in a low-memory situation. Specifically, it never has to save any changes to the storage when its IPersistStorage::Save() is called with the fSameAsLoad flag set to TRUE. Because this is the only case where an IPersistStorage implementation should not attempt to allocate memory, it is totally unnecessary for HCosmo to cache any pointers to the stream.

Solution:

To correct the problem, make the following five changes to HCosmo's IPersistStorage implementation (HCosmo's implementation of IPersistStorage is in the source file IPERSTOR.CPP):

1. In CImpIPersistStorage::InitNew(), add the lines below immediately

   before the call to WriteClassStg():

      m_pObj->m_pIStream->Release();
      m_pObj->m_pIStream=NULL;

2. In CImpIPersistStorage::Load(), replace the line

      m_pObj->m_pIStream=pIStream;

   with the following:

      pIStream->Release();
      m_pObj->m_pIStream=NULL;

3. In CImpIPersistStorage::Save(), remove all the code inside the "if
   (fSameAsLoad)" condition (it is unnecessary).

4. In CImpIPersistStorage::SaveCompleted(), remove the lines of code below
   that occur inside the "if (NULL!=pIStorage)" condition:

      hr=pIStorage->OpenStream("CONTENTS", 0, STGM_DIRECT
          | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0
          , &pIStream);

      if (FAILED(hr))
          return hr;

      if (NULL!=m_pObj->m_pIStream)
          m_pObj->m_pIStream->Release();

      m_pObj->m_pIStream=pIStream;

5. In CImpIPersistStorage::HandsOffStorage(), remove the condition and body
   of code below:

      if (NULL!=m_pObj->m_pIStream)
          {
          m_pObj->m_pIStream->Release();
          m_pObj->m_pIStream=NULL;
          }

The m_pIStream member of CFigure can then be eliminated entirely, because it is no longer used in this IPersistStorage implementation.

17. Patron Does Not Work with Some Printer Drivers

Summary:

The Patron sample applications do not work correctly with certain printer drivers, including the standard PostScript driver.

One symptom of failure is getting a blank document window when creating a new Patron document; that is, no page image appears. Another symptom is getting a GP fault in the printer driver when attempting to execute the Printer Setup command from Patron's File menu.

Problem:

The problem is caused by the fact that Patron is not sensitive to the variable length of the DEVMODE structure returned by the printer driver. Patron normally obtains the DEVMODE data by calling the Windows PrintDlg() API; it then copies that data to a private stream in its Compound File. However, Patron only copies sizeof(DEVMODE) bytes, and therefore loses any additional driver-specific information that might exist at the end of the DEVMODE structure.

Some printer drivers, such as the Hewlett-Packard (HP) LaserJet PCL drivers, use no extra information. Patron works correctly with these drivers. Other drivers, such as the standard PostScript driver, use extra information; with these drivers, Patron fails.

Solution:

To correct this problem, change the implementation of Patron's CPages class so that it works with variable length DEVMODE structures, and therefore with all printer drivers.

The changes are too extensive to list in this article; as noted at the top of this article, an updated version of the Inside OLE 2 sample code source tree is available on the Software Library. The following is a brief overview of the code that needs to be changed to correct this problem:

The CPages class is defined in the header file PAGES.H, and implemented in PAGES.CPP. The functions that need to be updated are:

After these changes have been made, the new Patron will be unable to read files generated by previous versions of Patron. There is no automatic conversion facility provided in these samples. To convert a file from the old Patron format to the new Patron format, follow these steps:

1. Run an old version of Patron from Chapter 14. 2. Run a new version of Patron from Chapter 15 (or, run the old Chapter 15

   Patron and the new Chapter 14 Patron).
3. Load the old file into the old Patron. 4. Transfer all objects from the old Patron to the new Patron, using the
   clipboard or drag-and-drop method.
5. Save the new document in the new Patron.

18. ClassLib Creates Table of Invalid String Pointers

Summary:

The ClassLib sample framework implements a string table class called CStringTable. In certain situations the implementation of this class can create a table of invalid string pointers.

Problem:

The function CStringTable::FInit() allocates a block of memory and loads strings from the application's resources into that memory. It then initializes a table of far pointers into that memory, one pointer for each string.

FInit() then reallocates the memory block, in order to free up any unused space. This reallocation could move the memory block. Because the far pointers in the pointer array point into the block that has moved, they all become invalid.

Solution:

To avoid this problem, delete the following lines in the CStringTable::FInit() function:

   //Now reallocate the string memory to however much we used, plus 1
   psz=(LPSTR)_frealloc(m_pszStrings, cchUsed+1);

   if (NULL!=psz)
       m_pszStrings=psz;

This solution wastes a little memory, but is simpler than trying to recompute all the string pointers; ClassLib is intended to be a simple example.

CStringTable::FInit() is in ClassLib's CSTRABL.CPP file.

19. Errors in Enumerator Samples

Summary:

The enumerator samples in "Inside OLE 2" contain two errors. This section describes the errors and the changes necessary to fix them. The changes are detailed for the IEnumRect enumerator found in Chapter 3; similar changes are required in all enumerators in the book's sample code.

Problem 1:

If the first parameter to an enumerator's Next() function (called "celt" in the OLE 2 documentation, "cRects" in the sample) is 1, it is permissible for the last parameter (called "pceltFetched" in the documentation, "pdwRects" in the sample) to be NULL. IEnumRect::Next() simply returns FALSE in this case, which is incorrect.

Solution 1:

To correct this error, change the lines of code in IEnumRect::New() that read

   if (NULL==pdwRects)
       return FALSE;

   *pdwRects=0L;

to read as follows:

   if (NULL==pdwRects)
       {
       if (1L!=cRect)
           return FALSE;
       }
   else
       *pdwRects=0L;

Problem 2:

IEnumRect::Next() stores an incorrect value into the out parameter pdwRects.

Solution 2:

To correct this error, change the line of code in IEnumRect::New() that reads

   *pdwRects=(cRectReturn-cRect);

to read as follows:

   if (NULL!=pdwRects)
       *pdwRects=cRectReturn;

The same changes should be made to the IENUM.CPP files in the following "Inside OLE 2" sample code directories:

   interface
   chap03\enumc
   chap03\enumcpp
   chap06\polyline
   chap06\ddataobj
   chap06\edataobj
   chap07\datatran

20. Samples GP Fault When Closing Iconized Documents

Summary:

The sample applications may cause a GP fault when closing one or more iconized documents.

Problem:

The CClient::QueryCloseAllDocuments() function (found in the ClassLib source file CCLIENT.CPP) can cause this GP fault through incorrect use of its local variable hPrevClose.

Solution:

To correct this problem, change the code to CClient::QueryCloseAllDocuments() by adding the two lines below that read "hPrevClose=NULL":

BOOL CClient::QueryCloseAllDocuments(BOOL fClose)

    {
    ...

    for ( ; hWndT; hWndT=GetWindow(hWndT, GW_HWNDNEXT))
        {
        if (NULL!=hPrevClose)
            {
            pDoc=(LPCDocument)SendMessage(hPrevClose
                , DOCM_PDOCUMENT, 0, 0L);
            CloseDocument(pDoc);
            hPrevClose=NULL;            // ADD THIS LINE
            }

    ...

    //Close the last window as necessary.
    if (fClose && NULL!=hPrevClose)
        {
        pDoc=(LPCDocument)SendMessage(hPrevClose, DOCM_PDOCUMENT, 0, 0L);
        CloseDocument(pDoc);
        hPrevClose=NULL;                // ADD THIS LINE
        }

    ...

Additional query words: kbfile gpf gp-fault

Keywords          : kbsample LeTwoOth 
Version           : 2.01 | 2.10
Platform          : NT WINDOWS

Last Reviewed: December 11, 1998