📄 datacache.c
字号:
/*
* OLE 2 Data cache
*
* Copyright 1999 Francis Beaudet
* Copyright 2000 Abey George
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* NOTES:
* The OLE2 data cache supports a whole whack of
* interfaces including:
* IDataObject, IPersistStorage, IViewObject2,
* IOleCache2 and IOleCacheControl.
*
* Most of the implementation details are taken from: Inside OLE
* second edition by Kraig Brockschmidt,
*
* NOTES
* - This implementation of the datacache will let your application
* load documents that have embedded OLE objects in them and it will
* also retrieve the metafile representation of those objects.
* - This implementation of the datacache will also allow your
* application to save new documents with OLE objects in them.
* - The main thing that it doesn't do is allow you to activate
* or modify the OLE objects in any way.
* - I haven't found any good documentation on the real usage of
* the streams created by the data cache. In particular, How to
* determine what the XXX stands for in the stream name
* "\002OlePresXXX". It appears to just be a counter.
* - Also, I don't know the real content of the presentation stream
* header. I was able to figure-out where the extent of the object
* was stored and the aspect, but that's about it.
*/
#include <assert.h>
#include <stdarg.h>
#include <string.h>
#define COBJMACROS
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "winerror.h"
#include "wine/unicode.h"
#include "ole2.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(ole);
/****************************************************************************
* PresentationDataHeader
*
* This structure represents the header of the \002OlePresXXX stream in
* the OLE object strorage.
*
* Most fields are still unknown.
*/
typedef struct PresentationDataHeader
{
DWORD unknown1; /* -1 */
DWORD unknown2; /* 3, possibly CF_METAFILEPICT */
DWORD unknown3; /* 4, possibly TYMED_ISTREAM */
DVASPECT dvAspect;
DWORD unknown5; /* -1 */
DWORD unknown6;
DWORD unknown7; /* 0 */
DWORD dwObjectExtentX;
DWORD dwObjectExtentY;
DWORD dwSize;
} PresentationDataHeader;
/****************************************************************************
* DataCache
*/
struct DataCache
{
/*
* List all interface VTables here
*/
const IDataObjectVtbl* lpVtbl;
const IUnknownVtbl* lpvtblNDIUnknown;
const IPersistStorageVtbl* lpvtblIPersistStorage;
const IViewObject2Vtbl* lpvtblIViewObject;
const IOleCache2Vtbl* lpvtblIOleCache2;
const IOleCacheControlVtbl* lpvtblIOleCacheControl;
/*
* Reference count of this object
*/
LONG ref;
/*
* IUnknown implementation of the outer object.
*/
IUnknown* outerUnknown;
/*
* This storage pointer is set through a call to
* IPersistStorage_Load. This is where the visual
* representation of the object is stored.
*/
IStorage* presentationStorage;
/*
* The user of this object can setup ONE advise sink
* connection with the object. These parameters describe
* that connection.
*/
DWORD sinkAspects;
DWORD sinkAdviseFlag;
IAdviseSink* sinkInterface;
};
typedef struct DataCache DataCache;
/*
* Here, I define utility macros to help with the casting of the
* "this" parameter.
* There is a version to accommodate all of the VTables implemented
* by this object.
*/
static inline DataCache *impl_from_IDataObject( IDataObject *iface )
{
return (DataCache *)((char*)iface - FIELD_OFFSET(DataCache, lpVtbl));
}
static inline DataCache *impl_from_NDIUnknown( IUnknown *iface )
{
return (DataCache *)((char*)iface - FIELD_OFFSET(DataCache, lpvtblNDIUnknown));
}
static inline DataCache *impl_from_IPersistStorage( IPersistStorage *iface )
{
return (DataCache *)((char*)iface - FIELD_OFFSET(DataCache, lpvtblIPersistStorage));
}
static inline DataCache *impl_from_IViewObject2( IViewObject2 *iface )
{
return (DataCache *)((char*)iface - FIELD_OFFSET(DataCache, lpvtblIViewObject));
}
static inline DataCache *impl_from_IOleCache2( IOleCache2 *iface )
{
return (DataCache *)((char*)iface - FIELD_OFFSET(DataCache, lpvtblIOleCache2));
}
static inline DataCache *impl_from_IOleCacheControl( IOleCacheControl *iface )
{
return (DataCache *)((char*)iface - FIELD_OFFSET(DataCache, lpvtblIOleCacheControl));
}
/*
* Prototypes for the methods of the DataCache class.
*/
static DataCache* DataCache_Construct(REFCLSID clsid,
LPUNKNOWN pUnkOuter);
static HRESULT DataCache_OpenPresStream(DataCache *this,
DWORD drawAspect,
IStream **pStm);
static void DataCache_Destroy(
DataCache* ptrToDestroy)
{
TRACE("()\n");
if (ptrToDestroy->sinkInterface != NULL)
{
IAdviseSink_Release(ptrToDestroy->sinkInterface);
ptrToDestroy->sinkInterface = NULL;
}
if (ptrToDestroy->presentationStorage != NULL)
{
IStorage_Release(ptrToDestroy->presentationStorage);
ptrToDestroy->presentationStorage = NULL;
}
/*
* Free the datacache pointer.
*/
HeapFree(GetProcessHeap(), 0, ptrToDestroy);
}
/************************************************************************
* DataCache_ReadPresentationData
*
* This method will read information for the requested presentation
* into the given structure.
*
* Param:
* this - Pointer to the DataCache object
* drawAspect - The aspect of the object that we wish to draw.
* header - The structure containing information about this
* aspect of the object.
*/
static HRESULT DataCache_ReadPresentationData(
DataCache* this,
DWORD drawAspect,
PresentationDataHeader* header)
{
IStream* presStream = NULL;
HRESULT hres;
/*
* Open the presentation stream.
*/
hres = DataCache_OpenPresStream(
this,
drawAspect,
&presStream);
if (FAILED(hres))
return hres;
/*
* Read the header.
*/
hres = IStream_Read(
presStream,
header,
sizeof(PresentationDataHeader),
NULL);
/*
* Cleanup.
*/
IStream_Release(presStream);
/*
* We don't want to propagate any other error
* code than a failure.
*/
if (hres!=S_OK)
hres = E_FAIL;
return hres;
}
/************************************************************************
* DataCache_FireOnViewChange
*
* This method will fire an OnViewChange notification to the advise
* sink registered with the datacache.
*
* See IAdviseSink::OnViewChange for more details.
*/
static void DataCache_FireOnViewChange(
DataCache* this,
DWORD aspect,
LONG lindex)
{
TRACE("(%p, %lx, %ld)\n", this, aspect, lindex);
/*
* The sink supplies a filter when it registers
* we make sure we only send the notifications when that
* filter matches.
*/
if ((this->sinkAspects & aspect) != 0)
{
if (this->sinkInterface != NULL)
{
IAdviseSink_OnViewChange(this->sinkInterface,
aspect,
lindex);
/*
* Some sinks want to be unregistered automatically when
* the first notification goes out.
*/
if ( (this->sinkAdviseFlag & ADVF_ONLYONCE) != 0)
{
IAdviseSink_Release(this->sinkInterface);
this->sinkInterface = NULL;
this->sinkAspects = 0;
this->sinkAdviseFlag = 0;
}
}
}
}
/* Helper for DataCache_OpenPresStream */
static BOOL DataCache_IsPresentationStream(const STATSTG *elem)
{
/* The presentation streams have names of the form "\002OlePresXXX",
* where XXX goes from 000 to 999. */
static const WCHAR OlePres[] = { 2,'O','l','e','P','r','e','s' };
LPCWSTR name = elem->pwcsName;
return (elem->type == STGTY_STREAM)
&& (elem->cbSize.u.LowPart >= sizeof(PresentationDataHeader))
&& (strlenW(name) == 11)
&& (strncmpW(name, OlePres, 8) == 0)
&& (name[8] >= '0') && (name[8] <= '9')
&& (name[9] >= '0') && (name[9] <= '9')
&& (name[10] >= '0') && (name[10] <= '9');
}
/************************************************************************
* DataCache_OpenPresStream
*
* This method will find the stream for the given presentation. It makes
* no attempt at fallback.
*
* Param:
* this - Pointer to the DataCache object
* drawAspect - The aspect of the object that we wish to draw.
* pStm - A returned stream. It points to the beginning of the
* - presentation data, including the header.
*
* Errors:
* S_OK The requested stream has been opened.
* OLE_E_BLANK The requested stream could not be found.
* Quite a few others I'm too lazy to map correctly.
*
* Notes:
* Algorithm: Scan the elements of the presentation storage, looking
* for presentation streams. For each presentation stream,
* load the header and check to see if the aspect maches.
*
* If a fallback is desired, just opening the first presentation stream
* is a possibility.
*/
static HRESULT DataCache_OpenPresStream(
DataCache *this,
DWORD drawAspect,
IStream **ppStm)
{
STATSTG elem;
IEnumSTATSTG *pEnum;
HRESULT hr;
if (!ppStm) return E_POINTER;
hr = IStorage_EnumElements(this->presentationStorage, 0, NULL, 0, &pEnum);
if (FAILED(hr)) return hr;
while ((hr = IEnumSTATSTG_Next(pEnum, 1, &elem, NULL)) == S_OK)
{
if (DataCache_IsPresentationStream(&elem))
{
IStream *pStm;
hr = IStorage_OpenStream(this->presentationStorage, elem.pwcsName,
NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0,
&pStm);
if (SUCCEEDED(hr))
{
PresentationDataHeader header;
ULONG actual_read;
hr = IStream_Read(pStm, &header, sizeof(header), &actual_read);
/* can't use SUCCEEDED(hr): S_FALSE counts as an error */
if (hr == S_OK && actual_read == sizeof(header)
&& header.dvAspect == drawAspect)
{
/* Rewind the stream before returning it. */
LARGE_INTEGER offset;
offset.u.LowPart = 0;
offset.u.HighPart = 0;
IStream_Seek(pStm, offset, STREAM_SEEK_SET, NULL);
*ppStm = pStm;
CoTaskMemFree(elem.pwcsName);
IEnumSTATSTG_Release(pEnum);
return S_OK;
}
IStream_Release(pStm);
}
}
CoTaskMemFree(elem.pwcsName);
}
IEnumSTATSTG_Release(pEnum);
return (hr == S_FALSE ? OLE_E_BLANK : hr);
}
/************************************************************************
* DataCache_ReadPresentationData
*
* This method will read information for the requested presentation
* into the given structure.
*
* Param:
* this - Pointer to the DataCache object
* drawAspect - The aspect of the object that we wish to draw.
*
* Returns:
* This method returns a metafile handle if it is successful.
* it will return 0 if not.
*/
static HMETAFILE DataCache_ReadPresMetafile(
DataCache* this,
DWORD drawAspect)
{
LARGE_INTEGER offset;
IStream* presStream = NULL;
HRESULT hres;
void* metafileBits;
STATSTG streamInfo;
HMETAFILE newMetafile = 0;
/*
* Open the presentation stream.
*/
hres = DataCache_OpenPresStream(
this,
drawAspect,
&presStream);
if (FAILED(hres))
return (HMETAFILE)hres;
/*
* Get the size of the stream.
*/
hres = IStream_Stat(presStream,
&streamInfo,
STATFLAG_NONAME);
/*
* Skip the header
*/
offset.u.HighPart = 0;
offset.u.LowPart = sizeof(PresentationDataHeader);
hres = IStream_Seek(
presStream,
offset,
STREAM_SEEK_SET,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -