Updates to Inside OLE 2 Book and Sample Code, January 1994
Pre-Release Win32 versions (through Chapter 2)

This file lists describe bugs found in the sample code of the
book Inside OLE 2 and corrections for those problems.  Updated
code is found on Compuserve, Internet, and the MSDN CD (this also
contains the book text).

There are three sections to this file:
    Compiler Errors:    Changes to the OLE 2 SDK that caused
                        compiler errors with original Inside OLE 2
                        samples.  Also some considerations for
                        different compilers.

    Bug Fixes:          Other changes to the source code to fix
                        a few major and a number of minor problems.

    Other Topics:       List of typographical errors in the book
                        text (MSDN CD version includes most
                        corrections already, but do double-check)
                        and other topics of interest, such as Win32
                        version information.


Please send additional bug reports and problems to the author at
kraigb@microsft.com on Internet.  CompuServe users must prefix the
address with ">INTERNET:"



---------------------------
Section 1:  Compiler Errors
---------------------------

The sample code of the book Inside OLE 2 was originally written
for the OLE 2.00 libraries.  The OLE 2.01 libraries included
a few minor changes that cause compilation errors with the original
book samples.


1.  Errors concerning IViewObject::Draw.  The OLE 2.01 SDK
    changed the prototype of IViewObject::Draw from using
    "const LPRECTL" to "LPCRECTL."  This affects HCOSMO and
    Polyline samples of chapter 11 and Poltilne sample of
    chapter 16, causing them to fail compiling.

    To correct the error, replace all occurances of
    "const LPRECTL" in declaration of CImpIViewObject::Draw
    to "LPCRECTL" to match OLE 2.01.  This occurs in the
    HCOSMO.H and IVIEWOBJ.CPP files of HCOSMO in chapter 11,
    and the POLYLINE.H and IVIEWOBJ.CPP files of Polyline in
    chapters 11 and 16.  Note that there are also affected
    files in the INTERFAC directory, IVIEWOBJ.H and IVIEWOBJ.CPP,
    although these will not show in compilations.


2.  Errors concerning OleUIAddVerbMenu in Patron samples of
    chapters 9, 12, 13, 14, and 15.

    The OLE 2.01 UI library added a parameter to this function,
    an added "idVerbMax" that indicates the largest number the
    container allows for a verb menu item identifier.  This
    new parameter occurs immediately after the "idVerbMin" parameter.

    In the Patron directories of chapters 9, 12, 13, 14, and 15
    in the files PAGE.CPP and TENANT.CPP, the parameter IDM_VERBMAX
    is added after the IDM_VERBMIN parameter (both are defined
    in resource.h).  The functions affected are CTenant::AddVerbMenu
    (TENANT.CPP) and CPage::FQueryObjectSelected (PAGE.CPP).


3.  Errors concerning OleGetObjectDescriptorFromOleObject affecting
    Patron samples in chapters 9, 12, 13, 14, and 15.

    The OLE 2.01 UI library added a parameter to the end of
    this function: an LPSIZEL that contains the extents of the
    object.  The OLE 2.00 code appeared as follows:

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

    The corrections for OLE 2.01 require the declaration and
    initialization of a new SIZEL variable for the last parameter:


        //Declare new variable
        SIZEL   szl;

        ...


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

        //Add to parameter list.
        stm.hGlobal=OleStdGetObjectDescriptorDataFromOleObject
            (m_pIOleObject, NULL, m_fe.dwAspect, ptl, &szl);

    This correctly computes the size of the object in question
    and stores those extents in szl and in the object descriptor.

    The changes affect the function CTenant::CopyEmbeddedObject
    in chapters 9, 12, 13, 14, and 15.  The function is found
    in all the TENANT.CPP files in the PATRON directories
    of these chapters.  Note that it also affects the function
    CTenant::CopyLinkedObject.in chapters 12, 13, 14, and 15,


