[SCRRUN_WINETEST] Sync with Wine Staging 1.9.23. CORE-12409
[reactos.git] / rostests / winetests / scrrun / dictionary.c
index e28bbb1..82cedc6 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2012 Alistair Leslie-Hughes
+ * Copyright 2015 Nikolay Sivov for CodeWeavers
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -22,6 +23,7 @@
 #include "windows.h"
 #include "ole2.h"
 #include "oleauto.h"
+#include "olectl.h"
 #include "dispex.h"
 
 #include "wine/test.h"
@@ -44,10 +46,7 @@ static void test_interfaces(void)
 
     hr = CoCreateInstance(&CLSID_Dictionary, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
             &IID_IDispatch, (void**)&disp);
-    if(FAILED(hr)) {
-        win_skip("Could not create FileSystem object: %08x\n", hr);
-        return;
-    }
+    ok(hr == S_OK, "got 0x%08x\n", hr);
 
     VariantInit(&key);
     VariantInit(&value);
@@ -66,37 +65,1003 @@ static void test_interfaces(void)
     V_VT(&value) = VT_BSTR;
     V_BSTR(&value) = SysAllocString(key_add_value);
     hr = IDictionary_Add(dict, &key, &value);
-    todo_wine ok(hr == S_OK, "got 0x%08x, expected 0x%08x\n", hr, S_OK);
+    ok(hr == S_OK, "got 0x%08x, expected 0x%08x\n", hr, S_OK);
     VariantClear(&value);
 
     exists = VARIANT_FALSE;
     hr = IDictionary_Exists(dict, &key, &exists);
-    todo_wine ok(hr == S_OK, "got 0x%08x, expected 0x%08x\n", hr, S_OK);
-    todo_wine ok(exists == VARIANT_TRUE, "Expected TRUE but got FALSE.\n");
+    ok(hr == S_OK, "got 0x%08x, expected 0x%08x\n", hr, S_OK);
+    ok(exists == VARIANT_TRUE, "Expected TRUE but got FALSE.\n");
     VariantClear(&key);
 
     exists = VARIANT_TRUE;
     V_VT(&key) = VT_BSTR;
     V_BSTR(&key) = SysAllocString(key_non_exist);
     hr = IDictionary_Exists(dict, &key, &exists);
-    todo_wine ok(hr == S_OK, "got 0x%08x, expected 0x%08x\n", hr, S_OK);
-    todo_wine ok(exists == VARIANT_FALSE, "Expected FALSE but got TRUE.\n");
+    ok(hr == S_OK, "got 0x%08x, expected 0x%08x\n", hr, S_OK);
+    ok(exists == VARIANT_FALSE, "Expected FALSE but got TRUE.\n");
     VariantClear(&key);
 
     hr = IDictionary_get_Count(dict, &count);
     ok(hr == S_OK, "got 0x%08x, expected 0x%08x\n", hr, S_OK);
-    todo_wine ok(count == 1, "got %d, expected 1\n", count);
+    ok(count == 1, "got %d, expected 1\n", count);
 
     IDictionary_Release(dict);
     IDispatch_Release(disp);
 }
 
