/* ddedo.cpp DDE Data Object copyright (c) 1992 Microsoft Corporation Abstract: This module contains the methods for DdeObject::DataObject Author: Jason Fuller (jasonful) 24-July-1992 */ #include "ddeproxy.h" #include #include "trgt_dev.h" #define f10UserModel // Should we ignore a request by a 2.0 client to get advise-on-change, // so that the user must do an explicit File/Update or File/Close? // Probably yes, because: // 1) Advise-on-change can be expensive for apps like PaintBrush. // 2) It is confusing if the container asks for change updates // ONLY on presentation and not on native because when the user // closes the server and is asked "Do you want to update?" he'll say no // because the picture LOOKS correct even though the container does not // have the native data. // 3) Excel: if A1 is the first cell you create, changes to other cells // will not be sent to the client until you change A1 again. // If advises are only sent explicitly, then all the cells extant at that // time will be considered part of the object. ASSERTDATA // // DataObject methods // STDUNKIMPL_FORDERIVED(DdeObject, DataObjectImpl) static inline INTERNAL_(BOOL) NotEqual (DVTARGETDEVICE FAR* ptd1, DVTARGETDEVICE FAR* ptd2) { if (NULL==ptd1 && NULL==ptd2) return FALSE; else if ((ptd1 && !ptd2) || (ptd2 && !ptd1) || (ptd1->tdSize != ptd2->tdSize)) { return TRUE; } else #ifdef WIN32 return 0 != memcmp(ptd1, ptd2, (size_t)ptd1->tdSize); #else return 0 != _fmemcmp(ptd1, ptd2, (size_t)ptd1->tdSize); #endif } // GetData // // The data is copied out of a private cache consisting of // DdeObject::m_hNative, DdeObject::m_hPict, and DdeObject::m_hExtra. // If the cache is empty, data is requested using WM_DDE_REQUEST. // The cache should only be empty before the first DDE_DATA message // is received. // See DdeObject::KeepData() // STDMETHODIMP NC(CDdeObject,CDataObjectImpl)::GetData (LPFORMATETC pformatetcIn, LPSTGMEDIUM pmedium) { intrDebugOut((DEB_ITRACE, "CDdeObject::GetData(%x,pformatetcIn=%x)\n", this,pformatetcIn)); lStart: intrDebugOut((DEB_ITRACE,"::GetData(%x)lStart\n",this)); LPSTR lpGlobal=NULL; HRESULT hres; VDATEPTROUT (pmedium, STGMEDIUM); pmedium->tymed = TYMED_NULL; pmedium->pUnkForRelease = NULL; if ((hres = wVerifyFormatEtc (pformatetcIn)) != NOERROR) { goto exitRtn; } hres = E_UNEXPECTED; // assume error unless a clipboard format is found. if (DVASPECT_ICON & pformatetcIn->dwAspect) { hres = GetDefaultIcon(m_pDdeObject->m_clsid, NULL, &pmedium->hGlobal); if (hres != NOERROR) { goto exitRtn; } hres = NOERROR; goto lDone; } if (m_pDdeObject->m_fGotCloseData) { // If we already got DDE_DATA on close, don't try requesting more // data. (MSDraw will give a bogus metafile.) hres=OLE_E_NOTRUNNING; goto exitRtn; } if (NotEqual (pformatetcIn->ptd, m_pDdeObject->m_ptd)) { // If caller is asking for a different target device // (We assume a different pointer points to a different target device) if (NOERROR!=m_pDdeObject->SetTargetDevice (pformatetcIn->ptd)) { // 1.0 server did not accept target device hres=DATA_E_FORMATETC; goto exitRtn; } Assert (hres!=NOERROR); // Must do RequestData with new target device } else { // Pick a member handle (H) to return, based on clipboard format CF. // If caller did not pass in its own medium, we must allocate a new // handle. #define macro(CF,H) \ if (pformatetcIn->cfFormat == CF) { \ if (m_pDdeObject->H) { \ if (pmedium->tymed == TYMED_NULL) { \ intrDebugOut((DEB_ITRACE,"::GetData giving cf==%x hData=%x\n",CF,m_pDdeObject->H)); \ pmedium->hGlobal = m_pDdeObject->H; \ m_pDdeObject->H = NULL; \ } \ hres = NOERROR; /* found data in right format */ \ } \ } macro (g_cfNative, m_hNative) else macro (m_pDdeObject->m_cfPict, m_hPict ) else macro (m_pDdeObject->m_cfExtra,m_hExtra ) // If we gave away our picture, we must forget its format. if (pformatetcIn->cfFormat == m_pDdeObject->m_cfPict) m_pDdeObject->m_cfPict = 0; #undef macro } if (hres!=NOERROR) { intrDebugOut((DEB_ITRACE, "::GetData(%x) posting DDE_REQUEST for cf==%x\n", this, (ULONG)pformatetcIn->cfFormat)); // Didn't find a handle for the requested format, // or handle was NULL, so request it. // The sequence should be: // GetData -> DDE_REQUEST -> DDE_DATA -> OnData -> return to GetData if (hres=m_pDdeObject->RequestData (pformatetcIn->cfFormat) != NOERROR) { intrDebugOut((DEB_ITRACE, "::GetData(%x) RequestData returns error %x\n", this,hres)); hres = DV_E_CLIPFORMAT; goto exitRtn; } // By now, a KeepData() should have been done with the right cf, // so try again. intrDebugOut((DEB_ITRACE, "::GetData(%x) KeepData should have been called. Go again\n", this)); Puts ("KeepData should have been called. Trying GetData again.\n"); goto lStart; } lDone: Puts ("pmedium->hGlobal =="); Puth(pmedium->hGlobal); Putn(); pmedium->pUnkForRelease = NULL; // Let caller release medium // Must set tymed _after_ the goto loop. // Otherwise it'll be changed the second time around. // tell caller what we're returning pmedium->tymed = UtFormatToTymed (pformatetcIn->cfFormat); intrDebugOut((DEB_ITRACE, "::GetData(%x)tymed=%x cfFormat=%x hGlobal=%x\n", this, pmedium->tymed, (USHORT)pformatetcIn->cfFormat, pmedium->hGlobal)); exitRtn: intrDebugOut((DEB_ITRACE, "::GetData(%x)hres=%x\n", this, hres)); return hres; } STDMETHODIMP NC(CDdeObject,CDataObjectImpl)::GetDataHere (LPFORMATETC pformatetcIn, LPSTGMEDIUM pmedium) { intrDebugOut((DEB_ITRACE, "CDdeObject::GetDataHere(%x,pformatetcIn=%x)\n", this, pformatetcIn)); HRESULT hresult = NOERROR; STGMEDIUM medium; if (!(pformatetcIn->tymed & TYMED_HGLOBAL)) { intrDebugOut((DEB_ITRACE, "::GetDataHere(%x)DV_E_TYMED(%x)\n", this,DV_E_TYMED)); // Cannot GetDataHere for GDI objects hresult = DV_E_TYMED; goto exitRtn; } RetErr (GetData (pformatetcIn, &medium)); if (medium.tymed != TYMED_HGLOBAL) { intrDebugOut((DEB_ITRACE, "::GetDataHere(%x)medium.tymed != TYMED_HGLOBAL\n", this)); hresult = ResultFromScode (DV_E_TYMED); goto errRtn; } pmedium->tymed = medium.tymed; pmedium->pUnkForRelease = medium.pUnkForRelease; ErrRtnH (wHandleCopy (pmedium->hGlobal, medium.hGlobal)); errRtn: ReleaseStgMedium (&medium); exitRtn: intrDebugOut((DEB_ITRACE, "CDdeObject::GetDataHere(%x) returning %x\n", this,hresult)); return hresult; } STDMETHODIMP NC(CDdeObject,CDataObjectImpl)::QueryGetData (LPFORMATETC pformatetcIn) { HRESULT hr; intrDebugOut((DEB_ITRACE, "%x _IN CDdeObject::QueryGetData(pformatetcIn=%x)\n", this, pformatetcIn)); hr = wVerifyFormatEtc (pformatetcIn); if (hr != NOERROR) { goto exitRtn; } if (pformatetcIn->cfFormat == g_cfEmbeddedObject || pformatetcIn->cfFormat == g_cfEmbedSource || pformatetcIn->cfFormat == g_cfLinkSource || pformatetcIn->cfFormat == g_cfFileName || pformatetcIn->cfFormat == g_cfCustomLinkSource || pformatetcIn->cfFormat == g_cfObjectDescriptor || pformatetcIn->cfFormat == g_cfLinkSrcDescriptor) { hr = S_FALSE; } hr = m_pDdeObject->IsFormatAvailable (pformatetcIn); exitRtn: intrDebugOut((DEB_ITRACE, "%x _OUT CDdeObject::QueryGetData returning %x\n", this,hr)); return(hr); } STDMETHODIMP NC(CDdeObject,CDataObjectImpl)::SetData (LPFORMATETC pformatetc, STGMEDIUM FAR* pmedium, BOOL fRelease) { HANDLE hDdePoke; HRESULT hresult; intrDebugOut((DEB_ITRACE, "%x _IN CDdeObject::SetData(pformatetc=%x)\n", this, pformatetc)); hresult = wVerifyFormatEtc (pformatetc); if (hresult != NOERROR) { goto exitRtn; } intrDebugOut((DEB_ITRACE, "%x ::SetData(pformatetc->cfFormat=%x)\n", this, (ULONG)pformatetc->cfFormat)); if (pformatetc->dwAspect & DVASPECT_ICON) { intrDebugOut((DEB_ITRACE, "%x ::SetData dwAspect & DVASPECT_ICON\n", this)); hresult = DV_E_DVASPECT; goto exitRtn; } if (pformatetc->ptd != m_pDdeObject->m_ptd) { // If caller is setting with a different target device // (We assume a different pointer points to a different target device) if (NOERROR != m_pDdeObject->SetTargetDevice (pformatetc->ptd)) { intrDebugOut((DEB_IERROR, "%x ::SetData server did not accept target device\n", this)); hresult = DV_E_DVTARGETDEVICE; goto exitRtn; } } if (hDdePoke = wPreparePokeBlock (pmedium->hGlobal, pformatetc->cfFormat, m_pDdeObject->m_aClass, m_pDdeObject->m_bOldSvr)) { hresult = m_pDdeObject->Poke (m_pDdeObject->m_aItem, hDdePoke); if (fRelease) ReleaseStgMedium (pmedium); goto exitRtn; } else { hresult = E_OUTOFMEMORY; } exitRtn: intrDebugOut((DEB_ITRACE,"%x _OUT ::SetData returns %x\n",this,hresult)); return(hresult); } STDMETHODIMP NC(CDdeObject,CDataObjectImpl)::DAdvise (FORMATETC FAR* pformatetc, DWORD grfAdvf, IAdviseSink FAR* pAdvSink, DWORD FAR* pdwConnection) { HRESULT hresult; HRESULT hresLookup; FORMATETC formatetc; intrDebugOut((DEB_ITRACE, "%x _IN CDdeObject::DAdvise(pformatetc=%x,grfAdvf=%x,pAdvSink=%x)\n", this, pformatetc, grfAdvf, pAdvSink)); VDATEPTROUT (pdwConnection, DWORD); *pdwConnection = 0; wNormalize (pformatetc, &formatetc); hresult =wVerifyFormatEtc (&formatetc); if ( hresult != NOERROR) { goto errRtn; } intrDebugOut((DEB_ITRACE, "%x ::DAdvise pformatetc->cfFormat=%x\n", this, pformatetc->cfFormat)); if (NotEqual (formatetc.ptd, m_pDdeObject->m_ptd)) { if (NOERROR != m_pDdeObject->SetTargetDevice (formatetc.ptd)) { hresult= DV_E_DVTARGETDEVICE; goto errRtn; } } hresLookup = m_pDdeObject->m_ConnectionTable.Lookup (formatetc.cfFormat, NULL); if (hresLookup != NOERROR) { // We have not already done a DDE advise for this format Puts (" m_iAdvChange = "); Puti (m_pDdeObject->m_iAdvChange); Puts("\n"); if (m_pDdeObject->m_ulObjType == OT_LINK) { ErrRtnH (m_pDdeObject->AdviseOn (formatetc.cfFormat, ON_CHANGE)); ErrRtnH (m_pDdeObject->AdviseOn (formatetc.cfFormat, ON_SAVE)); } else { ErrRtnH (m_pDdeObject->AdviseOn (formatetc.cfFormat, ON_SAVE)); ErrRtnH (m_pDdeObject->AdviseOn (formatetc.cfFormat, ON_CLOSE)); } } ErrZS (m_pDdeObject->m_pDataAdvHolder, E_OUTOFMEMORY); hresult = m_pDdeObject->m_pDataAdvHolder->Advise (this, pformatetc, grfAdvf, pAdvSink, pdwConnection); m_pDdeObject->m_ConnectionTable.Add (*pdwConnection, formatetc.cfFormat, grfAdvf); errRtn: intrDebugOut((DEB_ITRACE, "%x _OUT CDdeObject::DAdvise hresult=%x\n", this, hresult)); return hresult; } STDMETHODIMP NC(CDdeObject,CDataObjectImpl)::DUnadvise (DWORD dwConnection) { intrDebugOut((DEB_ITRACE, "CDdeObject::DUnadvise(%x,dwConnection=%x)\n", this, dwConnection)); CLIPFORMAT cf; HRESULT hres; DWORD grfAdvf; // Remove connection from table. Lookup the cf for this connection. if (m_pDdeObject->m_ConnectionTable.Subtract (dwConnection, &cf, &grfAdvf) == NOERROR) { // If there is not another connection that needs this format if (m_pDdeObject->m_ConnectionTable.Lookup (cf, NULL) != NOERROR) { // We did a DDE advise for this connection, so undo it. if (m_pDdeObject->m_ulObjType == OT_LINK) { if (NOERROR != (hres=m_pDdeObject->UnAdviseOn (cf, ON_CHANGE))) { intrDebugOut((DEB_IWARN, "::DUnadvise(%x,dwConnection=%x) ON_CHANGE failed\n", this, dwConnection)); } if (NOERROR != (hres=m_pDdeObject->UnAdviseOn (cf, ON_SAVE))) { intrDebugOut((DEB_IWARN, "::DUnadvise(%x,dwConnection=%x) ON_SAVE failed\n", this, dwConnection)); } } else { if (NOERROR != (hres=m_pDdeObject->UnAdviseOn (cf, ON_SAVE))) { intrDebugOut((DEB_IWARN, "::DUnadvise(%x,dwConnection=%x) ON_SAVE failed\n", this, dwConnection)); } if (NOERROR != (hres=m_pDdeObject->UnAdviseOn (cf, ON_CLOSE))) { intrDebugOut((DEB_IWARN, "::DUnadvise(%x,dwConnection=%x) ON_CLOSE failed\n", this, dwConnection)); } } } } // Delegate rest of the work to the DataAdviseHolder RetZS (m_pDdeObject->m_pDataAdvHolder, E_OUTOFMEMORY); return m_pDdeObject->m_pDataAdvHolder->Unadvise (dwConnection); } STDMETHODIMP NC(CDdeObject,CDataObjectImpl)::EnumDAdvise (THIS_ LPENUMSTATDATA FAR* ppenumAdvise) { RetZS (m_pDdeObject->m_pDataAdvHolder, E_OUTOFMEMORY); return m_pDdeObject->m_pDataAdvHolder->EnumAdvise(ppenumAdvise); } STDMETHODIMP NC(CDdeObject,CDataObjectImpl)::EnumFormatEtc (DWORD dwDirection, LPENUMFORMATETC FAR* ppenumFormatEtc) { return OleRegEnumFormatEtc (m_pDdeObject->m_clsid, dwDirection, ppenumFormatEtc); } STDMETHODIMP NC(CDdeObject,CDataObjectImpl)::GetCanonicalFormatEtc (LPFORMATETC pformatetc, LPFORMATETC pformatetcOut) { VDATEPTROUT (pformatetcOut, FORMATETC); memcpy (pformatetcOut, pformatetc, sizeof (FORMATETC)); return ReportResult(0, DATA_S_SAMEFORMATETC, 0, 0); // We must be very conservative and assume data will be different for // every formatetc } INTERNAL CDdeObject::RequestData (CLIPFORMAT cf) { intrDebugOut((DEB_ITRACE, "CDdeObject::RequestData(%x,cf=%x)\n", this,cf)); LPARAM lparam; RetZ (m_pDocChannel); intrAssert(wIsValidAtom(m_aItem)); ATOM aItem = wDupAtom (m_aItem); intrAssert(wIsValidAtom(aItem)); lparam = MAKE_DDE_LPARAM (WM_DDE_REQUEST,cf, aItem); if (!wPostMessageToServer (m_pDocChannel, WM_DDE_REQUEST, lparam, TRUE)) { if (aItem) GlobalDeleteAtom (aItem); return RPC_E_SERVER_DIED; } return WaitForReply(m_pDocChannel, AA_REQUEST); } // special name const char achSpecialName[] = "DISPLAY"; // // Return a 1.0 target device for the screen // static INTERNAL DefaultTargetDevice (HANDLE FAR* ph) { intrDebugOut((DEB_ITRACE, "DefaultTargetDevice(ph=%x)\n",ph)); VDATEPTROUT ((LPVOID) ph, HANDLE); LPOLETARGETDEVICE p1=NULL; *ph = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, sizeof (*p1) + 10); RetZS (*ph, E_OUTOFMEMORY); p1 = (LPOLETARGETDEVICE) GlobalLock (*ph); RetZS (p1, E_OUTOFMEMORY); p1->otdDeviceNameOffset = 8; p1->otdDriverNameOffset = 0; // The driver name is at otdData p1->otdPortNameOffset = 9; p1->otdExtDevmodeOffset = 0; p1->otdExtDevmodeSize = 0; p1->otdEnvironmentOffset= 0; p1->otdEnvironmentSize = 0; // // Note that memcpy is moving a constant string. Therefore, sizeof() // will include the NULL terminator // // memcpy((LPSTR)p1->otdData, achSpecialName,sizeof(achSpecialName)); p1->otdData[8] = 0; // NULL the otdDeviceName p1->otdData[9] = 0; // NULL the PortNameOffset GlobalUnlock (*ph); return NOERROR; } //+--------------------------------------------------------------------------- // // Function: Convert20TargetDevice // // Synopsis: Converts a 2.0 TargetDevice into a 1.0 OLETARGETDEVICE // // Effects: First converts the 2.0 UNICODE target device into ANSI, // then converts that into a 1.0 OLETARGETDEVICE. The astute // reader would say: Why not just 2.0 UNICODE to OLETARGETDEVICE? // // Two reasons: time before we ship vs time needed elsewhere. // // If you can spare some time, please change this to go // directly from one to the other. // // Arguments: [ptd] -- // [phTD1] -- // // Requires: // // Returns: // // Signals: // // Modifies: // // Algorithm: // // History: 5-03-94 kevinro Created // // Notes: // // BUGBUG: the NT version of the OLE 1.0 used UINT as the size of the // structures members. This was baaaad, since we really need them to be // a fixed size. I am currently in the works of changing the NT 1.0 header // file to reflect what we really need it to be, which is USHORT's // // // We have a DVTARGETDEVICE, but we want a OLETARGETDEVICE, which looks like // // typedef struct _OLETARGETDEVICE { // USHORT otdDeviceNameOffset; // USHORT otdDriverNameOffset; // USHORT otdPortNameOffset; // USHORT otdExtDevmodeOffset; // USHORT otdExtDevmodeSize; // USHORT otdEnvironmentOffset; // USHORT otdEnvironmentSize; // BYTE otdData[1]; // } OLETARGETDEVICE; // // A couple things to note: // // 1) The Names in the OLETARGETDEVICE need to be Ansi // 2) The Environment member doens't exist in the DVTARGETDEVICE, and will // be created in this conversion // 3) The ExtDevmode also needs to be ANSI // //---------------------------------------------------------------------------- INTERNAL Convert20TargetDevice (const DVTARGETDEVICE FAR* ptd, // in parm HANDLE FAR* phTD1) // out parm { const size_t cbHeader = SIZEOF_DVTARGETDEVICE_HEADER; HRESULT hr; LPOLETARGETDEVICE ptd1 = NULL; size_t cbTD1; size_t cbDevmode; size_t cbOffset; LPDEVMODEA pdevmode; intrDebugOut((DEB_ITRACE, "Convert20TargetDevice(ptd=%x)\n",ptd)); VDATEPTROUT ((LPVOID) phTD1, HANDLE); *phTD1 = NULL; // // If no device specified, then return the default // if (NULL==ptd) { return DefaultTargetDevice (phTD1); } // // Compute information for doing conversion using routines in utils.cpp // The following structure will get the sizes // DVTDINFO dvtdInfo; hr = UtGetDvtd32Info(ptd,&dvtdInfo); if (hr != NOERROR) { return DV_E_DVTARGETDEVICE; } // // The conversion routines require us to allocate memory to pass in. // DVTARGETDEVICE *pdvtdAnsi = (DVTARGETDEVICE *) PrivMemAlloc(dvtdInfo.cbConvertSize); if (pdvtdAnsi == NULL) { return(E_OUTOFMEMORY); } // // Convert the UNICODE target device into an ANSI target device // hr = UtConvertDvtd32toDvtd16(ptd,&dvtdInfo,pdvtdAnsi); if (hr != NOERROR) { goto errRtn; } // // pdvtdAnsi now holds an ANSI version of the DVTARGETDEVICE. Turns // out the structure we really want is the DVTARGETDEVICE, plus a // couple of extra header bytes. Therefore, we can just do a block // copy of the DVTARGETDEVICE's data, and fix up our OLETARGETDEVICE // header to have the correct offsets in the data. // // offset of data block from beginning of 2.0 target device // cbOffset = offsetof (DVTARGETDEVICE, tdData); // // Calculate a pointer to the DEVMODEA // pdevmode = pdvtdAnsi->tdExtDevmodeOffset ? (LPDEVMODEA)((LPBYTE)pdvtdAnsi + pdvtdAnsi->tdExtDevmodeOffset) : NULL; // // Quick sanity check on the resulting pointer. // if (pdevmode && IsBadReadPtr (pdevmode, sizeof(DEVMODEA))) { hr = DV_E_DVTARGETDEVICE; goto errRtn; } // // Calculate the size of the devmode part. // cbDevmode = (pdevmode ? pdevmode->dmSize + pdevmode->dmDriverExtra:0); // // Calculate the total size needed. The DVTARGETDEVICE header has 12 bytes, // and the OLETARGETDEVICE has 14 bytes. We also need to make an extra copy // of the cbDevmode structure to fill in the environment. Therefore, there is // an extra cbDevmode, and a sizeof(USHORT) added to the size. The size includes // the size of the DVTARGETHEADER // cbTD1 = (size_t) pdvtdAnsi->tdSize + cbDevmode + // For extra Environment data sizeof (USHORT); // for Environment Size field *phTD1 = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, cbTD1); if (NULL== *phTD1) { intrAssert (!"GlobalAlloc Failed"); hr = E_OUTOFMEMORY; goto errRtn; } ptd1 = (LPOLETARGETDEVICE) GlobalLock (*phTD1); if (NULL== ptd1) { intrAssert (!"GlobalLock Failed"); hr = E_OUTOFMEMORY; goto errRtn; } // Set x1 (1.0 offset) based on x2 (2.0 offset) // // Note that the OLETARGETDEVICE offsets are relative to the array of bytes, // where the DVTARGETDEVICE is relative to the start of the structure. Thats // why cbOffset is subtracted // #define ConvertOffset(x1, x2) (x1 = (x2 ? x2 - cbOffset : 0)) // // Using the above macro, and assuming // ConvertOffset (ptd1->otdDeviceNameOffset, pdvtdAnsi->tdDeviceNameOffset); ConvertOffset (ptd1->otdDriverNameOffset, pdvtdAnsi->tdDriverNameOffset); ConvertOffset (ptd1->otdPortNameOffset , pdvtdAnsi->tdPortNameOffset ); ConvertOffset (ptd1->otdExtDevmodeOffset, pdvtdAnsi->tdExtDevmodeOffset); ptd1->otdExtDevmodeSize = cbDevmode; // // I found this in the OLE 2 information on OLETARGETDEVICE: // // The otdDeviceNameOffset, otdDriverNameOffset, and otdPortNameOffset // members should be null-terminated. In Windows 3.1, the ability to // connect multiple printers to one port has made the environment // obsolete. The environment information retrieved by the // GetEnvironment function can occasionally be incorrect. To ensure that the // OLETARGETDEVICE structure is initialized correctly, the application // should copy information from the DEVMODEA structure retrieved by a // call to the ExtDeviceMode function to the environment position of // the OLETARGETDEVICE structure. // // // // Adjust the environment offset to the end of the converted structure, and // set the size. the sizeof(USHORT) accounts for the addition of the // otdEnvironmentSize field. The offsetof accounts for the fact that the // OLETARGETDEVICE offsets are based from the otdData array. // ptd1->otdEnvironmentOffset = (USHORT) pdvtdAnsi->tdSize + sizeof(USHORT) - offsetof(OLETARGETDEVICE,otdData); ptd1->otdEnvironmentSize = cbDevmode; // Copy data block if(IsBadWritePtr (ptd1->otdData, (size_t) pdvtdAnsi->tdSize - cbHeader)) { hr = E_UNEXPECTED; goto errRtn; } memcpy (ptd1->otdData, pdvtdAnsi->tdData, (size_t) pdvtdAnsi->tdSize - cbHeader); if (cbDevmode != 0) { if(IsBadWritePtr (ptd1->otdData, sizeof (DEVMODEA))) { hr = E_UNEXPECTED; goto errRtn; } // Copy 2.0 Devmode into 1.0 environment memcpy (ptd1->otdData + ptd1->otdEnvironmentOffset, pdvtdAnsi->tdData + pdvtdAnsi->tdExtDevmodeOffset, cbDevmode); } hr = NOERROR; errRtn: if (ptd1 != NULL) { GlobalUnlock(*phTD1); } if (pdvtdAnsi != NULL) { PrivMemFree(pdvtdAnsi); } intrDebugOut((DEB_ITRACE, "Convert20TargetDevice(ptd=%x) returns %x\n",ptd,hr)); return(hr); } static INTERNAL CopyTargetDevice (const DVTARGETDEVICE FAR* ptd, DVTARGETDEVICE FAR* FAR* pptd) { intrDebugOut((DEB_ITRACE, "CopyTargetDevice(ptd=%x)\n",ptd)); if (*pptd) { delete *pptd; // delete old target device } if (NULL==ptd) { *pptd = NULL; } else { *pptd = (DVTARGETDEVICE FAR*) operator new ((size_t) (ptd->tdSize)); if (NULL==*pptd) { return ReportResult(0, E_OUTOFMEMORY, 0, 0); } _fmemcpy (*pptd, ptd, (size_t) ptd->tdSize); } return NOERROR; } INTERNAL CDdeObject::SetTargetDevice (const DVTARGETDEVICE FAR* ptd) { intrDebugOut((DEB_ITRACE, "CDdeObject::SetTargetDevice(%x,ptd=%x)\n", this, ptd)); HANDLE hTD1 = NULL; HANDLE hDdePoke=NULL; RetErr (Convert20TargetDevice (ptd, &hTD1)); Assert (hTD1); Verify (hDdePoke = wPreparePokeBlock (hTD1, g_cfBinary, m_aClass, m_bOldSvr)); if (hTD1) { GlobalFree (hTD1); } // Poke new target device to 1.0 server aStdTargetDevice = GlobalAddAtom (L"StdTargetDevice"); intrAssert(wIsValidAtom(aStdTargetDevice)); RetErr (Poke (aStdTargetDevice, hDdePoke)); // Remember current target device RetErr (CopyTargetDevice (ptd, &m_ptd)); // Flush the cache because it contains a picture for the wrong // target device. if (m_hPict) wFreeData (m_hPict, m_cfPict); m_cfPict = (CLIPFORMAT)0; m_hPict = NULL; return NOERROR; }