Corrections for Inside OLE 2 Sample Code

Last reviewed: August 5, 1996
Article ID: Q113255
The information in this article applies to:
  • Microsoft OLE Libraries for Windows and Win32s, version 2.01
  • Microsoft OLE Libraries, version 2.1, included with:

        - Microsoft Windows NT, version 3.5
    

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. The changes are described in detail in the "More Information" section of this article below; below a quick list of the problems that required changes:

  • Compiler Errors on IViewObject::Draw()
  • Compiler Errors on OleUIAddVerbMenu()
  • Compiler Errors on OleStdGetObjectDescriptorFromOleObject()
  • Compiler Errors on IDS_CLOSE
  • Linker Fails with CLASSLIB Sample
  • Compiler Errors Referencing GETICON.H
  • Incorrect Prototypes for LibMain() and WEP()
  • Patron GP Faults During Activate As
  • Patron's Convert To Fails
  • CLASSLIB String Allocations Fail with Some Compilers
  • CImpIPolyline::WriteToFile() Returns an Incorrect Value
  • CImpIOleObject::GetClientSite() Returns an Incorrect Value
  • Polyline Registers a Format with an Uninitialized String
  • DataTran Reference Counting Problem
  • Component Cosmo Fails to Release Polyline Object
  • Cosmo Fails to Load When Used with HCosmo Handler
  • Patron Does Not Work with Some Printer Drivers
  • ClassLib Creates Table of Invalid String Pointers
  • Errors in Enumerator Samples
  • Samples GP Fault When Closing Iconized Documents

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.

You can find INOLE2.EXE (size: 2107743 bytes) 
                       , a self-extracting file, on these services:

  • Microsoft's World Wide Web site on the Internet

          On the www.microsoft.com home page, click the Support icon
          Click Knowledge Base, and select the product
    
          Enter kbfile INOLE2.EXE (size: 2107743 bytes) 
                                 , and click GO!
          Open the article, and click the button to download the file
    
    
  • Internet (anonymous FTP)

          ftp ftp.microsoft.com
          Change to the Softlib/Mslfiles folder
          Get INOLE2.EXE (size: 2107743 bytes) 
    
  • The Microsoft Network

          On the Edit menu, click Go To, and then click Other Location
          Type "mssupport" (without the quotation marks)
          Double-click the MS Software Library icon
          Find the appropriate product area
          Locate and Download INOLE2.EXE
    
  • Microsoft Download Service (MSDL)

          Dial (206) 936-6735 to connect to MSDL
          Download INOLE2.EXE (size: 2107743 bytes) 
    

    For additional information about downloading, 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.

    MORE INFORMATION

    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
    
    

  • 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.

    1. 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
    
    

  • 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".

    1. 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
    
    

  • 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.

    1. 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.

    1. 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();
    
       ...
    
    

  • 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;
    
    

  • 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.

    1. 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.

    1. 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.

    1. 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.

    1. 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.

    1. 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.

    1. 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:

    • CPages::DevmodeSet() and CPages::DevModeGet() in all chapters
    • CPages::ConfigureForDevice() in Chapters 2 and 5
    • CPages::DevReadConfig() in all chapters other than 2 and 5

    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.

    1. 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.

    1. 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
    
    

  • 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 reference words: 2.01 gpf gp-fault
    KBCategory: kbole kbfile kbwebcontent
    KBSubcategory: LeTwoOth


    THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

    Last reviewed: August 5, 1996
    © 1998 Microsoft Corporation. All rights reserved. Terms of Use.