[OLE32_WINETEST]
authorChristoph von Wittich <christoph_vw@reactos.org>
Wed, 10 Mar 2010 21:52:40 +0000 (21:52 +0000)
committerChristoph von Wittich <christoph_vw@reactos.org>
Wed, 10 Mar 2010 21:52:40 +0000 (21:52 +0000)
sync OLE32_WINETEST with wine 1.1.40

svn path=/trunk/; revision=46077

rostests/winetests/ole32/clipboard.c
rostests/winetests/ole32/compobj.c
rostests/winetests/ole32/hglobalstream.c
rostests/winetests/ole32/marshal.c
rostests/winetests/ole32/moniker.c
rostests/winetests/ole32/ole2.c
rostests/winetests/ole32/stg_prop.c
rostests/winetests/ole32/storage32.c
rostests/winetests/ole32/usrmarshal.c

index f06a482..626d244 100644 (file)
@@ -477,8 +477,6 @@ static void test_get_clipboard(void)
     hr = IDataObject_QueryGetData(data_obj, &fmtetc);
     ok(hr == DV_E_FORMATETC || broken(hr == S_OK),
         "IDataObject_QueryGetData should have failed with DV_E_FORMATETC instead of 0x%08x\n", hr);
-    if (hr == S_OK)
-        ReleaseStgMedium(&stgmedium);
 
     InitFormatEtc(fmtetc, CF_TEXT, TYMED_HGLOBAL);
     fmtetc.cfFormat = CF_RIFF;
@@ -499,19 +497,19 @@ static void test_get_clipboard(void)
     InitFormatEtc(fmtetc, CF_TEXT, TYMED_HGLOBAL);
     hr = IDataObject_GetData(data_obj, &fmtetc, &stgmedium);
     ok(hr == S_OK, "IDataObject_GetData failed with error 0x%08x\n", hr);
-    ReleaseStgMedium(&stgmedium);
+    if(SUCCEEDED(hr)) ReleaseStgMedium(&stgmedium);
 
     InitFormatEtc(fmtetc, CF_TEXT, TYMED_HGLOBAL);
     fmtetc.dwAspect = 0xdeadbeef;
     hr = IDataObject_GetData(data_obj, &fmtetc, &stgmedium);
     ok(hr == S_OK, "IDataObject_GetData failed with error 0x%08x\n", hr);
-    ReleaseStgMedium(&stgmedium);
+    if(SUCCEEDED(hr)) ReleaseStgMedium(&stgmedium);
 
     InitFormatEtc(fmtetc, CF_TEXT, TYMED_HGLOBAL);
     fmtetc.dwAspect = DVASPECT_THUMBNAIL;
     hr = IDataObject_GetData(data_obj, &fmtetc, &stgmedium);
     ok(hr == S_OK, "IDataObject_GetData failed with error 0x%08x\n", hr);
-    ReleaseStgMedium(&stgmedium);
+    if(SUCCEEDED(hr)) ReleaseStgMedium(&stgmedium);
 
     InitFormatEtc(fmtetc, CF_TEXT, TYMED_HGLOBAL);
     fmtetc.lindex = 256;
@@ -528,11 +526,13 @@ static void test_get_clipboard(void)
     fmtetc.cfFormat = CF_RIFF;
     hr = IDataObject_GetData(data_obj, &fmtetc, &stgmedium);
     ok(hr == DV_E_FORMATETC, "IDataObject_GetData should have failed with DV_E_FORMATETC instead of 0x%08x\n", hr);
+    if(SUCCEEDED(hr)) ReleaseStgMedium(&stgmedium);
 
     InitFormatEtc(fmtetc, CF_TEXT, TYMED_HGLOBAL);
     fmtetc.tymed = TYMED_FILE;
     hr = IDataObject_GetData(data_obj, &fmtetc, &stgmedium);
     ok(hr == DV_E_TYMED, "IDataObject_GetData should have failed with DV_E_TYMED instead of 0x%08x\n", hr);
+    if(SUCCEEDED(hr)) ReleaseStgMedium(&stgmedium);
 
     ok(DataObjectImpl_GetData_calls == 6, "DataObjectImpl_GetData should have been called 6 times instead of %d times\n", DataObjectImpl_GetData_calls);
 
@@ -735,7 +735,7 @@ static void test_cf_dataobject(IDataObject *data)
                             DVTARGETDEVICE *target;
 
                             ok(fmt_ptr->fmt.ptd != NULL, "target device offset zero\n");
-                            target = (DVTARGETDEVICE*)((char*)priv + (DWORD)fmt_ptr->fmt.ptd);
+                            target = (DVTARGETDEVICE*)((char*)priv + (DWORD_PTR)fmt_ptr->fmt.ptd);
                             ok(!memcmp(target, fmt.ptd, fmt.ptd->tdSize), "target devices differ\n");
                             CoTaskMemFree(fmt.ptd);
                         }
@@ -967,7 +967,7 @@ static void test_consumer_refs(void)
     hr = IDataObject_GetData(get1, &fmt, &med);
     ok(hr == S_OK, "got %08x\n", hr);
     ok(DataObjectImpl_GetData_calls == 0, "GetData called\n");
-    ReleaseStgMedium(&med);
+    if(SUCCEEDED(hr)) ReleaseStgMedium(&med);
 
     hr = OleGetClipboard(&get2);
     ok(hr == S_OK, "got %08x\n", hr);
@@ -1004,7 +1004,7 @@ static void test_consumer_refs(void)
     hr = IDataObject_GetData(get1, &fmt, &med);
     ok(hr == S_OK, "got %08x\n", hr);
     ok(DataObjectImpl_GetData_calls == 1, "GetData not called\n");
-    ReleaseStgMedium(&med);
+    if(SUCCEEDED(hr)) ReleaseStgMedium(&med);
     refs = count_refs(src);
     ok(refs == old_refs + 1, "%d %d\n", refs, old_refs);
 
@@ -1014,7 +1014,7 @@ static void test_consumer_refs(void)
     hr = IDataObject_GetData(get1, &fmt, &med);
     ok(hr == S_OK, "got %08x\n", hr);
     ok(DataObjectImpl_GetData_calls == 1, "GetData not called\n");
-    ReleaseStgMedium(&med);
+    if(SUCCEEDED(hr)) ReleaseStgMedium(&med);
 
     refs = count_refs(src);
     ok(refs == 2, "%d\n", refs);
@@ -1047,7 +1047,7 @@ static void test_consumer_refs(void)
     hr = IDataObject_GetData(get1, &fmt, &med);
     ok(hr == S_OK, "got %08x\n", hr);
     ok(DataObjectImpl_GetData_calls == 1, "GetData not called\n");
-    ReleaseStgMedium(&med);
+    if(SUCCEEDED(hr)) ReleaseStgMedium(&med);
 
     refs = count_refs(src);
     ok(refs == 1, "%d\n", refs);
@@ -1096,23 +1096,24 @@ static void test_flushed_getdata(void)
     hr = IDataObject_GetData(get, &fmt, &med);
     ok(hr == S_OK, "got %08x\n", hr);
     ok(med.tymed == TYMED_HGLOBAL, "got %x\n", med.tymed);
-    ReleaseStgMedium(&med);
+    if(SUCCEEDED(hr)) ReleaseStgMedium(&med);
 
     InitFormatEtc(fmt, CF_TEXT, TYMED_ISTREAM);
     hr = IDataObject_GetData(get, &fmt, &med);
     ok(hr == S_OK, "got %08x\n", hr);
     ok(med.tymed == TYMED_ISTREAM, "got %x\n", med.tymed);
-    ReleaseStgMedium(&med);
+    if(SUCCEEDED(hr)) ReleaseStgMedium(&med);
 
     InitFormatEtc(fmt, CF_TEXT, TYMED_ISTORAGE);
     hr = IDataObject_GetData(get, &fmt, &med);
     ok(hr == E_FAIL, "got %08x\n", hr);
+    if(SUCCEEDED(hr)) ReleaseStgMedium(&med);
 
     InitFormatEtc(fmt, CF_TEXT, 0xffff);
     hr = IDataObject_GetData(get, &fmt, &med);
     ok(hr == S_OK, "got %08x\n", hr);
     ok(med.tymed == TYMED_HGLOBAL, "got %x\n", med.tymed);
-    ReleaseStgMedium(&med);
+    if(SUCCEEDED(hr)) ReleaseStgMedium(&med);
 
     /* stream format -> global & stream */
 
@@ -1120,23 +1121,24 @@ static void test_flushed_getdata(void)
     hr = IDataObject_GetData(get, &fmt, &med);
     ok(hr == S_OK, "got %08x\n", hr);
     ok(med.tymed == TYMED_ISTREAM, "got %x\n", med.tymed);
-    ReleaseStgMedium(&med);
+    if(SUCCEEDED(hr)) ReleaseStgMedium(&med);
 
     InitFormatEtc(fmt, cf_stream, TYMED_ISTORAGE);
     hr = IDataObject_GetData(get, &fmt, &med);
     ok(hr == E_FAIL, "got %08x\n", hr);
+    if(SUCCEEDED(hr)) ReleaseStgMedium(&med);
 
     InitFormatEtc(fmt, cf_stream, TYMED_HGLOBAL);
     hr = IDataObject_GetData(get, &fmt, &med);
     ok(hr == S_OK, "got %08x\n", hr);
     ok(med.tymed == TYMED_HGLOBAL, "got %x\n", med.tymed);
-    ReleaseStgMedium(&med);
+    if(SUCCEEDED(hr)) ReleaseStgMedium(&med);
 
     InitFormatEtc(fmt, cf_stream, 0xffff);
     hr = IDataObject_GetData(get, &fmt, &med);
     ok(hr == S_OK, "got %08x\n", hr);
     ok(med.tymed == TYMED_ISTREAM, "got %x\n", med.tymed);
-    ReleaseStgMedium(&med);
+    if(SUCCEEDED(hr)) ReleaseStgMedium(&med);
 
     /* storage format -> global, stream & storage */
 
@@ -1144,34 +1146,36 @@ static void test_flushed_getdata(void)
     hr = IDataObject_GetData(get, &fmt, &med);
     ok(hr == S_OK, "got %08x\n", hr);
     ok(med.tymed == TYMED_ISTORAGE, "got %x\n", med.tymed);
-    hr = IStorage_Stat(med.u.pstg, &stat, STATFLAG_NONAME);
-    ok(hr == S_OK, "got %08x\n", hr);
-    ok(stat.grfMode == (STGM_SHARE_EXCLUSIVE | STGM_READWRITE), "got %08x\n", stat.grfMode);
-    ReleaseStgMedium(&med);
+    if(SUCCEEDED(hr)) {
+        hr = IStorage_Stat(med.u.pstg, &stat, STATFLAG_NONAME);
+        ok(hr == S_OK, "got %08x\n", hr);
+        ok(stat.grfMode == (STGM_SHARE_EXCLUSIVE | STGM_READWRITE), "got %08x\n", stat.grfMode);
+        ReleaseStgMedium(&med);
+    }
 
     InitFormatEtc(fmt, cf_storage, TYMED_ISTREAM);
     hr = IDataObject_GetData(get, &fmt, &med);
     ok(hr == S_OK, "got %08x\n", hr);
     ok(med.tymed == TYMED_ISTREAM, "got %x\n", med.tymed);
-    ReleaseStgMedium(&med);
+    if(SUCCEEDED(hr)) ReleaseStgMedium(&med);
 
     InitFormatEtc(fmt, cf_storage, TYMED_HGLOBAL);
     hr = IDataObject_GetData(get, &fmt, &med);
     ok(hr == S_OK, "got %08x\n", hr);
     ok(med.tymed == TYMED_HGLOBAL, "got %x\n", med.tymed);
-    ReleaseStgMedium(&med);
+    if(SUCCEEDED(hr)) ReleaseStgMedium(&med);
 
     InitFormatEtc(fmt, cf_storage, TYMED_HGLOBAL | TYMED_ISTREAM);
     hr = IDataObject_GetData(get, &fmt, &med);
     ok(hr == S_OK, "got %08x\n", hr);
     ok(med.tymed == TYMED_HGLOBAL, "got %x\n", med.tymed);
-    ReleaseStgMedium(&med);
+    if(SUCCEEDED(hr)) ReleaseStgMedium(&med);
 
     InitFormatEtc(fmt, cf_storage, 0xffff);
     hr = IDataObject_GetData(get, &fmt, &med);
     ok(hr == S_OK, "got %08x\n", hr);
     ok(med.tymed == TYMED_ISTORAGE, "got %x\n", med.tymed);
-    ReleaseStgMedium(&med);
+    if(SUCCEEDED(hr)) ReleaseStgMedium(&med);
 
     /* complex format with target device */
 
@@ -1180,7 +1184,7 @@ static void test_flushed_getdata(void)
     ok(hr == DV_E_FORMATETC ||
        broken(hr == S_OK), /* win9x, winme & nt4 */
        "got %08x\n", hr);
-    if(hr == S_OK) ReleaseStgMedium(&med);
+    if(SUCCEEDED(hr)) ReleaseStgMedium(&med);
 
     InitFormatEtc(fmt, cf_another, 0xffff);
     memset(&dm, 0, sizeof(dm));
@@ -1199,7 +1203,7 @@ static void test_flushed_getdata(void)
     hr = IDataObject_GetData(get, &fmt, &med);
     ok(hr == S_OK, "got %08x\n", hr);
     ok(med.tymed == TYMED_ISTORAGE, "got %x\n", med.tymed);
-    ReleaseStgMedium(&med);
+    if(SUCCEEDED(hr)) ReleaseStgMedium(&med);
 
     HeapFree(GetProcessHeap(), 0, fmt.ptd);
 
@@ -1235,6 +1239,8 @@ static void test_nonole_clipboard(void)
     FORMATETC fmt;
     HGLOBAL h, hblob, htext;
     HENHMETAFILE emf;
+    STGMEDIUM med;
+    DWORD obj_type;
 
     r = OpenClipboard(NULL);
     ok(r, "gle %d\n", GetLastError());
@@ -1349,6 +1355,13 @@ static void test_nonole_clipboard(void)
     ok(hr == S_FALSE, "got %08x\n", hr);
     IEnumFORMATETC_Release(enum_fmt);
 
+    InitFormatEtc(fmt, CF_ENHMETAFILE, TYMED_ENHMF);
+    hr = IDataObject_GetData(get, &fmt, &med);
+    ok(hr == S_OK, "got %08x\n", hr);
+    obj_type = GetObjectType(U(med).hEnhMetaFile);
+    ok(obj_type == OBJ_ENHMETAFILE, "got %d\n", obj_type);
+    if(SUCCEEDED(hr)) ReleaseStgMedium(&med);
+
     IDataObject_Release(get);
 
     r = OpenClipboard(NULL);
index 7363f30..9d469b4 100644 (file)
 
 #include "windef.h"
 #include "winbase.h"
+#define USE_COM_CONTEXT_DEF
+#include "initguid.h"
 #include "objbase.h"
 #include "shlguid.h"
 #include "urlmon.h" /* for CLSID_FileProtocol */
 
-#include "initguid.h"
 #include "ctxtcall.h"
 
 #include "wine/test.h"
@@ -39,6 +40,7 @@ HRESULT (WINAPI * pCoInitializeEx)(LPVOID lpReserved, DWORD dwCoInit);
 HRESULT (WINAPI * pCoGetObjectContext)(REFIID riid, LPVOID *ppv);
 HRESULT (WINAPI * pCoSwitchCallContext)(IUnknown *pObject, IUnknown **ppOldObject);
 HRESULT (WINAPI * pCoGetTreatAsClass)(REFCLSID clsidOld, LPCLSID pClsidNew);
+HRESULT (WINAPI * pCoGetContextToken)(ULONG_PTR *token);
 
 #define ok_ole_success(hr, func) ok(hr == S_OK, func " failed with error 0x%08x\n", hr)
 #define ok_more_than_one_lock() ok(cLocks > 0, "Number of locks should be > 0, but actually is %d\n", cLocks)
@@ -201,6 +203,13 @@ static void test_StringFromGUID2(void)
 {
   WCHAR str[50];
   int len;
+
+  /* invalid pointer */
+  SetLastError(0xdeadbeef);
+  len = StringFromGUID2(NULL,str,50);
+  ok(len == 0, "len: %d (expected 0)\n", len);
+  ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %x\n", GetLastError());
+
   /* Test corner cases for buffer size */
   len = StringFromGUID2(&CLSID_StdFont,str,50);
   ok(len == 39, "len: %d (expected 39)\n", len);
@@ -231,7 +240,7 @@ static DWORD CALLBACK ole_initialize_thread(LPVOID pv)
     hr = pCoInitializeEx(NULL, COINIT_MULTITHREADED);
 
     SetEvent(info->wait);
-    WaitForSingleObject(info->stop, INFINITE);
+    WaitForSingleObject(info->stop, 10000);
 
     CoUninitialize();
     return hr;
@@ -252,7 +261,25 @@ static void test_CoCreateInstance(void)
     ok(pUnk == NULL, "CoCreateInstance should have changed the passed in pointer to NULL, instead of %p\n", pUnk);
 
     OleInitialize(NULL);
+
+    /* test errors returned for non-registered clsids */
+    hr = CoCreateInstance(&CLSID_non_existent, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&pUnk);
+    ok(hr == REGDB_E_CLASSNOTREG, "CoCreateInstance for non-registered inproc server should have returned REGDB_E_CLASSNOTREG instead of 0x%08x\n", hr);
+    hr = CoCreateInstance(&CLSID_non_existent, NULL, CLSCTX_INPROC_HANDLER, &IID_IUnknown, (void **)&pUnk);
+    ok(hr == REGDB_E_CLASSNOTREG, "CoCreateInstance for non-registered inproc handler should have returned REGDB_E_CLASSNOTREG instead of 0x%08x\n", hr);
+    hr = CoCreateInstance(&CLSID_non_existent, NULL, CLSCTX_LOCAL_SERVER, &IID_IUnknown, (void **)&pUnk);
+    ok(hr == REGDB_E_CLASSNOTREG, "CoCreateInstance for non-registered local server should have returned REGDB_E_CLASSNOTREG instead of 0x%08x\n", hr);
+    hr = CoCreateInstance(&CLSID_non_existent, NULL, CLSCTX_REMOTE_SERVER, &IID_IUnknown, (void **)&pUnk);
+    ok(hr == REGDB_E_CLASSNOTREG, "CoCreateInstance for non-registered remote server should have returned REGDB_E_CLASSNOTREG instead of 0x%08x\n", hr);
+
     hr = CoCreateInstance(rclsid, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&pUnk);
+    if(hr == REGDB_E_CLASSNOTREG)
+    {
+        skip("IE not installed so can't test CoCreateInstance\n");
+        OleUninitialize();
+        return;
+    }
+
     ok_ole_success(hr, "CoCreateInstance");
     if(pUnk) IUnknown_Release(pUnk);
     OleUninitialize();
@@ -272,7 +299,7 @@ static void test_CoCreateInstance(void)
     thread = CreateThread(NULL, 0, ole_initialize_thread, &info, 0, &tid);
     ok(thread != NULL, "CreateThread failed with error %d\n", GetLastError());
 
-    WaitForSingleObject(info.wait, INFINITE);
+    ok( !WaitForSingleObject(info.wait, 10000 ), "wait timed out\n" );
 
     pUnk = (IUnknown *)0xdeadbeef;
     hr = CoCreateInstance(rclsid, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&pUnk);
@@ -280,7 +307,7 @@ static void test_CoCreateInstance(void)
     if (pUnk) IUnknown_Release(pUnk);
 
     SetEvent(info.stop);
-    WaitForSingleObject(thread, INFINITE);
+    ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" );
 
     GetExitCodeThread(thread, &exitcode);
     hr = exitcode;
@@ -321,15 +348,20 @@ static void test_CoGetClassObject(void)
     thread = CreateThread(NULL, 0, ole_initialize_thread, &info, 0, &tid);
     ok(thread != NULL, "CreateThread failed with error %d\n", GetLastError());
 
-    WaitForSingleObject(info.wait, INFINITE);
+    ok( !WaitForSingleObject(info.wait, 10000), "wait timed out\n" );
 
     pUnk = (IUnknown *)0xdeadbeef;
     hr = CoGetClassObject(rclsid, CLSCTX_INPROC_SERVER, NULL, &IID_IUnknown, (void **)&pUnk);
-    ok(hr == S_OK, "CoGetClassObject should have returned S_OK instead of 0x%08x\n", hr);
-    if (pUnk) IUnknown_Release(pUnk);
+    if(hr == REGDB_E_CLASSNOTREG)
+        skip("IE not installed so can't test CoGetClassObject\n");
+    else
+    {
+        ok(hr == S_OK, "CoGetClassObject should have returned S_OK instead of 0x%08x\n", hr);
+        if (pUnk) IUnknown_Release(pUnk);
+    }
 
     SetEvent(info.stop);
-    WaitForSingleObject(thread, INFINITE);
+    ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" );
 
     GetExitCodeThread(thread, &exitcode);
     hr = exitcode;
@@ -816,6 +848,14 @@ static void test_CoRegisterClassObject(void)
     hr = CoRevokeClassObject(cookie);
     ok_ole_success(hr, "CoRevokeClassObject");
 
+    /* test whether an object that doesn't support IClassFactory can be
+     * registered for CLSCTX_LOCAL_SERVER */
+    hr = CoRegisterClassObject(&CLSID_WineOOPTest, &Test_Unknown,
+                               CLSCTX_LOCAL_SERVER, REGCLS_SINGLEUSE, &cookie);
+    ok_ole_success(hr, "CoRegisterClassObject");
+    hr = CoRevokeClassObject(cookie);
+    ok_ole_success(hr, "CoRevokeClassObject");
+
     /* test whether registered class becomes invalid when apartment is destroyed */
     hr = CoRegisterClassObject(&CLSID_WineOOPTest, (IUnknown *)&Test_ClassFactory,
                                CLSCTX_INPROC_SERVER, REGCLS_SINGLEUSE, &cookie);
@@ -935,7 +975,7 @@ static void test_registered_object_thread_affinity(void)
 
     thread = CreateThread(NULL, 0, get_class_object_thread, (LPVOID)CLSCTX_INPROC_SERVER, 0, &tid);
     ok(thread != NULL, "CreateThread failed with error %d\n", GetLastError());
-    WaitForSingleObject(thread, INFINITE);
+    ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" );
     GetExitCodeThread(thread, &exitcode);
     hr = exitcode;
     ok(hr == REGDB_E_CLASSNOTREG, "CoGetClassObject on inproc object "
@@ -948,7 +988,7 @@ static void test_registered_object_thread_affinity(void)
 
     thread = CreateThread(NULL, 0, register_class_object_thread, NULL, 0, &tid);
     ok(thread != NULL, "CreateThread failed with error %d\n", GetLastError());
-    WaitForSingleObject(thread, INFINITE);
+    ok ( !WaitForSingleObject(thread, 10000), "wait timed out\n" );
     GetExitCodeThread(thread, &exitcode);
     hr = exitcode;
     ok(hr == S_OK, "CoRegisterClassObject with same CLSID but in different thread should return S_OK instead of 0x%08x\n", hr);
@@ -964,7 +1004,7 @@ static void test_registered_object_thread_affinity(void)
 
     thread = CreateThread(NULL, 0, get_class_object_proxy_thread, (LPVOID)CLSCTX_LOCAL_SERVER, 0, &tid);
     ok(thread != NULL, "CreateThread failed with error %d\n", GetLastError());
-    while (MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, QS_ALLINPUT) == WAIT_OBJECT_0 + 1)
+    while (MsgWaitForMultipleObjects(1, &thread, FALSE, 10000, QS_ALLINPUT) == WAIT_OBJECT_0 + 1)
     {
         MSG msg;
         while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE))
@@ -985,7 +1025,7 @@ static void test_registered_object_thread_affinity(void)
 
     thread = CreateThread(NULL, 0, revoke_class_object_thread, (LPVOID)(DWORD_PTR)cookie, 0, &tid);
     ok(thread != NULL, "CreateThread failed with error %d\n", GetLastError());
