[OLEAUT32_WINETEST]
authorAmine Khaldi <amine.khaldi@reactos.org>
Thu, 24 Apr 2014 15:13:22 +0000 (15:13 +0000)
committerAmine Khaldi <amine.khaldi@reactos.org>
Thu, 24 Apr 2014 15:13:22 +0000 (15:13 +0000)
* Sync with Wine 1.7.17.
CORE-8080

svn path=/trunk/; revision=62943

12 files changed:
rostests/winetests/oleaut32/CMakeLists.txt
rostests/winetests/oleaut32/dispatch.c
rostests/winetests/oleaut32/olefont.c
rostests/winetests/oleaut32/olepicture.c
rostests/winetests/oleaut32/safearray.c
rostests/winetests/oleaut32/test_reg.idl
rostests/winetests/oleaut32/test_tlb.idl
rostests/winetests/oleaut32/tmarshal.c
rostests/winetests/oleaut32/tmarshal.idl
rostests/winetests/oleaut32/typelib.c
rostests/winetests/oleaut32/vartest.c
rostests/winetests/oleaut32/vartype.c

index 0c85ba3..2de2a00 100644 (file)
@@ -2,8 +2,8 @@
 add_definitions(-DUSE_WINE_TODOS)
 include_directories(${REACTOS_SOURCE_DIR}/include/reactos/wine)
 add_typelib(test_reg.idl test_tlb.idl tmarshal.idl)
-add_idl_Headers(oleaut32_idlheaders test_reg.idl tmarshal.idl)
-generate_idl_iids(tmarshal.idl test_reg.idl)
+add_idl_Headers(oleaut32_idlheaders test_reg.idl test_tlb.idl tmarshal.idl)
+generate_idl_iids(test_reg.idl test_tlb.idl tmarshal.idl)
 
 list(APPEND SOURCE
     dispatch.c
@@ -18,6 +18,7 @@ list(APPEND SOURCE
     vartest.c
     vartype.c
     ${CMAKE_CURRENT_BINARY_DIR}/test_reg_i.c
+    ${CMAKE_CURRENT_BINARY_DIR}/test_tlb_i.c
     ${CMAKE_CURRENT_BINARY_DIR}/tmarshal_i.c)
 
 set(tmarshal.rc_DEPS
@@ -30,6 +31,9 @@ set_source_files_properties(tmarshal.rc PROPERTIES OBJECT_DEPENDS "${tmarshal.rc
 add_executable(oleaut32_winetest ${SOURCE} tmarshal.rc)
 target_link_libraries(oleaut32_winetest uuid)
 set_module_type(oleaut32_winetest win32cui)
-add_importlibs(oleaut32_winetest oleaut32 ole32 rpcrt4 user32 gdi32 advapi32 msvcrt kernel32 ntdll)
+add_importlibs(oleaut32_winetest oleaut32 ole32 rpcrt4 user32 gdi32 advapi32 msvcrt kernel32)
+if(MSVC)
+    add_importlibs(oleaut32_winetest ntdll)
+endif()
 add_dependencies(oleaut32_winetest stdole2 oleaut32_typelibs oleaut32_idlheaders)
 add_cd_file(TARGET oleaut32_winetest DESTINATION reactos/bin FOR all)
index cd53128..d5ac4f0 100644 (file)
@@ -22,6 +22,9 @@
 #define _INC_WINDOWS
 #define COM_NO_WINDOWS_H
 
+#define COBJMACROS
+#define CONST_VTABLE
+
 #include <wine/test.h>
 //#include <windef.h>
 //#include <winbase.h>
@@ -273,7 +276,74 @@ static void test_DispGetParam(void)
     VariantClear(&result);
 }
 
+static HRESULT WINAPI unk_QI(IUnknown *iface, REFIID riid, void **obj)
+{
+    if (IsEqualIID(riid, &IID_IUnknown))
+    {
+        *obj = iface;
+        return S_OK;
+    }
+    else
+    {
+        *obj = NULL;
+        return E_NOINTERFACE;
+    }
+}
+
+static ULONG WINAPI unk_AddRef(IUnknown *iface)
+{
+    return 2;
+}
+
+static ULONG WINAPI unk_Release(IUnknown *iface)
+{
+    return 1;
+}
+
+static const IUnknownVtbl unkvtbl =
+{
+    unk_QI,
+    unk_AddRef,
+    unk_Release
+};
+
+static IUnknown test_unk = { &unkvtbl };
+
+static void test_CreateStdDispatch(void)
+{
+    static const WCHAR stdole2W[] = {'s','t','d','o','l','e','2','.','t','l','b',0};
+    ITypeLib *tl;
+    ITypeInfo *ti;
+    IUnknown *unk;
+    HRESULT hr;
+
+    hr = CreateStdDispatch(NULL, NULL, NULL, NULL);
+    ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
+
+    hr = CreateStdDispatch(NULL, NULL, NULL, &unk);
+    ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
+
+    hr = LoadTypeLib(stdole2W, &tl);
+    ok(hr == S_OK, "got %08x\n", hr);
+    hr = ITypeLib_GetTypeInfoOfGuid(tl, &IID_IUnknown, &ti);
+    ok(hr == S_OK, "got %08x\n", hr);
+    ITypeLib_Release(tl);
+
+    hr = CreateStdDispatch(NULL, &test_unk, NULL, &unk);
+    ok(hr == E_INVALIDARG, "got %08x\n", hr);
+
+    hr = CreateStdDispatch(NULL, NULL, ti, &unk);
+    ok(hr == E_INVALIDARG, "got %08x\n", hr);
+
+    hr = CreateStdDispatch(NULL, &test_unk, ti, &unk);
+    ok(hr == S_OK, "got %08x\n", hr);
+    IUnknown_Release(unk);
+
+    ITypeInfo_Release(ti);
+}
+
 START_TEST(dispatch)
 {
     test_DispGetParam();
+    test_CreateStdDispatch();
 }
index 3a20f2f..634b1c7 100644 (file)
@@ -71,7 +71,7 @@ static void test_ifont_size(LONG lo_size, LONG hi_size,
        LPVOID pvObj = NULL;
        IFont* ifnt = NULL;
        HFONT hfont;
-       LOGFONT lf;
+       LOGFONTA lf;
        CY psize;
        HRESULT hres;
         DWORD rtnval;
@@ -82,9 +82,9 @@ static void test_ifont_size(LONG lo_size, LONG hi_size,
        S(fd.cySize).Hi   = hi_size;
        fd.sWeight        = 0;
        fd.sCharset       = 0;
-       fd.fItalic        = 0;
-       fd.fUnderline     = 0;
-       fd.fStrikethrough = 0;
+        fd.fItalic        = FALSE;
+        fd.fUnderline     = FALSE;
+        fd.fStrikethrough = FALSE;
 
        /* Create font, test that it worked. */
        hres = pOleCreateFontIndirect(&fd, &IID_IFont, &pvObj);
@@ -93,13 +93,10 @@ static void test_ifont_size(LONG lo_size, LONG hi_size,
                test_name, hres);
        ok(pvObj != NULL,"%s: OCFI returns NULL.\n", test_name);
 
-       /* If scaling ration specified, change ratio. */
-        if(ratio_logical && ratio_himetric)
-        {
-          hres = IFont_SetRatio(ifnt, ratio_logical, ratio_himetric);
-          ok(hres == S_OK,"%s: IFont_SetRatio returns 0x%08x instead of S_OK.\n",
-            test_name, hres);
-        }
+        /* Change the scaling ratio */
+        hres = IFont_SetRatio(ifnt, ratio_logical, ratio_himetric);
+        ok((ratio_logical && ratio_himetric) ? hres == S_OK : hres == E_FAIL,
+           "%s: IFont_SetRatio unexpectedly returned 0x%08x.\n", test_name, hres);
 
        /* Read back size. */
        hres = IFont_get_Size(ifnt, &psize);
@@ -115,7 +112,7 @@ static void test_ifont_size(LONG lo_size, LONG hi_size,
        hres = IFont_get_hFont (ifnt, &hfont);
        ok(hres == S_OK, "%s: IFont_get_hFont returns 0x%08x instead of S_OK.\n",
                test_name, hres);
-       rtnval = GetObject (hfont, sizeof(LOGFONT), &lf);
+       rtnval = GetObjectA(hfont, sizeof(LOGFONTA), &lf);
         ok(rtnval > 0, "GetObject(hfont) failed\n");
 
         /* Since font scaling may encounter rounding errors, allow 1 pixel deviation. */
@@ -156,6 +153,41 @@ static void test_ifont_sizes(void)
     test_ifont_size(180000, 0, 144, 2540, -36, "ratio2");  /* another ratio */
     test_ifont_size(180000, 0, 72,  1270, -36, "ratio3");  /* yet another ratio */
     test_ifont_size(186000, 0, 72,  2540, -19, "rounding+ratio"); /* test rounding with ratio */
+
+    /* test various combinations of logical == himetric */
+    test_ifont_size(180000, 0, 10, 10, -635, "identical ratio 1");
+    test_ifont_size(240000, 0, 10, 10, -848, "identical ratio 2");
+    test_ifont_size(300000, 0, 10, 10, -1058, "identical ratio 3");
+
+    /* test various combinations of logical and himetric both set to 1 */
+    test_ifont_size(180000, 0, 1, 1, -24, "1:1 ratio 1");
+    test_ifont_size(240000, 0, 1, 1, -32, "1:1 ratio 2");
+    test_ifont_size(300000, 0, 1, 1, -40, "1:1 ratio 3");
+
+    /* test various combinations of logical set to 1 */
+    test_ifont_size(180000, 0, 1, 0, -24, "1:0 ratio 1");
+    test_ifont_size(240000, 0, 1, 0, -32, "1:0 ratio 2");
+    test_ifont_size(300000, 0, 1, 0, -40, "1:0 ratio 3");
+
+    /* test various combinations of himetric set to 1 */
+    test_ifont_size(180000, 0, 0, 1, -24, "0:1 ratio 1");
+    test_ifont_size(240000, 0, 0, 1, -32, "0:1 ratio 2");
+    test_ifont_size(300000, 0, 0, 1, -40, "0:1 ratio 3");
+
+    /* test various combinations of 2:1 logical:himetric */
+    test_ifont_size(180000, 0, 2, 1, -1270, "2:1 ratio 1");
+    test_ifont_size(240000, 0, 2, 1, -1694, "2:1 ratio 2");
+    test_ifont_size(300000, 0, 2, 1, -2117, "2:1 ratio 3");
+
+    /* test various combinations of 1:2 logical:himetric */
+    test_ifont_size(180000, 0, 1, 2, -318, "1:2 ratio 1");
+    test_ifont_size(240000, 0, 1, 2, -424, "1:2 ratio 2");
+    test_ifont_size(300000, 0, 1, 2, -529, "1:2 ratio 3");
+
+    /* test various combinations of logical and himetric both set to 2 */
+    test_ifont_size(180000, 0, 2, 2, -635, "2:2 ratio 1");
+    test_ifont_size(240000, 0, 2, 2, -848, "2:2 ratio 2");
+    test_ifont_size(300000, 0, 2, 2, -1058, "2:2 ratio 3");
 }
 
 static void test_QueryInterface(void)
@@ -234,10 +266,7 @@ static void test_type_info(void)
        IFontDisp_Release(fontdisp);
 }
 
-static HRESULT WINAPI FontEventsDisp_QueryInterface(
-        IFontEventsDisp *iface,
-    /* [in] */ REFIID riid,
-    /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
+static HRESULT WINAPI FontEventsDisp_QueryInterface(IFontEventsDisp *iface, REFIID riid, void **ppvObject)
 {
     if (IsEqualIID(riid, &IID_IFontEventsDisp) || IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDispatch))
     {
@@ -264,26 +293,48 @@ static ULONG WINAPI FontEventsDisp_Release(
     return 1;
 }
 
-static int fonteventsdisp_invoke_called = 0;
+static HRESULT WINAPI FontEventsDisp_GetTypeInfoCount(IFontEventsDisp *iface, UINT *pctinfo)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI FontEventsDisp_GetTypeInfo(IFontEventsDisp *iface, UINT itinfo, LCID lcid, ITypeInfo **pptinfo)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI FontEventsDisp_GetIDsOfNames(IFontEventsDisp *iface, REFIID riid, LPOLESTR *names, UINT cNames, LCID lcid,
+    DISPID *dispid)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static int fonteventsdisp_invoke_called;
+static BSTR fonteventsdisp_invoke_arg0;
 
 static HRESULT WINAPI FontEventsDisp_Invoke(
-        IFontEventsDisp __RPC_FAR * iface,
-    /* [in] */ DISPID dispIdMember,
-    /* [in] */ REFIID riid,
-    /* [in] */ LCID lcid,
-    /* [in] */ WORD wFlags,
-    /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams,
-    /* [out] */ VARIANT __RPC_FAR *pVarResult,
-    /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo,
-    /* [out] */ UINT __RPC_FAR *puArgErr)
+        IFontEventsDisp *iface,
+        DISPID dispid,
+        REFIID riid,
+        LCID lcid,
+        WORD wFlags,
+        DISPPARAMS *pDispParams,
+        VARIANT *pVarResult,
+        EXCEPINFO *pExcepInfo,
+        UINT *puArgErr)
 {
-    static const WCHAR wszBold[] = {'B','o','l','d',0};
-    ok(wFlags == INVOKE_FUNC, "invoke flags should have been INVOKE_FUNC instead of 0x%x\n", wFlags);
-    ok(dispIdMember == DISPID_FONT_CHANGED, "dispIdMember should have been DISPID_FONT_CHANGED instead of 0x%x\n", dispIdMember);
-    ok(pDispParams->cArgs == 1, "pDispParams->cArgs should have been 1 instead of %d\n", pDispParams->cArgs);
-    ok(V_VT(&pDispParams->rgvarg[0]) == VT_BSTR, "VT of first param should have been VT_BSTR instead of %d\n", V_VT(&pDispParams->rgvarg[0]));
-    ok(!lstrcmpW(V_BSTR(&pDispParams->rgvarg[0]), wszBold), "String in first param should have been \"Bold\"\n");
+    VARIANTARG *arg0 = &pDispParams->rgvarg[0];
 
+    ok(dispid == DISPID_FONT_CHANGED, "expected DISPID_FONT_CHANGED instead of 0x%x\n", dispid);
+    ok(IsEqualGUID(riid, &GUID_NULL), "got riid %s\n", wine_dbgstr_guid(riid));
+    ok(wFlags == INVOKE_FUNC, "expected INVOKE_FUNC instead of 0x%x\n", wFlags);
+    ok(pDispParams->cArgs == 1, "expected arg count 1, got %d\n", pDispParams->cArgs);
+    ok(V_VT(arg0) == VT_BSTR, "expected VT_BSTR, got %d\n", V_VT(arg0));
+
+    fonteventsdisp_invoke_arg0 = SysAllocString(V_BSTR(arg0));
     fonteventsdisp_invoke_called++;
     return S_OK;
 }
@@ -293,16 +344,43 @@ static IFontEventsDispVtbl FontEventsDisp_Vtbl =
     FontEventsDisp_QueryInterface,
     FontEventsDisp_AddRef,
     FontEventsDisp_Release,
-    NULL,
-    NULL,
-    NULL,
+    FontEventsDisp_GetTypeInfoCount,
+    FontEventsDisp_GetTypeInfo,
+    FontEventsDisp_GetIDsOfNames,
     FontEventsDisp_Invoke
 };
 
 static IFontEventsDisp FontEventsDisp = { &FontEventsDisp_Vtbl };
 
+    struct font_dispid
+    {
+        DISPID dispid;
+        const WCHAR *name;
+    };
+
 static void test_font_events_disp(void)
 {
+    static const WCHAR nameW[] = {'N','a','m','e',0};
+    static const WCHAR sizeW[] = {'S','i','z','e',0};
+    static const WCHAR boldW[] = {'B','o','l','d',0};
+    static const WCHAR italicW[] = {'I','t','a','l','i','c',0};
+    static const WCHAR underlineW[] = {'U','n','d','e','r','l','i','n','e',0};
+    static const WCHAR strikeW[] = {'S','t','r','i','k','e','t','h','r','o','u','g','h',0};
+    static const WCHAR weightW[] = {'W','e','i','g','h','t',0};
+    static const WCHAR charsetW[] = {'C','h','a','r','s','e','t',0};
+
+    static const struct font_dispid font_dispids[] =
+    {
+        { DISPID_FONT_NAME, nameW },
+        { DISPID_FONT_SIZE, sizeW },
+        { DISPID_FONT_BOLD, boldW },
+        { DISPID_FONT_ITALIC, italicW },
+        { DISPID_FONT_UNDER, underlineW },
+        { DISPID_FONT_STRIKE, strikeW },
+        { DISPID_FONT_WEIGHT, weightW },
+        { DISPID_FONT_CHARSET, charsetW }
+    };
+
     IFont *pFont;
     IFont *pFont2;
     IConnectionPointContainer *pCPC;
@@ -313,6 +391,7 @@ static void test_font_events_disp(void)
     IFontDisp *pFontDisp;
     DISPPARAMS dispparams;
     VARIANTARG vararg;
+    INT i;
 
     fontdesc.cbSizeofstruct = sizeof(fontdesc);
     fontdesc.lpstrName = MSSansSerif_font;
@@ -337,6 +416,7 @@ static void test_font_events_disp(void)
     EXPECT_HR(hr, S_OK);
     IConnectionPoint_Release(pCP);
 
+    fonteventsdisp_invoke_called = 0;
     hr = IFont_put_Bold(pFont, TRUE);
     EXPECT_HR(hr, S_OK);
 
@@ -345,30 +425,73 @@ static void test_font_events_disp(void)
     hr = IFont_QueryInterface(pFont, &IID_IFontDisp, (void **)&pFontDisp);
     EXPECT_HR(hr, S_OK);
 
-    V_VT(&vararg) = VT_BOOL;
-    V_BOOL(&vararg) = VARIANT_FALSE;
-    dispparams.cNamedArgs = 0;
-    dispparams.rgdispidNamedArgs = NULL;
-    dispparams.cArgs = 1;
-    dispparams.rgvarg = &vararg;
-    hr = IFontDisp_Invoke(pFontDisp, DISPID_FONT_BOLD, &IID_NULL, 0, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL);
-    EXPECT_HR(hr, S_OK);
+    for (i = 0; i < sizeof(font_dispids)/sizeof(font_dispids[0]); i++)
+    {
+        switch (font_dispids[i].dispid)
+        {
+        case DISPID_FONT_NAME:
+        {
+            static const WCHAR arialW[] = {'A','r','i','a','l',0};
+            V_VT(&vararg) = VT_BSTR;
+            V_BSTR(&vararg) = SysAllocString(arialW);
+            break;
+        }
+        case DISPID_FONT_SIZE:
+            V_VT(&vararg) = VT_CY;
+            S(V_CY(&vararg)).Lo = 25;
+            S(V_CY(&vararg)).Hi = 0;
+            break;
+        case DISPID_FONT_BOLD:
+            V_VT(&vararg) = VT_BOOL;
+            V_BOOL(&vararg) = VARIANT_FALSE;
+            break;
+        case DISPID_FONT_ITALIC:
+        case DISPID_FONT_UNDER:
+        case DISPID_FONT_STRIKE:
+            V_VT(&vararg) = VT_BOOL;
+            V_BOOL(&vararg) = VARIANT_TRUE;
+            break;
+        case DISPID_FONT_WEIGHT:
+            V_VT(&vararg) = VT_I2;
+            V_I2(&vararg) = FW_BLACK;
+            break;
+        case DISPID_FONT_CHARSET:
+            V_VT(&vararg) = VT_I2;
+            V_I2(&vararg) = 1;
+            break;
+        default:
+            ;
+        }
 
-    IFontDisp_Release(pFontDisp);
+        dispparams.cNamedArgs = 0;
+        dispparams.rgdispidNamedArgs = NULL;
+        dispparams.cArgs = 1;
+        dispparams.rgvarg = &vararg;
+        fonteventsdisp_invoke_called = 0;
+        hr = IFontDisp_Invoke(pFontDisp, font_dispids[i].dispid, &IID_NULL, 0, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL);
+        ok(hr == S_OK, "dispid=%d, got 0x%08x\n", font_dispids[i].dispid, hr);
+        ok(fonteventsdisp_invoke_called == 1, "dispid=%d, DISPID_FONT_CHANGED not called, got %d\n", font_dispids[i].dispid,
+            fonteventsdisp_invoke_called);
+        if (hr == S_OK)
+        {
+            ok(!lstrcmpW(font_dispids[i].name, fonteventsdisp_invoke_arg0), "dispid=%d, got %s, expected %s\n",
+                font_dispids[i].dispid, wine_dbgstr_w(fonteventsdisp_invoke_arg0), wine_dbgstr_w(font_dispids[i].name));
+            SysFreeString(fonteventsdisp_invoke_arg0);
+        }
+        VariantClear(&vararg);
+    }
 
-    ok(fonteventsdisp_invoke_called == 2, "IFontEventDisp::Invoke was called %d times instead of twice\n",
-        fonteventsdisp_invoke_called);
+    IFontDisp_Release(pFontDisp);
 
     hr = IFont_Clone(pFont, &pFont2);
     EXPECT_HR(hr, S_OK);
     IFont_Release(pFont);
 
+    /* this test shows that the notification routine isn't called again */
+    fonteventsdisp_invoke_called = 0;
     hr = IFont_put_Bold(pFont2, FALSE);
     EXPECT_HR(hr, S_OK);
-
-    /* this test shows that the notification routine isn't called again */
-    ok(fonteventsdisp_invoke_called == 2, "IFontEventDisp::Invoke was called %d times instead of twice\n",
-        fonteventsdisp_invoke_called);
+    ok(fonteventsdisp_invoke_called == 0, "got %d\n", fonteventsdisp_invoke_called);
 
     IFont_Release(pFont2);
 }
@@ -544,9 +667,9 @@ static void test_IsEqual(void)
     S(fd.cySize).Hi   = 100;
     fd.sWeight        = 0;
     fd.sCharset       = 0;
-    fd.fItalic        = 0;
-    fd.fUnderline     = 0;
-    fd.fStrikethrough = 0;
+    fd.fItalic        = FALSE;
+    fd.fUnderline     = FALSE;
+    fd.fStrikethrough = FALSE;
 
     /* Create font */
     pOleCreateFontIndirect(&fd, &IID_IFont, (void **)&ifnt);
@@ -609,30 +732,30 @@ static void test_IsEqual(void)
     IFont_Release(ifnt2);
 
     /* Test italic setting */
-    fd.fItalic = 1;
+    fd.fItalic = TRUE;
     pOleCreateFontIndirect(&fd, &IID_IFont, (void **)&ifnt2);
     hres = IFont_IsEqual(ifnt,ifnt2);
     ok(hres == S_FALSE,
         "IFont_IsEqual: (Italic) Expected S_FALSE but got 0x%08x\n",hres);
-    fd.fItalic = 0;
+    fd.fItalic = FALSE;
     IFont_Release(ifnt2);
 
     /* Test underline setting */
-    fd.fUnderline = 1;
+    fd.fUnderline = TRUE;
     pOleCreateFontIndirect(&fd, &IID_IFont, (void **)&ifnt2);
     hres = IFont_IsEqual(ifnt,ifnt2);
     ok(hres == S_FALSE,
         "IFont_IsEqual: (Underline) Expected S_FALSE but got 0x%08x\n",hres);
-    fd.fUnderline = 0;
+    fd.fUnderline = FALSE;
     IFont_Release(ifnt2);
 
     /* Test strikethrough setting */
-    fd.fStrikethrough = 1;
+    fd.fStrikethrough = TRUE;
     pOleCreateFontIndirect(&fd, &IID_IFont, (void **)&ifnt2);
     hres = IFont_IsEqual(ifnt,ifnt2);
     ok(hres == S_FALSE,
         "IFont_IsEqual: (Strikethrough) Expected S_FALSE but got 0x%08x\n",hres);
-    fd.fStrikethrough = 0;
+    fd.fStrikethrough = FALSE;
     IFont_Release(ifnt2);
 
     /* Free IFont. */
@@ -657,9 +780,9 @@ static void test_ReleaseHfont(void)
     S(fd.cySize).Hi   = 100;
     fd.sWeight        = 0;
     fd.sCharset       = 0;
-    fd.fItalic        = 0;
-    fd.fUnderline     = 0;
-    fd.fStrikethrough = 0;
+    fd.fItalic        = FALSE;
+    fd.fUnderline     = FALSE;
+    fd.fStrikethrough = FALSE;
 
     /* Create HFONTs and IFONTs */
     pOleCreateFontIndirect(&fd, &IID_IFont, &pvObj1);
@@ -727,9 +850,9 @@ static void test_AddRefHfont(void)
     S(fd.cySize).Hi   = 100;
     fd.sWeight        = 0;
     fd.sCharset       = 0;
-    fd.fItalic        = 0;
-    fd.fUnderline     = 0;
-    fd.fStrikethrough = 0;
+    fd.fItalic        = FALSE;
+    fd.fUnderline     = FALSE;
+    fd.fStrikethrough = FALSE;
 
     /* Create HFONTs and IFONTs */
     pOleCreateFontIndirect(&fd, &IID_IFont, (void **)&ifnt1);
@@ -818,7 +941,7 @@ static void test_AddRefHfont(void)
     IFont_Release(ifnt2);
 
     /* Need to make a new IFONT for testing */
-    fd.fUnderline = 1;
+    fd.fUnderline = TRUE;
     pOleCreateFontIndirect(&fd, &IID_IFont, (void **)&ifnt3);
     IFont_get_hFont(ifnt3,&hfnt3);
 
index 40118a5..df99547 100644 (file)
@@ -224,7 +224,7 @@ test_pic_with_stream(LPSTREAM stream, unsigned int imgsize)
         if (handle)
         {
             BITMAP bmp;
-            GetObject(UlongToHandle(handle), sizeof(BITMAP), &bmp);
+            GetObjectA(UlongToHandle(handle), sizeof(BITMAP), &bmp);
             todo_wine ok(bmp.bmBits != 0, "not a dib\n");
         }
 
@@ -416,12 +416,14 @@ static void test_Invoke(void)
 {
     IPictureDisp *picdisp;
     HRESULT hr;
-    VARIANTARG vararg;
+    VARIANTARG vararg, args[10];
     DISPPARAMS dispparams;
     VARIANT varresult;
     IStream *stream;
     HGLOBAL hglob;
     void *data;
+    HDC hdc;
+    int i;
 
     hglob = GlobalAlloc (0, sizeof(gifimage));
     data = GlobalLock(hglob);
@@ -481,6 +483,44 @@ static void test_Invoke(void)
     hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_NULL, 0, DISPATCH_PROPERTYGET, &dispparams, &varresult, NULL, NULL);
     ok(hr == DISP_E_BADPARAMCOUNT, "IPictureDisp_Invoke should have returned DISP_E_BADPARAMCOUNT instead of 0x%08x\n", hr);
 
+    /* DISPID_PICT_RENDER */
+    hdc = GetDC(0);
+
+    for (i = 0; i < sizeof(args)/sizeof(args[0]); i++)
+        V_VT(&args[i]) = VT_I4;
+
+    V_I4(&args[0]) = 0;
+    V_I4(&args[1]) = 10;
+    V_I4(&args[2]) = 10;
+    V_I4(&args[3]) = 0;
+    V_I4(&args[4]) = 0;
+    V_I4(&args[5]) = 10;
+    V_I4(&args[6]) = 10;
+    V_I4(&args[7]) = 0;
+    V_I4(&args[8]) = 0;
+    V_I4(&args[9]) = HandleToLong(hdc);
+
+    dispparams.rgvarg = args;
+    dispparams.rgdispidNamedArgs = NULL;
+    dispparams.cArgs = 10;
+    dispparams.cNamedArgs = 0;
+
+    V_VT(&varresult) = VT_EMPTY;
+    hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_RENDER, &GUID_NULL, 0, DISPATCH_METHOD, &dispparams, &varresult, NULL, NULL);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    /* Try with one argument set to VT_I2, it'd still work if coerced. */
+    V_VT(&args[3]) = VT_I2;
+    hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_RENDER, &GUID_NULL, 0, DISPATCH_METHOD, &dispparams, &varresult, NULL, NULL);
+    ok(hr == DISP_E_TYPEMISMATCH, "got 0x%08x\n", hr);
+    V_VT(&args[3]) = VT_I4;
+
+    /* Wrong argument count */
+    dispparams.cArgs = 9;
+    hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_RENDER, &GUID_NULL, 0, DISPATCH_METHOD, &dispparams, &varresult, NULL, NULL);
+    ok(hr == DISP_E_BADPARAMCOUNT, "got 0x%08x\n", hr);
+
+    ReleaseDC(NULL, hdc);
     IPictureDisp_Release(picdisp);
 }
 
