955 lines
26 KiB
C++
955 lines
26 KiB
C++
/*
|
|
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 <stddef.h>
|
|
#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;
|
|
}
|