[OLE32_WINETEST] Sync with Wine 3.0. CORE-14225
authorAmine Khaldi <amine.khaldi@reactos.org>
Sat, 20 Jan 2018 11:58:03 +0000 (12:58 +0100)
committerAmine Khaldi <amine.khaldi@reactos.org>
Sat, 20 Jan 2018 11:58:03 +0000 (12:58 +0100)
modules/rostests/winetests/ole32/hglobalstream.c
modules/rostests/winetests/ole32/marshal.c
modules/rostests/winetests/ole32/ole2.c
modules/rostests/winetests/ole32/usrmarshal.c

index 8789623..9974cd3 100644 (file)
@@ -2,7 +2,6 @@
  * Stream on HGLOBAL Tests
  *
  * Copyright 2006 Robert Shearman (for CodeWeavers)
- * Copyright 2016 Dmitry Timoshkov
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -501,240 +500,11 @@ static void test_freed_hglobal(void)
     IStream_Release(pStream);
 }
 
-static void stream_info(IStream *stream, HGLOBAL *hmem, int *size, int *pos)
-{
-    HRESULT hr;
-    STATSTG stat;
-    LARGE_INTEGER offset;
-    ULARGE_INTEGER newpos;
-
-    *hmem = 0;
-    *size = *pos = -1;
-
-    hr = GetHGlobalFromStream(stream, hmem);
-    ok(hr == S_OK, "unexpected %#x\n", hr);
-
-    memset(&stat, 0x55, sizeof(stat));
-    hr = IStream_Stat(stream, &stat, STATFLAG_DEFAULT);
-    ok(hr == S_OK, "unexpected %#x\n", hr);
-    ok(stat.type == STGTY_STREAM, "unexpected %#x\n", stat.type);
-    ok(!stat.pwcsName, "unexpected %p\n", stat.pwcsName);
-    ok(IsEqualIID(&stat.clsid, &GUID_NULL), "unexpected %s\n", wine_dbgstr_guid(&stat.clsid));
-    ok(!stat.cbSize.HighPart, "unexpected %#x\n", stat.cbSize.HighPart);
-    *size = stat.cbSize.LowPart;
-
-    offset.QuadPart = 0;
-    hr = IStream_Seek(stream, offset, STREAM_SEEK_CUR, &newpos);
-    ok(hr == S_OK, "unexpected %#x\n", hr);
-    ok(!newpos.HighPart, "unexpected %#x\n", newpos.HighPart);
-    *pos = newpos.LowPart;
-}
-
-static void test_IStream_Clone(void)
-{
-    static const char hello[] = "Hello World!";
-    char buf[32];
-    HRESULT hr;
-    IStream *stream, *clone;
-    HGLOBAL orig_hmem, hmem, hmem_clone;
-    ULARGE_INTEGER newsize;
-    LARGE_INTEGER offset;
-    int size, pos, ret;
-
-    /* test simple case for Clone */
-    orig_hmem = GlobalAlloc(GMEM_MOVEABLE, 0);
-    ok(orig_hmem != 0, "unexpected %p\n", orig_hmem);
-    hr = CreateStreamOnHGlobal(orig_hmem, TRUE, &stream);
-    ok(hr == S_OK, "unexpected %#x\n", hr);
-
-    hr = GetHGlobalFromStream(stream, NULL);
-    ok(hr == E_INVALIDARG, "unexpected %#x\n", hr);
-
-    hr = GetHGlobalFromStream(NULL, &hmem);
-    ok(hr == E_INVALIDARG, "unexpected %#x\n", hr);
-
-    stream_info(stream, &hmem, &size, &pos);
-    ok(hmem == orig_hmem, "handles should match\n");
-    ok(size == 0,  "unexpected %d\n", size);
-    ok(pos == 0,  "unexpected %d\n", pos);
-
-    hr = IStream_Clone(stream, &clone);
-    ok(hr == S_OK, "unexpected %#x\n", hr);
-
-    hr = IStream_Write(stream, hello, sizeof(hello), NULL);
-    ok(hr == S_OK, "unexpected %#x\n", hr);
-
-    stream_info(stream, &hmem, &size, &pos);
-    ok(hmem != 0, "unexpected %p\n", hmem);
-    ok(size == 13,  "unexpected %d\n", size);
-    ok(pos == 13,  "unexpected %d\n", pos);
-
-    stream_info(clone, &hmem_clone, &size, &pos);
-    ok(hmem_clone == hmem, "handles should match\n");
-    ok(size == 13,  "unexpected %d\n", size);
-    ok(pos == 0,  "unexpected %d\n", pos);
-
-    buf[0] = 0;
-    hr = IStream_Read(clone, buf, sizeof(buf), NULL);
-    ok(hr == S_OK, "unexpected %#x\n", hr);
-    ok(!strcmp(buf, hello), "wrong stream contents\n");
-
-    newsize.QuadPart = 0x8000;
-    hr = IStream_SetSize(stream, newsize);
-    ok(hr == S_OK, "unexpected %#x\n", hr);
-
-    stream_info(stream, &hmem, &size, &pos);
-    ok(hmem != 0,  "unexpected %p\n", hmem);
-    ok(hmem == orig_hmem,  "unexpected %p\n", hmem);
-    ok(size == 0x8000,  "unexpected %#x\n", size);
-    ok(pos == 13,  "unexpected %d\n", pos);
-
-    stream_info(clone, &hmem_clone, &size, &pos);
-    ok(hmem_clone == hmem, "handles should match\n");
-    ok(size == 0x8000,  "unexpected %#x\n", size);
-    ok(pos == 13,  "unexpected %d\n", pos);
-
-    IStream_Release(clone);
-    IStream_Release(stream);
-
-    /* exploit GMEM_FIXED forced move for the same base streams */
-    orig_hmem = GlobalAlloc(GMEM_FIXED, 1);
-    ok(orig_hmem != 0, "unexpected %p\n", orig_hmem);
-    hr = CreateStreamOnHGlobal(orig_hmem, TRUE, &stream);
-    ok(hr == S_OK, "unexpected %#x\n", hr);
-
-    hr = IStream_Clone(stream, &clone);
-    ok(hr == S_OK, "unexpected %#x\n", hr);
-
-    stream_info(stream, &hmem, &size, &pos);
-    ok(hmem != 0,  "unexpected %p\n", hmem);
-    ok(size == 1,  "unexpected %d\n", size);
-    ok(pos == 0,  "unexpected %d\n", pos);
-
-    stream_info(clone, &hmem_clone, &size, &pos);
-    ok(hmem_clone == hmem, "handles should match\n");
-    ok(size == 1,  "unexpected %d\n", size);
-    ok(pos == 0,  "unexpected %d\n", pos);
-
-    newsize.QuadPart = 0x8000;
-    hr = IStream_SetSize(stream, newsize);
-    ok(hr == S_OK, "unexpected %#x\n", hr);
-
-    stream_info(stream, &hmem, &size, &pos);
-    ok(hmem != 0,  "unexpected %p\n", hmem);
-    ok(hmem != orig_hmem,  "unexpected %p\n", hmem);
-    ok(size == 0x8000,  "unexpected %#x\n", size);
-    ok(pos == 0,  "unexpected %d\n", pos);
-
-    stream_info(clone, &hmem_clone, &size, &pos);
-    ok(hmem_clone == hmem, "handles should match\n");
-    ok(size == 0x8000,  "unexpected %#x\n", size);
-    ok(pos == 0,  "unexpected %d\n", pos);
-
-    IStream_Release(stream);
-    IStream_Release(clone);
-
-    /* exploit GMEM_FIXED forced move for different base streams */
-    orig_hmem = GlobalAlloc(GMEM_FIXED, 1);
-    ok(orig_hmem != 0, "unexpected %p\n", orig_hmem);
-    hr = CreateStreamOnHGlobal(orig_hmem, TRUE, &stream);
-    ok(hr == S_OK, "unexpected %#x\n", hr);
-
-    hr = CreateStreamOnHGlobal(orig_hmem, TRUE, &clone);
-    ok(hr == S_OK, "unexpected %#x\n", hr);
-
-    stream_info(stream, &hmem, &size, &pos);
-    ok(hmem != 0,  "unexpected %p\n", hmem);
-    ok(size == 1,  "unexpected %d\n", size);
-    ok(pos == 0,  "unexpected %d\n", pos);
-
-    stream_info(clone, &hmem_clone, &size, &pos);
-    ok(hmem_clone == hmem, "handles should match\n");
-    ok(size == 1,  "unexpected %d\n", size);
-    ok(pos == 0,  "unexpected %d\n", pos);
-
-    newsize.QuadPart = 0x8000;
-    hr = IStream_SetSize(stream, newsize);
-    ok(hr == S_OK, "unexpected %#x\n", hr);
-
-    stream_info(stream, &hmem, &size, &pos);
-    ok(hmem != 0,  "unexpected %p\n", hmem);
-    ok(hmem != orig_hmem,  "unexpected %p\n", hmem);
-    ok(size == 0x8000,  "unexpected %#x\n", size);
-    ok(pos == 0,  "unexpected %d\n", pos);
-
-    stream_info(clone, &hmem_clone, &size, &pos);
-    ok(hmem_clone != hmem, "handles should not match\n");
-    ok(size == 1,  "unexpected %#x\n", size);
-    ok(pos == 0,  "unexpected %d\n", pos);
-
-    IStream_Release(stream);
-    /* releasing clone leads to test termination under windows
-    IStream_Release(clone);
-    */
-
-    /* test Release for a being cloned stream */
-    hr = CreateStreamOnHGlobal(0, TRUE, &stream);
-    ok(hr == S_OK, "unexpected %#x\n", hr);
-
-    hr = IStream_Clone(stream, &clone);
-    ok(hr == S_OK, "unexpected %#x\n", hr);
-
-    stream_info(stream, &hmem, &size, &pos);
-    ok(hmem != 0,  "unexpected %p\n", hmem);
-    ok(size == 0,  "unexpected %d\n", size);
-    ok(pos == 0,  "unexpected %d\n", pos);
-
-    stream_info(clone, &hmem_clone, &size, &pos);
-    ok(hmem_clone == hmem, "handles should match\n");
-    ok(size == 0,  "unexpected %#x\n", size);
-    ok(pos == 0,  "unexpected %d\n", pos);
-
-    ret = IStream_Release(stream);
-    ok(ret == 0, "unexpected %d\n", ret);
-
-    newsize.QuadPart = 0x8000;
-    hr = IStream_SetSize(clone, newsize);
-    ok(hr == S_OK, "unexpected %#x\n", hr);
-
-    stream_info(clone, &hmem_clone, &size, &pos);
-    ok(hmem_clone == hmem, "handles should match\n");
-    ok(size == 0x8000,  "unexpected %#x\n", size);
-    ok(pos == 0,  "unexpected %d\n", pos);
-
-    hr = IStream_Write(clone, hello, sizeof(hello), NULL);
-    ok(hr == S_OK, "unexpected %#x\n", hr);
-
-    stream_info(clone, &hmem_clone, &size, &pos);
-    ok(hmem_clone == hmem, "handles should match\n");
-    ok(size == 0x8000,  "unexpected %#x\n", size);
-    ok(pos == 13,  "unexpected %d\n", pos);
-
-    offset.QuadPart = 0;
-    hr = IStream_Seek(clone, offset, STREAM_SEEK_SET, NULL);
-    ok(hr == S_OK, "unexpected %#x\n", hr);
-
-    buf[0] = 0;
-    hr = IStream_Read(clone, buf, sizeof(buf), NULL);
-    ok(hr == S_OK, "unexpected %#x\n", hr);
-    ok(!strcmp(buf, hello), "wrong stream contents\n");
-
-    stream_info(clone, &hmem_clone, &size, &pos);
-    ok(hmem_clone == hmem, "handles should match\n");
-    ok(size == 0x8000,  "unexpected %#x\n", size);
-    ok(pos == 32,  "unexpected %d\n", pos);
-
-    ret = IStream_Release(clone);
-    ok(ret == 0, "unexpected %d\n", ret);
-}
-
 START_TEST(hglobalstream)
 {
     HRESULT hr;
     IStream *pStream;
 
-    test_IStream_Clone();
-
     hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream);
     ok_ole_success(hr, "CreateStreamOnHGlobal");
 
index 10564cd..2a29e66 100644 (file)
@@ -2955,6 +2955,12 @@ static HRESULT WINAPI local_server_GetClassID(IPersist *iface, CLSID *clsid)
     hr = CoDisconnectObject((IUnknown *)iface, 0);
     ok(hr == S_OK, "got %08x\n", hr);
 
+    /* Initialize and uninitialize the apartment to show that we
+     * remain in the autojoined mta */
+    hr = pCoInitializeEx( NULL, COINIT_MULTITHREADED );
+    ok( hr == S_FALSE, "got %08x\n", hr );
+    CoUninitialize();
+
     return S_OK;
 }
 
@@ -3736,7 +3742,7 @@ START_TEST(marshal)
     argc = winetest_get_mainargs( &argv );
     if (argc > 2 && (!strcmp(argv[2], "-Embedding")))
     {
-        pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+        pCoInitializeEx(NULL, COINIT_MULTITHREADED);
         test_register_local_server();
         CoUninitialize();
 
index 288517c..d6dafc0 100644 (file)
@@ -2,6 +2,7 @@
  * Object Linking and Embedding Tests
  *
  * Copyright 2005 Robert Shearman
+ * Copyright 2017 Dmitry Timoshkov
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -56,9 +57,15 @@ DEFINE_GUID(CLSID_Picture_EnhMetafile,0x319,0,0,0xc0,0,0,0,0,0,0,0x46);
 
 DEFINE_EXPECT(Storage_Stat);
 DEFINE_EXPECT(Storage_OpenStream_CompObj);
+DEFINE_EXPECT(Storage_OpenStream_OlePres);
 DEFINE_EXPECT(Storage_SetClass);
 DEFINE_EXPECT(Storage_CreateStream_CompObj);
+DEFINE_EXPECT(Storage_CreateStream_OlePres);
 DEFINE_EXPECT(Storage_OpenStream_Ole);
+DEFINE_EXPECT(Storage_DestroyElement);
+
+static const CLSID *Storage_SetClass_CLSID;
+static int Storage_DestroyElement_limit;
 
 static IPersistStorage OleObjectPersistStg;
 static IOleCache *cache;
@@ -95,6 +102,7 @@ struct expected_method
 {
     const char *method;
     unsigned int flags;
+    FORMATETC fmt;
 };
 
 static const struct expected_method *expected_method_list;
@@ -107,6 +115,8 @@ static HRESULT g_QIFailsWith;
 
 static UINT cf_test_1, cf_test_2, cf_test_3;
 
+static FORMATETC *g_dataobject_fmts;
+
 /****************************************************************************
  * PresentationDataHeader
  *
@@ -123,7 +133,8 @@ typedef struct PresentationDataHeader
      *  DWORD length;
      *  CHAR format_name[length]; (null-terminated)
      */
-    DWORD unknown3; /* 4, possibly TYMED_ISTREAM */
+    DWORD tdSize; /* This is actually a truncated DVTARGETDEVICE, if tdSize > sizeof(DWORD)
+                     then there are tdSize - sizeof(DWORD) more bytes before dvAspect */
     DVASPECT dvAspect;
     DWORD lindex;
     DWORD advf;
@@ -133,27 +144,43 @@ typedef struct PresentationDataHeader
     DWORD dwSize;
 } PresentationDataHeader;
 