@@ -616,6 +656,55 @@ static void test_enhmetafile(void)
     IStream_Release(stream);
 }
 
+static HRESULT picture_render(IPicture *iface, HDC hdc, LONG x, LONG y, LONG cx, LONG cy,
+                              OLE_XPOS_HIMETRIC xSrc,
+                              OLE_YPOS_HIMETRIC ySrc,
+                              OLE_XSIZE_HIMETRIC cxSrc,
+                              OLE_YSIZE_HIMETRIC cySrc,
+                              const RECT *bounds)
+{
+    VARIANT ret, args[10];
+    HRESULT hr, hr_disp;
+    DISPPARAMS params;
+    IDispatch *disp;
+    int i;
+
+    hr = IPicture_Render(iface, hdc, x, y, cx, cy, xSrc, ySrc, cxSrc, cySrc, bounds);
+
+    IPicture_QueryInterface(iface, &IID_IDispatch, (void**)&disp);
+
+    /* This is broken on 64 bits - accepted pointer argument type is still VT_I4 */
+    for (i = 0; i < sizeof(args)/sizeof(args[0]); i++)
+        V_VT(&args[i]) = VT_I4;
+
+    /* pack arguments and call */
+    V_INT_PTR(&args[0]) = (INT_PTR)bounds;
+    V_I4(&args[1]) = cySrc;
+    V_I4(&args[2]) = cxSrc;
+    V_I4(&args[3]) = ySrc;
+    V_I4(&args[4]) = xSrc;
+    V_I4(&args[5]) = cy;
+    V_I4(&args[6]) = cx;
+    V_I4(&args[7]) = y;
+    V_I4(&args[8]) = x;
+    V_I4(&args[9]) = HandleToLong(hdc);
+
+    params.rgvarg = args;
+    params.rgdispidNamedArgs = NULL;
+    params.cArgs = 10;
+    params.cNamedArgs = 0;
+
+    V_VT(&ret) = VT_EMPTY;
+    hr_disp = IDispatch_Invoke(disp, DISPID_PICT_RENDER, &GUID_NULL, 0, DISPATCH_METHOD,
+        &params, &ret, NULL, NULL);
+    ok(hr == hr_disp, "DISPID_PICT_RENDER returned wrong code, 0x%08x, expected 0x%08x\n",
+       hr_disp, hr);
+
+    IDispatch_Release(disp);
+
+    return hr;
+}
+
 static void test_Render(void)
 {
     IPicture *pic;
@@ -633,28 +722,28 @@ static void test_Render(void)
     ok(hres == S_OK, "IPicture_get_Type does not return S_OK, but 0x%08x\n", hres);
     ok(type == PICTYPE_UNINITIALIZED, "Expected type = PICTYPE_UNINITIALIZED, got = %d\n", type);
     /* zero dimensions */
-    hres = IPicture_Render(pic, hdc, 0, 0, 0, 0, 0, 0, 0, 0, NULL);
+    hres = picture_render(pic, hdc, 0, 0, 0, 0, 0, 0, 0, 0, NULL);
     ole_expect(hres, CTL_E_INVALIDPROPERTYVALUE);
-    hres = IPicture_Render(pic, hdc, 0, 0, 10, 10, 0, 0, 10, 0, NULL);
+    hres = picture_render(pic, hdc, 0, 0, 10, 10, 0, 0, 10, 0, NULL);
     ole_expect(hres, CTL_E_INVALIDPROPERTYVALUE);
-    hres = IPicture_Render(pic, hdc, 0, 0, 10, 10, 0, 0, 0, 10, NULL);
+    hres = picture_render(pic, hdc, 0, 0, 10, 10, 0, 0, 0, 10, NULL);
     ole_expect(hres, CTL_E_INVALIDPROPERTYVALUE);
-    hres = IPicture_Render(pic, hdc, 0, 0, 10, 10, 0, 0, 0, 0, NULL);
+    hres = picture_render(pic, hdc, 0, 0, 10, 10, 0, 0, 0, 0, NULL);
     ole_expect(hres, CTL_E_INVALIDPROPERTYVALUE);
-    hres = IPicture_Render(pic, hdc, 0, 0, 0, 10, 0, 0, 10, 10, NULL);
+    hres = picture_render(pic, hdc, 0, 0, 0, 10, 0, 0, 10, 10, NULL);
     ole_expect(hres, CTL_E_INVALIDPROPERTYVALUE);
-    hres = IPicture_Render(pic, hdc, 0, 0, 10, 0, 0, 0, 10, 10, NULL);
+    hres = picture_render(pic, hdc, 0, 0, 10, 0, 0, 0, 10, 10, NULL);
     ole_expect(hres, CTL_E_INVALIDPROPERTYVALUE);
-    hres = IPicture_Render(pic, hdc, 0, 0, 0, 0, 0, 0, 10, 10, NULL);
+    hres = picture_render(pic, hdc, 0, 0, 0, 0, 0, 0, 10, 10, NULL);
     ole_expect(hres, CTL_E_INVALIDPROPERTYVALUE);
     /* nonzero dimensions, PICTYPE_UNINITIALIZED */
-    hres = IPicture_Render(pic, hdc, 0, 0, 10, 10, 0, 0, 10, 10, NULL);
+    hres = picture_render(pic, hdc, 0, 0, 10, 10, 0, 0, 10, 10, NULL);
     ole_expect(hres, S_OK);
     IPicture_Release(pic);
 
     desc.cbSizeofstruct = sizeof(PICTDESC);
     desc.picType = PICTYPE_ICON;
-    desc.u.icon.hicon = LoadIcon(NULL, IDI_APPLICATION);
+    desc.u.icon.hicon = LoadIconA(NULL, (LPCSTR)IDI_APPLICATION);
     if(!desc.u.icon.hicon){
         win_skip("LoadIcon failed. Skipping...\n");
         ReleaseDC(NULL, hdc);
@@ -663,19 +752,19 @@ static void test_Render(void)
 
     OleCreatePictureIndirect(&desc, &IID_IPicture, TRUE, (VOID**)&pic);
     /* zero dimensions, PICTYPE_ICON */
-    hres = IPicture_Render(pic, hdc, 0, 0, 0, 0, 0, 0, 0, 0, NULL);
+    hres = picture_render(pic, hdc, 0, 0, 0, 0, 0, 0, 0, 0, NULL);
     ole_expect(hres, CTL_E_INVALIDPROPERTYVALUE);
-    hres = IPicture_Render(pic, hdc, 0, 0, 10, 10, 0, 0, 10, 0, NULL);
+    hres = picture_render(pic, hdc, 0, 0, 10, 10, 0, 0, 10, 0, NULL);
     ole_expect(hres, CTL_E_INVALIDPROPERTYVALUE);
-    hres = IPicture_Render(pic, hdc, 0, 0, 10, 10, 0, 0, 0, 10, NULL);
+    hres = picture_render(pic, hdc, 0, 0, 10, 10, 0, 0, 0, 10, NULL);
     ole_expect(hres, CTL_E_INVALIDPROPERTYVALUE);
-    hres = IPicture_Render(pic, hdc, 0, 0, 10, 10, 0, 0, 0, 0, NULL);
+    hres = picture_render(pic, hdc, 0, 0, 10, 10, 0, 0, 0, 0, NULL);
     ole_expect(hres, CTL_E_INVALIDPROPERTYVALUE);
-    hres = IPicture_Render(pic, hdc, 0, 0, 0, 10, 0, 0, 10, 10, NULL);
+    hres = picture_render(pic, hdc, 0, 0, 0, 10, 0, 0, 10, 10, NULL);
     ole_expect(hres, CTL_E_INVALIDPROPERTYVALUE);
-    hres = IPicture_Render(pic, hdc, 0, 0, 10, 0, 0, 0, 10, 10, NULL);
+    hres = picture_render(pic, hdc, 0, 0, 10, 0, 0, 0, 10, 10, NULL);
     ole_expect(hres, CTL_E_INVALIDPROPERTYVALUE);
-    hres = IPicture_Render(pic, hdc, 0, 0, 0, 0, 0, 0, 10, 10, NULL);
+    hres = picture_render(pic, hdc, 0, 0, 0, 0, 0, 0, 10, 10, NULL);
     ole_expect(hres, CTL_E_INVALIDPROPERTYVALUE);
 
     /* Check if target size and position is respected */
@@ -687,7 +776,7 @@ static void test_Render(void)
     SetPixelV(hdc, 10, 10, 0x00223344);
     expected = GetPixel(hdc, 0, 0);
 
-    hres = IPicture_Render(pic, hdc, 1, 1, 9, 9, 0, 0, pWidth, -pHeight, NULL);
+    hres = picture_render(pic, hdc, 1, 1, 9, 9, 0, 0, pWidth, -pHeight, NULL);
     ole_expect(hres, S_OK);
 
     if(hres != S_OK) {
@@ -1034,7 +1123,7 @@ static void test_load_save_icon(void)
 
     desc.cbSizeofstruct = sizeof(desc);
     desc.picType = PICTYPE_ICON;
-    desc.u.icon.hicon = LoadIcon(0, IDI_APPLICATION);
+    desc.u.icon.hicon = LoadIconA(NULL, (LPCSTR)IDI_APPLICATION);
     hr = OleCreatePictureIndirect(&desc, &IID_IPicture, FALSE, (void**)&pic);
     ok(hr == S_OK, "OleCreatePictureIndirect error %#x\n", hr);
 
@@ -1082,7 +1171,8 @@ static void test_load_save_empty_picture(void)
     HGLOBAL hmem;
     DWORD *mem;
     IPersistStream *src_stream;
-    IStream *dst_stream;
+    IStream *dst_stream, *stream;
+    LARGE_INTEGER offset;
     HRESULT hr;
 
     memset(&pic, 0, sizeof(pic));
@@ -1117,10 +1207,57 @@ static void test_load_save_empty_picture(void)
     GlobalUnlock(hmem);
 
     IPersistStream_Release(src_stream);
+    IPicture_Release(pic);
+
+    /* first with statable and seekable stream */
+    offset.QuadPart = 0;
+    hr = IStream_Seek(dst_stream, offset, SEEK_SET, NULL);
+    ok(hr == S_OK, "IStream_Seek %#x\n", hr);
+
+    pic = NULL;
+    hr = pOleLoadPicture(dst_stream, 0, FALSE, &IID_IPicture, (void **)&pic);
+    ok(hr == S_OK, "OleLoadPicture error %#x\n", hr);
+    ok(pic != NULL,"picture should not be not NULL\n");
+    if (pic != NULL)
+    {
+        type = -1;
+        hr = IPicture_get_Type(pic, &type);
+        ok(hr == S_OK,"get_Type error %#8x\n", hr);
+        ok(type == PICTYPE_NONE,"expected picture type PICTYPE_NONE, got %d\n", type);
+
+        handle = (OLE_HANDLE)0xdeadbeef;
+        hr = IPicture_get_Handle(pic, &handle);
+        ok(hr == S_OK,"get_Handle error %#8x\n", hr);
+        ok(!handle, "get_Handle returned wrong handle %#x\n", handle);
+
+        IPicture_Release(pic);
+    }
     IStream_Release(dst_stream);
 
-    GlobalFree(hmem);
-    IPicture_Release(pic);
+    /* again with non-statable and non-seekable stream */
+    stream = NoStatStream_Construct(hmem);
+    ok(stream != NULL, "failed to create empty image stream\n");
+
+    pic = NULL;
+    hr = pOleLoadPicture(stream, 0, FALSE, &IID_IPicture, (void **)&pic);
+    ok(hr == S_OK, "OleLoadPicture error %#x\n", hr);
+    ok(pic != NULL,"picture should not be not NULL\n");
+    if (pic != NULL)
+    {
+        type = -1;
+        hr = IPicture_get_Type(pic, &type);
+        ok(hr == S_OK,"get_Type error %#8x\n", hr);
+        ok(type == PICTYPE_NONE,"expected picture type PICTYPE_NONE, got %d\n", type);
+
+        handle = (OLE_HANDLE)0xdeadbeef;
+        hr = IPicture_get_Handle(pic, &handle);
+        ok(hr == S_OK,"get_Handle error %#8x\n", hr);
+        ok(!handle, "get_Handle returned wrong handle %#x\n", handle);
+
+        IPicture_Release(pic);
+    }
+    /* Non-statable impl always deletes on release */
+    IStream_Release(stream);
 }
 
 START_TEST(olepicture)
index 859deb3..ae687ff 100644 (file)
 //#include "wtypes.h"
 #include <oleauto.h>
 
+#ifndef FADF_CREATEVECTOR
+  const USHORT FADF_CREATEVECTOR = 0x2000;
+#endif
+
 static HMODULE hOleaut32;
 
 static HRESULT (WINAPI *pSafeArrayAllocDescriptorEx)(VARTYPE,UINT,SAFEARRAY**);
@@ -61,6 +65,14 @@ static BOOL has_i8;
 /* Has INT_PTR/UINT_PTR type? */
 static BOOL has_int_ptr;
 
+static const USHORT ignored_copy_features[] =
+    {
+        FADF_AUTO,
+        FADF_STATIC,
+        FADF_EMBEDDED,
+        FADF_FIXEDSIZE
+    };
+
 #define START_REF_COUNT 1
 #define RECORD_SIZE 64
 #define RECORD_SIZE_FAIL 17
@@ -71,36 +83,38 @@ typedef struct IRecordInfoImpl
 {
   IRecordInfo IRecordInfo_iface;
   LONG ref;
-  DWORD sizeCalled;
-  DWORD clearCalled;
+  unsigned int sizeCalled;
+  unsigned int clearCalled;
+  unsigned int recordcopy;
 } IRecordInfoImpl;
 
-static const IRecordInfoVtbl IRecordInfoImpl_VTable;
-
 static inline IRecordInfoImpl *impl_from_IRecordInfo(IRecordInfo *iface)
 {
   return CONTAINING_RECORD(iface, IRecordInfoImpl, IRecordInfo_iface);
 }
 
-static IRecordInfoImpl *IRecordInfoImpl_Construct(void)
+static HRESULT WINAPI RecordInfo_QueryInterface(IRecordInfo *iface, REFIID riid, void **obj)
 {
-  IRecordInfoImpl *rec;
+  *obj = NULL;
 
-  rec = HeapAlloc(GetProcessHeap(), 0, sizeof(IRecordInfoImpl));
-  rec->IRecordInfo_iface.lpVtbl = &IRecordInfoImpl_VTable;
-  rec->ref = START_REF_COUNT;
-  rec->clearCalled = 0;
-  rec->sizeCalled = 0;
-  return rec;
+  if (IsEqualIID(riid, &IID_IUnknown) ||
+      IsEqualIID(riid, &IID_IRecordInfo))
+  {
+      *obj = iface;
+      IRecordInfo_AddRef(iface);
+      return S_OK;
+  }
+
+  return E_NOINTERFACE;
 }
 
-static ULONG CALLBACK IRecordInfoImpl_AddRef(IRecordInfo *iface)
+static ULONG WINAPI RecordInfo_AddRef(IRecordInfo *iface)
 {
   IRecordInfoImpl* This = impl_from_IRecordInfo(iface);
   return InterlockedIncrement(&This->ref);
 }
 
-static ULONG CALLBACK IRecordInfoImpl_Release(IRecordInfo *iface)
+static ULONG WINAPI RecordInfo_Release(IRecordInfo *iface)
 {
   IRecordInfoImpl* This = impl_from_IRecordInfo(iface);
   ULONG ref = InterlockedDecrement(&This->ref);
@@ -111,16 +125,41 @@ static ULONG CALLBACK IRecordInfoImpl_Release(IRecordInfo *iface)
   return ref;
 }
 
+static HRESULT WINAPI RecordInfo_RecordInit(IRecordInfo *iface, PVOID pvNew)
+{
+  ok(0, "unexpected call\n");
+  return E_NOTIMPL;
+}
+
 static BOOL fail_GetSize; /* Whether to fail the GetSize call */
 
-static HRESULT CALLBACK IRecordInfoImpl_RecordClear(IRecordInfo *iface, PVOID pvExisting)
+static HRESULT WINAPI RecordInfo_RecordClear(IRecordInfo *iface, PVOID pvExisting)
 {
   IRecordInfoImpl* This = impl_from_IRecordInfo(iface);
   This->clearCalled++;
   return S_OK;
 }
 
-static HRESULT CALLBACK IRecordInfoImpl_GetSize(IRecordInfo *iface, ULONG* size)
+static HRESULT WINAPI RecordInfo_RecordCopy(IRecordInfo *iface, PVOID pvExisting, PVOID pvNew)
+{
+  IRecordInfoImpl* This = impl_from_IRecordInfo(iface);
+  This->recordcopy++;
+  return S_OK;
+}
+
+static HRESULT WINAPI RecordInfo_GetGuid(IRecordInfo *iface, GUID *pguid)
+{
+  ok(0, "unexpected call\n");
+  return E_NOTIMPL;
+}
+
+static HRESULT WINAPI RecordInfo_GetName(IRecordInfo *iface, BSTR *pbstrName)
+{
+  ok(0, "unexpected call\n");
+  return E_NOTIMPL;
+}
+
+static HRESULT WINAPI RecordInfo_GetSize(IRecordInfo *iface, ULONG* size)
 {
   IRecordInfoImpl* This = impl_from_IRecordInfo(iface);
   This->sizeCalled++;
@@ -133,37 +172,107 @@ static HRESULT CALLBACK IRecordInfoImpl_GetSize(IRecordInfo *iface, ULONG* size)
   return S_OK;
 }
 
-static HRESULT CALLBACK IRecordInfoImpl_Dummy(IRecordInfo *iface)
+static HRESULT WINAPI RecordInfo_GetTypeInfo(IRecordInfo *iface, ITypeInfo **ppTypeInfo)
+{
+  ok(0, "unexpected call\n");
+  return E_NOTIMPL;
+}
+
+static HRESULT WINAPI RecordInfo_GetField(IRecordInfo *iface, PVOID pvData,
+                                                LPCOLESTR szFieldName, VARIANT *pvarField)
 {
-  trace("Called an unexpected IRecordInfo method - please report!\n");
-  /* Quit because we'll just crash anyway */
-  fflush(NULL);
-  exit(255);
+  ok(0, "unexpected call\n");
+  return E_NOTIMPL;
 }
 
-static const IRecordInfoVtbl IRecordInfoImpl_VTable =
+static HRESULT WINAPI RecordInfo_GetFieldNoCopy(IRecordInfo *iface, PVOID pvData,
+                            LPCOLESTR szFieldName, VARIANT *pvarField, PVOID *ppvDataCArray)
 {
-  (PVOID)IRecordInfoImpl_Dummy,
-  IRecordInfoImpl_AddRef,
-  IRecordInfoImpl_Release,
-  (PVOID)IRecordInfoImpl_Dummy,
-  IRecordInfoImpl_RecordClear,
-  (PVOID)IRecordInfoImpl_Dummy,
-  (PVOID)IRecordInfoImpl_Dummy,
-  (PVOID)IRecordInfoImpl_Dummy,
-  IRecordInfoImpl_GetSize,
-  (PVOID)IRecordInfoImpl_Dummy,
-  (PVOID)IRecordInfoImpl_Dummy,
-  (PVOID)IRecordInfoImpl_Dummy,
-  (PVOID)IRecordInfoImpl_Dummy,
-  (PVOID)IRecordInfoImpl_Dummy,
-  (PVOID)IRecordInfoImpl_Dummy,
-  (PVOID)IRecordInfoImpl_Dummy,
-  (PVOID)IRecordInfoImpl_Dummy,
-  (PVOID)IRecordInfoImpl_Dummy,
-  (PVOID)IRecordInfoImpl_Dummy
+  ok(0, "unexpected call\n");
+  return E_NOTIMPL;
+}
+
+static HRESULT WINAPI RecordInfo_PutField(IRecordInfo *iface, ULONG wFlags, PVOID pvData,
+                                            LPCOLESTR szFieldName, VARIANT *pvarField)
+{
+  ok(0, "unexpected call\n");
+  return E_NOTIMPL;
+}
+
+static HRESULT WINAPI RecordInfo_PutFieldNoCopy(IRecordInfo *iface, ULONG wFlags,
+                PVOID pvData, LPCOLESTR szFieldName, VARIANT *pvarField)
+{
+  ok(0, "unexpected call\n");
+  return E_NOTIMPL;
+}
+
+static HRESULT WINAPI RecordInfo_GetFieldNames(IRecordInfo *iface, ULONG *pcNames,
+                                                BSTR *rgBstrNames)
+{
+  ok(0, "unexpected call\n");
+  return E_NOTIMPL;
+}
+
+static BOOL WINAPI RecordInfo_IsMatchingType(IRecordInfo *iface, IRecordInfo *info2)
+{
+  ok(0, "unexpected call\n");
+  return E_NOTIMPL;
+}
+
+static PVOID WINAPI RecordInfo_RecordCreate(IRecordInfo *iface)
+{
+  ok(0, "unexpected call\n");
+  return NULL;
+}
+
+static HRESULT WINAPI RecordInfo_RecordCreateCopy(IRecordInfo *iface, PVOID pvSource,
+                                                    PVOID *ppvDest)
+{
+  ok(0, "unexpected call\n");
+  return E_NOTIMPL;
+}
+
+static HRESULT WINAPI RecordInfo_RecordDestroy(IRecordInfo *iface, PVOID pvRecord)
+{
+  ok(0, "unexpected call\n");
+  return E_NOTIMPL;
+}
+
+static const IRecordInfoVtbl RecordInfoVtbl =
+{
+  RecordInfo_QueryInterface,
+  RecordInfo_AddRef,
+  RecordInfo_Release,
+  RecordInfo_RecordInit,
+  RecordInfo_RecordClear,
+  RecordInfo_RecordCopy,
+  RecordInfo_GetGuid,
+  RecordInfo_GetName,
+  RecordInfo_GetSize,
+  RecordInfo_GetTypeInfo,
+  RecordInfo_GetField,
+  RecordInfo_GetFieldNoCopy,
+  RecordInfo_PutField,
+  RecordInfo_PutFieldNoCopy,
+  RecordInfo_GetFieldNames,
+  RecordInfo_IsMatchingType,
+  RecordInfo_RecordCreate,
+  RecordInfo_RecordCreateCopy,
+  RecordInfo_RecordDestroy
 };
 
+static IRecordInfoImpl *IRecordInfoImpl_Construct(void)
+{
+  IRecordInfoImpl *rec;
+
+  rec = HeapAlloc(GetProcessHeap(), 0, sizeof(IRecordInfoImpl));
+  rec->IRecordInfo_iface.lpVtbl = &RecordInfoVtbl;
+  rec->ref = START_REF_COUNT;
+  rec->clearCalled = 0;
+  rec->sizeCalled = 0;
+  return rec;
+}
+
 static DWORD SAFEARRAY_GetVTSize(VARTYPE vt)
 {
   switch (vt)
@@ -957,10 +1066,11 @@ test_LockUnlock_Vector:
 static void test_SafeArrayGetPutElement(void)
 {
   SAFEARRAYBOUND sab[4];
-  LONG indices[NUM_DIMENSIONS];
+  LONG indices[NUM_DIMENSIONS], index;
   SAFEARRAY *sa;
   HRESULT hres;
   int value = 0, gotvalue, dimension;
+  IRecordInfoImpl *irec;
   unsigned int x,y,z,a;
 
   for (dimension = 0; dimension < NUM_DIMENSIONS; dimension++)
@@ -974,8 +1084,6 @@ static void test_SafeArrayGetPutElement(void)
     return; /* Some early versions can't handle > 3 dims */
 
   ok(sa->cbElements == sizeof(value), "int size mismatch\n");
-  if (sa->cbElements != sizeof(value))
-    return;
 
   /* Failure cases */
   for (x = 0; x < NUM_DIMENSIONS; x++)
@@ -1079,6 +1187,34 @@ static void test_SafeArrayGetPutElement(void)
   }
   hres = SafeArrayDestroy(sa);
   ok(hres == S_OK, "got 0x%08x\n", hres);
+
+  /* VT_RECORD array */
+  irec = IRecordInfoImpl_Construct();
+  irec->ref = 1;
+
+  sab[0].lLbound = 0;
+  sab[0].cElements = 8;
+
+  sa = pSafeArrayCreateEx(VT_RECORD, 1, sab, &irec->IRecordInfo_iface);
+  ok(sa != NULL, "failed to create array\n");
+  ok(irec->ref == 2, "got %d\n", irec->ref);
+
+  index = 0;
+  irec->recordcopy = 0;
+  hres = SafeArrayPutElement(sa, &index, (void*)0xdeadbeef);
+  ok(hres == S_OK, "got 0x%08x\n", hres);
+  ok(irec->recordcopy == 1, "got %d\n", irec->recordcopy);
+
+  index = 0;
+  irec->recordcopy = 0;
+  hres = SafeArrayGetElement(sa, &index, (void*)0xdeadbeef);
+  ok(hres == S_OK, "got 0x%08x\n", hres);
+  ok(irec->recordcopy == 1, "got %d\n", irec->recordcopy);
+
+  hres = SafeArrayDestroy(sa);
+  ok(hres == S_OK, "got 0x%08x\n", hres);
+  ok(irec->ref == 1, "got %d\n", irec->ref);
+  IRecordInfo_Release(&irec->IRecordInfo_iface);
 }
 
 static void test_SafeArrayGetPutElement_BSTR(void)
@@ -1099,8 +1235,6 @@ static void test_SafeArrayGetPutElement_BSTR(void)
     return;
 
   ok(sa->cbElements == sizeof(BSTR), "BSTR size mismatch\n");
-  if (sa->cbElements != sizeof(BSTR))
-    return;
 
   indices[0] = sab.lLbound;
   value = SysAllocString(szTest);
@@ -1163,8 +1297,6 @@ static void test_SafeArrayGetPutElement_IUnknown(void)
     return;
 
   ok(sa->cbElements == sizeof(LPUNKNOWN), "LPUNKNOWN size mismatch\n");
-  if (sa->cbElements != sizeof(LPUNKNOWN))
-    return;
 
   indices[0] = sab.lLbound;
   xtunk.ref = 1;
@@ -1197,8 +1329,6 @@ static void test_SafeArrayRedim_IUnknown(void)
     return;
 
   ok(sa->cbElements == sizeof(LPUNKNOWN), "LPUNKNOWN size mismatch\n");
-  if (sa->cbElements != sizeof(LPUNKNOWN))
-    return;
 
   indices[0] = 2;
   xtunk.ref = 1;
@@ -1229,8 +1359,6 @@ static void test_SafeArrayGetPutElement_VARIANT(void)
     return;
 
   ok(sa->cbElements == sizeof(VARIANT), "VARIANT size mismatch\n");
-  if (sa->cbElements != sizeof(VARIANT))
-    return;
 
   indices[0] = sab.lLbound;
   V_VT(&value) = VT_I4;
@@ -1254,14 +1382,13 @@ static void test_SafeArrayGetPutElement_VARIANT(void)
   ok(hres == S_OK, "got 0x%08x\n", hres);
 }
 
-
 static void test_SafeArrayCopyData(void)
 {
   SAFEARRAYBOUND sab[4];
   SAFEARRAY *sa;
   SAFEARRAY *sacopy;
   HRESULT hres;
-  int dimension,size=1;
+  int dimension, size = 1, i;
 
   if (!pSafeArrayCopyData)
   {
@@ -1285,8 +1412,6 @@ static void test_SafeArrayCopyData(void)
     return;
 
   ok(sa->cbElements == sizeof(int), "int size mismatch\n");
-  if (sa->cbElements != sizeof(int))
-    return;
 
   /* Fill the source array with some data; it doesn't matter what */
   for (dimension = 0; dimension < size; dimension++)
@@ -1334,17 +1459,58 @@ static void test_SafeArrayCopyData(void)
 
   hres = SafeArrayCopy(sa, &sacopy);
   ok(hres == S_OK, "copy failed hres 0x%x\n", hres);
-  if (hres == S_OK)
+  ok(SafeArrayGetElemsize(sa) == SafeArrayGetElemsize(sacopy),"elemsize wrong\n");
+  ok(SafeArrayGetDim(sa) == SafeArrayGetDim(sacopy),"dimensions wrong\n");
+  ok(!memcmp(sa->pvData, sacopy->pvData, size * sizeof(int)), "compared different\n");
+  hres = SafeArrayDestroy(sacopy);
+  ok(hres == S_OK, "got 0x%08x\n", hres);
+
+  sacopy = SafeArrayCreate(VT_INT, NUM_DIMENSIONS, sab);
+  ok(sacopy != NULL, "Copy test couldn't create copy array\n");
+  ok(sacopy->fFeatures == FADF_HAVEVARTYPE, "0x%04x\n", sacopy->fFeatures);
+
+  for (i = 0; i < sizeof(ignored_copy_features)/sizeof(USHORT); i++)
   {
-    ok(SafeArrayGetElemsize(sa) == SafeArrayGetElemsize(sacopy),"elemsize wrong\n");
-    ok(SafeArrayGetDim(sa) == SafeArrayGetDim(sacopy),"dimensions wrong\n");
-    ok(!memcmp(sa->pvData, sacopy->pvData, size * sizeof(int)), "compared different\n");
-    hres = SafeArrayDestroy(sacopy);
-    ok(hres == S_OK, "got 0x%08x\n", hres);
+      USHORT feature = ignored_copy_features[i];
+      USHORT orig = sacopy->fFeatures;
+
+      sa->fFeatures |= feature;
+      hres = SafeArrayCopyData(sa, sacopy);
+      ok(hres == S_OK, "got 0x%08x\n", hres);
+      ok(sacopy->fFeatures == orig && orig == FADF_HAVEVARTYPE, "got features 0x%04x\n", sacopy->fFeatures);
+      sa->fFeatures &= ~feature;
   }
 
+  hres = SafeArrayDestroy(sacopy);
+  ok(hres == S_OK, "got 0x%08x\n", hres);
   hres = SafeArrayDestroy(sa);
   ok(hres == S_OK, "got 0x%08x\n", hres);
+
+  /* copy data from a vector */
+  sa = SafeArrayCreateVector(VT_UI1, 0, 2);
+
+  sacopy = SafeArrayCreateVector(VT_UI1, 0, 2);
+  ok(sa->fFeatures == (FADF_HAVEVARTYPE|FADF_CREATEVECTOR) ||
+     broken(sa->fFeatures == FADF_CREATEVECTOR /* W2k */),
+     "got 0x%08x\n", sa->fFeatures);
+  ok(sacopy->fFeatures == (FADF_HAVEVARTYPE|FADF_CREATEVECTOR) ||
+     broken(sacopy->fFeatures == FADF_CREATEVECTOR /* W2k */),
+     "got 0x%08x\n", sacopy->fFeatures);
+  hres = SafeArrayCopyData(sa, sacopy);
+  ok(hres == S_OK, "got 0x%08x\n", hres);
+  ok(sacopy->fFeatures == (FADF_HAVEVARTYPE|FADF_CREATEVECTOR) ||
+     broken(sacopy->fFeatures == FADF_CREATEVECTOR /* W2k */),
+     "got 0x%04x\n", sacopy->fFeatures);
+  SafeArrayDestroy(sacopy);
+
+  sacopy = SafeArrayCreate(VT_UI1, NUM_DIMENSIONS, sab);
+  ok(sacopy != NULL, "Copy test couldn't create copy array\n");
+  ok(sacopy->fFeatures == FADF_HAVEVARTYPE, "0x%04x\n", sacopy->fFeatures);
+  hres = SafeArrayCopyData(sa, sacopy);
+  ok(hres == E_INVALIDARG, "got 0x%08x\n", hres);
+  SafeArrayDestroy(sacopy);
+
+  SafeArrayDestroy(sa);
 }
 
 static void test_SafeArrayCreateEx(void)
@@ -1447,7 +1613,7 @@ static void test_SafeArrayCreateEx(void)
 
   /* Win32 doesn't care if GetSize fails */
   fail_GetSize = TRUE;
-  sa = pSafeArrayCreateEx(VT_RECORD, 1, sab, iRec);
+  sa = pSafeArrayCreateEx(VT_RECORD, 1, sab, &iRec->IRecordInfo_iface);
   ok(sa != NULL, "CreateEx (Fail Size) failed\n");
   ok(iRec->ref == START_REF_COUNT + 1, "Wrong iRec refcount %d\n", iRec->ref);
   ok(iRec->sizeCalled == 1, "GetSize called %d times\n", iRec->sizeCalled);
@@ -1466,7 +1632,7 @@ static void test_SafeArrayCreateEx(void)
   iRec->ref = START_REF_COUNT;
   iRec->sizeCalled = 0;
   iRec->clearCalled = 0;
-  sa = pSafeArrayCreateEx(VT_RECORD, 1, sab, iRec);
+  sa = pSafeArrayCreateEx(VT_RECORD, 1, sab, &iRec->IRecordInfo_iface);
   ok(sa != NULL, "CreateEx (Rec) failed\n");
   ok(iRec->ref == START_REF_COUNT + 1, "Wrong iRec refcount %d\n", iRec->ref);
   ok(iRec->sizeCalled == 1, "GetSize called %d times\n", iRec->sizeCalled);
@@ -1474,19 +1640,34 @@ static void test_SafeArrayCreateEx(void)
   if (sa && pSafeArrayGetRecordInfo)
   {
     IRecordInfo* saRec = NULL;
-    hres = pSafeArrayGetRecordInfo(sa, &saRec);
+    SAFEARRAY *sacopy;
 
+    hres = pSafeArrayGetRecordInfo(sa, &saRec);
     ok(hres == S_OK,"GRI failed\n");
     ok(saRec == &iRec->IRecordInfo_iface, "Different saRec\n");
     ok(iRec->ref == START_REF_COUNT + 2, "Didn't AddRef %d\n", iRec->ref);
-    if (iRec->ref == START_REF_COUNT + 2)
-      IRecordInfo_Release(saRec);
+    IRecordInfo_Release(saRec);
 
     ok(sa->cbElements == RECORD_SIZE,"Elemsize is %d\n", sa->cbElements);
 
+    /* try to copy record based arrays */
+    sacopy = pSafeArrayCreateEx(VT_RECORD, 1, sab, &iRec->IRecordInfo_iface);
+    iRec->recordcopy = 0;
+    iRec->clearCalled = 0;
+    /* array copy code doesn't explicitely clear a record */
+    hres = SafeArrayCopyData(sa, sacopy);
+    ok(hres == S_OK, "got 0x%08x\n", hres);
+    ok(iRec->recordcopy == sab[0].cElements, "got %d\n", iRec->recordcopy);
+    ok(iRec->clearCalled == 0, "got %d\n", iRec->clearCalled);
+
+    hres = SafeArrayDestroy(sacopy);
+    ok(hres == S_OK, "got 0x%08x\n", hres);
+
+    iRec->clearCalled = 0;
+    iRec->sizeCalled = 0;
     hres = SafeArrayDestroy(sa);
     ok(hres == S_OK, "got 0x%08x\n", hres);
-    ok(iRec->sizeCalled == 1, "Destroy->GetSize called %d times\n", iRec->sizeCalled);
+    ok(iRec->sizeCalled == 0, "Destroy->GetSize called %d times\n", iRec->sizeCalled);
     ok(iRec->clearCalled == sab[0].cElements, "Destroy->Clear called %d times\n", iRec->clearCalled);
     ok(iRec->ref == START_REF_COUNT, "Wrong iRec refcount %d\n", iRec->ref);
   }
@@ -1545,6 +1726,7 @@ static void test_SafeArrayCopy(void)
   SAFEARRAY *sa, *sa2;
   VARIANTARG vSrc, vDst;
   HRESULT hres;
+  int i;
 
   sab.lLbound = 0;
   sab.cElements = 10;
@@ -1609,6 +1791,41 @@ static void test_SafeArrayCopy(void)
   ok(hres == S_OK, "got 0x%08x\n", hres);
   hres = SafeArrayDestroy(sa);
   ok(hres == S_OK, "got 0x%08x\n", hres);
+
+  /* test feature copy */
+  hres = SafeArrayAllocDescriptor(1, &sa);
+  ok(hres == S_OK, "SafeArrayAllocDescriptor failed with error 0x%08x\n", hres);
+  ok(sa->fFeatures == 0, "got src features 0x%04x\n", sa->fFeatures);
+  sa->cbElements = 16;
+
+  for (i = 0; i < sizeof(ignored_copy_features)/sizeof(USHORT); i++)
+  {
+      USHORT feature = ignored_copy_features[i];
+
+      sa->fFeatures |= feature;
+      hres = SafeArrayCopy(sa, &sa2);
+      ok(hres == S_OK, "got 0x%08x\n", hres);
+      ok(sa2->fFeatures == 0, "got features 0x%04x\n", sa2->fFeatures);
+      hres = SafeArrayDestroy(sa2);
+      ok(hres == S_OK, "got 0x%08x\n", hres);
+      sa->fFeatures &= ~feature;
+  }
+
+  SafeArrayDestroy(sa);
+
+  /* copy from a vector */
+  sa = SafeArrayCreateVector(VT_UI1, 0, 2);
+  ok(sa->fFeatures == (FADF_HAVEVARTYPE|FADF_CREATEVECTOR) ||
+     broken(sa->fFeatures == FADF_CREATEVECTOR /* W2k */),
+     "got 0x%08x\n", sa->fFeatures);
+  hres = SafeArrayCopy(sa, &sa2);
+  ok(hres == S_OK, "got 0x%08x\n", hres);
+  ok(sa2->fFeatures == FADF_HAVEVARTYPE ||
+     broken(!sa2->fFeatures /* W2k */), "got 0x%04x\n",
+     sa2->fFeatures);
+
+  SafeArrayDestroy(sa2);
+  SafeArrayDestroy(sa);
 }
 
 #define MKARRAY(low,num,typ) sab.lLbound = low; sab.cElements = num; \
@@ -1753,7 +1970,7 @@ static void test_SafeArrayDestroyData (void)
   HRESULT hres;
   int value = 0xdeadbeef;
   LONG index[1];
-  void HUGEP *temp_pvData;
+  void *temp_pvData;
 
   sab.lLbound = 0;
   sab.cElements = 10;
@@ -1784,6 +2001,63 @@ static void test_SafeArrayDestroyData (void)
   ok(hres == S_OK, "SAD failed, error code %x.\n", hres);
 }
 
