Corrections for Inside OLE 2 Sample CodeLast reviewed: August 5, 1996Article ID: Q113255 |
The information in this article applies to:
SUMMARYThe 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:
Updated Sample Code AvailableThe 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: 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 ftp ftp.microsoft.com Change to the Softlib/Mslfiles folder Get INOLE2.EXE (size: 2107743 bytes) 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 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 ServicesNOTE: This archive contains subdirectories, be sure to use the -d command line switch during extraction.
MORE INFORMATION
BOOKUI.DLLIf 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.
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 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:
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:
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.cppThese changes will also be necessary in the function CTenant::CopyLinkedObject(), found in TENANT.CPP in Chapters 12, 13, 14, and 15.
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 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".
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 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.
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.
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(); ... 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; 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.
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.
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.
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.
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.
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):
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:
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.
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.
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 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
© 1998 Microsoft Corporation. All rights reserved. Terms of Use. |