-    WaitForSingleObject(thread, INFINITE);
+    ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" );
     GetExitCodeThread(thread, &exitcode);
     hr = exitcode;
     ok(hr == RPC_E_WRONG_THREAD, "CoRevokeClassObject called from different "
@@ -993,7 +1033,7 @@ static void test_registered_object_thread_affinity(void)
 
     thread = CreateThread(NULL, 0, register_class_object_thread, NULL, 0, &tid);
     ok(thread != NULL, "CreateThread failed with error %d\n", GetLastError());
-    WaitForSingleObject(thread, INFINITE);
+    ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" );
     GetExitCodeThread(thread, &exitcode);
     hr = exitcode;
     ok(hr == S_OK, "CoRegisterClassObject with same CLSID but in different "
@@ -1030,7 +1070,7 @@ static void test_CoFreeUnusedLibraries(void)
     hr = CoCreateInstance(&CLSID_FileProtocol, NULL, CLSCTX_INPROC_SERVER, &IID_IInternetProtocol, (void **)&pUnk);
     if (hr == REGDB_E_CLASSNOTREG)
     {
-        trace("IE not installed so can't run CoFreeUnusedLibraries test\n");
+        skip("IE not installed so can't run CoFreeUnusedLibraries test\n");
         CoUninitialize();
         return;
     }
@@ -1047,7 +1087,7 @@ static void test_CoFreeUnusedLibraries(void)
     ok(is_module_loaded("urlmon.dll"), "urlmon.dll should be loaded\n");
 
     thread = CreateThread(NULL, 0, free_libraries_thread, NULL, 0, &tid);
-    WaitForSingleObject(thread, INFINITE);
+    ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" );
     CloseHandle(thread);
 
     ok(is_module_loaded("urlmon.dll"), "urlmon.dll should be loaded\n");
@@ -1065,8 +1105,12 @@ static void test_CoGetObjectContext(void)
     ULONG refs;
     IComThreadingInfo *pComThreadingInfo;
     IContextCallback *pContextCallback;
+    IObjContext *pObjContext;
     APTTYPE apttype;
     THDTYPE thdtype;
+    struct info info;
+    HANDLE thread;
+    DWORD tid, exitcode;
 
     if (!pCoGetObjectContext)
     {
@@ -1078,6 +1122,36 @@ static void test_CoGetObjectContext(void)
     ok(hr == CO_E_NOTINITIALIZED, "CoGetObjectContext should have returned CO_E_NOTINITIALIZED instead of 0x%08x\n", hr);
     ok(pComThreadingInfo == NULL, "pComThreadingInfo should have been set to NULL\n");
 
+    /* show that COM doesn't have to be initialized for multi-threaded apartments if another
+       thread has already done so */
+
+    info.wait = CreateEvent(NULL, TRUE, FALSE, NULL);
+    ok(info.wait != NULL, "CreateEvent failed with error %d\n", GetLastError());
+
+    info.stop = CreateEvent(NULL, TRUE, FALSE, NULL);
+    ok(info.stop != NULL, "CreateEvent failed with error %d\n", GetLastError());
+
+    thread = CreateThread(NULL, 0, ole_initialize_thread, &info, 0, &tid);
+    ok(thread != NULL, "CreateThread failed with error %d\n", GetLastError());
+
+    ok( !WaitForSingleObject(info.wait, 10000), "wait timed out\n" );
+
+    pComThreadingInfo = NULL;
+    hr = pCoGetObjectContext(&IID_IComThreadingInfo, (void **)&pComThreadingInfo);
+    ok(hr == S_OK, "Expected S_OK, got 0x%08x\n", hr);
+    IComThreadingInfo_Release(pComThreadingInfo);
+
+    SetEvent(info.stop);
+    ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" );
+
+    GetExitCodeThread(thread, &exitcode);
+    hr = exitcode;
+    ok(hr == S_OK, "thread should have returned S_OK instead of 0x%08x\n", hr);
+
+    CloseHandle(thread);
+    CloseHandle(info.wait);
+    CloseHandle(info.stop);
+
     pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
 
     hr = pCoGetObjectContext(&IID_IComThreadingInfo, (void **)&pComThreadingInfo);
@@ -1130,6 +1204,12 @@ static void test_CoGetObjectContext(void)
         ok(refs == 0, "pContextCallback should have 0 refs instead of %d refs\n", refs);
     }
 
+    hr = pCoGetObjectContext(&IID_IObjContext, (void **)&pObjContext);
+    ok_ole_success(hr, "CoGetObjectContext");
+
+    refs = IObjContext_Release(pObjContext);
+    ok(refs == 0, "pObjContext should have 0 refs instead of %d refs\n", refs);
+
     CoUninitialize();
 }
 
@@ -1233,6 +1313,101 @@ static void test_CoGetCallContext(void)
     CoUninitialize();
 }
 
+static void test_CoGetContextToken(void)
+{
+    HRESULT hr;
+    ULONG refs;
+    ULONG_PTR token;
+    IObjContext *ctx;
+    struct info info;
+    HANDLE thread;
+    DWORD tid, exitcode;
+
+    if (!pCoGetContextToken)
+    {
+        win_skip("CoGetContextToken not present\n");
+        return;
+    }
+
+    token = 0xdeadbeef;
+    hr = pCoGetContextToken(&token);
+    ok(hr == CO_E_NOTINITIALIZED, "Expected CO_E_NOTINITIALIZED, got 0x%08x\n", hr);
+    ok(token == 0xdeadbeef, "Expected 0, got 0x%lx\n", token);
+
+    /* show that COM doesn't have to be initialized for multi-threaded apartments if another
+       thread has already done so */
+
+    info.wait = CreateEvent(NULL, TRUE, FALSE, NULL);
+    ok(info.wait != NULL, "CreateEvent failed with error %d\n", GetLastError());
+
+    info.stop = CreateEvent(NULL, TRUE, FALSE, NULL);
+    ok(info.stop != NULL, "CreateEvent failed with error %d\n", GetLastError());
+
+    thread = CreateThread(NULL, 0, ole_initialize_thread, &info, 0, &tid);
+    ok(thread != NULL, "CreateThread failed with error %d\n", GetLastError());
+
+    ok( !WaitForSingleObject(info.wait, 10000), "wait timed out\n" );
+
+    token = 0;
+    hr = pCoGetContextToken(&token);
+    ok(hr == S_OK, "Expected S_OK, got 0x%08x\n", hr);
+
+    SetEvent(info.stop);
+    ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" );
+
+    GetExitCodeThread(thread, &exitcode);
+    hr = exitcode;
+    ok(hr == S_OK, "thread should have returned S_OK instead of 0x%08x\n", hr);
+
+    CloseHandle(thread);
+    CloseHandle(info.wait);
+    CloseHandle(info.stop);
+
+    CoInitialize(NULL);
+
+    hr = pCoGetContextToken(NULL);
+    ok(hr == E_POINTER, "Expected E_POINTER, got 0x%08x\n", hr);
+
+    token = 0;
+    hr = pCoGetContextToken(&token);
+    ok(hr == S_OK, "Expected S_OK, got 0x%08x\n", hr);
+    ok(token, "Expected token != 0\n");
+
+    refs = IUnknown_AddRef((IUnknown *)token);
+    todo_wine ok(refs == 1, "Expected 1, got %u\n", refs);
+
+    hr = pCoGetObjectContext(&IID_IObjContext, (void **)&ctx);
+    ok(hr == S_OK, "Expected S_OK, got 0x%08x\n", hr);
+    todo_wine ok(ctx == (IObjContext *)token, "Expected interface pointers to be the same\n");
+
+    refs = IUnknown_AddRef((IUnknown *)ctx);
+    todo_wine ok(refs == 3, "Expected 3, got %u\n", refs);
+
+    refs = IUnknown_Release((IUnknown *)ctx);
+    todo_wine ok(refs == 2, "Expected 2, got %u\n", refs);
+
+    refs = IUnknown_Release((IUnknown *)token);
+    ok(refs == 1, "Expected 1, got %u\n", refs);
+
+    /* CoGetContextToken does not add a reference */
+    token = 0;
+    hr = pCoGetContextToken(&token);
+    ok(hr == S_OK, "Expected S_OK, got 0x%08x\n", hr);
+    ok(token, "Expected token != 0\n");
+    todo_wine ok(ctx == (IObjContext *)token, "Expected interface pointers to be the same\n");
+
+    refs = IUnknown_AddRef((IUnknown *)ctx);
+    ok(refs == 2, "Expected 1, got %u\n", refs);
+
+    refs = IUnknown_Release((IUnknown *)ctx);
+    ok(refs == 1, "Expected 0, got %u\n", refs);
+
+    refs = IUnknown_Release((IUnknown *)ctx);
+    ok(refs == 0, "Expected 0, got %u\n", refs);
+
+    CoUninitialize();
+}
+
 static void test_CoGetTreatAsClass(void)
 {
     HRESULT hr;
@@ -1259,7 +1434,7 @@ static void test_CoInitializeEx(void)
     /* Calling OleInitialize for the first time should yield S_OK even with
      * apartment already initialized by previous CoInitialize(Ex) calls. */
     hr = OleInitialize(NULL);
-    todo_wine ok(hr == S_OK, "OleInitialize failed with error 0x%08x\n", hr);
+    ok(hr == S_OK, "OleInitialize failed with error 0x%08x\n", hr);
 
     /* Subsequent calls to OleInitialize should return S_FALSE */
     hr = OleInitialize(NULL);
@@ -1276,6 +1451,7 @@ START_TEST(compobj)
     pCoGetObjectContext = (void*)GetProcAddress(hOle32, "CoGetObjectContext");
     pCoSwitchCallContext = (void*)GetProcAddress(hOle32, "CoSwitchCallContext");
     pCoGetTreatAsClass = (void*)GetProcAddress(hOle32,"CoGetTreatAsClass");
+    pCoGetContextToken = (void*)GetProcAddress(hOle32, "CoGetContextToken");
     if (!(pCoInitializeEx = (void*)GetProcAddress(hOle32, "CoInitializeEx")))
     {
         trace("You need DCOM95 installed to run this test\n");
@@ -1301,6 +1477,7 @@ START_TEST(compobj)
     test_CoFreeUnusedLibraries();
     test_CoGetObjectContext();
     test_CoGetCallContext();
+    test_CoGetContextToken();
     test_CoGetTreatAsClass();
     test_CoInitializeEx();
 }
index cae13ed..5632b24 100644 (file)
@@ -75,6 +75,157 @@ static void test_streamonhglobal(IStream *pStream)
     hr = IStream_SetSize(pStream, ull);
     ok_ole_success(hr, "IStream_SetSize");
 
+    /* IStream_Seek -- NULL position argument */
+    ll.u.HighPart = 0;
+    ll.u.LowPart = 0;
+    hr = IStream_Seek(pStream, ll, STREAM_SEEK_CUR, NULL);
+    ok_ole_success(hr, "IStream_Seek");
+
+    /* IStream_Seek -- valid position argument (seek from current position) */
+    ull.u.HighPart = 0xCAFECAFE;
+    ull.u.LowPart = 0xCAFECAFE;
+    ll.u.HighPart = 0;
+    ll.u.LowPart = 0;
+    hr = IStream_Seek(pStream, ll, STREAM_SEEK_CUR, &ull);
+    ok_ole_success(hr, "IStream_Seek");
+    ok(ull.u.LowPart == sizeof(data), "LowPart set to %d\n", ull.u.LowPart);
+    ok(ull.u.HighPart == 0, "should have set HighPart to 0 instead of %d\n", ull.u.HighPart);
+
+    /* IStream_Seek -- invalid seek argument */
+    ull.u.HighPart = 0xCAFECAFE;
+    ull.u.LowPart = 0xCAFECAFE;
+    ll.u.HighPart = 0;
+    ll.u.LowPart = 123;
+    hr = IStream_Seek(pStream, ll, STREAM_SEEK_END+1, &ull);
+    ok(hr == STG_E_SEEKERROR, "IStream_Seek should have returned STG_E_SEEKERROR instead of 0x%08x\n", hr);
+    ok(ull.u.LowPart == sizeof(data), "LowPart set to %d\n", ull.u.LowPart);
+    ok(ull.u.HighPart == 0, "should not have changed HighPart, got %d\n", ull.u.HighPart);
+
+    /* IStream_Seek -- valid position argument (seek to beginning) */
+    ull.u.HighPart = 0xCAFECAFE;
+    ull.u.LowPart = 0xCAFECAFE;
+    ll.u.HighPart = 0;
+    ll.u.LowPart = 0;
+    hr = IStream_Seek(pStream, ll, STREAM_SEEK_SET, &ull);
+    ok_ole_success(hr, "IStream_Seek");
+    ok(ull.u.LowPart == 0, "should have set LowPart to 0 instead of %d\n", ull.u.LowPart);
+    ok(ull.u.HighPart == 0, "should have set HighPart to 0 instead of %d\n", ull.u.HighPart);
+
+    /* IStream_Seek -- valid position argument (seek to end) */
+    ull.u.HighPart = 0xCAFECAFE;
+    ull.u.LowPart = 0xCAFECAFE;
+    ll.u.HighPart = 0;
+    ll.u.LowPart = 0;
+    hr = IStream_Seek(pStream, ll, STREAM_SEEK_END, &ull);
+    ok_ole_success(hr, "IStream_Seek");
+    ok(ull.u.LowPart == 0, "should have set LowPart to 0 instead of %d\n", ull.u.LowPart);
+    ok(ull.u.HighPart == 0, "should have set HighPart to 0 instead of %d\n", ull.u.HighPart);
+
+    /* IStream_Seek -- ignore HighPart in the move value (seek from current position) */
+    ll.u.HighPart = 0;
+    ll.u.LowPart = sizeof(data);
+    hr = IStream_Seek(pStream, ll, STREAM_SEEK_SET, &ull);
+    ok_ole_success(hr, "IStream_Seek");
+
+    ull.u.HighPart = 0xCAFECAFE;
+    ull.u.LowPart = 0xCAFECAFE;
+    ll.u.HighPart = -1;
+    ll.u.LowPart = 0;
+    hr = IStream_Seek(pStream, ll, STREAM_SEEK_CUR, &ull);
+    ok_ole_success(hr, "IStream_Seek");
+    ok(ull.u.LowPart == sizeof(data), "LowPart set to %d\n", ull.u.LowPart);
+    ok(ull.u.HighPart == 0, "should have set HighPart to 0 instead of %d\n", ull.u.HighPart);
+
+    /* IStream_Seek -- ignore HighPart in the move value (seek to beginning) */
+    ll.u.HighPart = 0;
+    ll.u.LowPart = sizeof(data);
+    hr = IStream_Seek(pStream, ll, STREAM_SEEK_SET, &ull);
+    ok_ole_success(hr, "IStream_Seek");
+
+    ull.u.HighPart = 0xCAFECAFE;
+    ull.u.LowPart = 0xCAFECAFE;
+    ll.u.HighPart = -1;
+    ll.u.LowPart = 0;
+    hr = IStream_Seek(pStream, ll, STREAM_SEEK_SET, &ull);
+    ok_ole_success(hr, "IStream_Seek");
+    ok(ull.u.LowPart == 0, "should have set LowPart to 0 instead of %d\n", ull.u.LowPart);
+    ok(ull.u.HighPart == 0, "should have set HighPart to 0 instead of %d\n", ull.u.HighPart);
+
+    /* IStream_Seek -- invalid LowPart value (seek from current position) */
+    ll.u.HighPart = 0;
+    ll.u.LowPart = sizeof(data);
+    hr = IStream_Seek(pStream, ll, STREAM_SEEK_SET, &ull);
+    ok_ole_success(hr, "IStream_Seek");
+
+    ull.u.HighPart = 0xCAFECAFE;
+    ull.u.LowPart = 0xCAFECAFE;
+    ll.u.HighPart = 0;
+    ll.u.LowPart = 0x80000000;
+    hr = IStream_Seek(pStream, ll, STREAM_SEEK_CUR, &ull);
+    ok(hr == STG_E_SEEKERROR, "IStream_Seek should have returned STG_E_SEEKERROR instead of 0x%08x\n", hr);
+    ok(ull.u.LowPart == sizeof(data), "LowPart set to %d\n", ull.u.LowPart);
+    ok(ull.u.HighPart == 0, "should have set HighPart to 0 instead of %d\n", ull.u.HighPart);
+
+    /* IStream_Seek -- invalid LowPart value (seek to beginning) */
+    ll.u.HighPart = 0;
+    ll.u.LowPart = sizeof(data);
+    hr = IStream_Seek(pStream, ll, STREAM_SEEK_SET, &ull);
+    ok_ole_success(hr, "IStream_Seek");
+
+    ull.u.HighPart = 0xCAFECAFE;
+    ull.u.LowPart = 0xCAFECAFE;
+    ll.u.HighPart = 0;
+    ll.u.LowPart = 0x80000000;
+    hr = IStream_Seek(pStream, ll, STREAM_SEEK_SET, &ull);
+    ok(hr == STG_E_SEEKERROR, "IStream_Seek should have returned STG_E_SEEKERROR instead of 0x%08x\n", hr);
+    ok(ull.u.LowPart == sizeof(data), "LowPart set to %d\n", ull.u.LowPart);
+    ok(ull.u.HighPart == 0, "should have set HighPart to 0 instead of %d\n", ull.u.HighPart);
+
+    /* IStream_Seek -- valid LowPart value (seek to beginning) */
+    ull.u.HighPart = 0xCAFECAFE;
+    ull.u.LowPart = 0xCAFECAFE;
+    ll.u.HighPart = 0;
+    ll.u.LowPart = 0x7FFFFFFF;
+    hr = IStream_Seek(pStream, ll, STREAM_SEEK_SET, &ull);
+    ok_ole_success(hr, "IStream_Seek");
+    ok(ull.u.LowPart == 0x7FFFFFFF, "should have set LowPart to 0x7FFFFFFF instead of %08x\n", ull.u.LowPart);
+    ok(ull.u.HighPart == 0, "should have set HighPart to 0 instead of %d\n", ull.u.HighPart);
+
+    /* IStream_Seek -- valid LowPart value (seek from current position) */
+    ll.u.HighPart = 0;
+    ll.u.LowPart = 0;
+    hr = IStream_Seek(pStream, ll, STREAM_SEEK_SET, &ull);
+    ok_ole_success(hr, "IStream_Seek");
+
+    ull.u.HighPart = 0xCAFECAFE;
+    ull.u.LowPart = 0xCAFECAFE;
+    ll.u.HighPart = 0;
+    ll.u.LowPart = 0x7FFFFFFF;
+    hr = IStream_Seek(pStream, ll, STREAM_SEEK_CUR, &ull);
+    ok_ole_success(hr, "IStream_Seek");
+    ok(ull.u.LowPart == 0x7FFFFFFF, "should have set LowPart to 0x7FFFFFFF instead of %08x\n", ull.u.LowPart);
+    ok(ull.u.HighPart == 0, "should have set HighPart to 0 instead of %d\n", ull.u.HighPart);
+
+    /* IStream_Seek -- second seek allows you to go past 0x7FFFFFFF size */
+    ull.u.HighPart = 0xCAFECAFE;
+    ull.u.LowPart = 0xCAFECAFE;
+    ll.u.HighPart = 0;
+    ll.u.LowPart = 9;
+    hr = IStream_Seek(pStream, ll, STREAM_SEEK_CUR, &ull);
+    ok_ole_success(hr, "IStream_Seek");
+    ok(ull.u.LowPart == 0x80000008, "should have set LowPart to 0x80000008 instead of %08x\n", ull.u.LowPart);
+    ok(ull.u.HighPart == 0, "should have set HighPart to 0 instead of %d\n", ull.u.HighPart);
+
+    /* IStream_Seek -- seek wraps position/size on integer overflow */
+    ull.u.HighPart = 0xCAFECAFE;
+    ull.u.LowPart = 0xCAFECAFE;
+    ll.u.HighPart = 0;
+    ll.u.LowPart = 0x7FFFFFFF;
+    hr = IStream_Seek(pStream, ll, STREAM_SEEK_CUR, &ull);
+    ok_ole_success(hr, "IStream_Seek");
+    ok(ull.u.LowPart == 0x00000007, "should have set LowPart to 0x00000007 instead of %08x\n", ull.u.LowPart);
+    ok(ull.u.HighPart == 0, "should have set HighPart to 0 instead of %d\n", ull.u.HighPart);
+
     hr = IStream_Commit(pStream, STGC_DEFAULT);
     ok_ole_success(hr, "IStream_Commit");
 
index 4082488..20b0d8b 100644 (file)
 
 #include "windef.h"
 #include "winbase.h"
-#include "initguid.h"
 #include "objbase.h"
 #include "olectl.h"
 #include "shlguid.h"
 #include "shobjidl.h"
+#include "initguid.h"
 
 #include "wine/test.h"
 
@@ -276,7 +276,7 @@ static DWORD start_host_object2(IStream *stream, REFIID riid, IUnknown *object,
     *thread = CreateThread(NULL, 0, host_object_proc, data, 0, &tid);
 
     /* wait for marshaling to complete before returning */
-    WaitForSingleObject(marshal_event, INFINITE);
+    ok( !WaitForSingleObject(marshal_event, 10000), "wait timed out\n" );
     CloseHandle(marshal_event);
 
     return tid;
@@ -293,7 +293,7 @@ static void release_host_object(DWORD tid)
 {
     HANDLE event = CreateEvent(NULL, FALSE, FALSE, NULL);
     PostThreadMessage(tid, RELEASEMARSHALDATA, 0, (LPARAM)event);
-    WaitForSingleObject(event, INFINITE);
+    ok( !WaitForSingleObject(event, 10000), "wait timed out\n" );
     CloseHandle(event);
 }
 
@@ -302,7 +302,7 @@ static void end_host_object(DWORD tid, HANDLE thread)
     BOOL ret = PostThreadMessage(tid, WM_QUIT, 0, 0);
     ok(ret, "PostThreadMessage failed with error %d\n", GetLastError());
     /* be careful of races - don't return until hosting thread has terminated */
-    WaitForSingleObject(thread, INFINITE);
+    ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" );
     CloseHandle(thread);
 }
 
@@ -843,7 +843,7 @@ static DWORD CALLBACK no_couninitialize_server_proc(LPVOID p)
 
     SetEvent(ncu_params->marshal_event);
 
-    WaitForSingleObject(ncu_params->unmarshal_event, INFINITE);
+    ok( !WaitForSingleObject(ncu_params->unmarshal_event, 10000), "wait timed out\n" );
 
     /* die without calling CoUninitialize */
 
@@ -872,7 +872,7 @@ static void test_no_couninitialize_server(void)
 
     thread = CreateThread(NULL, 0, no_couninitialize_server_proc, &ncu_params, 0, &tid);
 
-    WaitForSingleObject(ncu_params.marshal_event, INFINITE);
+    ok( !WaitForSingleObject(ncu_params.marshal_event, 10000), "wait timed out\n" );
     ok_more_than_one_lock();
 
     IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL);
@@ -883,7 +883,7 @@ static void test_no_couninitialize_server(void)
     ok_more_than_one_lock();
 
     SetEvent(ncu_params.unmarshal_event);
-    WaitForSingleObject(thread, INFINITE);
+    ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" );
 
     ok_no_locks();
 
@@ -942,7 +942,7 @@ static void test_no_couninitialize_client(void)
 
     thread = CreateThread(NULL, 0, no_couninitialize_client_proc, &ncu_params, 0, &tid);
 
-    WaitForSingleObject(thread, INFINITE);
+    ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" );
     CloseHandle(thread);
 
     ok_no_locks();
@@ -1109,7 +1109,7 @@ static DWORD CALLBACK weak_and_normal_marshal_thread_proc(void *p)
 
     SetEvent(data->hReadyEvent);
 