4.  Errors concerning IDS_CLOSE in Cosmo samples of chapters 10, 13,
    14, and 16.

    To correct the problem the IDS_CLOSE in RESOURCE.H is renamed
    to "IDS_CLOSE2" and occurances of that symbol in COSMO.RC are
    changed to match.  This affects these two files in chapters
    10, 13, 14, and 16.


5.  Errors concerning GETICON.H in Patron samples of chapters
    14 and 15.  This file was necessary to include in container
    applications that supports the Convert dialog in OLE 2.00.
    The OLE 2.01 SDK removed the dependency on this file, so it
    no longer has to be included.  Therefore the #include statement
    in the TENANT.CPP files of Patron in chapters 14 and 15
    have been commented out.

    To correct the problem, the line "#include <geticon.h>"
    is commented out at the top of the TENANT.CPP files of
    Patron in chapters 14 and 15.


6.  Linker errors on Windows NT hosted development environments
    concerning the CLASSLIB library.  A filename in the FILES.LST
    of CLASSLIB has an extra character causing it to appear as a
    long filename, which on a Windows NT system is valid but does
    not exist.

    The first file in FILES.LST is changed from "cstrtable.obj"
    to "cstrtabl.obj" to correct the problem, that is, remove the
    extra 'e'.



7.  The function declarations for LibMain and WEP in all DLLs
    are slightly incorrect and have been updated.  These can
    ause compiler errors on non-Microsoft compilers.  LibMain should
    return an int (not a HANDLE) and take an HINSTANCE as the first
    parameter (not a HANDLE either).  WEP should return an int rather
    than void.  WEP should always return a zero to provide some return
    value although that value is not used.





---------------------------------
Section 2:  Sample Code Bug Fixes
---------------------------------

1.  GP Fault with Patron sample during Activate As,
    chapters 14 and 15.

    The problem lies in the function CTenant::Close with the fReopen
    flag.  When this function determines that there are no references
    to the tenant IStorage, it normally resets the internal state of
    the tenant to defaults, including setting the member m_pIStorage
    to NULL.  However, if fReopen is TRUE, this NULL assignment is
    skipped.

    The crash occurs when m_pIStorage is left non-NULL but the
    storage itself has been destroyed.  Thus m_pIStorage is an
    invalid pointer.

    To correct this problem it is necessary to insure that the
    m_pIStorage variable is set to NULL any time the actual
    reference count on it is zero

    A condition in the function CTenant::Close in TENANT.CPP of
    both chapters is removed to correct the problem:

    The lines:

        if (!fReopen)
            m_pIStorage=NULL;

    are replaced with just:

        m_pIStorage=NULL;


    The fReopen flag was used in an attempt to provide some
    optimization when performing Activate As, but is not
    necessary.  The updated sample code has removed the flag
    entirely.  This affects PAGE.CPP, TENANT.CPP, and TENANT.H
    files in Patron of chapters 14 and 15.


2.  Convert To function doesn't seem to work in Patron samples
    of chapters 14 and 15.  The problem occurs in the function
    CPage::FConvertObject in the PAGE.CPP file of both chapters.

     The problem is that CPage::FConvertObject is releasing the
     page's IStorage pointer without commiting it.  Since Patron
     uses transacted storage, this discards all changes including
     the conversion that just happened.

     The code in error 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.  To correct this
     problem, include a call to pIStorage->Commit:

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

             hr=OleStdDoConvert(pIStorage, ct.clsidNew);

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

         ...


    Note that the second parameter to CTenant::Close has also been
    removed reflecting Bug Fix #1.


3.  Unitialized variable in CLASSLIB causes string allocation
    failure with non-Microsoft compilers.  In the file
    CSTRTABL.CPP, function CStringTable::FInit, initialize the
    variable cchUsed to zero:

        UINT        cchUsed=0;


4.  The CImpIPolyline::WriteToFile function in PolyLine chapter 4
    returns an incorrect value.  In this function in the file
    IPOLYLIN.CPP, the line:

        return (m_pObj->m_fDirty) ?

    is changed to read:

        return (!m_pObj->m_fDirty) ?