+static void test_safearray_layout(void)
+{
+    IRecordInfoImpl *irec;
+    IRecordInfo *record;
+    GUID guid, *guidptr;
+    SAFEARRAYBOUND sab;
+    SAFEARRAY *sa;
+    DWORD *dwptr;
+    HRESULT hr;
+
+    sab.lLbound = 0;
+    sab.cElements = 10;
+
+    /* GUID field */
+    sa = SafeArrayCreate(VT_UNKNOWN, 1, &sab);
+    ok(sa != NULL, "got %p\n", sa);
+
+    guidptr = (GUID*)sa - 1;
+    ok(IsEqualIID(guidptr, &IID_IUnknown), "got %s\n", wine_dbgstr_guid(guidptr));
+
+    hr = SafeArraySetIID(sa, &IID_IDispatch);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(IsEqualIID(guidptr, &IID_IDispatch), "got %s\n", wine_dbgstr_guid(guidptr));
+
+    memcpy(guidptr, &IID_IUnknown, sizeof(GUID));
+    hr = SafeArrayGetIID(sa, &guid);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(IsEqualIID(&guid, &IID_IUnknown), "got %s\n", wine_dbgstr_guid(&guid));
+
+    hr = SafeArrayDestroy(sa);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    /* VARTYPE field */
+    sa = SafeArrayCreate(VT_UI1, 1, &sab);
+    ok(sa != NULL, "got %p\n", sa);
+
+    dwptr = (DWORD*)sa - 1;
+    ok(*dwptr == VT_UI1, "got %d\n", *dwptr);
+
+    hr = SafeArrayDestroy(sa);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    /* IRecordInfo pointer */
+    irec = IRecordInfoImpl_Construct();
+    irec->ref = 1;
+
+    sa = pSafeArrayCreateEx(VT_RECORD, 1, &sab, &irec->IRecordInfo_iface);
+    ok(sa != NULL, "failed to create array\n");
+
+    record = *((IRecordInfo**)sa - 1);
+    ok(record == &irec->IRecordInfo_iface, "got %p\n", record);
+
+    hr = SafeArrayDestroy(sa);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    IRecordInfo_Release(&irec->IRecordInfo_iface);
+}
+
 START_TEST(safearray)
 {
     hOleaut32 = GetModuleHandleA("oleaut32.dll");
@@ -1816,4 +2090,5 @@ START_TEST(safearray)
     test_SafeArrayGetPutElement_IUnknown();
     test_SafeArrayRedim_IUnknown();
     test_SafeArrayGetPutElement_VARIANT();
+    test_safearray_layout();
 }
index 7f73b25..ff32a1c 100644 (file)
@@ -18,6 +18,9 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#pragma makedep ident
+#pragma makedep typelib
+
 import "oaidl.idl"; /* needed by widl */
 
 [
@@ -134,5 +137,9 @@ library register_test
     {
         [propget, id(DISPID_VALUE)]
         LONG test([in] LONG i);
+        [propputref, id(1)]
+        LONG testprop([in] LONG *i);
+        [propputref, id(2)]
+        LONG testprop2([in] IUnknown *i);
     }
 }
index 91b6a76..59425be 100644 (file)
@@ -18,6 +18,9 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#pragma makedep ident
+#pragma makedep typelib
+
 import "oaidl.idl"; /* needed by widl */
 
 [uuid(8b05fe77-4a6c-4133-b9cd-8f81747af784)]
@@ -36,4 +39,13 @@ library Test
        {
                HRESULT test();
        }
+
+       [uuid(4029f190-ca4a-4611-aeb9-673983cb96dd)]
+       struct test_struct
+       {
+               HRESULT hr;
+               VARIANT_BOOL b;
+               IDispatch *disp;
+               BSTR bstr;
+       };
 }
index 68361c6..5222d0e 100644 (file)
@@ -41,7 +41,7 @@
 static HRESULT (WINAPI *pVarAdd)(LPVARIANT,LPVARIANT,LPVARIANT);
 
 
-#define ok_ole_success(hr, func) ok(hr == S_OK, #func " failed with error 0x%08lx\n", (unsigned long int)hr)
+#define ok_ole_success(hr, func) ok(hr == S_OK, #func " failed with error 0x%08x\n", hr)
 
 /* ULL suffix is not portable */
 #define ULL_CONST(dw1, dw2) ((((ULONGLONG)dw1) << 32) | (ULONGLONG)dw2)
@@ -89,11 +89,11 @@ static DWORD CALLBACK host_object_proc(LPVOID p)
     ok_ole_success(hr, CoMarshalInterface);
 
     /* force the message queue to be created before signaling parent thread */