-    while (WAIT_OBJECT_0 + 1 == MsgWaitForMultipleObjects(1, &hQuitEvent, FALSE, INFINITE, QS_ALLINPUT))
+    while (WAIT_OBJECT_0 + 1 == MsgWaitForMultipleObjects(1, &hQuitEvent, FALSE, 10000, QS_ALLINPUT))
     {
         while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
             DispatchMessage(&msg);
@@ -1141,7 +1141,7 @@ static void test_tableweak_and_normal_marshal_and_unmarshal(void)
     ok_ole_success(hr, CreateStreamOnHGlobal);
 
     thread = CreateThread(NULL, 0, weak_and_normal_marshal_thread_proc, &data, 0, &tid);
-    WaitForSingleObject(data.hReadyEvent, INFINITE);
+    ok( !WaitForSingleObject(data.hReadyEvent, 10000), "wait timed out\n" );
     CloseHandle(data.hReadyEvent);
 
     ok_more_than_one_lock();
@@ -1170,7 +1170,7 @@ static void test_tableweak_and_normal_marshal_and_unmarshal(void)
     IStream_Release(data.pStreamNormal);
 
     SetEvent(data.hQuitEvent);
-    WaitForSingleObject(thread, INFINITE);
+    ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" );
     CloseHandle(thread);
 }
 
@@ -1257,7 +1257,7 @@ static void test_lock_object_external(void)
     IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL);
     hr = CoReleaseMarshalData(pStream);
     ok_ole_success(hr, CoReleaseMarshalData);
-    IStream_Release(pStream);
+    IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL);
 
     ok_more_than_one_lock();
 
@@ -1268,6 +1268,39 @@ static void test_lock_object_external(void)
     CoLockObjectExternal((IUnknown*)&Test_ClassFactory, FALSE, TRUE);
 
     ok_no_locks();
+
+    /* test CoLockObjectExternal releases reference to object with
+     * fLastUnlockReleases as TRUE and there are only strong references on
+     * the object */
+    CoLockObjectExternal((IUnknown*)&Test_ClassFactory, TRUE, FALSE);
+
+    ok_more_than_one_lock();
+
+    CoLockObjectExternal((IUnknown*)&Test_ClassFactory, FALSE, FALSE);
+
+    ok_no_locks();
+
+    /* test CoLockObjectExternal doesn't release the last reference to an
+     * object with fLastUnlockReleases as TRUE and there is a weak reference
+     * on the object */
+    hr = CoMarshalInterface(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHCTX_INPROC, NULL, MSHLFLAGS_TABLEWEAK);
+    ok_ole_success(hr, CoMarshalInterface);
+
+    ok_more_than_one_lock();
+
+    CoLockObjectExternal((IUnknown*)&Test_ClassFactory, TRUE, FALSE);
+
+    ok_more_than_one_lock();
+
+    CoLockObjectExternal((IUnknown*)&Test_ClassFactory, FALSE, FALSE);
+
+    ok_more_than_one_lock();
+
+    CoDisconnectObject((IUnknown*)&Test_ClassFactory, 0);
+
+    ok_no_locks();
+
+    IStream_Release(pStream);
 }
 
 /* tests disconnecting stubs */
@@ -1449,7 +1482,7 @@ static void test_proxy_used_in_wrong_thread(void)
     /* create a thread that we can misbehave in */
     thread = CreateThread(NULL, 0, bad_thread_proc, pProxy, 0, &tid2);
 
-    WaitForSingleObject(thread, INFINITE);
+    ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" );
     CloseHandle(thread);
 
     /* do release statement on Win9x that we should have done above */
@@ -1799,6 +1832,7 @@ static HRESULT WINAPI TestRE_IClassFactory_CreateInstance(
         BOOL ret = SendMessageTimeout(hwnd_app, WM_NULL, 0, 0, SMTO_BLOCK, 5000, &res);
         ok(ret, "Timed out sending a message to originating window during RPC call\n");
     }
+    *ppvObj = NULL;
     return S_FALSE;
 }
 
@@ -2071,7 +2105,7 @@ static void test_freethreadedmarshaldata(IStream *pStream, MSHCTX mshctx, void *
     {
         DWORD expected_size = round_global_size(3*sizeof(DWORD) + sizeof(GUID));
         ok(size == expected_size ||
-           broken(size == round_global_size(2*sizeof(DWORD))) /* Win9x & NT4 */,
+           broken(size == (2*sizeof(DWORD))) /* Win9x & NT4 */,
            "size should have been %d instead of %d\n", expected_size, size);
 
         ok(*(DWORD *)marshal_data == mshlflags, "expected 0x%x, but got 0x%x for mshctx\n", mshlflags, *(DWORD *)marshal_data);
@@ -2259,7 +2293,7 @@ static void reg_unreg_wine_test_class(BOOL Register)
     {
         error = RegCreateKeyEx(HKEY_CLASSES_ROOT, buffer, 0, NULL, 0, KEY_SET_VALUE, NULL, &hkey, &dwDisposition);
         ok(error == ERROR_SUCCESS, "RegCreateKeyEx failed with error %d\n", error);
-        error = RegSetValueEx(hkey, NULL, 0, REG_SZ, (const unsigned char *)"ole32.dll", strlen("ole32.dll") + 1);
+        error = RegSetValueEx(hkey, NULL, 0, REG_SZ, (const unsigned char *)"\"ole32.dll\"", strlen("\"ole32.dll\"") + 1);
         ok(error == ERROR_SUCCESS, "RegSetValueEx failed with error %d\n", error);
         RegCloseKey(hkey);
     }
@@ -2280,7 +2314,6 @@ static void test_inproc_handler(void)
     reg_unreg_wine_test_class(TRUE);
 
     hr = CoCreateInstance(&CLSID_WineTest, NULL, CLSCTX_INPROC_HANDLER, &IID_IUnknown, (void **)&pObject);
-    todo_wine
     ok_ole_success(hr, "CoCreateInstance");
 
     if (SUCCEEDED(hr))
@@ -2701,7 +2734,7 @@ static void test_local_server(void)
     ok(process != NULL, "couldn't start local server process, error was %d\n", GetLastError());
 
     ready_event = CreateEvent(NULL, FALSE, FALSE, "Wine COM Test Ready Event");
-    WaitForSingleObject(ready_event, INFINITE);
+    ok( !WaitForSingleObject(ready_event, 10000), "wait timed out\n" );
     CloseHandle(ready_event);
 
     hr = CoCreateInstance(&CLSID_WineOOPTest, NULL, CLSCTX_LOCAL_SERVER, &IID_IClassFactory, (void **)&cf);
@@ -2781,13 +2814,13 @@ static void test_globalinterfacetable(void)
         * to exit before we can return */
        thread = CreateThread(NULL, 0, get_global_interface_proc, &params, 0, &tid);
 
-       ret = MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, QS_ALLINPUT);
+       ret = MsgWaitForMultipleObjects(1, &thread, FALSE, 10000, QS_ALLINPUT);
        while (ret == WAIT_OBJECT_0 + 1)
        {
                MSG msg;
                while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
                        DispatchMessage(&msg);
-               ret = MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, QS_ALLINPUT);
+               ret = MsgWaitForMultipleObjects(1, &thread, FALSE, 10000, QS_ALLINPUT);
        }
 
        CloseHandle(thread);
index 413045a..b4e3e2f 100644 (file)
@@ -28,6 +28,7 @@
 #include "windef.h"
 #include "winbase.h"
 #include "objbase.h"
+#include "initguid.h"
 #include "comcat.h"
 #include "olectl.h"
 