-#define CHECK_EXPECTED_METHOD(method_name) \
-    do { \
-        trace("%s\n", method_name); \
-        ok(expected_method_list->method != NULL, "Extra method %s called\n", method_name); \
-        if (!strcmp(expected_method_list->method, "WINE_EXTRA")) \
-        { \
-            todo_wine ok(0, "Too many method calls.\n"); \
-            break; \
-        } \
-        if (expected_method_list->method) \
-        { \
-            while (expected_method_list->flags & TEST_OPTIONAL && \
-                   strcmp(expected_method_list->method, method_name) != 0) \
-                expected_method_list++; \
-            todo_wine_if (expected_method_list->flags & TEST_TODO) \
-                ok(!strcmp(expected_method_list->method, method_name), \
-                   "Expected %s to be called instead of %s\n", \
-                   expected_method_list->method, method_name); \
-            expected_method_list++; \
-        } \
-    } while(0)
+static inline void check_expected_method_fmt(const char *method_name, const FORMATETC *fmt)
+{
+    trace("%s\n", method_name);
+    ok(expected_method_list->method != NULL, "Extra method %s called\n", method_name);
+    if (!strcmp(expected_method_list->method, "WINE_EXTRA"))
+    {
+        todo_wine ok(0, "Too many method calls.\n");
+        return;
+    }
+    if (expected_method_list->method)
+    {
+        while (expected_method_list->flags & TEST_OPTIONAL &&
+               strcmp(expected_method_list->method, method_name) != 0)
+            expected_method_list++;
+        todo_wine_if (expected_method_list->flags & TEST_TODO)
+        {
+            ok(!strcmp(expected_method_list->method, method_name),
+               "Expected %s to be called instead of %s\n",
+               expected_method_list->method, method_name);
+            if (fmt)
+            {
+                ok(fmt->cfFormat == expected_method_list->fmt.cfFormat, "got cf %04x vs %04x\n",
+                   fmt->cfFormat, expected_method_list->fmt.cfFormat );
+                ok(fmt->dwAspect == expected_method_list->fmt.dwAspect, "got aspect %d vs %d\n",
+                   fmt->dwAspect, expected_method_list->fmt.dwAspect );
+                ok(fmt->lindex == expected_method_list->fmt.lindex, "got lindex %d vs %d\n",
+                   fmt->lindex, expected_method_list->fmt.lindex );
+                ok(fmt->tymed == expected_method_list->fmt.tymed, "got tymed %d vs %d\n",
+                   fmt->tymed, expected_method_list->fmt.tymed );
+            }
+        }
+        expected_method_list++;
+    }
+}
+
+#define CHECK_EXPECTED_METHOD(method_name) check_expected_method_fmt(method_name, NULL)
+#define CHECK_EXPECTED_METHOD_FMT(method_name, fmt) check_expected_method_fmt(method_name, fmt)
 
 #define CHECK_NO_EXTRA_METHODS() \
     do { \
@@ -162,6 +189,64 @@ typedef struct PresentationDataHeader
         ok(!expected_method_list->method, "Method sequence starting from %s not called\n", expected_method_list->method); \
     } while (0)
 
+/* 2 x 1 x 32 bpp dib. PelsPerMeter = 200x400 */
+static const BYTE dib[] =
+{
+    0x28, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+    0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x20, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xc8, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+static void create_dib( STGMEDIUM *med )
+{
+    void *ptr;
+
+    med->tymed = TYMED_HGLOBAL;
+    U(med)->hGlobal = GlobalAlloc( GMEM_MOVEABLE, sizeof(dib) );
+    ptr = GlobalLock( U(med)->hGlobal );
+    memcpy( ptr, dib, sizeof(dib) );
+    GlobalUnlock( U(med)->hGlobal );
+    med->pUnkForRelease = NULL;
+}
+
+static void create_bitmap( STGMEDIUM *med )
+{
+    med->tymed = TYMED_GDI;
+    U(med)->hBitmap = CreateBitmap( 1, 1, 1, 1, NULL );
+    med->pUnkForRelease = NULL;
+}
+
+static void create_emf(STGMEDIUM *med)
+{
+    HDC hdc = CreateEnhMetaFileW(NULL, NULL, NULL, NULL);
+
+    Rectangle(hdc, 0, 0, 150, 300);
+    med->tymed = TYMED_ENHMF;
+    U(med)->hEnhMetaFile = CloseEnhMetaFile(hdc);
+    med->pUnkForRelease = NULL;
+}
+
+static void create_mfpict(STGMEDIUM *med)
+{
+    METAFILEPICT *mf;
+    HDC hdc = CreateMetaFileW(NULL);
+
+    Rectangle(hdc, 0, 0, 100, 200);
+
+    med->tymed = TYMED_MFPICT;
+    U(med)->hMetaFilePict = GlobalAlloc(GMEM_MOVEABLE, sizeof(METAFILEPICT));
+    mf = GlobalLock(U(med)->hMetaFilePict);
+    mf->mm = MM_ANISOTROPIC;
+    mf->xExt = 100;
+    mf->yExt = 200;
+    mf->hMF = CloseMetaFile(hdc);
+    GlobalUnlock(U(med)->hMetaFilePict);
+    med->pUnkForRelease = NULL;
+}
+
 static HRESULT WINAPI OleObject_QueryInterface(IOleObject *iface, REFIID riid, void **ppv)
 {
     CHECK_EXPECTED_METHOD("OleObject_QueryInterface");
@@ -1213,7 +1298,7 @@ static void test_OleLoad(IStorage *pStorage)
                 break;
             }
 
-            header.unknown3 = 4;
+            header.tdSize = sizeof(header.tdSize);
             header.dvAspect = DVASPECT_CONTENT;
             header.lindex = -1;
             header.advf = 1 << i;
@@ -1368,13 +1453,40 @@ static ULONG WINAPI DataObject_Release(
     return 1;
 }
 
-static HRESULT WINAPI DataObject_GetData(
-        IDataObject*     iface,
-        LPFORMATETC      pformatetcIn,
-        STGMEDIUM*       pmedium)
+static inline BOOL fmtetc_equal( const FORMATETC *a, const FORMATETC *b )
 {
-    CHECK_EXPECTED_METHOD("DataObject_GetData");
-    return E_NOTIMPL;
+    /* FIXME ptd */
+    return a->cfFormat == b->cfFormat && a->dwAspect == b->dwAspect &&
+        a->lindex == b->lindex && a->tymed == b->tymed;
+
+}
+
+static HRESULT WINAPI DataObject_GetData( IDataObject *iface, FORMATETC *fmt_in,
+                                          STGMEDIUM *med )
+{
+    FORMATETC *fmt;
+
+    CHECK_EXPECTED_METHOD_FMT("DataObject_GetData", fmt_in);
+
+    for (fmt = g_dataobject_fmts; fmt && fmt->cfFormat != 0; fmt++)
+    {
+        if (fmtetc_equal( fmt_in, fmt ))
+        {
+            switch (fmt->cfFormat)
+            {
+            case CF_DIB:
+                create_dib( med );
+                return S_OK;
+            case CF_BITMAP:
+                create_bitmap( med );
+                return S_OK;
+            default:
+                trace( "unhandled fmt %d\n", fmt->cfFormat );
+            }
+        }
+    }
+
+    return S_FALSE;
 }
 
 static HRESULT WINAPI DataObject_GetDataHere(
@@ -1386,12 +1498,16 @@ static HRESULT WINAPI DataObject_GetDataHere(
     return E_NOTIMPL;
 }
 
-static HRESULT WINAPI DataObject_QueryGetData(
-        IDataObject*     iface,
-        LPFORMATETC      pformatetc)
+static HRESULT WINAPI DataObject_QueryGetData( IDataObject *iface, FORMATETC *fmt_in )
 {
-    CHECK_EXPECTED_METHOD("DataObject_QueryGetData");
-    return S_OK;
+    FORMATETC *fmt;
+
+    CHECK_EXPECTED_METHOD_FMT("DataObject_QueryGetData", fmt_in);
+
+    for (fmt = g_dataobject_fmts; fmt && fmt->cfFormat != 0; fmt++)
+        if (fmtetc_equal( fmt_in, fmt )) return S_OK;
+
+    return S_FALSE;
 }
 
 static HRESULT WINAPI DataObject_GetCanonicalFormatEtc(
@@ -1550,7 +1666,7 @@ static void test_data_cache(void)
     IOleCache2 *pOleCache;
     IOleCache *olecache;
     IStorage *pStorage;
-    IUnknown *unk;
+    IUnknown *unk, *unk2;
     IPersistStorage *pPS;
     IViewObject *pViewObject;
     IOleCacheControl *pOleCacheControl;
@@ -1584,9 +1700,9 @@ static void test_data_cache(void)
         { "draw_continue", 1 },
         { "draw_continue", 1 },
         { "draw_continue", 1 },
-        { "DataObject_GetData", 0 },
-        { "DataObject_GetData", 0 },
-        { "DataObject_GetData", 0 },
+        { "DataObject_GetData", 0, { CF_DIB,          NULL, DVASPECT_THUMBNAIL, -1, TYMED_HGLOBAL} },
+        { "DataObject_GetData", 0, { CF_BITMAP,       NULL, DVASPECT_THUMBNAIL, -1, TYMED_GDI} },
+        { "DataObject_GetData", 0, { CF_METAFILEPICT, NULL, DVASPECT_ICON,      -1, TYMED_MFPICT} },
         { NULL, 0 }
     };
     static const struct expected_method methods_cachethenrun[] =
@@ -1594,7 +1710,6 @@ static void test_data_cache(void)
         { "DataObject_DAdvise", 0 },
         { "DataObject_DAdvise", 0 },
         { "DataObject_DAdvise", 0 },
-        { "DataObject_QueryGetData", 1 }, /* called by win9x and nt4 */
         { "DataObject_DAdvise", 0 },
         { "DataObject_DUnadvise", 0 },
         { "DataObject_DUnadvise", 0 },
@@ -1641,10 +1756,12 @@ static void test_data_cache(void)
     ok(hr == S_OK, "got 0x%08x\n", hr);
     hr = IUnknown_QueryInterface(unk, &IID_IOleCache2, (void**)&pOleCache);
     ok(hr == S_OK, "got 0x%08x\n", hr);
-todo_wine {
+    hr = IUnknown_QueryInterface(unk, &IID_IUnknown, (void**)&unk2);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
     ok(unk == (IUnknown*)olecache, "got %p, expected %p\n", olecache, unk);
     ok(unk == (IUnknown*)pOleCache, "got %p, expected %p\n", pOleCache, unk);
-}
+    ok(unk == unk2, "got %p, expected %p\n", unk2, unk);
+    IUnknown_Release(unk2);
     IOleCache2_Release(pOleCache);
     IOleCache_Release(olecache);
     IUnknown_Release(unk);
@@ -1860,18 +1977,14 @@ todo_wine {
 
     DeleteDC(hdcMem);
 
-    todo_wine {
     hr = IOleCache2_InitCache(pOleCache, &DataObject);
     ok(hr == CACHE_E_NOCACHE_UPDATED, "IOleCache_InitCache should have returned CACHE_E_NOCACHE_UPDATED instead of 0x%08x\n", hr);
-    }
 
     IPersistStorage_Release(pPS);
     IViewObject_Release(pViewObject);
     IOleCache2_Release(pOleCache);
 
-    todo_wine {
     CHECK_NO_EXTRA_METHODS();
-    }
 
     hr = CreateDataCache(NULL, &CLSID_NULL, &IID_IOleCache2, (LPVOID *)&pOleCache);
     ok_ole_success(hr, "CreateDataCache");
@@ -1939,21 +2052,17 @@ todo_wine {
     IStorage_Release(pStorage);
 }
 
-
 static const WCHAR CONTENTS[] = {'C','O','N','T','E','N','T','S',0};
 
 /* 2 x 1 x 32 bpp dib. PelsPerMeter = 200x400 */
-static BYTE dib[] =
+static BYTE file_dib[] =
 {
     0x42, 0x4d, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00,
-
     0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00,
     0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00,
-
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x00,
     0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x00, 0x00,
-
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
     0xff, 0xff, 0xff, 0xff, 0xff, 0xff
 };
@@ -1973,27 +2082,15 @@ static IStorage *create_storage( int num )
     ok( hr == S_OK, "got %08x\n", hr);
     if (num == 1) /* Set biXPelsPerMeter = 0 */
     {
-        dib[0x26] = 0;
-        dib[0x27] = 0;
+        file_dib[0x26] = 0;
+        file_dib[0x27] = 0;
     }
-    hr = IStream_Write( stm, dib, sizeof(dib), &written );
+    hr = IStream_Write( stm, file_dib, sizeof(file_dib), &written );
     ok( hr == S_OK, "got %08x\n", hr);
     IStream_Release( stm );
     return stg;
 }
 
-static HGLOBAL create_dib( void )
-{
-    HGLOBAL h;
-    void *ptr;
-
-    h = GlobalAlloc( GMEM_MOVEABLE, sizeof(dib) - sizeof(BITMAPFILEHEADER) );
-    ptr = GlobalLock( h );
-    memcpy( ptr, dib + sizeof(BITMAPFILEHEADER), sizeof(dib) - sizeof(BITMAPFILEHEADER) );
-    GlobalUnlock( h );
-    return h;
-}
-
 static void test_data_cache_dib_contents_stream(int num)
 {
     HRESULT hr;
@@ -2043,7 +2140,7 @@ static void test_data_cache_dib_contents_stream(int num)
         "got %lu\n", GlobalSize( U(med).hGlobal ) );
     ptr = GlobalLock( U(med).hGlobal );
 
-    expect_info = *(BITMAPINFOHEADER *)(dib + sizeof(BITMAPFILEHEADER));
+    expect_info = *(BITMAPINFOHEADER *)(file_dib + sizeof(BITMAPFILEHEADER));
     if (expect_info.biXPelsPerMeter == 0 || expect_info.biYPelsPerMeter == 0)
     {
         HDC hdc = GetDC( 0 );
@@ -2052,8 +2149,8 @@ static void test_data_cache_dib_contents_stream(int num)
         ReleaseDC( 0, hdc );
     }
     ok( !memcmp( ptr, &expect_info, sizeof(expect_info) ), "mismatch\n" );
-    ok( !memcmp( ptr + sizeof(expect_info), dib + sizeof(BITMAPFILEHEADER) + sizeof(expect_info),
-                 sizeof(dib) - sizeof(BITMAPFILEHEADER) - sizeof(expect_info) ), "mismatch\n" );
+    ok( !memcmp( ptr + sizeof(expect_info), file_dib + sizeof(BITMAPFILEHEADER) + sizeof(expect_info),
+                 sizeof(file_dib) - sizeof(BITMAPFILEHEADER) - sizeof(expect_info) ), "mismatch\n" );
     GlobalUnlock( U(med).hGlobal );
     ReleaseStgMedium( &med );
 
@@ -2103,7 +2200,7 @@ static void check_dib_size( HGLOBAL h, int cx, int cy )
     GlobalUnlock( h );
 }
 
-static void test_data_cache_bitmap(void)
+static void test_data_cache_cache(void)
 {
     HRESULT hr;
     IOleCache2 *cache;
@@ -2118,6 +2215,13 @@ static void test_data_cache_bitmap(void)
         {{ CF_METAFILEPICT, 0, DVASPECT_CONTENT, -1, TYMED_MFPICT },  0, NULL, 0 },
         {{ CF_ENHMETAFILE,  0, DVASPECT_CONTENT, -1, TYMED_ENHMF },   0, NULL, 0 }
     };
+    STATDATA view_caching[] =
+    {
+        {{ 0,               0, DVASPECT_CONTENT,   -1, TYMED_ENHMF },   0, NULL, 0 },
+        {{ 0,               0, DVASPECT_THUMBNAIL, -1, TYMED_HGLOBAL }, 0, NULL, 0 },
+        {{ 0,               0, DVASPECT_DOCPRINT,  -1, TYMED_HGLOBAL }, 0, NULL, 0 },
+        {{ CF_METAFILEPICT, 0, DVASPECT_ICON,      -1, TYMED_MFPICT },  0, NULL, 0 }
+    };
 
     hr = CreateDataCache( NULL, &CLSID_NULL, &IID_IOleCache2, (void **)&cache );
     ok( hr == S_OK, "got %08x\n", hr );
@@ -2194,9 +2298,7 @@ static void test_data_cache_bitmap(void)
     hr = IOleCache2_QueryInterface( cache, &IID_IDataObject, (void **) &data );
     ok( hr == S_OK, "got %08x\n", hr );
 
-    med.tymed = TYMED_GDI;
-    U(med).hBitmap = CreateBitmap( 1, 1, 1, 1, NULL );
-    med.pUnkForRelease = NULL;
+    create_bitmap( &med );
 
     hr = IOleCache2_SetData( cache, &fmt, &med, TRUE );
     ok( hr == S_OK, "got %08x\n", hr );
@@ -2218,8 +2320,7 @@ static void test_data_cache_bitmap(void)
     /* Now set a 2x1 dib */
     fmt.cfFormat = CF_DIB;
     fmt.tymed = TYMED_HGLOBAL;
-    med.tymed = TYMED_HGLOBAL;
-    U(med).hGlobal = create_dib();
+    create_dib( &med );
 
     hr = IOleCache2_SetData( cache, &fmt, &med, TRUE );
     ok( hr == S_OK, "got %08x\n", hr );
@@ -2240,6 +2341,79 @@ static void test_data_cache_bitmap(void)
     check_dib_size( U(med).hGlobal, 2, 1 );
     ReleaseStgMedium( &med );
 
+    /* uncache everything */
+    hr = IOleCache2_Uncache( cache, conn );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    /* view caching */
+    fmt.cfFormat = 0;
+    fmt.tymed = TYMED_ENHMF;
+    hr = IOleCache2_Cache( cache, &fmt, 0, &conn );
+    ok( hr == S_OK, "got %08x\n", hr );
+    view_caching[0].dwConnection = conn;
+
+    fmt.tymed = TYMED_HGLOBAL;
+    hr = IOleCache2_Cache( cache, &fmt, 0, &conn );
+    ok( hr == CACHE_S_SAMECACHE, "got %08x\n", hr );
+
+    fmt.dwAspect = DVASPECT_THUMBNAIL;
+    hr = IOleCache2_Cache( cache, &fmt, 0, &conn );
+    ok( hr == S_OK, "got %08x\n", hr );
+    view_caching[1].dwConnection = conn;
+
+    fmt.dwAspect = DVASPECT_DOCPRINT;
+    hr = IOleCache2_Cache( cache, &fmt, 0, &conn );
+    ok( hr == S_OK, "got %08x\n", hr );
+    view_caching[2].dwConnection = conn;
+
+    /* DVASPECT_ICON view cache gets mapped to CF_METAFILEPICT */
+    fmt.dwAspect = DVASPECT_ICON;
+    hr = IOleCache2_Cache( cache, &fmt, 0, &conn );
+    ok( hr == S_OK, "got %08x\n", hr );
+    view_caching[3].dwConnection = conn;
+
+    check_enum_cache( cache, view_caching, 4 );
+
+    /* uncache everything */
+    hr = IOleCache2_Uncache( cache, view_caching[3].dwConnection );
+    ok( hr == S_OK, "got %08x\n", hr );
+    hr = IOleCache2_Uncache( cache, view_caching[2].dwConnection );
+    ok( hr == S_OK, "got %08x\n", hr );
+    hr = IOleCache2_Uncache( cache, view_caching[1].dwConnection );
+    ok( hr == S_OK, "got %08x\n", hr );
+    hr = IOleCache2_Uncache( cache, view_caching[0].dwConnection );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    /* Only able to set cfFormat == CF_METAFILEPICT (or == 0, see above) for DVASPECT_ICON */
+    fmt.dwAspect = DVASPECT_ICON;
+    fmt.cfFormat = CF_DIB;
+    fmt.tymed = TYMED_HGLOBAL;
+    hr = IOleCache2_Cache( cache, &fmt, 0, &conn );
+    ok( hr == DV_E_FORMATETC, "got %08x\n", hr );
+    fmt.cfFormat = CF_BITMAP;
+    fmt.tymed = TYMED_GDI;
+    hr = IOleCache2_Cache( cache, &fmt, 0, &conn );
+    ok( hr == DV_E_FORMATETC, "got %08x\n", hr );
+    fmt.cfFormat = CF_ENHMETAFILE;
+    fmt.tymed = TYMED_ENHMF;
+    hr = IOleCache2_Cache( cache, &fmt, 0, &conn );
+    ok( hr == DV_E_FORMATETC, "got %08x\n", hr );
+    fmt.cfFormat = CF_METAFILEPICT;
+    fmt.tymed = TYMED_MFPICT;
+    hr = IOleCache2_Cache( cache, &fmt, 0, &conn );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    /* uncache everything */
+    hr = IOleCache2_Uncache( cache, conn );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    /* tymed == 0 */
+    fmt.cfFormat = CF_ENHMETAFILE;
+    fmt.dwAspect = DVASPECT_CONTENT;
+    fmt.tymed = 0;
+    hr = IOleCache2_Cache( cache, &fmt, 0, &conn );
+    ok( hr == DV_E_TYMED, "got %08x\n", hr );
+
     IDataObject_Release( data );
     IOleCache2_Release( cache );
 }
