ole32: Implement saving of the data cache.
Document a few of the unknown fields in the presentation data header.
This commit is contained in:
parent
e5c82d3aa4
commit
587ba29174
|
@ -73,12 +73,11 @@ WINE_DEFAULT_DEBUG_CHANNEL(ole);
|
|||
typedef struct PresentationDataHeader
|
||||
{
|
||||
DWORD unknown1; /* -1 */
|
||||
DWORD unknown2; /* 3, possibly CF_METAFILEPICT */
|
||||
DWORD clipformat;
|
||||
DWORD unknown3; /* 4, possibly TYMED_ISTREAM */
|
||||
DVASPECT dvAspect;
|
||||
DWORD unknown5; /* -1 */
|
||||
|
||||
DWORD unknown6;
|
||||
DWORD lindex;
|
||||
DWORD tymed;
|
||||
DWORD unknown7; /* 0 */
|
||||
DWORD dwObjectExtentX;
|
||||
DWORD dwObjectExtentY;
|
||||
|
@ -102,6 +101,8 @@ typedef struct DataCacheEntry
|
|||
DWORD id;
|
||||
/* dirty flag */
|
||||
BOOL dirty;
|
||||
/* stream number (-1 if not set ) */
|
||||
unsigned short stream_number;
|
||||
} DataCacheEntry;
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -186,6 +187,12 @@ static inline DataCache *impl_from_IOleCacheControl( IOleCacheControl *iface )
|
|||
return (DataCache *)((char*)iface - FIELD_OFFSET(DataCache, lpvtblIOleCacheControl));
|
||||
}
|
||||
|
||||
static void dump_FORMATETC(const FORMATETC *formatetc)
|
||||
{
|
||||
TRACE("{ cfFormat = 0x%x, ptd = %p, dwAspect = %d, lindex = %d, tymed = %d }",
|
||||
formatetc->cfFormat, formatetc->ptd, formatetc->dwAspect,
|
||||
formatetc->lindex, formatetc->tymed);
|
||||
}
|
||||
|
||||
/*
|
||||
* Prototypes for the methods of the DataCache class.
|
||||
|
@ -259,6 +266,7 @@ static HRESULT DataCache_CreateEntry(DataCache *This, const FORMATETC *formatetc
|
|||
(*cache_entry)->storage = NULL;
|
||||
(*cache_entry)->id = This->last_cache_id++;
|
||||
(*cache_entry)->dirty = TRUE;
|
||||
(*cache_entry)->stream_number = -1;
|
||||
list_add_tail(&This->cache_list, &(*cache_entry)->entry);
|
||||
return S_OK;
|
||||
}
|
||||
|
@ -517,6 +525,117 @@ static HRESULT DataCacheEntry_LoadData(DataCacheEntry *This)
|
|||
return hres;
|
||||
}
|
||||
|
||||
static HRESULT DataCacheEntry_CreateStream(DataCacheEntry *This,
|
||||
IStorage *storage, IStream **stream)
|
||||
{
|
||||
HRESULT hr;
|
||||
WCHAR wszName[] = {2,'O','l','e','P','r','e','s',
|
||||
'0' + (This->stream_number / 100) % 10,
|
||||
'0' + (This->stream_number / 10) % 10,
|
||||
'0' + This->stream_number % 10, 0};
|
||||
|
||||
/* FIXME: cache the created stream in This? */
|
||||
hr = IStorage_CreateStream(storage, wszName,
|
||||
STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE,
|
||||
0, 0, stream);
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT DataCacheEntry_Save(DataCacheEntry *This, IStorage *storage,
|
||||
BOOL same_as_load)
|
||||
{
|
||||
PresentationDataHeader header;
|
||||
HRESULT hr;
|
||||
IStream *pres_stream;
|
||||
void *data = NULL;
|
||||
|
||||
TRACE("stream_number = %d, fmtetc = ", This->stream_number); dump_FORMATETC(&This->fmtetc); TRACE("\n");
|
||||
|
||||
hr = DataCacheEntry_CreateStream(This, storage, &pres_stream);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
/* custom clipformat */
|
||||
if (This->fmtetc.cfFormat > 0xc000)
|
||||
FIXME("custom clipboard format not serialized properly\n");
|
||||
header.unknown1 = -1;
|
||||
header.clipformat = This->fmtetc.cfFormat;
|
||||
if (This->fmtetc.ptd)
|
||||
FIXME("ptd not serialized\n");
|
||||
header.unknown3 = 4;
|
||||
header.dvAspect = This->fmtetc.dwAspect;
|
||||
header.lindex = This->fmtetc.lindex;
|
||||
header.tymed = This->fmtetc.tymed;
|
||||
header.unknown7 = 0;
|
||||
header.dwObjectExtentX = 0;
|
||||
header.dwObjectExtentY = 0;
|
||||
header.dwSize = 0;
|
||||
|
||||
/* size the data */
|
||||
switch (This->fmtetc.cfFormat)
|
||||
{
|
||||
case CF_METAFILEPICT:
|
||||
{
|
||||
if (This->stgmedium.tymed != TYMED_NULL)
|
||||
{
|
||||
const METAFILEPICT *mfpict = GlobalLock(This->stgmedium.u.hMetaFilePict);
|
||||
if (!mfpict)
|
||||
{
|
||||
IStream_Release(pres_stream);
|
||||
return DV_E_STGMEDIUM;
|
||||
}
|
||||
header.dwObjectExtentX = mfpict->xExt;
|
||||
header.dwObjectExtentY = mfpict->yExt;
|
||||
header.dwSize = GetMetaFileBitsEx(mfpict->hMF, 0, NULL);
|
||||
GlobalUnlock(This->stgmedium.u.hMetaFilePict);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the header.
|
||||
*/
|
||||
hr = IStream_Write(pres_stream, &header, sizeof(PresentationDataHeader),
|
||||
NULL);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
IStream_Release(pres_stream);
|
||||
return hr;
|
||||
}
|
||||
|
||||
/* get the data */
|
||||
switch (This->fmtetc.cfFormat)
|
||||
{
|
||||
case CF_METAFILEPICT:
|
||||
{
|
||||
if (This->stgmedium.tymed != TYMED_NULL)
|
||||
{
|
||||
const METAFILEPICT *mfpict = GlobalLock(This->stgmedium.u.hMetaFilePict);
|
||||
if (!mfpict)
|
||||
{
|
||||
IStream_Release(pres_stream);
|
||||
return DV_E_STGMEDIUM;
|
||||
}
|
||||
data = HeapAlloc(GetProcessHeap(), 0, header.dwSize);
|
||||
GetMetaFileBitsEx(mfpict->hMF, header.dwSize, data);
|
||||
GlobalUnlock(This->stgmedium.u.hMetaFilePict);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (data)
|
||||
hr = IStream_Write(pres_stream, data, header.dwSize, NULL);
|
||||
|
||||
IStream_Release(pres_stream);
|
||||
return hr;
|
||||
}
|
||||
|
||||
/* helper for copying STGMEDIUM of type bitmap, MF, EMF or HGLOBAL.
|
||||
* does no checking of whether src_stgm has a supported tymed, so this should be
|
||||
* done in the caller */
|
||||
|
@ -1153,11 +1272,13 @@ static HRESULT WINAPI DataCache_Load(
|
|||
DataCacheEntry *cache_entry;
|
||||
FORMATETC fmtetc;
|
||||
|
||||
fmtetc.cfFormat = header.unknown2;
|
||||
fmtetc.cfFormat = header.clipformat;
|
||||
fmtetc.ptd = NULL; /* FIXME */
|
||||
fmtetc.dwAspect = header.dvAspect;
|
||||
fmtetc.lindex = header.unknown5;
|
||||
fmtetc.tymed = header.unknown6;
|
||||
fmtetc.lindex = header.lindex;
|
||||
fmtetc.tymed = header.tymed;
|
||||
|
||||
TRACE("loading entry with formatetc: "); dump_FORMATETC(&fmtetc); TRACE("\n");
|
||||
|
||||
cache_entry = DataCache_GetEntryForFormatEtc(This, &fmtetc);
|
||||
if (!cache_entry)
|
||||
|
@ -1205,6 +1326,8 @@ static HRESULT WINAPI DataCache_Save(
|
|||
DataCache *This = impl_from_IPersistStorage(iface);
|
||||
DataCacheEntry *cache_entry;
|
||||
BOOL dirty = FALSE;
|
||||
HRESULT hr = S_OK;
|
||||
unsigned short stream_number = 0;
|
||||
|
||||
TRACE("(%p, %p, %d)\n", iface, pStg, fSameAsLoad);
|
||||
|
||||
|
@ -1225,14 +1348,32 @@ static HRESULT WINAPI DataCache_Save(
|
|||
return IStorage_CopyTo(This->presentationStorage, 0, NULL, NULL, pStg);
|
||||
}
|
||||
|
||||
/* assign stream numbers to the cache entries */
|
||||
LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
|
||||
{
|
||||
/* FIXME: actually do the save here */
|
||||
cache_entry->dirty = FALSE;
|
||||
if (cache_entry->stream_number != stream_number)
|
||||
{
|
||||
cache_entry->dirty = TRUE; /* needs to be written out again */
|
||||
cache_entry->stream_number = stream_number;
|
||||
}
|
||||
stream_number++;
|
||||
}
|
||||
|
||||
/* write out the cache entries */
|
||||
LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
|
||||
{
|
||||
if (!fSameAsLoad || cache_entry->dirty)
|
||||
{
|
||||
hr = DataCacheEntry_Save(cache_entry, pStg, fSameAsLoad);
|
||||
if (FAILED(hr))
|
||||
break;
|
||||
|
||||
cache_entry->dirty = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
This->dirty = FALSE;
|
||||
return S_OK;
|
||||
return hr;
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
|
@ -1737,6 +1878,7 @@ static HRESULT WINAPI DataCache_Cache(
|
|||
HRESULT hr;
|
||||
|
||||
TRACE("(%p, 0x%x, %p)\n", pformatetc, advf, pdwConnection);
|
||||
TRACE("pformatetc = "); dump_FORMATETC(pformatetc); TRACE("\n");
|
||||
|
||||
*pdwConnection = 0;
|
||||
|
||||
|
|
|
@ -1205,9 +1205,7 @@ static void test_data_cache(void)
|
|||
fmtetc.ptd = NULL;
|
||||
fmtetc.tymed = TYMED_MFPICT;
|
||||
hr = IOleCache_Cache(pOleCache, &fmtetc, 0, &dwConnection);
|
||||
todo_wine {
|
||||
ok(hr == CACHE_S_SAMECACHE, "IOleCache_Cache with already loaded data format type should return CACHE_S_SAMECACHE instead of 0x%x\n", hr);
|
||||
}
|
||||
|
||||
rcBounds.left = 0;
|
||||
rcBounds.top = 0;
|
||||
|
@ -1216,9 +1214,7 @@ static void test_data_cache(void)
|
|||
hdcMem = CreateCompatibleDC(NULL);
|
||||
|
||||
hr = IViewObject_Draw(pViewObject, DVASPECT_ICON, -1, NULL, NULL, NULL, hdcMem, &rcBounds, NULL, draw_continue, 0xdeadbeef);
|
||||
todo_wine {
|
||||
ok_ole_success(hr, "IViewObject_Draw");
|
||||
}
|
||||
|
||||
hr = IViewObject_Draw(pViewObject, DVASPECT_CONTENT, -1, NULL, NULL, NULL, hdcMem, &rcBounds, NULL, draw_continue, 0xdeadbeef);
|
||||
ok(hr == OLE_E_BLANK, "IViewObject_Draw with uncached aspect should have returned OLE_E_BLANK instead of 0x%08x\n", hr);
|
||||
|
@ -1227,21 +1223,21 @@ static void test_data_cache(void)
|
|||
hr = IOleCache2_DiscardCache(pOleCache, DISCARDCACHE_NOSAVE);
|
||||
todo_wine {
|
||||
ok_ole_success(hr, "IOleCache2_DiscardCache");
|
||||
}
|
||||
hr = IViewObject_Draw(pViewObject, DVASPECT_ICON, -1, NULL, NULL, NULL, hdcMem, &rcBounds, NULL, draw_continue, 0xdeadbeef);
|
||||
ok_ole_success(hr, "IViewObject_Draw");
|
||||
}
|
||||
|
||||
/* unload the cached storage object, but don't allow it to be reloaded */
|
||||
hr = IPersistStorage_HandsOffStorage(pPS);
|
||||
ok_ole_success(hr, "IPersistStorage_HandsOffStorage");
|
||||
todo_wine {
|
||||
hr = IViewObject_Draw(pViewObject, DVASPECT_ICON, -1, NULL, NULL, NULL, hdcMem, &rcBounds, NULL, draw_continue, 0xdeadbeef);
|
||||
ok_ole_success(hr, "IViewObject_Draw");
|
||||
todo_wine {
|
||||
hr = IOleCache2_DiscardCache(pOleCache, DISCARDCACHE_NOSAVE);
|
||||
ok_ole_success(hr, "IOleCache2_DiscardCache");
|
||||
}
|
||||
hr = IViewObject_Draw(pViewObject, DVASPECT_ICON, -1, NULL, NULL, NULL, hdcMem, &rcBounds, NULL, draw_continue, 0xdeadbeef);
|
||||
ok(hr == OLE_E_BLANK, "IViewObject_Draw with uncached aspect should have returned OLE_E_BLANK instead of 0x%08x\n", hr);
|
||||
}
|
||||
|
||||
DeleteDC(hdcMem);
|
||||
|
||||
|
|
Loading…
Reference in New Issue