5.  The Polyline samples of chapters 5, 6, 11, and 16 attempt to
    initialize a clipboard format with an invalid string.  The
    following line is found in the CPolyline::CPolyline function
    in the POLYLINE.CPP file of chapters 5, 6, 11, and 16:

        m_cf=RegisterClipboardFormat(PSZ(IDS_STORAGEFORMAT));

    This call is incorrectly made before the stringtable used
    by the PSZ macro is initialized.  To correct the problem,
    the call is moved into CPolyline::FInit after the stringtable
    is initialized:

        ...

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

        m_cf=RegisterClipboardFormat(PSZ(IDS_STORAGEFORMAT));

        ...

    In chapter 6 it also requires movement of lines that depend on
    m_cf from the constructor until after the code above in
    CPolyline::FInit.  This affects code that initializes the
    FORMATETC arrays elements m_rgfeGet[0] and m_rgfeSet[0].


6.  The return value from CImpIOleObject::GetClientSite in the
    IOLEOBJ.CPP file of Cosmo, chapters 10, 13, 14, and 16 is changed
    to NOERROR from ResultFromScode(E_NOTIMPL).


7.  Component Cosmo, Chapter 6, fails to release the Polyline's
    IDataObject interface in the destructor CCosmoDoc::~CCosmoDoc
    after calling IDataObject::DUnadvise.  In DOCUMENT.CPP the
    lines below:

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

    are changed to read:

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


8.  The Data Transfer Object of Chapter 7 does not call AddRef on
    IStorage and IStream objects returned from CImpIDataObject::GetData
    (IDATAOBJ.CPP).  In this function the following lines are added
    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();


    This will correctly insure that multiple copies of a STGMEDIUM
    containing a storage element are each counted such that the
    storage element does not become invalid prematurely.


9.  The Patron samples of chapters 5 and beyond do not work correctly
    with some printer drivers.  The problem is that Patron is not
    sensitive to drivers that store driver-specific information at the
    end of the DEVMODE structure.  In making copies of the DEVMODE
    structure, Patron truncates that driver-specific data.

    This will cause the CreateDC and CreateIC functions to fail, which
    manifests in Patron as a blank document window with no visible
    pages.  It will also cause the PrintDlg function to GP Fault inside
    the printer driver when that driver attempts to access driver-
    specific data that does not exist.  The problems only appear on
    some printer drivers because other use no driver-specific data and
    are thus unaffected by the truncation.

    The correction identically affects the implementation of the CPages
    class of Patron in chapters 2, 5, 7, 8, 9, 12, 13, 14, and 15.  CPages
    is defined in PAGES.H and implemented in PAGES.CPP.  The functions of
    interest are CPages::DevModeSet, CPages::DevModeGet in all chapters,
    CPages::ConfigureForDevice in chapter 5, and CPages::DevReadConfig
    in all chapters except 5.  These functions are modified to be
    sensitive to the variable-length nature of DEVMODE in order to work
    with all printer drivers, and the changes are too complex to be
    listed here.  The listings of these functions on pages 257-259 of
    Inside OLE 2 are thus incorrect as well.

    NOTE:  the changes render files generated by previous versions of
    Patron unreadable by a new version.  This is, after all, a sample
    not meant for production work.  Files may, however, be converted
    by running an old version of Patron from chapter 14 or 15, loading the
    file, and transferring all objects to a new document opened in another
    version of Patron from chapter 15 or 14 (opposite of the old version
    you are running), and saving that new document.

    On a related not, printing page ranges from Patron always prints
    the range offset by one.  Corrections to this problem have been
    made in the CPages::Print function of the PAGEWIN.CPP file in all
    chapters.