@@ -2404,111 +2578,412 @@ static void test_data_cache_initnew(void)
     IOleCache2_Release( cache );
 }
 
-static void test_default_handler(void)
+static void test_data_cache_updatecache( void )
 {
     HRESULT hr;
-    IOleObject *pObject;
-    IRunnableObject *pRunnableObject;
-    IOleClientSite *pClientSite;
-    IDataObject *pDataObject;
-    SIZEL sizel;
-    DWORD dwStatus;
-    CLSID clsid;
-    LPOLESTR pszUserType;
-    LOGPALETTE palette;
-    DWORD dwAdvConn;
-    IMoniker *pMoniker;
-    FORMATETC fmtetc;
-    IOleInPlaceObject *pInPlaceObj;
-    IEnumOLEVERB *pEnumVerbs;
-    DWORD dwRegister;
-    static const WCHAR wszUnknown[] = {'U','n','k','n','o','w','n',0};
-    static const WCHAR wszHostName[] = {'W','i','n','e',' ','T','e','s','t',' ','P','r','o','g','r','a','m',0};
-    static const WCHAR wszDelim[] = {'!',0};
+    IOleCache2 *cache;
+    FORMATETC fmt;
+    DWORD conn[4];
 
-    static const struct expected_method methods_embeddinghelper[] =
+    static const struct expected_method methods_dib[] =
     {
-        { "OleObject_QueryInterface", 0 },
-        { "OleObject_AddRef", 0 },
-        { "OleObject_QueryInterface", 0 },
-        { "OleObject_QueryInterface", TEST_TODO },
-        { "OleObject_QueryInterface", 0 },
-        { "OleObject_QueryInterface", 0 },
-        { "OleObject_QueryInterface", TEST_OPTIONAL }, /* Win95/98/NT4 */
-        { "OleObject_Release", TEST_TODO },
-        { "WINE_EXTRA", TEST_OPTIONAL },
-        { NULL, 0 }
+        { "DataObject_GetData", 0, { CF_DIB,          NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL } },
+        { "DataObject_GetData", 0, { CF_BITMAP,       NULL, DVASPECT_CONTENT, -1, TYMED_GDI } },
+        { NULL }
     };
 
-    hr = CoCreateInstance(&CLSID_WineTest, NULL, CLSCTX_INPROC_HANDLER, &IID_IOleObject, (void **)&pObject);
-    ok(hr == REGDB_E_CLASSNOTREG, "CoCreateInstance should have failed with REGDB_E_CLASSNOTREG instead of 0x%08x\n", hr);
+    static const struct expected_method methods_dib_emf[] =
+    {
+        { "DataObject_GetData", 0, { CF_DIB,          NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL } },
+        { "DataObject_GetData", 0, { CF_ENHMETAFILE,  NULL, DVASPECT_CONTENT, -1, TYMED_ENHMF } },
+        { "DataObject_GetData", 0, { CF_METAFILEPICT, NULL, DVASPECT_CONTENT, -1, TYMED_MFPICT } },
+        { NULL }
+    };
+    static const struct expected_method methods_dib_wmf[] =
+    {
+        { "DataObject_GetData", 0, { CF_DIB,          NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL } },
+        { "DataObject_GetData", 0, { CF_METAFILEPICT, NULL, DVASPECT_CONTENT, -1, TYMED_MFPICT } },
+        { NULL }
+    };
+    static const struct expected_method methods_viewcache[] =
+    {
+        { "DataObject_QueryGetData", 0, { CF_METAFILEPICT, NULL, DVASPECT_CONTENT, -1, TYMED_MFPICT } },
+        { "DataObject_QueryGetData", 0, { CF_ENHMETAFILE,  NULL, DVASPECT_CONTENT, -1, TYMED_ENHMF } },
+        { "DataObject_QueryGetData", 0, { CF_DIB,          NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL } },
+        { "DataObject_QueryGetData", 0, { CF_BITMAP,       NULL, DVASPECT_CONTENT, -1, TYMED_GDI } },
+        { NULL }
+    };
+    static const struct expected_method methods_viewcache_with_dib[] =
+    {
+        { "DataObject_QueryGetData", 0, { CF_METAFILEPICT, NULL, DVASPECT_CONTENT, -1, TYMED_MFPICT } },
+        { "DataObject_QueryGetData", 0, { CF_ENHMETAFILE,  NULL, DVASPECT_CONTENT, -1, TYMED_ENHMF } },
+        { "DataObject_QueryGetData", 0, { CF_DIB,          NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL } },
+        { "DataObject_GetData",      0, { CF_DIB,          NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL } },
+        { NULL }
+    };
+    static const struct expected_method methods_flags_all[] =
+    {
+        { "DataObject_GetData", 0, { CF_DIB,          NULL, DVASPECT_CONTENT,   -1, TYMED_HGLOBAL } },
+        { "DataObject_GetData", 0, { CF_ENHMETAFILE,  NULL, DVASPECT_CONTENT,   -1, TYMED_ENHMF } },
+        { "DataObject_GetData", 0, { CF_METAFILEPICT, NULL, DVASPECT_CONTENT,   -1, TYMED_MFPICT } },
+        { "DataObject_GetData", 0, { CF_METAFILEPICT, NULL, DVASPECT_CONTENT,   -1, TYMED_MFPICT } },
+        { NULL }
+    };
+    static const struct expected_method methods_flags_ifblank_1[] =
+    {
+        { "DataObject_GetData", 0, { CF_METAFILEPICT, NULL, DVASPECT_CONTENT,   -1, TYMED_MFPICT } },
+        { NULL }
+    };
+    static const struct expected_method methods_flags_ifblank_2[] =
+    {
+        { "DataObject_GetData", 0, { CF_DIB,          NULL, DVASPECT_CONTENT,   -1, TYMED_HGLOBAL } },
+        { "DataObject_GetData", 0, { CF_METAFILEPICT, NULL, DVASPECT_CONTENT,   -1, TYMED_MFPICT } },
+        { NULL }
+    };
+    static const struct expected_method methods_flags_normal[] =
+    {
+        { "DataObject_GetData", 0, { CF_DIB,          NULL, DVASPECT_CONTENT,   -1, TYMED_HGLOBAL } },
+        { NULL }
+    };
+    static const struct expected_method methods_initcache[] =
+    {
+        { "DataObject_GetData", 0, { CF_DIB,          NULL, DVASPECT_CONTENT,   -1, TYMED_HGLOBAL } },
+        { "DataObject_GetData", 0, { CF_METAFILEPICT, NULL, DVASPECT_CONTENT,   -1, TYMED_MFPICT } },
+        { NULL }
+    };
+    static const struct expected_method methods_empty[] =
+    {
+        { NULL }
+    };
 
-    hr = OleCreateDefaultHandler(&CLSID_WineTest, NULL, &IID_IOleObject, (void **)&pObject);
-    ok_ole_success(hr, "OleCreateDefaultHandler");
+    static STATDATA view_cache[] =
+    {
+        {{ 0,          0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }, 0, NULL, 0 }
+    };
+    static STATDATA view_cache_after_dib[] =
+    {
+        {{ CF_DIB,          0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }, 0, NULL, 0 },
+        {{ CF_BITMAP,       0, DVASPECT_CONTENT, -1, TYMED_GDI },     0, NULL, 0 }
+    };
 
-    hr = IOleObject_QueryInterface(pObject, &IID_IOleInPlaceObject, (void **)&pInPlaceObj);
-    ok(hr == E_NOINTERFACE, "IOleObject_QueryInterface(&IID_IOleInPlaceObject) should return E_NOINTERFACE instead of 0x%08x\n", hr);
+    static FORMATETC dib_fmt[] =
+    {
+        { CF_DIB,       NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL },
+        { 0 }
+    };
 
-    hr = IOleObject_Advise(pObject, &AdviseSink, &dwAdvConn);
-    ok_ole_success(hr, "IOleObject_Advise");
+    hr = CreateDataCache( NULL, &CLSID_WineTestOld, &IID_IOleCache2, (void **)&cache );
+    ok( hr == S_OK, "got %08x\n", hr );
 
-    hr = IOleObject_Close(pObject, OLECLOSE_NOSAVE);
-    ok_ole_success(hr, "IOleObject_Close");
+    /* No cache slots */
+    g_dataobject_fmts = NULL;
+    expected_method_list = NULL;
 
-    /* FIXME: test IOleObject_EnumAdvise */
+    hr = IOleCache2_UpdateCache( cache, &DataObject, UPDFCACHE_ALL, NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
 
-    hr = IOleObject_EnumVerbs(pObject, &pEnumVerbs);
-    ok(hr == REGDB_E_CLASSNOTREG, "IOleObject_EnumVerbs should have returned REGDB_E_CLASSNOTREG instead of 0x%08x\n", hr);
+    /* A dib cache slot */
+    fmt.cfFormat = CF_DIB;
+    fmt.ptd = NULL;
+    fmt.dwAspect = DVASPECT_CONTENT;
+    fmt.lindex = -1;
+    fmt.tymed = TYMED_HGLOBAL;
 
-    hr = IOleObject_GetClientSite(pObject, &pClientSite);
-    ok_ole_success(hr, "IOleObject_GetClientSite");
+    hr = IOleCache2_Cache( cache, &fmt, 0, &conn[0] );
+    ok( hr == S_OK, "got %08x\n", hr );
 
-    hr = IOleObject_SetClientSite(pObject, pClientSite);
-    ok_ole_success(hr, "IOleObject_SetClientSite");
+    expected_method_list = methods_dib;
 
-    hr = IOleObject_GetClipboardData(pObject, 0, &pDataObject);
-    ok(hr == OLE_E_NOTRUNNING,
-       "IOleObject_GetClipboardData should have returned OLE_E_NOTRUNNING instead of 0x%08x\n",
-       hr);
+    hr = IOleCache2_UpdateCache( cache, &DataObject, UPDFCACHE_ALL, NULL );
+    ok( hr == CACHE_E_NOCACHE_UPDATED, "got %08x\n", hr );
 
-    hr = IOleObject_GetExtent(pObject, DVASPECT_CONTENT, &sizel);
-    ok(hr == OLE_E_BLANK, "IOleObject_GetExtent should have returned OLE_E_BLANK instead of 0x%08x\n",
-       hr);
+    CHECK_NO_EXTRA_METHODS();
 
-    hr = IOleObject_GetMiscStatus(pObject, DVASPECT_CONTENT, &dwStatus);
-    ok(hr == REGDB_E_CLASSNOTREG, "IOleObject_GetMiscStatus should have returned REGDB_E_CLASSNOTREG instead of 0x%08x\n", hr);
+    /* Now with a dib available */
+    g_dataobject_fmts = dib_fmt;
+    expected_method_list = methods_dib;
 
-    hr = IOleObject_GetUserClassID(pObject, &clsid);
-    ok_ole_success(hr, "IOleObject_GetUserClassID");
-    ok(IsEqualCLSID(&clsid, &CLSID_WineTest), "clsid != CLSID_WineTest\n");
+    hr = IOleCache2_UpdateCache( cache, &DataObject, UPDFCACHE_ALL, NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
 
-    hr = IOleObject_GetUserType(pObject, USERCLASSTYPE_FULL, &pszUserType);
-    todo_wine {
-    ok_ole_success(hr, "IOleObject_GetUserType");
-    ok(!lstrcmpW(pszUserType, wszUnknown), "Retrieved user type was wrong\n");
-    }
+    /* Add an EMF cache slot */
+    fmt.cfFormat = CF_ENHMETAFILE;
+    fmt.ptd = NULL;
+    fmt.dwAspect = DVASPECT_CONTENT;
+    fmt.lindex = -1;
+    fmt.tymed = TYMED_ENHMF;
 
-    hr = IOleObject_InitFromData(pObject, NULL, TRUE, 0);
-    ok(hr == OLE_E_NOTRUNNING, "IOleObject_InitFromData should have returned OLE_E_NOTRUNNING instead of 0x%08x\n", hr);
+    hr = IOleCache2_Cache( cache, &fmt, 0, &conn[1] );
+    ok( hr == S_OK, "got %08x\n", hr );
 
-    hr = IOleObject_IsUpToDate(pObject);
-    ok(hr == OLE_E_NOTRUNNING, "IOleObject_IsUpToDate should have returned OLE_E_NOTRUNNING instead of 0x%08x\n", hr);
+    g_dataobject_fmts = dib_fmt;
+    expected_method_list = methods_dib_emf;
 
-    palette.palNumEntries = 1;
-    palette.palVersion = 2;
-    memset(&palette.palPalEntry[0], 0, sizeof(palette.palPalEntry[0]));
-    hr = IOleObject_SetColorScheme(pObject, &palette);
-    ok(hr == OLE_E_NOTRUNNING, "IOleObject_SetColorScheme should have returned OLE_E_NOTRUNNING instead of 0x%08x\n", hr);
+    /* Two slots to fill, only the dib will succeed */
+    hr = IOleCache2_UpdateCache( cache, &DataObject, UPDFCACHE_ALL, NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
 
-    sizel.cx = sizel.cy = 0;
-    hr = IOleObject_SetExtent(pObject, DVASPECT_CONTENT, &sizel);
-    ok(hr == OLE_E_NOTRUNNING, "IOleObject_SetExtent should have returned OLE_E_NOTRUNNING instead of 0x%08x\n", hr);
+    CHECK_NO_EXTRA_METHODS();
 
-    hr = IOleObject_SetHostNames(pObject, wszHostName, NULL);
-    ok_ole_success(hr, "IOleObject_SetHostNames");
+    /* Replace the emf slot with a wmf */
+    hr = IOleCache2_Uncache( cache, conn[1] );
+    ok( hr == S_OK, "got %08x\n", hr );
 
-    hr = CreateItemMoniker(wszDelim, wszHostName, &pMoniker);
-    ok_ole_success(hr, "CreateItemMoniker");
+    fmt.cfFormat = CF_METAFILEPICT;
+    fmt.ptd = NULL;
+    fmt.dwAspect = DVASPECT_CONTENT;
+    fmt.lindex = -1;
+    fmt.tymed = TYMED_MFPICT;
+
+    hr = IOleCache2_Cache( cache, &fmt, 0, &conn[1] );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    g_dataobject_fmts = dib_fmt;
+    expected_method_list = methods_dib_wmf;
+
+    hr = IOleCache2_UpdateCache( cache, &DataObject, UPDFCACHE_ALL, NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    hr = IOleCache2_Uncache( cache, conn[1] );
+    ok( hr == S_OK, "got %08x\n", hr );
+    hr = IOleCache2_Uncache( cache, conn[0] );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    /* View caching */
+    fmt.cfFormat = 0;
+    fmt.ptd = NULL;
+    fmt.dwAspect = DVASPECT_CONTENT;
+    fmt.lindex = -1;
+    fmt.tymed = TYMED_HGLOBAL;
+
+    hr = IOleCache2_Cache( cache, &fmt, 0, &conn[0] );
+    ok( hr == S_OK, "got %08x\n", hr );
+    view_cache[0].dwConnection = conn[0];
+
+    g_dataobject_fmts = NULL;
+    expected_method_list = methods_viewcache;
+
+    hr = IOleCache2_UpdateCache( cache, &DataObject, UPDFCACHE_ALL, NULL );
+    ok( hr == CACHE_E_NOCACHE_UPDATED, "got %08x\n", hr );
+
+    CHECK_NO_EXTRA_METHODS();
+    check_enum_cache( cache, view_cache, 1 );
+
+    g_dataobject_fmts = dib_fmt;
+    expected_method_list = methods_viewcache_with_dib;
+
+    hr = IOleCache2_UpdateCache( cache, &DataObject, UPDFCACHE_ALL, NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    CHECK_NO_EXTRA_METHODS();
+    view_cache_after_dib[0].dwConnection = view_cache_after_dib[1].dwConnection = view_cache[0].dwConnection;
+    check_enum_cache( cache, view_cache_after_dib, 2 );
+
+    hr = IOleCache2_Uncache( cache, conn[0] );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    /* Try some different flags */
+
+    fmt.cfFormat = CF_DIB;
+    fmt.ptd = NULL;
+    fmt.dwAspect = DVASPECT_CONTENT;
+    fmt.lindex = -1;
+    fmt.tymed = TYMED_HGLOBAL;
+
+    hr = IOleCache2_Cache( cache, &fmt, 0, &conn[0] );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    fmt.cfFormat = CF_ENHMETAFILE;
+    fmt.ptd = NULL;
+    fmt.dwAspect = DVASPECT_CONTENT;
+    fmt.lindex = -1;
+    fmt.tymed = TYMED_ENHMF;
+
+    hr = IOleCache2_Cache( cache, &fmt, ADVF_NODATA, &conn[1] );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    fmt.cfFormat = CF_METAFILEPICT;
+    fmt.ptd = NULL;
+    fmt.dwAspect = DVASPECT_CONTENT;
+    fmt.lindex = -1;
+    fmt.tymed = TYMED_MFPICT;
+
+    hr = IOleCache2_Cache( cache, &fmt, ADVFCACHE_ONSAVE, &conn[2] );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    g_dataobject_fmts = dib_fmt;
+    expected_method_list = methods_flags_all;
+
+    hr = IOleCache2_UpdateCache( cache, &DataObject, UPDFCACHE_ALL, NULL );
+
+    CHECK_NO_EXTRA_METHODS();
+
+    expected_method_list = methods_flags_all;
+
+    hr = IOleCache2_UpdateCache( cache, &DataObject, UPDFCACHE_ALL, NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    CHECK_NO_EXTRA_METHODS();
+
+    expected_method_list = methods_flags_ifblank_1;
+
+    hr = IOleCache2_UpdateCache( cache, &DataObject, UPDFCACHE_IFBLANK , NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    CHECK_NO_EXTRA_METHODS();
+
+    hr = IOleCache2_DiscardCache( cache, DISCARDCACHE_NOSAVE );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    expected_method_list = methods_flags_ifblank_2;
+
+    hr = IOleCache2_UpdateCache( cache, &DataObject, UPDFCACHE_IFBLANK , NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    CHECK_NO_EXTRA_METHODS();
+
+    hr = IOleCache2_DiscardCache( cache, DISCARDCACHE_NOSAVE );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    expected_method_list = methods_flags_all;
+
+    hr = IOleCache2_UpdateCache( cache, &DataObject, UPDFCACHE_IFBLANK | UPDFCACHE_NODATACACHE, NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    CHECK_NO_EXTRA_METHODS();
+
+    expected_method_list = methods_empty;
+
+    hr = IOleCache2_UpdateCache( cache, &DataObject, UPDFCACHE_ONLYIFBLANK | UPDFCACHE_NORMALCACHE, NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    CHECK_NO_EXTRA_METHODS();
+
+    hr = IOleCache2_DiscardCache( cache, DISCARDCACHE_NOSAVE );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    expected_method_list = methods_flags_normal;
+
+    hr = IOleCache2_UpdateCache( cache, &DataObject, UPDFCACHE_ONLYIFBLANK | UPDFCACHE_NORMALCACHE, NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    CHECK_NO_EXTRA_METHODS();
+
+    expected_method_list = methods_initcache;
+
+    hr = IOleCache2_InitCache( cache, &DataObject );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    CHECK_NO_EXTRA_METHODS();
+
+    IOleCache2_Release( cache );
+}
+
+static void test_default_handler(void)
+{
+    HRESULT hr;
+    IOleObject *pObject;
+    IRunnableObject *pRunnableObject;
+    IOleClientSite *pClientSite;
+    IDataObject *pDataObject;
+    SIZEL sizel;
+    DWORD dwStatus;
+    CLSID clsid;
+    LPOLESTR pszUserType;
+    LOGPALETTE palette;
+    DWORD dwAdvConn;
+    IMoniker *pMoniker;
+    FORMATETC fmtetc;
+    IOleInPlaceObject *pInPlaceObj;
+    IEnumOLEVERB *pEnumVerbs;
+    DWORD dwRegister;
+    static const WCHAR wszUnknown[] = {'U','n','k','n','o','w','n',0};
+    static const WCHAR wszHostName[] = {'W','i','n','e',' ','T','e','s','t',' ','P','r','o','g','r','a','m',0};
+    static const WCHAR wszDelim[] = {'!',0};
+
+    static const struct expected_method methods_embeddinghelper[] =
+    {
+        { "OleObject_QueryInterface", 0 },
+        { "OleObject_AddRef", 0 },
+        { "OleObject_QueryInterface", 0 },
+        { "OleObject_QueryInterface", TEST_TODO },
+        { "OleObject_QueryInterface", 0 },
+        { "OleObject_QueryInterface", 0 },
+        { "OleObject_QueryInterface", TEST_OPTIONAL }, /* Win95/98/NT4 */
+        { "OleObject_Release", TEST_TODO },
+        { "WINE_EXTRA", TEST_OPTIONAL },
+        { NULL, 0 }
+    };
+
+    hr = CoCreateInstance(&CLSID_WineTest, NULL, CLSCTX_INPROC_HANDLER, &IID_IOleObject, (void **)&pObject);
+    ok(hr == REGDB_E_CLASSNOTREG, "CoCreateInstance should have failed with REGDB_E_CLASSNOTREG instead of 0x%08x\n", hr);
+
+    hr = OleCreateDefaultHandler(&CLSID_WineTest, NULL, &IID_IOleObject, (void **)&pObject);
+    ok_ole_success(hr, "OleCreateDefaultHandler");
+
+    hr = IOleObject_QueryInterface(pObject, &IID_IOleInPlaceObject, (void **)&pInPlaceObj);
+    ok(hr == E_NOINTERFACE, "IOleObject_QueryInterface(&IID_IOleInPlaceObject) should return E_NOINTERFACE instead of 0x%08x\n", hr);
+
+    hr = IOleObject_Advise(pObject, &AdviseSink, &dwAdvConn);
+    ok_ole_success(hr, "IOleObject_Advise");
+
+    hr = IOleObject_Close(pObject, OLECLOSE_NOSAVE);
+    ok_ole_success(hr, "IOleObject_Close");
+
+    /* FIXME: test IOleObject_EnumAdvise */
+
+    hr = IOleObject_EnumVerbs(pObject, &pEnumVerbs);
+    ok(hr == REGDB_E_CLASSNOTREG, "IOleObject_EnumVerbs should have returned REGDB_E_CLASSNOTREG instead of 0x%08x\n", hr);
+
+    hr = IOleObject_GetClientSite(pObject, &pClientSite);
+    ok_ole_success(hr, "IOleObject_GetClientSite");
+
+    hr = IOleObject_SetClientSite(pObject, pClientSite);
+    ok_ole_success(hr, "IOleObject_SetClientSite");
+
+    hr = IOleObject_GetClipboardData(pObject, 0, &pDataObject);
+    ok(hr == OLE_E_NOTRUNNING,
+       "IOleObject_GetClipboardData should have returned OLE_E_NOTRUNNING instead of 0x%08x\n",
+       hr);
+
+    hr = IOleObject_GetExtent(pObject, DVASPECT_CONTENT, &sizel);
+    ok(hr == OLE_E_BLANK, "IOleObject_GetExtent should have returned OLE_E_BLANK instead of 0x%08x\n",
+       hr);
+
+    hr = IOleObject_GetMiscStatus(pObject, DVASPECT_CONTENT, &dwStatus);
+    ok(hr == REGDB_E_CLASSNOTREG, "IOleObject_GetMiscStatus should have returned REGDB_E_CLASSNOTREG instead of 0x%08x\n", hr);
+
+    hr = IOleObject_GetUserClassID(pObject, &clsid);
+    ok_ole_success(hr, "IOleObject_GetUserClassID");
+    ok(IsEqualCLSID(&clsid, &CLSID_WineTest), "clsid != CLSID_WineTest\n");
+
+    hr = IOleObject_GetUserType(pObject, USERCLASSTYPE_FULL, &pszUserType);
+    todo_wine {
+    ok_ole_success(hr, "IOleObject_GetUserType");
+    ok(!lstrcmpW(pszUserType, wszUnknown), "Retrieved user type was wrong\n");
+    }
+
+    hr = IOleObject_InitFromData(pObject, NULL, TRUE, 0);
+    ok(hr == OLE_E_NOTRUNNING, "IOleObject_InitFromData should have returned OLE_E_NOTRUNNING instead of 0x%08x\n", hr);
+
+    hr = IOleObject_IsUpToDate(pObject);
+    ok(hr == OLE_E_NOTRUNNING, "IOleObject_IsUpToDate should have returned OLE_E_NOTRUNNING instead of 0x%08x\n", hr);
+
+    palette.palNumEntries = 1;
+    palette.palVersion = 2;
+    memset(&palette.palPalEntry[0], 0, sizeof(palette.palPalEntry[0]));
+    hr = IOleObject_SetColorScheme(pObject, &palette);
+    ok(hr == OLE_E_NOTRUNNING, "IOleObject_SetColorScheme should have returned OLE_E_NOTRUNNING instead of 0x%08x\n", hr);
+
+    sizel.cx = sizel.cy = 0;
+    hr = IOleObject_SetExtent(pObject, DVASPECT_CONTENT, &sizel);
+    ok(hr == OLE_E_NOTRUNNING, "IOleObject_SetExtent should have returned OLE_E_NOTRUNNING instead of 0x%08x\n", hr);
+
+    hr = IOleObject_SetHostNames(pObject, wszHostName, NULL);
+    ok_ole_success(hr, "IOleObject_SetHostNames");
+
+    hr = CreateItemMoniker(wszDelim, wszHostName, &pMoniker);
+    ok_ole_success(hr, "CreateItemMoniker");
     hr = IOleObject_SetMoniker(pObject, OLEWHICHMK_CONTAINER, pMoniker);
     ok_ole_success(hr, "IOleObject_SetMoniker");
     IMoniker_Release(pMoniker);
@@ -2764,9 +3239,12 @@ static void test_OleDraw(void)
     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
 }
 
+static const WCHAR olepres0W[] = {2,'O','l','e','P','r','e','s','0','0','0',0};
 static const WCHAR comp_objW[] = {1,'C','o','m','p','O','b','j',0};
 static IStream *comp_obj_stream;
 static IStream *ole_stream;
+static IStream *olepres_stream;
+static IStream *contents_stream;
 
 static HRESULT WINAPI Storage_QueryInterface(IStorage *iface, REFIID riid, void **ppvObject)
 {
@@ -2792,18 +3270,40 @@ static HRESULT WINAPI Storage_CreateStream(IStorage *iface, LPCOLESTR pwcsName,
     LARGE_INTEGER pos = {{0}};
     HRESULT hr;
 
-    CHECK_EXPECT(Storage_CreateStream_CompObj);
-    ok(!lstrcmpW(pwcsName, comp_objW), "pwcsName = %s\n", wine_dbgstr_w(pwcsName));
-    todo_wine ok(grfMode == (STGM_CREATE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE), "grfMode = %x\n", grfMode);
+    if (!lstrcmpW(pwcsName, comp_objW))
+    {
+        CHECK_EXPECT(Storage_CreateStream_CompObj);
+        *ppstm = comp_obj_stream;
+
+        todo_wine ok(grfMode == (STGM_CREATE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE), "grfMode = %x\n", grfMode);
+    }
+    else if (!lstrcmpW(pwcsName, olepres0W))
+    {
+        CHECK_EXPECT(Storage_CreateStream_OlePres);
+        *ppstm = olepres_stream;
+
+        todo_wine ok(grfMode == (STGM_SHARE_EXCLUSIVE|STGM_READWRITE), "grfMode = %x\n", grfMode);
+    }
+    else
+    {
+todo_wine
+        ok(0, "unexpected stream name %s\n", wine_dbgstr_w(pwcsName));
+#if 0   /* FIXME: return NULL once Wine is fixed */
+        *ppstm = NULL;
+        return E_NOTIMPL;
+#else
+        *ppstm = contents_stream;
+#endif
+    }
+
     ok(!reserved1, "reserved1 = %x\n", reserved1);
     ok(!reserved2, "reserved2 = %x\n", reserved2);
     ok(!!ppstm, "ppstm = NULL\n");
 
-    *ppstm = comp_obj_stream;
-    IStream_AddRef(comp_obj_stream);
-    hr = IStream_Seek(comp_obj_stream, pos, STREAM_SEEK_SET, NULL);
+    IStream_AddRef(*ppstm);
+    hr = IStream_Seek(*ppstm, pos, STREAM_SEEK_SET, NULL);
     ok(hr == S_OK, "IStream_Seek returned %x\n", hr);
-    hr = IStream_SetSize(comp_obj_stream, size);
+    hr = IStream_SetSize(*ppstm, size);
     ok(hr == S_OK, "IStream_SetSize returned %x\n", hr);
     return S_OK;
 }
@@ -2830,6 +3330,15 @@ static HRESULT WINAPI Storage_OpenStream(IStorage *iface, LPCOLESTR pwcsName, vo
         return S_OK;
     }else if(!lstrcmpW(pwcsName, ole1W)) {
         CHECK_EXPECT(Storage_OpenStream_Ole);
+
+        if (!ole_stream)
+        {
+            ok(grfMode == (STGM_SHARE_EXCLUSIVE|STGM_READ), "grfMode = %x\n", grfMode);
+
+            *ppstm = NULL;
+            return STG_E_FILENOTFOUND;
+        }
+
         ok(grfMode == (STGM_SHARE_EXCLUSIVE|STGM_READWRITE), "grfMode = %x\n", grfMode);
 
         *ppstm = ole_stream;
@@ -2837,6 +3346,16 @@ static HRESULT WINAPI Storage_OpenStream(IStorage *iface, LPCOLESTR pwcsName, vo
         hr = IStream_Seek(ole_stream, pos, STREAM_SEEK_SET, NULL);
         ok(hr == S_OK, "IStream_Seek returned %x\n", hr);
         return S_OK;
+
+    }else if(!lstrcmpW(pwcsName, olepres0W)) {
+        CHECK_EXPECT(Storage_OpenStream_OlePres);
+        ok(grfMode == (STGM_SHARE_EXCLUSIVE|STGM_READWRITE), "grfMode = %x\n", grfMode);
+
+        *ppstm = olepres_stream;
+        IStream_AddRef(olepres_stream);
+        hr = IStream_Seek(olepres_stream, pos, STREAM_SEEK_SET, NULL);
+        ok(hr == S_OK, "IStream_Seek returned %x\n", hr);
+        return S_OK;
     }
 
     ok(0, "unexpected call to OpenStream: %s\n", wine_dbgstr_w(pwcsName));
@@ -2887,8 +3406,20 @@ static HRESULT WINAPI Storage_EnumElements(IStorage *iface, DWORD reserved1, voi
 
 static HRESULT WINAPI Storage_DestroyElement(IStorage *iface, LPCOLESTR pwcsName)
 {
-    ok(0, "unexpected call to DestroyElement\n");
-    return E_NOTIMPL;
+    char name[32];
+    int stream_n, cmp;
+
+    CHECK_EXPECT2(Storage_DestroyElement);
+    cmp = CompareStringW(LOCALE_NEUTRAL, 0, pwcsName, 8, olepres0W, 8);
+    ok(cmp == CSTR_EQUAL,
+       "unexpected call to DestroyElement(%s)\n", wine_dbgstr_w(pwcsName));
+
+    WideCharToMultiByte(CP_ACP, 0, pwcsName, -1, name, sizeof(name), NULL, NULL);
+    stream_n = atol(name + 8);
+    if (stream_n <= Storage_DestroyElement_limit)
+        return S_OK;
+
+    return STG_E_FILENOTFOUND;
 }
 
 static HRESULT WINAPI Storage_RenameElement(IStorage *iface, LPCOLESTR pwcsOldName, LPCOLESTR pwcsNewName)
@@ -2906,7 +3437,8 @@ static HRESULT WINAPI Storage_SetElementTimes(IStorage *iface, LPCOLESTR pwcsNam
 static HRESULT WINAPI Storage_SetClass(IStorage *iface, REFCLSID clsid)
 {
     CHECK_EXPECT(Storage_SetClass);
-    ok(IsEqualIID(clsid, &CLSID_WineTest), "clsid = %s\n", wine_dbgstr_guid(clsid));
+    ok(IsEqualIID(clsid, Storage_SetClass_CLSID), "expected %s, got %s\n",
+       wine_dbgstr_guid(Storage_SetClass_CLSID), wine_dbgstr_guid(clsid));
     return S_OK;
 }
 
@@ -3121,6 +3653,787 @@ static void test_OleDoAutoConvert(void)
     RegCloseKey(root);
 }
 
+/* 1x1 pixel bmp */
+static const unsigned char bmpimage[] =
+{
+    0x42,0x4d,0x42,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x3e,0x00,0x00,0x00,0x28,0x00,
+    0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,
+    0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x00,
+    0x00,0x00,0x04,0x00,0x00,0x00,0x12,0x0b,
+    0x00,0x00,0x12,0x0b,0x00,0x00,0x02,0x00,
+    0x00,0x00,0x02,0x00,0x00,0x00,0xff,0xff,
+    0xff,0x00,0xff,0xff,0xff,0x00,0x00,0x00,
+    0x00,0x00
+};
+
+static const unsigned char mf_blank_bits[] =
+{
+    0x01,0x00,0x09,0x00,0x00,0x03,0x0c,0x00,
+    0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,
+    0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00
+};
+
+static void test_data_cache_save(void)
+{
+    static const WCHAR contentsW[] = { 'C','o','n','t','e','n','t','s',0 };
+    HRESULT hr;
+    ILockBytes *ilb;
+    IStorage *doc;
+    IStream *stm;
+    IOleCache2 *cache;
+    IPersistStorage *stg;
+    DWORD clipformat[2];
+    PresentationDataHeader hdr;
+
+    hr = CreateILockBytesOnHGlobal(0, TRUE, &ilb);
+    ok(hr == S_OK, "unexpected %#x\n", hr);
+    hr = StgCreateDocfileOnILockBytes(ilb, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0,  &doc);
+    ok(hr == S_OK, "unexpected %#x\n", hr);
+
+    ILockBytes_Release(ilb);
+
+    hr = IStorage_SetClass(doc, &CLSID_WineTest);
+    ok(hr == S_OK, "unexpected %#x\n", hr);
+
+    hr = IStorage_CreateStream(doc, contentsW, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
+    ok(hr == S_OK, "unexpected %#x\n", hr);
+    hr = IStream_Write(stm, bmpimage, sizeof(bmpimage), NULL);
+    ok(hr == S_OK, "unexpected %#x\n", hr);
+    IStream_Release(stm);
+
+    hr = IStorage_CreateStream(doc, olepres0W, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
+    ok(hr == S_OK, "unexpected %#x\n", hr);
+
+    clipformat[0] = -1;
+    clipformat[1] = CF_METAFILEPICT;
+    hr = IStream_Write(stm, clipformat, sizeof(clipformat), NULL);
+    ok(hr == S_OK, "unexpected %#x\n", hr);
+
+    hdr.tdSize = sizeof(hdr.tdSize);
+    hdr.dvAspect = DVASPECT_CONTENT;
+    hdr.lindex = -1;
+    hdr.advf = ADVF_PRIMEFIRST;
+    hdr.unknown7 = 0;
+    hdr.dwObjectExtentX = 0;
+    hdr.dwObjectExtentY = 0;
+    hdr.dwSize = sizeof(mf_blank_bits);
+    hr = IStream_Write(stm, &hdr, sizeof(hdr), NULL);
+    ok(hr == S_OK, "unexpected %#x\n", hr);
+    hr = IStream_Write(stm, mf_blank_bits, sizeof(mf_blank_bits), NULL);
+    ok(hr == S_OK, "unexpected %#x\n", hr);
+
+    IStream_Release(stm);
+
+    hr = CreateDataCache(NULL, &CLSID_NULL, &IID_IUnknown, (void **)&cache);
+    ok(hr == S_OK, "unexpected %#x\n", hr);
+    hr = IOleCache2_QueryInterface(cache, &IID_IPersistStorage, (void **)&stg);
+    ok(hr == S_OK, "unexpected %#x\n", hr);
+    hr = IPersistStorage_Load(stg, doc);
+    ok(hr == S_OK, "unexpected %#x\n", hr);
+
+    IStorage_Release(doc);
+
+    hr = IPersistStorage_IsDirty(stg);
+    ok(hr == S_FALSE, "unexpected %#x\n", hr);
+
+    ole_stream = NULL;
+    hr = CreateStreamOnHGlobal(NULL, TRUE, &olepres_stream);
+    ok(hr == S_OK, "unexpected %#x\n", hr);
+
+    /* FIXME: remove this stream once Wine is fixed */
+    hr = CreateStreamOnHGlobal(NULL, TRUE, &contents_stream);
+    ok(hr == S_OK, "unexpected %#x\n", hr);
+
+    SET_EXPECT(Storage_CreateStream_OlePres);
+    SET_EXPECT(Storage_OpenStream_OlePres);
+    SET_EXPECT(Storage_OpenStream_Ole);
+    SET_EXPECT(Storage_DestroyElement);
+    Storage_DestroyElement_limit = 50;
+    Storage_SetClass_CLSID = &CLSID_NULL;
+    trace("IPersistStorage_Save:\n");
+    hr = IPersistStorage_Save(stg, &Storage, FALSE);
+    ok(hr == S_OK, "unexpected %#x\n", hr);
+    CHECK_CALLED(Storage_CreateStream_OlePres);
+todo_wine
+    CHECK_CALLED(Storage_OpenStream_OlePres);
+todo_wine
+    CHECK_CALLED(Storage_OpenStream_Ole);
+todo_wine
+    CHECK_CALLED(Storage_DestroyElement);
+
+    IStream_Release(olepres_stream);
+    IStream_Release(contents_stream);
+
+    IPersistStorage_Release(stg);
+    IOleCache2_Release(cache);
+}
+
+#define MAX_STREAM 16
+
+struct stream_def
+{
+    const char *name;
+    int cf;
+    DVASPECT dvAspect;
+    ADVF advf;
+    const void *data;
+    size_t data_size;
+};
+
+struct storage_def
+{
+    const CLSID *clsid;
+    int stream_count;
+    struct stream_def stream[MAX_STREAM];
+};
+
+static const struct storage_def stg_def_0 =
+{
+    &CLSID_NULL, 1,
+    {{ "Contents", -1, 0, 0, bmpimage, sizeof(bmpimage) }}
+};
+static const struct storage_def stg_def_0_saved =
+{
+    &CLSID_NULL, 0, {{ 0 }}
+};
+static const struct storage_def stg_def_1 =
+{
+    &CLSID_NULL, 2,
+    {{ "Contents", -1, 0, 0, NULL, 0 },
+    { "\2OlePres000", 0, DVASPECT_ICON, ADVF_PRIMEFIRST | ADVF_ONLYONCE, NULL, 0 }}
+};
+static const struct storage_def stg_def_1_saved =
+{
+    &CLSID_NULL, 1,
+    {{ "\2OlePres000", 0, DVASPECT_ICON, ADVF_PRIMEFIRST | ADVF_ONLYONCE, NULL, 0 }}
+};
+static const struct storage_def stg_def_2 =
+{
+    &CLSID_ManualResetEvent, 2,
+    {{ "Contents", -1, 0, 0, bmpimage, sizeof(bmpimage) },
+    { "\2OlePres000", 0, DVASPECT_ICON, ADVF_PRIMEFIRST | ADVF_ONLYONCE, NULL, 0 }}
+};
+static const struct storage_def stg_def_2_saved =
+{
+    &CLSID_NULL, 1,
+    {{ "\2OlePres000", 0, DVASPECT_ICON, ADVF_PRIMEFIRST | ADVF_ONLYONCE, NULL, 0 }}
+};
+static const struct storage_def stg_def_3 =
+{
+    &CLSID_NULL, 5,
+    {{ "Contents", -1, 0, 0, bmpimage, sizeof(bmpimage) },
+    { "\2OlePres000", 0, DVASPECT_ICON, ADVF_PRIMEFIRST | ADVF_ONLYONCE, NULL, 0 },
+    { "\2OlePres001", CF_METAFILEPICT, DVASPECT_CONTENT, ADVF_PRIMEFIRST, mf_blank_bits, sizeof(mf_blank_bits) },
+    { "\2OlePres002", CF_DIB, DVASPECT_CONTENT, ADVF_PRIMEFIRST, bmpimage, sizeof(bmpimage) },
+    { "MyStream", -1, 0, 0, "Hello World!", 13 }}
+};
+static const struct storage_def stg_def_3_saved =
+{
+    &CLSID_NULL, 3,
+    {{ "\2OlePres000", 0, DVASPECT_ICON, ADVF_PRIMEFIRST | ADVF_ONLYONCE, NULL, 0 },
+    { "\2OlePres001", CF_METAFILEPICT, DVASPECT_CONTENT, ADVF_PRIMEFIRST, mf_blank_bits, sizeof(mf_blank_bits) },
+    { "\2OlePres002", CF_DIB, DVASPECT_CONTENT, ADVF_PRIMEFIRST, bmpimage, sizeof(bmpimage) }}
+};
+static const struct storage_def stg_def_4 =
+{
+    &CLSID_Picture_EnhMetafile, 5,
+    {{ "Contents", -1, 0, 0, bmpimage, sizeof(bmpimage) },
+    { "\2OlePres000", 0, DVASPECT_ICON, ADVF_PRIMEFIRST | ADVF_ONLYONCE, NULL, 0 },
+    { "\2OlePres001", CF_METAFILEPICT, DVASPECT_CONTENT, ADVF_PRIMEFIRST, mf_blank_bits, sizeof(mf_blank_bits) },
+    { "\2OlePres002", CF_DIB, DVASPECT_CONTENT, ADVF_PRIMEFIRST, bmpimage, sizeof(bmpimage) },
+    { "MyStream", -1, 0, 0, "Hello World!", 13 }}
+};
+static const struct storage_def stg_def_4_saved =
+{
+    &CLSID_NULL, 1,
+    {{ "\2OlePres000", 0, DVASPECT_ICON, ADVF_PRIMEFIRST | ADVF_ONLYONCE, NULL, 0 }}
+};
+static const struct storage_def stg_def_5 =
+{
+    &CLSID_Picture_Dib, 5,
+    {{ "Contents", -1, 0, 0, bmpimage, sizeof(bmpimage) },
+    { "\2OlePres002", CF_DIB, DVASPECT_CONTENT, ADVF_PRIMEFIRST, bmpimage, sizeof(bmpimage) },
+    { "\2OlePres001", CF_METAFILEPICT, DVASPECT_CONTENT, ADVF_PRIMEFIRST, mf_blank_bits, sizeof(mf_blank_bits) },
+    { "\2OlePres000", 0, DVASPECT_ICON, ADVF_PRIMEFIRST | ADVF_ONLYONCE, NULL, 0 },
+    { "MyStream", -1, 0, 0, "Hello World!", 13 }}
+};
+static const struct storage_def stg_def_5_saved =
+{
+    &CLSID_NULL, 1,
+    {{ "\2OlePres000", 0, DVASPECT_ICON, ADVF_PRIMEFIRST | ADVF_ONLYONCE, NULL, 0 }}
+};
+static const struct storage_def stg_def_6 =
+{
+    &CLSID_Picture_Metafile, 5,
+    {{ "Contents", -1, 0, 0, bmpimage, sizeof(bmpimage) },
+    { "\2OlePres001", CF_METAFILEPICT, DVASPECT_CONTENT, ADVF_PRIMEFIRST, mf_blank_bits, sizeof(mf_blank_bits) },
+    { "\2OlePres000", 0, DVASPECT_ICON, ADVF_PRIMEFIRST | ADVF_ONLYONCE, NULL, 0 },
+    { "\2OlePres002", CF_DIB, DVASPECT_CONTENT, ADVF_PRIMEFIRST, bmpimage, sizeof(bmpimage) },
+    { "MyStream", -1, 0, 0, "Hello World!", 13 }}
+};
+static const struct storage_def stg_def_6_saved =
+{
+    &CLSID_NULL, 1,
+    {{ "\2OlePres000", 0, DVASPECT_ICON, ADVF_PRIMEFIRST | ADVF_ONLYONCE, NULL, 0 }}
+};
+static const struct storage_def stg_def_7 =
+{
+    &CLSID_Picture_Dib, 1,
+    {{ "Contents", -1, 0, 0, bmpimage, sizeof(bmpimage) }}
+};
+static const struct storage_def stg_def_7_saved =
+{
+    &CLSID_NULL, 0, {{ 0 }}
+};
+static const struct storage_def stg_def_8 =
+{
+    &CLSID_Picture_Metafile, 1,
+    {{ "Contents", -1, 0, 0, mf_blank_bits, sizeof(mf_blank_bits) }}
+};
+static const struct storage_def stg_def_8_saved =
+{
+    &CLSID_NULL, 0, {{ 0 }}
+};
+static const struct storage_def stg_def_9 =
+{
+    &CLSID_Picture_EnhMetafile, 1,
+    {{ "Contents", -1, 0, 0, bmpimage, sizeof(bmpimage) }}
+};
+static const struct storage_def stg_def_9_saved =
+{
+    &CLSID_NULL, 0, {{ 0 }}
+};
+
+static int read_clipformat(IStream *stream)
+{
+    HRESULT hr;
+    ULONG bytes;
+    int length, clipformat = -2;
+
+    hr = IStream_Read(stream, &length, sizeof(length), &bytes);
+    if (hr != S_OK || bytes != sizeof(length))
+        return -2;
+    if (length == 0)
+        return 0;
+    if (length == -1)
+    {
+        hr = IStream_Read(stream, &clipformat, sizeof(clipformat), &bytes);
+        if (hr != S_OK || bytes != sizeof(clipformat))
+            return -2;
+    }
+    else
+        ok(0, "unhandled clipformat length %d\n", length);
+
+    return clipformat;
+}
+
+static void check_storage_contents(IStorage *stg, const struct storage_def *stg_def,
+        int *enumerated_streams, int *matched_streams)
+{
+    HRESULT hr;
+    IEnumSTATSTG *enumstg;
+    IStream *stream;
+    STATSTG stat;
+    int i, seen_stream[MAX_STREAM] = { 0 };
+
+    if (winetest_debug > 1)
+        trace("check_storage_contents:\n=============================================\n");
+
+    *enumerated_streams = 0;
+    *matched_streams = 0;
+
+    hr = IStorage_Stat(stg, &stat, STATFLAG_NONAME);
+    ok(hr == S_OK, "unexpected %#x\n", hr);
+    ok(IsEqualCLSID(stg_def->clsid, &stat.clsid), "expected %s, got %s\n",
+       wine_dbgstr_guid(stg_def->clsid), wine_dbgstr_guid(&stat.clsid));
+
+    hr = IStorage_EnumElements(stg, 0, NULL, 0, &enumstg);
+    ok(hr == S_OK, "unexpected %#x\n", hr);
+
+    for (;;)
+    {
+        ULONG bytes;
+        int clipformat = -1;
+        PresentationDataHeader header;
+        char name[32];
+        BYTE data[1024];
+
+        memset(&header, 0, sizeof(header));
+
+        hr = IEnumSTATSTG_Next(enumstg, 1, &stat, NULL);
+        if(hr == S_FALSE) break;
+        ok(hr == S_OK, "unexpected %#x\n", hr);
+
+        if (winetest_debug > 1)
+            trace("name %s, type %u, size %d, clsid %s\n",
+                wine_dbgstr_w(stat.pwcsName), stat.type, stat.cbSize.u.LowPart, wine_dbgstr_guid(&stat.clsid));
+
+        ok(stat.type == STGTY_STREAM, "unexpected %#x\n", stat.type);
+
+        WideCharToMultiByte(CP_ACP, 0, stat.pwcsName, -1, name, sizeof(name), NULL, NULL);
+
+        hr = IStorage_OpenStream(stg, stat.pwcsName, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream);
+        ok(hr == S_OK, "unexpected %#x\n", hr);
+
+        if (!memcmp(name, "\2OlePres", 7))
+        {
+            clipformat = read_clipformat(stream);
+
+            hr = IStream_Read(stream, &header, sizeof(header), &bytes);
+            ok(hr == S_OK, "unexpected %#x\n", hr);
+            ok(bytes >= 24, "read %u bytes\n", bytes);
+
+            if (winetest_debug > 1)
+                trace("header: tdSize %#x, dvAspect %#x, lindex %#x, advf %#x, unknown7 %#x, dwObjectExtentX %#x, dwObjectExtentY %#x, dwSize %#x\n",
+                    header.tdSize, header.dvAspect, header.lindex, header.advf, header.unknown7,
+                    header.dwObjectExtentX, header.dwObjectExtentY, header.dwSize);
+        }
+
+        memset(data, 0, sizeof(data));
+        hr = IStream_Read(stream, data, sizeof(data), &bytes);
+        ok(hr == S_OK, "unexpected %#x\n", hr);
+        if (winetest_debug > 1)
+            trace("stream data (%u bytes): %02x %02x %02x %02x\n", bytes, data[0], data[1], data[2], data[3]);
+
+        for (i = 0; i < stg_def->stream_count; i++)
+        {
+            if (seen_stream[i]) continue;
+
+            if (winetest_debug > 1)
+                trace("%s/%s, %d/%d, %d/%d, %d/%d\n",
+                    stg_def->stream[i].name, name,
+                    stg_def->stream[i].cf, clipformat,
+                    stg_def->stream[i].dvAspect, header.dvAspect,
+                    stg_def->stream[i].advf, header.advf);
+
+            if (!strcmp(stg_def->stream[i].name, name) &&
+                stg_def->stream[i].cf == clipformat &&
+                stg_def->stream[i].dvAspect == header.dvAspect &&
+                stg_def->stream[i].advf == header.advf &&
+                stg_def->stream[i].data_size <= bytes &&
+                (!stg_def->stream[i].data_size ||
+                    (!memcmp(stg_def->stream[i].data, data, min(stg_def->stream[i].data_size, bytes)))))
+            {
+                if (winetest_debug > 1)
+                    trace("stream %d matches def stream %d\n", *enumerated_streams, i);
+                seen_stream[i] = 1;
+                *matched_streams += 1;
+            }
+        }
+
+        CoTaskMemFree(stat.pwcsName);
+        IStream_Release(stream);
+
+        *enumerated_streams += 1;
+    }
+}
+
+static IStorage *create_storage_from_def(const struct storage_def *stg_def)
+{
+    HRESULT hr;
+    IStorage *stg;
+    IStream *stm;
+    int i;
+
+    hr = StgCreateDocfile(NULL, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_DELETEONRELEASE, 0, &stg);
+    ok(hr == S_OK, "unexpected %#x\n", hr);
+
+    hr = IStorage_SetClass(stg, stg_def->clsid);
+    ok(hr == S_OK, "unexpected %#x\n", hr);
+
+    for (i = 0; i < stg_def->stream_count; i++)
+    {
+        WCHAR name[32];
+
+        MultiByteToWideChar(CP_ACP, 0, stg_def->stream[i].name, -1, name, 32);
+        hr = IStorage_CreateStream(stg, name, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
+        ok(hr == S_OK, "unexpected %#x\n", hr);
+
+        if (stg_def->stream[i].cf != -1)
+        {
+            int clipformat[2];
+            PresentationDataHeader hdr;
+
+            if (stg_def->stream[i].cf)
+            {
+                clipformat[0] = -1;
+                clipformat[1] = stg_def->stream[i].cf;
+                hr = IStream_Write(stm, clipformat, sizeof(clipformat), NULL);
+            }
+            else
+            {
+                clipformat[0] = 0;
+                hr = IStream_Write(stm, &clipformat[0], sizeof(clipformat[0]), NULL);
+            }
+            ok(hr == S_OK, "unexpected %#x\n", hr);
+
+            hdr.tdSize = sizeof(hdr.tdSize);
+            hdr.dvAspect = stg_def->stream[i].dvAspect;
+            hdr.lindex = -1;
+            hdr.advf = stg_def->stream[i].advf;
+            hdr.unknown7 = 0;
+            hdr.dwObjectExtentX = 0;
+            hdr.dwObjectExtentY = 0;
+            hdr.dwSize = stg_def->stream[i].data_size;
+            hr = IStream_Write(stm, &hdr, sizeof(hdr), NULL);
+            ok(hr == S_OK, "unexpected %#x\n", hr);
+        }
+
+        if (stg_def->stream[i].data_size)
+        {
+            hr = IStream_Write(stm, stg_def->stream[i].data, stg_def->stream[i].data_size, NULL);
+            ok(hr == S_OK, "unexpected %#x\n", hr);
+        }
+
+        IStream_Release(stm);
+    }
+
+    return stg;
+}
+
+static const BYTE dib_inf[] =
+{
+    0x42, 0x4d, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x36, 0x00, 0x00, 0x00
+};
+
+static const BYTE mf_rec[] =
+{
+    0xd7, 0xcd, 0xc6, 0x9a, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x16, 0x00, 0x2d, 0x00, 0x40, 0x02,
+    0x00, 0x00, 0x00, 0x00, 0x6a, 0x55
+};
+
+static void get_stgdef(struct storage_def *stg_def, CLIPFORMAT cf, STGMEDIUM *stg_med, int stm_idx)
+{
+    BYTE *data;
+    int data_size;
+    METAFILEPICT *mfpict;
+    HDC hdc;
+
+    switch (cf)
+    {
+    case CF_DIB:
+        data_size = sizeof(dib);
+        if (!strcmp(stg_def->stream[stm_idx].name, "CONTENTS"))
+        {
+            data_size += sizeof(dib_inf);
+            data = HeapAlloc(GetProcessHeap(), 0, data_size);
+            memcpy(data, dib_inf, sizeof(dib_inf));
+            memcpy(data + sizeof(dib_inf), dib, sizeof(dib));
+        }
+        else
+        {
+            data = HeapAlloc(GetProcessHeap(), 0, data_size);
+            memcpy(data, dib, sizeof(dib));
+        }
+        stg_def->stream[stm_idx].data = data;
+        stg_def->stream[stm_idx].data_size = data_size;
+        break;
+    case CF_METAFILEPICT:
+        mfpict = GlobalLock(U(stg_med)->hMetaFilePict);
+        data_size = GetMetaFileBitsEx(mfpict->hMF, 0, NULL);
+        if (!strcmp(stg_def->stream[stm_idx].name, "CONTENTS"))
+        {
+            data = HeapAlloc(GetProcessHeap(), 0, data_size + sizeof(mf_rec));
+            memcpy(data, mf_rec, sizeof(mf_rec));
+            GetMetaFileBitsEx(mfpict->hMF, data_size, data + sizeof(mf_rec));
+            data_size += sizeof(mf_rec);
+        }
+        else
+        {
+            data = HeapAlloc(GetProcessHeap(), 0, data_size);
+            GetMetaFileBitsEx(mfpict->hMF, data_size, data);
+        }
+        GlobalUnlock(U(stg_med)->hMetaFilePict);
+        stg_def->stream[stm_idx].data_size = data_size;
+        stg_def->stream[stm_idx].data = data;
+        break;
+    case CF_ENHMETAFILE:
+        if (!strcmp(stg_def->stream[stm_idx].name, "CONTENTS"))
+        {
+            data_size = GetEnhMetaFileBits(U(stg_med)->hEnhMetaFile, 0, NULL);
+            data = HeapAlloc(GetProcessHeap(), 0, sizeof(DWORD) + sizeof(ENHMETAHEADER) + data_size);
+            *((DWORD *)data) = sizeof(ENHMETAHEADER);
+            GetEnhMetaFileBits(U(stg_med)->hEnhMetaFile, data_size, data + sizeof(DWORD) + sizeof(ENHMETAHEADER));
+            memcpy(data + sizeof(DWORD), data + sizeof(DWORD) + sizeof(ENHMETAHEADER), sizeof(ENHMETAHEADER));
+            data_size += sizeof(DWORD) + sizeof(ENHMETAHEADER);
+        }
+        else
+        {
+            hdc = GetDC(NULL);
+            data_size = GetWinMetaFileBits(U(stg_med)->hEnhMetaFile, 0, NULL, MM_ANISOTROPIC, hdc);
+            data = HeapAlloc(GetProcessHeap(), 0, data_size);
+            GetWinMetaFileBits(U(stg_med)->hEnhMetaFile, data_size, data, MM_ANISOTROPIC, hdc);
+            ReleaseDC(NULL, hdc);
+        }
+        stg_def->stream[stm_idx].data_size = data_size;
+        stg_def->stream[stm_idx].data = data;
+        break;
+    }
+}
+
+static void get_stgmedium(CLIPFORMAT cfFormat, STGMEDIUM *stgmedium)
+{
+    switch (cfFormat)
+    {
+    case CF_DIB:
+        create_dib(stgmedium);
+        break;
+    case CF_METAFILEPICT:
+        create_mfpict(stgmedium);
+        break;
+    case CF_ENHMETAFILE:
+        create_emf(stgmedium);
+        break;
+    default:
+        ok(0, "cf %x not implemented\n", cfFormat);
+    }
+}
+
+#define MAX_FMTS 5
+static void test_data_cache_save_data(void)
+{
+    HRESULT hr;
+    STGMEDIUM stgmed;
+    ILockBytes *ilb;
+    IStorage *doc;
+    IOleCache2 *cache;
+    IPersistStorage *persist;
+    int enumerated_streams, matched_streams, i;
+    DWORD dummy;
+    struct tests_data_cache
+    {
+        FORMATETC fmts[MAX_FMTS];
+        int num_fmts, num_set;
+        const CLSID *clsid;
+        struct storage_def stg_def;
+    };
+
+    static struct tests_data_cache *pdata, data[] =
+    {
+        {
+            {
+                { CF_DIB, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL },
+                { CF_METAFILEPICT, 0, DVASPECT_CONTENT, -1, TYMED_MFPICT },
+                { CF_ENHMETAFILE, 0, DVASPECT_CONTENT, -1, TYMED_ENHMF },
+            },
+            3, 3, &CLSID_WineTest,
+            {
+                &CLSID_WineTestOld, 3, { { "\2OlePres000", CF_DIB, DVASPECT_CONTENT, 0, NULL, 0 },
+                                         { "\2OlePres001", CF_METAFILEPICT, DVASPECT_CONTENT, 0, NULL, 0 },
+                                         { "\2OlePres002", CF_ENHMETAFILE, DVASPECT_CONTENT, 0, NULL, 0 } }
+            }
+        },
+        /* without setting data */
+        {
+            {
+                { CF_DIB, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL },
+                { CF_METAFILEPICT, 0, DVASPECT_CONTENT, -1, TYMED_MFPICT },
+                { CF_ENHMETAFILE, 0, DVASPECT_CONTENT, -1, TYMED_ENHMF },
+            },
+            3, 0, &CLSID_WineTest,
+            {
+                &CLSID_WineTestOld, 3, { { "\2OlePres000", CF_DIB, DVASPECT_CONTENT, 0, NULL, 0 },
+                                         { "\2OlePres001", CF_METAFILEPICT, DVASPECT_CONTENT, 0, NULL, 0 },
+                                         { "\2OlePres002", CF_ENHMETAFILE, DVASPECT_CONTENT, 0, NULL, 0 } }
+            }
+        },
+        /* static picture clsids */
+        {
+            {
+                { CF_DIB, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL },
+            },
+            1, 1, &CLSID_Picture_Dib,
+            {
+                &CLSID_WineTestOld, 1, { { "CONTENTS", -1, 0, 0, NULL, 0 } }
+            }
+        },
+        {
+            {
+                { CF_METAFILEPICT, 0, DVASPECT_CONTENT, -1, TYMED_MFPICT },
+            },
+            1, 1, &CLSID_Picture_Metafile,
+            {
+                &CLSID_WineTestOld, 1, { { "CONTENTS", -1, 0, 0, NULL, 0 } }
+            }
+        },
+        {
+            {
+                { CF_ENHMETAFILE, 0, DVASPECT_CONTENT, -1, TYMED_ENHMF },
+            },
+            1, 1, &CLSID_Picture_EnhMetafile,
+            {
+                &CLSID_WineTestOld, 1, { { "CONTENTS", -1, 0, 0, NULL, 0 } }
+            }
+        },
+        /* static picture clsids without setting any data */
+        {
+            {
+                { CF_DIB, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL },
+            },
+            1, 0, &CLSID_Picture_Dib,
+            {
+                &CLSID_WineTestOld, 1, { { "CONTENTS", -1, 0, 0, NULL, 0 } }
+            }
+        },
+        {
+            {
+                { CF_METAFILEPICT, 0, DVASPECT_CONTENT, -1, TYMED_MFPICT },
+            },
+            1, 0, &CLSID_Picture_Metafile,
+            {
+                &CLSID_WineTestOld, 1, { { "CONTENTS", -1, 0, 0, NULL, 0 } }
+            }
+        },
+        {
+            {
+                { CF_ENHMETAFILE, 0, DVASPECT_CONTENT, -1, TYMED_ENHMF },
+            },
+            1, 0, &CLSID_Picture_EnhMetafile,
+            {
+                &CLSID_WineTestOld, 1, { { "CONTENTS", -1, 0, 0, NULL, 0 } }
+            }
+        },
+        {
+            {
+                { 0 }
+            }
+        }
+    };
+
+    /* test _Save after caching directly through _Cache + _SetData */
+    for (pdata = data; pdata->clsid != NULL; pdata++)
+    {
+        hr = CreateDataCache(NULL, pdata->clsid, &IID_IOleCache2, (void **)&cache);
+        ok(hr == S_OK, "unexpected %#x\n", hr);
+
+        for (i = 0; i < pdata->num_fmts; i++)
+        {
+            hr = IOleCache2_Cache(cache, &pdata->fmts[i], 0, &dummy);
+            ok(SUCCEEDED(hr), "unexpected %#x\n", hr);
+            if (i < pdata->num_set)
+            {
+                get_stgmedium(pdata->fmts[i].cfFormat, &stgmed);
+                get_stgdef(&pdata->stg_def, pdata->fmts[i].cfFormat, &stgmed, i);
+                hr = IOleCache2_SetData(cache, &pdata->fmts[i], &stgmed, TRUE);
+                ok(hr == S_OK, "unexpected %#x\n", hr);
+            }
+        }
+
+        /* create Storage in memory where we'll save cache */
+        hr = CreateILockBytesOnHGlobal(0, TRUE, &ilb);
+        ok(hr == S_OK, "unexpected %#x\n", hr);
+        hr = StgCreateDocfileOnILockBytes(ilb, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &doc);
+        ok(hr == S_OK, "unexpected %#x\n", hr);
+        ILockBytes_Release(ilb);
+        hr = IStorage_SetClass(doc, &CLSID_WineTestOld);
+        ok(hr == S_OK, "unexpected %#x\n", hr);
+
+        hr = IOleCache2_QueryInterface(cache, &IID_IPersistStorage, (void **)&persist);
+        ok(hr == S_OK, "unexpected %#x\n", hr);
+
+        /* cache entries are dirty. test saving them to stg */
+        trace("IPersistStorage_Save:\n");
+        hr = IPersistStorage_Save(persist, doc, FALSE);
+        ok(hr == S_OK, "unexpected %#x\n", hr);
+
+        hr = IPersistStorage_IsDirty(persist);
+        ok(hr == S_OK, "unexpected %#x\n", hr);
+
+        check_storage_contents(doc, &pdata->stg_def, &enumerated_streams, &matched_streams);
+        ok(enumerated_streams == matched_streams, "enumerated %d != matched %d\n",
+           enumerated_streams, matched_streams);
+        ok(enumerated_streams == pdata->stg_def.stream_count, "created %d != def streams %d\n",
+           enumerated_streams, pdata->stg_def.stream_count);
+
+        for (i = 0; i < pdata->num_set; i++)
+            HeapFree(GetProcessHeap(), 0, (void *)pdata->stg_def.stream[i].data);
+
+        IPersistStorage_Release(persist);
+        IStorage_Release(doc);
+        IOleCache2_Release(cache);
+    }
+}
+
+static void test_data_cache_contents(void)
+{
+    HRESULT hr;
+    IStorage *doc1, *doc2;
+    IOleCache2 *cache;
+    IPersistStorage *stg;
+    int i, enumerated_streams, matched_streams;
+    static const struct
+    {
+        const struct storage_def *in;
+        const struct storage_def *out;
+    } test_data[] =
+    {
+        { &stg_def_0, &stg_def_0_saved },
+        { &stg_def_1, &stg_def_1_saved },
+        { &stg_def_2, &stg_def_2_saved },
+        { &stg_def_3, &stg_def_3_saved },
+        { &stg_def_4, &stg_def_4_saved },
+        { &stg_def_5, &stg_def_5_saved },
+        { &stg_def_6, &stg_def_6_saved },
+        { &stg_def_7, &stg_def_7_saved },
+        { &stg_def_8, &stg_def_8_saved },
+        { &stg_def_9, &stg_def_9_saved },
+    };
+
+    for (i = 0; i < sizeof(test_data)/sizeof(test_data[0]); i++)
+    {
+        if (winetest_debug > 1)
+            trace("start testing storage def %d\n", i);
+
+        doc1 = create_storage_from_def(test_data[i].in);
+        if (!doc1) continue;
+
+        enumerated_streams = matched_streams = -1;
+        check_storage_contents(doc1, test_data[i].in, &enumerated_streams, &matched_streams);
+        ok(enumerated_streams == matched_streams, "%d in: enumerated %d != matched %d\n", i,
+           enumerated_streams, matched_streams);
+        ok(enumerated_streams == test_data[i].in->stream_count, "%d: created %d != def streams %d\n", i,
+           enumerated_streams, test_data[i].in->stream_count);
+
+        hr = CreateDataCache(NULL, &CLSID_NULL, &IID_IUnknown, (void **)&cache);
+        ok(hr == S_OK, "unexpected %#x\n", hr);
+        hr = IOleCache2_QueryInterface(cache, &IID_IPersistStorage, (void **)&stg);
+        ok(hr == S_OK, "unexpected %#x\n", hr);
+        hr = IPersistStorage_Load(stg, doc1);
+        ok(hr == S_OK, "unexpected %#x\n", hr);
+
+        IStorage_Release(doc1);
+
+        hr = StgCreateDocfile(NULL, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_DELETEONRELEASE, 0, &doc2);
+        ok(hr == S_OK, "unexpected %#x\n", hr);
+
+        hr = IPersistStorage_IsDirty(stg);
+todo_wine_if(test_data[i].in == &stg_def_4 || test_data[i].in == &stg_def_8 || test_data[i].in == &stg_def_9)
+        ok(hr == S_FALSE, "%d: unexpected %#x\n", i, hr);
+
+        hr = IPersistStorage_Save(stg, doc2, FALSE);
+        ok(hr == S_OK, "unexpected %#x\n", hr);
+
+        IPersistStorage_Release(stg);
+
+        enumerated_streams = matched_streams = -1;
+        check_storage_contents(doc2, test_data[i].out, &enumerated_streams, &matched_streams);
+todo_wine_if(!(test_data[i].in == &stg_def_0 || test_data[i].in == &stg_def_1 || test_data[i].in == &stg_def_2))
+        ok(enumerated_streams == matched_streams, "%d out: enumerated %d != matched %d\n", i,
+           enumerated_streams, matched_streams);
+todo_wine_if(!(test_data[i].in == &stg_def_0 || test_data[i].in == &stg_def_5))
+        ok(enumerated_streams == test_data[i].out->stream_count, "%d: saved streams %d != def streams %d\n", i,
+            enumerated_streams, test_data[i].out->stream_count);
+
+        IStorage_Release(doc2);
+
+        if (winetest_debug > 1)
+            trace("done testing storage def %d\n", i);
+    }
+}
+
 START_TEST(ole2)
 {
     DWORD dwRegister;
@@ -3153,18 +4466,24 @@ START_TEST(ole2)
     hr = CoRevokeClassObject(dwRegister);
     ok_ole_success(hr, "CoRevokeClassObject");
 
+    Storage_SetClass_CLSID = &CLSID_WineTest;
+
     test_data_cache();
     test_data_cache_dib_contents_stream( 0 );
     test_data_cache_dib_contents_stream( 1 );
-    test_data_cache_bitmap();
+    test_data_cache_cache();
     test_data_cache_init();
     test_data_cache_initnew();
+    test_data_cache_updatecache();
     test_default_handler();
     test_runnable();
     test_OleRun();
     test_OleLockRunning();
     test_OleDraw();
     test_OleDoAutoConvert();
+    test_data_cache_save();
+    test_data_cache_save_data();
+    test_data_cache_contents();
 
     CoUninitialize();
 }
index bc95259..8576ed7 100644 (file)
@@ -178,31 +178,32 @@ static void test_marshal_CLIPFORMAT(void)
     USER_MARSHAL_CB umcb;
     MIDL_STUB_MESSAGE stub_msg;
     RPC_MESSAGE rpc_msg;
-    unsigned char *buffer;
+    unsigned char *buffer, *buffer_end;
     ULONG i, size;
     CLIPFORMAT cf = RegisterClipboardFormatA("MyFormat");
     CLIPFORMAT cf2;
 
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE);
-    size = CLIPFORMAT_UserSize(&umcb.Flags, 0, &cf);
-    ok(size == 8 + sizeof(cf_marshaled) ||
-       broken(size == 12 + sizeof(cf_marshaled)) ||  /* win64 adds 4 extra (unused) bytes */
-       broken(size == 8 + sizeof(cf_marshaled) - 2), /* win9x and winnt don't include the '\0' */
+    size = CLIPFORMAT_UserSize(&umcb.Flags, 1, &cf);
+    ok(size == 12 + sizeof(cf_marshaled) ||
+       broken(size == 16 + sizeof(cf_marshaled)),  /* win64 adds 4 extra (unused) bytes */
               "CLIPFORMAT: Wrong size %d\n", size);
 
     buffer = HeapAlloc(GetProcessHeap(), 0, size);
     memset( buffer, 0xcc, size );
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_DIFFERENTMACHINE);
-    CLIPFORMAT_UserMarshal(&umcb.Flags, buffer, &cf);
-    ok(*(LONG *)(buffer + 0) == WDT_REMOTE_CALL, "CLIPFORMAT: Context should be WDT_REMOTE_CALL instead of 0x%08x\n", *(LONG *)(buffer + 0));
-    ok(*(DWORD *)(buffer + 4) == cf, "CLIPFORMAT: Marshaled value should be 0x%04x instead of 0x%04x\n", cf, *(DWORD *)(buffer + 4));
-    ok(!memcmp(buffer + 8, cf_marshaled, min( sizeof(cf_marshaled), size-8 )), "Marshaled data differs\n");
-    if (size > sizeof(cf_marshaled) + 8)  /* make sure the extra bytes are not used */
-        for (i = sizeof(cf_marshaled) + 8; i < size; i++)
+    buffer_end = CLIPFORMAT_UserMarshal(&umcb.Flags, buffer + 1, &cf);
+    ok(buffer_end == buffer + 12 + sizeof(cf_marshaled), "got %p buffer %p\n", buffer_end, buffer);
+    ok(*(LONG *)(buffer + 4) == WDT_REMOTE_CALL, "CLIPFORMAT: Context should be WDT_REMOTE_CALL instead of 0x%08x\n", *(LONG *)(buffer + 0));
+    ok(*(DWORD *)(buffer + 8) == cf, "CLIPFORMAT: Marshaled value should be 0x%04x instead of 0x%04x\n", cf, *(DWORD *)(buffer + 4));
+    ok(!memcmp(buffer + 12, cf_marshaled, min( sizeof(cf_marshaled), size-12 )), "Marshaled data differs\n");
+    if (size > sizeof(cf_marshaled) + 12)  /* make sure the extra bytes are not used */
+        for (i = sizeof(cf_marshaled) + 12; i < size; i++)
             ok( buffer[i] == 0xcc, "buffer offset %u has been set to %x\n", i, buffer[i] );
 
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_DIFFERENTMACHINE);
-    CLIPFORMAT_UserUnmarshal(&umcb.Flags, buffer, &cf2);
+    buffer_end = CLIPFORMAT_UserUnmarshal(&umcb.Flags, buffer + 1, &cf2);
+    ok(buffer_end == buffer + 12 + sizeof(cf_marshaled), "got %p buffer %p\n", buffer_end, buffer);
     ok(cf == cf2, "CLIPFORMAT: Didn't unmarshal properly\n");
     HeapFree(GetProcessHeap(), 0, buffer);
 
@@ -215,25 +216,27 @@ static void test_marshal_HWND(void)
     USER_MARSHAL_CB umcb;
     MIDL_STUB_MESSAGE stub_msg;
     RPC_MESSAGE rpc_msg;
-    unsigned char *buffer;
+    unsigned char *buffer, *buffer_end;
     ULONG size;
     HWND hwnd = GetDesktopWindow();
     HWND hwnd2;
     wireHWND wirehwnd;
 
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_LOCAL);
-    size = HWND_UserSize(&umcb.Flags, 0, &hwnd);
-    ok(size == sizeof(*wirehwnd), "Wrong size %d\n", size);
+    size = HWND_UserSize(&umcb.Flags, 1, &hwnd);
+    ok(size == 4 + sizeof(*wirehwnd), "Wrong size %d\n", size);
 
     buffer = HeapAlloc(GetProcessHeap(), 0, size);
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_LOCAL);
-    HWND_UserMarshal(&umcb.Flags, buffer, &hwnd);
-    wirehwnd = (wireHWND)buffer;
+    buffer_end = HWND_UserMarshal(&umcb.Flags, buffer + 1, &hwnd);
+    ok(buffer_end == buffer + size, "got %p buffer %p\n", buffer_end, buffer);
+    wirehwnd = (wireHWND)(buffer + 4);
     ok(wirehwnd->fContext == WDT_INPROC_CALL, "Context should be WDT_INPROC_CALL instead of 0x%08x\n", wirehwnd->fContext);
     ok(wirehwnd->u.hInproc == (LONG_PTR)hwnd, "Marshaled value should be %p instead of %x\n", hwnd, wirehwnd->u.hRemote);
 
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_LOCAL);
-    HWND_UserUnmarshal(&umcb.Flags, buffer, &hwnd2);
+    buffer_end = HWND_UserUnmarshal(&umcb.Flags, buffer + 1, &hwnd2);
+    ok(buffer_end == buffer + size, "got %p buffer %p\n", buffer_end, buffer);
     ok(hwnd == hwnd2, "Didn't unmarshal properly\n");
     HeapFree(GetProcessHeap(), 0, buffer);
 
@@ -335,7 +338,7 @@ static void test_marshal_HENHMETAFILE(void)
     USER_MARSHAL_CB umcb;
     MIDL_STUB_MESSAGE stub_msg;
     RPC_MESSAGE rpc_msg;
-    unsigned char *buffer;
+    unsigned char *buffer, *buffer_end;
     ULONG size;
     HENHMETAFILE hemf;
     HENHMETAFILE hemf2 = NULL;
@@ -344,26 +347,28 @@ static void test_marshal_HENHMETAFILE(void)
     hemf = create_emf();
 
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE);
-    size = HENHMETAFILE_UserSize(&umcb.Flags, 0, &hemf);
-    ok(size > 20, "size should be at least 20 bytes, not %d\n", size);
+    size = HENHMETAFILE_UserSize(&umcb.Flags, 1, &hemf);
+    ok(size > 24, "size should be at least 24 bytes, not %d\n", size);
     buffer = HeapAlloc(GetProcessHeap(), 0, size);
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_DIFFERENTMACHINE);
-    HENHMETAFILE_UserMarshal(&umcb.Flags, buffer, &hemf);
-    wirehemf = buffer;
+    buffer_end = HENHMETAFILE_UserMarshal(&umcb.Flags, buffer + 1, &hemf);
+    ok(buffer_end == buffer + size, "got %p buffer %p\n", buffer_end, buffer);
+    wirehemf = buffer + 4;
     ok(*(DWORD *)wirehemf == WDT_REMOTE_CALL, "wirestgm + 0x0 should be WDT_REMOTE_CALL instead of 0x%08x\n", *(DWORD *)wirehemf);
     wirehemf += sizeof(DWORD);
     ok(*(DWORD *)wirehemf == (DWORD)(DWORD_PTR)hemf, "wirestgm + 0x4 should be hemf instead of 0x%08x\n", *(DWORD *)wirehemf);
     wirehemf += sizeof(DWORD);
-    ok(*(DWORD *)wirehemf == (size - 0x10), "wirestgm + 0x8 should be size - 0x10 instead of 0x%08x\n", *(DWORD *)wirehemf);
+    ok(*(DWORD *)wirehemf == (size - 0x14), "wirestgm + 0x8 should be size - 0x14 instead of 0x%08x\n", *(DWORD *)wirehemf);
     wirehemf += sizeof(DWORD);
-    ok(*(DWORD *)wirehemf == (size - 0x10), "wirestgm + 0xc should be size - 0x10 instead of 0x%08x\n", *(DWORD *)wirehemf);
+    ok(*(DWORD *)wirehemf == (size - 0x14), "wirestgm + 0xc should be size - 0x14 instead of 0x%08x\n", *(DWORD *)wirehemf);
     wirehemf += sizeof(DWORD);
     ok(*(DWORD *)wirehemf == EMR_HEADER, "wirestgm + 0x10 should be EMR_HEADER instead of %d\n", *(DWORD *)wirehemf);
     /* ... rest of data not tested - refer to tests for GetEnhMetaFileBits
      * at this point */
 
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_DIFFERENTMACHINE);
-    HENHMETAFILE_UserUnmarshal(&umcb.Flags, buffer, &hemf2);
+    buffer_end = HENHMETAFILE_UserUnmarshal(&umcb.Flags, buffer + 1, &hemf2);
+    ok(buffer_end == buffer + size, "got %p buffer %p\n", buffer_end, buffer);
     ok(hemf2 != NULL, "HENHMETAFILE didn't unmarshal\n");
     HeapFree(GetProcessHeap(), 0, buffer);
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE);
@@ -374,18 +379,20 @@ static void test_marshal_HENHMETAFILE(void)
     hemf = NULL;
 
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE);
-    size = HENHMETAFILE_UserSize(&umcb.Flags, 0, &hemf);
-    ok(size == 8, "size should be 8 bytes, not %d\n", size);
+    size = HENHMETAFILE_UserSize(&umcb.Flags, 1, &hemf);
+    ok(size == 12, "size should be 12 bytes, not %d\n", size);
     buffer = HeapAlloc(GetProcessHeap(), 0, size);
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_DIFFERENTMACHINE);
-    HENHMETAFILE_UserMarshal(&umcb.Flags, buffer, &hemf);
-    wirehemf = buffer;
+    buffer_end = HENHMETAFILE_UserMarshal(&umcb.Flags, buffer + 1, &hemf);
+    ok(buffer_end == buffer + size, "got %p buffer %p\n", buffer_end, buffer);
+    wirehemf = buffer + 4;
     ok(*(DWORD *)wirehemf == WDT_REMOTE_CALL, "wirestgm + 0x0 should be WDT_REMOTE_CALL instead of 0x%08x\n", *(DWORD *)wirehemf);
     wirehemf += sizeof(DWORD);
     ok(*(DWORD *)wirehemf == (DWORD)(DWORD_PTR)hemf, "wirestgm + 0x4 should be hemf instead of 0x%08x\n", *(DWORD *)wirehemf);
 
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_DIFFERENTMACHINE);
-    HENHMETAFILE_UserUnmarshal(&umcb.Flags, buffer, &hemf2);
+    buffer_end = HENHMETAFILE_UserUnmarshal(&umcb.Flags, buffer + 1, &hemf2);
+    ok(buffer_end == buffer + size, "got %p buffer %p\n", buffer_end, buffer);
     ok(hemf2 == NULL, "NULL HENHMETAFILE didn't unmarshal\n");
     HeapFree(GetProcessHeap(), 0, buffer);
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE);
@@ -486,12 +493,12 @@ static void test_marshal_HMETAFILEPICT(void)
     GlobalUnlock(hmfp);
 
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE);
-    size = HMETAFILEPICT_UserSize(&umcb.Flags, 0, &hmfp);
-    ok(size > 20, "size should be at least 20 bytes, not %d\n", size);
+    size = HMETAFILEPICT_UserSize(&umcb.Flags, 1, &hmfp);
+    ok(size > 24, "size should be at least 24 bytes, not %d\n", size);
     buffer = HeapAlloc(GetProcessHeap(), 0, size);
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_DIFFERENTMACHINE);
-    buffer_end = HMETAFILEPICT_UserMarshal(&umcb.Flags, buffer, &hmfp);
-    wirehmfp = buffer;
+    buffer_end = HMETAFILEPICT_UserMarshal(&umcb.Flags, buffer + 1, &hmfp);
+    wirehmfp = buffer + 4;
     ok(*(DWORD *)wirehmfp == WDT_REMOTE_CALL, "wirestgm + 0x0 should be WDT_REMOTE_CALL instead of 0x%08x\n", *(DWORD *)wirehmfp);
     wirehmfp += sizeof(DWORD);
     ok(*(DWORD *)wirehmfp == (DWORD)(DWORD_PTR)hmfp, "wirestgm + 0x4 should be hmf instead of 0x%08x\n", *(DWORD *)wirehmfp);
@@ -512,16 +519,16 @@ static void test_marshal_HMETAFILEPICT(void)
     wirehmfp += sizeof(DWORD);
     /* Note use (buffer_end - buffer) instead of size here, because size is an
      * overestimate with native */
-    ok(*(DWORD *)wirehmfp == (buffer_end - buffer - 0x28), "wirestgm + 0x20 should be size - 0x34 instead of 0x%08x\n", *(DWORD *)wirehmfp);
+    ok(*(DWORD *)wirehmfp == (buffer_end - buffer - 0x2c), "wirestgm + 0x20 should be size - 0x34 instead of 0x%08x\n", *(DWORD *)wirehmfp);
     wirehmfp += sizeof(DWORD);
-    ok(*(DWORD *)wirehmfp == (buffer_end - buffer - 0x28), "wirestgm + 0x24 should be size - 0x34 instead of 0x%08x\n", *(DWORD *)wirehmfp);
+    ok(*(DWORD *)wirehmfp == (buffer_end - buffer - 0x2c), "wirestgm + 0x24 should be size - 0x34 instead of 0x%08x\n", *(DWORD *)wirehmfp);
     wirehmfp += sizeof(DWORD);
     ok(*(WORD *)wirehmfp == 1, "wirehmfp + 0x28 should be 1 instead of 0x%08x\n", *(DWORD *)wirehmfp);
     /* ... rest of data not tested - refer to tests for GetMetaFileBits
      * at this point */
 
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_DIFFERENTMACHINE);
-    HMETAFILEPICT_UserUnmarshal(&umcb.Flags, buffer, &hmfp2);
+    HMETAFILEPICT_UserUnmarshal(&umcb.Flags, buffer + 1, &hmfp2);
     ok(hmfp2 != NULL, "HMETAFILEPICT didn't unmarshal\n");
     HeapFree(GetProcessHeap(), 0, buffer);
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE);
@@ -535,12 +542,13 @@ static void test_marshal_HMETAFILEPICT(void)
     hmfp = NULL;
 
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE);
-    size = HMETAFILEPICT_UserSize(&umcb.Flags, 0, &hmfp);
-    ok(size == 8, "size should be 8 bytes, not %d\n", size);
+    size = HMETAFILEPICT_UserSize(&umcb.Flags, 1, &hmfp);
+    ok(size == 12, "size should be 12 bytes, not %d\n", size);
     buffer = HeapAlloc(GetProcessHeap(), 0, size);
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_DIFFERENTMACHINE);
-    HMETAFILEPICT_UserMarshal(&umcb.Flags, buffer, &hmfp);
-    wirehmfp = buffer;
+    buffer_end = HMETAFILEPICT_UserMarshal(&umcb.Flags, buffer + 1, &hmfp);
+    ok(buffer_end == buffer + size, "got %p buffer %p\n", buffer_end, buffer);
+    wirehmfp = buffer + 4;
     ok(*(DWORD *)wirehmfp == WDT_REMOTE_CALL, "wirestgm + 0x0 should be WDT_REMOTE_CALL instead of 0x%08x\n", *(DWORD *)wirehmfp);
     wirehmfp += sizeof(DWORD);
     ok(*(DWORD *)wirehmfp == (DWORD)(DWORD_PTR)hmfp, "wirestgm + 0x4 should be hmf instead of 0x%08x\n", *(DWORD *)wirehmfp);