@@ -1142,7 +1143,7 @@ static void test_moniker(
     LPBYTE moniker_data;
     DWORD moniker_size;
     DWORD i;
-    BOOL same = TRUE;
+    BOOL same;
     BYTE buffer[128];
     IMoniker * moniker_proxy;
     LPOLESTR display_name;
@@ -1182,6 +1183,7 @@ static void test_moniker(
         testname, sizeof_expected_moniker_comparison_data, moniker_size);
 
     /* then do a byte-by-byte comparison */
+    same = TRUE;
     for (i = 0; i < min(moniker_size, sizeof_expected_moniker_comparison_data); i++)
     {
         if (expected_moniker_comparison_data[i] != buffer[i])
@@ -1225,6 +1227,7 @@ static void test_moniker(
         testname, (DWORD)round_global_size(sizeof_expected_moniker_saved_data), moniker_size);
 
     /* then do a byte-by-byte comparison */
+    same = TRUE;
     for (i = 0; i < min(moniker_size, round_global_size(sizeof_expected_moniker_saved_data)); i++)
     {
         if (expected_moniker_saved_data[i] != moniker_data[i])
@@ -1271,6 +1274,7 @@ static void test_moniker(
         testname, (DWORD)round_global_size(sizeof_expected_moniker_marshal_data), moniker_size);
 
     /* then do a byte-by-byte comparison */
+    same = TRUE;
     if (expected_moniker_marshal_data)
     {
         for (i = 0; i < min(moniker_size, round_global_size(sizeof_expected_moniker_marshal_data)); i++)
@@ -1428,6 +1432,16 @@ static void test_file_monikers(void)
     for (i = 0; i < COUNTOF(wszFile); ++i)
     {
         int j ;
+        if (i == 2)
+        {
+            BOOL used;
+            WideCharToMultiByte( CP_ACP, WC_NO_BEST_FIT_CHARS, wszFile[i], -1, NULL, 0, NULL, &used );
+            if (used)
+            {
+                skip("string 2 doesn't round trip in codepage %u\n", GetACP() );
+                continue;
+            }
+        }
         for (j = lstrlenW(wszFile[i]); j > 0; --j)
         {
             wszFile[i][j] = 0;
@@ -1481,7 +1495,6 @@ static void test_item_moniker(void)
 
     /* IsRunning test */
     hr = IMoniker_IsRunning(moniker, NULL, NULL, NULL);
-    todo_wine
     ok(hr == E_INVALIDARG, "IMoniker_IsRunning should return E_INVALIDARG, not 0x%08x\n", hr);
 
     hr = IMoniker_IsRunning(moniker, bindctx, NULL, NULL);
@@ -1617,7 +1630,6 @@ static void test_generic_composite_moniker(void)
 
     /* IsRunning test */
     hr = IMoniker_IsRunning(moniker, NULL, NULL, NULL);
-    todo_wine
     ok(hr == E_INVALIDARG, "IMoniker_IsRunning should return E_INVALIDARG, not 0x%08x\n", hr);
 
     hr = IMoniker_IsRunning(moniker, bindctx, NULL, NULL);
@@ -1631,7 +1643,6 @@ static void test_generic_composite_moniker(void)
     todo_wine
     ok(hr == E_INVALIDARG, "IMoniker_BindToObject should return E_INVALIDARG, not 0x%08x\n", hr);
 
-    todo_wine
     hr = IMoniker_BindToStorage(moniker, bindctx, NULL, &IID_IUnknown, (void **)&unknown);
     ok(hr == E_INVALIDARG, "IMoniker_BindToStorage should return E_INVALIDARG, not 0x%08x\n", hr);
 
@@ -1853,6 +1864,75 @@ static void test_bind_context(void)
     ok(!refs, "bound object should have been destroyed, instead of having %d refs\n", refs);
 }
 
+static void test_save_load_filemoniker(void)
+{
+    IMoniker* pMk;
+    IStream* pStm;
+    HRESULT hr;
+    ULARGE_INTEGER size;
+    LARGE_INTEGER zero_pos, dead_pos, nulls_pos;
+    DWORD some_val = 0xFEDCBA98;
+    int i;
+
+    /* see FileMonikerImpl_Save docs */
+    zero_pos.QuadPart = 0;
+    dead_pos.QuadPart = sizeof(WORD) + sizeof(DWORD) + (lstrlenW(wszFileName1) + 1) + sizeof(WORD);
+    nulls_pos.QuadPart = dead_pos.QuadPart + sizeof(WORD);
+
+    /* create the stream we're going to write to */
+    hr = CreateStreamOnHGlobal(NULL, TRUE, &pStm);
+    ok_ole_success(hr, "CreateStreamOnHGlobal");
+
+    size.u.LowPart = 128;
+    hr = IStream_SetSize(pStm, size);
+    ok_ole_success(hr, "IStream_SetSize");
+
+    /* create and save a moniker */
+    hr = CreateFileMoniker(wszFileName1, &pMk);
+    ok_ole_success(hr, "CreateFileMoniker");
+
+    hr = IMoniker_Save(pMk, pStm, TRUE);
+    ok_ole_success(hr, "IMoniker_Save");
+
+    hr = IMoniker_Release(pMk);
+    ok_ole_success(hr, "IMoniker_Release");
+
+    /* overwrite the constants with various values */
+    hr = IStream_Seek(pStm, zero_pos, STREAM_SEEK_SET, NULL);
+    ok_ole_success(hr, "IStream_Seek");
+    hr = IStream_Write(pStm, &some_val, sizeof(WORD), NULL);
+    ok_ole_success(hr, "IStream_Write");
+
+    hr = IStream_Seek(pStm, dead_pos, STREAM_SEEK_SET, NULL);
+    ok_ole_success(hr, "IStream_Seek");
+    hr = IStream_Write(pStm, &some_val, sizeof(WORD), NULL);
+    ok_ole_success(hr, "IStream_Write");
+
+    hr = IStream_Seek(pStm, nulls_pos, STREAM_SEEK_SET, NULL);
+    ok_ole_success(hr, "IStream_Seek");
+    for(i = 0; i < 5; ++i){
+        hr = IStream_Write(pStm, &some_val, sizeof(DWORD), NULL);
+        ok_ole_success(hr, "IStream_Write");
+    }
+
+    /* go back to the start of the stream */
+    hr = IStream_Seek(pStm, zero_pos, STREAM_SEEK_SET, NULL);
+    ok_ole_success(hr, "IStream_Seek");
+
+    /* create a new moniker and load into it */
+    hr = CreateFileMoniker(wszFileName1, &pMk);
+    ok_ole_success(hr, "CreateFileMoniker");
+
+    hr = IMoniker_Load(pMk, pStm);
+    ok_ole_success(hr, "IMoniker_Load");
+
+    hr = IMoniker_Release(pMk);
+    ok_ole_success(hr, "IMoniker_Release");
+
+    hr = IStream_Release(pStm);
+    ok_ole_success(hr, "IStream_Release");
+}
+
 START_TEST(moniker)
 {
     CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
@@ -1866,6 +1946,7 @@ START_TEST(moniker)
     test_anti_moniker();
     test_generic_composite_moniker();
     test_pointer_moniker();
+    test_save_load_filemoniker();
 
     /* FIXME: test moniker creation funcs and parsing other moniker formats */
 
index 214c17e..f796cd7 100644 (file)
@@ -1556,6 +1556,45 @@ static void test_runnable(void)
     g_showRunnable = TRUE;
 }
 
+static HRESULT WINAPI Unknown_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
+{
+    *ppv = NULL;
+    if (IsEqualIID(riid, &IID_IUnknown)) *ppv = iface;
+    if (*ppv)
+    {
+        IUnknown_AddRef((IUnknown *)*ppv);
+        return S_OK;
+    }
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI Unknown_AddRef(IUnknown *iface)
+{
+    return 2;
+}
+
+static ULONG WINAPI Unknown_Release(IUnknown *iface)
+{
+    return 1;
+}
+
+static const IUnknownVtbl UnknownVtbl =
+{
+    Unknown_QueryInterface,
+    Unknown_AddRef,
+    Unknown_Release
+};
+
+static IUnknown Unknown2 = { &UnknownVtbl };
+
+static void test_OleLockRunning(void)
+{
+    HRESULT hr;
+
+    hr = OleLockRunning((LPUNKNOWN)&Unknown2, TRUE, FALSE);
+    ok(hr == S_OK, "OleLockRunning failed 0x%08x\n", hr);
+}
+
 START_TEST(ole2)
 {
     DWORD dwRegister;
@@ -1587,6 +1626,7 @@ START_TEST(ole2)
     test_data_cache();
     test_default_handler();
     test_runnable();
+    test_OleLockRunning();
 
     CoUninitialize();
 }
index 8260946..37e8e10 100644 (file)
@@ -178,6 +178,7 @@ static void testProps(void)
     ok(var.vt == VT_LPSTR && !lstrcmpA(U(var).pszVal, val),
      "Didn't get expected type or value for property (got type %d, value %s)\n",
      var.vt, U(var).pszVal);
+    PropVariantClear(&var);
 
     /* read clipboard format */
     spec.ulKind = PRSPEC_PROPID;
@@ -282,6 +283,7 @@ static void testProps(void)
     ok(var.vt == VT_LPSTR && !lstrcmpA(U(var).pszVal, val),
      "Didn't get expected type or value for property (got type %d, value %s)\n",
      var.vt, U(var).pszVal);
+    PropVariantClear(&var);
 
     IPropertyStorage_Release(propertyStorage);
     IPropertySetStorage_Release(propSetStorage);
@@ -364,11 +366,13 @@ static void testCodepage(void)
     ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr);
     ok(var.vt == VT_LPSTR && !strcmp(U(var).pszVal, "hi"),
      "Didn't get expected type or value for property\n");
+    PropVariantClear(&var);
     /* This seemingly non-sensical test is to show that the string is indeed
      * interpreted according to the current system code page, not according to
      * the property set's code page.  (If the latter were true, the whole
      * string would be maintained.  As it is, only the first character is.)
      */
+    var.vt = VT_LPSTR;
     U(var).pszVal = (LPSTR)wval;
     hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
     ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr);
@@ -376,6 +380,8 @@ static void testCodepage(void)
     ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr);
     ok(var.vt == VT_LPSTR && !strcmp(U(var).pszVal, "h"),
      "Didn't get expected type or value for property\n");
+    PropVariantClear(&var);
+
     /* now that a property's been set, you can't change the code page */
     spec.ulKind = PRSPEC_PROPID;
     U(spec).propid = PID_CODEPAGE;
index da2d0aa..7568a0c 100644 (file)
@@ -33,6 +33,28 @@ DEFINE_GUID( test_stg_cls, 0x88888888, 0x0425, 0x0000, 0,0,0,0,0,0,0,0);
 
 #define ok_ole_success(hr, func) ok(hr == S_OK, func " failed with error 0x%08x\n", hr)
 
+static CHAR filenameA[MAX_PATH];
+static WCHAR filename[MAX_PATH];
+
+static const char file1_nameA[] = {'c','o','p','y','t','e','s','t','A',0};
+static const WCHAR file1_name[] = {'c','o','p','y','t','e','s','t','A',0};
+static const char file2_nameA[] = {'c','o','p','y','t','e','s','t','B',0};
+static const WCHAR file2_name[] = {'c','o','p','y','t','e','s','t','B',0};
+static const WCHAR stgA_name[] = {'S','t','o','r','a','g','e','A',0};
+static const WCHAR stgB_name[] = {'S','t','o','r','a','g','e','B',0};
+static const WCHAR strmA_name[] = {'S','t','r','e','a','m','A',0};
+static const WCHAR strmB_name[] = {'S','t','r','e','a','m','B',0};
+static const WCHAR strmC_name[] = {'S','t','r','e','a','m','C',0};
+
+/* Win9x and WinMe don't have lstrcmpW */
+static int strcmp_ww(LPCWSTR strw1, LPCWSTR strw2)
+{
+    CHAR stra1[512], stra2[512];
+    WideCharToMultiByte(CP_ACP, 0, strw1, -1, stra1, sizeof(stra1), NULL, NULL);
+    WideCharToMultiByte(CP_ACP, 0, strw2, -1, stra2, sizeof(stra2), NULL, NULL);
+    return lstrcmpA(stra1, stra2);
+}
+
 static void test_hglobal_storage_stat(void)
 {
     ILockBytes *ilb = NULL;
@@ -67,16 +89,10 @@ static void test_hglobal_storage_stat(void)
 
 static void test_create_storage_modes(void)
 {
-   static const WCHAR szPrefix[] = { 's','t','g',0 };
-   static const WCHAR szDot[] = { '.',0 };
-   WCHAR filename[MAX_PATH];
    IStorage *stg = NULL;
    HRESULT r;
 
-   if(!GetTempFileNameW(szDot, szPrefix, 0, filename))
-      return;
-
-   DeleteFileW(filename);
+   DeleteFileA(filenameA);
 
    /* test with some invalid parameters */
    r = StgCreateDocfile( NULL, 0, 0, &stg);
@@ -144,7 +160,7 @@ static void test_create_storage_modes(void)
    ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile wrong error\n");
    r = StgCreateDocfile( filename, STGM_TRANSACTED | STGM_SHARE_DENY_WRITE | STGM_READ, 0, &stg);
    ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile wrong error\n");
-   ok(DeleteFileW(filename), "failed to delete file\n");
+   ok(DeleteFileA(filenameA), "failed to delete file\n");
 
    r = StgCreateDocfile( filename, STGM_SHARE_EXCLUSIVE | STGM_READWRITE |STGM_TRANSACTED, 0, &stg);
    ok(r==S_OK, "StgCreateDocfile failed\n");
@@ -161,13 +177,13 @@ static void test_create_storage_modes(void)
    ok(r==S_OK, "StgCreateDocfile failed\n");
    r = IStorage_Release(stg);
    ok(r == 0, "storage not released\n");
-   ok(DeleteFileW(filename), "failed to delete file\n");
+   ok(DeleteFileA(filenameA), "failed to delete file\n");
 
    r = StgCreateDocfile( filename, STGM_CREATE | STGM_READWRITE |STGM_TRANSACTED, 0, &stg);
    ok(r==S_OK, "StgCreateDocfile failed\n");
    r = IStorage_Release(stg);
    ok(r == 0, "storage not released\n");
-   ok(DeleteFileW(filename), "failed to delete file\n");
+   ok(DeleteFileA(filenameA), "failed to delete file\n");
 
    /* test the way excel uses StgCreateDocFile */
    r = StgCreateDocfile( filename, STGM_TRANSACTED|STGM_CREATE|STGM_SHARE_DENY_WRITE|STGM_READWRITE, 0, &stg);
@@ -176,7 +192,7 @@ static void test_create_storage_modes(void)
    {
       r = IStorage_Release(stg);
       ok(r == 0, "storage not released\n");
-      ok(DeleteFileW(filename), "failed to delete file\n");
+      ok(DeleteFileA(filenameA), "failed to delete file\n");
    }
 
    /* and the way windows media uses it ... */
@@ -186,7 +202,7 @@ static void test_create_storage_modes(void)
    {
       r = IStorage_Release(stg);
       ok(r == 0, "storage not released\n");
-      ok(DeleteFileW(filename), "failed to delete file\n");
+      ok(DeleteFileA(filenameA), "failed to delete file\n");
    }
 
    /* looks like we need STGM_TRANSACTED or STGM_CREATE */
@@ -196,7 +212,7 @@ static void test_create_storage_modes(void)
    {
       r = IStorage_Release(stg);
       ok(r == 0, "storage not released\n");
-      ok(DeleteFileW(filename), "failed to delete file\n");
+      ok(DeleteFileA(filenameA), "failed to delete file\n");
    }
 
    r = StgCreateDocfile( filename, STGM_TRANSACTED|STGM_CREATE|STGM_SHARE_DENY_WRITE|STGM_WRITE, 0, &stg);
@@ -205,7 +221,7 @@ static void test_create_storage_modes(void)
    {
       r = IStorage_Release(stg);
       ok(r == 0, "storage not released\n");
-      ok(DeleteFileW(filename), "failed to delete file\n");
+      ok(DeleteFileA(filenameA), "failed to delete file\n");
    }
 
    r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &stg);
@@ -214,7 +230,7 @@ static void test_create_storage_modes(void)
    {
       r = IStorage_Release(stg);
       ok(r == 0, "storage not released\n");
-      ok(DeleteFileW(filename), "failed to delete file\n");
+      ok(DeleteFileA(filenameA), "failed to delete file\n");
    }
 
    /* test the way msi uses StgCreateDocfile */
@@ -222,19 +238,16 @@ static void test_create_storage_modes(void)
    ok(r==S_OK, "StgCreateDocFile failed\n");
    r = IStorage_Release(stg);
    ok(r == 0, "storage not released\n");
-   ok(DeleteFileW(filename), "failed to delete file\n");
+   ok(DeleteFileA(filenameA), "failed to delete file\n");
 }
 
 static void test_storage_stream(void)
 {
     static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 };
-    static const WCHAR szPrefix[] = { 's','t','g',0 };
-    static const WCHAR szDot[] = { '.',0 };
     static const WCHAR longname[] = {
         'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',
         'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',0
     };
-    WCHAR filename[MAX_PATH];
     IStorage *stg = NULL;
     HRESULT r;
     IStream *stm = NULL;
@@ -244,10 +257,7 @@ static void test_storage_stream(void)
     ULARGE_INTEGER p;
     unsigned char buffer[0x100];
 
-    if(!GetTempFileNameW(szDot, szPrefix, 0, filename))
-        return;
-
-    DeleteFileW(filename);
+    DeleteFileA(filenameA);
 
     r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE |STGM_TRANSACTED, 0, &stg);
     ok(r==S_OK, "StgCreateDocfile failed\n");
@@ -345,15 +355,28 @@ static void test_storage_stream(void)
 
     r = IStorage_Release(stg);
     ok(r == 0, "wrong ref count\n");
-    r = DeleteFileW(filename);
+
+    /* try create some invalid streams */
+    stg = NULL;
+    stm = NULL;
+    r = StgOpenStorage(filename, NULL, STGM_READ | STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
+    ok(r == S_OK, "should succeed\n");
+    if (stg)
+    {
+        r = IStorage_OpenStream(stg, stmname, NULL, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &stm);
+        ok(r == STG_E_INVALIDFLAG, "IStorage->OpenStream should return STG_E_INVALIDFLAG instead of 0x%08x\n", r);
+        IStorage_Release(stg);
+    }
+
+    r = DeleteFileA(filenameA);
     ok(r, "file should exist\n");
 }
 
-static BOOL touch_file(LPCWSTR filename)
+static BOOL touch_file(LPCSTR filename)
 {
     HANDLE file;
 
-    file = CreateFileW(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL, 
+    file = CreateFileA(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL,
                 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
     if (file==INVALID_HANDLE_VALUE)
         return FALSE;
@@ -361,12 +384,12 @@ static BOOL touch_file(LPCWSTR filename)
     return TRUE;
 }
 
-static BOOL is_zero_length(LPCWSTR filename)
+static BOOL is_zero_length(LPCSTR filename)
 {
     HANDLE file;
     DWORD len;
 
-    file = CreateFileW(filename, GENERIC_READ, 0, NULL, 
+    file = CreateFileA(filename, GENERIC_READ, 0, NULL,
                 OPEN_EXISTING, 0, NULL);
     if (file==INVALID_HANDLE_VALUE)
         return FALSE;
@@ -375,11 +398,11 @@ static BOOL is_zero_length(LPCWSTR filename)
     return len == 0;
 }
 
-static BOOL is_existing_file(LPCWSTR filename)
+static BOOL is_existing_file(LPCSTR filename)
 {
     HANDLE file;
 
-    file = CreateFileW(filename, GENERIC_READ, 0, NULL,
+    file = CreateFileA(filename, GENERIC_READ, 0, NULL,
                        OPEN_EXISTING, 0, NULL);
     if (file==INVALID_HANDLE_VALUE)
         return FALSE;
@@ -389,20 +412,14 @@ static BOOL is_existing_file(LPCWSTR filename)
 
 static void test_open_storage(void)
 {
-    static const WCHAR szPrefix[] = { 's','t','g',0 };
     static const WCHAR szNonExist[] = { 'n','o','n','e','x','i','s','t',0 };
-    static const WCHAR szDot[] = { '.',0 };
-    WCHAR filename[MAX_PATH];
     IStorage *stg = NULL, *stg2 = NULL;
     HRESULT r;
     DWORD stgm;
 
-    if(!GetTempFileNameW(szDot, szPrefix, 0, filename))
-        return;
-
     /* try opening a zero length file - it should stay zero length */
-    DeleteFileW(filename);
-    touch_file(filename);
+    DeleteFileA(filenameA);
+    touch_file(filenameA);
     stgm = STGM_NOSCRATCH | STGM_TRANSACTED | STGM_SHARE_DENY_WRITE | STGM_READWRITE;
     r = StgOpenStorage( filename, NULL, stgm, NULL, 0, &stg);
     ok(r==STG_E_FILEALREADYEXISTS, "StgOpenStorage didn't fail\n");
@@ -410,17 +427,17 @@ static void test_open_storage(void)
     stgm = STGM_SHARE_EXCLUSIVE | STGM_READWRITE;
     r = StgOpenStorage( filename, NULL, stgm, NULL, 0, &stg);
     ok(r==STG_E_FILEALREADYEXISTS, "StgOpenStorage didn't fail\n");
-    ok(is_zero_length(filename), "file length changed\n");
+    ok(is_zero_length(filenameA), "file length changed\n");
 
-    DeleteFileW(filename);
+    DeleteFileA(filenameA);
 
     /* try opening a nonexistent file - it should not create it */
     stgm = STGM_DIRECT | STGM_SHARE_EXCLUSIVE | STGM_READWRITE;
     r = StgOpenStorage( filename, NULL, stgm, NULL, 0, &stg);
     ok(r!=S_OK, "StgOpenStorage failed: 0x%08x\n", r);
     if (r==S_OK) IStorage_Release(stg);
-    ok(!is_existing_file(filename), "StgOpenStorage should not create a file\n");
-    DeleteFileW(filename);
+    ok(!is_existing_file(filenameA), "StgOpenStorage should not create a file\n");
+    DeleteFileA(filenameA);
 
     /* create the file */
     r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE |STGM_TRANSACTED, 0, &stg);
@@ -558,24 +575,18 @@ static void test_open_storage(void)
     r = StgOpenStorage( filename, NULL, STGM_NOSNAPSHOT | STGM_PRIORITY, NULL, 0, &stg);
     ok(r == STG_E_INVALIDFLAG, "should fail\n");
 
-    r = DeleteFileW(filename);
+    r = DeleteFileA(filenameA);
     ok(r, "file didn't exist\n");
 }
 
 static void test_storage_suminfo(void)
 {
-    static const WCHAR szDot[] = { '.',0 };
-    static const WCHAR szPrefix[] = { 's','t','g',0 };
-    WCHAR filename[MAX_PATH];
     IStorage *stg = NULL;
     IPropertySetStorage *propset = NULL;
     IPropertyStorage *ps = NULL;
     HRESULT r;
 
-    if(!GetTempFileNameW(szDot, szPrefix, 0, filename))
-        return;
-
-    DeleteFileW(filename);
+    DeleteFileA(filenameA);
 
     /* create the file */
     r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | 
@@ -673,14 +684,11 @@ static void test_storage_suminfo(void)
     r = IStorage_Release(stg);
     ok(r == 0, "ref count wrong\n");
 
-    DeleteFileW(filename);
+    DeleteFileA(filenameA);
 }
 
 static void test_storage_refcount(void)
 {
-    static const WCHAR szPrefix[] = { 's','t','g',0 };
-    static const WCHAR szDot[] = { '.',0 };
-    WCHAR filename[MAX_PATH];
     IStorage *stg = NULL;
     IStorage *stgprio = NULL;
     HRESULT r;
@@ -691,10 +699,7 @@ static void test_storage_refcount(void)
     STATSTG stat;
     char buffer[10];
 
-    if(!GetTempFileNameW(szDot, szPrefix, 0, filename))
-        return;
-
-    DeleteFileW(filename);
+    DeleteFileA(filenameA);
 
     /* create the file */
     r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | 
@@ -774,7 +779,7 @@ static void test_storage_refcount(void)
 
         r = IStorage_Stat( stg2, &statstg, STATFLAG_DEFAULT );
         ok(r == S_OK, "Stat should have succeded instead of returning 0x%08x\n", r);
-        ok(!lstrcmpW(statstg.pwcsName, stgname),
+        ok(!memcmp(statstg.pwcsName, stgname, sizeof(stgname)),
             "Statstg pwcsName should have been the name the storage was created with\n");
         ok(statstg.type == STGTY_STORAGE, "Statstg type should have been STGTY_STORAGE instead of %d\n", statstg.type);
         ok(U(statstg.cbSize).LowPart == 0, "Statstg cbSize.LowPart should have been 0 instead of %d\n", U(statstg.cbSize).LowPart);
@@ -800,22 +805,16 @@ static void test_storage_refcount(void)
     }
     IStorage_Release(stgprio);
 
-    DeleteFileW(filename);
+    DeleteFileA(filenameA);
 }
 
 static void test_writeclassstg(void)
 {
-    static const WCHAR szPrefix[] = { 's','t','g',0 };
-    static const WCHAR szDot[] = { '.',0 };
-    WCHAR filename[MAX_PATH];
     IStorage *stg = NULL;
     HRESULT r;
     CLSID temp_cls;
 
-    if(!GetTempFileNameW(szDot, szPrefix, 0, filename))
-        return;
-
-    DeleteFileW(filename);
+    DeleteFileA(filenameA);
 
     /* create the file */
     r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE |
@@ -850,26 +849,22 @@ static void test_writeclassstg(void)
     r = IStorage_Release( stg );
     ok (r == 0, "storage not released\n");
 
-    DeleteFileW(filename);
+    DeleteFileA(filenameA);
 }
 
 static void test_streamenum(void)
 {
-    static const WCHAR szPrefix[] = { 's','t','g',0 };
-    static const WCHAR szDot[] = { '.',0 };
-    WCHAR filename[MAX_PATH];
     IStorage *stg = NULL;
     HRESULT r;
     IStream *stm = NULL;
     static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 };
+    static const WCHAR stmname2[] = { 'A','B','C','D','E','F','G','H','I',0 };
+    static const WCHAR stmname3[] = { 'A','B','C','D','E','F','G','H','I','J',0 };
     STATSTG stat;
     IEnumSTATSTG *ee = NULL;
     ULONG count;
 
-    if(!GetTempFileNameW(szDot, szPrefix, 0, filename))
-        return;
-
-    DeleteFileW(filename);
+    DeleteFileA(filenameA);
 
     /* create the file */
     r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE |
@@ -897,6 +892,9 @@ static void test_streamenum(void)
     ok(r==S_OK, "IEnumSTATSTG->Next failed\n");
     ok(count == 1, "count wrong\n");
 
+    if (r == S_OK)
+        CoTaskMemFree(stat.pwcsName);
+
     r = IEnumSTATSTG_Release(ee);
 
     /* second enum... destroy the stream before reading */
@@ -904,14 +902,12 @@ static void test_streamenum(void)
     ok(r==S_OK, "IStorage->EnumElements failed\n");
 
     r = IStorage_DestroyElement(stg, stmname);
-    ok(r==S_OK, "IStorage->EnumElements failed\n");
+    ok(r==S_OK, "IStorage->DestroyElement failed\n");
 
-    todo_wine {
     count = 0xf00;
     r = IEnumSTATSTG_Next(ee, 1, &stat, &count);
     ok(r==S_FALSE, "IEnumSTATSTG->Next failed\n");
     ok(count == 0, "count wrong\n");
-    }
 
     /* reset and try again */
     r = IEnumSTATSTG_Reset(ee);
@@ -922,49 +918,161 @@ static void test_streamenum(void)
     ok(r==S_FALSE, "IEnumSTATSTG->Next failed\n");
     ok(count == 0, "count wrong\n");
 
-    r = IEnumSTATSTG_Release(ee);
-    ok (r == 0, "enum not released\n");
+    /* add a stream before reading */
+    r = IEnumSTATSTG_Reset(ee);
+    ok(r==S_OK, "IEnumSTATSTG->Reset failed\n");
+
+    r = IStorage_CreateStream(stg, stmname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm );
+    ok(r==S_OK, "IStorage->CreateStream failed\n");
+
+    r = IStream_Release(stm);
+
+    count = 0xf00;
+    r = IEnumSTATSTG_Next(ee, 1, &stat, &count);
+    ok(r==S_OK, "IEnumSTATSTG->Next failed\n");
+    ok(count == 1, "count wrong\n");
+
+    if (r == S_OK)
+    {
+        ok(lstrcmpiW(stat.pwcsName, stmname) == 0, "expected CONTENTS, got %s\n", wine_dbgstr_w(stat.pwcsName));
+        CoTaskMemFree(stat.pwcsName);
+    }
+
+    r = IStorage_CreateStream(stg, stmname2, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm );
+    ok(r==S_OK, "IStorage->CreateStream failed\n");
+
+    r = IStream_Release(stm);
+
+    count = 0xf00;
+    r = IEnumSTATSTG_Next(ee, 1, &stat, &count);
+    ok(r==S_OK, "IEnumSTATSTG->Next failed\n");
+    ok(count == 1, "count wrong\n");
+
+    if (r == S_OK)
+    {
+        ok(lstrcmpiW(stat.pwcsName, stmname2) == 0, "expected ABCDEFGHI, got %s\n", wine_dbgstr_w(stat.pwcsName));
+        CoTaskMemFree(stat.pwcsName);
+    }
+
+    /* delete previous and next stream after reading */
+    r = IStorage_CreateStream(stg, stmname3, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm );
+    ok(r==S_OK, "IStorage->CreateStream failed\n");
+
+    r = IStream_Release(stm);
+
+    r = IEnumSTATSTG_Reset(ee);
+    ok(r==S_OK, "IEnumSTATSTG->Reset failed\n");
+
+    count = 0xf00;
+    r = IEnumSTATSTG_Next(ee, 1, &stat, &count);
+    ok(r==S_OK, "IEnumSTATSTG->Next failed\n");
+    ok(count == 1, "count wrong\n");
+
+    if (r == S_OK)
+    {
+        ok(lstrcmpiW(stat.pwcsName, stmname) == 0, "expected CONTENTS, got %s\n", wine_dbgstr_w(stat.pwcsName));
+        CoTaskMemFree(stat.pwcsName);
+    }
+
+    r = IStorage_DestroyElement(stg, stmname);
+    ok(r==S_OK, "IStorage->DestroyElement failed\n");
+
+    r = IStorage_DestroyElement(stg, stmname2);
+    ok(r==S_OK, "IStorage->DestroyElement failed\n");
+
+    count = 0xf00;
+    r = IEnumSTATSTG_Next(ee, 1, &stat, &count);
+    ok(r==S_OK, "IEnumSTATSTG->Next failed\n");
+    ok(count == 1, "count wrong\n");
+
+    if (r == S_OK)
+    {
+        ok(lstrcmpiW(stat.pwcsName, stmname3) == 0, "expected ABCDEFGHIJ, got %s\n", wine_dbgstr_w(stat.pwcsName));
+        CoTaskMemFree(stat.pwcsName);
+    }
 
     r = IStorage_Release( stg );
-    ok (r == 0, "storage not released\n");
+    todo_wine ok (r == 0, "storage not released\n");
+
+    /* enumerator is still valid and working after the storage is released */
+    r = IEnumSTATSTG_Reset(ee);
+    ok(r==S_OK, "IEnumSTATSTG->Reset failed\n");
+
+    count = 0xf00;
+    r = IEnumSTATSTG_Next(ee, 1, &stat, &count);
+    ok(r==S_OK, "IEnumSTATSTG->Next failed\n");
+    ok(count == 1, "count wrong\n");
+
+    if (r == S_OK)
+    {
+        ok(lstrcmpiW(stat.pwcsName, stmname3) == 0, "expected ABCDEFGHIJ, got %s\n", wine_dbgstr_w(stat.pwcsName));
+        CoTaskMemFree(stat.pwcsName);
+    }
+
+    /* the storage is left open until the enumerator is freed */
+    r = StgOpenStorage( filename, NULL, STGM_SHARE_EXCLUSIVE |
+                            STGM_READWRITE |STGM_TRANSACTED, NULL, 0, &stg);
+    ok(r==STG_E_SHAREVIOLATION ||
+       r==STG_E_LOCKVIOLATION, /* XP-SP2/W2K3-SP1 and below */
+       "StgCreateDocfile failed, res=%x\n", r);
+
+    r = IEnumSTATSTG_Release(ee);
+    ok (r == 0, "enum not released\n");
 
-    DeleteFileW(filename);
+    DeleteFileA(filenameA);
 }
 
 static void test_transact(void)
 {
-    static const WCHAR szPrefix[] = { 's','t','g',0 };
-    static const WCHAR szDot[] = { '.',0 };
-    WCHAR filename[MAX_PATH];
-    IStorage *stg = NULL, *stg2 = NULL;
+    IStorage *stg = NULL, *stg2 = NULL, *stg3 = NULL;
     HRESULT r;
     IStream *stm = NULL;
     static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 };
     static const WCHAR stmname2[] = { 'F','O','O',0 };
+    static const WCHAR stgname[] = { 'P','E','R','M','S','T','G',0 };
+    static const WCHAR stgname2[] = { 'T','E','M','P','S','T','G',0 };
 
-    if(!GetTempFileNameW(szDot, szPrefix, 0, filename))
-        return;
-
-    DeleteFileW(filename);
+    DeleteFileA(filenameA);
 
     /* create the file */
     r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | 
                             STGM_READWRITE |STGM_TRANSACTED, 0, &stg);
     ok(r==S_OK, "StgCreateDocfile failed\n");
 
-    /* now create a stream, but don't commit it */
+    /* commit a new stream and storage */
     r = IStorage_CreateStream(stg, stmname2, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm );
     ok(r==S_OK, "IStorage->CreateStream failed\n");
 
     r = IStream_Write(stm, "this is stream 1\n", 16, NULL);
     ok(r==S_OK, "IStream->Write failed\n");
 
-    r = IStream_Release(stm);
+    IStream_Release(stm);
+
+    r = IStorage_CreateStorage(stg, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg2);
+    ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r);
+
+    if (r == S_OK)
+    {
+        /* Create two substorages but only commit one */
+        r = IStorage_CreateStorage(stg2, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg3);
+        ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r);
 
-    r = IStorage_Commit(stg, 0);
-    ok(r==S_OK, "IStorage->Commit failed\n");
+        if (r == S_OK)
+            IStorage_Release(stg3);
 
-    /* now create a stream, but don't commit it */
+        r = IStorage_Commit(stg, 0);
+        ok(r==S_OK, "IStorage->Commit failed\n");
+
+        r = IStorage_CreateStorage(stg2, stgname2, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg3);
+        ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r);
+
+        if (r == S_OK)
+            IStorage_Release(stg3);
+
+        IStorage_Release(stg2);
+    }
+
+    /* now create a stream and storage, but don't commit them */
     stm = NULL;
     r = IStorage_CreateStream(stg, stmname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm );
     ok(r==S_OK, "IStorage->CreateStream failed\n");
@@ -972,10 +1080,17 @@ static void test_transact(void)
     r = IStream_Write(stm, "this is stream 2\n", 16, NULL);
     ok(r==S_OK, "IStream->Write failed\n");
 
+    /* IStream::Commit does nothing for OLE storage streams */
     r = IStream_Commit(stm, STGC_ONLYIFCURRENT | STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE);
     ok(r==S_OK, "IStream->Commit failed\n");
 
-    r = IStream_Release(stm);
+    r = IStorage_CreateStorage(stg, stgname2, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg2);
+    ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r);
+
+    if (r == S_OK)
+        IStorage_Release(stg2);
+
+    IStream_Release(stm);
 
     IStorage_Release(stg);
 
@@ -999,276 +1114,1727 @@ static void test_transact(void)
     r = IStorage_OpenStorage(stg, stmname, NULL, STGM_TRANSACTED|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg2 );
     ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStream failed %08x\n", r);
 
-    todo_wine {
     r = IStorage_OpenStream(stg, stmname, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &stm );
     ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStream should fail %08x\n", r);
-    }
-    if (stm)
-        r = IStream_Release(stm);
+    if (r == S_OK)
+        IStream_Release(stm);
+
+    r = IStorage_OpenStorage(stg, stgname2, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg2 );
+    ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStorage should fail %08x\n", r);
+    if (r == S_OK)
+        IStorage_Release(stg2);
 
     r = IStorage_OpenStorage(stg, stmname2, NULL, STGM_TRANSACTED|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg2 );
     ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStream failed %08x\n", r);
 
     r = IStorage_OpenStream(stg, stmname2, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &stm );
-    ok(r==S_OK, "IStorage->OpenStream should fail %08x\n", r);
-    if (stm)
-        r = IStream_Release(stm);
+    ok(r==S_OK, "IStorage->OpenStream should succeed %08x\n", r);
+    if (r == S_OK)
+        IStream_Release(stm);
+
+    r = IStorage_OpenStorage(stg, stgname, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg2 );
+    ok(r==S_OK, "IStorage->OpenStorage should succeed %08x\n", r);
+    if (r == S_OK)
+    {
+        r = IStorage_OpenStorage(stg2, stgname, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg3 );
+        ok(r==S_OK, "IStorage->OpenStorage should succeed %08x\n", r);
+        if (r == S_OK)
+            IStorage_Release(stg3);
+
+        r = IStorage_OpenStorage(stg2, stgname2, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg3 );
+        ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStorage should fail %08x\n", r);
+        if (r == S_OK)
+            IStorage_Release(stg3);
+
+        IStorage_Release(stg2);
+    }
 
     IStorage_Release(stg);
 
-    r = DeleteFileW(filename);
+    r = DeleteFileA(filenameA);
     ok( r == TRUE, "deleted file\n");
 }
 
-static void test_ReadClassStm(void)
+static void test_substorage_share(void)
 {
-    CLSID clsid;
-    HRESULT hr;
-    IStream *pStream;
-    static const LARGE_INTEGER llZero;
+    IStorage *stg, *stg2, *stg3;
+    IStream *stm, *stm2;
+    HRESULT r;
+    static const WCHAR stgname[] = { 'P','E','R','M','S','T','G',0 };
+    static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 };
+    static const WCHAR othername[] = { 'N','E','W','N','A','M','E',0 };
 
-    hr = ReadClassStm(NULL, &clsid);
-    ok(hr == E_INVALIDARG, "ReadClassStm should have returned E_INVALIDARG instead of 0x%08x\n", hr);
+    DeleteFileA(filenameA);
 
-    hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream);
-    ok_ole_success(hr, "CreateStreamOnHGlobal");
-    hr = WriteClassStm(pStream, &test_stg_cls);
-    ok_ole_success(hr, "WriteClassStm");
+    /* create the file */
+    r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE |
+                            STGM_READWRITE, 0, &stg);
+    ok(r==S_OK, "StgCreateDocfile failed\n");
 
-    hr = ReadClassStm(pStream, NULL);
-    ok(hr == E_INVALIDARG, "ReadClassStm should have returned E_INVALIDARG instead of 0x%08x\n", hr);
+    /* create a read/write storage and try to open it again */
+    r = IStorage_CreateStorage(stg, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg2);
+    ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r);
 
-    /* test not rewound stream */
-    hr = ReadClassStm(pStream, &clsid);
-    ok(hr == STG_E_READFAULT, "ReadClassStm should have returned STG_E_READFAULT instead of 0x%08x\n", hr);
-    ok(IsEqualCLSID(&clsid, &CLSID_NULL), "clsid should have been zeroed\n");
+    if (r == S_OK)
+    {
+        r = IStorage_OpenStorage(stg, stgname, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg3);
+        ok(r==STG_E_ACCESSDENIED, "IStorage->OpenStorage should fail %08x\n", r);
 
-    hr = IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL);
-    ok_ole_success(hr, "IStream_Seek");
-    hr = ReadClassStm(pStream, &clsid);
-    ok_ole_success(hr, "ReadClassStm");
-    ok(IsEqualCLSID(&clsid, &test_stg_cls), "clsid should have been set to CLSID_WineTest\n");
-}
+        if (r == S_OK)
+            IStorage_Release(stg3);
 
-struct access_res
-{
-    BOOL gothandle;
-    DWORD lasterr;
-    BOOL ignore;
-};
+        r = IStorage_OpenStorage(stg, stgname, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, 0, &stg3);
+        ok(r==STG_E_ACCESSDENIED, "IStorage->OpenStorage should fail %08x\n", r);
 
-static const struct access_res create[16] =
-{
-    { TRUE, ERROR_SUCCESS, TRUE },
-    { TRUE, ERROR_SUCCESS, TRUE },
-    { TRUE, ERROR_SUCCESS, FALSE },
-    { TRUE, ERROR_SUCCESS, FALSE },
-    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
-    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
-    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
-    { TRUE, ERROR_SUCCESS, FALSE },
-    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
-    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
-    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
-    { TRUE, ERROR_SUCCESS, TRUE },
-    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
-    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
-    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
-    { TRUE, ERROR_SUCCESS, TRUE }
-};
+        if (r == S_OK)
+            IStorage_Release(stg3);
 