10. The Cosmo application will fail to load an object when activated
    if used in conjunction with the custom object handler HCOSMO
    from chapter 11.  This will occur when either reloading an object
    from a save container file or reactivating the object after it has
    been created and deactivated once already.

    The problem is that HCOSMO imcorrectly keeps the object stream open
    in the object's IStorage so it can perform low-memory saves without
    having to open the stream again.  However, the OLE 2 implementation
    of STORAGE.DLL only allows one process to open any one particular
    stream using the same IStorage pointer as a parent.  Therefore when
    the Cosmo application receives the IStorage pointer and attempts
    to open the same stream, it fails.  This shows up in HCOSMO's
    implementation of IOleObject::DoVerb where it delegates the call
    to the default handler.  In this case the default handler passes
    back Cosmo's error code, which is STG_E_READFAULT.

    Since the HCOSMO handler never makes changes to the object, it will
    never have to save anything in a low-memory situation.  Specifically,
    it will never have to save any changes to the storage when its
    IPersistStorage::Save is called with the fSameAsLoad flag set to TRUE.
    Since 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.

    To correct the problem, remove the m_pIStorage and m_pIStream
    members from the CFigure class defined in HCOSMO.H and FIGURE.CPP.
    Then remove any code that deals with them in HCOSMO's implementation
    of IPersistStorage found in IPERSTOR.CPP:

        a.  In CImpIPersistStorage::InitNew,  remove the "HRESULT hr;"
            local variable and the NULL check on m_pObj->m_pIStorage
            at the top of the function.  Then remove all code between
            and including the lines:

                hr=pIStorage->CreateStream("CONTENTS", STGM_DIRECT

            and

                m_pObj->m_pIStorage=pIStorage;

            The remainder of the code is still important to initailize
            the handler's internal storage.

        b.  In CImpIPersistStorage::Load, remove the NULL check on
            m_pObj->m_pIStorage.  After the call to pIStream->Read,
            add the line:

                pIStream->Release();

            Then remove the lines that hold the IStorage:

                pIStorage->AddRef();
                m_pObj->m_pIStorage=pIStorage;


        c.  In CImpIPersistStorage::Save, replace the "if (fSameAsLoad)"
            condition with:

                if (fSameAsLoad)
                    {
                    m_pObj->m_pDefIPersistStorage->Save(pIStorage
                        , fSameAsLoad);
                    return NOERROR;
                    }

            Move all the remaining code in the "else" condition
            out of the condition completely and add the following
            line just before the call to WriteFmtUserTypeStg:

                WriteClassStg(pIStorage, CLSID_Cosmo2Figure);

        d.  In CImpIPersistStorage::SaveCompleted, remove all code
            except for:

                m_pObj->m_pDefIPersistStorage->SaveCompleted(pIStorage);
                return NOERROR;

        e.  In CImpIPersistStorage::HandsOffStorage, remove all code
            except for:

                m_pObj->m_pDefIPersistStorage->HandsOffStorage();
                return NOERROR;


11. The "(c)" in all ABOUT.DLG files is replaced with the ANSI 169
    copyright character.


12. The CClient::QueryCloseAllDocuments function in the CCLIENT.CPP
    file of CLASSLIB may cause a GP Fault when closing an application
    with one or more documents iconized.  The problem may be corrected
    by adding the line "hPrevClose=NULL" in the code below:

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

        ...


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


        ...


13. STASTRIP, file INIT.C, contains a call to LocalAlloc where the
    return value is incorrectly cast immediately into a far pointer:

        pST->ppsz=(LPSTR *)LocalAlloc(...)

    This is changed to first cast the return value into a near pointer
    then into a far pointer to insure the conversion happens properly:

        pST->ppsz=(LPSTR *)(char *)LocalAlloc(...)


14. The implementation of IEnumRECT::Next in both CHAP03\ENUMCPP and
    CHAP03\ENUMC samples, file IENUM.C[PP] is incorrect on two counts.
    First, an enumerator can accept a NULL in the last parameter
    to Next (called pceltFetched in the OLE 2 documentation, pdwRects
    in these samples) if the first parameter (celt in docs, cRects
    in samples) is one.  Therefore the code:

        if (NULL==pdwRects)
            return FALSE;

        *pdwRects=0L;

    is changed to read:

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


    Second, the value stored in pceltFetched (pdwRects) is incorrect.
    The line:

        *pdwRects=(cRectReturn-cRect);

    is changed to:

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


    The same changes affect the IENUM.CPP files in the INTERFACE,
    CHAP06\POLYLINE, CHAP06\DDATAOBJ, CHAP06\EDATAOBJ, and
    CHAP07\DATATRAN directories as well.




15. The CStringTable in CLASSLIB file CSTRTABL.CPP will create an
    array of invalid string pointers in CStringTable::FInit if the
    call to _frealloc returns a different memory block.  To avoid
    this problem simply delete the following lines:

        //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 wastes a little memory, but is simpler than trying to recompute
    all the string pointers.  CLASSLIB is, after all, meant to be simple
    for the purposes of sample code.


------------------------
Section 3:  Other Topics
------------------------

1.  Corrections to Inside OLE 2 printed text.


    Page 82-:  The exact implementations of CImpIEnumRECT::Next are
    Page 83;   incorrect according to Bug Fix #14 above.  Also, on page
    Page 92-   83 there should be no space between "book" and "1632"
    Page 93    in the "#include <book 1632.h>"

    Page 105:  The second use of QueryInterface in the Transitive
               property should read

                   "pInterface2->QueryInterface(IInterface3)"

               The book has IInterface2 as the parameter to
               QueryInterface, which is incorrect.


    Page 157:  A space is missing between "LPLPVOID" and "ppv" in the
               last parameter of the declaration CKoala::QueryInterface.


    Page 161:  The prototype for WEP should return an int and not VOID.


    Page 197:  The line of code under "if (IsEqual(riid, IID_IAnimal))"
               should read "*ppv=(LPVOID)m_pAnimal".  In the text the
               underscore is mistakenly a space, dash, and space.


    Page 199:  A space is missing between "LPLPVOID" and "ppv" in the
               last parameter of the declaration CKoala::QueryInterface.


    Page 243:  The second sentence in the second paragraph should read:
               "The Structured Storage definition of IStream allows
               streams to contain up to 2^64 (2 raised to the 64th power)
               addressable bytes of data."  The text incorrectly reads
               "264" instead of "2^64."

    Page 257-: The code listing of CPages::DevModeSet, CPages::DevModeGet,
    Page 259   and CPages::ConfigureForDevice will change according to
               Bug Fix #9 above.


    Page 331:  The description of IDataObject::GetCanonicalFormatEtc
               is incorrect.  For correct details, see the OLE 2
               Programmer's Reference.


    Page 289-: Both LibMain and WEP functions should return an int as
    Page 290   described in Compile Fix #7.


    Page 316:  The implementation of CImpIEnumFormatEtc::Next is
               incorrect according to Bug fix #14 above.


    Page 365:  The "const LPRECTL" parameters in IViewObject::Draw
               should be of type "LPCRECTL" to match OLE 2.01 as
               described in Compiler Error #1 above.

    Page 544-: In OLE 2.01 there is an additional parameter to
    Page 546   OleUIAddVerbMenu called "idVerbMax" which comes immediately
               after "idVerbMin."  This was an addition to OLE 2.01 which
               is not present in OLE 2.00 as described in Compiler Error
               #2 above.


    Page 554:  Under OLE 2.01 the OleStdGetObjectDescriptorFromOleObject
               function used in the code sample requires an additional
               LPSIZEL as the last parameter as described in Compiler
               Error #3 above.


    Page 630:  The right curly brace just above the "else" at the bottom
               of the page should be indented to align with the call
               to ModifyMenu above it.


    Page 663-: The bottom of page 663 mentions how a handler should
    Page 665   hold onto open streams for low-memory save situations.
               This is ONLY important for elements that are entirely
               manipulated by the handler and never touched by the server.
               The handler should not cache or hold pointers to the
               same elements the server must access as described
               in Bug Fix #10 above.  If a handler manipulates and
               changes data unaffected by the server, it must fulfill
               the IPersistStorage contract for only those formats.  The
               server is responsible for all others that it modifies.


    Page 668:  The "const LPRECTL" parameters in IViewObject::Draw
               should be of type "LPCRECTL" to match OLE 2.01 as
               described in Compiler Error #1 above.


    Page 687:  There should not be "//" before the large bold
               "IOLEOBJ.CPP" in this code listing.  The label is not
               part of the code.


    Page 788:  Same problem with OleStdGetObjectDescriptorFromOleObject
               as described above for page 554.


    Page 814:  Same problem with OleUIAddVerbMenu as described above for
               pages 544-546.


    Page 817:  Remove the second parameter to pTenantCur->Close.  It should
               read "pTenantCur->Close(FALSE);"  This matches the change
               necessary for Bug Fix #1 above.


    Page 817:  There should be a call to
               pIStorage->Commit(STGC_ONLYIFCURRENT) immediately after
               the call to OleStdDoConvert and before the call to
               pIStorage->Release, as described in Bug Fix #2 above.


    Page 819:  Remove the second parameter to pTenantCur->Close.  It should
               read "pTenantCur->Close(FALSE);"  This matches the change
               necessary for Bug Fix #1 above.

    Page 861:  The second sentence of the third paragraph under "Active
               vs. UI Active and Inside-Out Objects" should read:  "The
               answer...as they are visible-end users DO NOT have to
               double-click..."  The DO NOT is important.



2.  Clarification of 64K limit on IMalloc allocations

    Inside OLE 2 states on page 127 that any single allocation
    done through the default shared or task allocators in OLE 2
    are limited to 64K.  This is true for both OLE 2.00 and OLE 2.01.

    The OLE 2 specifications and Programmer's Refernece state that
    "If the allocation is greater than 64K, a huge pointer is
    returned."  This seems to contradict the actual allocators
    implemented in OLE 2.

    The reason is that the specs and documentation provide the
    standard definition of the IMalloc interface, not the specs
    nor the documenation for OLE 2's implementation of that
    interface on its standard shared and task allocators.  While
    IMalloc is not inherently limited to 64K in the specifications,
    OLE 2's default implementation is limited.

    An application can implement its own allocator and provide an
    IMalloc interface that can allocate blocks larger than 64K, in
    which case that custom allocator must return a huge pointer for
    that allocation.


3.  HCOSMO handler limitations.  The sample handler HCOSMO from chapter
    11 of Inside OLE 2 is meant to be used with the version of Cosmo
    from Chapter 10.  There are no provisions for using this handler
    with any other version of Cosmo, specifically those in chapters 13,
    14, and 16.  With those later versions, you may encounter GP Faults.
    To bypass problems, be sure that OLE2.DLL is registered for Cosmo's
    InProcHandler instead of HCOSMO.DLL.  The .REG files in the CHAP13,
    CHAP14, and CHAP16 directories will make this change.  Be sure to
    merge those files with your registration database before using any
    sample from the corresponding chapter.


4.  Information on Windows NT ports of Inside OLE 2 Samples

    This revision of the sample code in the following directories
    contain full pre-release Win32 ports of their respective samples:
        INC
        BTTNCUR
        GIZMOBAR
        STASTRIP
        CLASSLIB
        CHAP02\SKEL
        CHAP02\COSMO10
        CHAP02\COSMO
        CHAP02\PATRON

    These are intended to serve as examples of how the remainder of
    the code would be ported.  Mostly there are changes to remove
    "FAR PASCAL" function types, change functions like "MoveTo" to
    "MoveToEx" and correcting minor oversights in parameter packing
    and so forth.  Note that several new macros and definitions have
    been added to the file \INC\BOOK1632.H to handle the updated
    sources that compile for both Win3.1 and Windows NT target
    environments.  If you encounter a strange function or macro in
    the sources that you don't recognize, check in this file.

    The samples above have been compiled with Visual C++ 1.1 for
    Windows NT and run under Windows NT version 3.1.  There is, however,
    no guarantee for perfect compilations or execution of these
    pre-release samples.

    Note that the .LIB files in the LIB directory and the .DLL files
    in the BUILD directory are Win16 versions.

    These samples are currently ANSI.  A final Unicode Win32 port of
    the sample code will be available after OLE 2 on Windows NT has
    shipped final.  Other interim releases may be available before
    then, but no guarantees.