@@ -548,7 +556,8 @@ static void test_marshal_HMETAFILEPICT(void)
 
     hmfp2 = NULL;
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_DIFFERENTMACHINE);
-    HMETAFILEPICT_UserUnmarshal(&umcb.Flags, buffer, &hmfp2);
+    buffer_end = HMETAFILEPICT_UserUnmarshal(&umcb.Flags, buffer + 1, &hmfp2);
+    ok(buffer_end == buffer + size, "got %p buffer %p\n", buffer_end, buffer);
     ok(hmfp2 == NULL, "NULL HMETAFILE didn't unmarshal\n");
     HeapFree(GetProcessHeap(), 0, buffer);
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE);
@@ -1082,23 +1091,25 @@ static void test_marshal_HDC(void)
     HDC hdc = GetDC(0), hdc2;
     USER_MARSHAL_CB umcb;
     RPC_MESSAGE rpc_msg;
-    unsigned char *buffer;
+    unsigned char *buffer, *buffer_end;
     wireHDC wirehdc;
     ULONG size;
 
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_LOCAL);
-    size = HDC_UserSize(&umcb.Flags, 0, &hdc);
-    ok(size == sizeof(*wirehdc), "Wrong size %d\n", size);
+    size = HDC_UserSize(&umcb.Flags, 1, &hdc);
+    ok(size == 4 + sizeof(*wirehdc), "Wrong size %d\n", size);
 
     buffer = HeapAlloc(GetProcessHeap(), 0, size);
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_LOCAL);
-    HDC_UserMarshal(&umcb.Flags, buffer, &hdc);
-    wirehdc = (wireHDC)buffer;
+    buffer_end = HDC_UserMarshal(&umcb.Flags, buffer + 1, &hdc);
+    ok(buffer_end == buffer + 4 + sizeof(*wirehdc), "got %p buffer %p\n", buffer_end, buffer);
+    wirehdc = (wireHDC)(buffer + 4);
     ok(wirehdc->fContext == WDT_INPROC_CALL, "Context should be WDT_INPROC_CALL instead of 0x%08x\n", wirehdc->fContext);
     ok(wirehdc->u.hInproc == (LONG_PTR)hdc, "Marshaled value should be %p instead of %x\n", hdc, wirehdc->u.hRemote);
 
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_LOCAL);
-    HDC_UserUnmarshal(&umcb.Flags, buffer, &hdc2);
+    buffer_end = HDC_UserUnmarshal(&umcb.Flags, buffer + 1, &hdc2);
+    ok(buffer_end == buffer + 4 + sizeof(*wirehdc), "got %p buffer %p\n", buffer_end, buffer);
     ok(hdc == hdc2, "Didn't unmarshal properly\n");
     HeapFree(GetProcessHeap(), 0, buffer);
 
