//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1993. // // File: mf.cpp // // Contents: Implentation of hte metafile picture object // // Classes: CMfObject // // Functions: OleIsDcMeta // // History: dd-mmm-yy Author Comment // 01-Feb-95 t-ScottH added Dump method to CMfObject // added DumpCMfObject API // initialize m_pfnContinue in constructor // 25-Jan-94 alexgo first pass at converting to Cairo-style // memory allocations. // 11-Jan-93 alexgo added VDATEHEAP macros to every // function and method // 31-Dec-93 ErikGav chicago port // 17-Dec-93 ChrisWe fixed second argument to SelectPalette calls // in CallbackFuncForDraw // 07-Dec-93 ChrisWe made default params to StSetSize explicit // 07-Dec-93 alexgo merged 16bit RC9 changes // 29-Nov-93 alexgo 32bit port // 04-Jun-93 srinik support for demand loading and discarding // of caches // 13-Mar-92 srinik created // //-------------------------------------------------------------------------- #include #include #include "mf.h" #ifdef _DEBUG #include #endif // _DEBUG #define M_HPRES() (m_hPres ? m_hPres : LoadHPRES()) /* * IMPLEMENTATION of CMfObject * */ //+------------------------------------------------------------------------- // // Member: CMfObject::CMfObject // // Synopsis: constructor for the metafile object // // Effects: // // Arguments: [pCacheNode] -- pointer to the cache node for this object // [dwAspect] -- drawing aspect for the object // [fConvert] -- specifies whether to convert from Mac // QuickDraw format // // Requires: // // Returns: // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 13-Feb-95 t-ScottH initialize m_pfnContinue // 29-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- CMfObject::CMfObject(LPCACHENODE pCacheNode, DWORD dwAspect, BOOL fConvert) { VDATEHEAP(); m_ulRefs = 1; m_hPres = NULL; m_dwSize = 0; m_dwAspect = dwAspect; m_pCacheNode = pCacheNode; m_dwContinue = 0; m_pfnContinue = NULL; m_lWidth = 0; m_lHeight = 0; m_fConvert = fConvert; m_pMetaInfo = NULL; m_pCurMdc = NULL; m_fMetaDC = FALSE; m_nRecord = 0; m_error = NOERROR; m_pColorSet = NULL; m_hPalDCOriginal = NULL; m_hPalLast = NULL; } //+------------------------------------------------------------------------- // // Member: CMfObject::~CMfObject // // Synopsis: Destroys a metafile presentation object // // Effects: // // Arguments: void // // Requires: // // Returns: void // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 29-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- CMfObject::~CMfObject (void) { VDATEHEAP(); CMfObject::DiscardHPRES(); } //+------------------------------------------------------------------------- // // Member: CMfObject::QueryInterface // // Synopsis: returns supported interfaces // // Effects: // // Arguments: [iid] -- the requested interface ID // [ppvObj] -- where to put the interface pointer // // Requires: // // Returns: NOERROR, E_NOINTERFACE // // Signals: // // Modifies: // // Derivation: IOlePresObj // // Algorithm: // // History: dd-mmm-yy Author Comment // 29-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CMfObject::QueryInterface (REFIID iid, void FAR* FAR* ppvObj) { VDATEHEAP(); if (IsEqualIID(iid, IID_IUnknown) || IsEqualIID(iid, IID_IOlePresObj)) { *ppvObj = this; AddRef(); return NOERROR; } else { *ppvObj = NULL; return E_NOINTERFACE; } } //+------------------------------------------------------------------------- // // Member: CMfObject::AddRef // // Synopsis: Increments the reference count // // Effects: // // Arguments: void // // Requires: // // Returns: ULONG -- the new reference count // // Signals: // // Modifies: // // Derivation: IOlePresObj // // Algorithm: // // History: dd-mmm-yy Author Comment // 29-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CMfObject::AddRef(void) { VDATEHEAP(); return ++m_ulRefs; } //+------------------------------------------------------------------------- // // Member: CMfObject::Release // // Synopsis: decrements the reference count // // Effects: deletes the object once the ref count goes to zero // // Arguments: void // // Requires: // // Returns: ULONG -- the new reference count // // Signals: // // Modifies: // // Derivation: IOlePresObj // // Algorithm: // // History: dd-mmm-yy Author Comment // 29-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CMfObject::Release(void) { VDATEHEAP(); if (--m_ulRefs == 0) { delete this; return 0; } return m_ulRefs; } //+------------------------------------------------------------------------- // // Member: CMfObject::GetData // // Synopsis: Retrieves data in the specified format from the object // // Effects: // // Arguments: [pformatetcIn] -- the requested data format // [pmedium] -- where to put the data // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOlePresObject // // Algorithm: Does error checking and then gets a copy of the metafilepict // // History: dd-mmm-yy Author Comment // 29-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CMfObject::GetData(LPFORMATETC pformatetcIn, LPSTGMEDIUM pmedium) { VDATEHEAP(); SCODE sc; // null out in case of error pmedium->tymed = (DWORD) TYMED_NULL; pmedium->pUnkForRelease = NULL; if (!(pformatetcIn->tymed & (DWORD) TYMED_MFPICT)) { sc = DV_E_TYMED; } else if (pformatetcIn->cfFormat != CF_METAFILEPICT) { sc = DV_E_CLIPFORMAT; } else if (IsBlank()) { sc = OLE_E_BLANK; } // here we actually try to get the data else if (NULL == (pmedium->hGlobal = GetHmfp())) { sc = E_OUTOFMEMORY; } else { pmedium->tymed = (DWORD) TYMED_MFPICT; return NOERROR; } return ResultFromScode(sc); } //+------------------------------------------------------------------------- // // Member: CMfObject::GetDataHere // // Synopsis: Retrieves data of the specified format into the specified // medium // // Effects: // // Arguments: [pformatetcIn] -- the requested data format // [pmedium] -- where to put the data // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOlePresObj // // Algorithm: Does error checking and then copies the metafile into a // stream. // // History: dd-mmm-yy Author Comment // 29-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CMfObject::GetDataHere (LPFORMATETC pformatetcIn, LPSTGMEDIUM pmedium) { VDATEHEAP(); SCODE sc; if (pformatetcIn->cfFormat != CF_METAFILEPICT) { sc = DV_E_CLIPFORMAT; } else if (pmedium->tymed != (DWORD) TYMED_ISTREAM) { sc = DV_E_TYMED; } else if (pmedium->pstm == NULL) { sc = E_INVALIDARG; } else if (IsBlank()) { sc = OLE_E_BLANK; } else { HANDLE hpres = M_HPRES(); return UtHMFToPlaceableMFStm(&hpres, m_dwSize, m_lWidth, m_lHeight, pmedium->pstm); } return ResultFromScode(sc); } //+------------------------------------------------------------------------- // // Member: CMfObject::SetDataWDO // // Synopsis: Stores a metafile in this object // // Effects: // // Arguments: [pformatetc] -- format of the data coming in // [pmedium] -- the new metafile (data) // [fRelease] -- if true, then we'll release the [pmedium] // [pDataObj] -- unused for MF objects // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOlePresObj // // Algorithm: does error checking and then stores the new data. // // History: dd-mmm-yy Author Comment // 29-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CMfObject::SetDataWDO (LPFORMATETC pformatetc, STGMEDIUM * pmedium, BOOL fRelease, IDataObject * /* UNUSED */) { VDATEHEAP(); HRESULT error; BOOL fTakeData = FALSE; if (pformatetc->cfFormat != CF_METAFILEPICT) { return ResultFromScode(DV_E_CLIPFORMAT); } if (pmedium->tymed != (DWORD) TYMED_MFPICT) { return ResultFromScode(DV_E_TYMED); } if ((pmedium->pUnkForRelease == NULL) && fRelease) { // we can take the ownership of the data fTakeData = TRUE; } // ChangeData will keep the data if fRelease is TRUE, else it copies error = ChangeData (pmedium->hGlobal, fTakeData); if (fTakeData) { pmedium->tymed = (DWORD) TYMED_NULL; } else if (fRelease) { ReleaseStgMedium(pmedium); } return error; } //+------------------------------------------------------------------------- // // Member: CMfObject::GetHmfp (internal) // // Synopsis: Gets a copy of the stored metafile presentation // // Effects: // // Arguments: void // // Requires: // // Returns: HANDLE // // Signals: // // Modifies: // // Derivation: IOlePresObj // // Algorithm: // // History: dd-mmm-yy Author Comment // 29-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- INTERNAL_(HANDLE) CMfObject::GetHmfp (void) { VDATEHEAP(); return UtGetHMFPICT((HMETAFILE)GetCopyOfHPRES(), TRUE, m_lWidth, m_lHeight); } //+------------------------------------------------------------------------- // // Member: CMfObject::ChangeData (internal) // // Synopsis: Swaps the stored metafile presentation // // Effects: // // Arguments: [hMfp] -- the new metafile // [fDelete] -- if TRUE, then delete [hMfp] // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 29-Nov-93 alexgo 32bit port, fixed GlobalUnlock bug // Notes: // // If the routine fails then the object will be left with it's old data. // If fDelete is TRUE, then hMeta, and the hMF it contains will be deleted // whether the routine is successful or not. // //-------------------------------------------------------------------------- INTERNAL CMfObject::ChangeData (HANDLE hMfp, BOOL fDelete) { VDATEHEAP(); HANDLE hNewMF; LPMETAFILEPICT lpMetaPict; DWORD dwSize; HRESULT error = NOERROR; if ((lpMetaPict = (LPMETAFILEPICT) GlobalLock (hMfp)) == NULL) { if (fDelete) { LEVERIFY( NULL == GlobalFree (hMfp) ); } return E_OUTOFMEMORY; } if (!fDelete) { if (NULL == (hNewMF = CopyMetaFile (lpMetaPict->hMF, NULL))) { return E_OUTOFMEMORY; } } else { hNewMF = lpMetaPict->hMF; } if (lpMetaPict->mm != MM_ANISOTROPIC) { error = ResultFromScode(E_UNSPEC); LEWARN( error, "Mapping mode is not anisotropic" ); } else if (0 == (dwSize = MfGetSize (&hNewMF))) { error = ResultFromScode(OLE_E_BLANK); } else { if (m_hPres) { LEVERIFY( DeleteMetaFile (m_hPres) ); } m_hPres = (HMETAFILE)hNewMF; m_dwSize = dwSize; m_lWidth = lpMetaPict->xExt; m_lHeight = lpMetaPict->yExt; } GlobalUnlock (hMfp); if (error != NOERROR) { LEVERIFY( DeleteMetaFile ((HMETAFILE)hNewMF) ); } if (fDelete) { LEVERIFY( NULL == GlobalFree (hMfp) ); } return error; } //+------------------------------------------------------------------------- // // Member: CMfObject::Draw // // Synopsis: Draws the stored presentation // // Effects: // // Arguments: [pvAspect] -- the drawing aspect // [hicTargetDev] -- the target device // [hdcDraw] -- hdc to draw into // [lprcBounds] -- bounding rectangle to draw into // [lprcWBounds] -- bounding rectangle for the metafile // [pfnContinue] -- function to call while drawing // [dwContinue] -- parameter to [pfnContinue] // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOlePresObj // // Algorithm: Sets the viewport and metafile boundaries, then plays // the metafile // // History: dd-mmm-yy Author Comment // 29-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CMfObject::Draw (void * /* UNUSED pvAspect */, HDC /* UNUSED hicTargetDev */, HDC hdcDraw, LPCRECTL lprcBounds, LPCRECTL lprcWBounds, BOOL (CALLBACK * pfnContinue)(ULONG_PTR), ULONG_PTR dwContinue) { VDATEHEAP(); m_error = NOERROR; int iRgn; int iOldDc; RECT rect; LPRECT lpRrc = (LPRECT) ▭ Assert(lprcBounds); if (!M_HPRES()) { return ResultFromScode(OLE_E_BLANK); } rect.left = lprcBounds->left; rect.right = lprcBounds->right; rect.top = lprcBounds->top; rect.bottom = lprcBounds->bottom; iOldDc = SaveDC (hdcDraw); if (0 == iOldDc) { return ResultFromScode(E_OUTOFMEMORY); } m_nRecord = RECORD_COUNT; m_fMetaDC = OleIsDcMeta (hdcDraw); if (!m_fMetaDC) { iRgn = IntersectClipRect (hdcDraw, lpRrc->left, lpRrc->top, lpRrc->right, lpRrc->bottom); Assert( ERROR != iRgn ); if (iRgn == NULLREGION) { goto errRtn; } if (iRgn == ERROR) { m_error = ResultFromScode(E_UNSPEC); goto errRtn; } // Because the LPToDP conversion takes the current world // transform into consideration, we must check to see // if we are in a GM_ADVANCED device context. If so, // save its state and reset to GM_COMPATIBLE while we // convert LP to DP (then restore the DC). if (GM_ADVANCED == GetGraphicsMode(hdcDraw)) { HDC screendc = GetDC(NULL); RECT rect = {0, 0, 1000, 1000}; HDC emfdc = CreateEnhMetaFile(screendc, NULL, &rect, NULL); PlayMetaFile( emfdc, m_hPres); HENHMETAFILE hemf = CloseEnhMetaFile(emfdc); PlayEnhMetaFile( hdcDraw, hemf, lpRrc); DeleteEnhMetaFile( hemf ); goto errRtn; } else { LEVERIFY( LPtoDP (hdcDraw, (LPPOINT) lpRrc, 2) ); } LEVERIFY( 0 != SetMapMode (hdcDraw, MM_ANISOTROPIC) ); LEVERIFY( SetViewportOrg (hdcDraw, lpRrc->left, lpRrc->top) ); LEVERIFY( SetViewportExt (hdcDraw, lpRrc->right - lpRrc->left, lpRrc->bottom - lpRrc->top) ); } else { iOldDc = -1; if (!lprcWBounds) { return ResultFromScode(E_DRAW); } m_pMetaInfo = (LPMETAINFO)PrivMemAlloc(sizeof(METAINFO)); if( !m_pMetaInfo ) { AssertSz(m_pMetaInfo, "Memory allocation failed"); m_error = ResultFromScode(E_OUTOFMEMORY); goto errRtn; } m_pCurMdc = (LPMETADC) (m_pMetaInfo); m_pMetaInfo->xwo = lprcWBounds->left; m_pMetaInfo->ywo = lprcWBounds->top; m_pMetaInfo->xwe = lprcWBounds->right; m_pMetaInfo->ywe = lprcWBounds->bottom; m_pMetaInfo->xro = lpRrc->left - lprcWBounds->left; m_pMetaInfo->yro = lpRrc->top - lprcWBounds->top; m_pCurMdc->xre = lpRrc->right - lpRrc->left; m_pCurMdc->yre = lpRrc->bottom - lpRrc->top; m_pCurMdc->xMwo = 0; m_pCurMdc->yMwo = 0; m_pCurMdc->xMwe = 0; m_pCurMdc->yMwe = 0; m_pCurMdc->pNext = NULL; } m_pfnContinue = pfnContinue; m_dwContinue = dwContinue; // m_hPalDCOriginal and m_hPalLast are used to clean up any // palettes that are selected into a DC during the metafile // enumeration. m_hPalDCOriginal = NULL; m_hPalLast = NULL; LEVERIFY( EnumMetaFile(hdcDraw, m_hPres, MfCallbackFuncForDraw, (LPARAM) this) ); if (m_fMetaDC) { CleanStack(); } // if m_hPalLast exists at this point, we have duped a palette // and it needs to be freed. if (m_hPalLast) { HPALETTE hPalTemp; // when calling SelectPalette on a Metafile DC, the old // palette is not returned. We need to select another // palette into the metafile DC so DeleteObject can be // called. To do this, we will use the stock palette. if (m_fMetaDC) { hPalTemp = (HPALETTE)GetStockObject(DEFAULT_PALETTE); } else { // Get the original palette selected in the DC. hPalTemp = m_hPalDCOriginal; } // hPalTemp could be NULL... if (hPalTemp) { // Should this palette be selected in the foreground? // [Probably not, this code is well-tested] SelectPalette(hdcDraw, hPalTemp, TRUE); } DeleteObject(m_hPalLast); } m_fMetaDC = FALSE; errRtn: LEVERIFY( RestoreDC (hdcDraw, iOldDc) ); return m_error; } //+------------------------------------------------------------------------- // // Member: CMfObject::GetColorSet // // Synopsis: Retrieves the logical palette associated with the metafile // // Effects: // // Arguments: [pvAspect] -- the drawing aspect // [hicTargetDev] -- target device // [ppColorSet] -- where to put the logical palette pointer // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOlePresObj // // Algorithm: Plays the metafile into a new metafile. The play callback // function stores the metafile palette which is then returned. // // History: dd-mmm-yy Author Comment // 29-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CMfObject::GetColorSet (LPVOID /* UNUSED pvAspect */, HDC /* UNUSED hicTargetDev */, LPLOGPALETTE * ppColorSet) { VDATEHEAP(); if (IsBlank() || !M_HPRES()) { return ResultFromScode(OLE_E_BLANK); } m_pColorSet = NULL; HDC hdcMeta = CreateMetaFile(NULL); if (NULL == hdcMeta) { return ResultFromScode(E_OUTOFMEMORY); } m_error = NOERROR; LEVERIFY( EnumMetaFile(hdcMeta, m_hPres, MfCallbackFuncForGetColorSet, (LPARAM) this) ); HMETAFILE hMetaFile = CloseMetaFile(hdcMeta); if( hMetaFile ) { DeleteMetaFile(hMetaFile); } if( m_error != NOERROR ) { return m_error; } if ((*ppColorSet = m_pColorSet) == NULL) { return ResultFromScode(S_FALSE); } return NOERROR; } //+------------------------------------------------------------------------- // // Function: MfCallBackFunForDraw // // Synopsis: callback function for drawing metafiles -- call's the caller's // draw method (via a passed in this pointer) // // Effects: // // Arguments: [hdc] -- the device context // [lpHTable] -- pointer to the MF handle table // [lpMFR] -- pointer to metafile record // [nObj] -- number of objects // // Requires: // // Returns: non-zero to continue, zero stops the drawing // // Signals: // // Modifies: // // Algorithm: // // History: dd-mmm-yy Author Comment // 29-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- int CALLBACK __loadds MfCallbackFuncForDraw (HDC hdc, HANDLETABLE FAR* lpHTable, METARECORD FAR* lpMFR, int nObj, LPARAM lpobj) { VDATEHEAP(); return ((CMfObject FAR*) lpobj)->CallbackFuncForDraw(hdc, lpHTable, lpMFR, nObj); } //+------------------------------------------------------------------------- // // Function: MfCallbackFuncForGetColorSet // // Synopsis: callback function for grabbing the palette from a metafile // // Effects: // // Arguments: [hdc] -- the device context // [lpHTable] -- pointer to the MF handle table // [lpMFR] -- pointer to metafile record // [nObj] -- number of objects // // Requires: // // Returns: non-zero to continue, zero stops the drawing // // Signals: // // Modifies: // // Algorithm: // // History: dd-mmm-yy Author Comment // 29-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- int CALLBACK __loadds MfCallbackFuncForGetColorSet (HDC hdc, HANDLETABLE FAR* lpHTable, METARECORD FAR* lpMFR, int nObj, LPARAM lpobj) { VDATEHEAP(); return ((CMfObject FAR*) lpobj)->CallbackFuncForGetColorSet(hdc, lpHTable, lpMFR, nObj); } //+------------------------------------------------------------------------- // // Member: CMfObject::CallbackFuncForGetColorSet // // Synopsis: Merges all the color palettes in the metafile into // one palette (called when GetColorSet enumerates the metafile) // // Effects: // // Arguments: [hdc] -- the device context // [lpHTable] -- pointer to the MF handle table // [lpMFR] -- pointer to metafile record // [nObj] -- number of objects // // Requires: // // Returns: non-zero to continue, zero stops the drawing // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 29-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- int CMfObject::CallbackFuncForGetColorSet(HDC /* UNUSED hdc */, LPHANDLETABLE /* UNUSED lpHTable */, LPMETARECORD lpMFR, int /* UNUSED nObj */) { VDATEHEAP(); if (lpMFR->rdFunction == META_CREATEPALETTE) { LPLOGPALETTE lplogpal = (LPLOGPALETTE) &(lpMFR->rdParm[0]); UINT uPalSize = (lplogpal->palNumEntries) * sizeof(PALETTEENTRY) + 2 * sizeof(WORD); if (m_pColorSet == NULL) { // This is the first CreatePalette record. m_pColorSet = (LPLOGPALETTE)PubMemAlloc(uPalSize); if(NULL == m_pColorSet) { m_error = ResultFromScode(E_OUTOFMEMORY); return FALSE; } _xmemcpy(m_pColorSet, lplogpal, (size_t) uPalSize); } // if we hit more than one CreatePalette record then, we need to // merge those palette records. // REVIEW32:: err, we don't ever seem to do this // mergeing referred to above :( } return TRUE; } //+------------------------------------------------------------------------- // // Member: CMfObject::CallbackFuncForDraw // // Synopsis: Draws the metafile // // Effects: // // Arguments: [hdc] -- the device context // [lpHTable] -- pointer to the MF handle table // [lpMFR] -- pointer to metafile record // [nObj] -- number of objects // // Requires: // // Returns: non-zero to continue, zero stops the drawing // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 17-Dec-93 ChrisWe fixed second argument to SelectPalette calls // 29-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- int CMfObject::CallbackFuncForDraw (HDC hdc, LPHANDLETABLE lpHTable, LPMETARECORD lpMFR, int nObj) { VDATEHEAP(); if (!--m_nRecord) { m_nRecord = RECORD_COUNT; if (m_pfnContinue && !((*(m_pfnContinue))(m_dwContinue))) { m_error = ResultFromScode(E_ABORT); return FALSE; } } if (m_fMetaDC) { switch (lpMFR->rdFunction) { case META_SETWINDOWORG: SetPictOrg (hdc, (SHORT)lpMFR->rdParm[1], (SHORT)lpMFR->rdParm[0], FALSE); return TRUE; case META_OFFSETWINDOWORG: SetPictOrg (hdc, (SHORT)lpMFR->rdParm[1], (SHORT)lpMFR->rdParm[0], TRUE); return TRUE; case META_SETWINDOWEXT: SetPictExt (hdc, (SHORT)lpMFR->rdParm[1], (SHORT)lpMFR->rdParm[0]); return TRUE; case META_SCALEWINDOWEXT: ScalePictExt (hdc, (SHORT)lpMFR->rdParm[3], (SHORT)lpMFR->rdParm[2], (SHORT)lpMFR->rdParm[1], (SHORT)lpMFR->rdParm[0]); return TRUE; case META_SCALEVIEWPORTEXT: ScaleRectExt (hdc, (SHORT)lpMFR->rdParm[3], (SHORT)lpMFR->rdParm[2], (SHORT)lpMFR->rdParm[1], (SHORT)lpMFR->rdParm[0]); return TRUE; case META_SAVEDC: { BOOL fSucceeded = PushDc(); LEVERIFY( fSucceeded ); if (!fSucceeded) return FALSE; break; } case META_RESTOREDC: LEVERIFY( PopDc() ); break; case META_SELECTPALETTE: { // All selectpalette records are recorded such that the // palette is rendered in foreground mode. Rather than // allowing the record to play out in this fashion, we // grab the handle and select it ourselves, forcing it // to background (using colors already mapped in the DC) // Dupe the palette. HPALETTE hPal = UtDupPalette((HPALETTE) lpHTable->objectHandle[lpMFR->rdParm[0]]); // Select the dupe into the DC. EnumMetaFile calls DeleteObject // on the palette handle in the metafile handle table. If that // palette is currently selected into a DC, we rip and leak the // resource. LEVERIFY( NULL != SelectPalette(hdc, hPal, TRUE) ); // if we had previously saved a palette, we need to delete // it (for the case of multiple SelectPalette calls in a // MetaFile). if (m_hPalLast) { DeleteObject(m_hPalLast); } // remember our duped palette so that it can be properly destroyed // later. m_hPalLast = hPal; return TRUE; } case META_OFFSETVIEWPORTORG: AssertSz(0, "OffsetViewportOrg() in metafile"); return TRUE; case META_SETVIEWPORTORG: AssertSz(0, "SetViewportOrg() in metafile"); return TRUE; case META_SETVIEWPORTEXT: AssertSz(0, "SetViewportExt() in metafile"); return TRUE; case META_SETMAPMODE: AssertSz(lpMFR->rdParm[0] == MM_ANISOTROPIC, "SetmapMode() in metafile with invalid mapping mode"); return TRUE; default: break; } } else { // non-metafile DC. (ScreenDC or other DC...) if (lpMFR->rdFunction == META_SELECTPALETTE) { // All selectpalette records are recorded such that the // palette is rendered in foreground mode. Rather than // allowing the record to play out in this fashion, we // grab the handle and select it ourselves, forcing it // to background (using colors already mapped in the DC) HPALETTE hPal = UtDupPalette((HPALETTE) lpHTable->objectHandle[lpMFR->rdParm[0]]); HPALETTE hPalOld = SelectPalette(hdc, hPal, TRUE); if (!m_hPalDCOriginal) { m_hPalDCOriginal = hPalOld; } else { // This case gets hit if we have already stored // the original palette from the DC. This means // that hPalOld is a palette we have created using // UtDupPal above. This means we need to delete // the old palette and remember the new one. if(hPalOld) DeleteObject(hPalOld); } m_hPalLast = hPal; return TRUE; } } LEVERIFY( PlayMetaFileRecord (hdc, lpHTable, lpMFR, (unsigned) nObj) ); return TRUE; } //+------------------------------------------------------------------------- // // Member: CMfObject::SetPictOrg (private) // // Synopsis: Sets the hdc's origin via SetWindowOrg // // Effects: // // Arguments: [hdc] -- the device context // [xOrg] -- the x origin // [yOrg] -- the y origin // [fOffset] -- if TRUE, [xOrg] and [yOrg] are offsets // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 29-Nov-93 alexgo 32bit port // // Notes: used by the metafile interpreter // //-------------------------------------------------------------------------- INTERNAL_(void) CMfObject::SetPictOrg (HDC hdc, int xOrg, int yOrg, BOOL fOffset) { VDATEHEAP(); if (fOffset) { m_pCurMdc->xMwo += xOrg; m_pCurMdc->yMwo += yOrg; } else { m_pCurMdc->xMwo = xOrg; m_pCurMdc->yMwo = yOrg; } if (m_pCurMdc->xMwe && m_pCurMdc->yMwe) { LEVERIFY ( SetWindowOrg (hdc, // Review (davepl) MEIN GOT! (m_pCurMdc->xMwo - MulDiv (m_pMetaInfo->xro, m_pCurMdc->xMwe, m_pCurMdc->xre)), (m_pCurMdc->yMwo - MulDiv (m_pMetaInfo->yro, m_pCurMdc->yMwe, m_pCurMdc->yre))) ); } } //+------------------------------------------------------------------------- // // Member: CMfObject::SetPictExt // // Synopsis: Sets teh metafile's extents // // Effects: // // Arguments: [hdc] -- the device context // [xExt] -- the X-extent // [yExt] -- the Y-extent // // Requires: // // Returns: void // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 30-Nov-93 alexgo 32bit port // // Notes: used by the metafile interpreter // //-------------------------------------------------------------------------- INTERNAL_(void) CMfObject::SetPictExt (HDC hdc, int xExt, int yExt) { VDATEHEAP(); m_pCurMdc->xMwe = xExt; m_pCurMdc->yMwe = yExt; // Assert( m_pCurMdc->xre && m_pCurMdc->yre ); int xNewExt = MulDiv (m_pMetaInfo->xwe, xExt, m_pCurMdc->xre); int yNewExt = MulDiv (m_pMetaInfo->ywe, yExt, m_pCurMdc->yre); int xNewOrg = m_pCurMdc->xMwo - MulDiv (m_pMetaInfo->xro, xExt, m_pCurMdc->xre); int yNewOrg = m_pCurMdc->yMwo - MulDiv (m_pMetaInfo->yro, yExt, m_pCurMdc->yre); LEVERIFY( SetWindowExt (hdc, xNewExt, yNewExt) ); LEVERIFY( SetWindowOrg (hdc, xNewOrg, yNewOrg) ); } //+------------------------------------------------------------------------- // // Member: CMfObject::ScalePictExt // // Synopsis: Scales the metafile // // Effects: // // Arguments: [hdc] -- the device context // [xNum] -- the x numerator // [xDenom] -- the x demominator // [yNum] -- the y numberator // [yDenom] -- the y demoninator // // Requires: // // Returns: void // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 30-Nov-93 alexgo 32bit port // // Notes: used by the metafile interpreter // //-------------------------------------------------------------------------- INTERNAL_(void) CMfObject::ScalePictExt (HDC hdc, int xNum, int xDenom, int yNum, int yDenom) { VDATEHEAP(); Assert( xDenom && yDenom ); int xNewExt = MulDiv (m_pCurMdc->xMwe, xNum, xDenom); int yNewExt = MulDiv (m_pCurMdc->yMwe, yNum, yDenom); SetPictExt(hdc, xNewExt, yNewExt); } //+------------------------------------------------------------------------- // // Member: CMfObject::ScaleRectExt // // Synopsis: scales the viewport // // Effects: // // Arguments: [hdc] -- the device context // [xNum] -- the x numerator // [xDenom] -- the x demominator // [yNum] -- the y numberator // [yDenom] -- the y demoninator // // Requires: // // Returns: void // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 30-Nov-93 alexgo 32bit port // // Notes: Used by the metafile interpreter // //-------------------------------------------------------------------------- INTERNAL_(void) CMfObject::ScaleRectExt(HDC hdc, int xNum, int xDenom, int yNum, int yDenom) { VDATEHEAP(); AssertSz( xDenom, "Denominator is 0 for x-rect scaling"); AssertSz( yDenom, "Denominator is 0 for y-rect scaling"); m_pCurMdc->xre = MulDiv (m_pCurMdc->xre, xNum, xDenom); m_pCurMdc->yre = MulDiv (m_pCurMdc->yre, yNum, yDenom); SetPictExt (hdc, m_pCurMdc->xMwe, m_pCurMdc->yMwe); } //+------------------------------------------------------------------------- // // Member: CMfObject::PushDC // // Synopsis: pushes metafile info onto a stack // // Effects: // // Arguments: void // // Requires: // // Returns: BOOL -- TRUE if successful, FALSE otherwise // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 30-Nov-93 alexgo 32bit port // // Notes: Used by the metafile interpreter engine. // //-------------------------------------------------------------------------- INTERNAL_(BOOL) CMfObject::PushDc (void) { VDATEHEAP(); LPMETADC pNode = NULL; pNode = (LPMETADC) PrivMemAlloc(sizeof(METADC)); if (pNode) { *pNode = *m_pCurMdc; m_pCurMdc->pNext = pNode; pNode->pNext = NULL; m_pCurMdc = pNode; return TRUE; } m_error = ResultFromScode(E_OUTOFMEMORY); return FALSE; } //+------------------------------------------------------------------------- // // Member: CMfObject::PopDC // // Synopsis: pops metafile info from the metafile info stack // // Effects: // // Arguments: void // // Requires: // // Returns: BOOL -- TRUE if successful, FALSE otherwise (more pops // than pushes) // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 30-Nov-93 alexgo 32bit port // // Notes: used in the metafile interpreter // //-------------------------------------------------------------------------- INTERNAL_(BOOL) CMfObject::PopDc (void) { VDATEHEAP(); LPMETADC pPrev = (LPMETADC) (m_pMetaInfo); LPMETADC pCur = ((LPMETADC) (m_pMetaInfo))->pNext; if (NULL == pCur) { LEWARN( NULL == pCur, "More pops than pushes from DC stack" ); return FALSE; } while (pCur->pNext) { pPrev = pCur; pCur = pCur->pNext; } if (pCur) { PrivMemFree(pCur); } pPrev->pNext = NULL; m_pCurMdc = pPrev; return TRUE; } //+------------------------------------------------------------------------- // // Member: CMfObject::CleanStack // // Synopsis: Deletes the stack of metafile info // // Effects: // // Arguments: void // // Requires: // // Returns: void // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 30-Nov-93 alexgo 32bit port // // Notes: used in the metafile interpreter // //-------------------------------------------------------------------------- INTERNAL_(void) CMfObject::CleanStack (void) { VDATEHEAP(); LPMETADC pCur; while (NULL != (pCur = ((LPMETADC) (m_pMetaInfo))->pNext)) { ((LPMETADC) (m_pMetaInfo))->pNext = pCur->pNext; PrivMemFree(pCur); } PrivMemFree(m_pMetaInfo); m_pCurMdc = NULL; m_pMetaInfo = NULL; } //+------------------------------------------------------------------------- // // Function: QD2GDI // // Synopsis: Converts macintosh pictures to Win32 GDI metafiles // // Effects: // // Arguments: [hBits] -- handle to the mac picture bits // // Requires: // // Returns: HMETAFILE // // Signals: // // Modifies: // // Algorithm: Loads ole2conv.dll and calls QD2GDI in that dll // // History: dd-mmm-yy Author Comment // 30-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- FARINTERNAL_(HMETAFILE) QD2GDI (HANDLE hBits) { VDATEHEAP(); USERPREFS userPrefs = { {'Q','D','2','G','D','I'}, //signature 2, //structure version number sizeof(USERPREFS), //structure size NULL, //source filename NULL, //or source handle NULL, //returned memory-based //metafile 3, //simulated pattern lines 5, //use max dimension //non-squarer pen 1, //arithmetic transfer 1, //create opaque text 1, //simulate non-rectangular //regions 0, //don't optimize for PowerPoint {0,0,0,0,0,0} //reserved }; HINSTANCE hinstFilter; void (FAR PASCAL *qd2gdiPtr)( USERPREFS FAR *, PICTINFO FAR *); PICTINFO pictinfo; hinstFilter = LoadLibrary(OLESTR("OLECNV32.DLL")); if (hinstFilter == NULL) { return NULL; } *((FARPROC*)&qd2gdiPtr) = GetProcAddress(hinstFilter, "QD2GDI"); userPrefs.sourceFilename = NULL; userPrefs.sourceHandle = hBits; pictinfo.hmf = NULL; if (qd2gdiPtr == NULL) { goto errRtn; } (*qd2gdiPtr)( (USERPREFS FAR *)&userPrefs, (PICTINFO FAR *)&pictinfo); errRtn: LEVERIFY( FreeLibrary(hinstFilter) ); return (HMETAFILE)pictinfo.hmf; } //+------------------------------------------------------------------------- // // Function: MfGetSize // // Synopsis: Returns the size of a metafile // // Effects: // // Arguments: [lphmf] -- pointer to the metafile handle // // Requires: // // Returns: DWORD -- the size of the metafile // // Signals: // // Modifies: // // Algorithm: // // History: dd-mmm-yy Author Comment // 30-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- INTERNAL_(DWORD) MfGetSize (LPHANDLE lphmf) { VDATEHEAP(); return GetMetaFileBitsEx((HMETAFILE)*lphmf,0,NULL); } //+------------------------------------------------------------------------- // // Function: OleIsDcMeta // // Synopsis: Returns whether a device context is really a metafile // // Effects: // // Arguments: [hdc] -- the device context // // Requires: // // Returns: BOOL (TRUE if a metafile) // // Signals: // // Modifies: // // Algorithm: // // History: dd-mmm-yy Author Comment // 30-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDAPI_(BOOL) OleIsDcMeta (HDC hdc) { VDATEHEAP(); return (GetDeviceCaps (hdc, TECHNOLOGY) == DT_METAFILE); } //+------------------------------------------------------------------------- // // Member: CMfObject::Load // // Synopsis: Loads an metafile object from the given stream // // Effects: // // Arguments: [lpstream] -- the stream from which to load // [fReadHeaderOnly] -- if TRUE, then only the header is // read // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOlePresObj // // Algorithm: // // History: dd-mmm-yy Author Comment // 30-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CMfObject::Load(LPSTREAM lpstream, BOOL fReadHeaderOnly) { VDATEHEAP(); DWORD dwBuf[4]; HRESULT error; /* read dwCompression, width, height, size of data */ error = StRead(lpstream, dwBuf, 4*sizeof(DWORD)); if (error) { return error; } AssertSz (dwBuf[0] == 0, "Picture compression factor is non-zero"); m_lWidth = (LONG) dwBuf[1]; m_lHeight = (LONG) dwBuf[2]; m_dwSize = dwBuf[3]; if (!m_dwSize || fReadHeaderOnly) { return NOERROR; } return UtGetHMFFromMFStm(lpstream, m_dwSize, m_fConvert, (LPLPVOID) &m_hPres); } //+------------------------------------------------------------------------- // // Member: CMfObject::Save // // Synopsis: Saves the metafile to the given stream // // Effects: // // Arguments: [lpstream] -- the stream to save to // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOlePresObj // // Algorithm: // // History: dd-mmm-yy Author Comment // 30-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CMfObject::Save(LPSTREAM lpstream) { VDATEHEAP(); HRESULT error; DWORD dwBuf[4]; /* write dwCompression, width, height, size of data */ dwBuf[0] = 0L; dwBuf[1] = (DWORD) m_lWidth; dwBuf[2] = (DWORD) m_lHeight; dwBuf[3] = m_dwSize; error = StWrite(lpstream, dwBuf, sizeof(dwBuf)); if (error) { return error; } // if blank object, don't write any more; no error. if (IsBlank() || m_hPres == NULL) { StSetSize(lpstream, 0, TRUE); return NOERROR; } return UtHMFToMFStm((LPLPVOID)&m_hPres, m_dwSize, lpstream); } //+------------------------------------------------------------------------- // // Member: CMfObject::GetExtent // // Synopsis: Retrieves the extents of the metafile // // Effects: // // Arguments: [dwDrawAspect] -- the drawing aspect we're interested in // (must match the aspect of the current // metafile) // [lpsizel] -- where to put the extent info // // Requires: // // Returns: NOERROR, DV_E_DVASPECT, OLE_E_BLANK // // Signals: // // Modifies: // // Derivation: IOlePresObj // // Algorithm: // // History: dd-mmm-yy Author Comment // 30-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CMfObject::GetExtent(DWORD dwDrawAspect, LPSIZEL lpsizel) { VDATEHEAP(); if (0 == (dwDrawAspect & m_dwAspect)) { return ResultFromScode(DV_E_DVASPECT); } if (IsBlank()) { return ResultFromScode(OLE_E_BLANK); } lpsizel->cx = m_lWidth; lpsizel->cy = m_lHeight; return NOERROR; } //+------------------------------------------------------------------------- // // Member: CMfObject::IsBlank // // Synopsis: Returns whether or not the metafile is blank // // Effects: // // Arguments: void // // Requires: // // Returns: TRUE/FALSE // // Signals: // // Modifies: // // Derivation: IOlePresObj // // Algorithm: // // History: dd-mmm-yy Author Comment // 30-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP_(BOOL) CMfObject::IsBlank(void) { VDATEHEAP(); return (m_dwSize ? FALSE : TRUE); } //+------------------------------------------------------------------------- // // Member: CMfObject::LoadHPRES (private) // // Synopsis: Loads the presentation from the cache's stream and returns // a handle to it // // Effects: // // Arguments: void // // Requires: // // Returns: HANDLE to the metafile // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 30-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- INTERNAL_(HANDLE) CMfObject::LoadHPRES(void) { VDATEHEAP(); LPSTREAM pstm = NULL; if (NULL != m_pCacheNode) { pstm = m_pCacheNode->GetStm(TRUE /*fSeekToPresBits*/, STGM_READ); } if (pstm) { LEVERIFY( SUCCEEDED(Load(pstm))); pstm->Release(); } return m_hPres; } //+------------------------------------------------------------------------- // // Member: CMfObject::DiscardHPRES // // Synopsis: deletes the stored metafile // // Effects: // // Arguments: void // // Requires: // // Returns: void // // Signals: // // Modifies: // // Derivation: IOlePresObj // // Algorithm: // // History: dd-mmm-yy Author Comment // 30-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP_(void) CMfObject::DiscardHPRES(void) { VDATEHEAP(); if (m_hPres) { LEVERIFY( DeleteMetaFile (m_hPres) ); m_hPres = NULL; } } //+------------------------------------------------------------------------- // // Member: CMfObject::GetCopyOfHPRES (private) // // Synopsis: makes a copy of the metafile (if one is present), otherwise // just load it from the stream (but don't store it in [this] // object) // // Effects: // // Arguments: void // // Requires: // // Returns: HANDLE to the metafile // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 30-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- INTERNAL_(HANDLE) CMfObject::GetCopyOfHPRES(void) { VDATEHEAP(); HANDLE hPres; // Make a copy if the presentation data is already loaded if (m_hPres) { return CopyMetaFile(m_hPres, NULL); } // Load the presentation data now and return the same handle. // No need to copy the data. If the caller wants the m_hPres to be // set he would call LoadHPRES() directly. hPres = LoadHPRES(); m_hPres = NULL; // NULL this, LoadHPRES set it. return hPres; } //+------------------------------------------------------------------------- // // Member: CMfObject::Dump, public (_DEBUG only) // // Synopsis: return a string containing the contents of the data members // // Effects: // // Arguments: [ppszDump] - an out pointer to a null terminated character array // [ulFlag] - flag determining prefix of all newlines of the // out character array (default is 0 - no prefix) // [nIndentLevel] - will add a indent prefix after the other prefix // for ALL newlines (including those with no prefix) // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: [ppszDump] - argument // // Derivation: // // Algorithm: use dbgstream to create a string containing information on the // content of data structures // // History: dd-mmm-yy Author Comment // 01-Feb-95 t-ScottH author // // Notes: // //-------------------------------------------------------------------------- #ifdef _DEBUG HRESULT CMfObject::Dump(char **ppszDump, ULONG ulFlag, int nIndentLevel) { int i; char *pszPrefix; char *pszHRESULT; char *pszDVASPECT; dbgstream dstrPrefix; dbgstream dstrDump(500); // determine prefix of newlines if ( ulFlag & DEB_VERBOSE ) { dstrPrefix << this << " _VB "; } // determine indentation prefix for all newlines for (i = 0; i < nIndentLevel; i++) { dstrPrefix << DUMPTAB; } pszPrefix = dstrPrefix.str(); // put data members in stream dstrDump << pszPrefix << "No. of References = " << m_ulRefs << endl; dstrDump << pszPrefix << "pMETAINFO (Metafile information) = " << m_pMetaInfo << endl; dstrDump << pszPrefix << "pMETADC (current device context) = " << m_pCurMdc << endl; dstrDump << pszPrefix << "IsMetaDeviceContext? = "; if (m_fMetaDC == TRUE) { dstrDump << "TRUE" << endl; } else { dstrDump << "FALSE" << endl; } dstrDump << pszPrefix << "No. of Records in Metafile = " << m_nRecord << endl; pszHRESULT = DumpHRESULT(m_error); dstrDump << pszPrefix << "Error code = " << pszHRESULT << endl; CoTaskMemFree(pszHRESULT); dstrDump << pszPrefix << "pLOGPALETTE (Color set palette) = " << m_pColorSet << endl; dstrDump << pszPrefix << "ConvertFromMac? = "; if (m_fConvert == TRUE) { dstrDump << "TRUE" << endl; } else { dstrDump << "FALSE" << endl; } dstrDump << pszPrefix << "Continue = " << ((ULONG) m_dwContinue) << endl; dstrDump << pszPrefix << "fp Continue = " << m_pfnContinue<< endl; pszDVASPECT = DumpDVASPECTFlags(m_dwAspect); dstrDump << pszPrefix << "Aspect flags = " << pszDVASPECT << endl; CoTaskMemFree(pszDVASPECT); dstrDump << pszPrefix << "Size = " << m_dwSize << endl; dstrDump << pszPrefix << "Width = " << m_lWidth << endl; dstrDump << pszPrefix << "Height = " << m_lHeight << endl; dstrDump << pszPrefix << "pCacheNode = " << m_pCacheNode << endl; // cleanup and provide pointer to character array *ppszDump = dstrDump.str(); if (*ppszDump == NULL) { *ppszDump = UtDupStringA(szDumpErrorMessage); } CoTaskMemFree(pszPrefix); return NOERROR; } #endif // _DEBUG //+------------------------------------------------------------------------- // // Function: DumpCMfObject, public (_DEBUG only) // // Synopsis: calls the CMfObject::Dump method, takes care of errors and // returns the zero terminated string // // Effects: // // Arguments: [pMFO] - pointer to CMfObject // [ulFlag] - flag determining prefix of all newlines of the // out character array (default is 0 - no prefix) // [nIndentLevel] - will add a indent prefix after the other prefix // for ALL newlines (including those with no prefix) // // Requires: // // Returns: character array of structure dump or error (null terminated) // // Signals: // // Modifies: // // Algorithm: // // History: dd-mmm-yy Author Comment // 01-Feb-95 t-ScottH author // // Notes: // //-------------------------------------------------------------------------- #ifdef _DEBUG char *DumpCMfObject(CMfObject *pMFO, ULONG ulFlag, int nIndentLevel) { HRESULT hresult; char *pszDump; if (pMFO == NULL) { return UtDupStringA(szDumpBadPtr); } hresult = pMFO->Dump(&pszDump, ulFlag, nIndentLevel); if (hresult != NOERROR) { CoTaskMemFree(pszDump); return DumpHRESULT(hresult); } return pszDump; } #endif // _DEBUG