+static void test_comparemode(void)
+{
+    CompareMethod method;
+    IDictionary *dict;
+    VARIANT key, item;
+    HRESULT hr;
+
+    hr = CoCreateInstance(&CLSID_Dictionary, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
+            &IID_IDictionary, (void**)&dict);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    if (0) /* crashes on native */
+        hr = IDictionary_get_CompareMode(dict, NULL);
+
+    method = 10;
+    hr = IDictionary_get_CompareMode(dict, &method);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(method == BinaryCompare, "got %d\n", method);
+
+    /* invalid mode value is not checked */
+    hr = IDictionary_put_CompareMode(dict, 10);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    hr = IDictionary_get_CompareMode(dict, &method);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(method == 10, "got %d\n", method);
+
+    hr = IDictionary_put_CompareMode(dict, DatabaseCompare);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    hr = IDictionary_get_CompareMode(dict, &method);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(method == DatabaseCompare, "got %d\n", method);
+
+    /* try to change mode of a non-empty dict */
+    V_VT(&key) = VT_I2;
+    V_I2(&key) = 0;
+    VariantInit(&item);
+    hr = IDictionary_Add(dict, &key, &item);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    hr = IDictionary_put_CompareMode(dict, BinaryCompare);
+    ok(hr == CTL_E_ILLEGALFUNCTIONCALL, "got 0x%08x\n", hr);
+
+    IDictionary_Release(dict);
+}
+
+static DWORD get_str_hash(const WCHAR *str, CompareMethod method)
+{
+    DWORD hash = 0;
+
+    while (*str) {
+        WCHAR ch;
+
+        if (method == TextCompare || method == DatabaseCompare)
+            ch = PtrToInt(CharLowerW(IntToPtr(*str)));
+        else
+            ch = *str;
+
+        hash += (hash << 4) + ch;
+        str++;
+    }
+
+    return hash % 1201;
+}
+
+static DWORD get_num_hash(FLOAT num)
+{
+    return (*((DWORD*)&num)) % 1201;
+}
+
+static DWORD get_ptr_hash(void *ptr)
+{
+    return PtrToUlong(ptr) % 1201;
+}
+
+typedef union
+{
+    struct
+    {
+        unsigned int m : 23;
+        unsigned int exp_bias : 8;
+        unsigned int sign : 1;
+    } i;
+    float f;
+} R4_FIELDS;
+
+typedef union
+{
+    struct
+    {
+        unsigned int m_lo : 32;     /* 52 bits of precision */
+        unsigned int m_hi : 20;
+        unsigned int exp_bias : 11; /* bias == 1023 */
+        unsigned int sign : 1;
+    } i;
+    double d;
+} R8_FIELDS;
+
+static HRESULT WINAPI test_unk_QI(IUnknown *iface, REFIID riid, void **obj)
+{
+    if (IsEqualIID(riid, &IID_IUnknown)) {
+        *obj = iface;
+        return S_OK;
+    }
+
+    ok(0, "unexpected %s\n", wine_dbgstr_guid(riid));
+    *obj = NULL;
+    return E_NOINTERFACE;
+}
+
+static HRESULT WINAPI test_unk_no_QI(IUnknown *iface, REFIID riid, void **obj)
+{
+    *obj = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI test_unk_AddRef(IUnknown *iface)
+{
+    ok(0, "unexpected\n");
+    return 2;
+}
+
+static ULONG WINAPI test_unk_Release(IUnknown *iface)
+{
+    return 1;
+}
+
+static /* const */ IUnknownVtbl test_unk_vtbl = {
+    test_unk_QI,
+    test_unk_AddRef,
+    test_unk_Release
+};
+
+static /* const */ IUnknownVtbl test_unk_no_vtbl = {
+    test_unk_no_QI,
+    test_unk_AddRef,
+    test_unk_Release
+};
+
+static HRESULT WINAPI test_disp_QI(IDispatch *iface, REFIID riid, void **obj)
+{
+    if (IsEqualIID(riid, &IID_IDispatch) || IsEqualIID(riid, &IID_IUnknown)) {
+        *obj = iface;
+        return S_OK;
+    }
+
+    ok(0, "unexpected %s\n", wine_dbgstr_guid(riid));
+    *obj = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI test_disp_AddRef(IDispatch *iface)
+{
+    ok(0, "unexpected\n");
+    return 2;
+}
+
+static ULONG WINAPI test_disp_Release(IDispatch *iface)
+{
+    return 1;
+}
+
+static HRESULT WINAPI test_disp_GetTypeInfoCount(IDispatch *iface, UINT *count)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI test_disp_GetTypeInfo(IDispatch *iface, UINT index, LCID lcid, ITypeInfo **ti)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI test_disp_GetIDsOfNames(IDispatch *iface, REFIID riid, LPOLESTR *names,
+    UINT name_count, LCID lcid, DISPID *dispid)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI test_disp_Invoke(IDispatch *iface, DISPID dispid, REFIID riid,
+    LCID lcid, WORD flags, DISPPARAMS *params, VARIANT *result, EXCEPINFO *excepinfo, UINT *arg_err)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static /* const */ IDispatchVtbl test_disp_vtbl = {
+    test_disp_QI,
+    test_disp_AddRef,
+    test_disp_Release,
+    test_disp_GetTypeInfoCount,
+    test_disp_GetTypeInfo,
+    test_disp_GetIDsOfNames,
+    test_disp_Invoke
+};
+
+static IUnknown test_unk = { &test_unk_vtbl };
+static IUnknown test_unk2 = { &test_unk_no_vtbl };
+static IDispatch test_disp = { &test_disp_vtbl };
+
+static void test_hash_value(void)
+{
+    /* string test data */
+    static const WCHAR str_hash_tests[][10] = {
+        {'a','b','c','d',0},
+        {'a','B','C','d','1',0},
+        {'1','2','3',0},
+        {'A',0},
+        {'a',0},
+        { 0 }
+    };
+
+    static const int int_hash_tests[] = {
+        0, -1, 100, 1, 255
+    };
+
+    static const FLOAT float_hash_tests[] = {
+        0.0, -1.0, 100.0, 1.0, 255.0, 1.234
+    };
+
+    IDictionary *dict;
+    VARIANT key, hash;
+    IDispatch *disp;
+    DWORD expected;
+    IUnknown *unk;
+    R8_FIELDS fx8;
+    R4_FIELDS fx4;
+    HRESULT hr;
+    unsigned i;
+
+    hr = CoCreateInstance(&CLSID_Dictionary, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
+            &IID_IDictionary, (void**)&dict);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    V_VT(&key) = VT_BSTR;
+    V_BSTR(&key) = NULL;
+    VariantInit(&hash);
+    hr = IDictionary_get_HashVal(dict, &key, &hash);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+    ok(V_I4(&hash) == 0, "got %d\n", V_I4(&hash));
+
+    for (i = 0; i < sizeof(str_hash_tests)/sizeof(str_hash_tests[0]); i++) {
+        expected = get_str_hash(str_hash_tests[i], BinaryCompare);
+
+        hr = IDictionary_put_CompareMode(dict, BinaryCompare);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+
+        V_VT(&key) = VT_BSTR;
+        V_BSTR(&key) = SysAllocString(str_hash_tests[i]);
+        VariantInit(&hash);
+        hr = IDictionary_get_HashVal(dict, &key, &hash);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+        ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+        ok(V_I4(&hash) == expected, "%d: binary mode: got hash 0x%08x, expected 0x%08x\n", i, V_I4(&hash),
+            expected);
+        VariantClear(&key);
+
+        expected = get_str_hash(str_hash_tests[i], TextCompare);
+        hr = IDictionary_put_CompareMode(dict, TextCompare);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+
+        V_VT(&key) = VT_BSTR;
+        V_BSTR(&key) = SysAllocString(str_hash_tests[i]);
+        VariantInit(&hash);
+        hr = IDictionary_get_HashVal(dict, &key, &hash);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+        ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+        ok(V_I4(&hash) == expected, "%d: text mode: got hash 0x%08x, expected 0x%08x\n", i, V_I4(&hash),
+            expected);
+        VariantClear(&key);
+
+        expected = get_str_hash(str_hash_tests[i], DatabaseCompare);
+        hr = IDictionary_put_CompareMode(dict, DatabaseCompare);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+
+        V_VT(&key) = VT_BSTR;
+        V_BSTR(&key) = SysAllocString(str_hash_tests[i]);
+        VariantInit(&hash);
+        hr = IDictionary_get_HashVal(dict, &key, &hash);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+        ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+        ok(V_I4(&hash) == expected, "%d: db mode: got hash 0x%08x, expected 0x%08x\n", i, V_I4(&hash),
+            expected);
+        VariantClear(&key);
+    }
+
+    V_VT(&key) = VT_INT;
+    V_INT(&key) = 1;
+    VariantInit(&hash);
+    hr = IDictionary_get_HashVal(dict, &key, &hash);
+    ok(hr == CTL_E_ILLEGALFUNCTIONCALL || broken(hr == S_OK) /* win2k, win2k3 */, "got 0x%08x\n", hr);
+    ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+    ok(V_I4(&hash) == ~0u, "got hash 0x%08x\n", V_I4(&hash));
+
+    V_VT(&key) = VT_UINT;
+    V_UINT(&key) = 1;
+    VariantInit(&hash);
+    hr = IDictionary_get_HashVal(dict, &key, &hash);
+    ok(hr == CTL_E_ILLEGALFUNCTIONCALL || broken(hr == S_OK) /* win2k, win2k3 */, "got 0x%08x\n", hr);
+    ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+    ok(V_I4(&hash) == ~0u, "got hash 0x%08x\n", V_I4(&hash));
+
+    V_VT(&key) = VT_I1;
+    V_I1(&key) = 1;
+    VariantInit(&hash);
+    hr = IDictionary_get_HashVal(dict, &key, &hash);
+    ok(hr == CTL_E_ILLEGALFUNCTIONCALL || broken(hr == S_OK) /* win2k, win2k3 */, "got 0x%08x\n", hr);
+    ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+    ok(V_I4(&hash) == ~0u || broken(V_I4(&hash) == 0xa1), "got hash 0x%08x\n", V_I4(&hash));
+
+    V_VT(&key) = VT_I8;
+    V_I8(&key) = 1;
+    VariantInit(&hash);
+    hr = IDictionary_get_HashVal(dict, &key, &hash);
+    ok(hr == CTL_E_ILLEGALFUNCTIONCALL || broken(hr == S_OK) /* win2k, win2k3 */, "got 0x%08x\n", hr);
+    ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+    ok(V_I4(&hash) == ~0u, "got hash 0x%08x\n", V_I4(&hash));
+
+    V_VT(&key) = VT_UI2;
+    V_UI2(&key) = 1;
+    VariantInit(&hash);
+    hr = IDictionary_get_HashVal(dict, &key, &hash);
+    ok(hr == CTL_E_ILLEGALFUNCTIONCALL || broken(hr == S_OK) /* win2k, win2k3 */, "got 0x%08x\n", hr);
+    ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+    ok(V_I4(&hash) == ~0u, "got hash 0x%08x\n", V_I4(&hash));
+
+    V_VT(&key) = VT_UI4;
+    V_UI4(&key) = 1;
+    VariantInit(&hash);
+    hr = IDictionary_get_HashVal(dict, &key, &hash);
+    ok(hr == CTL_E_ILLEGALFUNCTIONCALL || broken(hr == S_OK) /* win2k, win2k3 */, "got 0x%08x\n", hr);
+    ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+    ok(V_I4(&hash) == ~0u, "got hash 0x%08x\n", V_I4(&hash));
+
+    for (i = 0; i < sizeof(int_hash_tests)/sizeof(int_hash_tests[0]); i++) {
+        SHORT i2;
+        BYTE ui1;
+        LONG i4;
+
+        expected = get_num_hash(int_hash_tests[i]);
+
+        V_VT(&key) = VT_I2;
+        V_I2(&key) = int_hash_tests[i];
+        VariantInit(&hash);
+        hr = IDictionary_get_HashVal(dict, &key, &hash);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+        ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+        ok(V_I4(&hash) == expected, "%d: got hash 0x%08x, expected 0x%08x\n", i, V_I4(&hash),
+            expected);
+
+        i2 = int_hash_tests[i];
+        V_VT(&key) = VT_I2|VT_BYREF;
+        V_I2REF(&key) = &i2;
+        VariantInit(&hash);
+        hr = IDictionary_get_HashVal(dict, &key, &hash);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+        ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+        ok(V_I4(&hash) == expected, "%d: got hash 0x%08x, expected 0x%08x\n", i, V_I4(&hash),
+            expected);
+
+        V_VT(&key) = VT_I4;
+        V_I4(&key) = int_hash_tests[i];
+        VariantInit(&hash);
+        hr = IDictionary_get_HashVal(dict, &key, &hash);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+        ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+        ok(V_I4(&hash) == expected, "%d: got hash 0x%08x, expected 0x%08x\n", i, V_I4(&hash),
+            expected);
+
+        i4 = int_hash_tests[i];
+        V_VT(&key) = VT_I4|VT_BYREF;
+        V_I4REF(&key) = &i4;
+        VariantInit(&hash);
+        hr = IDictionary_get_HashVal(dict, &key, &hash);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+        ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+        ok(V_I4(&hash) == expected, "%d: got hash 0x%08x, expected 0x%08x\n", i, V_I4(&hash),
+            expected);
+
+        expected = get_num_hash((FLOAT)(BYTE)int_hash_tests[i]);
+        V_VT(&key) = VT_UI1;
+        V_UI1(&key) = int_hash_tests[i];
+        VariantInit(&hash);
+        hr = IDictionary_get_HashVal(dict, &key, &hash);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+        ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+        ok(V_I4(&hash) == expected, "%d: got hash 0x%08x, expected 0x%08x\n", i, V_I4(&hash),
+            expected);
+
+        ui1 = int_hash_tests[i];
+        V_VT(&key) = VT_UI1|VT_BYREF;
+        V_UI1REF(&key) = &ui1;
+        VariantInit(&hash);
+        hr = IDictionary_get_HashVal(dict, &key, &hash);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+        ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+        ok(V_I4(&hash) == expected, "%d: got hash 0x%08x, expected 0x%08x\n", i, V_I4(&hash),
+            expected);
+    }
+
+    /* nan */
+    fx4.f = 10.0;
+    fx4.i.exp_bias = 0xff;
+
+    V_VT(&key) = VT_R4;
+    V_R4(&key) = fx4.f;
+    VariantInit(&hash);
+    hr = IDictionary_get_HashVal(dict, &key, &hash);
+    ok(hr == CTL_E_ILLEGALFUNCTIONCALL || broken(hr == S_OK) /* win2k, win2k3 */, "got 0x%08x\n", hr);
+    ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+    ok(V_I4(&hash) == ~0u || broken(V_I4(&hash) == 0 /* win2k */ ||
+        V_I4(&hash) == 0x1f4 /* vista, win2k8 */), "got hash 0x%08x\n", V_I4(&hash));
+
+    /* inf */
+    fx4.f = 10.0;
+    fx4.i.m = 0;
+    fx4.i.exp_bias = 0xff;
+
+    V_VT(&key) = VT_R4;
+    V_R4(&key) = fx4.f;
+    V_I4(&hash) = 10;
+    hr = IDictionary_get_HashVal(dict, &key, &hash);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+    ok(V_I4(&hash) == 0, "got hash 0x%08x\n", V_I4(&hash));
+
+    /* nan */
+    fx8.d = 10.0;
+    fx8.i.exp_bias = 0x7ff;
+
+    V_VT(&key) = VT_R8;
+    V_R8(&key) = fx8.d;
+    VariantInit(&hash);
+    hr = IDictionary_get_HashVal(dict, &key, &hash);
+    ok(hr == CTL_E_ILLEGALFUNCTIONCALL || broken(hr == S_OK) /* win2k, win2k3 */, "got 0x%08x\n", hr);
+    ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+    ok(V_I4(&hash) == ~0u || broken(V_I4(&hash) == 0 /* win2k */ ||
+        V_I4(&hash) == 0x1f4 /* vista, win2k8 */), "got hash 0x%08x\n", V_I4(&hash));
+
+    V_VT(&key) = VT_DATE;
+    V_DATE(&key) = fx8.d;
+    VariantInit(&hash);
+    hr = IDictionary_get_HashVal(dict, &key, &hash);
+    ok(hr == CTL_E_ILLEGALFUNCTIONCALL || broken(hr == S_OK) /* win2k, win2k3 */, "got 0x%08x\n", hr);
+    ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+    ok(V_I4(&hash) == ~0u || broken(V_I4(&hash) == 0 /* win2k */ ||
+        V_I4(&hash) == 0x1f4 /* vista, win2k8 */), "got hash 0x%08x\n", V_I4(&hash));
+
+    /* inf */
+    fx8.d = 10.0;
+    fx8.i.m_lo = 0;
+    fx8.i.m_hi = 0;
+    fx8.i.exp_bias = 0x7ff;
+
+    V_VT(&key) = VT_R8;
+    V_R8(&key) = fx8.d;
+    V_I4(&hash) = 10;
+    hr = IDictionary_get_HashVal(dict, &key, &hash);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+    ok(V_I4(&hash) == 0, "got hash 0x%08x\n", V_I4(&hash));
+
+    V_VT(&key) = VT_DATE;
+    V_DATE(&key) = fx8.d;
+    V_I4(&hash) = 10;
+    hr = IDictionary_get_HashVal(dict, &key, &hash);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+    ok(V_I4(&hash) == 0, "got hash 0x%08x\n", V_I4(&hash));
+
+    for (i = 0; i < sizeof(float_hash_tests)/sizeof(float_hash_tests[0]); i++) {
+        double dbl;
+        FLOAT flt;
+        DATE date;
+
+        expected = get_num_hash(float_hash_tests[i]);
+
+        V_VT(&key) = VT_R4;
+        V_R4(&key) = float_hash_tests[i];
+        VariantInit(&hash);
+        hr = IDictionary_get_HashVal(dict, &key, &hash);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+        ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+        ok(V_I4(&hash) == expected, "%d: got hash 0x%08x, expected 0x%08x\n", i, V_I4(&hash),
+            expected);
+
+        flt = float_hash_tests[i];
+        V_VT(&key) = VT_R4|VT_BYREF;
+        V_R4REF(&key) = &flt;
+        VariantInit(&hash);
+        hr = IDictionary_get_HashVal(dict, &key, &hash);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+        ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+        ok(V_I4(&hash) == expected, "%d: got hash 0x%08x, expected 0x%08x\n", i, V_I4(&hash),
+            expected);
+
+        V_VT(&key) = VT_R8;
+        V_R8(&key) = float_hash_tests[i];
+        VariantInit(&hash);
+        hr = IDictionary_get_HashVal(dict, &key, &hash);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+        ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+        ok(V_I4(&hash) == expected, "%d: got hash 0x%08x, expected 0x%08x\n", i, V_I4(&hash),
+            expected);
+
+        dbl = float_hash_tests[i];
+        V_VT(&key) = VT_R8|VT_BYREF;
+        V_R8REF(&key) = &dbl;
+        VariantInit(&hash);
+        hr = IDictionary_get_HashVal(dict, &key, &hash);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+        ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+        ok(V_I4(&hash) == expected, "%d: got hash 0x%08x, expected 0x%08x\n", i, V_I4(&hash),
+            expected);
+
+        V_VT(&key) = VT_DATE;
+        V_DATE(&key) = float_hash_tests[i];
+        VariantInit(&hash);
+        hr = IDictionary_get_HashVal(dict, &key, &hash);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+        ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+        ok(V_I4(&hash) == expected, "%d: got hash 0x%08x, expected 0x%08x\n", i, V_I4(&hash),
+            expected);
+
+        V_VT(&key) = VT_DATE|VT_BYREF;
+        date = float_hash_tests[i];
+        V_DATEREF(&key) = &date;
+        VariantInit(&hash);
+        hr = IDictionary_get_HashVal(dict, &key, &hash);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+        ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+        ok(V_I4(&hash) == expected, "%d: got hash 0x%08x, expected 0x%08x\n", i, V_I4(&hash),
+            expected);
+    }
+
+    /* interface pointers as keys */
+    V_VT(&key) = VT_UNKNOWN;
+    V_UNKNOWN(&key) = 0;
+    VariantInit(&hash);
+    V_I4(&hash) = 1;
+    hr = IDictionary_get_HashVal(dict, &key, &hash);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+    ok(V_I4(&hash) == 0, "got hash 0x%08x, expected 0\n", V_I4(&hash));
+
+    /* QI doesn't work */
+    V_VT(&key) = VT_UNKNOWN;
+    V_UNKNOWN(&key) = &test_unk2;
+    VariantInit(&hash);
+    expected = get_ptr_hash(&test_unk2);
+    hr = IDictionary_get_HashVal(dict, &key, &hash);
+    ok(hr == CTL_E_ILLEGALFUNCTIONCALL || broken(hr == S_OK) /* win2k */, "got 0x%08x\n", hr);
+    ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+    ok(V_I4(&hash) == ~0u, "got hash 0x%08x, expected 0x%08x\n", V_I4(&hash), expected);
+
+    V_VT(&key) = VT_UNKNOWN;
+    V_UNKNOWN(&key) = &test_unk;
+    VariantInit(&hash);
+    expected = get_ptr_hash(&test_unk);
+    hr = IDictionary_get_HashVal(dict, &key, &hash);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+    ok(V_I4(&hash) == expected, "got hash 0x%08x, expected 0x%08x\n", V_I4(&hash), expected);
+
+    /* interface without IDispatch support */
+    V_VT(&key) = VT_DISPATCH;
+    V_DISPATCH(&key) = (IDispatch*)&test_unk;
+    VariantInit(&hash);
+    expected = get_ptr_hash(&test_unk);
+    hr = IDictionary_get_HashVal(dict, &key, &hash);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+    ok(V_I4(&hash) == expected, "got hash 0x%08x, expected 0x%08x\n", V_I4(&hash), expected);
+
+    V_VT(&key) = VT_DISPATCH;
+    V_DISPATCH(&key) = &test_disp;
+    VariantInit(&hash);
+    expected = get_ptr_hash(&test_disp);
+    hr = IDictionary_get_HashVal(dict, &key, &hash);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+    ok(V_I4(&hash) == expected, "got hash 0x%08x, expected 0x%08x\n", V_I4(&hash), expected);
+
+    /* same with BYREF */
+if (0) { /* crashes on native */
+    V_VT(&key) = VT_UNKNOWN|VT_BYREF;
+    V_UNKNOWNREF(&key) = 0;
+    hr = IDictionary_get_HashVal(dict, &key, &hash);
+}
+    unk = NULL;
+    V_VT(&key) = VT_UNKNOWN|VT_BYREF;
+    V_UNKNOWNREF(&key) = &unk;
+    VariantInit(&hash);
+    V_I4(&hash) = 1;
+    hr = IDictionary_get_HashVal(dict, &key, &hash);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+    ok(V_I4(&hash) == 0, "got hash 0x%08x, expected 0\n", V_I4(&hash));
+
+    V_VT(&key) = VT_UNKNOWN|VT_BYREF;
+    unk = &test_unk;
+    V_UNKNOWNREF(&key) = &unk;
+    VariantInit(&hash);
+    expected = get_ptr_hash(&test_unk);
+    hr = IDictionary_get_HashVal(dict, &key, &hash);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+    ok(V_I4(&hash) == expected, "got hash 0x%08x, expected 0x%08x\n", V_I4(&hash), expected);
+
+    /* interface without IDispatch support */
+    V_VT(&key) = VT_DISPATCH|VT_BYREF;
+    unk = &test_unk;
+    V_DISPATCHREF(&key) = (IDispatch**)&unk;
+    VariantInit(&hash);
+    expected = get_ptr_hash(&test_unk);
+    hr = IDictionary_get_HashVal(dict, &key, &hash);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+    ok(V_I4(&hash) == expected, "got hash 0x%08x, expected 0x%08x\n", V_I4(&hash), expected);
+
+    V_VT(&key) = VT_DISPATCH|VT_BYREF;
+    disp = &test_disp;
+    V_DISPATCHREF(&key) = &disp;
+    VariantInit(&hash);
+    expected = get_ptr_hash(&test_disp);
+    hr = IDictionary_get_HashVal(dict, &key, &hash);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
+    ok(V_I4(&hash) == expected, "got hash 0x%08x, expected 0x%08x\n", V_I4(&hash), expected);
+
+    IDictionary_Release(dict);
+}
+
+static void test_Exists(void)
+{
+    VARIANT_BOOL exists;
+    IDictionary *dict;
+    VARIANT key, item;
+    HRESULT hr;
+
+    hr = CoCreateInstance(&CLSID_Dictionary, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
+            &IID_IDictionary, (void**)&dict);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+if (0) /* crashes on native */
+    hr = IDictionary_Exists(dict, NULL, NULL);
+
+    V_VT(&key) = VT_I2;
+    V_I2(&key) = 0;
+    hr = IDictionary_Exists(dict, &key, NULL);
+    ok(hr == CTL_E_ILLEGALFUNCTIONCALL, "got 0x%08x\n", hr);
+
+    V_VT(&key) = VT_I2;
+    V_I2(&key) = 0;
+    exists = VARIANT_TRUE;
+    hr = IDictionary_Exists(dict, &key, &exists);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(exists == VARIANT_FALSE, "got %x\n", exists);
+
+    VariantInit(&item);
+    hr = IDictionary_Add(dict, &key, &item);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    V_VT(&key) = VT_R4;
+    V_R4(&key) = 0.0;
+    hr = IDictionary_Add(dict, &key, &item);
+    ok(hr == CTL_E_KEY_ALREADY_EXISTS, "got 0x%08x\n", hr);
+
+    V_VT(&key) = VT_I2;
+    V_I2(&key) = 0;
+    hr = IDictionary_Exists(dict, &key, NULL);
+    ok(hr == CTL_E_ILLEGALFUNCTIONCALL, "got 0x%08x\n", hr);
+
+    V_VT(&key) = VT_I2;
+    V_I2(&key) = 0;
+    exists = VARIANT_FALSE;
+    hr = IDictionary_Exists(dict, &key, &exists);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(exists == VARIANT_TRUE, "got %x\n", exists);
+
+    /* key of different type, but resolves to same hash value */
+    V_VT(&key) = VT_R4;
+    V_R4(&key) = 0.0;
+    exists = VARIANT_FALSE;
+    hr = IDictionary_Exists(dict, &key, &exists);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(exists == VARIANT_TRUE, "got %x\n", exists);
+
+    IDictionary_Release(dict);
+}
+
+static void test_Keys(void)
+{
+    VARIANT key, keys, item;
+    IDictionary *dict;
+    LONG index;
+    HRESULT hr;
+
+    hr = CoCreateInstance(&CLSID_Dictionary, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
+            &IID_IDictionary, (void**)&dict);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    hr = IDictionary_Keys(dict, NULL);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    VariantInit(&keys);
+    hr = IDictionary_Keys(dict, &keys);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(V_VT(&keys) == (VT_ARRAY|VT_VARIANT), "got %d\n", V_VT(&keys));
+    VariantClear(&keys);
+
+    V_VT(&key) = VT_R4;
+    V_R4(&key) = 0.0;
+    VariantInit(&item);
+    hr = IDictionary_Add(dict, &key, &item);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    VariantInit(&keys);
+    hr = IDictionary_Keys(dict, &keys);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(V_VT(&keys) == (VT_ARRAY|VT_VARIANT), "got %d\n", V_VT(&keys));
+
+    VariantInit(&key);
+    index = 0;
+    hr = SafeArrayGetElement(V_ARRAY(&keys), &index, &key);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(V_VT(&key) == VT_R4, "got %d\n", V_VT(&key));
+
+    index = SafeArrayGetDim(V_ARRAY(&keys));
+    ok(index == 1, "got %d\n", index);
+
+    hr = SafeArrayGetUBound(V_ARRAY(&keys), 1, &index);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(index == 0, "got %d\n", index);
+
+    VariantClear(&keys);
+
+    IDictionary_Release(dict);
+}
+
+static void test_Remove(void)
+{
+    VARIANT key, item;
+    IDictionary *dict;
+    HRESULT hr;
+
+    hr = CoCreateInstance(&CLSID_Dictionary, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
+            &IID_IDictionary, (void**)&dict);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+if (0)
+    hr = IDictionary_Remove(dict, NULL);
+
+    /* nothing added yet */
+    V_VT(&key) = VT_R4;
+    V_R4(&key) = 0.0;
+    hr = IDictionary_Remove(dict, &key);
+    ok(hr == CTL_E_ELEMENT_NOT_FOUND, "got 0x%08x\n", hr);
+
+    VariantInit(&item);
+    hr = IDictionary_Add(dict, &key, &item);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    hr = IDictionary_Remove(dict, &key);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    IDictionary_Release(dict);
+}
+
+static void test_Item(void)
+{
+    VARIANT key, item;
+    IDictionary *dict;
+    HRESULT hr;
+
+    hr = CoCreateInstance(&CLSID_Dictionary, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
+            &IID_IDictionary, (void**)&dict);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    V_VT(&key) = VT_I2;
+    V_I2(&key) = 10;
+    V_VT(&item) = VT_I2;
+    V_I2(&item) = 123;
+    hr = IDictionary_get_Item(dict, &key, &item);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(V_VT(&item) == VT_EMPTY, "got %d\n", V_VT(&item));
+
+    V_VT(&key) = VT_I2;
+    V_I2(&key) = 10;
+    V_VT(&item) = VT_I2;
+    hr = IDictionary_get_Item(dict, &key, &item);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(V_VT(&item) == VT_EMPTY, "got %d\n", V_VT(&item));
+
+    IDictionary_Release(dict);
+}
+
+static void test_Add(void)
+{
+    static const WCHAR testW[] = {'t','e','s','t','W',0};
+    VARIANT key, item;
+    IDictionary *dict;
+    HRESULT hr;
+    BSTR str;
+
+    hr = CoCreateInstance(&CLSID_Dictionary, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
+            &IID_IDictionary, (void**)&dict);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    str = SysAllocString(testW);
+    V_VT(&key) = VT_I2;
+    V_I2(&key) = 1;
+    V_VT(&item) = VT_BSTR|VT_BYREF;
+    V_BSTRREF(&item) = &str;
+    hr = IDictionary_Add(dict, &key, &item);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    hr = IDictionary_get_Item(dict, &key, &item);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(V_VT(&item) == VT_BSTR, "got %d\n", V_VT(&item));
+
+    SysFreeString(str);
+
+    IDictionary_Release(dict);
+}
+
+static void test_IEnumVARIANT(void)
+{
+    IUnknown *enum1, *enum2;
+    IEnumVARIANT *enumvar;
+    VARIANT key, item;
+    IDictionary *dict;
+    ULONG fetched;
+    HRESULT hr;
+
+    hr = CoCreateInstance(&CLSID_Dictionary, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
+            &IID_IDictionary, (void**)&dict);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    if (0) /* crashes on native */
+        hr = IDictionary__NewEnum(dict, NULL);
+
+    hr = IDictionary__NewEnum(dict, &enum1);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    hr = IDictionary__NewEnum(dict, &enum2);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(enum1 != enum2, "got %p, %p\n", enum2, enum1);
+    IUnknown_Release(enum2);
+
+    hr = IUnknown_QueryInterface(enum1, &IID_IEnumVARIANT, (void**)&enumvar);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    IUnknown_Release(enum1);
+
+    /* dictionary is empty */
+    hr = IEnumVARIANT_Skip(enumvar, 1);
+    ok(hr == S_FALSE, "got 0x%08x\n", hr);
+
+    hr = IEnumVARIANT_Skip(enumvar, 0);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    V_VT(&key) = VT_I2;
+    V_I2(&key) = 1;
+    V_VT(&item) = VT_I4;
+    V_I4(&item) = 100;
+    hr = IDictionary_Add(dict, &key, &item);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    hr = IEnumVARIANT_Skip(enumvar, 0);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    hr = IEnumVARIANT_Reset(enumvar);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    hr = IEnumVARIANT_Skip(enumvar, 1);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    hr = IEnumVARIANT_Skip(enumvar, 1);
+    ok(hr == S_FALSE, "got 0x%08x\n", hr);
+
+    V_VT(&key) = VT_I2;
+    V_I2(&key) = 4000;
+    V_VT(&item) = VT_I4;
+    V_I4(&item) = 200;
+    hr = IDictionary_Add(dict, &key, &item);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    V_VT(&key) = VT_I2;
+    V_I2(&key) = 0;
+    V_VT(&item) = VT_I4;
+    V_I4(&item) = 300;
+    hr = IDictionary_Add(dict, &key, &item);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    hr = IEnumVARIANT_Reset(enumvar);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    VariantInit(&key);
+    hr = IEnumVARIANT_Next(enumvar, 1, &key, &fetched);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(V_VT(&key) == VT_I2, "got %d\n", V_VT(&key));
+    ok(V_I2(&key) == 1, "got %d\n", V_I2(&key));
+    ok(fetched == 1, "got %u\n", fetched);
+
+    hr = IEnumVARIANT_Reset(enumvar);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    hr = IDictionary_Remove(dict, &key);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    VariantInit(&key);
+    hr = IEnumVARIANT_Next(enumvar, 1, &key, &fetched);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(V_VT(&key) == VT_I2, "got %d\n", V_VT(&key));
+    ok(V_I2(&key) == 4000, "got %d\n", V_I2(&key));
+    ok(fetched == 1, "got %u\n", fetched);
+
+    VariantInit(&key);
+    hr = IEnumVARIANT_Next(enumvar, 1, &key, &fetched);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(V_VT(&key) == VT_I2, "got %d\n", V_VT(&key));
+    ok(V_I2(&key) == 0, "got %d\n", V_I2(&key));
+    ok(fetched == 1, "got %u\n", fetched);
+
+    /* enumeration reached the bottom, add one more pair */
+    VariantInit(&key);
+    hr = IEnumVARIANT_Next(enumvar, 1, &key, &fetched);
+    ok(hr == S_FALSE, "got 0x%08x\n", hr);
+
+    V_VT(&key) = VT_I2;
+    V_I2(&key) = 13;
+    V_VT(&item) = VT_I4;
+    V_I4(&item) = 350;
+    hr = IDictionary_Add(dict, &key, &item);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    /* still doesn't work until Reset() */
+    VariantInit(&key);
+    hr = IEnumVARIANT_Next(enumvar, 1, &key, &fetched);
+    ok(hr == S_FALSE, "got 0x%08x\n", hr);
+
+    IEnumVARIANT_Release(enumvar);
+    IDictionary_Release(dict);
+}
+
 START_TEST(dictionary)
 {
+    IDispatch *disp;
+    HRESULT hr;
+
     CoInitialize(NULL);
 
-    test_interfaces();
+    hr = CoCreateInstance(&CLSID_Dictionary, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
+            &IID_IDispatch, (void**)&disp);
+    if(FAILED(hr)) {
+        win_skip("Dictionary object is not supported: %08x\n", hr);
+        CoUninitialize();
+        return;
+    }
+    IDispatch_Release(disp);
 
+    test_interfaces();
+    test_comparemode();
+    test_hash_value();
+    test_Exists();
+    test_Keys();
+    test_Remove();
+    test_Item();
+    test_Add();
+    test_IEnumVARIANT();
 
     CoUninitialize();
 }