1998 lines
57 KiB
C++
1998 lines
57 KiB
C++
//+---------------------------------------------------------------------
|
||
//
|
||
// File: sdv.cxx
|
||
//
|
||
//------------------------------------------------------------------------
|
||
|
||
//[ srvrdv_overview
|
||
/*
|
||
SrvrDV Overview
|
||
|
||
The SrvrDV base class implements the persistent data and view aspects common
|
||
to most OLE Compound Document objects. It implements the IDataObject,
|
||
IViewObject, and IPersist family of interfaces.
|
||
|
||
The set of formats supported in IDataObject::GetData and SetData methods is
|
||
very object dependent. SrvrDV uses a table approach to allow each derived
|
||
class to specify precisely exactly the set of formats supported. For each
|
||
of GetData and SetData there is a pair of tables. One is a table of FORMATETC
|
||
structures that specify information about the format supported. A parallel
|
||
table contains a pointer to a function that implements each format supported.
|
||
SrvrDV has a set of static methods that implement the standard OLE clipboard
|
||
formats. The derived class can include these methods in its Get/Set tables.
|
||
|
||
*/
|
||
//]
|
||
|
||
#include "headers.hxx"
|
||
#pragma hdrstop
|
||
|
||
OLECHAR szContents[] = OLETEXT("contents");
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::SrvrDV, protected
|
||
//
|
||
// Synopsis: Constructor for SrvrCtrl object
|
||
//
|
||
// Notes: To create a properly initialized object you must
|
||
// call the Init method immediately after construction.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
SrvrDV::SrvrDV(void)
|
||
{
|
||
DOUT(TEXT("SrvrDV: Constructing\r\n"));
|
||
|
||
_pmk = NULL;
|
||
_lpstrDisplayName = NULL;
|
||
_sizel.cx = 0; _sizel.cy = 0;
|
||
_pDataAdviseHolder = NULL;
|
||
|
||
_fFrozen = FALSE;
|
||
_pViewAdviseHolder = NULL;
|
||
|
||
_fDirty = FALSE;
|
||
_fNoScribble = FALSE;
|
||
_pStg = NULL;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::Init, protected
|
||
//
|
||
// Synopsis: Fully initializes a SrvrCtrl object as part of a compound
|
||
// document server aggregate
|
||
//
|
||
// Arguments: [pClass] -- The initialized class descriptor for the server
|
||
// [pCtrl] -- The control subobject of the server we are a part of.
|
||
//
|
||
// Returns: NOERROR if successful
|
||
//
|
||
// Notes: The Init method of the control subobject creates the data/view and
|
||
// inplace subobjects of the object and calls the respective Init methods
|
||
// on each, including this one.
|
||
// The class descriptor pointer is saved in the protected _pClass
|
||
// member variable where it is accessible during the lifetime
|
||
// of the object.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
HRESULT
|
||
SrvrDV::Init(LPCLASSDESCRIPTOR pClass, LPSRVRCTRL pCtrl)
|
||
{
|
||
_pClass = pClass;
|
||
_pCtrl = pCtrl;
|
||
|
||
return NOERROR;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::Init, protected
|
||
//
|
||
// Synopsis: Fully initializes a SrvrCtrl object from an existing object
|
||
// to be used as a data transfer object
|
||
//
|
||
// Arguments: [pClass] -- The initialized class descriptor for the server
|
||
// [pDV] -- The data/view subobject of the object that is the source
|
||
// of this transfer data object
|
||
//
|
||
// Returns: NOERROR if successful
|
||
//
|
||
// Notes: This method is used in the process of the GetClipboardCopy method
|
||
// for obtaining a transfer data object from an existing object.
|
||
// The class descriptor pointer is saved in the protected _pClass
|
||
// member variable where it is accessible during the lifetime
|
||
// of the object.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
HRESULT
|
||
SrvrDV::Init(LPCLASSDESCRIPTOR pClass, LPSRVRDV pDV)
|
||
{
|
||
_pClass = pClass; // stash the class descriptor pointer
|
||
_pCtrl = NULL; // we are a free-floating transfer data object
|
||
|
||
// copy over member variables as appropriate
|
||
_pmk = pDV->_pmk;
|
||
if (_pmk != NULL)
|
||
_pmk->AddRef();
|
||
|
||
TaskAllocString(pDV->_lpstrDisplayName, &_lpstrDisplayName);
|
||
_sizel = pDV->_sizel;
|
||
|
||
// we create a temporary IStorage and take a snapshot of the storage.
|
||
// The temporary IStorage will be automatically deleted when this
|
||
// data object is released
|
||
// What happens when we run out of memory?
|
||
// Should re-try with disk-based docfile?
|
||
//
|
||
HRESULT hr;
|
||
LPSTORAGE pStg;
|
||
if (OK(hr = CreateStorageOnHGlobal(NULL, &pStg)))
|
||
{
|
||
// do a "Save Copy As" into our storage instance.
|
||
if (OK(hr = OleSave((LPPERSISTSTORAGE)pDV, pStg, FALSE)))
|
||
{
|
||
if (OK(hr = pStg->Commit(STGC_DEFAULT)))
|
||
{
|
||
if (OK(hr = ((LPPERSISTSTORAGE)pDV)->SaveCompleted(NULL)))
|
||
(_pStg = pStg)->AddRef();
|
||
}
|
||
}
|
||
pStg->Release();
|
||
}
|
||
|
||
return hr;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::~SrvrDV, protected
|
||
//
|
||
// Synopsis: Destructor for the SrvrDV object
|
||
//
|
||
// Notes: The destructor is called as a result of the servers
|
||
// reference count going to 0. It releases any held resources
|
||
// including advise holders and storages.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
SrvrDV::~SrvrDV(void)
|
||
{
|
||
if (_pmk != NULL)
|
||
_pmk->Release();
|
||
|
||
if (_lpstrDisplayName != NULL)
|
||
TaskFreeString(_lpstrDisplayName);
|
||
|
||
if (_pDataAdviseHolder != NULL)
|
||
_pDataAdviseHolder->Release();
|
||
|
||
if (_pViewAdviseHolder != NULL)
|
||
_pViewAdviseHolder->Release();
|
||
|
||
if (_pStg != NULL)
|
||
_pStg->Release();
|
||
|
||
DOUT(TEXT("SrvrDV: Destructed\r\n"));
|
||
}
|
||
|
||
#ifdef DOCGEN // documentation for pure virtual function
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::GetClipboardCopy, public
|
||
//
|
||
// Synopsis: Produces a data-transfer object representing a snapshot
|
||
// of this data/view object
|
||
//
|
||
// Arguments: [ppDV] -- The place where the data-transfer object is returned
|
||
//
|
||
// Returns: Success if the data-transfer object was created.
|
||
//
|
||
// Notes: This method is called
|
||
// as a result of an IOleObject::GetClipboardData call on the
|
||
// control subobject. The overridden method should create a new
|
||
// instance of itself and call the appropriate Init method on
|
||
// the new instance passing the `this' pointer. This way the
|
||
// new instance can initialize itself as a snapshot of this object.
|
||
// All servers must override this pure virtual method.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
HRESULT
|
||
SrvrDV::GetClipboardCopy(LPSRVRDV FAR* ppDV)
|
||
{}
|
||
#endif // DOCGEN
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::SetExtent, public
|
||
//
|
||
// Synopsis: Informs the object that it has a new expected size
|
||
//
|
||
// Notes: IOleObject::SetExtent and GetExtent methods are passed
|
||
// directly from the control subobject to the data/view subobject
|
||
// via these methods. See OLE documentation for the
|
||
// arguments and return values.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
HRESULT
|
||
SrvrDV::SetExtent(DWORD dwAspect, SIZEL& sizel)
|
||
{
|
||
HRESULT hr = NOERROR;
|
||
switch(dwAspect)
|
||
{
|
||
case DVASPECT_CONTENT:
|
||
case DVASPECT_DOCPRINT:
|
||
#if 1
|
||
// NTBug #20692: Fail Metafile setextent, because
|
||
// Powerpoint tries to resize things in weird ways.
|
||
hr = E_FAIL; // icon aspect is fixed size
|
||
#else
|
||
_sizel = sizel;
|
||
#endif
|
||
break;
|
||
case DVASPECT_THUMBNAIL: //REVIEW: what size is a thumbnail?
|
||
case DVASPECT_ICON:
|
||
DOUT(TEXT("SrvrDV::SetExtent E_FAIL\r\n"));
|
||
hr = E_FAIL; // icon aspect is fixed size
|
||
break;
|
||
default:
|
||
DOUT(TEXT("SrvrDV::SetExtent E_INVALIDARG\r\n"));
|
||
hr = E_INVALIDARG;
|
||
break;
|
||
}
|
||
return hr;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::GetExtent, public
|
||
//
|
||
// Synopsis: Requests the current size for some draw aspect of the object
|
||
//
|
||
// Notes: IOleObject::SetExtent and GetExtent methods are passed
|
||
// directly from the control subobject to the data/view subobject
|
||
// via these methods. See OLE documentation for the
|
||
// arguments and return values.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
HRESULT
|
||
SrvrDV::GetExtent(DWORD dwAspect, LPSIZEL lpsizel)
|
||
{
|
||
#if DBG
|
||
TCHAR achTemp[256];
|
||
wsprintf(achTemp,TEXT("SrvrDV::GetExtent (dwApsect = %ld, cx = %ld, cy = %ld)\r\n"),
|
||
dwAspect, _sizel.cx, _sizel.cy);
|
||
DOUT(achTemp);
|
||
#endif
|
||
|
||
HRESULT hr = NOERROR;
|
||
switch(dwAspect) {
|
||
default:
|
||
DOUT(TEXT("SrvrDV::GetExtent INVALIDARG\r\n"));
|
||
case DVASPECT_CONTENT:
|
||
case DVASPECT_DOCPRINT:
|
||
*lpsizel = _sizel;
|
||
break;
|
||
|
||
case DVASPECT_THUMBNAIL:
|
||
case DVASPECT_ICON:
|
||
// The iconic view is actually a metafile of the
|
||
// icon with a text-string underneath.
|
||
// This isn't the right calculation...
|
||
lpsizel->cx = HimetricFromHPix(32);
|
||
lpsizel->cy = HimetricFromVPix(32);
|
||
break;
|
||
|
||
}
|
||
return hr;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::OnDataChange, public
|
||
//
|
||
// Synopsis: Sets the dirty flag and raises data and view changed
|
||
// to all registered advises
|
||
//
|
||
// Arguments: [dwAdvf] -- from the ADVF Data Advise flags.
|
||
// Usually this is 0. ADVF_DATAONSTOP is used
|
||
// when the object is closing down.
|
||
// Notes: This function should be called whenever the native
|
||
// data of the object is modified.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
void
|
||
SrvrDV::OnDataChange(DWORD dwAdvf)
|
||
{
|
||
_fDirty = TRUE;
|
||
if (_pViewAdviseHolder != NULL)
|
||
_pViewAdviseHolder->SendOnViewChange(DVASPECT_CONTENT);
|
||
if (_pDataAdviseHolder != NULL)
|
||
_pDataAdviseHolder->SendOnDataChange((LPDATAOBJECT)this, 0, dwAdvf);
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::SetMoniker, public
|
||
//
|
||
// Synopsis: Informs the object of its new, full moniker
|
||
//
|
||
// Arguments: [pmk] -- full moniker to the object
|
||
//
|
||
// Notes: The data/view subobject records the full moniker to the
|
||
// object so it can properly dispense the standard OLE
|
||
// Object Descriptor and Link Source clipboard formats.
|
||
// This method is called whenever the IOleObject::SetMoniker
|
||
// method is called on the control subobject.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
void
|
||
SrvrDV::SetMoniker(LPMONIKER pmk)
|
||
{
|
||
if (_pmk != NULL)
|
||
{
|
||
_pmk->Release();
|
||
|
||
if (_lpstrDisplayName != NULL) // flush our cached display name
|
||
{
|
||
TaskFreeString(_lpstrDisplayName);
|
||
_lpstrDisplayName = NULL;
|
||
}
|
||
}
|
||
|
||
_pmk = pmk;
|
||
|
||
if (_pmk != NULL)
|
||
{
|
||
_pmk->AddRef();
|
||
}
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::GetMoniker, public
|
||
//
|
||
// Synopsis: Returns the full moniker to the object
|
||
//
|
||
// Arguments: [dwAssign] -- See IOleObject::GetMoniker
|
||
// [ppmk] -- The place where the moniker is returned
|
||
//
|
||
// Returns: Success if the moniker is available.
|
||
//
|
||
// Notes: This returns the moniker that this data/view subobject has
|
||
// previously recorded. If no moniker is yet assigned
|
||
// then the moniker is requested from the client site
|
||
// via the IOleObject::GetMoniker method on the control subobject.
|
||
// This method is used by the GetOBJECTDESCRIPTOR and GetLINKSOURCE
|
||
// methods.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
HRESULT
|
||
SrvrDV::GetMoniker(DWORD dwAssign, LPMONIKER FAR* ppmk)
|
||
{
|
||
*ppmk = NULL; // set out params to NULL
|
||
|
||
HRESULT hr = NOERROR;
|
||
if (_pmk == NULL)
|
||
{
|
||
if (_pCtrl == NULL)
|
||
{
|
||
DOUT(TEXT("SrvrDV::GetMoniker E_INVALIDARG\r\n"));
|
||
hr = MK_E_UNAVAILABLE;
|
||
}
|
||
else
|
||
{
|
||
hr = _pCtrl->GetMoniker(dwAssign, OLEWHICHMK_OBJFULL, &_pmk);
|
||
}
|
||
}
|
||
|
||
if (OK(hr))
|
||
(*ppmk = _pmk)->AddRef();
|
||
|
||
return hr;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::GetMonikerDisplayName, public
|
||
//
|
||
// Synopsis: Returns the display name from the object's moniker
|
||
//
|
||
// Notes: The display name of the object is used in for
|
||
// dispensing the Object Descriptor clipboard format.
|
||
// The caller must not free the string returned.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
LPOLESTR
|
||
SrvrDV::GetMonikerDisplayName(DWORD dwAssign)
|
||
{
|
||
//
|
||
// NOTE: default dwAssign is OLEGETMONIKER_ONLYIFTHERE
|
||
//
|
||
// we maintain a moniker display name cache in the form of member
|
||
// variable _lpstrDisplayName.
|
||
//
|
||
// If we don't have a display name cached then take our moniker and
|
||
// squeeze a display name out of it
|
||
//
|
||
if (!_lpstrDisplayName)
|
||
{
|
||
LPMONIKER pmk;
|
||
if (OK(GetMoniker(dwAssign, &pmk)))
|
||
{
|
||
::GetMonikerDisplayName(pmk, &_lpstrDisplayName);
|
||
pmk->Release();
|
||
}
|
||
}
|
||
return _lpstrDisplayName;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::GetEMBEDDEDOBJECT, public
|
||
//
|
||
// Synopsis: Implementation of IDataObject::GetData and GetDataHere
|
||
// for the standard Embedded Object clipboard format
|
||
//
|
||
// Arguments: [pv] -- pointer to a SrvrDV object
|
||
// [pformatetc] -- as in IDataObject::GetData, GetDataHere
|
||
// [pmedium] -- as in IDataObject::GetData, GetDataHere
|
||
// [fHere] -- TRUE for GetDataHere, FALSE for GetData
|
||
//
|
||
// Returns: Success if the clipboard format could be dispensed
|
||
//
|
||
// Notes: This and the other static GetXXX methods are for use
|
||
// in the server's Get format tables.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
HRESULT
|
||
SrvrDV::GetEMBEDDEDOBJECT( LPSRVRDV pDV,
|
||
LPFORMATETC pformatetc,
|
||
LPSTGMEDIUM pmedium,
|
||
BOOL fHere)
|
||
{
|
||
LPPERSISTSTORAGE pPStg = (LPPERSISTSTORAGE)(LPSRVRDV)pDV;
|
||
HRESULT hr = NOERROR;
|
||
if (!fHere && pmedium)
|
||
{
|
||
// fill in the pmedium structure
|
||
pmedium->tymed = TYMED_ISTORAGE;
|
||
hr = StgCreateDocfile(NULL,
|
||
STGM_DFRALL | STGM_CREATE | STGM_DELETEONRELEASE,
|
||
0L,
|
||
&pmedium->pstg);
|
||
pmedium->pUnkForRelease = NULL;
|
||
}
|
||
|
||
if (OK(hr) && pmedium && pPStg)
|
||
{
|
||
if (OK(hr = OleSave(pPStg, pmedium->pstg, FALSE)))
|
||
hr = pPStg->SaveCompleted(NULL);
|
||
|
||
// if we failed somehow and yet created a docfile, then we will
|
||
// release the docfile to delete it
|
||
//
|
||
if (!OK(hr) && !fHere)
|
||
pmedium->pstg->Release();
|
||
}
|
||
|
||
return hr;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::GetMETAFILEPICT, public
|
||
//
|
||
// Synopsis: Implementation of IDataObject::GetData and GetDataHere
|
||
// for the standard Metafilepict clipboard format
|
||
//
|
||
// Arguments: [pv] -- pointer to a SrvrDV object
|
||
// [pformatetc] -- as in IDataObject::GetData, GetDataHere
|
||
// [pmedium] -- as in IDataObject::GetData, GetDataHere
|
||
// [fHere] -- TRUE for GetDataHere, FALSE for GetData
|
||
//
|
||
// Returns: Success if the clipboard format could be dispensed
|
||
//
|
||
// Notes: This member function uses IViewObject::Draw to construct
|
||
// the metafile pict.
|
||
// This and the other static GetXXX methods are for use
|
||
// in the server's Get format tables.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
HRESULT
|
||
SrvrDV::GetMETAFILEPICT( LPSRVRDV pDV,
|
||
LPFORMATETC pformatetc,
|
||
LPSTGMEDIUM pmedium,
|
||
BOOL fHere)
|
||
{
|
||
DOUT(TEXT("o2base/SrvrDV::GetMETAFILEPICT\r\n"));
|
||
|
||
LPVIEWOBJECT pView = (LPVIEWOBJECT)pDV;
|
||
SIZEL sizel;
|
||
pDV->GetExtent(pformatetc->dwAspect, &sizel);
|
||
|
||
RECT rc = { 0, 0, sizel.cx, sizel.cy };
|
||
HRESULT hr = NOERROR;
|
||
if (!fHere)
|
||
{
|
||
// fill in the pmedium structure
|
||
pmedium->tymed = TYMED_MFPICT;
|
||
pmedium->hGlobal = GlobalAlloc(GMEM_SHARE, sizeof(METAFILEPICT));
|
||
if (pmedium->hGlobal == NULL)
|
||
{
|
||
DOUT(TEXT("o2base/SrvrDV::GetMETAFILEPICT failed\r\n"));
|
||
hr = E_OUTOFMEMORY;
|
||
}
|
||
pmedium->pUnkForRelease = NULL;
|
||
}
|
||
|
||
if (OK(hr))
|
||
{
|
||
HMETAFILE hmf;
|
||
if (OK(hr = DrawMetafile(pView, rc, pformatetc->dwAspect, &hmf)))
|
||
{
|
||
LPMETAFILEPICT pPict = (LPMETAFILEPICT)GlobalLock(pmedium->hGlobal);
|
||
if (pPict == NULL)
|
||
{
|
||
DOUT(TEXT("SrvrDV::GetMETAFILEPICT E_INVALIDARG\r\n"));
|
||
|
||
DeleteMetaFile(hmf);
|
||
hr = E_INVALIDARG;
|
||
}
|
||
else
|
||
{
|
||
// fill in the object descriptor
|
||
pPict->mm = MM_ANISOTROPIC;
|
||
pPict->hMF = hmf;
|
||
pPict->xExt = rc.right;
|
||
pPict->yExt = rc.bottom;
|
||
|
||
GlobalUnlock(pmedium->hGlobal);
|
||
}
|
||
}
|
||
|
||
// if we failed somehow and yet allocated memory,
|
||
// then we will release it here...
|
||
//
|
||
if (!OK(hr) && !fHere)
|
||
{
|
||
GlobalFree(pmedium->hGlobal);
|
||
pmedium->hGlobal = NULL;
|
||
}
|
||
}
|
||
|
||
return hr;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::GetOBJECTDESCRIPTOR, public
|
||
//
|
||
// Synopsis: Implementation of IDataObject::GetData and GetDataHere
|
||
// for the standard Object Descriptor clipboard format
|
||
//
|
||
// Arguments: [pv] -- pointer to a SrvrDV object
|
||
// [pformatetc] -- as in IDataObject::GetData, GetDataHere
|
||
// [pmedium] -- as in IDataObject::GetData, GetDataHere
|
||
// [fHere] -- TRUE for GetDataHere, FALSE for GetData
|
||
//
|
||
// Returns: Success if the clipboard format could be dispensed
|
||
//
|
||
// Notes: This and the other static GetXXX methods are for use
|
||
// in the server's Get format tables.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
HRESULT
|
||
SrvrDV::GetOBJECTDESCRIPTOR( LPSRVRDV pDV,
|
||
LPFORMATETC pformatetc,
|
||
LPSTGMEDIUM pmedium,
|
||
BOOL fHere)
|
||
{
|
||
HRESULT hr = NOERROR;
|
||
LPCLASSDESCRIPTOR pClass = pDV->_pClass;
|
||
|
||
//
|
||
//REVIEW: what's the best display name for the OBJECTDESCRIPTOR?
|
||
//
|
||
|
||
OLECHAR achDisplay[256];
|
||
|
||
ostrcpy(achDisplay, OLETEXT("Microsoft "));
|
||
ostrcat(achDisplay, pClass->_szUserClassType[USERCLASSTYPE_FULL]);
|
||
|
||
LPOLESTR lpstrDisplay = achDisplay;
|
||
DWORD dwDisplay = lpstrDisplay ?
|
||
(ostrlen(lpstrDisplay) + 1) * sizeof(OLECHAR) : 0;
|
||
LPOLESTR lpstrUserTypeFull = pClass->_szUserClassType[USERCLASSTYPE_FULL];
|
||
DWORD dwUserTypeFull = lpstrUserTypeFull ?
|
||
(ostrlen(lpstrUserTypeFull) + 1) * sizeof(OLECHAR) : 0;
|
||
|
||
DWORD dwSize = sizeof(OBJECTDESCRIPTOR) + dwUserTypeFull + dwDisplay;
|
||
|
||
if (!fHere)
|
||
{
|
||
// compute the amount of memory required
|
||
|
||
// fill in the pmedium structure
|
||
pmedium->tymed = TYMED_HGLOBAL;
|
||
pmedium->hGlobal = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, dwSize);
|
||
if (pmedium->hGlobal == NULL)
|
||
{
|
||
DOUT(TEXT("o2base/SrvrDV::GetOBJECTDESCRIPTOR failed (pmedium)\r\n"));
|
||
hr = E_OUTOFMEMORY;
|
||
}
|
||
|
||
pmedium->pUnkForRelease = NULL;
|
||
}
|
||
|
||
if (OK(hr) && (GlobalSize(pmedium->hGlobal) >= dwSize))
|
||
{
|
||
LPOBJECTDESCRIPTOR pObjDesc =
|
||
(LPOBJECTDESCRIPTOR)GlobalLock(pmedium->hGlobal);
|
||
if (pObjDesc == NULL)
|
||
{
|
||
DOUT(TEXT("o2base/SrvrDV::GetOBJECTDESCRIPTOR failed\r\n"));
|
||
hr = E_OUTOFMEMORY;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// fill in the object descriptor
|
||
//
|
||
pObjDesc->cbSize = dwSize;
|
||
pObjDesc->clsid = pClass->_clsid;
|
||
pObjDesc->dwDrawAspect = DVASPECT_CONTENT;
|
||
pObjDesc->sizel = pDV->_sizel;
|
||
pObjDesc->pointl.y = pObjDesc->pointl.x = 0;
|
||
pObjDesc->dwStatus = pClass->_dwMiscStatus;
|
||
|
||
LPOLESTR lpstrDest = (LPOLESTR)(pObjDesc + 1);
|
||
if(lpstrUserTypeFull)
|
||
{
|
||
ostrcpy(lpstrDest, lpstrUserTypeFull);
|
||
#if DBG
|
||
#if !defined(UNICODE) && !defined(OLE2ANSI)
|
||
LPSTR lp = ConvertOLESTRToMB(lpstrDest, -1);
|
||
OutputDebugStringA(lp);
|
||
OutputDebugStringA("\r\n");
|
||
TaskFreeMem(lp);
|
||
#endif
|
||
#endif
|
||
pObjDesc->dwFullUserTypeName = sizeof(OBJECTDESCRIPTOR);
|
||
lpstrDest += ostrlen(lpstrUserTypeFull) + 1;
|
||
|
||
}
|
||
else
|
||
pObjDesc->dwFullUserTypeName = 0;
|
||
|
||
if (lpstrDisplay)
|
||
{
|
||
pObjDesc->dwSrcOfCopy = pObjDesc->dwFullUserTypeName + dwUserTypeFull;
|
||
ostrcpy(lpstrDest, lpstrDisplay);
|
||
#if DBG
|
||
#if !defined(UNICODE) && !defined(OLE2ANSI)
|
||
LPSTR lp = ConvertOLESTRToMB(lpstrDest, -1);
|
||
OutputDebugStringA(lp);
|
||
OutputDebugStringA("\r\n");
|
||
TaskFreeMem(lp);
|
||
#endif
|
||
#endif
|
||
}
|
||
else
|
||
pObjDesc->dwSrcOfCopy = 0;
|
||
|
||
GlobalUnlock(pmedium->hGlobal);
|
||
hr = NOERROR;
|
||
}
|
||
|
||
// if we failed somehow and yet allocated memory,
|
||
// then we will release it here...
|
||
if (!OK(hr) && !fHere)
|
||
{
|
||
GlobalFree(pmedium->hGlobal);
|
||
pmedium->hGlobal = NULL;
|
||
}
|
||
}
|
||
else
|
||
hr = E_OUTOFMEMORY;
|
||
|
||
return hr;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::GetLINKSOURCE, public
|
||
//
|
||
// Synopsis: Implementation of IDataObject::GetData and GetDataHere
|
||
// for the standard Link Source clipboard format
|
||
//
|
||
// Arguments: [pv] -- pointer to a SrvrDV object
|
||
// [pformatetc] -- as in IDataObject::GetData, GetDataHere
|
||
// [pmedium] -- as in IDataObject::GetData, GetDataHere
|
||
// [fHere] -- TRUE for GetDataHere, FALSE for GetData
|
||
//
|
||
// Returns: Success if the clipboard format could be dispensed
|
||
//
|
||
// Notes: This method uses the moniker cached by the data/view
|
||
// object.
|
||
// This and the other static GetXXX methods are for use
|
||
// in the server's Get format tables.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
HRESULT
|
||
SrvrDV::GetLINKSOURCE( LPSRVRDV pDV,
|
||
LPFORMATETC pformatetc,
|
||
LPSTGMEDIUM pmedium,
|
||
BOOL fHere)
|
||
{
|
||
LPMONIKER pmk;
|
||
HRESULT hr;
|
||
|
||
if (OK(hr = pDV->GetMoniker(OLEGETMONIKER_FORCEASSIGN, &pmk)))
|
||
{
|
||
if (!fHere)
|
||
{
|
||
pmedium->tymed = TYMED_ISTREAM;
|
||
HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, &pmedium->pstm);
|
||
pmedium->pUnkForRelease = NULL;
|
||
}
|
||
|
||
if (OK(hr))
|
||
{
|
||
CLSID clsid;
|
||
if (OK(hr = pmk->GetClassID(&clsid)))
|
||
{
|
||
if (OK(hr = WriteClassStm(pmedium->pstm, clsid)))
|
||
hr = pmk->Save(pmedium->pstm, FALSE);
|
||
|
||
pDV->GetClassID(&clsid);
|
||
WriteClassStm(pmedium->pstm, clsid);
|
||
}
|
||
}
|
||
|
||
pmk->Release();
|
||
}
|
||
|
||
return hr;
|
||
}
|
||
|
||
|
||
//+--------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::GetData, public
|
||
//
|
||
// Synopsis: Method of IDataObject interface
|
||
//
|
||
// Notes: This method searches the server's Get format table
|
||
// for a compatible format. If one is found it calls
|
||
// the corresponding Get function.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
STDMETHODIMP
|
||
SrvrDV::GetData(LPFORMATETC pformatetc, LPSTGMEDIUM pmedium)
|
||
{
|
||
DOUT(TEXT("SrvrDV:GetData\r\n"));
|
||
|
||
if (pformatetc == NULL || pmedium == NULL)
|
||
{
|
||
DOUT(TEXT("SrvrDV::GetData E_INVALIDARG\r\n"));
|
||
return E_INVALIDARG;
|
||
}
|
||
|
||
HRESULT hr;
|
||
|
||
int i = FindCompatibleFormat(_pClass->_pGetFmtTable,
|
||
_pClass->_cGetFmtTable,
|
||
*pformatetc);
|
||
|
||
if (i < 0)
|
||
{
|
||
DOUT(TEXT("SrvrDV::GetData DV_E_FORMATETC\r\n"));
|
||
hr = DV_E_FORMATETC;
|
||
}
|
||
else
|
||
{
|
||
hr = (*_pGetFuncs[i]) (this, pformatetc, pmedium, FALSE);
|
||
}
|
||
|
||
return hr;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::GetDataHere, public
|
||
//
|
||
// Synopsis: Method of IDataObject interface
|
||
//
|
||
// Notes: This method searches the server's Get format table
|
||
// for a compatible format. If one is found it calls
|
||
// the corresponding Get function.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
STDMETHODIMP
|
||
SrvrDV::GetDataHere(LPFORMATETC pformatetc, LPSTGMEDIUM pmedium)
|
||
{
|
||
DOUT(TEXT("SrvrDV:GetDataHere\r\n"));
|
||
|
||
HRESULT hr;
|
||
int i = FindCompatibleFormat(_pClass->_pGetFmtTable,
|
||
_pClass->_cGetFmtTable,
|
||
*pformatetc);
|
||
if (i < 0)
|
||
{
|
||
DOUT(TEXT("SrvrDV::GetDataHere DV_E_FORMATETC\r\n"));
|
||
hr = DV_E_FORMATETC;
|
||
}
|
||
else
|
||
{
|
||
hr = (*_pGetFuncs[i]) (this, pformatetc, pmedium, TRUE);
|
||
}
|
||
|
||
return hr;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::QueryGetData, public
|
||
//
|
||
// Synopsis: Method of IDataObject interface
|
||
//
|
||
// Notes: This method searches the server's Get format table
|
||
// for a compatible format. The return value indicates
|
||
// whether or not a compatible format was found.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
STDMETHODIMP
|
||
SrvrDV::QueryGetData(LPFORMATETC pformatetc)
|
||
{
|
||
return FindCompatibleFormat(_pClass->_pGetFmtTable,
|
||
_pClass->_cGetFmtTable,
|
||
*pformatetc) >=0 ? NOERROR : DV_E_FORMATETC;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::GetCanonicalFormatEtc, public
|
||
//
|
||
// Synopsis: Method of IDataObject interface
|
||
//
|
||
// Notes: This method returns DATA_S_SAMEFORMATETC assuming
|
||
// that each format the server dispenses is its own
|
||
// canonical format. If this is not the case then this
|
||
// method should be overridden.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
STDMETHODIMP
|
||
SrvrDV::GetCanonicalFormatEtc(LPFORMATETC pformatetc,
|
||
LPFORMATETC pformatetcOut)
|
||
{
|
||
return DATA_S_SAMEFORMATETC;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::SetData, public
|
||
//
|
||
// Synopsis: Method of IDataObject interface
|
||
//
|
||
// Notes: This method searches the server's Set format table
|
||
// for a compatible format. If one is found it calls
|
||
// the corresponding Set function.
|
||
//
|
||
//---------------------------------------------------------------
|
||
STDMETHODIMP
|
||
SrvrDV::SetData(LPFORMATETC pformatetc, STGMEDIUM FAR *pmedium, BOOL fRelease)
|
||
{
|
||
DOUT(TEXT("SrvrDV:SetData\r\n"));
|
||
|
||
HRESULT hr;
|
||
int i = FindCompatibleFormat(_pClass->_pSetFmtTable,
|
||
_pClass->_cSetFmtTable,
|
||
*pformatetc);
|
||
if (i < 0)
|
||
{
|
||
DOUT(TEXT("SrvrDV::SetData DV_E_FORMATETC\r\n"));
|
||
hr = DV_E_FORMATETC;
|
||
}
|
||
else
|
||
{
|
||
hr = (*_pSetFuncs[i]) (this, pformatetc, pmedium);
|
||
}
|
||
|
||
if (fRelease)
|
||
{
|
||
ReleaseStgMedium(pmedium);
|
||
}
|
||
|
||
return hr;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::EnumFormatEtc, public
|
||
//
|
||
// Synopsis: Method of IDataObject interface
|
||
//
|
||
// Notes: This method creates an enumerator over the Get or
|
||
// Set format tables depending on the value of the
|
||
// dwDirection argument.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
STDMETHODIMP
|
||
SrvrDV::EnumFormatEtc(DWORD dwDirection, LPENUMFORMATETC FAR* ppenumFormatEtc)
|
||
{
|
||
if (ppenumFormatEtc == NULL)
|
||
{
|
||
DOUT(TEXT("SrvrDV::EnumFormatEtc E_INVALIDARG\r\n"));
|
||
return E_INVALIDARG;
|
||
}
|
||
*ppenumFormatEtc = NULL; // set out params to NULL
|
||
|
||
// create an enumerator over our static format table.
|
||
HRESULT hr = E_INVALIDARG;
|
||
switch (dwDirection)
|
||
{
|
||
case DATADIR_GET:
|
||
hr = CreateFORMATETCEnum(_pClass->_pGetFmtTable,
|
||
_pClass->_cGetFmtTable,
|
||
ppenumFormatEtc);
|
||
break;
|
||
case DATADIR_SET:
|
||
hr = CreateFORMATETCEnum(_pClass->_pSetFmtTable,
|
||
_pClass->_cSetFmtTable,
|
||
ppenumFormatEtc);
|
||
break;
|
||
default:
|
||
DOUT(TEXT("SrvrDV::EnumFormatEtc E_INVALIDARG (2)\r\n"));
|
||
break;
|
||
}
|
||
|
||
return hr;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::DAdvise, public
|
||
//
|
||
// Synopsis: Method of IDataObject interface
|
||
//
|
||
// Notes: This method uses the standard OLE data advise holder.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
STDMETHODIMP
|
||
SrvrDV::DAdvise(FORMATETC FAR* pFormatetc,
|
||
DWORD advf,
|
||
LPADVISESINK pAdvSink,
|
||
DWORD FAR* pdwConnection)
|
||
{
|
||
if (pdwConnection == NULL)
|
||
{
|
||
DOUT(TEXT("SrvrDV::DAdvise E_INVALIDARG\r\n"));
|
||
return E_INVALIDARG;
|
||
}
|
||
|
||
*pdwConnection = NULL; // set out params to NULL
|
||
|
||
HRESULT hr = NOERROR;
|
||
if (_pDataAdviseHolder == NULL)
|
||
hr = CreateDataAdviseHolder(&_pDataAdviseHolder);
|
||
|
||
if (OK(hr))
|
||
{
|
||
hr = _pDataAdviseHolder->Advise((LPDATAOBJECT)this,
|
||
pFormatetc,
|
||
advf,
|
||
pAdvSink,
|
||
pdwConnection);
|
||
}
|
||
|
||
return hr;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::DUnadvise, public
|
||
//
|
||
// Synopsis: Method of IDataObject interface
|
||
//
|
||
// Notes: This method uses the standard OLE data advise holder.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
STDMETHODIMP
|
||
SrvrDV::DUnadvise(DWORD dwConnection)
|
||
{
|
||
if (_pDataAdviseHolder == NULL)
|
||
return NOERROR;
|
||
|
||
return _pDataAdviseHolder->Unadvise(dwConnection);
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::EnumDAdvise, public
|
||
//
|
||
// Synopsis: Method of IDataObject interface
|
||
//
|
||
// Notes: This method uses the standard OLE data advise holder.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
STDMETHODIMP
|
||
SrvrDV::EnumDAdvise(LPENUMSTATDATA FAR* ppenumAdvise)
|
||
{
|
||
if (ppenumAdvise == NULL)
|
||
{
|
||
DOUT(TEXT("SrvrDV::EnumDAdvise E_INVALIDARG\r\n"));
|
||
return E_INVALIDARG;
|
||
}
|
||
|
||
*ppenumAdvise = NULL; // set out params to NULL
|
||
|
||
HRESULT hr;
|
||
if (_pDataAdviseHolder == NULL)
|
||
hr = NOERROR;
|
||
else
|
||
hr = _pDataAdviseHolder->EnumAdvise(ppenumAdvise);
|
||
|
||
return hr;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::RenderContent, public
|
||
//
|
||
// Synopsis: Used to draw the content aspect of the object
|
||
//
|
||
// Notes: This method is used by the implementation of IViewObject::Draw
|
||
// when the content aspect is requested. The parameters are
|
||
// identical to those of IViewObject::Draw.
|
||
// All objects should override this method.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
HRESULT
|
||
SrvrDV::RenderContent(DWORD dwDrawAspect,
|
||
LONG lindex,
|
||
void FAR* pvAspect,
|
||
DVTARGETDEVICE FAR * ptd,
|
||
HDC hicTargetDev,
|
||
HDC hdcDraw,
|
||
LPCRECTL lprectl,
|
||
LPCRECTL lprcWBounds,
|
||
BOOL (CALLBACK * pfnContinue) (ULONG_PTR),
|
||
ULONG_PTR dwContinue)
|
||
{
|
||
return NOERROR;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::RenderPrint, public
|
||
//
|
||
// Synopsis: Used to draw the print aspect of the object
|
||
//
|
||
// Notes: This method is used by the implementation of IViewObject::Draw
|
||
// when the docprint aspect is requested. The parameters are
|
||
// identical to those of IViewObject::Draw.
|
||
// By default this method calls RenderContent. If the
|
||
// server has special processing for the print case then
|
||
// this method should be overridden.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
HRESULT
|
||
SrvrDV::RenderPrint(DWORD dwDrawAspect,
|
||
LONG lindex,
|
||
void FAR* pvAspect,
|
||
DVTARGETDEVICE FAR * ptd,
|
||
HDC hicTargetDev,
|
||
HDC hdcDraw,
|
||
LPCRECTL lprectl,
|
||
LPCRECTL lprcWBounds,
|
||
BOOL (CALLBACK * pfnContinue) (ULONG_PTR),
|
||
ULONG_PTR dwContinue)
|
||
{
|
||
return RenderContent(dwDrawAspect,
|
||
lindex,
|
||
pvAspect,
|
||
ptd,
|
||
hicTargetDev,
|
||
hdcDraw,
|
||
lprectl,
|
||
lprcWBounds,
|
||
pfnContinue,
|
||
dwContinue);
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::RenderThumbnail, public
|
||
//
|
||
// Synopsis: Used to draw the thumbnail aspect of the object
|
||
//
|
||
// Notes: This method is used by the implementation of IViewObject::Draw
|
||
// when the thumbnail aspect is requested. The parameters are
|
||
// identical to those of IViewObject::Draw.
|
||
// All objects should override this method.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
HRESULT
|
||
SrvrDV::RenderThumbnail(DWORD dwDrawAspect,
|
||
LONG lindex,
|
||
void FAR* pvAspect,
|
||
DVTARGETDEVICE FAR * ptd,
|
||
HDC hicTargetDev,
|
||
HDC hdcDraw,
|
||
LPCRECTL lprectl,
|
||
LPCRECTL lprcWBounds,
|
||
BOOL (CALLBACK * pfnContinue) (ULONG_PTR),
|
||
ULONG_PTR dwContinue)
|
||
{
|
||
return NOERROR;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::Draw, public
|
||
//
|
||
// Synopsis: Method of IViewObject interface
|
||
//
|
||
// Notes: This method calls RenderContent/Print/Thumbnail for
|
||
// those respective aspects. It handles the icon aspect
|
||
// automatically using the icon found in the class descriptor
|
||
// indicated by the _pClass member.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
STDMETHODIMP
|
||
SrvrDV::Draw(DWORD dwDrawAspect,
|
||
LONG lindex,
|
||
void FAR* pvAspect,
|
||
DVTARGETDEVICE FAR * ptd,
|
||
HDC hicTargetDev,
|
||
HDC hdcDraw,
|
||
LPCRECTL lprectl,
|
||
LPCRECTL lprcWBounds,
|
||
BOOL (CALLBACK * pfnContinue) (ULONG_PTR),
|
||
ULONG_PTR dwContinue)
|
||
{
|
||
HRESULT hr;
|
||
switch(dwDrawAspect) {
|
||
case DVASPECT_CONTENT:
|
||
hr = RenderContent(dwDrawAspect,
|
||
lindex,
|
||
pvAspect,
|
||
ptd,
|
||
hicTargetDev,
|
||
hdcDraw,
|
||
lprectl,
|
||
lprcWBounds,
|
||
pfnContinue,
|
||
dwContinue);
|
||
break;
|
||
|
||
case DVASPECT_DOCPRINT:
|
||
hr = RenderPrint(dwDrawAspect,
|
||
lindex,
|
||
pvAspect,
|
||
ptd,
|
||
hicTargetDev,
|
||
hdcDraw,
|
||
lprectl,
|
||
lprcWBounds,
|
||
pfnContinue,
|
||
dwContinue);
|
||
break;
|
||
|
||
case DVASPECT_THUMBNAIL:
|
||
hr = RenderThumbnail(dwDrawAspect,
|
||
lindex,
|
||
pvAspect,
|
||
ptd,
|
||
hicTargetDev,
|
||
hdcDraw,
|
||
lprectl,
|
||
lprcWBounds,
|
||
pfnContinue,
|
||
dwContinue);
|
||
break;
|
||
|
||
case DVASPECT_ICON:
|
||
{
|
||
// This is not the right way to do iconic aspect rendering!
|
||
RECT rc;
|
||
RECTL rcTemp = *lprectl;
|
||
RECTLtoRECT(rcTemp, &rc);
|
||
DrawIcon(hdcDraw, rc.left, rc.top, _pClass->_hicon);
|
||
hr = NOERROR;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
DOUT(TEXT("SrvrDV::Draw E_INVALIDARG\r\n"));
|
||
hr = E_INVALIDARG;
|
||
break;
|
||
}
|
||
|
||
return hr;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::GetColorSet, public
|
||
//
|
||
// Synopsis: Method of IViewObject interface
|
||
//
|
||
// Notes: This method returns S_FALSE indicating the server
|
||
// does not support this functionality. Server's that
|
||
// wish to support it should override this method.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
STDMETHODIMP
|
||
SrvrDV::GetColorSet(DWORD dwDrawAspect,
|
||
LONG lindex,
|
||
void FAR* pvAspect,
|
||
DVTARGETDEVICE FAR * ptd,
|
||
HDC hicTargetDev,
|
||
LPLOGPALETTE FAR* ppColorSet)
|
||
{
|
||
if (ppColorSet == NULL)
|
||
{
|
||
DOUT(TEXT("SrvrDV::GetColorSet E_INVALIDARG\r\n"));
|
||
return E_INVALIDARG;
|
||
}
|
||
|
||
*ppColorSet = NULL; //set out params to NULL
|
||
|
||
return S_FALSE;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::Freeze, public
|
||
//
|
||
// Synopsis: Method of IViewObject interface
|
||
//
|
||
// Notes: This method sets the frozen flag, _fFrozen.
|
||
// The derived class must pay attention to this flag
|
||
// and not allow any modifications that would change
|
||
// the current rendering.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
STDMETHODIMP
|
||
SrvrDV::Freeze(DWORD dwDrawAspect,
|
||
LONG lindex,
|
||
void FAR* pvAspect,
|
||
DWORD FAR* pdwFreeze)
|
||
{
|
||
if (pdwFreeze == NULL)
|
||
{
|
||
DOUT(TEXT("SrvrDV::Freeze E_INVALIDARG\r\n"));
|
||
return E_INVALIDARG;
|
||
}
|
||
|
||
*pdwFreeze = 0; //set out params to NULL
|
||
|
||
_fFrozen = TRUE;
|
||
return NOERROR;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::Unfreeze, public
|
||
//
|
||
// Synopsis: Method of IViewObject interface
|
||
//
|
||
// Notes: This method clears the frozen flag, _fFrozen.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
STDMETHODIMP
|
||
SrvrDV::Unfreeze(DWORD dwFreeze)
|
||
{
|
||
_fFrozen = FALSE;
|
||
return NOERROR;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::SetAdvise, public
|
||
//
|
||
// Synopsis: Method of IViewObject interface
|
||
//
|
||
// Notes: This method implements an advise holder for the view
|
||
// advise.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
STDMETHODIMP
|
||
SrvrDV::SetAdvise(DWORD aspects, DWORD advf, LPADVISESINK pAdvSink)
|
||
{
|
||
HRESULT hr = NOERROR;
|
||
if (_pViewAdviseHolder == NULL)
|
||
hr = CreateViewAdviseHolder(&_pViewAdviseHolder);
|
||
|
||
if (OK(hr))
|
||
hr = _pViewAdviseHolder->SetAdvise(aspects, advf, pAdvSink);
|
||
|
||
return hr;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::GetAdvise, public
|
||
//
|
||
// Synopsis: Method of IViewObject interface
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
STDMETHODIMP
|
||
SrvrDV::GetAdvise(DWORD FAR* pAspects,
|
||
DWORD FAR* pAdvf,
|
||
LPADVISESINK FAR* ppAdvSink)
|
||
{
|
||
if (ppAdvSink == NULL)
|
||
{
|
||
DOUT(TEXT("SrvrDV::GetAdvise E_INVALIDARG\r\n"));
|
||
return E_INVALIDARG;
|
||
}
|
||
|
||
*ppAdvSink = NULL; // set out params to NULL
|
||
|
||
HRESULT hr;
|
||
if (_pViewAdviseHolder==NULL)
|
||
hr = NOERROR;
|
||
else
|
||
hr = _pViewAdviseHolder->GetAdvise(pAspects, pAdvf, ppAdvSink);
|
||
|
||
return hr;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::LoadFromStream, protected
|
||
//
|
||
// Synopsis: Loads the object's persistent state from a stream
|
||
//
|
||
// Arguments: [pStrm] -- stream to load from
|
||
//
|
||
// Returns: Success iff persistent state was read
|
||
//
|
||
// Notes: This function is used in the implementation of
|
||
// IPersistStream::Load and IPersistFile::Load when
|
||
// the file is not a docfile.
|
||
// All objects should override this method.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
HRESULT
|
||
SrvrDV::LoadFromStream(LPSTREAM pStrm)
|
||
{
|
||
return NOERROR;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::SaveToStream, protected
|
||
//
|
||
// Synopsis: Saves the object's persistent state to a stream
|
||
//
|
||
// Arguments: [pStrm] -- stream to save to
|
||
//
|
||
// Returns: Success iff persistent state was written
|
||
//
|
||
// Notes: This function is used in the implementation of
|
||
// IPersistStream::Save and IPersistFile::Save when
|
||
// the file is not a docfile.
|
||
// All objects should override this method.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
HRESULT
|
||
SrvrDV::SaveToStream(LPSTREAM pStrm)
|
||
{
|
||
return NOERROR;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::GetStreamSizeMax, protected
|
||
//
|
||
// Synopsis: Returns the number of bytes required to serialize object
|
||
//
|
||
// Notes: This function is used in the implementation of
|
||
// IPersistStream::GetSizeMax.
|
||
// All objects should override this method.
|
||
//
|
||
//---------------------------------------------------------------
|
||
DWORD
|
||
SrvrDV::GetStreamSizeMax(void)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::GetClassID, public
|
||
//
|
||
// Synopsis: Method of IPersist interface
|
||
//
|
||
// Notes: This method uses the class id in the class descriptor.
|
||
//
|
||
//---------------------------------------------------------------
|
||
STDMETHODIMP
|
||
SrvrDV::GetClassID(LPCLSID lpClassID)
|
||
{
|
||
if (lpClassID == NULL)
|
||
{
|
||
DOUT(TEXT("SrvrDV::GetClassID E_INVALIDARG\r\n"));
|
||
return E_INVALIDARG;
|
||
}
|
||
|
||
*lpClassID = _pClass->_clsid;
|
||
return NOERROR;
|
||
}
|
||
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::IsDirty, public
|
||
//
|
||
// Synopsis: Method of IPersistStream/Storage/File interface
|
||
//
|
||
// Notes: This method uses the dirty flag, _fDirty.
|
||
// Objects should not set the _fDirty flag directly
|
||
// but instead call the OnDataChange method to set the
|
||
// flag.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
STDMETHODIMP
|
||
SrvrDV::IsDirty(void)
|
||
{
|
||
return (_fDirty ? NOERROR : S_FALSE);
|
||
}
|
||
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::Load, public
|
||
//
|
||
// Synopsis: Method of IPersistStream interface
|
||
//
|
||
// Notes: This function uses the LoadFromStream method and
|
||
// transitions the object to the loaded state if the
|
||
// load was successful.
|
||
//
|
||
//---------------------------------------------------------------
|
||
STDMETHODIMP
|
||
SrvrDV::Load(LPSTREAM pStrm)
|
||
{
|
||
// object can be loaded only once!
|
||
if (_pCtrl->State() != OS_PASSIVE)
|
||
{
|
||
DOUT(TEXT("SrvrDV::Load E_FAIL\r\n"));
|
||
return E_FAIL;
|
||
}
|
||
|
||
HRESULT hr;
|
||
if (OK(hr = LoadFromStream(pStrm)))
|
||
hr = _pCtrl->TransitionTo(OS_LOADED);
|
||
|
||
return hr;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::Save, public
|
||
//
|
||
// Synopsis: Method of IPersistStream interface
|
||
//
|
||
// Notes: This method uses the SaveToStream method and
|
||
// clears the _fDirty flag as appropriate.
|
||
// Containers that have nonserializeable embeddings can
|
||
// override this method and return STG_E_CANTSAVE
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
STDMETHODIMP
|
||
SrvrDV::Save(LPSTREAM pStrm, BOOL fClearDirty)
|
||
{
|
||
HRESULT hr;
|
||
if (OK(hr = SaveToStream(pStrm)))
|
||
{
|
||
if (fClearDirty)
|
||
{
|
||
_fDirty = FALSE;
|
||
}
|
||
}
|
||
|
||
return hr;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::GetSizeMax
|
||
//
|
||
// Synopsis: Method of IPersistStream interface
|
||
//
|
||
// Notes:
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
STDMETHODIMP
|
||
SrvrDV::GetSizeMax(ULARGE_INTEGER FAR * pcbSize)
|
||
{
|
||
if (pcbSize == NULL)
|
||
{
|
||
DOUT(TEXT("SrvrDV::GetSizeMax E_INVALIDARG\r\n"));
|
||
return E_INVALIDARG;
|
||
}
|
||
|
||
ULISet32(*pcbSize, GetStreamSizeMax());
|
||
|
||
return NOERROR;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::LoadFromStorage, protected
|
||
//
|
||
// Synopsis: Loads the object's persistent state from a storage
|
||
//
|
||
// Arguments: [pSg] -- storage to load from
|
||
//
|
||
// Returns: Success iff persistent state was read
|
||
//
|
||
// Notes: This function is used in the implementation of
|
||
// IPersistStorage::Load and IPersistFile::Load when
|
||
// the file is a docfile.
|
||
// This method opens a stream, "CONTENTS", and uses
|
||
// method LoadFromStream to complete the load.
|
||
// Servers that do more sophisticated loading will want
|
||
// to override this method.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
HRESULT
|
||
SrvrDV::LoadFromStorage(LPSTORAGE pStg)
|
||
{
|
||
LPSTREAM pStrm;
|
||
HRESULT hr;
|
||
if (OK(hr = pStg->OpenStream(szContents, NULL, STGM_SRO, 0, &pStrm)))
|
||
{
|
||
hr = LoadFromStream(pStrm);
|
||
pStrm->Release();
|
||
}
|
||
|
||
return hr;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::SaveToStorage
|
||
//
|
||
// Synopsis: Saves the object's persistent state to a storage
|
||
//
|
||
// Arguments: [pSg] -- storage to save to
|
||
//
|
||
// Returns: Success iff persistent state was written
|
||
//
|
||
// Notes: This function is used in the implementation of
|
||
// IPersistStorage::Save and IPersistFile::Save when
|
||
// the file is a docfile.
|
||
// This method opens a stream, "CONTENTS", and uses
|
||
// method SaveToStream to complete the save.
|
||
// Servers that do more sophisticated saving will want
|
||
// to override this method.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
HRESULT
|
||
SrvrDV::SaveToStorage(LPSTORAGE pStg, BOOL fSameAsLoad)
|
||
{
|
||
// write our native data stream
|
||
HRESULT hr;
|
||
LPSTREAM pStrm;
|
||
hr = pStg->CreateStream(szContents, STGM_SALL|STGM_CREATE, 0L, 0L, &pStrm);
|
||
if (OK(hr))
|
||
{
|
||
hr = SaveToStream(pStrm);
|
||
pStrm->Release();
|
||
}
|
||
|
||
return hr;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::InitNew
|
||
//
|
||
// Synopsis: IPersistStorage Method
|
||
//
|
||
// Notes: This method transitions the object to loaded.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
STDMETHODIMP
|
||
SrvrDV::InitNew(LPSTORAGE pStg)
|
||
{
|
||
//
|
||
//REVIEW: what happens if we attempt to load the same ctrl more than once?
|
||
//
|
||
if (pStg == NULL)
|
||
{
|
||
DOUT(TEXT("SrvrDV::InitNew E_INVALIDARG\r\n"));
|
||
return E_INVALIDARG;
|
||
}
|
||
|
||
HRESULT hr;
|
||
if (OK(hr = _pCtrl->TransitionTo(OS_LOADED)))
|
||
(_pStg = pStg)->AddRef(); // hold on to the storage
|
||
|
||
return hr;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::Load
|
||
//
|
||
// Synopsis: IPersistStorage Method
|
||
//
|
||
// Notes: This method loads the object using LoadFromStorage and
|
||
// then transitions the object to loaded.
|
||
// A pointer to our storage is maintained in member variable
|
||
// _pStg.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
STDMETHODIMP
|
||
SrvrDV::Load(LPSTORAGE pStg)
|
||
{
|
||
// object can be loaded only once!
|
||
if (_pCtrl->State() != OS_PASSIVE)
|
||
{
|
||
DOUT(TEXT("SrvrDV::Load E_FAIL\r\n"));
|
||
return E_FAIL;
|
||
}
|
||
|
||
if (pStg == NULL)
|
||
{
|
||
DOUT(TEXT("SrvrDV::Load E_INVALIDARG\r\n"));
|
||
return E_INVALIDARG;
|
||
}
|
||
|
||
// do the load and move to the loaded state
|
||
HRESULT hr;
|
||
if (OK(hr = LoadFromStorage(pStg)))
|
||
{
|
||
if (OK(hr = _pCtrl->TransitionTo(OS_LOADED)))
|
||
{
|
||
(_pStg = pStg)->AddRef();
|
||
}
|
||
}
|
||
|
||
return hr;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::Save
|
||
//
|
||
// Synopsis: Method of IPersistStorage interface
|
||
//
|
||
// Notes: This method uses SaveToStorage to write the persistent
|
||
// state. It also writes the full user type string to the
|
||
// storage as is required.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
STDMETHODIMP
|
||
SrvrDV::Save(LPSTORAGE pStg, BOOL fSameAsLoad)
|
||
{
|
||
if (pStg == NULL)
|
||
{
|
||
DOUT(TEXT("SrvrDV::Save E_INVALIDARG\r\n"));
|
||
return E_INVALIDARG;
|
||
}
|
||
|
||
// write our native data stream
|
||
HRESULT hr;
|
||
if (OK(hr = SaveToStorage(pStg, fSameAsLoad)))
|
||
{
|
||
// Write the UserType string. We don't let this fail the operation.
|
||
WriteFmtUserTypeStg(pStg,
|
||
0,
|
||
_pClass->_szUserClassType[USERCLASSTYPE_FULL]);
|
||
|
||
_fNoScribble = TRUE;
|
||
}
|
||
|
||
return hr;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::SaveCompleted
|
||
//
|
||
// Synopsis: Method of IPersistStorage interface
|
||
//
|
||
// Notes: This method clears the dirty flag and updates our
|
||
// storage pointer, _pStg, if required.
|
||
// Servers that are also containers will want to override
|
||
// this method to pass the call recursively to all loaded
|
||
// embeddings.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
STDMETHODIMP
|
||
SrvrDV::SaveCompleted(LPSTORAGE pStg)
|
||
{
|
||
// if pStg is non-null then we are coming out of HANDS-OFF mode,
|
||
// otherwise we are coming out of NO-SCRIBBLE mode.
|
||
if (pStg != NULL)
|
||
{
|
||
// We should be in HANDS-OFF mode and hence able to Assert that
|
||
// _pStg is NULL here by virtue of the HandsOffStorage call.
|
||
// However, the official OLE sample container app "Outline"
|
||
// fail to make the HandsOffStorage call.
|
||
// In order to be robust we release our _pStg handle if it is
|
||
// "illegally" NON-NULL
|
||
//
|
||
if (_pStg != NULL)
|
||
{
|
||
DOUT(TEXT("SrvrDV: WARNING! SaveCompleted: "));
|
||
DOUT(TEXT("SrvrDV: Container failed to make required HandsOffStorage call.\n"));
|
||
_pStg->Release();
|
||
}
|
||
|
||
(_pStg = pStg)->AddRef(); // hold on to the new storage
|
||
}
|
||
|
||
_fDirty = FALSE; // clear our dirty flag
|
||
_fNoScribble = FALSE; // we are out of NO-SCRIBBLE mode
|
||
|
||
//REVIEW: should we advise in the case we are not fRemembering?
|
||
if (_pCtrl != NULL)
|
||
_pCtrl->OnSave(); // and notify any advises that we have saved
|
||
|
||
return NOERROR;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::HandsOffStorage
|
||
//
|
||
// Synopsis: Method of IPersistStorage interface
|
||
//
|
||
// Notes: This method releases the storage we are holding on to.
|
||
// Servers that are also containers will want to override
|
||
// this method to pass the call recursively to all loaded
|
||
// embeddings.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
STDMETHODIMP
|
||
SrvrDV::HandsOffStorage(void)
|
||
{
|
||
if (_pStg != NULL)
|
||
_pStg->Release();
|
||
_pStg = NULL;
|
||
|
||
return NOERROR;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::Load, public
|
||
//
|
||
// Synopsis: Method of IPersistFile interface
|
||
//
|
||
// Notes: This opens the file as a docfile and uses IPersistStorage::Load
|
||
// to complete the operation.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
STDMETHODIMP
|
||
SrvrDV::Load(LPCOLESTR lpstrFile, DWORD grfMode)
|
||
{
|
||
|
||
// use the default storage modes if no flags were specified
|
||
if (grfMode == 0)
|
||
grfMode = STGM_DFRALL;
|
||
|
||
// if they didn't specify a share mode then use deny-write
|
||
if ( (grfMode & STGM_SHARE) == 0)
|
||
grfMode |= STGM_SHARE_DENY_WRITE;
|
||
|
||
// of course, we use transacted mode
|
||
grfMode |= STGM_TRANSACTED;
|
||
|
||
HRESULT hr;
|
||
if (lpstrFile == NULL)
|
||
{
|
||
// lpstrFile NULL is a special-case indicating that we should
|
||
// create a temporary docfile for the new file case
|
||
//
|
||
grfMode |= STGM_CREATE | STGM_DELETEONRELEASE;
|
||
|
||
LPSTORAGE pStg;
|
||
if (OK(hr = StgCreateDocfile(NULL, grfMode, 0L, &pStg)))
|
||
{
|
||
hr = InitNew(pStg);
|
||
|
||
// IPersistStorage::InitNew will hold on to the pStg
|
||
pStg->Release();
|
||
}
|
||
return hr;
|
||
}
|
||
|
||
LPSTORAGE pStg;
|
||
if (OK(hr = StgOpenStorage(lpstrFile, NULL, grfMode, NULL, 0L, &pStg)))
|
||
{
|
||
hr = Load(pStg);
|
||
|
||
// IPersistStorage::Load will hold on to the pStg
|
||
pStg->Release();
|
||
}
|
||
//REVIEW: Is the first SetMoniker happening correctly?
|
||
return hr;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::Save, public
|
||
//
|
||
// Synopsis: Method of IPersistFile interface
|
||
//
|
||
// Notes: If a file is specified then this creates a docfile and
|
||
// uses IPersistStorage::Save to complete the operation.
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
STDMETHODIMP
|
||
SrvrDV::Save(LPCOLESTR lpstrFile, BOOL fRemember)
|
||
{
|
||
// if lpstrFile is NULL that means that we should save from where we
|
||
// loaded. Otherwise create a docfile with the specified name
|
||
HRESULT hr = NOERROR;
|
||
LPSTORAGE pStg;
|
||
|
||
if (lpstrFile == NULL)
|
||
(pStg = _pStg)->AddRef();
|
||
else
|
||
hr = StgCreateDocfile(lpstrFile, STGM_DFRALL|STGM_CREATE, 0L, &pStg);
|
||
|
||
if (OK(hr))
|
||
{
|
||
hr = OleSave((LPPERSISTSTORAGE)this, pStg, pStg == _pStg);
|
||
|
||
if (OK(hr))
|
||
{
|
||
// if we are to remember this storage then release our old
|
||
// storage and hold on to the new.
|
||
// Otherwise, wrap up a storage save by the usual SaveCompleted.
|
||
if (lpstrFile != NULL && fRemember)
|
||
{
|
||
// release our previous storage or stream
|
||
// and hold on to our new
|
||
HandsOffStorage();
|
||
((LPPERSISTSTORAGE)this)->SaveCompleted(pStg);
|
||
}
|
||
else
|
||
{
|
||
// If we did a storage save and we are not switching to a new
|
||
// storage then we complete the transaction with a SaveCompleted.
|
||
((LPPERSISTSTORAGE)this)->SaveCompleted(NULL);
|
||
}
|
||
}
|
||
|
||
// Release the storage. If we are supposed to hold on
|
||
// to it then we have already add-ref'd it.
|
||
pStg->Release();
|
||
}
|
||
|
||
// if we have renamed then
|
||
if (lpstrFile != NULL)
|
||
{
|
||
// TBD: Send On_Renamed advise?
|
||
//
|
||
// inform our object of its new moniker
|
||
//
|
||
LPMONIKER pmk;
|
||
if (OK(CreateFileMoniker(lpstrFile, &pmk)))
|
||
{
|
||
_pCtrl->SetMoniker(OLEWHICHMK_OBJFULL, pmk);
|
||
pmk->Release();
|
||
}
|
||
}
|
||
|
||
return hr;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::SaveCompleted, public
|
||
//
|
||
// Synopsis: Method of IPersistFile interface
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
STDMETHODIMP
|
||
SrvrDV::SaveCompleted(LPCOLESTR lpstrFile)
|
||
{
|
||
//REVIEW: should we launch advise in the case we are not fRemembering?
|
||
if (_pCtrl != NULL)
|
||
_pCtrl->OnSave(); // and notify any advises that we have saved
|
||
|
||
return NOERROR;
|
||
}
|
||
|
||
//+---------------------------------------------------------------
|
||
//
|
||
// Member: SrvrDV::GetCurFile, public
|
||
//
|
||
// Synopsis: Method of IPersistFile interface
|
||
//
|
||
//---------------------------------------------------------------
|
||
|
||
STDMETHODIMP
|
||
SrvrDV::GetCurFile(LPOLESTR FAR * ppstrFile)
|
||
{
|
||
if (ppstrFile == NULL)
|
||
{
|
||
DOUT(TEXT("SrvrDV::GetCurFile E_INVALIDARG\r\n"));
|
||
return E_INVALIDARG;
|
||
}
|
||
|
||
*ppstrFile = 0; //set out params to NULL
|
||
|
||
HRESULT hr;
|
||
|
||
// if we don't currently have a file then return the default filename
|
||
if (_pStg == NULL)
|
||
{
|
||
// the default filename is *.ext where ext is our docfile extension
|
||
OLECHAR szDefault[6];
|
||
#if defined(OLE2ANSI)
|
||
wsprintf(szDefault, TEXT("*%s"), _pClass->_szDocfileExt);
|
||
#else
|
||
wsprintfW(szDefault, OLETEXT("*%ws"), _pClass->_szDocfileExt);
|
||
#endif
|
||
hr = TaskAllocString(szDefault, ppstrFile);
|
||
}
|
||
else
|
||
{
|
||
// the caller will free the task-allocated file name
|
||
STATSTG statstg;
|
||
if (OK(hr = _pStg->Stat(&statstg, STATFLAG_DEFAULT)))
|
||
*ppstrFile = statstg.pwcsName;
|
||
}
|
||
|
||
return hr;
|
||
}
|
||
|