-    PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
+    PeekMessageA(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
 
     SetEvent(data->marshal_event);
 
-    while (GetMessage(&msg, NULL, 0, 0))
+    while (GetMessageA(&msg, NULL, 0, 0))
     {
         if (msg.hwnd == NULL && msg.message == RELEASEMARSHALDATA)
         {
@@ -102,7 +102,7 @@ static DWORD CALLBACK host_object_proc(LPVOID p)
             SetEvent((HANDLE)msg.lParam);
         }
         else
-            DispatchMessage(&msg);
+            DispatchMessageA(&msg);
     }
 
     HeapFree(GetProcessHeap(), 0, data);
@@ -115,7 +115,7 @@ static DWORD CALLBACK host_object_proc(LPVOID p)
 static DWORD start_host_object2(IStream *stream, REFIID riid, IUnknown *object, MSHLFLAGS marshal_flags, IMessageFilter *filter, HANDLE *thread)
 {
     DWORD tid = 0;
-    HANDLE marshal_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+    HANDLE marshal_event = CreateEventA(NULL, FALSE, FALSE, NULL);
     struct host_object_data *data = HeapAlloc(GetProcessHeap(), 0, sizeof(*data));
 
     data->stream = stream;
@@ -144,8 +144,8 @@ static DWORD start_host_object(IStream *stream, REFIID riid, IUnknown *object, M
  * same thread that marshaled the interface in the first place. */
 static void release_host_object(DWORD tid)
 {
-    HANDLE event = CreateEvent(NULL, FALSE, FALSE, NULL);
-    PostThreadMessage(tid, RELEASEMARSHALDATA, 0, (LPARAM)event);
+    HANDLE event = CreateEventA(NULL, FALSE, FALSE, NULL);
+    PostThreadMessageA(tid, RELEASEMARSHALDATA, 0, (LPARAM)event);
     WaitForSingleObject(event, INFINITE);
     CloseHandle(event);
 }
@@ -153,7 +153,7 @@ static void release_host_object(DWORD tid)
 
 static void end_host_object(DWORD tid, HANDLE thread)
 {
-    BOOL ret = PostThreadMessage(tid, WM_QUIT, 0, 0);
+    BOOL ret = PostThreadMessageA(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);
@@ -730,9 +730,8 @@ static BOOL mystruct_uint_ordered(MYSTRUCT *mystruct)
     int i;
     for (i = 0; i < sizeof(mystruct->uarr)/sizeof(mystruct->uarr[0]); i++)
         if (mystruct->uarr[i] != i)
-            return 0;
-
-    return 1;
+            return FALSE;
+    return TRUE;
 }
 
 static HRESULT WINAPI Widget_StructArgs(
@@ -835,7 +834,7 @@ static HRESULT WINAPI Widget_put_prop_req_arg(
     return S_OK;
 }
 
-static HRESULT WINAPI Widget_do_restrict(IWidget* iface, INT *i)
+static HRESULT WINAPI Widget__restrict(IWidget* iface, INT *i)
 {
     trace("restrict\n");
     *i = DISPID_TM_RESTRICTED;
@@ -885,7 +884,7 @@ static const struct IWidgetVtbl Widget_VTable =
     Widget_ByRefUInt,
     Widget_put_prop_opt_arg,
     Widget_put_prop_req_arg,
-    Widget_do_restrict,
+    Widget__restrict,
     Widget_neg_restrict
 };
 
@@ -1204,7 +1203,7 @@ static void test_typelibmarshal(void)
 {
     static const WCHAR szCat[] = { 'C','a','t',0 };
     static const WCHAR szTestTest[] = { 'T','e','s','t','T','e','s','t',0 };
-    static WCHAR szSuperman[] = { 'S','u','p','e','r','m','a','n',0 };
+    static const WCHAR szSuperman[] = { 'S','u','p','e','r','m','a','n',0 };
     HRESULT hr;
     IKindaEnumWidget *pKEW = KindaEnumWidget_Create();
     IWidget *pWidget;
index 92b5435..60854db 100644 (file)
@@ -17,6 +17,9 @@
  *
  */
 
+#pragma makedep ident
+#pragma makedep typelib
+
 #include "tmarshal_dispids.h"
 import "ocidl.idl";
 
index bb99aa6..f515bc6 100644 (file)
 #include <tmarshal.h>
 
 #include <test_reg.h>
+#include <test_tlb.h>
 
 #define expect_eq(expr, value, type, format) { type _ret = (expr); ok((value) == _ret, #expr " expected " format " got " format "\n", value, _ret); }
 #define expect_int(expr, value) expect_eq(expr, (int)(value), int, "%d")
 #define expect_hex(expr, value) expect_eq(expr, (int)(value), int, "0x%x")
 #define expect_null(expr) expect_eq(expr, NULL, const void *, "%p")
+#define expect_guid(expected, guid) { ok(IsEqualGUID(expected, guid), "got wrong guid\n"); }
 
 #define expect_wstr_acpval(expr, value) \
     { \
         CHAR buf[260]; \
         expect_eq(!WideCharToMultiByte(CP_ACP, 0, (expr), -1, buf, 260, NULL, NULL), 0, int, "%d"); \
-        ok(lstrcmp(value, buf) == 0, #expr " expected \"%s\" got \"%s\"\n", value, buf); \
+        ok(strcmp(value, buf) == 0, #expr " expected \"%s\" got \"%s\"\n", value, buf); \
     }
 
 #define ole_expect(expr, expect) { \
@@ -83,7 +85,7 @@ static const WCHAR wszStdOle2[] = {'s','t','d','o','l','e','2','.','t','l','b',0
 static WCHAR wszGUID[] = {'G','U','I','D',0};
 static WCHAR wszguid[] = {'g','u','i','d',0};
 
-static const int is_win64 = sizeof(void *) > sizeof(int);
+static const BOOL is_win64 = sizeof(void *) > sizeof(int);
 
 static HRESULT WINAPI invoketest_QueryInterface(IInvokeTest *iface, REFIID riid, void **ret)
 {
@@ -140,6 +142,16 @@ static LONG WINAPI invoketest_get_test(IInvokeTest *iface, LONG i)
     return i+1;
 }
 
+static LONG WINAPI invoketest_putref_testprop(IInvokeTest *iface, LONG *i)
+{
+    return *i+2;
+}
+
+static LONG WINAPI invoketest_putref_testprop2(IInvokeTest *iface, IUnknown *i)
+{
+    return 6;
+}
+
 static const IInvokeTestVtbl invoketestvtbl = {
     invoketest_QueryInterface,
     invoketest_AddRef,
@@ -148,7 +160,9 @@ static const IInvokeTestVtbl invoketestvtbl = {
     invoketest_GetTypeInfo,
     invoketest_GetIDsOfNames,
     invoketest_Invoke,
-    invoketest_get_test
+    invoketest_get_test,
+    invoketest_putref_testprop,
+    invoketest_putref_testprop2
 };
 
 static IInvokeTest invoketest = { &invoketestvtbl };
@@ -635,14 +649,14 @@ static void write_typelib(int res_no, const char *filename)
     HRSRC res;
     void *ptr;
 
-    file = CreateFile( filename, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0 );
+    file = CreateFileA( filename, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0 );
     ok( file != INVALID_HANDLE_VALUE, "file creation failed\n" );
     if (file == INVALID_HANDLE_VALUE) return;
-    res = FindResource( GetModuleHandle(0), MAKEINTRESOURCE(res_no), "TYPELIB" );
+    res = FindResourceA( GetModuleHandleA(NULL), (LPCSTR)MAKEINTRESOURCE(res_no), "TYPELIB" );
     ok( res != 0, "couldn't find resource\n" );
-    ptr = LockResource( LoadResource( GetModuleHandle(0), res ));
-    WriteFile( file, ptr, SizeofResource( GetModuleHandle(0), res ), &written, NULL );
-    ok( written == SizeofResource( GetModuleHandle(0), res ), "couldn't write resource\n" );
+    ptr = LockResource( LoadResource( GetModuleHandleA(NULL), res ));
+    WriteFile( file, ptr, SizeofResource( GetModuleHandleA(NULL), res ), &written, NULL );
+    ok( written == SizeofResource( GetModuleHandleA(NULL), res ), "couldn't write resource\n" );
     CloseHandle( file );
 }
 
@@ -658,7 +672,7 @@ static const char *create_test_typelib(int res_no)
 static void test_TypeInfo(void)
 {
     ITypeLib *pTypeLib;
-    ITypeInfo *pTypeInfo;
+    ITypeInfo *pTypeInfo, *ti;
     ITypeInfo2 *pTypeInfo2;
     HRESULT hr;
     static WCHAR wszBogus[] = { 'b','o','g','u','s',0 };
@@ -675,6 +689,8 @@ static void test_TypeInfo(void)
     TYPEKIND kind;
     const char *filenameA;
     WCHAR filename[MAX_PATH];
+    TYPEATTR *attr;
+    LONG l;
 
     hr = LoadTypeLib(wszStdOle2, &pTypeLib);
     ok_ole_success(hr, LoadTypeLib);
@@ -772,8 +788,6 @@ static void test_TypeInfo(void)
 
     ITypeInfo_Release(pTypeInfo);
 
-
-
     hr = ITypeLib_GetTypeInfoOfGuid(pTypeLib, &IID_IDispatch, &pTypeInfo);
     ok_ole_success(hr, ITypeLib_GetTypeInfoOfGuid); 
 
@@ -799,6 +813,23 @@ static void test_TypeInfo(void)
         VariantClear(&var);
     }
 
+    /* Check instance size for IDispatch, typelib is loaded using system SYS_WIN* kind so it always matches
+       system bitness. */
+    hr = ITypeInfo_GetTypeAttr(pTypeInfo, &attr);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(attr->cbSizeInstance == sizeof(void*), "got size %d\n", attr->cbSizeInstance);
+    ok(attr->typekind == TKIND_INTERFACE, "got typekind %d\n", attr->typekind);
+    ITypeInfo_ReleaseTypeAttr(pTypeInfo, attr);
+
+    /* same size check with some general interface */
+    hr = ITypeLib_GetTypeInfoOfGuid(pTypeLib, &IID_IEnumVARIANT, &ti);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    hr = ITypeInfo_GetTypeAttr(ti, &attr);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(attr->cbSizeInstance == sizeof(void*), "got size %d\n", attr->cbSizeInstance);
+    ITypeInfo_ReleaseTypeAttr(ti, attr);
+    ITypeInfo_Release(ti);
+
             /* test invoking a method with a [restricted] keyword  */
 
     /* correct member id -- wrong flags -- cNamedArgs not bigger than cArgs */
@@ -854,8 +885,6 @@ static void test_TypeInfo(void)
     V_VT(&args[0]) = VT_I4;
     V_I4(&args[0]) = 0;
 
-    V_VT(&res) = VT_EMPTY;
-
     i = 0;
     V_VT(&res) = VT_EMPTY;
     V_I4(&res) = 0;
@@ -881,6 +910,48 @@ static void test_TypeInfo(void)
     ok(V_VT(&res) == VT_I4, "got %d\n", V_VT(&res));
     ok(V_I4(&res) == 1, "got %d\n", V_I4(&res));
 
+    /* DISPATCH_PROPERTYPUTREF */
+    l = 1;
+    V_VT(&args[0]) = VT_I4|VT_BYREF;
+    V_I4REF(&args[0]) = &l;
+
+    dispidMember = DISPID_PROPERTYPUT;
+    dispparams.cArgs = 1;
+    dispparams.cNamedArgs = 1;
+    dispparams.rgdispidNamedArgs = &dispidMember;
+    dispparams.rgvarg = args;
+
+    i = 0;
+    V_VT(&res) = VT_EMPTY;
+    V_I4(&res) = 0;
+    hr = ITypeInfo_Invoke(pTypeInfo, &invoketest, 1, DISPATCH_PROPERTYPUTREF, &dispparams, &res, NULL, &i);
+    ok(hr == S_OK, "got 0x%08x, %d\n", hr, i);
+    ok(V_VT(&res) == VT_I4, "got %d\n", V_VT(&res));
+    ok(V_I4(&res) == 3, "got %d\n", V_I4(&res));
+
+    i = 0;
+    V_VT(&res) = VT_EMPTY;
+    V_I4(&res) = 0;
+    hr = ITypeInfo_Invoke(pTypeInfo, &invoketest, 1, DISPATCH_PROPERTYPUT, &dispparams, &res, NULL, &i);
+    ok(hr == DISP_E_MEMBERNOTFOUND, "got 0x%08x, %d\n", hr, i);
+
+    i = 0;
+    V_VT(&args[0]) = VT_UNKNOWN;
+    V_UNKNOWN(&args[0]) = NULL;
+
+    V_VT(&res) = VT_EMPTY;
+    V_I4(&res) = 0;
+    hr = ITypeInfo_Invoke(pTypeInfo, &invoketest, 2, DISPATCH_PROPERTYPUTREF, &dispparams, &res, NULL, &i);
+    ok(hr == S_OK, "got 0x%08x, %d\n", hr, i);
+    ok(V_VT(&res) == VT_I4, "got %d\n", V_VT(&res));
+    ok(V_I4(&res) == 6, "got %d\n", V_I4(&res));
+
+    i = 0;
+    V_VT(&res) = VT_EMPTY;
+    V_I4(&res) = 0;
+    hr = ITypeInfo_Invoke(pTypeInfo, &invoketest, 2, DISPATCH_PROPERTYPUT, &dispparams, &res, NULL, &i);
+    ok(hr == DISP_E_MEMBERNOTFOUND, "got 0x%08x, %d\n", hr, i);
+
     ITypeInfo_Release(pTypeInfo);
     ITypeLib_Release(pTypeLib);
     DeleteFileA(filenameA);
@@ -1265,7 +1336,7 @@ static void test_inheritance(void)
     CHAR pathA[MAX_PATH];
     static const WCHAR tl_path[] = {'.','\\','m','i','d','l','_','t','m','a','r','s','h','a','l','.','t','l','b',0};
 
-    BOOL use_midl_tlb = 0;
+    BOOL use_midl_tlb = FALSE;
 
     GetModuleFileNameA(NULL, pathA, MAX_PATH);
     MultiByteToWideChar(CP_ACP, 0, pathA, -1, path, MAX_PATH);
@@ -1529,7 +1600,6 @@ if(use_midl_tlb) {
 }
 
 static void test_CreateTypeLib(SYSKIND sys) {
-    static const WCHAR stdoleW[] = {'s','t','d','o','l','e','2','.','t','l','b',0};
     static OLECHAR typelibW[] = {'t','y','p','e','l','i','b',0};
     static OLECHAR helpfileW[] = {'C',':','\\','b','o','g','u','s','.','h','l','p',0};
     static OLECHAR interface1W[] = {'i','n','t','e','r','f','a','c','e','1',0};
@@ -1537,8 +1607,8 @@ static void test_CreateTypeLib(SYSKIND sys) {
     static OLECHAR interface3W[] = {'i','n','t','e','r','f','a','c','e','3',0};
     static OLECHAR dualW[] = {'d','u','a','l',0};
     static OLECHAR coclassW[] = {'c','o','c','l','a','s','s',0};
-    static WCHAR defaultW[] = {'d','e','f','a','u','l','t',0x3213,0};
-    static WCHAR defaultQW[] = {'d','e','f','a','u','l','t','?',0};
+    static const WCHAR defaultW[] = {'d','e','f','a','u','l','t',0x3213,0};
+    static const WCHAR defaultQW[] = {'d','e','f','a','u','l','t','?',0};
     static OLECHAR func1W[] = {'f','u','n','c','1',0};
     static OLECHAR func2W[] = {'f','u','n','c','2',0};
     static OLECHAR prop1W[] = {'P','r','o','p','1',0};
@@ -1546,6 +1616,7 @@ static void test_CreateTypeLib(SYSKIND sys) {
     static OLECHAR param2W[] = {'p','a','r','a','m','2',0};
     static OLECHAR asdfW[] = {'A','s','d','f',0};
     static OLECHAR aliasW[] = {'a','l','i','a','s',0};
+    static OLECHAR invokeW[] = {'I','n','v','o','k','e',0};
     static OLECHAR *names1[] = {func1W, param1W, param2W};
     static OLECHAR *names2[] = {func2W, param1W, param2W};
     static OLECHAR *propname[] = {prop1W, param1W};
@@ -1561,7 +1632,10 @@ static void test_CreateTypeLib(SYSKIND sys) {
     ICreateTypeInfo2 *createti2;
     ITypeLib *tl, *stdole;
     ITypeInfo *interface1, *interface2, *dual, *unknown, *dispatch, *ti;
+    ITypeInfo *tinfos[2];
     ITypeInfo2 *ti2;
+    ITypeComp *tcomp;
+    MEMBERID memids[2];
     FUNCDESC funcdesc, *pfuncdesc;
     ELEMDESC elemdesc[5], *edesc;
     PARAMDESCEX paramdescex;
@@ -1573,9 +1647,12 @@ static void test_CreateTypeLib(SYSKIND sys) {
     DWORD helpcontext, ptr_size, alignment;
     int impltypeflags;
     unsigned int cnames;
+    USHORT found;
     VARIANT cust_data;
     HRESULT hres;
     TYPEKIND kind;
+    DESCKIND desckind;
+    BINDPTR bindptr;
 
     switch(sys){
     case SYS_WIN32:
@@ -1594,7 +1671,7 @@ static void test_CreateTypeLib(SYSKIND sys) {
 
     trace("CreateTypeLib tests\n");
 
-    hres = LoadTypeLib(stdoleW, &stdole);
+    hres = LoadTypeLib(wszStdOle2, &stdole);
     ok(hres == S_OK, "got %08x\n", hres);
 
     hres = ITypeLib_GetTypeInfoOfGuid(stdole, &IID_IUnknown, &unknown);
@@ -2666,10 +2743,49 @@ static void test_CreateTypeLib(SYSKIND sys) {
     ok(libattr->syskind == sys, "syskind = %d\n", libattr->syskind);
     ok(libattr->wMajorVerNum == 0, "wMajorVer = %d\n", libattr->wMajorVerNum);
     ok(libattr->wMinorVerNum == 0, "wMinorVerNum = %d\n", libattr->wMinorVerNum);
-    todo_wine
-        ok(libattr->wLibFlags == LIBFLAG_FHASDISKIMAGE, "wLibFlags = %d\n", libattr->wLibFlags);
+    ok(libattr->wLibFlags == LIBFLAG_FHASDISKIMAGE, "wLibFlags = %d\n", libattr->wLibFlags);
     ITypeLib_ReleaseTLibAttr(tl, libattr);
 
+    found = 2;
+    memset(tinfos, 0, sizeof(tinfos));
+    memids[0] = 0xdeadbeef;
+    memids[1] = 0xdeadbeef;
+    hres = ITypeLib_FindName(tl, param1W, 0, tinfos, memids, &found);
+    ok(hres == S_OK, "got: %08x\n", hres);
+    ok(found == 0, "got wrong count: %u\n", found);
+    ok(tinfos[0] == NULL, "got invalid typeinfo[0]\n");
+    ok(tinfos[1] == NULL, "got invalid typeinfo[1]\n");
+    ok(memids[0] == 0xdeadbeef, "got invalid memid[0]\n");
+    ok(memids[1] == 0xdeadbeef, "got invalid memid[1]\n");
+
+    found = 2;
+    memset(tinfos, 0, sizeof(tinfos));
+    memids[0] = 0xdeadbeef;
+    memids[1] = 0xdeadbeef;
+    hres = ITypeLib_FindName(tl, func1W, 0, tinfos, memids, &found);
+    ok(hres == S_OK, "got: %08x\n", hres);
+    ok(found == 1, "got wrong count: %u\n", found);
+    ok(tinfos[0] != NULL, "got invalid typeinfo[0]\n");
+    ok(tinfos[1] == NULL, "got invalid typeinfo[1]\n");
+    ok(memids[0] == 0, "got invalid memid[0]\n");
+    ok(memids[1] == 0xdeadbeef, "got invalid memid[1]\n");
+    if(tinfos[0])
+        ITypeInfo_Release(tinfos[0]);
+
+    found = 2;
+    memset(tinfos, 0, sizeof(tinfos));
+    memids[0] = 0xdeadbeef;
+    memids[1] = 0xdeadbeef;
+    hres = ITypeLib_FindName(tl, interface1W, 0, tinfos, memids, &found);
+    ok(hres == S_OK, "got: %08x\n", hres);
+    ok(found == 1, "got wrong count: %u\n", found);
+    ok(tinfos[0] != NULL, "got invalid typeinfo[0]\n");
+    ok(tinfos[1] == NULL, "got invalid typeinfo[1]\n");
+    ok(memids[0] == MEMBERID_NIL, "got invalid memid[0]: %x\n", memids[0]);
+    ok(memids[1] == 0xdeadbeef, "got invalid memid[1]\n");
+    if(tinfos[0])
+        ITypeInfo_Release(tinfos[0]);
+
     hres = ITypeLib_GetDocumentation(tl, -1, &name, &docstring, &helpcontext, &helpfile);
     ok(hres == S_OK, "got %08x\n", hres);
     ok(memcmp(typelibW, name, sizeof(typelibW)) == 0, "got wrong typelib name: %s\n",
@@ -3450,6 +3566,34 @@ static void test_CreateTypeLib(SYSKIND sys) {
     ok(typeattr->wMinorVerNum == 0, "wMinorVerNum = %d\n", typeattr->wMinorVerNum);
     ITypeInfo_ReleaseTypeAttr(ti, typeattr);
 
+    hres = ITypeInfo_GetTypeComp(ti, &tcomp);
+    ok(hres == S_OK, "got %08x\n", hres);
+
+    hres = ITypeComp_Bind(tcomp, invokeW, 0, INVOKE_FUNC, &interface1, &desckind, &bindptr);
+    ok(hres == S_OK, "got %08x\n", hres);
+    ok(desckind == DESCKIND_FUNCDESC, "got wrong desckind: 0x%x\n", desckind);
+    ok(bindptr.lpfuncdesc->memid == 0x60010003, "got %x\n", bindptr.lpfuncdesc->memid);
+    ok(bindptr.lpfuncdesc->lprgscode == NULL, "got %p\n", bindptr.lpfuncdesc->lprgscode);
+    ok(bindptr.lpfuncdesc->lprgelemdescParam != NULL, "got %p\n", bindptr.lpfuncdesc->lprgelemdescParam);
+    ok(bindptr.lpfuncdesc->funckind == FUNC_DISPATCH, "got 0x%x\n", bindptr.lpfuncdesc->funckind);
+    ok(bindptr.lpfuncdesc->invkind == INVOKE_FUNC, "got 0x%x\n", bindptr.lpfuncdesc->invkind);
+    ok(bindptr.lpfuncdesc->callconv == CC_STDCALL, "got 0x%x\n", bindptr.lpfuncdesc->callconv);
+    ok(bindptr.lpfuncdesc->cParams == 8, "got %d\n", bindptr.lpfuncdesc->cParams);
+    ok(bindptr.lpfuncdesc->cParamsOpt == 0, "got %d\n", bindptr.lpfuncdesc->cParamsOpt);
+#ifdef _WIN64
+    if(sys == SYS_WIN32)
+        todo_wine ok(bindptr.lpfuncdesc->oVft == 6 * sizeof(void*), "got %x\n", bindptr.lpfuncdesc->oVft);
+    else
+#endif
+        ok(bindptr.lpfuncdesc->oVft == 6 * sizeof(void*), "got %x\n", bindptr.lpfuncdesc->oVft);
+    ok(bindptr.lpfuncdesc->cScodes == 0, "got %d\n", bindptr.lpfuncdesc->cScodes);
+    ok(bindptr.lpfuncdesc->elemdescFunc.tdesc.vt == VT_VOID, "got %d\n", bindptr.lpfuncdesc->elemdescFunc.tdesc.vt);
+    ok(bindptr.lpfuncdesc->wFuncFlags == FUNCFLAG_FRESTRICTED, "got 0x%x\n", bindptr.lpfuncdesc->wFuncFlags);
+
+    ITypeInfo_ReleaseFuncDesc(interface1, bindptr.lpfuncdesc);
+    ITypeInfo_Release(interface1);
+    ITypeComp_Release(tcomp);
+
     hres = ITypeInfo_GetRefTypeOfImplType(ti, -1, &hreftype);
     ok(hres == S_OK, "got %08x\n", hres);
     ok(hreftype == -2, "got wrong hreftype: %x\n", hreftype);
@@ -3675,9 +3819,10 @@ typedef struct _function_info
     LPCSTR names[15];
 } function_info;
 
-typedef struct _interface_info
+typedef struct _type_info
 {
     LPCSTR name;
+    LPCSTR uuid;
     TYPEKIND type;
     WORD wTypeFlags;
     USHORT cbAlignment;
@@ -3685,17 +3830,17 @@ typedef struct _interface_info
     USHORT cbSizeVft;
     USHORT cFuncs;
     function_info funcs[20];
-} interface_info;
+} type_info;
 
-static const interface_info info[] = {
-/* interfaces count: 2 */
+static const type_info info[] = {
 {
   "IDualIface",
-  /*kind*/ TKIND_DISPATCH, /*flags*/ 0x1040, /*align*/ 4, /*size*/ 4,
+  "{b14b6bb5-904e-4ff9-b247-bd361f7aaedd}",
+  /*kind*/ TKIND_DISPATCH, /*flags*/ TYPEFLAG_FDISPATCHABLE|TYPEFLAG_FDUAL, /*align*/ 4, /*size*/ sizeof(void*),
   /*#vtbl*/ 7, /*#func*/ 8,
   {
     {
-      0x60000000, /*func*/ FUNC_DISPATCH, /*inv*/ INVOKE_FUNC, /*call*/ 0x4,
+      0x60000000, /*func*/ FUNC_DISPATCH, /*inv*/ INVOKE_FUNC, /*call*/ CC_STDCALL,
       /*#param*/ 2, /*#opt*/ 0, /*vtbl*/ 0, /*#scodes*/ 0, /*flags*/ 0x1,
       {24, 0}, /* ret */
       { /* params */
@@ -3711,7 +3856,7 @@ static const interface_info info[] = {
       },
     },
     {
-      0x60000001, /*func*/ FUNC_DISPATCH, /*inv*/ INVOKE_FUNC, /*call*/ 0x4,
+      0x60000001, /*func*/ FUNC_DISPATCH, /*inv*/ INVOKE_FUNC, /*call*/ CC_STDCALL,
       /*#param*/ 0, /*#opt*/ 0, /*vtbl*/ 1, /*#scodes*/ 0, /*flags*/ 0x1,
       {19, 0}, /* ret */
       { /* params */
@@ -3723,7 +3868,7 @@ static const interface_info info[] = {
       },
     },
     {
-      0x60000002, /*func*/ FUNC_DISPATCH, /*inv*/ INVOKE_FUNC, /*call*/ 0x4,
+      0x60000002, /*func*/ FUNC_DISPATCH, /*inv*/ INVOKE_FUNC, /*call*/ CC_STDCALL,
       /*#param*/ 0, /*#opt*/ 0, /*vtbl*/ 2, /*#scodes*/ 0, /*flags*/ 0x1,
       {19, 0}, /* ret */
       { /* params */
@@ -3735,7 +3880,7 @@ static const interface_info info[] = {
       },
     },
     {
-      0x60010000, /*func*/ FUNC_DISPATCH, /*inv*/ INVOKE_FUNC, /*call*/ 0x4,
+      0x60010000, /*func*/ FUNC_DISPATCH, /*inv*/ INVOKE_FUNC, /*call*/ CC_STDCALL,
       /*#param*/ 1, /*#opt*/ 0, /*vtbl*/ 3, /*#scodes*/ 0, /*flags*/ 0x1,
       {24, 0}, /* ret */
       { /* params */
@@ -3749,7 +3894,7 @@ static const interface_info info[] = {
       },
     },
     {
-      0x60010001, /*func*/ FUNC_DISPATCH, /*inv*/ INVOKE_FUNC, /*call*/ 0x4,
+      0x60010001, /*func*/ FUNC_DISPATCH, /*inv*/ INVOKE_FUNC, /*call*/ CC_STDCALL,
       /*#param*/ 3, /*#opt*/ 0, /*vtbl*/ 4, /*#scodes*/ 0, /*flags*/ 0x1,
       {24, 0}, /* ret */
       { /* params */
@@ -3767,7 +3912,7 @@ static const interface_info info[] = {
       },
     },
     {
-      0x60010002, /*func*/ FUNC_DISPATCH, /*inv*/ INVOKE_FUNC, /*call*/ 0x4,
+      0x60010002, /*func*/ FUNC_DISPATCH, /*inv*/ INVOKE_FUNC, /*call*/ CC_STDCALL,
       /*#param*/ 5, /*#opt*/ 0, /*vtbl*/ 5, /*#scodes*/ 0, /*flags*/ 0x1,
       {24, 0}, /* ret */
       { /* params */
@@ -3789,7 +3934,7 @@ static const interface_info info[] = {
       },
     },
     {
-      0x60010003, /*func*/ FUNC_DISPATCH, /*inv*/ INVOKE_FUNC, /*call*/ 0x4,
+      0x60010003, /*func*/ FUNC_DISPATCH, /*inv*/ INVOKE_FUNC, /*call*/ CC_STDCALL,
       /*#param*/ 8, /*#opt*/ 0, /*vtbl*/ 6, /*#scodes*/ 0, /*flags*/ 0x1,
       {24, 0}, /* ret */
       { /* params */
@@ -3817,7 +3962,7 @@ static const interface_info info[] = {
       },
     },
     {
-      0x60020000, /*func*/ FUNC_DISPATCH, /*inv*/ INVOKE_FUNC, /*call*/ 0x4,
+      0x60020000, /*func*/ FUNC_DISPATCH, /*inv*/ INVOKE_FUNC, /*call*/ CC_STDCALL,
       /*#param*/ 0, /*#opt*/ 0, /*vtbl*/ 7, /*#scodes*/ 0, /*flags*/ 0x0,
       {24, 0}, /* ret */
       { /* params */
@@ -3832,11 +3977,12 @@ static const interface_info info[] = {
 },
 {
   "ISimpleIface",
-  /*kind*/ TKIND_INTERFACE, /*flags*/ 0x1000, /*align*/ 4, /*size*/ 4,
+  "{ec5dfcd6-eeb0-4cd6-b51e-8030e1dac009}",
+  /*kind*/ TKIND_INTERFACE, /*flags*/ TYPEFLAG_FDISPATCHABLE, /*align*/ 4, /*size*/ sizeof(void*),
   /*#vtbl*/ 8, /*#func*/ 1,
   {
     {
-      0x60020000, /*func*/ FUNC_PUREVIRTUAL, /*inv*/ INVOKE_FUNC, /*call*/ 0x4,
+      0x60020000, /*func*/ FUNC_PUREVIRTUAL, /*inv*/ INVOKE_FUNC, /*call*/ CC_STDCALL,
       /*#param*/ 0, /*#opt*/ 0, /*vtbl*/ 7, /*#scodes*/ 0, /*flags*/ 0x0,
       {25, 0}, /* ret */
       { /* params */
@@ -3849,6 +3995,11 @@ static const interface_info info[] = {
     },
   }
 },
+{
+  "test_struct",
+  "{4029f190-ca4a-4611-aeb9-673983cb96dd}",
+  /* kind */ TKIND_RECORD, /*flags*/ 0, /*align*/ 4, /*size*/ sizeof(struct test_struct)
+}
 };
 
 #define check_type(elem, info) { \
@@ -3860,36 +4011,55 @@ static void test_dump_typelib(const char *name)
 {
     WCHAR wszName[MAX_PATH];
     ITypeLib *typelib;
-    int ifcount = sizeof(info)/sizeof(info[0]);
+    int ticount = sizeof(info)/sizeof(info[0]);
     int iface, func;
 
     MultiByteToWideChar(CP_ACP, 0, name, -1, wszName, MAX_PATH);
     ole_check(LoadTypeLibEx(wszName, REGKIND_NONE, &typelib));
-    expect_eq(ITypeLib_GetTypeInfoCount(typelib), ifcount, UINT, "%d");
-    for (iface = 0; iface < ifcount; iface++)
+    expect_eq(ITypeLib_GetTypeInfoCount(typelib), ticount, UINT, "%d");
+    for (iface = 0; iface < ticount; iface++)
     {
-        const interface_info *if_info = &info[iface];
+        const type_info *ti = &info[iface];
         ITypeInfo *typeinfo;
         TYPEATTR *typeattr;
         BSTR bstrIfName;
 
-        trace("Interface %s\n", if_info->name);
+        trace("Interface %s\n", ti->name);
         ole_check(ITypeLib_GetTypeInfo(typelib, iface, &typeinfo));
         ole_check(ITypeLib_GetDocumentation(typelib, iface, &bstrIfName, NULL, NULL, NULL));
-        expect_wstr_acpval(bstrIfName, if_info->name);
+        expect_wstr_acpval(bstrIfName, ti->name);
         SysFreeString(bstrIfName);
 
         ole_check(ITypeInfo_GetTypeAttr(typeinfo, &typeattr));
-        expect_int(typeattr->typekind, if_info->type);
-        expect_hex(typeattr->wTypeFlags, if_info->wTypeFlags);
-        expect_int(typeattr->cbAlignment, if_info->cbAlignment);
-        expect_int(typeattr->cbSizeInstance, if_info->cbSizeInstance);
-        expect_int(typeattr->cbSizeVft, if_info->cbSizeVft * sizeof(void*));
-        expect_int(typeattr->cFuncs, if_info->cFuncs);
+        expect_int(typeattr->typekind, ti->type);
+        expect_hex(typeattr->wTypeFlags, ti->wTypeFlags);
+        expect_int(typeattr->cbAlignment, ti->cbAlignment);
+        expect_int(typeattr->cbSizeInstance, ti->cbSizeInstance);
+        expect_int(typeattr->cbSizeVft, ti->cbSizeVft * sizeof(void*));
+        expect_int(typeattr->cFuncs, ti->cFuncs);
+
+        /* compare type uuid */
+        if (ti->uuid && *ti->uuid)
+        {
+            WCHAR guidW[39];
+            ITypeInfo *typeinfo2;
+            HRESULT hr;
+            GUID guid;
+
+            MultiByteToWideChar(CP_ACP, 0, ti->uuid, -1, guidW, sizeof(guidW)/sizeof(guidW[0]));
+            IIDFromString(guidW, &guid);
+            expect_guid(&guid, &typeattr->guid);
+
+            /* check that it's possible to search using this uuid */
+            typeinfo2 = NULL;
+            hr = ITypeLib_GetTypeInfoOfGuid(typelib, &guid, &typeinfo2);
+            ok(hr == S_OK, "got 0x%08x\n", hr);
+            ITypeInfo_Release(typeinfo2);
+        }
 
         for (func = 0; func < typeattr->cFuncs; func++)
         {
-            function_info *fn_info = (function_info *)&if_info->funcs[func];
+            function_info *fn_info = (function_info *)&ti->funcs[func];
             FUNCDESC *desc;
             BSTR namesTab[256];
             UINT cNames;
@@ -3941,7 +4111,9 @@ static void test_create_typelib_lcid(LCID lcid)
     HRESULT hr;
     ICreateTypeLib2 *tl;
     HANDLE file;
-    DWORD msft_header[7];
+    DWORD msft_header[8];
+    ITypeLib *typelib;
+    TLIBATTR *attr;
     DWORD read;
 
     GetTempFileNameA( ".", "tlb", 0, filename );
@@ -3950,6 +4122,14 @@ static void test_create_typelib_lcid(LCID lcid)
     hr = CreateTypeLib2(SYS_WIN32, name, &tl);
     ok(hr == S_OK, "got %08x\n", hr);
 
+    hr = ICreateTypeLib2_QueryInterface(tl, &IID_ITypeLib, (void**)&typelib);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = ITypeLib_GetLibAttr(typelib, &attr);
+    ok(hr == S_OK, "got %08x\n", hr);
+    ok(attr->wLibFlags == 0, "flags 0x%x\n", attr->wLibFlags);
+    ITypeLib_ReleaseTLibAttr(typelib, attr);
+
     hr = ICreateTypeLib2_SetLcid(tl, lcid);
     ok(hr == S_OK, "got %08x\n", hr);
 
@@ -3959,6 +4139,12 @@ static void test_create_typelib_lcid(LCID lcid)
     hr = ICreateTypeLib2_SaveAllChanges(tl);
     ok(hr == S_OK, "got %08x\n", hr);
 
+    hr = ITypeLib_GetLibAttr(typelib, &attr);
+    ok(hr == S_OK, "got %08x\n", hr);
+    ok(attr->wLibFlags == 0, "flags 0x%x\n", attr->wLibFlags);
+    ITypeLib_ReleaseTLibAttr(typelib, attr);
+
+    ITypeLib_Release(typelib);
     ICreateTypeLib2_Release(tl);
 
     file = CreateFileA( filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0 );
@@ -3974,6 +4160,17 @@ static void test_create_typelib_lcid(LCID lcid)
     ok(msft_header[3] == (lcid ? lcid : 0x409), "got %08x (lcid %08x)\n", msft_header[3], lcid);
     ok(msft_header[4] == lcid, "got %08x (lcid %08x)\n", msft_header[4], lcid);
     ok(msft_header[6] == 0x00040003, "got %08x\n", msft_header[6]);
+    ok(msft_header[7] == 0, "got %08x\n", msft_header[7]);
+
+    /* check flags after loading */
+    hr = LoadTypeLib(name, &typelib);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = ITypeLib_GetLibAttr(typelib, &attr);
+    ok(hr == S_OK, "got %08x\n", hr);
+    ok(attr->wLibFlags == LIBFLAG_FHASDISKIMAGE, "flags 0x%x\n", attr->wLibFlags);
+    ITypeLib_ReleaseTLibAttr(typelib, attr);
+    ITypeLib_Release(typelib);
 
     DeleteFileA(filename);
 }
@@ -4059,6 +4256,7 @@ static void test_register_typelib(BOOL system_registration)
     {
         ITypeInfo *typeinfo;
         TYPEATTR *attr;
+        REGSAM opposite = (sizeof(void*) == 8 ? KEY_WOW64_32KEY : KEY_WOW64_64KEY);
 
         hr = ITypeLib_GetTypeInfo(typelib, i, &typeinfo);
         ok(hr == S_OK, "got %08x\n", hr);
@@ -4108,6 +4306,11 @@ static void test_register_typelib(BOOL system_registration)
         ok(ret == expect_ret, "%d: got %d\n", i, ret);
         if(ret == ERROR_SUCCESS) RegCloseKey(hkey);
 
+        /* 32-bit typelibs should be registered into both registry bit modes */
+        ret = RegOpenKeyExA(HKEY_CLASSES_ROOT, key_name, 0, KEY_READ | opposite, &hkey);
+        ok(ret == expect_ret, "%d: got %d\n", i, ret);
+        if(ret == ERROR_SUCCESS) RegCloseKey(hkey);
+
         ITypeInfo_ReleaseTypeAttr(typeinfo, attr);
         ITypeInfo_Release(typeinfo);
     }
@@ -4199,8 +4402,8 @@ static void test_SetVarHelpContext(void)
     ok(pdesc->memid == 0x40000000, "got wrong memid: %x\n", pdesc->memid);
     ok(pdesc->elemdescVar.tdesc.vt == VT_INT, "got wrong vardesc type: %u\n", pdesc->elemdescVar.tdesc.vt);
     ok(pdesc->varkind == VAR_CONST, "got wrong varkind: %u\n", pdesc->varkind);
-    ok(V_VT(U(pdesc)->lpvarValue) == VT_INT, "got wrong value type: %u\n", V_VT(U(pdesc)->lpvarValue));
-    ok(V_INT(U(pdesc)->lpvarValue) == 1, "got wrong value: 0x%x\n", V_INT(U(pdesc)->lpvarValue));
+    ok(V_VT(U(*pdesc).lpvarValue) == VT_INT, "got wrong value type: %u\n", V_VT(U(*pdesc).lpvarValue));
+    ok(V_INT(U(*pdesc).lpvarValue) == 1, "got wrong value: 0x%x\n", V_INT(U(*pdesc).lpvarValue));
 
     hr = ITypeInfo_GetDocumentation(ti, pdesc->memid, NULL, NULL, &ctx, NULL);
     ok(hr == S_OK, "got %08x\n", hr);
@@ -4216,15 +4419,22 @@ static void test_SetVarHelpContext(void)
 static void test_SetFuncAndParamNames(void)
 {
     static OLECHAR nameW[] = {'n','a','m','e',0};
+    static OLECHAR name2W[] = {'n','a','m','e','2',0};
     static OLECHAR prop[] = {'p','r','o','p',0};
     static OLECHAR *propW[] = {prop};
+    static OLECHAR func[] = {'f','u','n','c',0};
+    static OLECHAR *funcW[] = {func, NULL};
     CHAR filenameA[MAX_PATH];
     WCHAR filenameW[MAX_PATH];
     ICreateTypeLib2 *ctl;
     ICreateTypeInfo *cti;
+    ITypeLib *tl;
+    ITypeInfo *infos[3];
+    MEMBERID memids[3];
     FUNCDESC funcdesc;
     ELEMDESC edesc;
     HRESULT hr;
+    USHORT found;
 
     GetTempFileNameA(".", "tlb", 0, filenameA);
     MultiByteToWideChar(CP_ACP, 0, filenameA, -1, filenameW, MAX_PATH);
@@ -4284,24 +4494,80 @@ static void test_SetFuncAndParamNames(void)
     hr = ICreateTypeInfo_SetFuncAndParamNames(cti, 3, propW, 1);
     ok(hr == TYPE_E_AMBIGUOUSNAME, "got 0x%08x\n", hr);
 
+    /* regular function */
+    funcdesc.invkind = INVOKE_FUNC;
+    funcdesc.cParams = 1;
+    hr = ICreateTypeInfo_AddFuncDesc(cti, 4, &funcdesc);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    hr = ICreateTypeInfo_SetFuncAndParamNames(cti, 4, funcW, 2);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    ICreateTypeInfo_Release(cti);
+
+    hr = ICreateTypeLib2_CreateTypeInfo(ctl, name2W, TKIND_INTERFACE, &cti);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    funcdesc.funckind = FUNC_PUREVIRTUAL;
+    funcdesc.invkind = INVOKE_FUNC;
+    funcdesc.cParams = 0;
+    funcdesc.lprgelemdescParam = NULL;
+    hr = ICreateTypeInfo_AddFuncDesc(cti, 0, &funcdesc);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    hr = ICreateTypeInfo_SetFuncAndParamNames(cti, 0, funcW, 1);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
     ICreateTypeInfo_Release(cti);
+
+    hr = ICreateTypeLib2_QueryInterface(ctl, &IID_ITypeLib, (void**)&tl);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    found = 1;
+    memset(infos, 0, sizeof(infos));
+    memids[0] = 0xdeadbeef;
+    memids[1] = 0xdeadbeef;
+    memids[2] = 0xdeadbeef;
+    hr = ITypeLib_FindName(tl, func, 0, infos, memids, &found);
+    ok(hr == S_OK, "got %08x\n", hr);
+    ok(found == 1, "got wrong count: %u\n", found);
+    ok(infos[0] && !infos[1] && !infos[2], "got wrong typeinfo\n");
+    ok(memids[0] == 0, "got wrong memid[0]\n");
+    ok(memids[1] == 0xdeadbeef && memids[2] == 0xdeadbeef, "got wrong memids\n");
+
+    found = 3;
+    memset(infos, 0, sizeof(infos));
+    memids[0] = 0xdeadbeef;
+    memids[1] = 0xdeadbeef;
+    memids[2] = 0xdeadbeef;
+    hr = ITypeLib_FindName(tl, func, 0, infos, memids, &found);
+    ok(hr == S_OK, "got %08x\n", hr);
+    ok(found == 2, "got wrong count: %u\n", found);
+    ok(infos[0] && infos[1] && infos[0] != infos[1], "got same typeinfo\n");
+    ok(memids[0] == 0, "got wrong memid[0]\n");
+    ok(memids[1] == 0, "got wrong memid[1]\n");
+
+    ITypeLib_Release(tl);
     ICreateTypeLib2_Release(ctl);
     DeleteFileA(filenameA);
 }
 
-static void test_SetVarDocString(void)
+static void test_SetDocString(void)
 {
     static OLECHAR nameW[] = {'n','a','m','e',0};
+    static OLECHAR name2W[] = {'n','a','m','e','2',0};
     static OLECHAR doc1W[] = {'d','o','c','1',0};
     static OLECHAR doc2W[] = {'d','o','c','2',0};
+    static OLECHAR var_nameW[] = {'v','a','r','n','a','m','e',0};
     CHAR filenameA[MAX_PATH];
     WCHAR filenameW[MAX_PATH];
     ICreateTypeLib2 *ctl;
     ICreateTypeInfo *cti;
     ITypeLib *tl;
     ITypeInfo *ti;
-    BSTR docstr;
+    BSTR namestr, docstr;
     VARDESC desc, *pdesc;
+    FUNCDESC funcdesc, *pfuncdesc;
     HRESULT hr;
     VARIANT v;
 
@@ -4331,6 +4597,15 @@ static void test_SetVarDocString(void)
     hr = ICreateTypeInfo_AddVarDesc(cti, 0, &desc);
     ok(hr == S_OK, "got %08x\n", hr);
 
+    hr = ICreateTypeInfo_SetVarName(cti, 0, NULL);
+    ok(hr == E_INVALIDARG, "got %08x\n", hr);
+
+    hr = ICreateTypeInfo_SetVarName(cti, 1, var_nameW);
+    ok(hr == TYPE_E_ELEMENTNOTFOUND, "got %08x\n", hr);
+
+    hr = ICreateTypeInfo_SetVarName(cti, 0, var_nameW);
+    ok(hr == S_OK, "got %08x\n", hr);
+
     hr = ICreateTypeInfo_SetVarDocString(cti, 0, NULL);
     ok(hr == E_INVALIDARG, "got %08x\n", hr);
 
@@ -4347,6 +4622,29 @@ static void test_SetVarDocString(void)
 
     ICreateTypeInfo_Release(cti);
 
+    hr = ICreateTypeLib2_CreateTypeInfo(ctl, name2W, TKIND_INTERFACE, &cti);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = ICreateTypeInfo_SetFuncDocString(cti, 0, doc1W);
+    ok(hr == TYPE_E_ELEMENTNOTFOUND, "got %08x\n", hr);
+
+    hr = ICreateTypeInfo_SetFuncDocString(cti, 0, NULL);
+    ok(hr == E_INVALIDARG, "got %08x\n", hr);
+
+    memset(&funcdesc, 0, sizeof(funcdesc));
+    funcdesc.memid = MEMBERID_NIL;
+    funcdesc.funckind = FUNC_PUREVIRTUAL;
+    funcdesc.invkind = INVOKE_FUNC;
+    funcdesc.callconv = CC_STDCALL;
+
+    hr = ICreateTypeInfo_AddFuncDesc(cti, 0, &funcdesc);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = ICreateTypeInfo_SetFuncDocString(cti, 0, doc1W);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    ICreateTypeInfo_Release(cti);
+
     hr = ICreateTypeLib2_SaveAllChanges(ctl);
     ok(hr == S_OK, "got: %08x\n", hr);
 
@@ -4363,17 +4661,40 @@ static void test_SetVarDocString(void)
     ok(pdesc->memid == 0x40000000, "got wrong memid: %x\n", pdesc->memid);
     ok(pdesc->elemdescVar.tdesc.vt == VT_INT, "got wrong vardesc type: %u\n", pdesc->elemdescVar.tdesc.vt);
     ok(pdesc->varkind == VAR_CONST, "got wrong varkind: %u\n", pdesc->varkind);
-    ok(V_VT(U(pdesc)->lpvarValue) == VT_INT, "got wrong value type: %u\n", V_VT(U(pdesc)->lpvarValue));
-    ok(V_INT(U(pdesc)->lpvarValue) == 1, "got wrong value: 0x%x\n", V_INT(U(pdesc)->lpvarValue));
+    ok(V_VT(U(*pdesc).lpvarValue) == VT_INT, "got wrong value type: %u\n", V_VT(U(*pdesc).lpvarValue));
+    ok(V_INT(U(*pdesc).lpvarValue) == 1, "got wrong value: 0x%x\n", V_INT(U(*pdesc).lpvarValue));
 
-    hr = ITypeInfo_GetDocumentation(ti, pdesc->memid, NULL, &docstr, NULL, NULL);
+    hr = ITypeInfo_GetDocumentation(ti, pdesc->memid, &namestr, &docstr, NULL, NULL);
     ok(hr == S_OK, "got %08x\n", hr);
+    ok(memcmp(namestr, var_nameW, sizeof(var_nameW)) == 0, "got wrong name: %s\n", wine_dbgstr_w(namestr));
     ok(memcmp(docstr, doc2W, sizeof(doc2W)) == 0, "got wrong docstring: %s\n", wine_dbgstr_w(docstr));
 
+    SysFreeString(namestr);
     SysFreeString(docstr);
 
     ITypeInfo_ReleaseVarDesc(ti, pdesc);
     ITypeInfo_Release(ti);
+
+    hr = ITypeLib_GetTypeInfo(tl, 1, &ti);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = ITypeInfo_GetFuncDesc(ti, 0, &pfuncdesc);
+    ok(hr == S_OK, "got %08x\n", hr);
+    ok(pfuncdesc->memid == 0x60000000, "got wrong memid: %x\n", pfuncdesc->memid);
+    ok(pfuncdesc->funckind == FUNC_PUREVIRTUAL, "got wrong funckind: %x\n", pfuncdesc->funckind);
+    ok(pfuncdesc->invkind == INVOKE_FUNC, "got wrong invkind: %x\n", pfuncdesc->invkind);
+    ok(pfuncdesc->callconv == CC_STDCALL, "got wrong callconv: %x\n", pfuncdesc->callconv);
+
+    hr = ITypeInfo_GetDocumentation(ti, pfuncdesc->memid, &namestr, &docstr, NULL, NULL);
+    ok(hr == S_OK, "got %08x\n", hr);
+    ok(namestr == NULL, "got wrong name: %s\n", wine_dbgstr_w(namestr));
+    ok(memcmp(docstr, doc1W, sizeof(doc1W)) == 0, "got wrong docstring: %s\n", wine_dbgstr_w(docstr));
+
+    SysFreeString(docstr);
+
+    ITypeInfo_ReleaseFuncDesc(ti, pfuncdesc);
+    ITypeInfo_Release(ti);
+
     ITypeLib_Release(tl);
 
     DeleteFileA(filenameA);
@@ -4418,8 +4739,7 @@ static void test_FindName(void)
     ti = (void*)0xdeadbeef;
     hr = ITypeLib_FindName(tl, buffW, 0, &ti, &memid, &c);
     ok(hr == S_OK, "got 0x%08x\n", hr);
-todo_wine
-    ok(memid == -1, "got %d\n", memid);
+    ok(memid == MEMBERID_NIL, "got %d\n", memid);
     ok(!lstrcmpW(buffW, wszGUID), "got %s\n", wine_dbgstr_w(buffW));
     ok(c == 1, "got %d\n", c);
     ITypeInfo_Release(ti);
@@ -4431,7 +4751,7 @@ todo_wine
     hr = ITypeLib_FindName(tl, buffW, 0, &ti, &memid, &c);
     ok(hr == S_OK, "got 0x%08x\n", hr);
 todo_wine {
-    ok(memid == -1, "got %d\n", memid);
+    ok(memid == MEMBERID_NIL, "got %d\n", memid);
     ok(!lstrcmpW(buffW, wszGUID), "got %s\n", wine_dbgstr_w(buffW));
     ok(c == 1, "got %d\n", c);
 }
@@ -4444,7 +4764,7 @@ todo_wine {
     ti = (void*)0xdeadbeef;
     hr = ITypeLib_FindName(tl, buffW, 0, &ti, &memid, &c);
     ok(hr == S_OK, "got 0x%08x\n", hr);
-    ok(memid == -1, "got %d\n", memid);
+    ok(memid == MEMBERID_NIL, "got %d\n", memid);
     ok(!lstrcmpW(buffW, invalidW), "got %s\n", wine_dbgstr_w(buffW));
     ok(c == 0, "got %d\n", c);
     ok(ti == (void*)0xdeadbeef, "got %p\n", ti);
@@ -4545,7 +4865,7 @@ static const char manifest_dep[] =
 "<assemblyIdentity version=\"1.2.3.4\" name=\"testdep\" type=\"win32\" processorArchitecture=\"" ARCH "\"/>"
 "<file name=\"test_actctx_tlb.tlb\">"
 " <typelib tlbid=\"{d96d8a3e-78b6-4c8d-8f27-059db959be8a}\" version=\"2.7\" helpdir=\"\" resourceid=\"409\""
-"          flags=\"RESTRICTED,CONTROL\""
+"          flags=\"Restricted,cONTROL\""
 " />"
 "</file>"
 "<file name=\"test_actctx_tlb2.tlb\">"
@@ -4608,19 +4928,24 @@ static void test_LoadRegTypeLib(void)
 
     path = NULL;
     hr = QueryPathOfRegTypeLib(&LIBID_TestTypelib, 2, 0, LOCALE_NEUTRAL, &path);
-todo_wine
     ok(hr == S_OK, "got 0x%08x\n", hr);
     SysFreeString(path);
 
     path = NULL;
     hr = QueryPathOfRegTypeLib(&LIBID_TestTypelib, 2, 0, lcid_en, &path);
-todo_wine
     ok(hr == S_OK, "got 0x%08x\n", hr);
     SysFreeString(path);
 
     path = NULL;
     hr = QueryPathOfRegTypeLib(&LIBID_TestTypelib, 2, 0, lcid_ru, &path);
-todo_wine
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    SysFreeString(path);
+
+    hr = QueryPathOfRegTypeLib(&LIBID_TestTypelib, 2, 8, LOCALE_NEUTRAL, &path);
+    ok(hr == TYPE_E_LIBNOTREGISTERED || broken(hr == S_OK) /* winxp */, "got 0x%08x\n", hr);
+
+    path = NULL;
+    hr = QueryPathOfRegTypeLib(&LIBID_TestTypelib, 2, 7, LOCALE_NEUTRAL, &path);
     ok(hr == S_OK, "got 0x%08x\n", hr);
     SysFreeString(path);
 
@@ -4639,22 +4964,18 @@ todo_wine
 
     /* manifest version is 2.7, actual is 2.5 */
     hr = LoadRegTypeLib(&LIBID_TestTypelib, 2, 0, LOCALE_NEUTRAL, &tl);
-todo_wine
     ok(hr == S_OK, "got 0x%08x\n", hr);
     if (hr == S_OK) ITypeLib_Release(tl);
 
     hr = LoadRegTypeLib(&LIBID_TestTypelib, 2, 1, LOCALE_NEUTRAL, &tl);
-todo_wine
     ok(hr == S_OK, "got 0x%08x\n", hr);
     if (hr == S_OK) ITypeLib_Release(tl);
 
     hr = LoadRegTypeLib(&LIBID_TestTypelib, 2, 0, lcid_en, &tl);
-todo_wine
     ok(hr == S_OK, "got 0x%08x\n", hr);
     if (hr == S_OK) ITypeLib_Release(tl);
 
     hr = LoadRegTypeLib(&LIBID_TestTypelib, 2, 0, lcid_ru, &tl);
-todo_wine
     ok(hr == S_OK, "got 0x%08x\n", hr);
     if (hr == S_OK) ITypeLib_Release(tl);
 
@@ -4662,11 +4983,8 @@ todo_wine
     ok(hr == TYPE_E_LIBNOTREGISTERED, "got 0x%08x\n", hr);
 
     hr = LoadRegTypeLib(&LIBID_TestTypelib, 2, 5, LOCALE_NEUTRAL, &tl);
-todo_wine
     ok(hr == S_OK, "got 0x%08x\n", hr);
 
-if (hr == S_OK)
-{
     hr = ITypeLib_GetLibAttr(tl, &attr);
     ok(hr == S_OK, "got 0x%08x\n", hr);
 
@@ -4677,7 +4995,6 @@ if (hr == S_OK)
 
     ITypeLib_ReleaseTLibAttr(tl, attr);
     ITypeLib_Release(tl);
-}
 
     hr = LoadRegTypeLib(&LIBID_TestTypelib, 1, 7, LOCALE_NEUTRAL, &tl);
     ok(hr == TYPE_E_LIBNOTREGISTERED, "got 0x%08x\n", hr);
@@ -4931,6 +5248,179 @@ static void test_SetTypeDescAlias(SYSKIND kind)
     DeleteFileA(filenameA);
 }
 
+static void test_GetLibAttr(void)
+{
+    ULONG ref1, ref2;
+    TLIBATTR *attr;
+    ITypeLib *tl;
+    HRESULT hr;
+
+    hr = LoadTypeLib(wszStdOle2, &tl);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    ref1 = ITypeLib_AddRef(tl);
+    ITypeLib_Release(tl);
+
+    hr = ITypeLib_GetLibAttr(tl, &attr);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    ref2 = ITypeLib_AddRef(tl);
+    ITypeLib_Release(tl);
+    ok(ref2 == ref1, "got %d, %d\n", ref2, ref1);
+
+    ITypeLib_ReleaseTLibAttr(tl, attr);
+    ITypeLib_Release(tl);
+}
+
+static HRESULT WINAPI uk_QueryInterface(IUnknown *obj, REFIID iid, void **out)
+{
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI uk_AddRef(IUnknown *obj)
+{
+    return 2;
+}
+
+static ULONG WINAPI uk_Release(IUnknown *obj)
+{
+    return 1;
+}
+
+IUnknownVtbl vt = {
+    uk_QueryInterface,
+    uk_AddRef,
+    uk_Release,
+};
+
+IUnknown uk = {&vt};
+
+static void test_stub(void)
+{
+    HRESULT hr;
+    CLSID clsid;
+    IPSFactoryBuffer *factory;
+    IRpcStubBuffer *base_stub;
+    ITypeLib *stdole;
+    ICreateTypeLib2 *ctl;
+    ICreateTypeInfo *cti;
+    ITypeLib *tl;
+    ITypeInfo *unk, *ti;
+    HREFTYPE href;
+    char filenameA[MAX_PATH];
+    WCHAR filenameW[MAX_PATH];
+    HKEY hkey;
+    LONG lr;
+
+    static const GUID libguid = {0x3b9ff02e,0x9675,0x4861,{0xb7,0x81,0xce,0xae,0xa4,0x78,0x2a,0xcc}};
+    static const GUID interfaceguid = {0x3b9ff02f,0x9675,0x4861,{0xb7,0x81,0xce,0xae,0xa4,0x78,0x2a,0xcc}};
+    static const GUID coclassguid = {0x3b9ff030,0x9675,0x4861,{0xb7,0x81,0xce,0xae,0xa4,0x78,0x2a,0xcc}};
+    static OLECHAR interfaceW[] = {'i','n','t','e','r','f','a','c','e',0};
+    static OLECHAR classW[] = {'c','l','a','s','s',0};
+
+    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+
+    hr = LoadTypeLib(wszStdOle2, &stdole);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = ITypeLib_GetTypeInfoOfGuid(stdole, &IID_IUnknown, &unk);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    GetTempFileNameA(".", "tlb", 0, filenameA);
+    MultiByteToWideChar(CP_ACP, 0, filenameA, -1, filenameW, MAX_PATH);
+
+    hr = CreateTypeLib2(SYS_WIN32, filenameW, &ctl);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = ICreateTypeLib2_SetGuid(ctl, &libguid);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = ICreateTypeLib2_SetLcid(ctl, LOCALE_NEUTRAL);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = ICreateTypeLib2_CreateTypeInfo(ctl, interfaceW, TKIND_INTERFACE, &cti);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = ICreateTypeInfo_SetGuid(cti, &interfaceguid);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = ICreateTypeInfo_SetTypeFlags(cti, TYPEFLAG_FOLEAUTOMATION);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = ICreateTypeInfo_AddRefTypeInfo(cti, unk, &href);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = ICreateTypeInfo_AddImplType(cti, 0, href);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = ICreateTypeInfo_QueryInterface(cti, &IID_ITypeInfo, (void**)&ti);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    ICreateTypeInfo_Release(cti);
+    ITypeInfo_Release(unk);
+    ITypeLib_Release(stdole);
+
+    hr = ICreateTypeLib2_CreateTypeInfo(ctl, classW, TKIND_COCLASS, &cti);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = ICreateTypeInfo_SetGuid(cti, &coclassguid);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = ICreateTypeInfo_AddRefTypeInfo(cti, ti, &href);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = ICreateTypeInfo_AddImplType(cti, 0, href);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    ITypeInfo_Release(ti);
+    ICreateTypeInfo_Release(cti);
+
+    hr = ICreateTypeLib2_SaveAllChanges(ctl);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = ICreateTypeLib2_QueryInterface(ctl, &IID_ITypeLib, (void**)&tl);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = RegisterTypeLib(tl, filenameW, NULL);
+    if (hr == TYPE_E_REGISTRYACCESS)
+    {
+        win_skip("Insufficient privileges to register typelib in the registry\n");
+        ITypeLib_Release(tl);
+        DeleteFileW(filenameW);
+        CoUninitialize();
+        return;
+    }
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    ITypeLib_Release(tl);
+    ok(0 == ICreateTypeLib2_Release(ctl), "Typelib still has references\n");
+
+    /* SYS_WIN32 typelibs should be registered only as 32-bit */
+    lr = RegOpenKeyExA(HKEY_CLASSES_ROOT, "TypeLib\\{3b9ff02e-9675-4861-b781-ceaea4782acc}\\0.0\\0\\win64", 0, KEY_READ, &hkey);
+    ok(lr == ERROR_FILE_NOT_FOUND, "got wrong return code: %u\n", lr);
+
+    lr = RegOpenKeyExA(HKEY_CLASSES_ROOT, "TypeLib\\{3b9ff02e-9675-4861-b781-ceaea4782acc}\\0.0\\0\\win32", 0, KEY_READ, &hkey);
+    ok(lr == ERROR_SUCCESS, "got wrong return code: %u\n", lr);
+    RegCloseKey(hkey);
+
+    hr = CoGetPSClsid(&interfaceguid, &clsid);
+    ok(hr == S_OK, "got: %x\n", hr);
+
+    hr = CoGetClassObject(&clsid, CLSCTX_INPROC_SERVER, NULL,
+            &IID_IPSFactoryBuffer, (void **)&factory);
+    ok(hr == S_OK, "got: %x\n", hr);
+
+    hr = IPSFactoryBuffer_CreateStub(factory, &interfaceguid, &uk, &base_stub);
+    ok(hr == S_OK, "got: %x\n", hr);
+
+    IPSFactoryBuffer_Release(factory);
+
+    UnRegisterTypeLib(&libguid, 0, 0, 0, SYS_WIN32);
+    DeleteFileW(filenameW);
+
+    CoUninitialize();
+}
+
 START_TEST(typelib)
 {
     const char *filename;
@@ -4953,13 +5443,13 @@ START_TEST(typelib)
     test_inheritance();
     test_SetVarHelpContext();
     test_SetFuncAndParamNames();
-    test_SetVarDocString();
+    test_SetDocString();
     test_FindName();
 
     if ((filename = create_test_typelib(2)))
     {
         test_dump_typelib( filename );
-        DeleteFile( filename );
+        DeleteFileA( filename );
     }
 
     test_register_typelib(TRUE);
@@ -4968,4 +5458,6 @@ START_TEST(typelib)
     test_LoadTypeLib();
     test_TypeInfo2_GetContainingTypeLib();
     test_LoadRegTypeLib();
+    test_GetLibAttr();
+    test_stub();
 }
index b78e871..0327af3 100644 (file)
@@ -28,6 +28,7 @@
 #include <math.h>
 #include <float.h>
 
+#define COBJMACROS
 #define CONST_VTABLE
 
 //#include "windef.h"
@@ -100,6 +101,198 @@ static BOOL has_i8;
 #define R8_MAX DBL_MAX
 #define R8_MIN DBL_MIN
 
+typedef struct IRecordInfoImpl
+{
+    IRecordInfo IRecordInfo_iface;
+    LONG ref;
+    unsigned int recordclear;
+    unsigned int getsize;
+    unsigned int recordcopy;
+    struct __tagBRECORD *rec;
+} IRecordInfoImpl;
+
+static inline IRecordInfoImpl *impl_from_IRecordInfo(IRecordInfo *iface)
+{
+    return CONTAINING_RECORD(iface, IRecordInfoImpl, IRecordInfo_iface);
+}
+
+static HRESULT WINAPI RecordInfo_QueryInterface(IRecordInfo *iface, REFIID riid, void **obj)
+{
+    *obj = NULL;
+
+    if (IsEqualIID(riid, &IID_IUnknown) ||
+        IsEqualIID(riid, &IID_IRecordInfo))
+    {
+        *obj = iface;
+        IRecordInfo_AddRef(iface);
+        return S_OK;
+    }
+
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI RecordInfo_AddRef(IRecordInfo *iface)
+{
+    IRecordInfoImpl* This = impl_from_IRecordInfo(iface);
+    return InterlockedIncrement(&This->ref);
+}
+
+static ULONG WINAPI RecordInfo_Release(IRecordInfo *iface)
+{
+    IRecordInfoImpl* This = impl_from_IRecordInfo(iface);
+    ULONG ref = InterlockedDecrement(&This->ref);
+
+    if (!ref)
+        HeapFree(GetProcessHeap(), 0, This);
+
+    return ref;
+}
+
+static HRESULT WINAPI RecordInfo_RecordInit(IRecordInfo *iface, PVOID pvNew)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI RecordInfo_RecordClear(IRecordInfo *iface, void *data)
+{
+    IRecordInfoImpl* This = impl_from_IRecordInfo(iface);
+    This->recordclear++;
+    This->rec->pvRecord = NULL;
+    return S_OK;
+}
+
+static HRESULT WINAPI RecordInfo_RecordCopy(IRecordInfo *iface, void *src, void *dest)
+{
+    IRecordInfoImpl* This = impl_from_IRecordInfo(iface);
+    This->recordcopy++;
+    ok(src == (void*)0xdeadbeef, "wrong src pointer %p\n", src);
+    return S_OK;
+}
+
+static HRESULT WINAPI RecordInfo_GetGuid(IRecordInfo *iface, GUID *pguid)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI RecordInfo_GetName(IRecordInfo *iface, BSTR *pbstrName)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI RecordInfo_GetSize(IRecordInfo *iface, ULONG* size)
+{
+    IRecordInfoImpl* This = impl_from_IRecordInfo(iface);
+    This->getsize++;
+    *size = 0;
+    return S_OK;
+}
+
+static HRESULT WINAPI RecordInfo_GetTypeInfo(IRecordInfo *iface, ITypeInfo **ppTypeInfo)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI RecordInfo_GetField(IRecordInfo *iface, PVOID pvData,
+                                                LPCOLESTR szFieldName, VARIANT *pvarField)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI RecordInfo_GetFieldNoCopy(IRecordInfo *iface, PVOID pvData,
+                            LPCOLESTR szFieldName, VARIANT *pvarField, PVOID *ppvDataCArray)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI RecordInfo_PutField(IRecordInfo *iface, ULONG wFlags, PVOID pvData,
+                                            LPCOLESTR szFieldName, VARIANT *pvarField)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI RecordInfo_PutFieldNoCopy(IRecordInfo *iface, ULONG wFlags,
+                PVOID pvData, LPCOLESTR szFieldName, VARIANT *pvarField)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI RecordInfo_GetFieldNames(IRecordInfo *iface, ULONG *pcNames,
+                                                BSTR *rgBstrNames)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static BOOL WINAPI RecordInfo_IsMatchingType(IRecordInfo *iface, IRecordInfo *info2)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static PVOID WINAPI RecordInfo_RecordCreate(IRecordInfo *iface)
+{
+    ok(0, "unexpected call\n");
+    return NULL;
+}
+
+static HRESULT WINAPI RecordInfo_RecordCreateCopy(IRecordInfo *iface, PVOID pvSource,
+                                                    PVOID *ppvDest)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI RecordInfo_RecordDestroy(IRecordInfo *iface, PVOID pvRecord)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static const IRecordInfoVtbl RecordInfoVtbl =
+{
+    RecordInfo_QueryInterface,
+    RecordInfo_AddRef,
+    RecordInfo_Release,
+    RecordInfo_RecordInit,
+    RecordInfo_RecordClear,
+    RecordInfo_RecordCopy,
+    RecordInfo_GetGuid,
+    RecordInfo_GetName,
+    RecordInfo_GetSize,
+    RecordInfo_GetTypeInfo,
+    RecordInfo_GetField,
+    RecordInfo_GetFieldNoCopy,
+    RecordInfo_PutField,
+    RecordInfo_PutFieldNoCopy,
+    RecordInfo_GetFieldNames,
+    RecordInfo_IsMatchingType,
+    RecordInfo_RecordCreate,
+    RecordInfo_RecordCreateCopy,
+    RecordInfo_RecordDestroy
+};
+
+static IRecordInfoImpl *get_test_recordinfo(void)
+{
+    IRecordInfoImpl *rec;
+
+    rec = HeapAlloc(GetProcessHeap(), 0, sizeof(IRecordInfoImpl));
+    rec->IRecordInfo_iface.lpVtbl = &RecordInfoVtbl;
+    rec->ref = 1;
+    rec->recordclear = 0;
+    rec->getsize = 0;
+    rec->recordcopy = 0;
+
+    return rec;
+}
+
 static void init(void)
 {
     BSTR bstr;
@@ -120,7 +313,7 @@ static void init(void)
     if (bstr) memcpy(&sz12_false[2], bstr, SysStringByteLen(bstr) + sizeof(WCHAR));
     SysFreeString(bstr);
 
-    hOleaut32 = GetModuleHandle("oleaut32.dll");
+    hOleaut32 = GetModuleHandleA("oleaut32.dll");
     has_i8 = GetProcAddress(hOleaut32, "VarI8FromI1") != NULL;
     if (!has_i8)
         skip("No support for I8 and UI8 data types\n");
@@ -294,7 +487,7 @@ static BOOL is_expected_variant( const VARIANT *result, const VARIANT *expected
         return !memcmp( &V_DECIMAL(result), &V_DECIMAL(expected), sizeof(DECIMAL) );
     default:
         ok(0, "unhandled variant type %s\n",vtstr(V_VT(expected)));
-        return 0;
+        return FALSE;
     }
 }
 
@@ -353,14 +546,11 @@ static void _test_bstr_var(unsigned line, const VARIANT *v, const char *str)
 
 static void test_VariantInit(void)
 {
-  VARIANTARG v1, v2;
+  VARIANT v;
 
-  /* Test that VariantInit() only sets the type */
-  memset(&v1, -1, sizeof(v1));
-  v2 = v1;
-  V_VT(&v2) = VT_EMPTY;
-  VariantInit(&v1);
-  ok(!memcmp(&v1, &v2, sizeof(v1)), "VariantInit() set extra fields\n");
+  memset(&v, -1, sizeof(v));
+  VariantInit(&v);
+  ok(V_VT(&v) == VT_EMPTY, "VariantInit() returned vt %d\n", V_VT(&v));
 }
 
 /* All possible combinations of extra V_VT() flags */
@@ -385,9 +575,9 @@ static const VARTYPE ExtraFlags[16] =
 };
 
 /* Determine if a vt is valid for VariantClear() */
-static int IsValidVariantClearVT(VARTYPE vt, VARTYPE extraFlags)
+static BOOL IsValidVariantClearVT(VARTYPE vt, VARTYPE extraFlags)
 {
-  int ret = 0;
+  BOOL ret = FALSE;
 
   /* Only the following flags/types are valid */
   if ((vt <= VT_LPWSTR || vt == VT_RECORD || vt == VT_CLSID) &&
@@ -396,10 +586,10 @@ static int IsValidVariantClearVT(VARTYPE vt, VARTYPE extraFlags)
       (!(extraFlags & (VT_BYREF|VT_ARRAY)) || vt > VT_NULL) &&
       (extraFlags == 0 || extraFlags == VT_BYREF || extraFlags == VT_ARRAY ||
        extraFlags == (VT_ARRAY|VT_BYREF)))
-    ret = 1; /* ok */
+    ret = TRUE; /* ok */
 
   if (!has_i8 && (vt == VT_I8 || vt == VT_UI8))
-    ret = 0; /* Old versions of oleaut32 */
+    ret = FALSE; /* Old versions of oleaut32 */
   return ret;
 }
 
@@ -445,6 +635,8 @@ static test_VariantClearImpl test_myVariantClearImpl = {{&test_VariantClear_vtbl
 
 static void test_VariantClear(void)
 {
+  struct __tagBRECORD *rec;
+  IRecordInfoImpl *recinfo;
   HRESULT hres;
   VARIANTARG v;
   VARIANT v2;
@@ -563,10 +755,28 @@ static void test_VariantClear(void)
   ok(V_DISPATCHREF(&v) == (IDispatch**)&punk, "dispatch ref %p\n", V_DISPATCHREF(&v));
   /* Check that nothing got called */
   ok(test_myVariantClearImpl.events ==  0, "Unexpected call. events %08x\n", test_myVariantClearImpl.events);
+
+  /* RECORD */
+  recinfo = get_test_recordinfo();
+  V_VT(&v) = VT_RECORD;
+  rec = &V_UNION(&v, brecVal);
+  rec->pRecInfo = &recinfo->IRecordInfo_iface;
+  rec->pvRecord = (void*)0xdeadbeef;
+  recinfo->recordclear = 0;
+  recinfo->ref = 2;
+  recinfo->rec = rec;
+  hres = VariantClear(&v);
+  ok(hres == S_OK, "ret %08x\n", hres);
+  ok(rec->pvRecord == NULL, "got %p\n", rec->pvRecord);
+  ok(recinfo->recordclear == 1, "got %d\n", recinfo->recordclear);
+  ok(recinfo->ref == 1, "got %d\n", recinfo->ref);
+  IRecordInfo_Release(&recinfo->IRecordInfo_iface);
 }
 
 static void test_VariantCopy(void)
 {
+  struct __tagBRECORD *rec;
+  IRecordInfoImpl *recinfo;
   VARIANTARG vSrc, vDst;
   VARTYPE vt;
   size_t i;
@@ -682,18 +892,45 @@ static void test_VariantCopy(void)
     }
     VariantClear(&vDst);
   }
+
+  /* copy RECORD */
+  recinfo = get_test_recordinfo();
+
+  memset(&vDst, 0, sizeof(vDst));
+  V_VT(&vDst) = VT_EMPTY;
+
+  V_VT(&vSrc) = VT_RECORD;
+  rec = &V_UNION(&vSrc, brecVal);
+  rec->pRecInfo = &recinfo->IRecordInfo_iface;
+  rec->pvRecord = (void*)0xdeadbeef;
+
+  recinfo->recordclear = 0;
+  recinfo->recordcopy = 0;
+  recinfo->getsize = 0;
+  recinfo->rec = rec;
+  hres = VariantCopy(&vDst, &vSrc);
+  ok(hres == S_OK, "ret %08x\n", hres);
+
+  rec = &V_UNION(&vDst, brecVal);
+  ok(rec->pvRecord != (void*)0xdeadbeef && rec->pvRecord != NULL, "got %p\n", rec->pvRecord);
+  ok(rec->pRecInfo == &recinfo->IRecordInfo_iface, "got %p\n", rec->pRecInfo);
+  ok(recinfo->getsize == 1, "got %d\n", recinfo->recordclear);
+  ok(recinfo->recordcopy == 1, "got %d\n", recinfo->recordclear);
+
+  VariantClear(&vDst);
+  VariantClear(&vSrc);
 }
 
 /* Determine if a vt is valid for VariantCopyInd() */
-static int IsValidVariantCopyIndVT(VARTYPE vt, VARTYPE extraFlags)
+static BOOL IsValidVariantCopyIndVT(VARTYPE vt, VARTYPE extraFlags)
 {
-  int ret = 0;
+  BOOL ret = FALSE;
 
   if ((extraFlags & VT_ARRAY) ||
      (vt > VT_NULL && vt != (VARTYPE)15 && vt < VT_VOID &&
      !(extraFlags & (VT_VECTOR|VT_RESERVED))))
   {
-    ret = 1; /* ok */
+    ret = TRUE; /* ok */
   }
   return ret;
 }
@@ -1731,6 +1968,10 @@ static void test_SystemTimeToVariantTime(void)
   ST2DT(2,1,1980,0,0,0,0,TRUE,29222.0);
   ST2DT(0,1,1980,0,0,0,0,TRUE,29220.0);   /* Rolls back to 31 Dec 1899 */
   ST2DT(1,13,1980,0,0,0,0,FALSE,29587.0); /* Fails on invalid month */
+  ST2DT(32,1,1980,0,0,0,0,FALSE,0.0);     /* Fails on invalid day */
+  ST2DT(1,1,-1,0,0,0,0,FALSE,0.0);        /* Fails on invalid year */
+  ST2DT(1,1,10000,0,0,0,0,FALSE,0.0);     /* Fails on invalid year */
+  ST2DT(1,1,9999,0,0,0,0,TRUE,2958101.0); /* 9999 is last valid year */
   ST2DT(31,12,90,0,0,0,0,TRUE,33238.0);   /* 30 <= year < 100 is 1900+year */
   ST2DT(1,1,30,0,0,0,0,TRUE,10959.0);     /* 30 <= year < 100 is 1900+year */
   ST2DT(1,1,29,0,0,0,0,TRUE,47119.0);     /* 0 <= year < 30 is 2000+year */
@@ -2879,7 +3120,7 @@ static void test_VarFix(void)
 
         for (vt = 0; vt <= VT_BSTR_BLOB; vt++)
         {
-            HRESULT bFail = TRUE;
+            BOOL bFail = TRUE;
 
             SKIPTESTS(vt);
 
@@ -2994,7 +3235,7 @@ static void test_VarInt(void)
 
         for (vt = 0; vt <= VT_BSTR_BLOB; vt++)
         {
-            HRESULT bFail = TRUE;
+            BOOL bFail = TRUE;
 
             SKIPTESTS(vt);
 
@@ -3115,7 +3356,7 @@ static void test_VarNeg(void)
 
         for (vt = 0; vt <= VT_BSTR_BLOB; vt++)
         {
-            HRESULT bFail = TRUE;
+            BOOL bFail = TRUE;
 
             SKIPTESTS(vt);
 
@@ -5370,7 +5611,7 @@ static void test_VarCat(void)
     static const WCHAR sz12_date[] = {'1','2','9','/','3','0','/','1','9','8','0','\0'};
     static const WCHAR sz12_date_broken[] = {'1','2','9','/','3','0','/','8','0','\0'};
     static const WCHAR sz_empty[] = {'\0'};
-    TCHAR orig_date_format[128];
+    CHAR orig_date_format[128];
     VARTYPE leftvt, rightvt, resultvt;
     HRESULT hres;
     HRESULT expected_error_num;
@@ -5379,8 +5620,8 @@ static void test_VarCat(void)
 
     /* Set date format for testing */
     lcid = LOCALE_USER_DEFAULT;
-    GetLocaleInfo(lcid,LOCALE_SSHORTDATE,orig_date_format,128);
-    SetLocaleInfo(lcid,LOCALE_SSHORTDATE,"M/d/yyyy");
+    GetLocaleInfoA(lcid,LOCALE_SSHORTDATE,orig_date_format,128);
+    SetLocaleInfoA(lcid,LOCALE_SSHORTDATE,"M/d/yyyy");
 
     VariantInit(&left);
     VariantInit(&right);
@@ -5701,7 +5942,7 @@ static void test_VarCat(void)
            "VarCat: EMPTY concat with EMPTY did not return empty VT_BSTR\n");
 
     /* Restore original date format settings */
-    SetLocaleInfo(lcid,LOCALE_SSHORTDATE,orig_date_format);
+    SetLocaleInfoA(lcid,LOCALE_SSHORTDATE,orig_date_format);
 
     VariantClear(&left);
     VariantClear(&right);
@@ -5763,11 +6004,11 @@ static void test_VarAnd(void)
     VARTYPE i;
     HRESULT hres;
 
+    CHECKPTR(VarAnd);
+
     true_str = SysAllocString(szTrue);
     false_str = SysAllocString(szFalse);
 
-    CHECKPTR(VarAnd);
-
     /* Test all possible flag/vt combinations & the resulting vt type */
     for (i = 0; i < sizeof(ExtraFlags)/sizeof(ExtraFlags[0]); i++)
     {
@@ -7242,11 +7483,11 @@ static void test_VarDiv(void)
     HRESULT hres;
     double r;
 
+    CHECKPTR(VarDiv);
+
     num1_str = SysAllocString(str1);
     num2_str = SysAllocString(str2);
 
-    CHECKPTR(VarDiv);
-
     /* Test all possible flag/vt combinations & the resulting vt type */
     for (i = 0; i < sizeof(ExtraFlags)/sizeof(ExtraFlags[0]); i++)
     {
index dda443d..8998d2d 100644 (file)
 #define COM_NO_WINDOWS_H
 
 #define CONST_VTABLE
+#define COBJMACROS
 
 #include <wine/test.h>
 #include <winnls.h>
 #include <objbase.h>
 #include <oleauto.h>
 #include <math.h>
+#include <test_tlb.h>
+
+#include <initguid.h>
+
+DEFINE_GUID(UUID_test_struct, 0x4029f190, 0xca4a, 0x4611, 0xae,0xb9,0x67,0x39,0x83,0xcb,0x96,0xdd);
 
 /* Some Visual C++ versions choke on __uint64 to float conversions.
  * To fix this you need either VC++ 6.0 plus the processor pack
@@ -251,6 +257,32 @@ static BOOL has_locales;
   BADVAR(VT_CLSID); \
   BADVAR(VT_BSTR_BLOB)
 
+#define DEFINE_EXPECT(func) \
+    static BOOL expect_ ## func = FALSE, called_ ## func = FALSE
+
+#define SET_EXPECT(func) \
+    do { called_ ## func = FALSE; expect_ ## func = TRUE; } while(0)
+
+#define CHECK_EXPECT2(func) \
+    do { \
+        ok(expect_ ##func, "unexpected call " #func "\n"); \
+        called_ ## func = TRUE; \
+    }while(0)
+
+#define CHECK_EXPECT(func) \
+    do { \
+        CHECK_EXPECT2(func); \
+        expect_ ## func = FALSE; \
+    }while(0)
+
+#define CHECK_CALLED(func) \
+    do { \
+        ok(called_ ## func, "expected " #func "\n"); \
+        expect_ ## func = called_ ## func = FALSE; \
+    }while(0)
+
+DEFINE_EXPECT(dispatch_invoke);
+
 /* Early versions of oleaut32 are missing many functions */
 static HRESULT (WINAPI *pVarI1FromUI1)(BYTE,signed char*);
 static HRESULT (WINAPI *pVarI1FromI2)(SHORT,signed char*);
@@ -461,6 +493,7 @@ static HRESULT (WINAPI *pVarDecDiv)(const DECIMAL*,const DECIMAL*,DECIMAL*);
 static HRESULT (WINAPI *pVarDecCmp)(const DECIMAL*,const DECIMAL*);
 static HRESULT (WINAPI *pVarDecCmpR8)(const DECIMAL*,double);
 static HRESULT (WINAPI *pVarDecNeg)(const DECIMAL*,DECIMAL*);
+static HRESULT (WINAPI *pVarDecRound)(const DECIMAL*,int,DECIMAL*);
 
 static HRESULT (WINAPI *pVarBoolFromUI1)(BYTE,VARIANT_BOOL*);
 static HRESULT (WINAPI *pVarBoolFromI2)(SHORT,VARIANT_BOOL*);
@@ -502,73 +535,93 @@ typedef struct
   BOOL bFailInvoke;
 } DummyDispatch;
 
-static DummyDispatch dispatch;
-
 static inline DummyDispatch *impl_from_IDispatch(IDispatch *iface)
 {
   return CONTAINING_RECORD(iface, DummyDispatch, IDispatch_iface);
 }
 
-static ULONG WINAPI DummyDispatch_AddRef(LPDISPATCH iface)
+static ULONG WINAPI DummyDispatch_AddRef(IDispatch *iface)
 {
   DummyDispatch *This = impl_from_IDispatch(iface);
-
-  trace("AddRef(%p)\n", iface);
   return InterlockedIncrement(&This->ref);
 }
 
-static ULONG WINAPI DummyDispatch_Release(LPDISPATCH iface)
+static ULONG WINAPI DummyDispatch_Release(IDispatch *iface)
 {
   DummyDispatch *This = impl_from_IDispatch(iface);
-
-  trace("Release(%p)\n", iface);
   return InterlockedDecrement(&This->ref);
 }
 
-static HRESULT WINAPI DummyDispatch_QueryInterface(LPDISPATCH iface,
+static HRESULT WINAPI DummyDispatch_QueryInterface(IDispatch *iface,
                                                    REFIID riid,
                                                    void** ppvObject)
 {
-  trace("QueryInterface(%p)\n", iface);
-  if (ppvObject)
+  *ppvObject = NULL;
+
+  if (IsEqualIID(riid, &IID_IDispatch) ||
+      IsEqualIID(riid, &IID_IUnknown))
   {
-    *ppvObject = NULL;
-    if (IsEqualIID(riid, &IID_IDispatch))
-    {
-      trace("Asked for IID_IDispatch\n");
-      *ppvObject = iface;
-    }
-    else if (IsEqualIID(riid, &IID_IUnknown))
-    {
-      trace("Asked for IID_IUnknown\n");
       *ppvObject = iface;
-    }
-    if (*ppvObject)
-    {
-      DummyDispatch_AddRef(*ppvObject);
-      return S_OK;
-    }
+      IDispatch_AddRef(iface);
   }
-  return E_NOINTERFACE;
+
+  return *ppvObject ? S_OK : E_NOINTERFACE;
+}
+
+static HRESULT WINAPI DummyDispatch_GetTypeInfoCount(IDispatch *iface, UINT *pctinfo)
+{
+  ok(0, "Unexpected call\n");
+  return E_NOTIMPL;
 }
 
-static HRESULT WINAPI DummyDispatch_Invoke(LPDISPATCH iface,
-                                           DISPID dispIdMember, REFIID riid,
+static HRESULT WINAPI DummyDispatch_GetTypeInfo(IDispatch *iface, UINT tinfo, LCID lcid, ITypeInfo **ti)
+{
+  ok(0, "Unexpected call\n");
+  return E_NOTIMPL;
+}
+
+static HRESULT WINAPI DummyDispatch_GetIDsOfNames(IDispatch *iface, REFIID riid, LPOLESTR *names,
+    UINT cnames, LCID lcid, DISPID *dispid)
+{
+  ok(0, "Unexpected call\n");
+  return E_NOTIMPL;
+}
+
+static HRESULT WINAPI DummyDispatch_Invoke(IDispatch *iface,
+                                           DISPID dispid, REFIID riid,
                                            LCID lcid, WORD wFlags,
-                                           DISPPARAMS *pDispParams,
-                                           VARIANT *pVarResult,
-                                           EXCEPINFO *pExcepInfo,
-                                           UINT *puArgErr)
+                                           DISPPARAMS *params,
+                                           VARIANT *res,
+                                           EXCEPINFO *ei,
+                                           UINT *arg_err)
 {
-  trace("Invoke(%p)\n", iface);
+  DummyDispatch *This = impl_from_IDispatch(iface);
+
+  CHECK_EXPECT(dispatch_invoke);
+
+  ok(dispid == DISPID_VALUE, "got dispid %d\n", dispid);
+  ok(IsEqualIID(riid, &IID_NULL), "go riid %s\n", wine_dbgstr_guid(riid));
   ok(wFlags == DISPATCH_PROPERTYGET, "Flags wrong\n");
-  ok(pDispParams->cArgs == 0, "Property get has args\n");
 
-  if (dispatch.bFailInvoke)
+  ok(params->rgvarg == NULL, "got %p\n", params->rgvarg);
+  ok(params->rgdispidNamedArgs == NULL, "got %p\n", params->rgdispidNamedArgs);
+  ok(params->cArgs == 0, "got %d\n", params->cArgs);
+  ok(params->cNamedArgs == 0, "got %d\n", params->cNamedArgs);
+
+  ok(res != NULL, "got %p\n", res);
+  ok(V_VT(res) == VT_EMPTY, "got %d\n", V_VT(res));
+  ok(ei == NULL, "got %p\n", ei);
+  ok(arg_err == NULL, "got %p\n", arg_err);
+
+  if (This->bFailInvoke)
     return E_OUTOFMEMORY;
 
-  memset(pVarResult, 0, sizeof(*pVarResult));
-  V_VT(pVarResult) = dispatch.vt;
+  V_VT(res) = This->vt;
+  if (This->vt == VT_UI1)
+      V_UI1(res) = 1;
+  else
+      memset(res, 0, sizeof(*res));
+
   return S_OK;
 }
 
@@ -577,13 +630,19 @@ static const IDispatchVtbl DummyDispatch_VTable =
   DummyDispatch_QueryInterface,
   DummyDispatch_AddRef,
   DummyDispatch_Release,
-  NULL,
-  NULL,
-  NULL,
+  DummyDispatch_GetTypeInfoCount,
+  DummyDispatch_GetTypeInfo,
+  DummyDispatch_GetIDsOfNames,
   DummyDispatch_Invoke
 };
 
-static DummyDispatch dispatch = { { &DummyDispatch_VTable }, 1, 0, 0 };
+static void init_test_dispatch(LONG ref, VARTYPE vt, DummyDispatch *dispatch)
+{
+    dispatch->IDispatch_iface.lpVtbl = &DummyDispatch_VTable;
+    dispatch->ref = ref;
+    dispatch->vt = vt;
+    dispatch->bFailInvoke = FALSE;
+}
 
 /*
  * VT_I1/VT_UI1
@@ -685,11 +744,15 @@ static void test_VarI1FromR4(void)
 
   CHECKPTR(VarI1FromR4);
   CONVERT(VarI1FromR4, -129.0f); EXPECT_OVERFLOW;
+  CONVERT(VarI1FromR4, -128.51f); EXPECT_OVERFLOW;
+  CONVERT(VarI1FromR4, -128.5f); EXPECT(-128);
   CONVERT(VarI1FromR4, -128.0f); EXPECT(-128);
   CONVERT(VarI1FromR4, -1.0f);   EXPECT(-1);
   CONVERT(VarI1FromR4, 0.0f);    EXPECT(0);
   CONVERT(VarI1FromR4, 1.0f);    EXPECT(1);
   CONVERT(VarI1FromR4, 127.0f);  EXPECT(127);
+  CONVERT(VarI1FromR4, 127.49f);  EXPECT(127);
+  CONVERT(VarI1FromR4, 127.5f);  EXPECT_OVERFLOW;
   CONVERT(VarI1FromR4, 128.0f);  EXPECT_OVERFLOW;
 
   CONVERT(VarI1FromR4, -1.5f); EXPECT(-2);
@@ -708,11 +771,15 @@ static void test_VarI1FromR8(void)
 
   CHECKPTR(VarI1FromR8);
   CONVERT(VarI1FromR8, -129.0); EXPECT_OVERFLOW;
+  CONVERT(VarI1FromR8, -128.51); EXPECT_OVERFLOW;
+  CONVERT(VarI1FromR8, -128.5); EXPECT(-128);
   CONVERT(VarI1FromR8, -128.0); EXPECT(-128);
   CONVERT(VarI1FromR8, -1.0);   EXPECT(-1);
   CONVERT(VarI1FromR8, 0.0);    EXPECT(0);
   CONVERT(VarI1FromR8, 1.0);    EXPECT(1);
   CONVERT(VarI1FromR8, 127.0);  EXPECT(127);
+  CONVERT(VarI1FromR8, 127.49);  EXPECT(127);
+  CONVERT(VarI1FromR8, 127.5);  EXPECT_OVERFLOW;
   CONVERT(VarI1FromR8, 128.0);  EXPECT_OVERFLOW;
 
   CONVERT(VarI1FromR8, -1.5); EXPECT(-2);
@@ -929,9 +996,13 @@ static void test_VarUI1FromR4(void)
 
   CHECKPTR(VarUI1FromR4);
   CONVERT(VarUI1FromR4, -1.0f);  EXPECT_OVERFLOW;
+  CONVERT(VarUI1FromR4, -0.51f);  EXPECT_OVERFLOW;
+  CONVERT(VarUI1FromR4, -0.5f);   EXPECT(0);
   CONVERT(VarUI1FromR4, 0.0f);   EXPECT(0);
   CONVERT(VarUI1FromR4, 1.0f);   EXPECT(1);
   CONVERT(VarUI1FromR4, 255.0f); EXPECT(255);
+  CONVERT(VarUI1FromR4, 255.49f); EXPECT(255);
+  CONVERT(VarUI1FromR4, 255.5f); EXPECT_OVERFLOW;
   CONVERT(VarUI1FromR4, 256.0f); EXPECT_OVERFLOW;
 
   /* Rounding */
@@ -951,9 +1022,13 @@ static void test_VarUI1FromR8(void)
 
   CHECKPTR(VarUI1FromR8);
   CONVERT(VarUI1FromR8, -1.0);  EXPECT_OVERFLOW;
+  CONVERT(VarUI1FromR8, -0.51);  EXPECT_OVERFLOW;
+  CONVERT(VarUI1FromR8, -0.5);   EXPECT(0);
   CONVERT(VarUI1FromR8, 0.0);   EXPECT(0);
   CONVERT(VarUI1FromR8, 1.0);   EXPECT(1);
   CONVERT(VarUI1FromR8, 255.0); EXPECT(255);
+  CONVERT(VarUI1FromR8, 255.49); EXPECT(255);
+  CONVERT(VarUI1FromR8, 255.5); EXPECT_OVERFLOW;
   CONVERT(VarUI1FromR8, 256.0); EXPECT_OVERFLOW;
 
   /* Rounding */
@@ -1057,6 +1132,7 @@ static void test_VarUI1FromStr(void)
 
 static void test_VarUI1FromDisp(void)
 {
+  DummyDispatch dispatch;
   CONVVARS(LCID);
   VARIANTARG vSrc, vDst;
 
@@ -1079,24 +1155,41 @@ static void test_VarUI1FromDisp(void)
   VariantInit(&vSrc);
   VariantInit(&vDst);
 
+  init_test_dispatch(1, VT_UI1, &dispatch);
   V_VT(&vSrc) = VT_DISPATCH;
   V_DISPATCH(&vSrc) = &dispatch.IDispatch_iface;
-  dispatch.vt = VT_UI1;
-  dispatch.bFailInvoke = FALSE;
 
+  SET_EXPECT(dispatch_invoke);
+  out = 10;
   hres = pVarUI1FromDisp(&dispatch.IDispatch_iface, in, &out);
-  trace("0x%08x\n", hres);
+  ok(broken(hres == DISP_E_BADVARTYPE) || hres == S_OK, "got 0x%08x\n", hres);
+  ok(broken(out == 10) || out == 1, "got %d\n", out);
+  CHECK_CALLED(dispatch_invoke);
 
+  SET_EXPECT(dispatch_invoke);
+  V_VT(&vDst) = VT_EMPTY;
+  V_UI1(&vDst) = 0;
   hres = VariantChangeTypeEx(&vDst, &vSrc, in, 0, VT_UI1);
-  trace("0x%08x\n", hres);
+  ok(hres == S_OK, "got 0x%08x\n", hres);
+  ok(V_VT(&vDst) == VT_UI1, "got %d\n", V_VT(&vDst));
+  ok(V_UI1(&vDst) == 1, "got %d\n", V_UI1(&vDst));
+  CHECK_CALLED(dispatch_invoke);
 
   dispatch.bFailInvoke = TRUE;
 
+  SET_EXPECT(dispatch_invoke);
+  out = 10;
   hres = pVarUI1FromDisp(&dispatch.IDispatch_iface, in, &out);
-  trace("0x%08x\n", hres);
+  ok(hres == DISP_E_TYPEMISMATCH, "got 0x%08x\n", hres);
+  ok(out == 10, "got %d\n", out);
+  CHECK_CALLED(dispatch_invoke);
 
+  SET_EXPECT(dispatch_invoke);
+  V_VT(&vDst) = VT_EMPTY;
   hres = VariantChangeTypeEx(&vDst, &vSrc, in, 0, VT_UI1);
-  trace("0x%08x\n", hres);
+  ok(hres == DISP_E_TYPEMISMATCH, "got 0x%08x\n", hres);
+  ok(V_VT(&vDst) == VT_EMPTY, "got %d\n", V_VT(&vDst));
+  CHECK_CALLED(dispatch_invoke);
 }
 
 static void test_VarUI1Copy(void)
@@ -1209,11 +1302,15 @@ static void test_VarI2FromR4(void)
 
   CHECKPTR(VarI2FromR4);
   CONVERT(VarI2FromR4, -32769.0f); EXPECT_OVERFLOW;
+  CONVERT(VarI2FromR4, -32768.51f); EXPECT_OVERFLOW;
+  CONVERT(VarI2FromR4, -32768.5f); EXPECT(-32768);
   CONVERT(VarI2FromR4, -32768.0f); EXPECT(-32768);
   CONVERT(VarI2FromR4, -1.0f);     EXPECT(-1);
   CONVERT(VarI2FromR4, 0.0f);      EXPECT(0);
   CONVERT(VarI2FromR4, 1.0f);      EXPECT(1);
   CONVERT(VarI2FromR4, 32767.0f);  EXPECT(32767);
+  CONVERT(VarI2FromR4, 32767.49f);  EXPECT(32767);
+  CONVERT(VarI2FromR4, 32767.5f);  EXPECT_OVERFLOW;
   CONVERT(VarI2FromR4, 32768.0f);  EXPECT_OVERFLOW;
 
   /* Rounding */
@@ -1233,11 +1330,15 @@ static void test_VarI2FromR8(void)
 
   CHECKPTR(VarI2FromR8);
   CONVERT(VarI2FromR8, -32769.0); EXPECT_OVERFLOW;
+  CONVERT(VarI2FromR8, -32768.51); EXPECT_OVERFLOW;
+  CONVERT(VarI2FromR8, -32768.5); EXPECT(-32768);
   CONVERT(VarI2FromR8, -32768.0); EXPECT(-32768);
   CONVERT(VarI2FromR8, -1.0);     EXPECT(-1);
   CONVERT(VarI2FromR8, 0.0);      EXPECT(0);
   CONVERT(VarI2FromR8, 1.0);      EXPECT(1);
   CONVERT(VarI2FromR8, 32767.0);  EXPECT(32767);
+  CONVERT(VarI2FromR8, 32767.49);  EXPECT(32767);
+  CONVERT(VarI2FromR8, 32767.5);  EXPECT_OVERFLOW;
   CONVERT(VarI2FromR8, 32768.0);  EXPECT_OVERFLOW;
 
   /* Rounding */
@@ -1457,9 +1558,13 @@ static void test_VarUI2FromR4(void)
 
   CHECKPTR(VarUI2FromR4);
   CONVERT(VarUI2FromR4, -1.0f);    EXPECT_OVERFLOW;
+  CONVERT(VarUI2FromR4, -0.51f);    EXPECT_OVERFLOW;
+  CONVERT(VarUI2FromR4, -0.5f);     EXPECT(0);
   CONVERT(VarUI2FromR4, 0.0f);     EXPECT(0);
   CONVERT(VarUI2FromR4, 1.0f);     EXPECT(1);
   CONVERT(VarUI2FromR4, 65535.0f); EXPECT(65535);
+  CONVERT(VarUI2FromR4, 65535.49f); EXPECT(65535);
+  CONVERT(VarUI2FromR4, 65535.5f); EXPECT_OVERFLOW;
   CONVERT(VarUI2FromR4, 65536.0f); EXPECT_OVERFLOW;
 
   /* Rounding */
@@ -1479,9 +1584,13 @@ static void test_VarUI2FromR8(void)
 
   CHECKPTR(VarUI2FromR8);
   CONVERT(VarUI2FromR8, -1.0);    EXPECT_OVERFLOW;
+  CONVERT(VarUI2FromR8, -0.51);    EXPECT_OVERFLOW;
+  CONVERT(VarUI2FromR8, -0.5);     EXPECT(0);
   CONVERT(VarUI2FromR8, 0.0);     EXPECT(0);
   CONVERT(VarUI2FromR8, 1.0);     EXPECT(1);
   CONVERT(VarUI2FromR8, 65535.0); EXPECT(65535);
+  CONVERT(VarUI2FromR8, 65535.49); EXPECT(65535);
+  CONVERT(VarUI2FromR8, 65535.5); EXPECT_OVERFLOW;
   CONVERT(VarUI2FromR8, 65536.0); EXPECT_OVERFLOW;
 
   /* Rounding */
@@ -1720,11 +1829,15 @@ static void test_VarI4FromR8(void)
 
   CHECKPTR(VarI4FromR8);
   CONVERT(VarI4FromR8, -2147483649.0); EXPECT_OVERFLOW;
+  CONVERT(VarI4FromR8, -2147483648.51); EXPECT_OVERFLOW;
+  CONVERT(VarI4FromR8, -2147483648.5); EXPECT(-2147483647 - 1);
   CONVERT(VarI4FromR8, -2147483648.0); EXPECT(-2147483647 - 1);
   CONVERT(VarI4FromR8, -1.0);          EXPECT(-1);
   CONVERT(VarI4FromR8, 0.0);           EXPECT(0);
   CONVERT(VarI4FromR8, 1.0);           EXPECT(1);
   CONVERT(VarI4FromR8, 2147483647.0);  EXPECT(2147483647);
+  CONVERT(VarI4FromR8, 2147483647.49);  EXPECT(2147483647);
+  CONVERT(VarI4FromR8, 2147483647.5);  EXPECT_OVERFLOW;
   CONVERT(VarI4FromR8, 2147483648.0);  EXPECT_OVERFLOW;
 
   CONVERT(VarI4FromR8, -1.5); EXPECT(-2);
@@ -1945,6 +2058,8 @@ static void test_VarUI4FromR4(void)
   CHECKPTR(VarUI4FromR4);
   /* We can't test max values as they are not exactly representable in a float */
   CONVERT(VarUI4FromR4, -1.0f); EXPECT_OVERFLOW;
+  CONVERT(VarUI4FromR4, -0.51f); EXPECT_OVERFLOW;
+  CONVERT(VarUI4FromR4, -0.5f);  EXPECT(0);
   CONVERT(VarUI4FromR4, 0.0f);  EXPECT(0);
   CONVERT(VarUI4FromR4, 1.0f);  EXPECT(1);
 
@@ -1965,9 +2080,13 @@ static void test_VarUI4FromR8(void)
 
   CHECKPTR(VarUI4FromR8);
   CONVERT(VarUI4FromR8, -1.0);         EXPECT_OVERFLOW;
+  CONVERT(VarUI4FromR4, -0.51f);       EXPECT_OVERFLOW;
+  CONVERT(VarUI4FromR4, -0.5f);        EXPECT(0);
   CONVERT(VarUI4FromR8, 0.0);          EXPECT(0);
   CONVERT(VarUI4FromR8, 1.0);          EXPECT(1);
   CONVERT(VarUI4FromR8, 4294967295.0); EXPECT(4294967295ul);
+  CONVERT(VarUI4FromR8, 4294967295.49); EXPECT(4294967295ul);
+  CONVERT(VarUI4FromR8, 4294967295.5); EXPECT_OVERFLOW;
   CONVERT(VarUI4FromR8, 4294967296.0); EXPECT_OVERFLOW;
 
   CONVERT(VarUI4FromR8, -1.5); EXPECT_OVERFLOW;
@@ -4551,6 +4670,39 @@ static void test_VarDecCmpR8(void)
   SETDEC(l,0,DECIMAL_NEG,-1,-1); r = DECIMAL_NEG; MATH3(VarDecCmpR8); EXPECT_LT;
 }
 
+#define CLEAR(x) memset(&(x), 0xBB, sizeof(x))
+
+static void test_VarDecRound(void)
+{
+    HRESULT hres;
+    DECIMAL l, out;
+
+    CHECKPTR(VarDecRound);
+
+    CLEAR(out); SETDEC(l, 0, 0, 0, 1); hres = pVarDecRound(&l, 3, &out); EXPECTDEC(0, 0, 0, 1);
+
+    CLEAR(out); SETDEC(l, 0, 0, 0, 1); hres = pVarDecRound(&l, 0, &out); EXPECTDEC(0, 0, 0, 1);
+    CLEAR(out); SETDEC(l, 1, 0, 0, 1); hres = pVarDecRound(&l, 0, &out); EXPECTDEC(0, 0, 0, 0);
+    CLEAR(out); SETDEC(l, 1, 0, 0, 1); hres = pVarDecRound(&l, 1, &out); EXPECTDEC(1, 0, 0, 1);
+    CLEAR(out); SETDEC(l, 2, 0, 0, 11); hres = pVarDecRound(&l, 1, &out); EXPECTDEC(1, 0, 0, 1);
+    CLEAR(out); SETDEC(l, 2, 0, 0, 15); hres = pVarDecRound(&l, 1, &out); EXPECTDEC(1, 0, 0, 2);
+    CLEAR(out); SETDEC(l, 6, 0, 0, 550001); hres = pVarDecRound(&l, 1, &out); EXPECTDEC(1, 0, 0, 6);
+
+    CLEAR(out); SETDEC(l, 0, DECIMAL_NEG, 0, 1); hres = pVarDecRound(&l, 0, &out); EXPECTDEC(0, DECIMAL_NEG, 0, 1);
+    CLEAR(out); SETDEC(l, 1, DECIMAL_NEG, 0, 1); hres = pVarDecRound(&l, 0, &out); EXPECTDEC(0, DECIMAL_NEG, 0, 0);
+    CLEAR(out); SETDEC(l, 1, DECIMAL_NEG, 0, 1); hres = pVarDecRound(&l, 1, &out); EXPECTDEC(1, DECIMAL_NEG, 0, 1);
+    CLEAR(out); SETDEC(l, 2, DECIMAL_NEG, 0, 11); hres = pVarDecRound(&l, 1, &out); EXPECTDEC(1, DECIMAL_NEG, 0, 1);
+    CLEAR(out); SETDEC(l, 2, DECIMAL_NEG, 0, 15); hres = pVarDecRound(&l, 1, &out); EXPECTDEC(1, DECIMAL_NEG, 0, 2);
+    CLEAR(out); SETDEC(l, 6, DECIMAL_NEG, 0, 550001); hres = pVarDecRound(&l, 1, &out); EXPECTDEC(1, DECIMAL_NEG, 0, 6);
+
+    CLEAR(out); SETDEC64(l, 0, 0, 0xffffffff, 0xffffffff, 0xffffffff); hres = pVarDecRound(&l, 0, &out); EXPECTDEC64(0, 0, 0xffffffff, 0xffffffff, 0xffffffff);
+    CLEAR(out); SETDEC64(l, 28, 0, 0xffffffff, 0xffffffff, 0xffffffff); hres = pVarDecRound(&l, 0, &out); EXPECTDEC64(0, 0, 0, 0, 8);
+    CLEAR(out); SETDEC64(l, 0, DECIMAL_NEG, 0xffffffff, 0xffffffff, 0xffffffff); hres = pVarDecRound(&l, 0, &out); EXPECTDEC64(0, DECIMAL_NEG, 0xffffffff, 0xffffffff, 0xffffffff);
+    CLEAR(out); SETDEC64(l, 28, DECIMAL_NEG, 0xffffffff, 0xffffffff, 0xffffffff); hres = pVarDecRound(&l, 0, &out); EXPECTDEC64(0, DECIMAL_NEG, 0, 0, 8);
+
+    CLEAR(out); SETDEC(l, 2, 0, 0, 0); hres = pVarDecRound(&l, 1, &out); EXPECTDEC(1, 0, 0, 0);
+}
+
 /*
  * VT_BOOL
  */
@@ -5588,8 +5740,11 @@ static void test_IUnknownClear(void)
 {
   HRESULT hres;
   VARIANTARG v;
-  DummyDispatch u = { { &DummyDispatch_VTable }, 1, VT_UI1, FALSE };
-  IUnknown* pu = (IUnknown*)&u.IDispatch_iface;
+  DummyDispatch u;
+  IUnknown* pu;
+
+  init_test_dispatch(1, VT_UI1, &u);
+  pu = (IUnknown*)&u.IDispatch_iface;
 
   /* Test that IUnknown_Release is called on by-value */
   V_VT(&v) = VT_UNKNOWN;
@@ -5613,8 +5768,11 @@ static void test_IUnknownCopy(void)
 {
   HRESULT hres;
   VARIANTARG vSrc, vDst;
-  DummyDispatch u = { { &DummyDispatch_VTable }, 1, VT_UI1, FALSE };
-  IUnknown* pu = (IUnknown*)&u.IDispatch_iface;
+  DummyDispatch u;
+  IUnknown* pu;
+
+  init_test_dispatch(1, VT_UI1, &u);
+  pu = (IUnknown*)&u.IDispatch_iface;
 
   /* AddRef is called on by-value copy */
   VariantInit(&vDst);
@@ -5661,8 +5819,11 @@ static void test_IUnknownChangeTypeEx(void)
   VARIANTARG vSrc, vDst;
   LCID lcid;
   VARTYPE vt;
-  DummyDispatch u = { { &DummyDispatch_VTable }, 1, VT_UI1, FALSE };
-  IUnknown* pu = (IUnknown*)&u.IDispatch_iface;
+  DummyDispatch u;
+  IUnknown* pu;
+
+  init_test_dispatch(1, VT_UI1, &u);
+  pu = (IUnknown*)&u.IDispatch_iface;
 
   lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
 
@@ -5726,8 +5887,11 @@ static void test_IDispatchClear(void)
 {
   HRESULT hres;
   VARIANTARG v;
-  DummyDispatch d = { { &DummyDispatch_VTable }, 1, VT_UI1, FALSE };
-  IDispatch* pd = &d.IDispatch_iface;
+  DummyDispatch d;
+  IDispatch* pd;
+
+  init_test_dispatch(1, VT_UI1, &d);
+  pd = &d.IDispatch_iface;
 
   /* As per IUnknown */
 
@@ -5751,8 +5915,11 @@ static void test_IDispatchCopy(void)
 {
   HRESULT hres;
   VARIANTARG vSrc, vDst;
-  DummyDispatch d = { { &DummyDispatch_VTable }, 1, VT_UI1, FALSE };
-  IDispatch* pd = &d.IDispatch_iface;
+  DummyDispatch d;
+  IDispatch* pd;
+
+  init_test_dispatch(1, VT_UI1, &d);
+  pd = &d.IDispatch_iface;
 
   /* As per IUnknown */
 
@@ -5796,8 +5963,11 @@ static void test_IDispatchChangeTypeEx(void)
   HRESULT hres;
   VARIANTARG vSrc, vDst;
   LCID lcid;
-  DummyDispatch d = { { &DummyDispatch_VTable }, 1, VT_UI1, FALSE };
-  IDispatch* pd = &d.IDispatch_iface;
+  DummyDispatch d;
+  IDispatch* pd;
+
+  init_test_dispatch(1, VT_UI1, &d);
+  pd = &d.IDispatch_iface;
 
   lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
 
@@ -5872,89 +6042,147 @@ static void test_ErrorChangeTypeEx(void)
 /* VT_EMPTY */
 static void test_EmptyChangeTypeEx(void)
 {
-  HRESULT hres;
-  VARIANTARG vSrc, vDst;
   VARTYPE vt;
   LCID lcid;
 
   lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
 
-  for (vt = 0; vt <= VT_BSTR_BLOB; vt++)
+  for (vt = VT_EMPTY; vt <= VT_BSTR_BLOB; vt++)
   {
-    HRESULT hExpected = DISP_E_BADVARTYPE;
+    HRESULT hExpected, hres;
+    VARIANTARG vSrc, vDst;
 
-    VariantInit(&vSrc);
-    memset(&vDst, 0, sizeof(vDst));
-    V_VT(&vDst) = VT_EMPTY;
+    /* skip for undefined types */
+    if ((vt == 15) || (vt > VT_VERSIONED_STREAM && vt < VT_BSTR_BLOB))
+        continue;
 
-    if (vt == VT_I8 || vt == VT_UI8)
+    switch (vt)
     {
+    case VT_I8:
+    case VT_UI8:
       if (has_i8)
         hExpected = S_OK;
-    }
-    else if (vt == VT_RECORD)
-    {
-      hExpected = DISP_E_TYPEMISMATCH;
-    }
-    else if (vt == VT_VARIANT || vt == VT_DISPATCH ||
-              vt == VT_UNKNOWN || vt == VT_ERROR)
-    {
+      else
+        hExpected = DISP_E_BADVARTYPE;
+      break;
+    case VT_RECORD:
+    case VT_VARIANT:
+    case VT_DISPATCH:
+    case VT_UNKNOWN:
+    case VT_ERROR:
       hExpected = DISP_E_TYPEMISMATCH;
-    }
-    else if (vt <= VT_UINT && vt != (VARTYPE)15)
+      break;
+    case VT_EMPTY:
+    case VT_NULL:
+    case VT_I2:
+    case VT_I4:
+    case VT_R4:
+    case VT_R8:
+    case VT_CY:
+    case VT_DATE:
+    case VT_BSTR:
+    case VT_BOOL:
+    case VT_DECIMAL:
+    case VT_I1:
+    case VT_UI1:
+    case VT_UI2:
+    case VT_UI4:
+    case VT_INT:
+    case VT_UINT:
       hExpected = S_OK;
+      break;
+    default:
+      hExpected = DISP_E_BADVARTYPE;
+    }
 
-    hres = VariantChangeTypeEx(&vDst, &vSrc, lcid, 0, vt);
+    VariantInit(&vSrc);
+    V_VT(&vSrc) = VT_EMPTY;
+    memset(&vDst, 0, sizeof(vDst));
+    V_VT(&vDst) = VT_NULL;
 
-    ok(hres == hExpected && (hres != S_OK || V_VT(&vDst) == vt),
-       "change empty: vt %d expected 0x%08x, got 0x%08x, vt %d\n",
-       vt, hExpected, hres, V_VT(&vDst));
-    if(hres == S_OK) VariantClear(&vDst);
+    hres = VariantChangeTypeEx(&vDst, &vSrc, lcid, 0, vt);
+    ok(hres == hExpected, "change empty: vt %d expected 0x%08x, got 0x%08x, vt %d\n",
+        vt, hExpected, hres, V_VT(&vDst));
+    if (hres == S_OK)
+    {
+        ok(V_VT(&vDst) == vt, "change empty: vt %d, got %d\n", vt, V_VT(&vDst));
+        VariantClear(&vDst);
+    }
   }
 }
 
 /* VT_NULL */
 static void test_NullChangeTypeEx(void)
 {
-  HRESULT hres;
-  VARIANTARG vSrc, vDst;
   VARTYPE vt;
   LCID lcid;
 
   lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
 
-  for (vt = 0; vt <= VT_BSTR_BLOB; vt++)
+  for (vt = VT_EMPTY; vt <= VT_BSTR_BLOB; vt++)
   {
-    HRESULT hExpected = DISP_E_BADVARTYPE;
+    VARIANTARG vSrc, vDst;
+    HRESULT hExpected, hres;
 
-    VariantInit(&vSrc);
-    V_VT(&vSrc) = VT_NULL;
-    memset(&vDst, 0, sizeof(vDst));
-    V_VT(&vDst) = VT_EMPTY;
+    /* skip for undefined types */
+    if ((vt == 15) || (vt > VT_VERSIONED_STREAM && vt < VT_BSTR_BLOB))
+        continue;
 
-    if (vt == VT_I8 || vt == VT_UI8)
+    switch (vt)
     {
-      if (has_i8)
+    case VT_I8:
+    case VT_UI8:
+        if (has_i8)
+            hExpected = DISP_E_TYPEMISMATCH;
+        else
+            hExpected = DISP_E_BADVARTYPE;
+        break;
+    case VT_NULL:
+        hExpected = S_OK;
+        break;
+    case VT_EMPTY:
+    case VT_I2:
+    case VT_I4:
+    case VT_R4:
+    case VT_R8:
+    case VT_CY:
+    case VT_DATE:
+    case VT_BSTR:
+    case VT_DISPATCH:
+    case VT_ERROR:
+    case VT_BOOL:
+    case VT_VARIANT:
+    case VT_UNKNOWN:
+    case VT_DECIMAL:
+    case VT_I1:
+    case VT_UI1:
+    case VT_UI2:
+    case VT_UI4:
+    case VT_INT:
+    case VT_UINT:
+    case VT_RECORD:
         hExpected = DISP_E_TYPEMISMATCH;
+        break;
+    default:
+        hExpected = DISP_E_BADVARTYPE;
     }
-    else if (vt == VT_RECORD)
-    {
-      hExpected = DISP_E_TYPEMISMATCH;
-    }
-    else if (vt == VT_NULL)
-    {
-      hExpected = S_OK;
-    }
-    else if (vt == VT_VARIANT || vt == VT_DISPATCH ||
-              vt == VT_UNKNOWN || vt == VT_ERROR ||
-              (vt <= VT_UINT && vt != (VARTYPE)15))
-      hExpected = DISP_E_TYPEMISMATCH;
 
-    hres = VariantChangeTypeEx(&vDst, &vSrc, lcid, 0, vt);
+    VariantInit(&vSrc);
+    V_VT(&vSrc) = VT_NULL;
+    memset(&vDst, 0, sizeof(vDst));
+    V_VT(&vDst) = VT_EMPTY;
 
-    ok(hres == hExpected && (hres != S_OK || V_VT(&vDst) == vt),
-       "change null: vt %d expected 0x%08x, got 0x%08x, vt %d\n",
+    hres = VariantChangeTypeEx(&vDst, &vSrc, lcid, 0, vt);
+    ok(hres == hExpected, "change null: vt %d expected 0x%08x, got 0x%08x, vt %d\n",
        vt, hExpected, hres, V_VT(&vDst));
+
+    /* should work only for VT_NULL -> VT_NULL case */
+    if (hres == S_OK)
+        ok(V_VT(&vDst) == VT_NULL, "change null: VT_NULL expected 0x%08x, got 0x%08x, vt %d\n",
+            hExpected, hres, V_VT(&vDst));
+    else
+        ok(V_VT(&vDst) == VT_EMPTY, "change null: vt %d expected 0x%08x, got 0x%08x, vt %d\n",
+            vt, hExpected, hres, V_VT(&vDst));
   }
 }
 
@@ -6090,6 +6318,129 @@ static void test_bstr_cache(void)
     SysFreeString(str2);
 }
 
+static void write_typelib(int res_no, const char *filename)
+{
+    DWORD written;
+    HANDLE file;
+    HRSRC res;
+    void *ptr;
+
+    file = CreateFileA( filename, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0 );
+    ok( file != INVALID_HANDLE_VALUE, "file creation failed\n" );
+    if (file == INVALID_HANDLE_VALUE) return;
+    res = FindResourceA( GetModuleHandleA(NULL), (LPCSTR)MAKEINTRESOURCE(res_no), "TYPELIB" );
+    ok( res != 0, "couldn't find resource\n" );
+    ptr = LockResource( LoadResource( GetModuleHandleA(NULL), res ));
+    WriteFile( file, ptr, SizeofResource( GetModuleHandleA(NULL), res ), &written, NULL );
+    ok( written == SizeofResource( GetModuleHandleA(NULL), res ), "couldn't write resource\n" );
+    CloseHandle( file );
+}
+
+static const char *create_test_typelib(int res_no)
+{
+    static char filename[MAX_PATH];
+
+    GetTempFileNameA( ".", "tlb", 0, filename );
+    write_typelib(res_no, filename);
+    return filename;
+}
+
+static void test_recinfo(void)
+{
+    static const WCHAR testW[] = {'t','e','s','t',0};
+    static WCHAR teststructW[] = {'t','e','s','t','_','s','t','r','u','c','t',0};
+    struct test_struct teststruct, testcopy;
+    WCHAR filenameW[MAX_PATH];
+    const char *filename;
+    IRecordInfo *recinfo;
+    ITypeInfo *typeinfo;
+    DummyDispatch dispatch;
+    ITypeLib *typelib;
+    TYPEATTR *attr;
+    MEMBERID memid;
+    UINT16 found;
+    HRESULT hr;
+    ULONG size;
+
+    filename = create_test_typelib(2);
+    MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, MAX_PATH);
+    hr = LoadTypeLibEx(filenameW, REGKIND_NONE, &typelib);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    typeinfo = NULL;
+    found = 1;
+    hr = ITypeLib_FindName(typelib, teststructW, 0, &typeinfo, &memid, &found);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(typeinfo != NULL, "got %p\n", typeinfo);
+    hr = ITypeInfo_GetTypeAttr(typeinfo, &attr);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(IsEqualGUID(&attr->guid, &UUID_test_struct), "got %s\n", wine_dbgstr_guid(&attr->guid));
+    ok(attr->typekind == TKIND_RECORD, "got %d\n", attr->typekind);
+
+    hr = GetRecordInfoFromTypeInfo(typeinfo, &recinfo);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    size = 0;
+    hr = IRecordInfo_GetSize(recinfo, &size);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(size == sizeof(struct test_struct), "got size %d\n", size);
+    ok(attr->cbSizeInstance == sizeof(struct test_struct), "got instance size %d\n", attr->cbSizeInstance);
+    ITypeInfo_ReleaseTypeAttr(typeinfo, attr);
+
+    /* RecordInit() */
+    teststruct.hr = E_FAIL;
+    teststruct.b = 0x1;
+    teststruct.disp = (void*)0xdeadbeef;
+    teststruct.bstr = (void*)0xdeadbeef;
+
+    hr = IRecordInfo_RecordInit(recinfo, &teststruct);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(teststruct.hr == 0, "got 0x%08x\n", teststruct.hr);
+    ok(teststruct.b == 0, "got 0x%08x\n", teststruct.b);
+    ok(teststruct.disp == NULL, "got %p\n", teststruct.disp);
+    ok(teststruct.bstr == NULL, "got %p\n", teststruct.bstr);
+
+    init_test_dispatch(10, VT_UI1, &dispatch);
+
+    /* RecordCopy(), interface field reference increased */
+    teststruct.hr = S_FALSE;
+    teststruct.b = VARIANT_TRUE;
+    teststruct.disp = &dispatch.IDispatch_iface;
+    teststruct.bstr = SysAllocString(testW);
+    memset(&testcopy, 0, sizeof(testcopy));
+    hr = IRecordInfo_RecordCopy(recinfo, &teststruct, &testcopy);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(testcopy.hr == S_FALSE, "got 0x%08x\n", testcopy.hr);
+    ok(testcopy.b == VARIANT_TRUE, "got %d\n", testcopy.b);
+    ok(testcopy.disp == teststruct.disp, "got %p\n", testcopy.disp);
+    ok(dispatch.ref == 11, "got %d\n", dispatch.ref);
+    ok(testcopy.bstr != teststruct.bstr, "got %p\n", testcopy.bstr);
+    ok(!lstrcmpW(testcopy.bstr, teststruct.bstr), "got %s, %s\n", wine_dbgstr_w(testcopy.bstr), wine_dbgstr_w(teststruct.bstr));
+
+    /* RecordClear() */
+    hr = IRecordInfo_RecordClear(recinfo, &teststruct);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(teststruct.bstr == NULL, "got %p\n", teststruct.bstr);
+    hr = IRecordInfo_RecordClear(recinfo, &testcopy);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(testcopy.bstr == NULL, "got %p\n", testcopy.bstr);
+
+    /* now destination contains inteface pointer */
+    memset(&testcopy, 0, sizeof(testcopy));
+    testcopy.disp = &dispatch.IDispatch_iface;
+    dispatch.ref = 10;
+
+    hr = IRecordInfo_RecordCopy(recinfo, &teststruct, &testcopy);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(dispatch.ref == 9, "got %d\n", dispatch.ref);
+
+    IRecordInfo_Release(recinfo);
+
+    ITypeInfo_Release(typeinfo);
+    ITypeLib_Release(typelib);
+    DeleteFileA(filename);
+}
+
 START_TEST(vartype)
 {
   hOleaut32 = GetModuleHandleA("oleaut32.dll");
@@ -6340,6 +6691,7 @@ START_TEST(vartype)
   test_VarDecCmpR8();
   test_VarDecMul();
   test_VarDecDiv();
+  test_VarDecRound();
 
   test_VarBoolFromI1();
   test_VarBoolFromUI1();
@@ -6390,4 +6742,6 @@ START_TEST(vartype)
 
   test_NullByRef();
   test_ChangeType_keep_dst();
+
+  test_recinfo();
 }