-static const struct access_res create_commit[16] =
-{
-    { TRUE, ERROR_SUCCESS, TRUE },
-    { TRUE, ERROR_SUCCESS, TRUE },
-    { TRUE, ERROR_SUCCESS, FALSE },
-    { TRUE, ERROR_SUCCESS, FALSE },
-    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
-    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
-    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
-    { TRUE, ERROR_SUCCESS, FALSE },
-    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
-    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
-    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
-    { TRUE, ERROR_SUCCESS, TRUE },
-    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
-    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
-    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
-    { TRUE, ERROR_SUCCESS, TRUE }
-};
+        /* cannot rename the storage while it's open */
+        r = IStorage_RenameElement(stg, stgname, othername);
+        ok(r==STG_E_ACCESSDENIED, "IStorage->RenameElement should fail %08x\n", r);
+        if (SUCCEEDED(r)) IStorage_RenameElement(stg, othername, stgname);
 
-static const struct access_res create_close[16] =
-{
-    { TRUE, ERROR_SUCCESS, FALSE },
-    { TRUE, ERROR_SUCCESS, FALSE },
-    { TRUE, ERROR_SUCCESS, FALSE },
-    { TRUE, ERROR_SUCCESS, FALSE },
-    { TRUE, ERROR_SUCCESS, FALSE },
-    { TRUE, ERROR_SUCCESS, FALSE },
-    { TRUE, ERROR_SUCCESS, FALSE },
-    { TRUE, ERROR_SUCCESS, FALSE },
-    { TRUE, ERROR_SUCCESS, FALSE },
-    { TRUE, ERROR_SUCCESS, FALSE },
-    { TRUE, ERROR_SUCCESS, FALSE },
-    { TRUE, ERROR_SUCCESS, FALSE },
-    { TRUE, ERROR_SUCCESS, FALSE },
-    { TRUE, ERROR_SUCCESS, FALSE },
-    { TRUE, ERROR_SUCCESS, FALSE },
-    { TRUE, ERROR_SUCCESS }
-};
+        /* destroying an object while it's open invalidates it */
+        r = IStorage_DestroyElement(stg, stgname);
+        ok(r==S_OK, "IStorage->DestroyElement failed, hr=%08x\n", r);
 
-static void _test_file_access(LPCSTR file, const struct access_res *ares, DWORD line)
-{
-    DWORD access = 0, share = 0;
-    DWORD lasterr;
-    HANDLE hfile;
-    int i, j, idx = 0;
+        r = IStorage_CreateStream(stg2, stmname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
+        ok(r==STG_E_REVERTED, "IStorage->CreateStream failed, hr=%08x\n", r);
 
-    for (i = 0; i < 4; i++)
-    {
-        if (i == 0) access = 0;
-        if (i == 1) access = GENERIC_READ;
-        if (i == 2) access = GENERIC_WRITE;
-        if (i == 3) access = GENERIC_READ | GENERIC_WRITE;
+        if (r == S_OK)
+            IStorage_Release(stm);
 
-        for (j = 0; j < 4; j++)
-        {
-            if (ares[idx].ignore)
-                continue;
+        IStorage_Release(stg2);
+    }
 
-            if (j == 0) share = 0;
-            if (j == 1) share = FILE_SHARE_READ;
-            if (j == 2) share = FILE_SHARE_WRITE;
-            if (j == 3) share = FILE_SHARE_READ | FILE_SHARE_WRITE;
+    /* create a read/write stream and try to open it again */
+    r = IStorage_CreateStream(stg, stmname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
+    ok(r==S_OK, "IStorage->CreateStream failed, hr=%08x\n", r);
 
-            SetLastError(0xdeadbeef);
-            hfile = CreateFileA(file, access, share, NULL, OPEN_EXISTING,
-                                FILE_ATTRIBUTE_NORMAL, 0);
-            lasterr = GetLastError();
+    if (r == S_OK)
+    {
+        r = IStorage_OpenStream(stg, stmname, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stm2);
+        ok(r==STG_E_ACCESSDENIED, "IStorage->OpenStream should fail %08x\n", r);
 
-            ok((hfile != INVALID_HANDLE_VALUE) == ares[idx].gothandle,
-               "(%d, handle, %d): Expected %d, got %d\n",
-               line, idx, ares[idx].gothandle,
-               (hfile != INVALID_HANDLE_VALUE));
+        if (r == S_OK)
+            IStorage_Release(stm2);
+
+        r = IStorage_OpenStream(stg, stmname, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm2);
+        ok(r==STG_E_ACCESSDENIED, "IStorage->OpenStream should fail %08x\n", r);
+
+        if (r == S_OK)
+            IStorage_Release(stm2);
+
+        /* cannot rename the stream while it's open */
+        r = IStorage_RenameElement(stg, stmname, othername);
+        ok(r==STG_E_ACCESSDENIED, "IStorage->RenameElement should fail %08x\n", r);
+        if (SUCCEEDED(r)) IStorage_RenameElement(stg, othername, stmname);
+
+        /* destroying an object while it's open invalidates it */
+        r = IStorage_DestroyElement(stg, stmname);
+        ok(r==S_OK, "IStorage->DestroyElement failed, hr=%08x\n", r);
+
+        r = IStream_Write(stm, "this shouldn't work\n", 20, NULL);
+        ok(r==STG_E_REVERTED, "IStream_Write should fail %08x\n", r);
+
+        IStorage_Release(stm);
+    }
+
+    IStorage_Release(stg);
+
+    r = DeleteFileA(filenameA);
+    ok( r == TRUE, "deleted file\n");
+}
+
+static void test_revert(void)
+{
+    IStorage *stg = NULL, *stg2 = NULL, *stg3 = NULL;
+    HRESULT r;
+    IStream *stm = NULL, *stm2 = NULL;
+    static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 };
+    static const WCHAR stmname2[] = { 'F','O','O',0 };
+    static const WCHAR stgname[] = { 'P','E','R','M','S','T','G',0 };
+    static const WCHAR stgname2[] = { 'T','E','M','P','S','T','G',0 };
+    STATSTG statstg;
+
+    DeleteFileA(filenameA);
+
+    /* create the file */
+    r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE |
+                            STGM_READWRITE |STGM_TRANSACTED, 0, &stg);
+    ok(r==S_OK, "StgCreateDocfile failed\n");
+
+    /* commit a new stream and storage */
+    r = IStorage_CreateStream(stg, stmname2, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm );
+    ok(r==S_OK, "IStorage->CreateStream failed\n");
+
+    r = IStream_Write(stm, "this is stream 1\n", 16, NULL);
+    ok(r==S_OK, "IStream->Write failed\n");
+
+    r = IStorage_CreateStorage(stg, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg2);
+    ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r);
+
+    if (r == S_OK)
+    {
+        /* Create two substorages but only commit one */
+        r = IStorage_CreateStorage(stg2, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg3);
+        ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r);
+
+        if (r == S_OK)
+            IStorage_Release(stg3);
+
+        r = IStorage_Commit(stg, 0);
+        ok(r==S_OK, "IStorage->Commit failed\n");
+
+        r = IStorage_CreateStorage(stg2, stgname2, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg3);
+        ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r);
+
+        if (r == S_OK)
+            IStorage_Release(stg3);
+    }
+
+    /* now create a stream and storage, then revert */
+    r = IStorage_CreateStream(stg, stmname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm2 );
+    ok(r==S_OK, "IStorage->CreateStream failed\n");
+
+    r = IStream_Write(stm2, "this is stream 2\n", 16, NULL);
+    ok(r==S_OK, "IStream->Write failed\n");
+
+    r = IStorage_CreateStorage(stg, stgname2, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg3);
+    ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r);
+
+    r = IStorage_Revert(stg);
+
+    /* all open objects become invalid */
+    r = IStream_Write(stm, "this shouldn't work\n", 20, NULL);
+    ok(r==STG_E_REVERTED, "IStream_Write should fail %08x\n", r);
+
+    r = IStream_Write(stm2, "this shouldn't work\n", 20, NULL);
+    ok(r==STG_E_REVERTED, "IStream_Write should fail %08x\n", r);
+
+    r = IStorage_Stat(stg2, &statstg, STATFLAG_NONAME);
+    ok(r==STG_E_REVERTED, "IStorage_Stat should fail %08x\n", r);
+
+    r = IStorage_Stat(stg3, &statstg, STATFLAG_NONAME);
+    ok(r==STG_E_REVERTED, "IStorage_Stat should fail %08x\n", r);
+
+    IStream_Release(stm);
+    IStream_Release(stm2);
+    IStorage_Release(stg2);
+    IStorage_Release(stg3);
+
+    r = IStorage_OpenStream(stg, stmname, NULL, STGM_SHARE_DENY_NONE|STGM_READ, 0, &stm );
+    ok(r==STG_E_INVALIDFLAG, "IStorage->OpenStream failed %08x\n", r);
+
+    r = IStorage_OpenStream(stg, stmname, NULL, STGM_DELETEONRELEASE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &stm );
+    ok(r==STG_E_INVALIDFUNCTION, "IStorage->OpenStream failed %08x\n", r);
+
+    r = IStorage_OpenStream(stg, stmname, NULL, STGM_TRANSACTED|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &stm );
+    ok(r==STG_E_INVALIDFUNCTION, "IStorage->OpenStream failed %08x\n", r);
+
+    r = IStorage_OpenStorage(stg, stmname, NULL, STGM_TRANSACTED|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg2 );
+    ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStream failed %08x\n", r);
+
+    r = IStorage_OpenStream(stg, stmname, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &stm );
+    ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStream should fail %08x\n", r);
+    if (r == S_OK)
+        IStream_Release(stm);
+
+    r = IStorage_OpenStorage(stg, stgname2, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg2 );
+    ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStorage should fail %08x\n", r);
+    if (r == S_OK)
+        IStorage_Release(stg2);
+
+    r = IStorage_OpenStorage(stg, stmname2, NULL, STGM_TRANSACTED|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg2 );
+    ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStream failed %08x\n", r);
+
+    r = IStorage_OpenStream(stg, stmname2, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &stm );
+    ok(r==S_OK, "IStorage->OpenStream should succeed %08x\n", r);
+    if (r == S_OK)
+        IStream_Release(stm);
+
+    r = IStorage_OpenStorage(stg, stgname, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg2 );
+    ok(r==S_OK, "IStorage->OpenStorage should succeed %08x\n", r);
+    if (r == S_OK)
+    {
+        r = IStorage_OpenStorage(stg2, stgname, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg3 );
+        ok(r==S_OK, "IStorage->OpenStorage should succeed %08x\n", r);
+        if (r == S_OK)
+            IStorage_Release(stg3);
+
+        r = IStorage_OpenStorage(stg2, stgname2, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg3 );
+        ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStorage should fail %08x\n", r);
+        if (r == S_OK)
+            IStorage_Release(stg3);
+
+        IStorage_Release(stg2);
+    }
+
+    IStorage_Release(stg);
+
+    r = DeleteFileA(filenameA);
+    ok( r == TRUE, "deleted file\n");
+
+    /* Revert only invalidates objects in transacted mode */
+    r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE |
+                            STGM_READWRITE, 0, &stg);
+    ok(r==S_OK, "StgCreateDocfile failed\n");
+
+    r = IStorage_CreateStream(stg, stmname2, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm );
+    ok(r==S_OK, "IStorage->CreateStream failed\n");
+
+    r = IStorage_Revert(stg);
+    ok(r==S_OK, "IStorage->Revert failed %08x\n", r);
+
+    r = IStream_Write(stm, "this works\n", 11, NULL);
+    ok(r==S_OK, "IStream_Write should succeed %08x\n", r);
+
+    IStream_Release(stm);
+    IStream_Release(stg);
+
+    r = DeleteFileA(filenameA);
+    ok( r == TRUE, "deleted file\n");
+}
+
+static void test_parent_free(void)
+{
+    IStorage *stg = NULL, *stg2 = NULL, *stg3 = NULL;
+    HRESULT r;
+    IStream *stm = NULL;
+    static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 };
+    static const WCHAR stgname[] = { 'P','E','R','M','S','T','G',0 };
+    ULONG ref;
+    STATSTG statstg;
+
+    DeleteFileA(filenameA);
+
+    /* create the file */
+    r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE |
+                            STGM_READWRITE |STGM_TRANSACTED, 0, &stg);
+    ok(r==S_OK, "StgCreateDocfile failed\n");
+
+    /* create a new storage */
+    r = IStorage_CreateStorage(stg, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg2);
+    ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r);
+
+    if (r == S_OK)
+    {
+        /* now create a stream inside the new storage */
+        r = IStorage_CreateStream(stg2, stmname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm );
+        ok(r==S_OK, "IStorage->CreateStream failed\n");
+
+        if (r == S_OK)
+        {
+            /* create a storage inside the new storage */
+            r = IStorage_CreateStorage(stg2, stgname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stg3 );
+            ok(r==S_OK, "IStorage->CreateStorage failed\n");
+        }
+
+        /* free the parent */
+        ref = IStorage_Release(stg2);
+        ok(ref == 0, "IStorage still has %u references\n", ref);
+
+        /* child objects are invalid */
+        if (r == S_OK)
+        {
+            r = IStream_Write(stm, "this should fail\n", 17, NULL);
+            ok(r==STG_E_REVERTED, "IStream->Write sould fail, hr=%x\n", r);
+
+            IStream_Release(stm);
+
+            r = IStorage_Stat(stg3, &statstg, STATFLAG_NONAME);
+            ok(r==STG_E_REVERTED, "IStorage_Stat should fail %08x\n", r);
+
+            r = IStorage_SetStateBits(stg3, 1, 1);
+            ok(r==STG_E_REVERTED, "IStorage_Stat should fail %08x\n", r);
+
+            IStorage_Release(stg3);
+        }
+    }
+
+    IStorage_Release(stg);
+
+    r = DeleteFileA(filenameA);
+    ok( r == TRUE, "deleted file\n");
+}
+
+static void test_nonroot_transacted(void)
+{
+    IStorage *stg = NULL, *stg2 = NULL, *stg3 = NULL;
+    HRESULT r;
+    IStream *stm = NULL;
+    static const WCHAR stgname[] = { 'P','E','R','M','S','T','G',0 };
+    static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 };
+    static const WCHAR stmname2[] = { 'F','O','O',0 };
+
+    DeleteFileA(filenameA);
+
+    /* create a transacted file */
+    r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE |
+                            STGM_READWRITE |STGM_TRANSACTED, 0, &stg);
+    ok(r==S_OK, "StgCreateDocfile failed\n");
+
+    /* create a transacted substorage */
+    r = IStorage_CreateStorage(stg, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_TRANSACTED, 0, 0, &stg2);
+    ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r);
+
+    if (r == S_OK)
+    {
+        /* create and commit stmname */
+        r = IStorage_CreateStream(stg2, stmname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm );
+        ok(r==S_OK, "IStorage->CreateStream failed\n");
+        if (r == S_OK)
+            IStream_Release(stm);
+
+        IStorage_Commit(stg2, 0);
+
+        /* create and revert stmname2 */
+        r = IStorage_CreateStream(stg2, stmname2, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm );
+        ok(r==S_OK, "IStorage->CreateStream failed\n");
+        if (r == S_OK)
+            IStream_Release(stm);
+
+        IStorage_Revert(stg2);
+
+        /* check that Commit and Revert really worked */
+        r = IStorage_OpenStream(stg2, stmname, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &stm );
+        ok(r==S_OK, "IStorage->OpenStream should succeed %08x\n", r);
+        if (r == S_OK)
+            IStream_Release(stm);
+
+        r = IStorage_OpenStream(stg2, stmname2, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &stm );
+        ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStream should fail %08x\n", r);
+        if (r == S_OK)
+            IStream_Release(stm);
+
+        IStorage_Release(stg2);
+    }
+
+    /* create a read-only transacted substorage */
+    r = IStorage_OpenStorage(stg, stgname, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE | STGM_TRANSACTED, NULL, 0, &stg2);
+    ok(r==S_OK, "IStorage->OpenStorage failed, hr=%08x\n", r);
+
+    if (r == S_OK)
+    {
+        /* The storage can be modified. */
+        r = IStorage_CreateStorage(stg2, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg3);
+        ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r);
+        if (r == S_OK)
+            IStream_Release(stg3);
+
+        /* But changes cannot be committed. */
+        r = IStorage_Commit(stg2, 0);
+        ok(r==STG_E_ACCESSDENIED, "IStorage->Commit should fail, hr=%08x\n", r);
+
+        IStorage_Release(stg2);
+    }
+
+    IStorage_Release(stg);
+
+    /* create a non-transacted file */
+    r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE |
+                            STGM_READWRITE, 0, &stg);
+    ok(r==S_OK, "StgCreateDocfile failed\n");
+
+    /* create a transacted substorage */
+    r = IStorage_CreateStorage(stg, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_TRANSACTED, 0, 0, &stg2);
+    ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r);
+
+    if (r == S_OK)
+    {
+        /* create and commit stmname */
+        r = IStorage_CreateStream(stg2, stmname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm );
+        ok(r==S_OK, "IStorage->CreateStream failed\n");
+        if (r == S_OK)
+            IStream_Release(stm);
+
+        IStorage_Commit(stg2, 0);
+
+        /* create and revert stmname2 */
+        r = IStorage_CreateStream(stg2, stmname2, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm );
+        ok(r==S_OK, "IStorage->CreateStream failed\n");
+        if (r == S_OK)
+            IStream_Release(stm);
+
+        IStorage_Revert(stg2);
+
+        /* check that Commit and Revert really worked */
+        r = IStorage_OpenStream(stg2, stmname, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &stm );
+        ok(r==S_OK, "IStorage->OpenStream should succeed %08x\n", r);
+        if (r == S_OK)
+            IStream_Release(stm);
+
+        r = IStorage_OpenStream(stg2, stmname2, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &stm );
+        ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStream should fail %08x\n", r);
+        if (r == S_OK)
+            IStream_Release(stm);
+
+        IStorage_Release(stg2);
+    }
+
+    IStream_Release(stg);
+
+    r = DeleteFileA(filenameA);
+    ok( r == TRUE, "deleted file\n");
+}
+
+static void test_ReadClassStm(void)
+{
+    CLSID clsid;
+    HRESULT hr;
+    IStream *pStream;
+    static const LARGE_INTEGER llZero;
+
+    hr = ReadClassStm(NULL, &clsid);
+    ok(hr == E_INVALIDARG, "ReadClassStm should have returned E_INVALIDARG instead of 0x%08x\n", hr);
+
+    hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream);
+    ok_ole_success(hr, "CreateStreamOnHGlobal");
+    hr = WriteClassStm(pStream, &test_stg_cls);
+    ok_ole_success(hr, "WriteClassStm");
+
+    hr = ReadClassStm(pStream, NULL);
+    ok(hr == E_INVALIDARG, "ReadClassStm should have returned E_INVALIDARG instead of 0x%08x\n", hr);
+
+    /* test not rewound stream */
+    hr = ReadClassStm(pStream, &clsid);
+    ok(hr == STG_E_READFAULT, "ReadClassStm should have returned STG_E_READFAULT instead of 0x%08x\n", hr);
+    ok(IsEqualCLSID(&clsid, &CLSID_NULL), "clsid should have been zeroed\n");
+
+    hr = IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL);
+    ok_ole_success(hr, "IStream_Seek");
+    hr = ReadClassStm(pStream, &clsid);
+    ok_ole_success(hr, "ReadClassStm");
+    ok(IsEqualCLSID(&clsid, &test_stg_cls), "clsid should have been set to CLSID_WineTest\n");
+
+    IStream_Release(pStream);
+}
+
+struct access_res
+{
+    BOOL gothandle;
+    DWORD lasterr;
+    BOOL ignore;
+};
+
+static const struct access_res create[16] =
+{
+    { TRUE, ERROR_SUCCESS, TRUE },
+    { TRUE, ERROR_SUCCESS, TRUE },
+    { TRUE, ERROR_SUCCESS, FALSE },
+    { TRUE, ERROR_SUCCESS, FALSE },
+    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
+    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
+    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
+    { TRUE, ERROR_SUCCESS, FALSE },
+    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
+    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
+    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
+    { TRUE, ERROR_SUCCESS, TRUE },
+    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
+    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
+    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
+    { TRUE, ERROR_SUCCESS, TRUE }
+};
+
+static const struct access_res create_commit[16] =
+{
+    { TRUE, ERROR_SUCCESS, TRUE },
+    { TRUE, ERROR_SUCCESS, TRUE },
+    { TRUE, ERROR_SUCCESS, FALSE },
+    { TRUE, ERROR_SUCCESS, FALSE },
+    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
+    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
+    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
+    { TRUE, ERROR_SUCCESS, FALSE },
+    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
+    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
+    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
+    { TRUE, ERROR_SUCCESS, TRUE },
+    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
+    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
+    { FALSE, ERROR_SHARING_VIOLATION, FALSE },
+    { TRUE, ERROR_SUCCESS, TRUE }
+};
+
+static const struct access_res create_close[16] =
+{
+    { TRUE, ERROR_SUCCESS, FALSE },
+    { TRUE, ERROR_SUCCESS, FALSE },
+    { TRUE, ERROR_SUCCESS, FALSE },
+    { TRUE, ERROR_SUCCESS, FALSE },
+    { TRUE, ERROR_SUCCESS, FALSE },
+    { TRUE, ERROR_SUCCESS, FALSE },
+    { TRUE, ERROR_SUCCESS, FALSE },
+    { TRUE, ERROR_SUCCESS, FALSE },
+    { TRUE, ERROR_SUCCESS, FALSE },
+    { TRUE, ERROR_SUCCESS, FALSE },
+    { TRUE, ERROR_SUCCESS, FALSE },
+    { TRUE, ERROR_SUCCESS, FALSE },
+    { TRUE, ERROR_SUCCESS, FALSE },
+    { TRUE, ERROR_SUCCESS, FALSE },
+    { TRUE, ERROR_SUCCESS, FALSE },
+    { TRUE, ERROR_SUCCESS }
+};
+
+static void _test_file_access(LPCSTR file, const struct access_res *ares, DWORD line)
+{
+    DWORD access = 0, share = 0;
+    DWORD lasterr;
+    HANDLE hfile;
+    int i, j, idx = 0;
+
+    for (i = 0; i < 4; i++)
+    {
+        if (i == 0) access = 0;
+        if (i == 1) access = GENERIC_READ;
+        if (i == 2) access = GENERIC_WRITE;
+        if (i == 3) access = GENERIC_READ | GENERIC_WRITE;
+
+        for (j = 0; j < 4; j++)
+        {
+            if (ares[idx].ignore)
+                continue;
+
+            if (j == 0) share = 0;
+            if (j == 1) share = FILE_SHARE_READ;
+            if (j == 2) share = FILE_SHARE_WRITE;
+            if (j == 3) share = FILE_SHARE_READ | FILE_SHARE_WRITE;
+
+            SetLastError(0xdeadbeef);
+            hfile = CreateFileA(file, access, share, NULL, OPEN_EXISTING,
+                                FILE_ATTRIBUTE_NORMAL, 0);
+            lasterr = GetLastError();
+
+            ok((hfile != INVALID_HANDLE_VALUE) == ares[idx].gothandle,
+               "(%d, handle, %d): Expected %d, got %d\n",
+               line, idx, ares[idx].gothandle,
+               (hfile != INVALID_HANDLE_VALUE));
 
             ok(lasterr == ares[idx].lasterr ||
                broken(lasterr == 0xdeadbeef) /* win9x */,
                "(%d, lasterr, %d): Expected %d, got %d\n",
                line, idx, ares[idx].lasterr, lasterr);
 
-            CloseHandle(hfile);
-            idx++;
+            CloseHandle(hfile);
+            idx++;
+        }
+    }
+}
+
+#define test_file_access(file, ares) _test_file_access(file, ares, __LINE__)
+
+static void test_access(void)
+{
+    IStorage *stg;
+    HRESULT hr;
+
+    static const WCHAR fileW[] = {'w','i','n','e','t','e','s','t',0};
+
+    /* STGM_TRANSACTED */
+
+    hr = StgCreateDocfile(fileW, STGM_CREATE | STGM_READWRITE |
+                          STGM_SHARE_EXCLUSIVE | STGM_TRANSACTED, 0, &stg);
+    ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
+
+    test_file_access("winetest", create);
+
+    hr = IStorage_Commit(stg, STGC_DEFAULT);
+    ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
+
+    test_file_access("winetest", create_commit);
+
+    IStorage_Release(stg);
+
+    test_file_access("winetest", create_close);
+
+    DeleteFileA("winetest");
+
+    /* STGM_DIRECT */
+
+    hr = StgCreateDocfile(fileW, STGM_CREATE | STGM_READWRITE |
+                          STGM_SHARE_EXCLUSIVE | STGM_DIRECT, 0, &stg);
+    ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
+
+    test_file_access("winetest", create);
+
+    hr = IStorage_Commit(stg, STGC_DEFAULT);
+    ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
+
+    test_file_access("winetest", create_commit);
+
+    IStorage_Release(stg);
+
+    test_file_access("winetest", create_close);
+
+    DeleteFileA("winetest");
+
+    /* STGM_SHARE_DENY_NONE */
+
+    hr = StgCreateDocfile(fileW, STGM_CREATE | STGM_READWRITE |
+                          STGM_SHARE_DENY_NONE | STGM_TRANSACTED, 0, &stg);
+    ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
+
+    test_file_access("winetest", create);
+
+    hr = IStorage_Commit(stg, STGC_DEFAULT);
+    ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
+
+    test_file_access("winetest", create_commit);
+
+    IStorage_Release(stg);
+
+    test_file_access("winetest", create_close);
+
+    DeleteFileA("winetest");
+
+    /* STGM_SHARE_DENY_READ */
+
+    hr = StgCreateDocfile(fileW, STGM_CREATE | STGM_READWRITE |
+                          STGM_SHARE_DENY_READ | STGM_TRANSACTED, 0, &stg);
+    ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
+
+    test_file_access("winetest", create);
+
+    hr = IStorage_Commit(stg, STGC_DEFAULT);
+    ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
+
+    test_file_access("winetest", create_commit);
+
+    IStorage_Release(stg);
+
+    test_file_access("winetest", create_close);
+
+    DeleteFileA("winetest");
+
+    /* STGM_SHARE_DENY_WRITE */
+
+    hr = StgCreateDocfile(fileW, STGM_CREATE | STGM_READWRITE |
+                          STGM_SHARE_DENY_WRITE | STGM_TRANSACTED, 0, &stg);
+    ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
+
+    test_file_access("winetest", create);
+
+    hr = IStorage_Commit(stg, STGC_DEFAULT);
+    ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
+
+    test_file_access("winetest", create_commit);
+
+    IStorage_Release(stg);
+
+    test_file_access("winetest", create_close);
+
+    DeleteFileA("winetest");
+}
+
+static void test_readonly(void)
+{
+    IStorage *stg, *stg2, *stg3;
+    IStream *stream;
+    HRESULT hr;
+    static const WCHAR fileW[] = {'w','i','n','e','t','e','s','t',0};
+    static const WCHAR storageW[] = {'s','t','o','r','a','g','e',0};
+    static const WCHAR streamW[] = {'s','t','r','e','a','m',0};
+
+    hr = StgCreateDocfile( fileW, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &stg);
+    ok(hr == S_OK, "should succeed, res=%x\n", hr);
+    if (SUCCEEDED(hr))
+    {
+        hr = IStorage_CreateStorage( stg, storageW, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stg2 );
+        ok(hr == S_OK, "should succeed, res=%x\n", hr);
+        if (SUCCEEDED(hr))
+        {
+            hr = IStorage_CreateStream( stg2, streamW, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stream );
+            ok(hr == S_OK, "should succeed, res=%x\n", hr);
+            if (SUCCEEDED(hr))
+                IStream_Release(stream);
+            IStorage_Release(stg2);
+        }
+        IStorage_Release(stg);
+    }
+
+    /* re-open read only */
+    hr = StgOpenStorage( fileW, NULL, STGM_TRANSACTED | STGM_SHARE_DENY_NONE | STGM_READ, NULL, 0, &stg);
+    ok(hr == S_OK, "should succeed, res=%x\n", hr);
+    if (SUCCEEDED(hr))
+    {
+        hr = IStorage_OpenStorage( stg, storageW, NULL, STGM_SHARE_EXCLUSIVE | STGM_READ, NULL, 0, &stg2 );
+        ok(hr == S_OK, "should succeed, res=%x\n", hr);
+        if (SUCCEEDED(hr))
+        {
+            /* CreateStream on read-only storage, name exists */
+            hr = IStorage_CreateStream( stg2, streamW, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READ, 0, 0, &stream );
+            ok(hr == STG_E_ACCESSDENIED, "should fail, res=%x\n", hr);
+            if (SUCCEEDED(hr))
+                IStream_Release(stream);
+
+            /* CreateStream on read-only storage, name does not exist */
+            hr = IStorage_CreateStream( stg2, storageW, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READ, 0, 0, &stream );
+            ok(hr == STG_E_ACCESSDENIED, "should fail, res=%x\n", hr);
+            if (SUCCEEDED(hr))
+                IStream_Release(stream);
+
+            /* CreateStorage on read-only storage, name exists */
+            hr = IStorage_CreateStorage( stg2, streamW, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READ, 0, 0, &stg3 );
+            ok(hr == STG_E_FILEALREADYEXISTS, "should fail, res=%x\n", hr);
+            if (SUCCEEDED(hr))
+                IStream_Release(stg3);
+
+            /* CreateStorage on read-only storage, name does not exist */
+            hr = IStorage_CreateStorage( stg2, storageW, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READ, 0, 0, &stg3 );
+            ok(hr == STG_E_ACCESSDENIED, "should fail, res=%x\n", hr);
+            if (SUCCEEDED(hr))
+                IStream_Release(stg3);
+
+            /* DestroyElement on read-only storage, name exists */
+            hr = IStorage_DestroyElement( stg2, streamW );
+            ok(hr == STG_E_ACCESSDENIED, "should fail, res=%x\n", hr);
+
+            /* DestroyElement on read-only storage, name does not exist */
+            hr = IStorage_DestroyElement( stg2, storageW );
+            ok(hr == STG_E_ACCESSDENIED, "should fail, res=%x\n", hr);
+
+            IStorage_Release(stg2);
+        }
+
+        IStorage_Release(stg);
+    }
+
+    DeleteFileA("winetest");
+}
+
+static void test_simple(void)
+{
+    /* Tests for STGM_SIMPLE mode */
+
+    IStorage *stg, *stg2;
+    HRESULT r;
+    IStream *stm;
+    static const WCHAR stgname[] = { 'S','t','g',0 };
+    static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 };
+    static const WCHAR stmname2[] = { 'S','m','a','l','l',0 };
+    LARGE_INTEGER pos;
+    ULARGE_INTEGER upos;
+    DWORD count;
+    STATSTG stat;
+
+    DeleteFileA(filenameA);
+
+    r = StgCreateDocfile( filename, STGM_SIMPLE | STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &stg);
+    ok(r == S_OK, "got %08x\n", r);
+
+    r = IStorage_CreateStorage(stg, stgname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stg2);
+    ok(r == STG_E_INVALIDFUNCTION, "got %08x\n", r);
+    if (SUCCEEDED(r)) IStorage_Release(stg2);
+
+    r = IStorage_CreateStream(stg, stmname, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm);
+    ok(r == STG_E_INVALIDFLAG, "got %08x\n", r);
+    r = IStorage_CreateStream(stg, stmname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm);
+    ok(r == S_OK, "got %08x\n", r);
+
+    upos.QuadPart = 6000;
+    r = IStream_SetSize(stm, upos);
+    ok(r == S_OK, "got %08x\n", r);
+
+    r = IStream_Write(stm, "foo", 3, &count);
+    ok(r == S_OK, "got %08x\n", r);
+    ok(count == 3, "got %d\n", count);
+
+    pos.QuadPart = 0;
+    r = IStream_Seek(stm, pos, STREAM_SEEK_CUR, &upos);
+    ok(r == S_OK, "got %08x\n", r);
+    ok(upos.QuadPart == 3, "got %d\n", upos.u.LowPart);
+
+    r = IStream_Stat(stm, &stat, STATFLAG_NONAME);
+    ok(r == S_OK ||
+       broken(r == STG_E_INVALIDFUNCTION), /* NT4 and below */
+       "got %08x\n", r);
+    if (r == S_OK)
+        ok(stat.cbSize.QuadPart == 3, "got %d\n", stat.cbSize.u.LowPart);
+
+    pos.QuadPart = 1;
+    r = IStream_Seek(stm, pos, STREAM_SEEK_SET, &upos);
+    ok(r == S_OK, "got %08x\n", r);
+    ok(upos.QuadPart == 1, "got %d\n", upos.u.LowPart);
+
+    r = IStream_Stat(stm, &stat, STATFLAG_NONAME);
+    ok(r == S_OK ||
+       broken(r == STG_E_INVALIDFUNCTION), /* NT4 and below */
+       "got %08x\n", r);
+    if (r == S_OK)
+        ok(stat.cbSize.QuadPart == 1, "got %d\n", stat.cbSize.u.LowPart);
+
+    IStream_Release(stm);
+
+    r = IStorage_CreateStream(stg, stmname2, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm);
+    ok(r == S_OK, "got %08x\n", r);
+
+    upos.QuadPart = 100;
+    r = IStream_SetSize(stm, upos);
+    ok(r == S_OK, "got %08x\n", r);
+
+    r = IStream_Write(stm, "foo", 3, &count);
+    ok(r == S_OK, "got %08x\n", r);
+    ok(count == 3, "got %d\n", count);
+
+    IStream_Release(stm);
+
+    IStorage_Commit(stg, STGC_DEFAULT);
+    IStorage_Release(stg);
+
+    r = StgOpenStorage( filename, NULL, STGM_SIMPLE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, NULL, 0, &stg);
+    if (r == STG_E_INVALIDFLAG)
+    {
+        win_skip("Flag combination is not supported on NT4 and below\n");
+        DeleteFileA(filenameA);
+        return;
+    }
+    ok(r == S_OK, "got %08x\n", r);
+
+    r = IStorage_OpenStorage(stg, stgname, NULL, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, NULL, 0, &stg2);
+    ok(r == STG_E_INVALIDFUNCTION, "got %08x\n", r);
+    if (SUCCEEDED(r)) IStorage_Release(stg2);
+
+    r = IStorage_OpenStream(stg, stmname, NULL, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &stm);
+    ok(r == S_OK, "got %08x\n", r);
+
+    r = IStream_Stat(stm, &stat, STATFLAG_NONAME);
+    ok(r == S_OK, "got %08x\n", r);
+    ok(stat.cbSize.QuadPart == 6000, "got %d\n", stat.cbSize.u.LowPart);
+
+    IStream_Release(stm);
+
+    r = IStorage_OpenStream(stg, stmname2, NULL, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &stm);
+    ok(r == S_OK, "got %08x\n", r);
+
+    r = IStream_Stat(stm, &stat, STATFLAG_NONAME);
+    ok(r == S_OK, "got %08x\n", r);
+    ok(stat.cbSize.QuadPart == 4096, "got %d\n", stat.cbSize.u.LowPart);
+
+    IStream_Release(stm);
+
+
+    IStorage_Release(stg);
+
+    DeleteFileA(filenameA);
+}
+
+static void test_fmtusertypestg(void)
+{
+    IStorage *stg;
+    IEnumSTATSTG *stat;
+    HRESULT hr;
+    static const char fileA[]  = {'f','m','t','t','e','s','t',0};
+    static const WCHAR fileW[] = {'f','m','t','t','e','s','t',0};
+    static WCHAR userTypeW[] = {'S','t','g','U','s','r','T','y','p','e',0};
+    static WCHAR strmNameW[] = {1,'C','o','m','p','O','b','j',0};
+
+    hr = StgCreateDocfile( fileW, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &stg);
+    ok(hr == S_OK, "should succeed, res=%x\n", hr);
+
+    if (SUCCEEDED(hr))
+    {
+        /* try to write the stream */
+        hr = WriteFmtUserTypeStg(stg, 0, userTypeW);
+        ok(hr == S_OK, "should succeed, res=%x\n", hr);
+
+        /* check that the stream was created */
+        hr = IStorage_EnumElements(stg, 0, NULL, 0, &stat);
+        ok(hr == S_OK, "should succeed, res=%x\n", hr);
+        if (SUCCEEDED(hr))
+        {
+            BOOL found = FALSE;
+            STATSTG statstg;
+            DWORD got;
+            while ((hr = IEnumSTATSTG_Next(stat, 1, &statstg, &got)) == S_OK && got == 1)
+            {
+                if (strcmp_ww(statstg.pwcsName, strmNameW) == 0)
+                    found = TRUE;
+                else
+                    ok(0, "found unexpected stream or storage\n");
+                CoTaskMemFree(statstg.pwcsName);
+            }
+            ok(found == TRUE, "expected storage to contain stream \\0001CompObj\n");
+            IEnumSTATSTG_Release(stat);
+        }
+
+        /* re-write the stream */
+        hr = WriteFmtUserTypeStg(stg, 0, userTypeW);
+        ok(hr == S_OK, "should succeed, res=%x\n", hr);
+
+        /* check that the stream is still there */
+        hr = IStorage_EnumElements(stg, 0, NULL, 0, &stat);
+        ok(hr == S_OK, "should succeed, res=%x\n", hr);
+        if (SUCCEEDED(hr))
+        {
+            BOOL found = FALSE;
+            STATSTG statstg;
+            DWORD got;
+            while ((hr = IEnumSTATSTG_Next(stat, 1, &statstg, &got)) == S_OK && got == 1)
+            {
+                if (strcmp_ww(statstg.pwcsName, strmNameW) == 0)
+                    found = TRUE;
+                else
+                    ok(0, "found unexpected stream or storage\n");
+                CoTaskMemFree(statstg.pwcsName);
+            }
+            ok(found == TRUE, "expected storage to contain stream \\0001CompObj\n");
+            IEnumSTATSTG_Release(stat);
         }
+
+        IStorage_Release(stg);
+        DeleteFileA( fileA );
     }
 }
 
