[OLE32_WINETEST] Sync with Wine Staging 4.18. CORE-16441
authorAmine Khaldi <amine.khaldi@reactos.org>
Sun, 10 Nov 2019 13:11:19 +0000 (14:11 +0100)
committerAmine Khaldi <amine.khaldi@reactos.org>
Sun, 10 Nov 2019 13:11:19 +0000 (14:11 +0100)
modules/rostests/winetests/ole32/clipboard.c
modules/rostests/winetests/ole32/compobj.c
modules/rostests/winetests/ole32/marshal.c
modules/rostests/winetests/ole32/ole_server.c
modules/rostests/winetests/ole32/propvariant.c

index d7f18ef..e3e1b27 100644 (file)
@@ -46,7 +46,7 @@ static inline char *dump_fmtetc(FORMATETC *fmt)
 {
     static char buf[100];
 
-    snprintf(buf, sizeof(buf), "cf %04x ptd %p aspect %x lindex %d tymed %x",
+    sprintf(buf, "cf %04x ptd %p aspect %x lindex %d tymed %x",
              fmt->cfFormat, fmt->ptd, fmt->dwAspect, fmt->lindex, fmt->tymed);
     return buf;
 }
index 33a7585..49faabd 100644 (file)
@@ -39,7 +39,7 @@
 #include "initguid.h"
 
 #define DEFINE_EXPECT(func) \
-    static BOOL expect_ ## func = FALSE, called_ ## func = FALSE
+    static BOOL expect_ ## func = FALSE; static unsigned int called_ ## func = 0
 
 #define SET_EXPECT(func) \
     expect_ ## func = TRUE
@@ -47,7 +47,7 @@
 #define CHECK_EXPECT2(func) \
     do { \
         ok(expect_ ##func, "unexpected call " #func "\n"); \
-        called_ ## func = TRUE; \
+        called_ ## func++; \
     }while(0)
 
 #define CHECK_EXPECT(func) \
         expect_ ## func = FALSE; \
     }while(0)
 
-#define CHECK_CALLED(func) \
+#define CHECK_CALLED(func, n) \
     do { \
-        ok(called_ ## func, "expected " #func "\n"); \
-        expect_ ## func = called_ ## func = FALSE; \
+        ok(called_ ## func == n, "expected " #func " called %u times, got %u\n", n, called_ ## func); \
+        expect_ ## func = FALSE; \
+        called_ ## func = 0; \
     }while(0)
 
 DEFINE_EXPECT(CreateStub);
+DEFINE_EXPECT(PreInitialize);
+DEFINE_EXPECT(PostInitialize);
+DEFINE_EXPECT(PreUninitialize);
+DEFINE_EXPECT(PostUninitialize);
 
 /* functions that are not present on all versions of Windows */
 static HRESULT (WINAPI * pCoInitializeEx)(LPVOID lpReserved, DWORD dwCoInit);
@@ -788,7 +793,7 @@ static HRESULT WINAPI MessageFilter_QueryInterface(IMessageFilter *iface, REFIID
     if (ppvObj == NULL) return E_POINTER;
 
     if (IsEqualGUID(riid, &IID_IUnknown) ||
-        IsEqualGUID(riid, &IID_IClassFactory))
+        IsEqualGUID(riid, &IID_IMessageFilter))
     {
         *ppvObj = iface;
         IMessageFilter_AddRef(iface);
@@ -1031,7 +1036,7 @@ static HRESULT WINAPI PSFactoryBuffer_CreateStub(
 {
     CHECK_EXPECT(CreateStub);
 
-    ok(pUnkServer == (IUnknown*)&Test_Unknown, "unexpected pUnkServer %p\n", pUnkServer);
+    ok(pUnkServer == &Test_Unknown, "unexpected pUnkServer %p\n", pUnkServer);
     if(!ps_factory_buffer)
         return E_NOTIMPL;
 
@@ -1120,7 +1125,7 @@ static void test_CoRegisterPSClsid(void)
     SET_EXPECT(CreateStub);
     hr = CoMarshalInterface(stream, &IID_IWineTest, &Test_Unknown, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
     ok(hr == E_NOTIMPL, "CoMarshalInterface should have returned E_NOTIMPL instead of 0x%08x\n", hr);
-    CHECK_CALLED(CreateStub);
+    CHECK_CALLED(CreateStub, 1);
 
     hr = CoGetPSClsid(&IID_IEnumOLEVERB, &clsid);
     ok_ole_success(hr, "CoGetPSClsid");
@@ -1134,7 +1139,7 @@ static void test_CoRegisterPSClsid(void)
     SET_EXPECT(CreateStub);
     hr = CoMarshalInterface(stream, &IID_IEnumOLEVERB, (IUnknown*)&EnumOLEVERB, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
     ok(hr == S_OK, "CoMarshalInterface should have returned S_OK instead of 0x%08x\n", hr);
-    CHECK_CALLED(CreateStub);
+    CHECK_CALLED(CreateStub, 1);
 
     hr = CoMarshalInterface(stream, &IID_IEnumOLEVERB, &Test_Unknown, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
     ok(hr == S_OK, "CoMarshalInterface should have returned S_OK instead of 0x%08x\n", hr);
@@ -3306,27 +3311,47 @@ static ULONG WINAPI testinitialize_Release(IInitializeSpy *iface)
     return 1;
 }
 
+static DWORD expected_coinit_flags;
+static ULARGE_INTEGER init_cookies[3];
+static BOOL revoke_spies_on_uninit;
+
 static HRESULT WINAPI testinitialize_PreInitialize(IInitializeSpy *iface, DWORD coinit, DWORD aptrefs)
 {
-    ok(0, "unexpected call\n");
-    return E_NOTIMPL;
+    CHECK_EXPECT2(PreInitialize);
+    ok(coinit == expected_coinit_flags, "Unexpected init flags %#x, expected %#x.\n", coinit, expected_coinit_flags);
+    return S_OK;
 }
 
 static HRESULT WINAPI testinitialize_PostInitialize(IInitializeSpy *iface, HRESULT hr, DWORD coinit, DWORD aptrefs)
 {
-    ok(0, "unexpected call\n");
-    return E_NOTIMPL;
+    CHECK_EXPECT2(PostInitialize);
+    ok(coinit == expected_coinit_flags, "Unexpected init flags %#x, expected %#x.\n", coinit, expected_coinit_flags);
+    return hr;
 }
 
 static HRESULT WINAPI testinitialize_PreUninitialize(IInitializeSpy *iface, DWORD aptrefs)
 {
-    ok(0, "unexpected call\n");
-    return E_NOTIMPL;
+    HRESULT hr;
+    CHECK_EXPECT2(PreUninitialize);
+    if (revoke_spies_on_uninit)
+    {
+        hr = CoRevokeInitializeSpy(init_cookies[0]);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+
+        hr = CoRevokeInitializeSpy(init_cookies[1]);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+
+        hr = CoRevokeInitializeSpy(init_cookies[2]);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+
+        revoke_spies_on_uninit = FALSE;
+    }
+    return S_OK;
 }
 
 static HRESULT WINAPI testinitialize_PostUninitialize(IInitializeSpy *iface, DWORD aptrefs)
 {
-    ok(0, "unexpected call\n");
+    CHECK_EXPECT2(PostUninitialize);
     return E_NOTIMPL;
 }
 
@@ -3343,76 +3368,134 @@ static const IInitializeSpyVtbl testinitializevtbl =
 
 static IInitializeSpy testinitialize = { &testinitializevtbl };
 
-static void test_IInitializeSpy(void)
+static DWORD WINAPI test_init_spies_proc(void *arg)
+{
+    HRESULT hr;
+
+    hr = CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
+    ok(hr == S_OK, "Failed to initialize COM, hr %#x.\n", hr);
+
+    hr = CoRevokeInitializeSpy(init_cookies[2]);
+    ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
+
+    CoUninitialize();
+    return 0;
+}
+
+static void test_IInitializeSpy(BOOL mt)
 {
-    ULARGE_INTEGER cookie, cookie1, cookie2;
     HRESULT hr;
 
+    if (mt)
+    {
+        hr = pCoInitializeEx(NULL, COINIT_MULTITHREADED);
+        ok(hr == S_OK, "CoInitializeEx failed: %#x\n", hr);
+    }
+
     hr = CoRegisterInitializeSpy(NULL, NULL);
     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
 
-    cookie.QuadPart = 1;
-    hr = CoRegisterInitializeSpy(NULL, &cookie);
+    init_cookies[0].QuadPart = 1;
+    hr = CoRegisterInitializeSpy(NULL, &init_cookies[0]);
     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
-    ok(cookie.QuadPart == 1, "got wrong cookie\n");
+    ok(init_cookies[0].QuadPart == 1, "got wrong cookie\n");
 
     hr = CoRegisterInitializeSpy(&testinitialize, NULL);
     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
 
-    cookie.HighPart = 0;
-    cookie.LowPart = 1;
-    hr = CoRegisterInitializeSpy(&testinitialize, &cookie);
+    init_cookies[0].HighPart = 0;
+    init_cookies[0].LowPart = 1;
+    hr = CoRegisterInitializeSpy(&testinitialize, &init_cookies[0]);
     ok(hr == S_OK, "got 0x%08x\n", hr);
-todo_wine {
-    ok(cookie.HighPart == GetCurrentThreadId(), "got high part 0x%08x, expected 0x%08x\n", cookie.HighPart,
+    ok(init_cookies[0].HighPart == GetCurrentThreadId(), "got high part 0x%08x, expected 0x%08x\n", init_cookies[0].HighPart,
         GetCurrentThreadId());
-    ok(cookie.LowPart == 0, "got wrong low part 0x%x\n", cookie.LowPart);
-}
+    if (!mt) ok(init_cookies[0].LowPart == 0, "got wrong low part 0x%x\n", init_cookies[0].LowPart);
+
     /* register same instance one more time */
-    cookie1.HighPart = 0;
-    cookie1.LowPart = 0;
-    hr = CoRegisterInitializeSpy(&testinitialize, &cookie1);
-todo_wine {
+    init_cookies[1].HighPart = 0;
+    init_cookies[1].LowPart = 0;
+    hr = CoRegisterInitializeSpy(&testinitialize, &init_cookies[1]);
     ok(hr == S_OK, "got 0x%08x\n", hr);
-    ok(cookie1.HighPart == GetCurrentThreadId(), "got high part 0x%08x, expected 0x%08x\n", cookie1.HighPart,
+    ok(init_cookies[1].HighPart == GetCurrentThreadId(), "got high part 0x%08x, expected 0x%08x\n", init_cookies[1].HighPart,
         GetCurrentThreadId());
-    ok(cookie1.LowPart == 1, "got wrong low part 0x%x\n", cookie1.LowPart);
-}
-    cookie2.HighPart = 0;
-    cookie2.LowPart = 0;
-    hr = CoRegisterInitializeSpy(&testinitialize, &cookie2);
-todo_wine {
+    if (!mt) ok(init_cookies[1].LowPart == 1, "got wrong low part 0x%x\n", init_cookies[1].LowPart);
+
+    init_cookies[2].HighPart = 0;
+    init_cookies[2].LowPart = 0;
+    hr = CoRegisterInitializeSpy(&testinitialize, &init_cookies[2]);
     ok(hr == S_OK, "got 0x%08x\n", hr);
-    ok(cookie2.HighPart == GetCurrentThreadId(), "got high part 0x%08x, expected 0x%08x\n", cookie2.HighPart,
+    ok(init_cookies[2].HighPart == GetCurrentThreadId(), "got high part 0x%08x, expected 0x%08x\n", init_cookies[2].HighPart,
         GetCurrentThreadId());
-    ok(cookie2.LowPart == 2, "got wrong low part 0x%x\n", cookie2.LowPart);
-}
-    hr = CoRevokeInitializeSpy(cookie1);
-todo_wine
+    if (!mt) ok(init_cookies[2].LowPart == 2, "got wrong low part 0x%x\n", init_cookies[2].LowPart);
+
+    hr = CoRevokeInitializeSpy(init_cookies[1]);
     ok(hr == S_OK, "got 0x%08x\n", hr);
 
-    hr = CoRevokeInitializeSpy(cookie1);
+    hr = CoRevokeInitializeSpy(init_cookies[1]);
     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
 
-    cookie1.HighPart = 0;
-    cookie1.LowPart = 0;
-    hr = CoRegisterInitializeSpy(&testinitialize, &cookie1);
-todo_wine {
+    init_cookies[1].HighPart = 0;
+    init_cookies[1].LowPart = 0;
+    hr = CoRegisterInitializeSpy(&testinitialize, &init_cookies[1]);
     ok(hr == S_OK, "got 0x%08x\n", hr);
-    ok(cookie1.HighPart == GetCurrentThreadId(), "got high part 0x%08x, expected 0x%08x\n", cookie1.HighPart,
+    ok(init_cookies[1].HighPart == GetCurrentThreadId(), "got high part 0x%08x, expected 0x%08x\n", init_cookies[1].HighPart,
         GetCurrentThreadId());
-    ok(cookie1.LowPart == 1, "got wrong low part 0x%x\n", cookie1.LowPart);
-}
-    hr = CoRevokeInitializeSpy(cookie);
-    ok(hr == S_OK, "got 0x%08x\n", hr);
+    if (!mt) ok(init_cookies[1].LowPart == 1, "got wrong low part 0x%x\n", init_cookies[1].LowPart);
 
-    hr = CoRevokeInitializeSpy(cookie1);
-todo_wine
-    ok(hr == S_OK, "got 0x%08x\n", hr);
+    SET_EXPECT(PreInitialize);
+    SET_EXPECT(PostInitialize);
+    hr = CoInitializeEx(NULL, expected_coinit_flags = ((mt ? COINIT_MULTITHREADED : COINIT_APARTMENTTHREADED) | COINIT_DISABLE_OLE1DDE));
+    ok(hr == (mt ? S_FALSE : S_OK), "Failed to initialize COM, hr %#x.\n", hr);
+    CHECK_CALLED(PreInitialize, 3);
+    CHECK_CALLED(PostInitialize, 3);
 
-    hr = CoRevokeInitializeSpy(cookie2);
-todo_wine
-    ok(hr == S_OK, "got 0x%08x\n", hr);
+    if (mt)
+    {
+        HANDLE thread;
+        thread = CreateThread(NULL, 0, test_init_spies_proc, NULL, 0, NULL);
+        ok(thread != NULL, "CreateThread failed: %u\n", GetLastError());
+        ok(!WaitForSingleObject(thread, 1000), "wait failed\n");
+    }
+
+    SET_EXPECT(PreInitialize);
+    SET_EXPECT(PostInitialize);
+    hr = CoInitializeEx(NULL, expected_coinit_flags = ((mt ? COINIT_MULTITHREADED : COINIT_APARTMENTTHREADED) | COINIT_DISABLE_OLE1DDE));
+    ok(hr == S_FALSE, "Failed to initialize COM, hr %#x.\n", hr);
+    CHECK_CALLED(PreInitialize, 3);
+    CHECK_CALLED(PostInitialize, 3);
+
+    SET_EXPECT(PreUninitialize);
+    SET_EXPECT(PostUninitialize);
+    CoUninitialize();
+    CHECK_CALLED(PreUninitialize, 3);
+    CHECK_CALLED(PostUninitialize, 3);
+
+    SET_EXPECT(PreUninitialize);
+    SET_EXPECT(PostUninitialize);
+    CoUninitialize();
+    CHECK_CALLED(PreUninitialize, 3);
+    CHECK_CALLED(PostUninitialize, 3);
+
+    if (mt)
+    {
+        SET_EXPECT(PreUninitialize);
+        SET_EXPECT(PostUninitialize);
+        CoUninitialize();
+        CHECK_CALLED(PreUninitialize, 3);
+        CHECK_CALLED(PostUninitialize, 3);
+    }
+
+    SET_EXPECT(PreInitialize);
+    SET_EXPECT(PostInitialize);
+    hr = CoInitializeEx(NULL, expected_coinit_flags = ((mt ? COINIT_MULTITHREADED : COINIT_APARTMENTTHREADED) | COINIT_DISABLE_OLE1DDE));
+    ok(hr == S_OK, "Failed to initialize COM, hr %#x.\n", hr);
+    CHECK_CALLED(PreInitialize, 3);
+    CHECK_CALLED(PostInitialize, 3);
+
+    SET_EXPECT(PreUninitialize);
+    revoke_spies_on_uninit = TRUE;
+    CoUninitialize();
+    CHECK_CALLED(PreUninitialize, 1);
 }
 
 static HRESULT g_persistfile_qi_ret;
@@ -3755,10 +3838,10 @@ static DWORD CALLBACK implicit_mta_proc(void *param)
     hr = CoRegisterMessageFilter(NULL, NULL);
     ok(hr == CO_E_NOT_SUPPORTED, "got %#x\n", hr);
 
-    hr = CoLockObjectExternal((IUnknown *)&Test_Unknown, TRUE, TRUE);
+    hr = CoLockObjectExternal(&Test_Unknown, TRUE, TRUE);
     ok_ole_success(hr, "CoLockObjectExternal");
 
-    hr = CoDisconnectObject((IUnknown *)&Test_Unknown, 0);
+    hr = CoDisconnectObject(&Test_Unknown, 0);
     ok_ole_success(hr, "CoDisconnectObject");
 
     return 0;
@@ -3824,7 +3907,8 @@ START_TEST(compobj)
     test_CoGetApartmentType();
     test_IMallocSpy();
     test_CoGetCurrentLogicalThreadId();
-    test_IInitializeSpy();
+    test_IInitializeSpy(FALSE);
+    test_IInitializeSpy(TRUE);
     test_CoGetInstanceFromFile();
     test_GlobalOptions();
     test_implicit_mta();
index 22a6796..881eddd 100644 (file)
@@ -329,7 +329,7 @@ static HRESULT WINAPI Test_IClassFactory_CreateInstance(
     LPVOID *ppvObj)
 {
     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
-    return IUnknown_QueryInterface((IUnknown*)&Test_Unknown, riid, ppvObj);
+    return IUnknown_QueryInterface(&Test_Unknown, riid, ppvObj);
 }
 
 static HRESULT WINAPI Test_IClassFactory_LockServer(
@@ -1205,7 +1205,9 @@ static void test_marshal_proxy_apartment_shutdown(void)
 {
     HRESULT hr;
     IStream *pStream = NULL;
-    IUnknown *pProxy = NULL;
+    IClassFactory *proxy;
+    IUnknown *unk;
+    ULONG ref;
     DWORD tid;
     HANDLE thread;
 
@@ -1220,7 +1222,7 @@ static void test_marshal_proxy_apartment_shutdown(void)
     ok_non_zero_external_conn();
     
     IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL);
-    hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy);
+    hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&proxy);
     ok_ole_success(hr, CoUnmarshalInterface);
     IStream_Release(pStream);
 
@@ -1233,7 +1235,11 @@ static void test_marshal_proxy_apartment_shutdown(void)
     ok_zero_external_conn();
     ok_last_release_closes(TRUE);
 
-    IUnknown_Release(pProxy);
+    hr = IClassFactory_CreateInstance(proxy, NULL, &IID_IUnknown, (void **)&unk);
+    ok(hr == CO_E_OBJNOTCONNECTED, "got %#x\n", hr);
+
+    ref = IClassFactory_Release(proxy);
+    ok(!ref, "got %d refs\n", ref);
 
     ok_no_locks();
 
index 2f5a3ca..83f7c66 100644 (file)
@@ -244,7 +244,7 @@ static void ole_server(void)
     if (hr == S_OK)
     {
         trace("server: registering class object\n");
-        hr = CoRegisterClassObject(&CLSID_WineTestObject, (IUnknown *)&factory,
+        hr = CoRegisterClassObject(&CLSID_WineTestObject, (IUnknown *)&factory.IClassFactory_iface,
                                    CLSCTX_SERVER, REGCLS_MULTIPLEUSE, &key);
         if (hr == S_OK)
         {
index 97c4eec..b1ec76d 100644 (file)
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#define COBJMACROS
+#ifdef __REACTOS__
+#define CONST_VTABLE
+#endif
+
 #include "windows.h"
 #include "wtypes.h"
 #include "ddeml.h"
@@ -44,28 +49,28 @@ static const struct valid_mapping
 {
     { PROP_V0 , PROP_INV, PROP_INV, PROP_INV }, /* VT_EMPTY */
     { PROP_V0 , PROP_INV, PROP_INV, PROP_INV }, /* VT_NULL */
-    { PROP_V0 , PROP_V1 | PROP_TODO , PROP_V0 , PROP_V1 | PROP_TODO  }, /* VT_I2 */
-    { PROP_V0 , PROP_V1 | PROP_TODO , PROP_V0 , PROP_V1 | PROP_TODO  }, /* VT_I4 */
-    { PROP_V0 , PROP_V1 | PROP_TODO , PROP_V0 , PROP_V1 | PROP_TODO  }, /* VT_R4 */
-    { PROP_V0 , PROP_V1 | PROP_TODO , PROP_V0 , PROP_V1 | PROP_TODO  }, /* VT_R8 */
-    { PROP_V0 , PROP_V1 | PROP_TODO , PROP_V0 , PROP_V1 | PROP_TODO  }, /* VT_CY */
-    { PROP_V0 , PROP_V1 | PROP_TODO , PROP_V0 , PROP_V1 | PROP_TODO  }, /* VT_DATE */
-    { PROP_V0 , PROP_V1 | PROP_TODO , PROP_V0 , PROP_V1 | PROP_TODO  }, /* VT_BSTR */
-    { PROP_V1 , PROP_V1 | PROP_TODO , PROP_INV, PROP_V1 | PROP_TODO  }, /* VT_DISPATCH */
-    { PROP_V0 , PROP_V1 | PROP_TODO , PROP_V0 , PROP_V1 | PROP_TODO  }, /* VT_ERROR */
-    { PROP_V0 , PROP_V1 | PROP_TODO , PROP_V0 , PROP_V1 | PROP_TODO  }, /* VT_BOOL */
-    { PROP_V1 | PROP_TODO , PROP_V1 | PROP_TODO , PROP_V0 , PROP_V1 | PROP_TODO  }, /* VT_VARIANT */
-    { PROP_V1 , PROP_V1 | PROP_TODO , PROP_INV, PROP_V1 | PROP_TODO  }, /* VT_UNKNOWN */
-    { PROP_V1 , PROP_V1 | PROP_TODO , PROP_INV, PROP_V1 | PROP_TODO  }, /* VT_DECIMAL */
+    { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 | PROP_TODO  }, /* VT_I2 */
+    { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 | PROP_TODO  }, /* VT_I4 */
+    { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 | PROP_TODO  }, /* VT_R4 */
+    { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 | PROP_TODO  }, /* VT_R8 */
+    { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 | PROP_TODO  }, /* VT_CY */
+    { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 | PROP_TODO  }, /* VT_DATE */
+    { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 | PROP_TODO  }, /* VT_BSTR */
+    { PROP_V1 , PROP_V1 , PROP_INV, PROP_V1 | PROP_TODO  }, /* VT_DISPATCH */
+    { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 | PROP_TODO  }, /* VT_ERROR */
+    { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 | PROP_TODO  }, /* VT_BOOL */
+    { PROP_V1 | PROP_TODO , PROP_V1 , PROP_V0 , PROP_V1 | PROP_TODO  }, /* VT_VARIANT */
+    { PROP_V1 , PROP_V1, PROP_INV, PROP_V1 | PROP_TODO  }, /* VT_UNKNOWN */
+    { PROP_V1 , PROP_V1 , PROP_INV, PROP_V1 | PROP_TODO  }, /* VT_DECIMAL */
     { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 15 */
-    { PROP_V1 , PROP_V1 | PROP_TODO , PROP_V1 , PROP_V1 | PROP_TODO  }, /* VT_I1 */
-    { PROP_V0 , PROP_V1 | PROP_TODO , PROP_V0 , PROP_V1 | PROP_TODO  }, /* VT_UI1 */
-    { PROP_V0 , PROP_V1 | PROP_TODO , PROP_V0 , PROP_V1 | PROP_TODO  }, /* VT_UI2 */
-    { PROP_V0 , PROP_V1 | PROP_TODO , PROP_V0 , PROP_V1 | PROP_TODO  }, /* VT_UI4 */
+    { PROP_V1 , PROP_V1 , PROP_V1 , PROP_V1 | PROP_TODO  }, /* VT_I1 */
+    { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 | PROP_TODO  }, /* VT_UI1 */
+    { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 | PROP_TODO  }, /* VT_UI2 */
+    { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 | PROP_TODO  }, /* VT_UI4 */
     { PROP_V0 , PROP_V1A | PROP_TODO, PROP_V0 , PROP_V1A | PROP_TODO }, /* VT_I8 */
     { PROP_V0 , PROP_V1A | PROP_TODO, PROP_V0 , PROP_V1A | PROP_TODO }, /* VT_UI8 */
-    { PROP_V1 , PROP_V1 | PROP_TODO , PROP_INV, PROP_V1 | PROP_TODO  }, /* VT_INT */
-    { PROP_V1 , PROP_V1 | PROP_TODO , PROP_INV, PROP_V1 | PROP_TODO  }, /* VT_UINT */
+    { PROP_V1 , PROP_V1 , PROP_INV, PROP_V1 | PROP_TODO  }, /* VT_INT */
+    { PROP_V1 , PROP_V1 , PROP_INV, PROP_V1 | PROP_TODO  }, /* VT_UINT */
     { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* VT_VOID */
     { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* VT_HRESULT */
     { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* VT_PTR */
@@ -298,12 +303,60 @@ static void test_validtypes(void)
     }
 }
 
+struct unk_impl
+{
+    IUnknown IUnknown_iface;
+    LONG ref;
+};
+
+static inline struct unk_impl *impl_from_IUnknown(IUnknown *iface)
+{
+    return CONTAINING_RECORD(iface, struct unk_impl, IUnknown_iface);
+}
+
+static HRESULT WINAPI unk_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
+{
+    struct unk_impl *This = impl_from_IUnknown(iface);
+    if(winetest_debug > 1)
+        trace("Call to unk_QueryInterface()\n");
+    *ppv = &This->IUnknown_iface;
+    IUnknown_AddRef(iface);
+    return S_OK;
+}
+
+static ULONG WINAPI unk_AddRef(IUnknown *iface)
+{
+    struct unk_impl *This = impl_from_IUnknown(iface);
+    if(winetest_debug > 1)
+        trace("Call to unk_AddRef()\n");
+    return InterlockedIncrement(&This->ref);
+}
+
+static ULONG WINAPI unk_Release(IUnknown *iface)
+{
+    struct unk_impl *This = impl_from_IUnknown(iface);
+    if(winetest_debug > 1)
+        trace("Call to unk_Release()\n");
+    return InterlockedDecrement(&This->ref);
+}
+
+static const IUnknownVtbl unk_vtbl =
+{
+    unk_QueryInterface,
+    unk_AddRef,
+    unk_Release
+};
+
 static void test_copy(void)
 {
     static char szTestString[] = "Test String";
     static WCHAR wszTestString[] = {'T','e','s','t',' ','S','t','r','i','n','g',0};
+    struct unk_impl unk_obj = {{&unk_vtbl}, 1};
     PROPVARIANT propvarSrc;
     PROPVARIANT propvarDst;
+    SAFEARRAY *sa;
+    SAFEARRAYBOUND sabound;
+    LONG saindex;
     HRESULT hr;
 
     propvarSrc.vt = VT_BSTR;
@@ -334,6 +387,39 @@ static void test_copy(void)
     hr = PropVariantClear(&propvarDst);
     ok(hr == S_OK, "PropVariantClear(...VT_LPSTR...) failed\n");
     memset(&propvarSrc, 0, sizeof(propvarSrc));
+
+    propvarSrc.vt = VT_UNKNOWN;
+    U(propvarSrc).punkVal = &unk_obj.IUnknown_iface;
+    hr = PropVariantCopy(&propvarDst, &propvarSrc);
+    ok(hr == S_OK, "PropVariantCopy(...VT_UNKNOWN...) failed: 0x%08x.\n", hr);
+    ok(U(propvarDst).punkVal == &unk_obj.IUnknown_iface, "Got wrong IUnknown pointer\n");
+    ok(unk_obj.ref == 2, "got wrong refcount: %d.\n", unk_obj.ref);
+    hr = PropVariantClear(&propvarDst);
+    ok(hr == S_OK, "PropVariantClear(...VT_UNKNOWN...) failed: 0x%08x.\n", hr);
+    ok(unk_obj.ref == 1, "got wrong refcount: %d.\n", unk_obj.ref);
+    memset(&propvarSrc, 0, sizeof(propvarSrc));
+
+    sabound.lLbound = 0;
+    sabound.cElements = 2;
+    sa = SafeArrayCreate(VT_UNKNOWN, 1, &sabound);
+    saindex = 0;
+    SafeArrayPutElement(sa, &saindex, &unk_obj.IUnknown_iface);
+    saindex = 1;
+    SafeArrayPutElement(sa, &saindex, &unk_obj.IUnknown_iface);
+    ok(unk_obj.ref == 3, "got wrong refcount: %d.\n", unk_obj.ref);
+
+    propvarSrc.vt = VT_ARRAY | VT_UNKNOWN;
+    U(propvarSrc).parray = sa;
+    hr = PropVariantCopy(&propvarDst, &propvarSrc);
+    ok(hr == S_OK, "PropVariantCopy(...VT_ARRAY|VT_UNKNOWN...) failed: 0x%08x.\n", hr);
+    ok(unk_obj.ref == 5, "got wrong refcount: %d.\n", unk_obj.ref);
+    hr = PropVariantClear(&propvarDst);
+    ok(hr == S_OK, "PropVariantClear(...VT_ARRAY|VT_UNKNOWN...) failed: 0x%08x.\n", hr);
+    ok(unk_obj.ref == 3, "got wrong refcount: %d.\n", unk_obj.ref);
+    hr = PropVariantClear(&propvarSrc);
+    ok(hr == S_OK, "PropVariantClear(...VT_ARRAY|VT_UNKNOWN...) failed: 0x%08x.\n", hr);
+    ok(unk_obj.ref == 1, "got wrong refcount: %d.\n", unk_obj.ref);
+    memset(&propvarSrc, 0, sizeof(propvarSrc));
 }
 
 struct _PMemoryAllocator_vtable {