@@ -1114,7 +1125,7 @@ static void test_marshal_HICON(void)
     HICON hIcon, hIcon2;
     USER_MARSHAL_CB umcb;
     RPC_MESSAGE rpc_msg;
-    unsigned char *buffer;
+    unsigned char *buffer, *buffer_end;
     wireHICON wirehicon;
     ULONG size;
 
@@ -1122,18 +1133,20 @@ static void test_marshal_HICON(void)
     ok(hIcon != 0, "CreateIcon failed\n");
 
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_LOCAL);
-    size = HICON_UserSize(&umcb.Flags, 0, &hIcon);
-    ok(size == sizeof(*wirehicon), "Wrong size %d\n", size);
+    size = HICON_UserSize(&umcb.Flags, 1, &hIcon);
+    ok(size == 4 + sizeof(*wirehicon), "Wrong size %d\n", size);
 
     buffer = HeapAlloc(GetProcessHeap(), 0, size);
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_LOCAL);
-    HICON_UserMarshal(&umcb.Flags, buffer, &hIcon);
-    wirehicon = (wireHICON)buffer;
+    buffer_end = HICON_UserMarshal(&umcb.Flags, buffer + 1, &hIcon);
+    ok(buffer_end == buffer + 4 + sizeof(*wirehicon), "got %p buffer %p\n", buffer_end, buffer);
+    wirehicon = (wireHICON)(buffer + 4);
     ok(wirehicon->fContext == WDT_INPROC_CALL, "Context should be WDT_INPROC_CALL instead of 0x%08x\n", wirehicon->fContext);
     ok(wirehicon->u.hInproc == (LONG_PTR)hIcon, "Marshaled value should be %p instead of %x\n", hIcon, wirehicon->u.hRemote);
 
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_LOCAL);
-    HICON_UserUnmarshal(&umcb.Flags, buffer, &hIcon2);
+    buffer_end = HICON_UserUnmarshal(&umcb.Flags, buffer + 1, &hIcon2);
+    ok(buffer_end == buffer + 4 + sizeof(*wirehicon), "got %p buffer %p\n", buffer_end, buffer);
     ok(hIcon == hIcon2, "Didn't unmarshal properly\n");
     HeapFree(GetProcessHeap(), 0, buffer);
 