-#define test_file_access(file, ares) _test_file_access(file, ares, __LINE__)
+static void test_references(void)
+{
+    IStorage *stg,*stg2;
+    HRESULT hr;
+    unsigned c1,c2;
+    static const WCHAR StorName[] = { 'D','a','t','a','S','p','a','c','e','I','n','f','o',0 };
 
-static void test_access(void)
+    DeleteFileA(filenameA);
+
+    hr = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE |STGM_TRANSACTED, 0, &stg);
+    ok(hr==S_OK, "StgCreateDocfile failed\n");
+
+    if (SUCCEEDED(hr))
+    {
+        IStorage_Release(stg);
+
+        hr = StgOpenStorage( filename, NULL, STGM_TRANSACTED | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, NULL, 0, &stg);
+        ok(hr==S_OK, "StgOpenStorage failed (result=%x)\n",hr);
+
+        if (SUCCEEDED(hr))
+        {
+            hr = IStorage_CreateStorage(stg,StorName,STGM_READWRITE | STGM_SHARE_EXCLUSIVE,0,0,&stg2);
+            ok(hr == S_OK, "IStorage_CreateStorage failed (result=%x)\n",hr);
+
+            if (SUCCEEDED(hr))
+            {
+                c1 = IStorage_AddRef(stg);
+                ok(c1 == 2, "creating internal storage added references to ancestor\n");
+                c1 = IStorage_AddRef(stg);
+                IStorage_Release(stg2);
+                c2 = IStorage_AddRef(stg) - 1;
+                ok(c1 == c2, "releasing internal storage removed references to ancestor\n");
+            }
+            c1 = IStorage_Release(stg);
+            while ( c1 ) c1 = IStorage_Release(stg);
+        }
+    }
+
+    DeleteFileA(filenameA);
+}
+
+/* dest
+ *  |-StorageA
+ *  |  `StreamA: "StreamA"
+ *  |-StorageB
+ *  |  `StreamB: "StreamB"
+ *  `StreamC: "StreamC"
+ */
+static HRESULT create_test_file(IStorage *dest)
 {
-    IStorage *stg;
+    IStorage *stgA = NULL, *stgB = NULL;
+    IStream *strmA = NULL, *strmB = NULL, *strmC = NULL;
+    const ULONG strmA_name_size = lstrlenW(strmA_name) * sizeof(WCHAR);
+    const ULONG strmB_name_size = lstrlenW(strmB_name) * sizeof(WCHAR);
+    const ULONG strmC_name_size = lstrlenW(strmC_name) * sizeof(WCHAR);
+    ULONG bytes;
     HRESULT hr;
 
-    static const WCHAR fileW[] = {'w','i','n','e','t','e','s','t',0};
+    hr = IStorage_CreateStorage(dest, stgA_name, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stgA);
+    ok(hr == S_OK, "IStorage_CreateStorage failed: 0x%08x\n", hr);
+    if(FAILED(hr))
+        goto cleanup;
+
+    hr = IStorage_CreateStream(stgA, strmA_name, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &strmA);
+    ok(hr == S_OK, "IStorage_CreateStream failed: 0x%08x\n", hr);
+    if(FAILED(hr))
+        goto cleanup;
+
+    hr = IStream_Write(strmA, strmA_name, strmA_name_size, &bytes);
+    ok(hr == S_OK && bytes == strmA_name_size, "IStream_Write failed: 0x%08x, %d of %d bytes written\n", hr, bytes, strmA_name_size);
+
+    hr = IStorage_CreateStorage(dest, stgB_name, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stgB);
+    ok(hr == S_OK, "IStorage_CreateStorage failed: 0x%08x\n", hr);
+    if(FAILED(hr))
+        goto cleanup;
+
+    hr = IStorage_CreateStream(stgB, strmB_name, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &strmB);
+    ok(hr == S_OK, "IStorage_CreateStream failed: 0x%08x\n", hr);
+    if(FAILED(hr))
+        goto cleanup;
+
+    hr = IStream_Write(strmB, strmB_name, strmB_name_size, &bytes);
+    ok(hr == S_OK && bytes == strmB_name_size, "IStream_Write failed: 0x%08x, %d of %d bytes written\n", hr, bytes, strmB_name_size);
+
+    hr = IStorage_CreateStream(dest, strmC_name, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &strmC);
+    ok(hr == S_OK, "IStorage_CreateStream failed: 0x%08x\n", hr);
+    if(FAILED(hr))
+        goto cleanup;
+
+    hr = IStream_Write(strmC, strmC_name, strmC_name_size, &bytes);
+    ok(hr == S_OK && bytes == strmC_name_size, "IStream_Write failed: 0x%08x, %d of %d bytes written\n", hr, bytes, strmC_name_size);
+
+cleanup:
+    if(strmC)
+        IStream_Release(strmC);
+    if(strmB)
+        IStream_Release(strmB);
+    if(stgB)
+        IStorage_Release(stgB);
+    if(strmA)
+        IStream_Release(strmA);
+    if(stgA)
+        IStorage_Release(stgA);
+
+    return hr;
+}
 
-    /* STGM_TRANSACTED */
+static void test_copyto(void)
+{
+    IStorage *file1 = NULL, *file2 = NULL, *stg_tmp;
+    IStream *strm_tmp;
+    WCHAR buf[64];
+    HRESULT hr;
 
-    hr = StgCreateDocfile(fileW, STGM_CREATE | STGM_READWRITE |
-                          STGM_SHARE_EXCLUSIVE | STGM_TRANSACTED, 0, &stg);
-    ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
+    /* create & populate file1 */
+    hr = StgCreateDocfile(file1_name, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &file1);
+    ok(hr == S_OK, "StgCreateDocfile failed: 0x%08x\n", hr);
+    if(FAILED(hr))
+        goto cleanup;
+
+    hr = create_test_file(file1);
+    if(FAILED(hr))
+        goto cleanup;
+
+    /* create file2 */
+    hr = StgCreateDocfile(file2_name, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &file2);
+    ok(hr == S_OK, "StgCreateDocfile failed: 0x%08x\n", hr);
+    if(FAILED(hr))
+        goto cleanup;
+
+    /* copy file1 into file2 */
+    hr = IStorage_CopyTo(file1, 0, NULL, NULL, NULL);
+    ok(hr == STG_E_INVALIDPOINTER, "CopyTo should give STG_E_INVALIDPONITER, gave: 0x%08x\n", hr);
+
+    hr = IStorage_CopyTo(file1, 0, NULL, NULL, file2);
+    ok(hr == S_OK, "CopyTo failed: 0x%08x\n", hr);
+    if(FAILED(hr))
+        goto cleanup;
+
+    /* verify that all of file1 was copied */
+    hr = IStorage_OpenStorage(file2, stgA_name, NULL,
+            STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL, 0, &stg_tmp);
+    ok(hr == S_OK, "OpenStorage failed: 0x%08x\n", hr);
+
+    if(SUCCEEDED(hr)){
+        hr = IStorage_OpenStream(stg_tmp, strmA_name, NULL,
+                STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &strm_tmp);
+        ok(hr == S_OK, "OpenStream failed: 0x%08x\n", hr);
+
+        if(SUCCEEDED(hr)){
+            memset(buf, 0, sizeof(buf));
+            hr = IStream_Read(strm_tmp, buf, sizeof(buf), NULL);
+            ok(hr == S_OK, "Read failed: 0x%08x\n", hr);
+            if(SUCCEEDED(hr))
+                ok(strcmp_ww(buf, strmA_name) == 0,
+                        "Expected %s to be read, got %s\n", wine_dbgstr_w(strmA_name), wine_dbgstr_w(buf));
+
+            IStream_Release(strm_tmp);
+        }
 
-    test_file_access("winetest", create);
+        IStorage_Release(stg_tmp);
+    }
 
-    hr = IStorage_Commit(stg, STGC_DEFAULT);
-    ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
+    hr = IStorage_OpenStorage(file2, stgB_name, NULL,
+            STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL, 0, &stg_tmp);
+    ok(hr == S_OK, "OpenStorage failed: 0x%08x\n", hr);
 
-    test_file_access("winetest", create_commit);
+    if(SUCCEEDED(hr)){
+        hr = IStorage_OpenStream(stg_tmp, strmB_name, NULL,
+                STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &strm_tmp);
+        ok(hr == S_OK, "OpenStream failed: 0x%08x\n", hr);
 
-    IStorage_Release(stg);
+        if(SUCCEEDED(hr)){
+            memset(buf, 0, sizeof(buf));
+            hr = IStream_Read(strm_tmp, buf, sizeof(buf), NULL);
+            ok(hr == S_OK, "Read failed: 0x%08x\n", hr);
+            if(SUCCEEDED(hr))
+                ok(strcmp_ww(buf, strmB_name) == 0,
+                        "Expected %s to be read, got %s\n", wine_dbgstr_w(strmB_name), wine_dbgstr_w(buf));
 
-    test_file_access("winetest", create_close);
+            IStream_Release(strm_tmp);
+        }
 
-    DeleteFileA("winetest");
+        IStorage_Release(stg_tmp);
+    }
 
-    /* STGM_DIRECT */
+    hr = IStorage_OpenStream(file2, strmC_name, NULL,
+            STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &strm_tmp);
+    ok(hr == S_OK, "OpenStream failed: 0x%08x\n", hr);
 
-    hr = StgCreateDocfile(fileW, STGM_CREATE | STGM_READWRITE |
-                          STGM_SHARE_EXCLUSIVE | STGM_DIRECT, 0, &stg);
-    ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
+    if(SUCCEEDED(hr)){
+        memset(buf, 0, sizeof(buf));
+        hr = IStream_Read(strm_tmp, buf, sizeof(buf), NULL);
+        ok(hr == S_OK, "Read failed: 0x%08x\n", hr);
+        if(SUCCEEDED(hr))
+            ok(strcmp_ww(buf, strmC_name) == 0,
+                    "Expected %s to be read, got %s\n", wine_dbgstr_w(strmC_name), wine_dbgstr_w(buf));
 
-    test_file_access("winetest", create);
+        IStream_Release(strm_tmp);
+    }
 
-    hr = IStorage_Commit(stg, STGC_DEFAULT);
-    ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
+cleanup:
+    if(file1)
+        IStorage_Release(file1);
+    if(file2)
+        IStorage_Release(file2);
 
-    test_file_access("winetest", create_commit);
+    DeleteFileA(file1_nameA);
+    DeleteFileA(file2_nameA);
+}
 
-    IStorage_Release(stg);
+static void test_copyto_snbexclusions(void)
+{
+    static const WCHAR *snb_exclude[] = {stgA_name, strmB_name, strmC_name, 0};
 
-    test_file_access("winetest", create_close);
+    IStorage *file1 = NULL, *file2 = NULL, *stg_tmp;
+    IStream *strm_tmp;
+    WCHAR buf[64];
+    HRESULT hr;
 
-    DeleteFileA("winetest");
+    /* create & populate file1 */
+    hr = StgCreateDocfile(file1_name, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &file1);
+    ok(hr == S_OK, "StgCreateDocfile failed: 0x%08x\n", hr);
+    if(FAILED(hr))
+        goto cleanup;
+
+    hr = create_test_file(file1);
+    if(FAILED(hr))
+        goto cleanup;
+
+    /* create file2 */
+    hr = StgCreateDocfile(file2_name, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &file2);
+    ok(hr == S_OK, "StgCreateDocfile failed: 0x%08x\n", hr);
+    if(FAILED(hr))
+        goto cleanup;
+
+    /* copy file1 to file2 with name exclusions */
+    hr = IStorage_CopyTo(file1, 0, NULL, (SNB)snb_exclude, file2);
+    ok(hr == S_OK, "CopyTo failed: 0x%08x\n", hr);
+    if(FAILED(hr))
+        goto cleanup;
+
+    /* verify that file1 copied over, respecting exclusions */
+    hr = IStorage_OpenStorage(file2, stgA_name, NULL,
+            STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL, 0, &stg_tmp);
+    ok(hr == STG_E_FILENOTFOUND, "OpenStorage should give STG_E_FILENOTFOUND, gave: 0x%08x\n", hr);
+    if(SUCCEEDED(hr))
+        IStorage_Release(stg_tmp);
+
+    hr = IStorage_OpenStream(file2, strmA_name, NULL,
+            STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &strm_tmp);
+    ok(hr == STG_E_FILENOTFOUND, "OpenStream should give STG_E_FILENOTFOUND, gave: 0x%08x\n", hr);
+    if(SUCCEEDED(hr))
+        IStream_Release(strm_tmp);
+
+    hr = IStorage_OpenStorage(file2, stgB_name, NULL,
+            STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL, 0, &stg_tmp);
+    ok(hr == S_OK, "OpenStorage failed: 0x%08x\n", hr);
+
+    if(SUCCEEDED(hr)){
+        hr = IStorage_OpenStream(stg_tmp, strmB_name, NULL,
+                STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &strm_tmp);
+        ok(hr == S_OK, "OpenStream failed: 0x%08x\n", hr);
+
+        if(SUCCEEDED(hr)){
+            memset(buf, 0, sizeof(buf));
+            hr = IStream_Read(strm_tmp, buf, sizeof(buf), NULL);
+            ok(hr == S_OK, "Read failed: 0x%08x\n", hr);
+            if(SUCCEEDED(hr))
+                ok(strcmp_ww(buf, strmB_name) == 0,
+                        "Expected %s to be read, got %s\n", wine_dbgstr_w(strmB_name), wine_dbgstr_w(buf));
+
+            IStream_Release(strm_tmp);
+        }
 
-    /* STGM_SHARE_DENY_NONE */
+        IStorage_Release(stg_tmp);
+    }
 
-    hr = StgCreateDocfile(fileW, STGM_CREATE | STGM_READWRITE |
-                          STGM_SHARE_DENY_NONE | STGM_TRANSACTED, 0, &stg);
-    ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
+    hr = IStorage_OpenStream(file2, strmC_name, NULL,
+            STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &strm_tmp);
+    ok(hr == STG_E_FILENOTFOUND, "OpenStream should give STG_E_FILENOTFOUND, gave: 0x%08x\n", hr);
+    if(SUCCEEDED(hr))
+        IStream_Release(strm_tmp);
 
-    test_file_access("winetest", create);
+cleanup:
+    if(file1)
+        IStorage_Release(file1);
+    if(file2)
+        IStorage_Release(file2);
 
-    hr = IStorage_Commit(stg, STGC_DEFAULT);
-    ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
+    DeleteFileA(file1_nameA);
+    DeleteFileA(file2_nameA);
+}
 
-    test_file_access("winetest", create_commit);
+static void test_copyto_iidexclusions_storage(void)
+{
+    IStorage *file1 = NULL, *file2 = NULL, *stg_tmp;
+    IStream *strm_tmp;
+    WCHAR buf[64];
+    HRESULT hr;
+
+    /* create & populate file1 */
+    hr = StgCreateDocfile(file1_name, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &file1);
+    ok(hr == S_OK, "StgCreateDocfile failed: 0x%08x\n", hr);
+    if(FAILED(hr))
+        goto cleanup;
+
+    hr = create_test_file(file1);
+    if(FAILED(hr))
+        goto cleanup;
+
+    /* create file2 */
+    hr = StgCreateDocfile(file2_name, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &file2);
+    ok(hr == S_OK, "StgCreateDocfile failed: 0x%08x\n", hr);
+    if(FAILED(hr))
+        goto cleanup;
+
+    /* copy file1 to file2 with iid exclusions */
+    hr = IStorage_CopyTo(file1, 1, &IID_IStorage, NULL, file2);
+    ok(hr == S_OK, "CopyTo failed: 0x%08x\n", hr);
+    if(FAILED(hr))
+        goto cleanup;
+
+    /* verify that file1 copied over, respecting exclusions */
+    hr = IStorage_OpenStorage(file2, stgA_name, NULL,
+            STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL, 0, &stg_tmp);
+    ok(hr == STG_E_FILENOTFOUND, "OpenStorage should give STG_E_FILENOTFOUND, gave: 0x%08x\n", hr);
+    if(SUCCEEDED(hr))
+        IStorage_Release(stg_tmp);
+
+    hr = IStorage_OpenStream(file2, strmA_name, NULL,
+            STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &strm_tmp);
+    ok(hr == STG_E_FILENOTFOUND, "OpenStream should give STG_E_FILENOTFOUND, gave: 0x%08x\n", hr);
+    if(SUCCEEDED(hr))
+        IStream_Release(strm_tmp);
+
+    hr = IStorage_OpenStorage(file2, stgB_name, NULL,
+            STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL, 0, &stg_tmp);
+    ok(hr == STG_E_FILENOTFOUND, "OpenStorage should give STG_E_FILENOTFOUND, gave: 0x%08x\n", hr);
+    if(SUCCEEDED(hr))
+        IStorage_Release(stg_tmp);
+
+    hr = IStorage_OpenStream(file2, strmB_name, NULL,
+            STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &strm_tmp);
+    ok(hr == STG_E_FILENOTFOUND, "OpenStream should give STG_E_FILENOTFOUND, gave: 0x%08x\n", hr);
+    if(SUCCEEDED(hr))
+        IStream_Release(strm_tmp);
+
+    hr = IStorage_OpenStream(file2, strmC_name, NULL,
+            STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &strm_tmp);
+    ok(hr == S_OK, "OpenStream failed: 0x%08x\n", hr);
+
+    if(SUCCEEDED(hr)){
+        memset(buf, 0, sizeof(buf));
+        hr = IStream_Read(strm_tmp, buf, sizeof(buf), NULL);
+        ok(hr == S_OK, "Read failed: 0x%08x\n", hr);
+        if(SUCCEEDED(hr))
+            ok(strcmp_ww(buf, strmC_name) == 0,
+                    "Expected %s to be read, got %s\n", wine_dbgstr_w(strmC_name), wine_dbgstr_w(buf));
+
+        IStream_Release(strm_tmp);
+    }
+
+cleanup:
+    if(file1)
+        IStorage_Release(file1);
+    if(file2)
+        IStorage_Release(file2);
+
+    DeleteFileA(file1_nameA);
+    DeleteFileA(file2_nameA);
+}
+
+static void test_copyto_iidexclusions_stream(void)
+{
+    IStorage *file1 = NULL, *file2 = NULL, *stg_tmp;
+    IStream *strm_tmp;
+    HRESULT hr;
+
+    /* create & populate file1 */
+    hr = StgCreateDocfile(file1_name, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &file1);
+    ok(hr == S_OK, "StgCreateDocfile failed: 0x%08x\n", hr);
+    if(FAILED(hr))
+        goto cleanup;
+
+    hr = create_test_file(file1);
+    if(FAILED(hr))
+        goto cleanup;
+
+    /* create file2 */
+    hr = StgCreateDocfile(file2_name, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &file2);
+    ok(hr == S_OK, "StgCreateDocfile failed: 0x%08x\n", hr);
+    if(FAILED(hr))
+        goto cleanup;
+
+    /* copy file1 to file2 with iid exclusions */
+    hr = IStorage_CopyTo(file1, 1, &IID_IStream, NULL, file2);
+    ok(hr == S_OK, "CopyTo failed: 0x%08x\n", hr);
+    if(FAILED(hr))
+        goto cleanup;
+
+    /* verify that file1 copied over, respecting exclusions */
+    hr = IStorage_OpenStorage(file2, stgA_name, NULL,
+            STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL, 0, &stg_tmp);
+    ok(hr == S_OK, "OpenStorage failed: 0x%08x\n", hr);
+
+    if(SUCCEEDED(hr)){
+        hr = IStorage_OpenStream(stg_tmp, strmA_name, NULL,
+                STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &strm_tmp);
+        ok(hr == STG_E_FILENOTFOUND, "OpenStream should give STG_E_FILENOTFOUND, gave: 0x%08x\n", hr);
+        if(SUCCEEDED(hr))
+            IStream_Release(strm_tmp);
+
+        IStorage_Release(stg_tmp);
+    }
+
+    hr = IStorage_OpenStorage(file2, stgB_name, NULL,
+            STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL, 0, &stg_tmp);
+    ok(hr == S_OK, "OpenStorage failed: 0x%08x\n", hr);
+
+    if(SUCCEEDED(hr)){
+        hr = IStorage_OpenStream(stg_tmp, strmB_name, NULL,
+                STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &strm_tmp);
+        ok(hr == STG_E_FILENOTFOUND, "OpenStream should give STG_E_FILENOTFOUND, gave: 0x%08x\n", hr);
+        if(SUCCEEDED(hr))
+            IStream_Release(strm_tmp);
+
+        IStorage_Release(stg_tmp);
+    }
+
+    hr = IStorage_OpenStream(file2, strmC_name, NULL,
+            STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &strm_tmp);
+    ok(hr == STG_E_FILENOTFOUND, "OpenStream should give STG_E_FILENOTFOUND, gave: 0x%08x\n", hr);
+    if(SUCCEEDED(hr))
+        IStream_Release(strm_tmp);
+
+cleanup:
+    if(file1)
+        IStorage_Release(file1);
+    if(file2)
+        IStorage_Release(file2);
+
+    DeleteFileA(file1_nameA);
+    DeleteFileA(file2_nameA);
+}
+
+static void test_rename(void)
+{
+    IStorage *stg, *stg2;
+    IStream *stm;
+    HRESULT r;
+    static const WCHAR stgname[] = { 'P','E','R','M','S','T','G',0 };
+    static const WCHAR stgname2[] = { 'S','T','G',0 };
+    static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 };
+    static const WCHAR stmname2[] = { 'E','N','T','S',0 };
+
+    DeleteFileA(filenameA);
+
+    /* create the file */
+    r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE |
+                            STGM_READWRITE, 0, &stg);
+    ok(r==S_OK, "StgCreateDocfile failed\n");
+
+    /* create a substorage */
+    r = IStorage_CreateStorage(stg, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg2);
+    ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r);
+
+    /* create a stream in the substorage */
+    r = IStorage_CreateStream(stg2, stmname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
+    ok(r==S_OK, "IStorage->CreateStream failed, hr=%08x\n", r);
+    IStream_Release(stm);
+
+    /* rename the stream */
+    r = IStorage_RenameElement(stg2, stmname, stmname2);
+    ok(r==S_OK, "IStorage->RenameElement failed, hr=%08x\n", r);
+
+    /* cannot open stream with old name */
+    r = IStorage_OpenStream(stg2, stmname, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stm);
+    ok(r==STG_E_FILENOTFOUND, "IStorage_OpenStream should fail, hr=%08x\n", r);
+    if (SUCCEEDED(r)) IStream_Release(stm);
+
+    /* can open stream with new name */
+    r = IStorage_OpenStream(stg2, stmname2, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stm);
+    ok(r==S_OK, "IStorage_OpenStream failed, hr=%08x\n", r);
+    if (SUCCEEDED(r)) IStream_Release(stm);
+
+    IStorage_Release(stg2);
+
+    /* rename the storage */
+    IStorage_RenameElement(stg, stgname, stgname2);
+
+    /* cannot open storage with old name */
+    r = IStorage_OpenStorage(stg, stgname, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL, 0, &stg2);
+    ok(r==STG_E_FILENOTFOUND, "IStorage_OpenStream should fail, hr=%08x\n", r);
+    if (SUCCEEDED(r)) IStorage_Release(stg2);
+
+    /* can open storage with new name */
+    r = IStorage_OpenStorage(stg, stgname2, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL, 0, &stg2);
+    ok(r==S_OK, "IStorage_OpenStream should fail, hr=%08x\n", r);
+    if (SUCCEEDED(r))
+    {
+        /* opened storage still has the stream */
+        r = IStorage_OpenStream(stg2, stmname2, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stm);
+        ok(r==S_OK, "IStorage_OpenStream failed, hr=%08x\n", r);
+        if (SUCCEEDED(r)) IStream_Release(stm);
+
+        IStorage_Release(stg2);
+    }
 
     IStorage_Release(stg);
 
-    test_file_access("winetest", create_close);
+    r = DeleteFileA(filenameA);
+    ok( r == TRUE, "deleted file\n");
+}
 
-    DeleteFileA("winetest");
+static void test_toplevel_stat(void)
+{
+    IStorage *stg = NULL;
+    HRESULT r;
+    STATSTG stat;
+    char prev_dir[MAX_PATH];
+    char temp[MAX_PATH];
+    char full_path[MAX_PATH];
+    LPSTR rel_pathA;
+    WCHAR rel_path[MAX_PATH];
 
-    /* STGM_SHARE_DENY_READ */
+    DeleteFileA(filenameA);
 
-    hr = StgCreateDocfile(fileW, STGM_CREATE | STGM_READWRITE |
-                          STGM_SHARE_DENY_READ | STGM_TRANSACTED, 0, &stg);
-    ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
+    r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE |
+                            STGM_READWRITE |STGM_TRANSACTED, 0, &stg);
+    ok(r==S_OK, "StgCreateDocfile failed\n");
 
-    test_file_access("winetest", create);
+    r = IStorage_Stat( stg, &stat, STATFLAG_DEFAULT );
+    ok(!strcmp_ww(stat.pwcsName, filename), "expected %s, got %s\n",
+        wine_dbgstr_w(filename), wine_dbgstr_w(stat.pwcsName));
+    CoTaskMemFree(stat.pwcsName);
 
-    hr = IStorage_Commit(stg, STGC_DEFAULT);
-    ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
+    IStorage_Release( stg );
 
-    test_file_access("winetest", create_commit);
+    r = StgOpenStorage( filename, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg);
+    ok(r==S_OK, "StgOpenStorage failed with error 0x%08x\n", r);
+
+    r = IStorage_Stat( stg, &stat, STATFLAG_DEFAULT );
+    ok(!strcmp_ww(stat.pwcsName, filename), "expected %s, got %s\n",
+        wine_dbgstr_w(filename), wine_dbgstr_w(stat.pwcsName));
+    CoTaskMemFree(stat.pwcsName);
+
+    IStorage_Release( stg );
+
+    DeleteFileA(filenameA);
+
+    /* Stat always returns the full path, even for files opened with a relative path. */
+    GetCurrentDirectoryA(MAX_PATH, prev_dir);
+
+    GetTempPathA(MAX_PATH, temp);
+
+    SetCurrentDirectoryA(temp);
+
+    GetFullPathNameA(filenameA, MAX_PATH, full_path, &rel_pathA);
+    MultiByteToWideChar(CP_ACP, 0, rel_pathA, -1, rel_path, MAX_PATH);
+
+    r = StgCreateDocfile( rel_path, STGM_CREATE | STGM_SHARE_EXCLUSIVE |
+                            STGM_READWRITE |STGM_TRANSACTED, 0, &stg);
+    ok(r==S_OK, "StgCreateDocfile failed\n");
+
+    r = IStorage_Stat( stg, &stat, STATFLAG_DEFAULT );
+    ok(!strcmp_ww(stat.pwcsName, filename), "expected %s, got %s\n",
+        wine_dbgstr_w(filename), wine_dbgstr_w(stat.pwcsName));
+    CoTaskMemFree(stat.pwcsName);
+
+    IStorage_Release( stg );
+
+    r = StgOpenStorage( rel_path, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg);
+    ok(r==S_OK, "StgOpenStorage failed with error 0x%08x\n", r);
+
+    r = IStorage_Stat( stg, &stat, STATFLAG_DEFAULT );
+    ok(!strcmp_ww(stat.pwcsName, filename), "expected %s, got %s\n",
+        wine_dbgstr_w(filename), wine_dbgstr_w(stat.pwcsName));
+    CoTaskMemFree(stat.pwcsName);
+
+    IStorage_Release( stg );
+
+    SetCurrentDirectoryA(prev_dir);
+
+    DeleteFileA(filenameA);
+}
+
+static void test_substorage_enum(void)
+{
+    IStorage *stg, *stg2;
+    IEnumSTATSTG *ee;
+    HRESULT r;
+    ULONG ref;
+    static const WCHAR stgname[] = { 'P','E','R','M','S','T','G',0 };
+
+    DeleteFileA(filenameA);
+
+    /* create the file */
+    r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE |
+                            STGM_READWRITE, 0, &stg);
+    ok(r==S_OK, "StgCreateDocfile failed\n");
+
+    /* create a substorage */
+    r = IStorage_CreateStorage(stg, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg2);
+    ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r);
+
+    /* create an enumelements */
+    r = IStorage_EnumElements(stg2, 0, NULL, 0, &ee);
+    ok(r==S_OK, "IStorage->EnumElements failed, hr=%08x\n", r);
+
+    /* release the substorage */
+    ref = IStorage_Release(stg2);
+    todo_wine ok(ref==0, "storage not released\n");
+
+    /* reopening fails, because the substorage is really still open */
+    r = IStorage_OpenStorage(stg, stgname, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg2);
+    ok(r==STG_E_ACCESSDENIED, "IStorage->OpenStorage failed, hr=%08x\n", r);
+
+    /* destroying the storage invalidates the enumerator */
+    r = IStorage_DestroyElement(stg, stgname);
+    ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r);
+
+    r = IEnumSTATSTG_Reset(ee);
+    ok(r==STG_E_REVERTED, "IEnumSTATSTG->Reset failed, hr=%08x\n", r);
+
+    IEnumSTATSTG_Release(ee);
 
     IStorage_Release(stg);
 
-    test_file_access("winetest", create_close);
+    r = DeleteFileA(filenameA);
+    ok( r == TRUE, "deleted file\n");
+}
 
-    DeleteFileA("winetest");
+static void test_copyto_locking(void)
+{
+    IStorage *stg, *stg2, *stg3, *stg4;
+    IStream *stm;
+    HRESULT r;
+    static const WCHAR stgname[] = { 'S','T','G','1',0 };
+    static const WCHAR stgname2[] = { 'S','T','G','2',0 };
+    static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 };
 
-    /* STGM_SHARE_DENY_WRITE */
+    DeleteFileA(filenameA);
 
-    hr = StgCreateDocfile(fileW, STGM_CREATE | STGM_READWRITE |
-                          STGM_SHARE_DENY_WRITE | STGM_TRANSACTED, 0, &stg);
-    ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
+    /* create the file */
+    r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE |
+                            STGM_READWRITE, 0, &stg);
+    ok(r==S_OK, "StgCreateDocfile failed\n");
 
-    test_file_access("winetest", create);
+    /* create a substorage */
+    r = IStorage_CreateStorage(stg, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg2);
+    ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r);
 
-    hr = IStorage_Commit(stg, STGC_DEFAULT);
-    ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
+    /* create another substorage */
+    r = IStorage_CreateStorage(stg, stgname2, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg3);
+    ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r);
 
-    test_file_access("winetest", create_commit);
+    /* add a stream, and leave it open */
+    r = IStorage_CreateStream(stg2, stmname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
+    ok(r==S_OK, "IStorage->CreateStream failed, hr=%08x\n", r);
 
+    /* Try to copy the storage while the stream is open */
+    r = IStorage_CopyTo(stg2, 0, NULL, NULL, stg3);
+    todo_wine ok(r==S_OK, "IStorage->CopyTo failed, hr=%08x\n", r);
+
+    IStream_Release(stm);
+
+    /* create a substorage */
+    r = IStorage_CreateStorage(stg2, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg4);
+    ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r);
+
+    /* Try to copy the storage while the substorage is open */
+    r = IStorage_CopyTo(stg2, 0, NULL, NULL, stg3);
+    todo_wine ok(r==S_OK, "IStorage->CopyTo failed, hr=%08x\n", r);
+
+    IStorage_Release(stg4);
+    IStorage_Release(stg3);
+    IStorage_Release(stg2);
     IStorage_Release(stg);
 
-    test_file_access("winetest", create_close);
+    r = DeleteFileA(filenameA);
+    ok( r == TRUE, "deleted file\n");
+}
 
-    DeleteFileA("winetest");
+static void test_copyto_recursive(void)
+{
+    IStorage *stg, *stg2, *stg3, *stg4;
+    HRESULT r;
+    static const WCHAR stgname[] = { 'S','T','G','1',0 };
+    static const WCHAR stgname2[] = { 'S','T','G','2',0 };
+
+    DeleteFileA(filenameA);
+
+    /* create the file */
+    r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE |
+                            STGM_READWRITE, 0, &stg);
+    ok(r==S_OK, "StgCreateDocfile failed\n");
+
+    /* create a substorage */
+    r = IStorage_CreateStorage(stg, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg2);
+    ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r);
+
+    /* copy the parent to the child */
+    r = IStorage_CopyTo(stg, 0, NULL, NULL, stg2);
+    ok(r==STG_E_ACCESSDENIED, "IStorage->CopyTo failed, hr=%08x\n", r);
+
+    /* create a transacted substorage */
+    r = IStorage_CreateStorage(stg, stgname2, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_TRANSACTED, 0, 0, &stg3);
+    ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r);
+
+    /* copy the parent to the transacted child */
+    r = IStorage_CopyTo(stg, 0, NULL, NULL, stg2);
+    ok(r==STG_E_ACCESSDENIED, "IStorage->CopyTo failed, hr=%08x\n", r);
+
+    /* create a transacted subsubstorage */
+    r = IStorage_CreateStorage(stg3, stgname2, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_TRANSACTED, 0, 0, &stg4);
+    ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r);
+
+    /* copy the parent to the transacted child of the transacted child */
+    r = IStorage_CopyTo(stg, 0, NULL, NULL, stg4);
+    ok(r==STG_E_ACCESSDENIED, "IStorage->CopyTo failed, hr=%08x\n", r);
+
+    /* copy the parent but exclude storage objects */
+    r = IStorage_CopyTo(stg, 1, &IID_IStorage, NULL, stg4);
+    ok(r==S_OK, "IStorage->CopyTo failed, hr=%08x\n", r);
+
+    IStorage_Release(stg4);
+    IStorage_Release(stg3);
+    IStorage_Release(stg2);
+    IStorage_Release(stg);
+
+    r = DeleteFileA(filenameA);
+    ok( r == TRUE, "deleted file\n");
 }
 
 START_TEST(storage32)
 {
+    CHAR temp[MAX_PATH];
+
+    GetTempPathA(MAX_PATH, temp);
+    if(!GetTempFileNameA(temp, "stg", 0, filenameA))
+    {
+        win_skip("Could not create temp file, %u\n", GetLastError());
+        return;
+    }
+    MultiByteToWideChar(CP_ACP, 0, filenameA, -1, filename, MAX_PATH);
+    DeleteFileA(filenameA);
+
     test_hglobal_storage_stat();
     test_create_storage_modes();
     test_storage_stream();
@@ -1277,7 +2843,24 @@ START_TEST(storage32)
     test_storage_refcount();
     test_streamenum();
     test_transact();
+    test_substorage_share();
+    test_revert();
+    test_parent_free();
+    test_nonroot_transacted();
     test_ReadClassStm();
     test_access();
     test_writeclassstg();
+    test_readonly();
+    test_simple();
+    test_fmtusertypestg();
+    test_references();
+    test_copyto();
+    test_copyto_snbexclusions();
+    test_copyto_iidexclusions_storage();
+    test_copyto_iidexclusions_stream();
+    test_rename();
+    test_toplevel_stat();
+    test_substorage_enum();
+    test_copyto_locking();
+    test_copyto_recursive();
 }