@@ -1148,7 +1161,7 @@ static void test_marshal_HBRUSH(void)
     HBRUSH hBrush, hBrush2;
     USER_MARSHAL_CB umcb;
     RPC_MESSAGE rpc_msg;
-    unsigned char *buffer;
+    unsigned char *buffer, *buffer_end;
     LOGBRUSH logbrush;
     wireHBRUSH wirehbrush;
     ULONG size;
@@ -1161,18 +1174,20 @@ static void test_marshal_HBRUSH(void)
     ok(hBrush != 0, "CreateBrushIndirect failed\n");
 
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_LOCAL);
-    size = HBRUSH_UserSize(&umcb.Flags, 0, &hBrush);
-    ok(size == sizeof(*wirehbrush), "Wrong size %d\n", size);
+    size = HBRUSH_UserSize(&umcb.Flags, 1, &hBrush);
+    ok(size == 4 + sizeof(*wirehbrush), "Wrong size %d\n", size);
 
     buffer = HeapAlloc(GetProcessHeap(), 0, size);
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_LOCAL);
-    HBRUSH_UserMarshal(&umcb.Flags, buffer, &hBrush);
-    wirehbrush = (wireHBRUSH)buffer;
+    buffer_end = HBRUSH_UserMarshal(&umcb.Flags, buffer + 1, &hBrush);
+    ok(buffer_end == buffer + 4 + sizeof(*wirehbrush), "got %p buffer %p\n", buffer_end, buffer);
+    wirehbrush = (wireHBRUSH)(buffer + 4);
     ok(wirehbrush->fContext == WDT_INPROC_CALL, "Context should be WDT_INPROC_CALL instead of 0x%08x\n", wirehbrush->fContext);
     ok(wirehbrush->u.hInproc == (LONG_PTR)hBrush, "Marshaled value should be %p instead of %x\n", hBrush, wirehbrush->u.hRemote);
 
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_LOCAL);
-    HBRUSH_UserUnmarshal(&umcb.Flags, buffer, &hBrush2);
+    buffer_end = HBRUSH_UserUnmarshal(&umcb.Flags, buffer + 1, &hBrush2);
+    ok(buffer_end == buffer + 4 + sizeof(*wirehbrush), "got %p buffer %p\n", buffer_end, buffer);
     ok(hBrush == hBrush2, "Didn't unmarshal properly\n");
     HeapFree(GetProcessHeap(), 0, buffer);
 
@@ -1181,6 +1196,69 @@ static void test_marshal_HBRUSH(void)
     DeleteObject(hBrush);
 }
 
+static void test_marshal_HBITMAP(void)
+{
+    static const ULONG header_size = FIELD_OFFSET(userBITMAP, cbSize);
+    static BYTE bmp_bits[1024];
+    MIDL_STUB_MESSAGE stub_msg;
+    HBITMAP hBitmap, hBitmap2;
+    USER_MARSHAL_CB umcb;
+    RPC_MESSAGE rpc_msg;
+    unsigned char *buffer, *buffer_end;
+    unsigned char bitmap[1024];
+    ULONG size, bitmap_size;
+
+    hBitmap = CreateBitmap(16, 16, 1, 1, bmp_bits);
+    ok(hBitmap != 0, "CreateBitmap failed\n");
+    size = GetObjectA(hBitmap, sizeof(bitmap), bitmap);
+    ok(size != 0, "GetObject failed\n");
+    bitmap_size = GetBitmapBits(hBitmap, 0, NULL);
+    ok(bitmap_size != 0, "GetBitmapBits failed\n");
+
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_INPROC);
+    size = HBITMAP_UserSize(&umcb.Flags, 1, &hBitmap);
+    ok(size == 0xc, "Wrong size %d\n", size);
+    buffer = HeapAlloc(GetProcessHeap(), 0, size + 4);
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_INPROC);
+    buffer_end = HBITMAP_UserMarshal(&umcb.Flags, buffer + 1, &hBitmap);
+    ok(buffer_end == buffer + 0xc, "HBITMAP_UserMarshal() returned wrong size %d\n", (LONG)(buffer_end - buffer));
+    ok(*(ULONG *)(buffer + 0x4) == WDT_INPROC_CALL, "Context should be WDT_INPROC_CALL instead of 0x%08x\n", *(ULONG *)(buffer + 0x4));
+    ok(*(ULONG *)(buffer + 0x8) == (ULONG)(ULONG_PTR)hBitmap, "wirestgm + 0x4 should be bitmap handle instead of 0x%08x\n", *(ULONG *)(buffer + 0x8));
+
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_INPROC);
+    HBITMAP_UserUnmarshal(&umcb.Flags, buffer + 1, &hBitmap2);
+    ok(hBitmap2 != NULL, "Didn't unmarshal properly\n");
+    HeapFree(GetProcessHeap(), 0, buffer);
+
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_INPROC);
+    HBITMAP_UserFree(&umcb.Flags, &hBitmap2);
+
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_LOCAL);
+    size = HBITMAP_UserSize(&umcb.Flags, 1, &hBitmap);
+    ok(size == 0x10 + header_size + bitmap_size ||
+       broken(size == 0x14 + header_size + bitmap_size), /* Windows adds 4 extra (unused) bytes */
+       "Wrong size %d\n", size);
+
+    buffer = HeapAlloc(GetProcessHeap(), 0, size + 4);
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_LOCAL);
+    buffer_end = HBITMAP_UserMarshal(&umcb.Flags, buffer + 1, &hBitmap);
+    ok(buffer_end == buffer + 0x10 + header_size + bitmap_size, "HBITMAP_UserMarshal() returned wrong size %d\n", (LONG)(buffer_end - buffer));
+    ok(*(ULONG *)(buffer + 0x4) == WDT_REMOTE_CALL, "Context should be WDT_REMOTE_CALL instead of 0x%08x\n", *(ULONG *)buffer);
+    ok(*(ULONG *)(buffer + 0x8) == (ULONG)(ULONG_PTR)hBitmap, "wirestgm + 0x4 should be bitmap handle instead of 0x%08x\n", *(ULONG *)(buffer + 0x4));
+    ok(*(ULONG *)(buffer + 0xc) == (ULONG)(ULONG_PTR)bitmap_size, "wirestgm + 0x8 should be bitmap size instead of 0x%08x\n", *(ULONG *)(buffer + 0x4));
+    ok(!memcmp(buffer + 0x10, bitmap, header_size), "buffer mismatch\n");
+    ok(!memcmp(buffer + 0x10 + header_size, bmp_bits, bitmap_size), "buffer mismatch\n");
+
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_LOCAL);
+    HBITMAP_UserUnmarshal(&umcb.Flags, buffer + 1, &hBitmap2);
+    ok(hBitmap2 != NULL, "Didn't unmarshal properly\n");
+    HeapFree(GetProcessHeap(), 0, buffer);
+
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_LOCAL);
+    HBITMAP_UserFree(&umcb.Flags, &hBitmap2);
+    DeleteObject(hBitmap);
+}
+
 struct obj
 {
     IDataObject IDataObject_iface;
@@ -1344,6 +1422,7 @@ START_TEST(usrmarshal)
     test_marshal_HDC();
     test_marshal_HICON();
     test_marshal_HBRUSH();
+    test_marshal_HBITMAP();
 
     test_GetDataHere_Proxy();