index 3411cc2..a655397 100644 (file)
@@ -29,7 +29,7 @@
 
 #include "wine/test.h"
 
-ULONG __RPC_USER HMETAFILE_UserSize(ULONG *, unsigned long, HMETAFILE *);
+ULONG __RPC_USER HMETAFILE_UserSize(ULONG *, ULONG, HMETAFILE *);
 unsigned char * __RPC_USER HMETAFILE_UserMarshal(ULONG *, unsigned char *, HMETAFILE *);
 unsigned char * __RPC_USER HMETAFILE_UserUnmarshal(ULONG *, unsigned char *, HMETAFILE *);
 void __RPC_USER HMETAFILE_UserFree(ULONG *, HMETAFILE *);
@@ -145,7 +145,7 @@ static void test_marshal_HWND(void)
     HWND_UserMarshal(&umcb.Flags, buffer, &hwnd);
     wirehwnd = (wireHWND)buffer;
     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 %p\n", hwnd, (HANDLE)wirehwnd->u.hRemote);
+    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);
@@ -162,7 +162,7 @@ static void test_marshal_HGLOBAL(void)
     MIDL_STUB_MESSAGE stub_msg;
     RPC_MESSAGE rpc_msg;
     unsigned char *buffer;
-    ULONG size;
+    ULONG size, block_size;
     HGLOBAL hglobal;
     HGLOBAL hglobal2;
     unsigned char *wirehglobal;
@@ -188,39 +188,52 @@ static void test_marshal_HGLOBAL(void)
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_LOCAL);
     HGLOBAL_UserFree(&umcb.Flags, &hglobal2);
 
-    hglobal = GlobalAlloc(0, 4);
-    buffer = GlobalLock(hglobal);
-    for (i = 0; i < 4; i++)
-        buffer[i] = i;
-    GlobalUnlock(hglobal);
-    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_LOCAL);
-    size = HGLOBAL_UserSize(&umcb.Flags, 0, &hglobal);
-    /* native is poorly programmed and allocates 4/8 bytes more than it needs to
-     * here - Wine doesn't have to emulate that */
-    ok((size == 24) || broken(size == 28) || broken(size == 32), "Size should be 24, instead of %d\n", size);
-    buffer = HeapAlloc(GetProcessHeap(), 0, size);
-    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_LOCAL);
-    HGLOBAL_UserMarshal(&umcb.Flags, buffer, &hglobal);
-    wirehglobal = buffer;
-    ok(*(ULONG *)wirehglobal == WDT_REMOTE_CALL, "Context should be WDT_REMOTE_CALL instead of 0x%08x\n", *(ULONG *)wirehglobal);
-    wirehglobal += sizeof(ULONG);
-    ok(*(ULONG *)wirehglobal == (ULONG)(ULONG_PTR)hglobal, "buffer+0x4 should be HGLOBAL\n");
-    wirehglobal += sizeof(ULONG);
-    ok(*(ULONG *)wirehglobal == 4, "buffer+0x8 should be size of HGLOBAL instead of %d\n", *(ULONG *)wirehglobal);
-    wirehglobal += sizeof(ULONG);
-    ok(*(ULONG *)wirehglobal == (ULONG)(ULONG_PTR)hglobal, "buffer+0xc should be HGLOBAL\n");
-    wirehglobal += sizeof(ULONG);
-    ok(*(ULONG *)wirehglobal == 4, "buffer+0x10 should be size of HGLOBAL instead of %d\n", *(ULONG *)wirehglobal);
-    wirehglobal += sizeof(ULONG);
-    for (i = 0; i < 4; i++)
-        ok(wirehglobal[i] == i, "buffer+0x%x should be %d\n", 0x10 + i, i);
-    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_LOCAL);
-    HGLOBAL_UserUnmarshal(&umcb.Flags, buffer, &hglobal2);
-    ok(hglobal2 != NULL, "Didn't unmarshal properly\n");
-    HeapFree(GetProcessHeap(), 0, buffer);
-    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_LOCAL);
-    HGLOBAL_UserFree(&umcb.Flags, &hglobal2);
-    GlobalFree(hglobal);
+
+    for(block_size = 0; block_size <= 17; block_size++)
+    {
+        ULONG actual_size, expected_size;
+
+        hglobal = GlobalAlloc(0, block_size);
+        buffer = GlobalLock(hglobal);
+        for (i = 0; i < block_size; i++)
+            buffer[i] = i;
+        GlobalUnlock(hglobal);
+        actual_size = GlobalSize(hglobal);
+        expected_size = actual_size + 5 * sizeof(DWORD);
+        trace("%d: actual size %d\n", block_size, actual_size);
+        init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_LOCAL);
+        size = HGLOBAL_UserSize(&umcb.Flags, 0, &hglobal);
+        /* native is poorly programmed and allocates 4/8 bytes more than it needs to
+         * here - Wine doesn't have to emulate that */
+        ok(size == expected_size ||
+           broken(size == expected_size + 4) ||
+           broken(size == expected_size + 8),
+           "%d: got size %d\n", block_size, size);
+        buffer = HeapAlloc(GetProcessHeap(), 0, size);
+        init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_LOCAL);
+        HGLOBAL_UserMarshal(&umcb.Flags, buffer, &hglobal);
+        wirehglobal = buffer;
+        ok(*(ULONG *)wirehglobal == WDT_REMOTE_CALL, "Context should be WDT_REMOTE_CALL instead of 0x%08x\n", *(ULONG *)wirehglobal);
+        wirehglobal += sizeof(ULONG);
+        ok(*(ULONG *)wirehglobal == (ULONG)(ULONG_PTR)hglobal, "buffer+0x4 should be HGLOBAL\n");
+        wirehglobal += sizeof(ULONG);
+        ok(*(ULONG *)wirehglobal == actual_size, "%d: buffer+0x8 %08x\n", block_size, *(ULONG *)wirehglobal);
+        wirehglobal += sizeof(ULONG);
+        ok(*(ULONG *)wirehglobal == (ULONG)(ULONG_PTR)hglobal, "buffer+0xc should be HGLOBAL\n");
+        wirehglobal += sizeof(ULONG);
+        ok(*(ULONG *)wirehglobal == actual_size, "%d: buffer+0x10 %08x\n", block_size, *(ULONG *)wirehglobal);
+        wirehglobal += sizeof(ULONG);
+        for (i = 0; i < block_size; i++)
+            ok(wirehglobal[i] == i, "buffer+0x%x should be %d\n", 0x10 + i, i);
+
+        init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_LOCAL);
+        HGLOBAL_UserUnmarshal(&umcb.Flags, buffer, &hglobal2);
+        ok(hglobal2 != NULL, "Didn't unmarshal properly\n");
+        HeapFree(GetProcessHeap(), 0, buffer);
+        init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_LOCAL);
+        HGLOBAL_UserFree(&umcb.Flags, &hglobal2);
+        GlobalFree(hglobal);
+    }
 }
 
 static HENHMETAFILE create_emf(void)
@@ -495,14 +508,50 @@ static const IUnknownVtbl TestUnknown_Vtbl =
     Test_IUnknown_Release,
 };
 
+static HRESULT WINAPI Test_IStream_QueryInterface(IStream *iface,
+                                                  REFIID riid, LPVOID *ppvObj)
+{
+    if (ppvObj == NULL) return E_POINTER;
+
+    if (IsEqualIID(riid, &IID_IUnknown) ||
+        IsEqualIID(riid, &IID_IStream))
+    {
+        *ppvObj = iface;
+        IStream_AddRef(iface);
+        return S_OK;
+    }
+
+    *ppvObj = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI Test_IStream_AddRef(IStream *iface)
+{
+    return 2; /* non-heap-based object */
+}
+
+static ULONG WINAPI Test_IStream_Release(IStream *iface)
+{
+    return 1; /* non-heap-based object */
+}
+
+static const IStreamVtbl TestStream_Vtbl =
+{
+    Test_IStream_QueryInterface,
+    Test_IStream_AddRef,
+    Test_IStream_Release
+    /* the rest can be NULLs */
+};
+
 static IUnknown Test_Unknown = { &TestUnknown_Vtbl };
+static IStream Test_Stream = { &TestStream_Vtbl };
 
 ULONG __RPC_USER WdtpInterfacePointer_UserSize(ULONG *, ULONG, ULONG, IUnknown *, REFIID);
 unsigned char * __RPC_USER WdtpInterfacePointer_UserMarshal(ULONG *, ULONG, unsigned char *, IUnknown *, REFIID);
 unsigned char * __RPC_USER WdtpInterfacePointer_UserUnmarshal(ULONG *, unsigned char *, IUnknown **, REFIID);
 void __RPC_USER WdtpInterfacePointer_UserFree(IUnknown *);
 
-static void test_marshal_WdtpInterfacePointer(void)
+static void marshal_WdtpInterfacePointer(DWORD umcb_ctx, DWORD ctx)
 {
     USER_MARSHAL_CB umcb;
     MIDL_STUB_MESSAGE stub_msg;
@@ -512,64 +561,196 @@ static void test_marshal_WdtpInterfacePointer(void)
     IUnknown *unk;
     IUnknown *unk2;
     unsigned char *wireip;
-    const IID *iid;
+    HGLOBAL h = GlobalAlloc(GMEM_MOVEABLE, 0);
+    IStream *stm;
+    void *marshal_data;
+    LARGE_INTEGER zero;
+    ULARGE_INTEGER pos;
+    DWORD marshal_size;
 
     /* shows that the WdtpInterfacePointer functions don't marshal anything for
      * NULL pointers, so code using these functions must handle that case
      * itself */
+
     unk = NULL;
-    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_INPROC);
-    size = WdtpInterfacePointer_UserSize(&umcb.Flags, umcb.Flags, 0, unk, &IID_IUnknown);
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, umcb_ctx);
+    size = WdtpInterfacePointer_UserSize(&umcb.Flags, ctx, 0, unk, &IID_IUnknown);
     ok(size == 0, "size should be 0 bytes, not %d\n", size);
     buffer = HeapAlloc(GetProcessHeap(), 0, size);
-    buffer_end = WdtpInterfacePointer_UserMarshal(&umcb.Flags, umcb.Flags, buffer, unk, &IID_IUnknown);
+    buffer_end = WdtpInterfacePointer_UserMarshal(&umcb.Flags, ctx, buffer, unk, &IID_IUnknown);
     wireip = buffer;
     HeapFree(GetProcessHeap(), 0, buffer);
 
+    /* Now for a non-NULL pointer. The marshalled data are two size DWORDS and then
+       the result of CoMarshalInterface called with the LOWORD of the ctx */
+
     unk = &Test_Unknown;
-    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_INPROC);
-    size = WdtpInterfacePointer_UserSize(&umcb.Flags, umcb.Flags, 0, unk, &IID_IUnknown);
-    todo_wine
-    ok(size > 28, "size should be > 28 bytes, not %d\n", size);
-    trace("WdtpInterfacePointer_UserSize returned %d\n", size);
+
+    CreateStreamOnHGlobal(h, TRUE, &stm);
+    CoMarshalInterface(stm, &IID_IUnknown, unk, LOWORD(ctx), NULL, MSHLFLAGS_NORMAL);
+    zero.QuadPart = 0;
+    IStream_Seek(stm, zero, STREAM_SEEK_CUR, &pos);
+    marshal_size = pos.u.LowPart;
+    marshal_data = GlobalLock(h);
+    trace("marshal_size %x\n", marshal_size);
+
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, umcb_ctx);
+    size = WdtpInterfacePointer_UserSize(&umcb.Flags, ctx, 0, unk, &IID_IUnknown);
+    ok(size >= marshal_size + 2 * sizeof(DWORD), "marshal size %x got %x\n", marshal_size, size);
+    trace("WdtpInterfacePointer_UserSize returned %x\n", size);
     buffer = HeapAlloc(GetProcessHeap(), 0, size);
-    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_INPROC);
-    buffer_end = WdtpInterfacePointer_UserMarshal(&umcb.Flags, umcb.Flags, buffer, unk, &IID_IUnknown);
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, umcb_ctx);
+    buffer_end = WdtpInterfacePointer_UserMarshal(&umcb.Flags, ctx, buffer, unk, &IID_IUnknown);
     wireip = buffer;
-    if (size >= 28)
-    {
-        ok(*(DWORD *)wireip == 0x44, "wireip + 0x0 should be 0x44 instead of 0x%08x\n", *(DWORD *)wireip);
-        wireip += sizeof(DWORD);
-        ok(*(DWORD *)wireip == 0x44, "wireip + 0x4 should be 0x44 instead of 0x%08x\n", *(DWORD *)wireip);
-        wireip += sizeof(DWORD);
-        ok(*(DWORD *)wireip == 0x574f454d /* 'MEOW' */, "wireip + 0x8 should be 0x574f454d instead of 0x%08x\n", *(DWORD *)wireip);
-        wireip += sizeof(DWORD);
-        ok(*(DWORD *)wireip == 0x1, "wireip + 0xc should be 0x1 instead of 0x%08x\n", *(DWORD *)wireip);
-        wireip += sizeof(DWORD);
-        iid = (const IID *)wireip;
-        ok(IsEqualIID(iid, &IID_IUnknown),
-           "wireip + 0x10 should be IID_IUnknown instead of {%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
-           iid->Data1, iid->Data2, iid->Data3,
-           iid->Data4[0], iid->Data4[1], iid->Data4[2], iid->Data4[3],
-           iid->Data4[4], iid->Data4[5], iid->Data4[6], iid->Data4[7]);
-        wireip += sizeof(IID);
-        ok(*(DWORD *)wireip == 0, "wireip + 0x1c should be 0 instead of 0x%08x\n", *(DWORD *)wireip);
-        wireip += sizeof(DWORD);
-        ok(*(DWORD *)wireip == 5, "wireip + 0x20 should be 5 instead of %d\n", *(DWORD *)wireip);
-        wireip += sizeof(DWORD);
-        /* the rest is dynamic so can't really be tested */
-    }
+
+    ok(buffer_end == buffer + marshal_size + 2 * sizeof(DWORD), "buffer_end %p buffer %p (diff %x)\n", buffer_end, buffer, buffer_end - buffer);
+
+    ok(*(DWORD *)wireip == marshal_size, "wireip + 0x0 should be %x instead of %x\n", marshal_size, *(DWORD *)wireip);
+    wireip += sizeof(DWORD);
+    ok(*(DWORD *)wireip == marshal_size, "wireip + 0x4 should be %x instead of %x\n", marshal_size, *(DWORD *)wireip);
+    wireip += sizeof(DWORD);
+
+    ok(!memcmp(marshal_data, wireip, marshal_size), "buffer mismatch\n");
+    GlobalUnlock(h);
+    zero.QuadPart = 0;
+    IStream_Seek(stm, zero, STREAM_SEEK_SET, NULL);
+    CoReleaseMarshalData(stm);
+    IStream_Release(stm);
 
     unk2 = NULL;
-    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_INPROC);
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, umcb_ctx);
     WdtpInterfacePointer_UserUnmarshal(&umcb.Flags, buffer, &unk2, &IID_IUnknown);
-    todo_wine
     ok(unk2 != NULL, "IUnknown object didn't unmarshal properly\n");
     HeapFree(GetProcessHeap(), 0, buffer);
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_INPROC);
     WdtpInterfacePointer_UserFree(unk2);
 }
 
+static void test_marshal_WdtpInterfacePointer(void)
+{
+    /*
+     * There are two places where we can pass the marshalling ctx: as
+     * part of the umcb and as a separate flag.  The loword of that
+     * separate flag field is what matters.
+     */
+
+    /* All three are marshalled as inproc */
+    marshal_WdtpInterfacePointer(MSHCTX_INPROC, MSHCTX_INPROC);
+    marshal_WdtpInterfacePointer(MSHCTX_DIFFERENTMACHINE, MSHCTX_INPROC);
+    marshal_WdtpInterfacePointer(MSHCTX_INPROC, MAKELONG(MSHCTX_INPROC, 0xffff));
+
+    /* All three are marshalled as remote */
+    marshal_WdtpInterfacePointer(MSHCTX_INPROC, MSHCTX_DIFFERENTMACHINE);
+    marshal_WdtpInterfacePointer(MSHCTX_DIFFERENTMACHINE, MSHCTX_DIFFERENTMACHINE);
+    marshal_WdtpInterfacePointer(MSHCTX_INPROC, MAKELONG(MSHCTX_DIFFERENTMACHINE, 0xffff));
+}
+
+static void test_marshal_STGMEDIUM(void)
+{
+    USER_MARSHAL_CB umcb;
+    MIDL_STUB_MESSAGE stub_msg;
+    RPC_MESSAGE rpc_msg;
+    unsigned char *buffer, *buffer_end, *expect_buffer, *expect_buffer_end;
+    ULONG size, expect_size;
+    STGMEDIUM med, med2;
+    IUnknown *unk = &Test_Unknown;
+    IStream *stm = &Test_Stream;
+
+    /* TYMED_NULL with pUnkForRelease */
+
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE);
+    expect_size = WdtpInterfacePointer_UserSize(&umcb.Flags, umcb.Flags, 2 * sizeof(DWORD), unk, &IID_IUnknown);
+    expect_buffer = HeapAlloc(GetProcessHeap(), 0, expect_size);
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, expect_buffer, expect_size, MSHCTX_DIFFERENTMACHINE);
+    expect_buffer_end = WdtpInterfacePointer_UserMarshal(&umcb.Flags, umcb.Flags, expect_buffer + 2 * sizeof(DWORD), unk, &IID_IUnknown);
+
+    med.tymed = TYMED_NULL;
+    U(med).pstg = NULL;
+    med.pUnkForRelease = unk;
+
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE);
+    size = STGMEDIUM_UserSize(&umcb.Flags, 0, &med);
+    ok(size == expect_size, "size %d should be %d bytes\n", size, expect_size);
+
+    buffer = HeapAlloc(GetProcessHeap(), 0, size);
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_DIFFERENTMACHINE);
+    buffer_end = STGMEDIUM_UserMarshal(&umcb.Flags, buffer, &med);
+    ok(buffer_end - buffer == expect_buffer_end - expect_buffer, "buffer size mismatch\n");
+    ok(*(DWORD*)buffer == TYMED_NULL, "got %08x\n", *(DWORD*)buffer);
+    ok(*((DWORD*)buffer+1) != 0, "got %08x\n", *((DWORD*)buffer+1));
+    ok(!memcmp(buffer+8, expect_buffer + 8, expect_buffer_end - expect_buffer - 8), "buffer mismatch\n");
+
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_DIFFERENTMACHINE);
+
+    /* native crashes if this is uninitialised, presumably because it
+       tries to release it */
+    med2.tymed = TYMED_NULL;
+    U(med2).pstm = NULL;
+    med2.pUnkForRelease = NULL;
+
+    STGMEDIUM_UserUnmarshal(&umcb.Flags, buffer, &med2);
+
+    ok(med2.tymed == TYMED_NULL, "got tymed %x\n", med2.tymed);
+    ok(med2.pUnkForRelease != NULL, "Incorrectly unmarshalled\n");
+
+    HeapFree(GetProcessHeap(), 0, buffer);
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE);
+    STGMEDIUM_UserFree(&umcb.Flags, &med2);
+
+    HeapFree(GetProcessHeap(), 0, expect_buffer);
+
+    /* TYMED_ISTREAM with pUnkForRelease */
+
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE);
+    expect_size = WdtpInterfacePointer_UserSize(&umcb.Flags, umcb.Flags, 3 * sizeof(DWORD), (IUnknown*)stm, &IID_IStream);
+    expect_size = WdtpInterfacePointer_UserSize(&umcb.Flags, umcb.Flags, expect_size, unk, &IID_IUnknown);
+
+    expect_buffer = HeapAlloc(GetProcessHeap(), 0, expect_size);
+    /* There may be a hole between the two interfaces so init the buffer to something */
+    memset(expect_buffer, 0xcc, expect_size);
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, expect_buffer, expect_size, MSHCTX_DIFFERENTMACHINE);
+    expect_buffer_end = WdtpInterfacePointer_UserMarshal(&umcb.Flags, umcb.Flags, expect_buffer + 3 * sizeof(DWORD), (IUnknown*)stm, &IID_IStream);
+    expect_buffer_end = WdtpInterfacePointer_UserMarshal(&umcb.Flags, umcb.Flags, expect_buffer_end, unk, &IID_IUnknown);
+
+    med.tymed = TYMED_ISTREAM;
+    U(med).pstm = stm;
+    med.pUnkForRelease = unk;
+
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE);
+    size = STGMEDIUM_UserSize(&umcb.Flags, 0, &med);
+    ok(size == expect_size, "size %d should be %d bytes\n", size, expect_size);
+
+    buffer = HeapAlloc(GetProcessHeap(), 0, size);
+    memset(buffer, 0xcc, size);
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_DIFFERENTMACHINE);
+    buffer_end = STGMEDIUM_UserMarshal(&umcb.Flags, buffer, &med);
+    ok(buffer_end - buffer == expect_buffer_end - expect_buffer, "buffer size mismatch\n");
+    ok(*(DWORD*)buffer == TYMED_ISTREAM, "got %08x\n", *(DWORD*)buffer);
+    ok(*((DWORD*)buffer+1) != 0, "got %08x\n", *((DWORD*)buffer+1));
+    ok(*((DWORD*)buffer+2) != 0, "got %08x\n", *((DWORD*)buffer+2));
+    ok(!memcmp(buffer + 12, expect_buffer + 12, (buffer_end - buffer) - 12), "buffer mismatch\n");
+
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_DIFFERENTMACHINE);
+
+    /* native crashes if this is uninitialised, presumably because it
+       tries to release it */
+    med2.tymed = TYMED_NULL;
+    U(med2).pstm = NULL;
+    med2.pUnkForRelease = NULL;
+
+    STGMEDIUM_UserUnmarshal(&umcb.Flags, buffer, &med2);
+
+    ok(med2.tymed == TYMED_ISTREAM, "got tymed %x\n", med2.tymed);
+    ok(U(med2).pstm != NULL, "Incorrectly unmarshalled\n");
+    ok(med2.pUnkForRelease != NULL, "Incorrectly unmarshalled\n");
+
+    HeapFree(GetProcessHeap(), 0, buffer);
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE);
+    STGMEDIUM_UserFree(&umcb.Flags, &med2);
+
+    HeapFree(GetProcessHeap(), 0, expect_buffer);
+}
+
 START_TEST(usrmarshal)
 {
     CoInitialize(NULL);
@@ -581,6 +762,7 @@ START_TEST(usrmarshal)
     test_marshal_HMETAFILE();
     test_marshal_HMETAFILEPICT();
     test_marshal_WdtpInterfacePointer();
+    test_marshal_STGMEDIUM();
 
     CoUninitialize();
 }