[URLMON_WINETEST] Sync with Wine 3.0. CORE-14225
[reactos.git] / modules / rostests / winetests / urlmon / misc.c
index 299dcdf..875334d 100644 (file)
@@ -114,6 +114,21 @@ static WCHAR *a2w(const char *str)
     return ret;
 }
 
+static WCHAR *a2co(const char *str)
+{
+    WCHAR *ret;
+    int len;
+
+    if(!str)
+        return NULL;
+
+    len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
+    ret = CoTaskMemAlloc(len*sizeof(WCHAR));
+    MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
+
+    return ret;
+}
+
 static void test_CreateFormatEnum(void)
 {
     IEnumFORMATETC *fenum = NULL, *fenum2 = NULL;
@@ -1930,6 +1945,713 @@ static void test_internet_features(void) {
     test_CoInternetSetFeatureEnabled();
 }
 
+static BINDINFO rem_bindinfo = { sizeof(rem_bindinfo) }, in_bindinfo;
+static DWORD rem_bindf;
+
+static HRESULT WINAPI BindStatusCallback_QueryInterface(IBindStatusCallbackEx *iface, REFIID riid, void **ppv)
+{
+    if(IsEqualGUID(&IID_IBindStatusCallbackEx, riid)
+       || IsEqualGUID(&IID_IBindStatusCallback, riid)
+       || IsEqualGUID(&IID_IUnknown, riid)) {
+        *ppv = iface;
+        return S_OK;
+    }
+
+    *ppv = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI BindStatusCallback_AddRef(IBindStatusCallbackEx *iface)
+{
+    return 2;
+}
+
+static ULONG WINAPI BindStatusCallback_Release(IBindStatusCallbackEx *iface)
+{
+    return 1;
+}
+
+static HRESULT WINAPI BindStatusCallback_OnStartBinding(IBindStatusCallbackEx *iface,
+        DWORD dwReserved, IBinding *pib)
+{
+    ok(0, "unexpected call\n");
+    return S_OK;
+}
+
+static HRESULT WINAPI BindStatusCallback_GetPriority(IBindStatusCallbackEx *iface, LONG *pnPriority)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI BindStatusCallback_OnLowResource(IBindStatusCallbackEx *iface, DWORD reserved)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI BindStatusCallback_OnProgress(IBindStatusCallbackEx *iface, ULONG ulProgress,
+        ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
+{
+    ok(0, "unexpected call\n");
+    return S_OK;
+}
+
+static HRESULT WINAPI BindStatusCallback_OnStopBinding(IBindStatusCallbackEx *iface, HRESULT hresult, LPCWSTR szError)
+{
+    ok(0, "unexpected call\n");
+    return S_OK;
+}
+
+static HRESULT WINAPI BindStatusCallback_GetBindInfo(IBindStatusCallbackEx *iface, DWORD *grfBINDF, BINDINFO *pbindinfo)
+{
+    in_bindinfo = *pbindinfo;
+    *grfBINDF = rem_bindf;
+    *pbindinfo = rem_bindinfo;
+    return S_OK;
+}
+
+static STGMEDIUM in_stgmed, rem_stgmed;
+
+static HRESULT WINAPI BindStatusCallback_OnDataAvailable(IBindStatusCallbackEx *iface, DWORD grfBSCF,
+        DWORD dwSize, FORMATETC* pformatetc, STGMEDIUM* pstgmed)
+{
+    in_stgmed = *pstgmed;
+    *pstgmed = rem_stgmed;
+    return S_OK;
+}
+
+static HRESULT WINAPI BindStatusCallback_OnObjectAvailable(IBindStatusCallbackEx *iface, REFIID riid, IUnknown *punk)
+{
+    ok(0, "unexpected call\n");
+    return S_OK;
+}
+
+static HRESULT WINAPI BindStatusCallbackEx_GetBindInfoEx(IBindStatusCallbackEx *iface, DWORD *grfBINDF,
+        BINDINFO *pbindinfo, DWORD *grfBINDF2, DWORD *pdwReserved)
+{
+    in_bindinfo = *pbindinfo;
+    *grfBINDF = rem_bindf;
+    *pbindinfo = rem_bindinfo;
+    *grfBINDF2 = 11;
+    *pdwReserved = 12;
+    return S_OK;
+}
+
+static const IBindStatusCallbackExVtbl BindStatusCallbackExVtbl = {
+    BindStatusCallback_QueryInterface,
+    BindStatusCallback_AddRef,
+    BindStatusCallback_Release,
+    BindStatusCallback_OnStartBinding,
+    BindStatusCallback_GetPriority,
+    BindStatusCallback_OnLowResource,
+    BindStatusCallback_OnProgress,
+    BindStatusCallback_OnStopBinding,
+    BindStatusCallback_GetBindInfo,
+    BindStatusCallback_OnDataAvailable,
+    BindStatusCallback_OnObjectAvailable,
+    BindStatusCallbackEx_GetBindInfoEx
+};
+
+static IBindStatusCallbackEx BindStatusCallback = { &BindStatusCallbackExVtbl };
+
+typedef struct {
+    IUnknown IUnknown_iface;
+    LONG ref;
+} RefUnk;
+
+static inline RefUnk *impl_from_IUnknown(IUnknown *iface)
+{
+    return CONTAINING_RECORD(iface, RefUnk, IUnknown_iface);
+}
+
+static HRESULT WINAPI RefUnk_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
+{
+    if(!IsEqualGUID(&IID_IUnknown, riid)) {
+        *ppv = NULL;
+        return E_NOINTERFACE;
+    }
+
+    IUnknown_AddRef(iface);
+    *ppv = iface;
+    return S_OK;
+}
+
+static ULONG WINAPI RefUnk_AddRef(IUnknown *iface)
+{
+    RefUnk *This = impl_from_IUnknown(iface);
+    return InterlockedIncrement(&This->ref);
+}
+
+static ULONG WINAPI RefUnk_Release(IUnknown *iface)
+{
+    RefUnk *This = impl_from_IUnknown(iface);
+    return InterlockedDecrement(&This->ref);
+}
+
+static const IUnknownVtbl RefUnkVtbl = {
+    RefUnk_QueryInterface,
+    RefUnk_AddRef,
+    RefUnk_Release
+};
+
+static RefUnk unk_in = {{&RefUnkVtbl}}, unk_out = {{&RefUnkVtbl}};
+
+static HANDLE thread_ready;
+
+static DWORD WINAPI bsc_thread(void *arg)
+{
+    IStream *stream = arg;
+    LARGE_INTEGER zero;
+    MSG msg;
+    HRESULT hres;
+
+    CoInitialize(NULL);
+
+    hres = CoMarshalInterface(stream, &IID_IBindStatusCallback, (IUnknown*)&BindStatusCallback,
+                              MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
+    ok(hres == S_OK, "CoMarshalInterface failed: %08x\n", hres);
+
+    zero.QuadPart = 0;
+    hres = IStream_Seek(stream, zero, STREAM_SEEK_SET, NULL);
+    ok(hres == S_OK, "Seek failed: 0x%08x\n", hres);
+
+    SetEvent(thread_ready);
+
+    while(GetMessageW(&msg, NULL, 0, 0)) {
+        TranslateMessage(&msg);
+        DispatchMessageW(&msg);
+    }
+
+    CoUninitialize();
+    return 0;
+}
+
+static void test_bsc_marshaling(void)
+{
+    FORMATETC formatetc = {0, NULL, 1, -1, TYMED_ISTREAM};
+    IBindStatusCallbackEx *callbackex;
+    IBindStatusCallback *bsc;
+    BINDINFO bindinfo;
+    IStream *stream, *binding_stream;
+    HANDLE thread;
+    WCHAR *extra_info_out;
+    WCHAR *verb_out;
+    LARGE_INTEGER zero;
+    STGMEDIUM stgmed;
+    void *buf;
+    DWORD bindf;
+    HRESULT hres;
+
+    hres = CreateStreamOnHGlobal(NULL, TRUE, &stream);
+    ok(hres == S_OK, "CreateStreamOnHGlobal returned: %08x\n", hres);
+
+    thread_ready = CreateEventW(NULL, TRUE, FALSE, NULL);
+    thread = CreateThread(NULL, 0, bsc_thread, stream, 0, NULL);
+    WaitForSingleObject(thread_ready, INFINITE);
+
+    hres = CoUnmarshalInterface(stream, &IID_IBindStatusCallback, (void**)&bsc);
+    ok(hres == S_OK, "CoUnmarshalInterface failed: %08x\n", hres);
+
+    hres = CreateStreamOnHGlobal(NULL, TRUE, &binding_stream);
+    ok(hres == S_OK, "CreateStreamOnHGlobal returned: %08x\n", hres);
+    hres = IStream_Write(binding_stream, "xxx", 3, NULL);
+    ok(hres == S_OK, "Write failed: %08x\n", hres);
+
+    rem_bindf = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA;
+    bindf = 0xdeadbeef;
+
+    memset(&bindinfo, 0, sizeof(bindinfo));
+    bindinfo.cbSize = sizeof(bindinfo);
+    bindinfo.grfBindInfoF = 12;
+    bindinfo.dwBindVerb = 13;
+    bindinfo.cbstgmedData = 19;
+    bindinfo.dwOptions = 14;
+    bindinfo.dwOptionsFlags = 15;
+    bindinfo.dwCodePage = 16;
+    bindinfo.securityAttributes.nLength = 30;
+    bindinfo.securityAttributes.lpSecurityDescriptor = (void*)0xdead0001;
+    bindinfo.securityAttributes.bInheritHandle = 31;
+    bindinfo.iid.Data1 = 17;
+    bindinfo.pUnk = (IUnknown*)0xdeadbeef;
+    bindinfo.dwReserved = 18;
+    bindinfo.stgmedData.pUnkForRelease = &unk_in.IUnknown_iface;
+    unk_in.ref = 1;
+
+    memset(&rem_bindinfo, 0, sizeof(bindinfo));
+    rem_bindinfo.cbSize = sizeof(rem_bindinfo);
+    rem_bindinfo.szExtraInfo = extra_info_out = a2co("extra info out");
+    rem_bindinfo.grfBindInfoF = 22;
+    rem_bindinfo.dwBindVerb = 23;
+    rem_bindinfo.szCustomVerb = verb_out = a2co("custom verb out");
+    rem_bindinfo.cbstgmedData = 29;
+    rem_bindinfo.dwOptions = 24;
+    rem_bindinfo.dwOptionsFlags = 25;
+    rem_bindinfo.dwCodePage = 16;
+    rem_bindinfo.securityAttributes.nLength = 40;
+    rem_bindinfo.securityAttributes.lpSecurityDescriptor = (void*)0xdead0002;
+    rem_bindinfo.securityAttributes.bInheritHandle = 41;
+    rem_bindinfo.iid.Data1 = 27;
+    rem_bindinfo.pUnk = (IUnknown*)0xdeadbeef;
+    rem_bindinfo.dwReserved = 18;
+    rem_bindinfo.stgmedData.pUnkForRelease = &unk_out.IUnknown_iface;
+    unk_out.ref = 1;
+
+    hres = IBindStatusCallback_GetBindInfo(bsc, &bindf, &bindinfo);
+    ok(hres == S_OK, "GetBindInfo failed: %08x\n", hres);
+    ok(bindf == rem_bindf, "bindf = %x, expected %x\n", bindf, rem_bindf);
+
+    ok(in_bindinfo.cbSize == sizeof(in_bindinfo), "cbSize = %u\n", in_bindinfo.cbSize);
+    ok(!in_bindinfo.szExtraInfo, "szExtraInfo = %s\n", wine_dbgstr_w(in_bindinfo.szExtraInfo));
+    ok(in_bindinfo.grfBindInfoF == 12, "cbSize = %u\n", in_bindinfo.grfBindInfoF);
+    ok(in_bindinfo.dwBindVerb == 13, "dwBindVerb = %u\n", in_bindinfo.dwBindVerb);
+    ok(!in_bindinfo.szCustomVerb, "szCustomVerb = %s\n", wine_dbgstr_w(in_bindinfo.szCustomVerb));
+    ok(in_bindinfo.cbstgmedData == 19, "cbstgmedData = %u\n", in_bindinfo.cbstgmedData);
+    ok(!in_bindinfo.dwOptions, "dwOptions = %u\n", in_bindinfo.dwOptions);
+    ok(!in_bindinfo.dwOptionsFlags, "dwOptionsFlags = %u\n", in_bindinfo.dwOptionsFlags);
+    ok(!in_bindinfo.dwCodePage, "dwCodePage = %u\n", in_bindinfo.dwCodePage);
+    ok(!in_bindinfo.iid.Data1, "iid.Data1 = %u\n", in_bindinfo.iid.Data1);
+    ok(!in_bindinfo.pUnk, "pUnk = %p\n", in_bindinfo.pUnk);
+    ok(!in_bindinfo.dwReserved, "dwReserved = %u\n", in_bindinfo.dwReserved);
+    ok(!in_bindinfo.securityAttributes.nLength, "securityAttributes.nLength = %u\n",
+       in_bindinfo.securityAttributes.nLength);
+    ok(!in_bindinfo.securityAttributes.lpSecurityDescriptor,
+       "securityAttributes.lpSecurityDescriptor = %p\n",
+       in_bindinfo.securityAttributes.lpSecurityDescriptor);
+    ok(!in_bindinfo.securityAttributes.bInheritHandle, "securityAttributes.bInheritHandle = %u\n",
+       in_bindinfo.securityAttributes.bInheritHandle);
+    ok(!in_bindinfo.stgmedData.pUnkForRelease, "pUnkForRelease = %p\n",
+       in_bindinfo.stgmedData.pUnkForRelease);
+
+    ok(bindinfo.cbSize == sizeof(rem_bindinfo), "cbSize = %u\n", rem_bindinfo.cbSize);
+    ok(!strcmp_wa(bindinfo.szExtraInfo, "extra info out"),
+       "szExtraInfo = %s\n", wine_dbgstr_w(bindinfo.szExtraInfo));
+    ok(bindinfo.grfBindInfoF == 22, "grfBindInfoF = %u\n", rem_bindinfo.grfBindInfoF);
+    ok(bindinfo.dwBindVerb == 23, "dwBindVerb = %u\n", bindinfo.dwBindVerb);
+    ok(bindinfo.szCustomVerb != verb_out, "szCustomVerb == inbuf\n");
+    ok(!strcmp_wa(bindinfo.szCustomVerb, "custom verb out"), "szCustomVerb = %s\n",
+       wine_dbgstr_w(bindinfo.szCustomVerb));
+    ok(bindinfo.cbstgmedData == 29, "cbstgmedData = %u\n", bindinfo.cbstgmedData);
+    ok(bindinfo.dwOptions == 24, "dwOptions = %u\n", bindinfo.dwOptions);
+    ok(bindinfo.dwOptionsFlags == 25, "dwOptionsFlags = %u\n", bindinfo.dwOptionsFlags);
+    ok(bindinfo.dwCodePage, "dwCodePage = %u\n", bindinfo.dwCodePage);
+    ok(!bindinfo.iid.Data1, "iid.Data1 = %u\n", bindinfo.iid.Data1);
+    ok(!bindinfo.pUnk, "pUnk = %p\n", bindinfo.pUnk);
+    ok(bindinfo.dwReserved == 18, "dwReserved = %u\n", bindinfo.dwReserved);
+    ok(bindinfo.securityAttributes.nLength == 30, "securityAttributes.nLength = %u\n",
+       bindinfo.securityAttributes.nLength);
+    ok(bindinfo.securityAttributes.lpSecurityDescriptor == (void*)0xdead0001,
+       "securityAttributes.lpSecurityDescriptor = %p\n",
+       bindinfo.securityAttributes.lpSecurityDescriptor);
+    ok(bindinfo.securityAttributes.bInheritHandle == 31, "securityAttributes.bInheritHandle = %u\n",
+       bindinfo.securityAttributes.bInheritHandle);
+    ok(bindinfo.stgmedData.pUnkForRelease == &unk_in.IUnknown_iface, "pUnkForRelease = %p\n",
+       bindinfo.stgmedData.pUnkForRelease);
+    ok(unk_out.ref == 1, "unk_out.ref = %u\n", unk_out.ref);
+
+    bindinfo.stgmedData.pUnkForRelease = NULL;
+    ReleaseBindInfo(&bindinfo);
+
+    zero.QuadPart = 0;
+    hres = IStream_Seek(binding_stream, zero, STREAM_SEEK_SET, NULL);
+    ok(hres == S_OK, "Seek failed: 0x%08x\n", hres);
+
+    /* Return IStream stgmed from GetBindInfo, it's not marshaled back */
+    rem_bindf = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA;
+    bindf = 0xdeadbeef;
+
+    memset(&bindinfo, 0, sizeof(bindinfo));
+    bindinfo.cbSize = sizeof(bindinfo);
+
+    memset(&rem_bindinfo, 0, sizeof(rem_bindinfo));
+    rem_bindinfo.cbSize = sizeof(rem_bindinfo);
+
+    rem_bindinfo.stgmedData.tymed = TYMED_ISTREAM;
+    rem_bindinfo.stgmedData.u.pstm = binding_stream;
+    rem_bindinfo.cbstgmedData = 3;
+    IStream_AddRef(binding_stream);
+
+    hres = IBindStatusCallback_GetBindInfo(bsc, &bindf, &bindinfo);
+    ok(hres == S_OK, "GetBindInfo failed: %08x\n", hres);
+    ok(bindf == rem_bindf, "bindf = %x, expected %x\n", bindf, rem_bindf);
+
+    ok(in_bindinfo.cbSize == sizeof(in_bindinfo), "cbSize = %u\n", in_bindinfo.cbSize);
+    ok(!in_bindinfo.pUnk, "pUnk = %p\n", in_bindinfo.pUnk);
+    ok(in_bindinfo.stgmedData.tymed == TYMED_NULL, "tymed = %u\n",
+       in_bindinfo.stgmedData.tymed);
+
+    ok(bindinfo.cbSize == sizeof(bindinfo), "cbSize = %u\n", bindinfo.cbSize);
+    ok(!bindinfo.pUnk, "pUnk = %p\n", bindinfo.pUnk);
+    ok(bindinfo.stgmedData.tymed == TYMED_NULL, "tymed = %u\n",
+       bindinfo.stgmedData.tymed);
+    ok(!bindinfo.stgmedData.u.pstm, "stm = %p\n",
+       bindinfo.stgmedData.u.pstm);
+    ok(!bindinfo.stgmedData.pUnkForRelease, "pUnkForRelease = %p\n",
+       bindinfo.stgmedData.pUnkForRelease);
+    ok(bindinfo.cbstgmedData == 3, "cbstgmedData = %u\n", bindinfo.cbstgmedData);
+
+    ReleaseBindInfo(&bindinfo);
+
+    /* Same, but with pUnkForRelease, it's not marshaled back */
+    rem_bindf = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA;
+    bindf = 0xdeadbeef;
+
+    memset(&bindinfo, 0, sizeof(bindinfo));
+    bindinfo.cbSize = sizeof(bindinfo);
+
+    memset(&rem_bindinfo, 0, sizeof(rem_bindinfo));
+    rem_bindinfo.cbSize = sizeof(rem_bindinfo);
+
+    rem_bindinfo.stgmedData.tymed = TYMED_ISTREAM;
+    rem_bindinfo.stgmedData.u.pstm = binding_stream;
+    rem_bindinfo.stgmedData.pUnkForRelease = &unk_out.IUnknown_iface;
+    unk_out.ref = 1;
+    rem_bindinfo.cbstgmedData = 3;
+    IStream_AddRef(binding_stream);
+
+    hres = IBindStatusCallback_GetBindInfo(bsc, &bindf, &bindinfo);
+    ok(hres == S_OK, "GetBindInfo failed: %08x\n", hres);
+    ok(bindf == rem_bindf, "bindf = %x, expected %x\n", bindf, rem_bindf);
+
+    ok(in_bindinfo.cbSize == sizeof(in_bindinfo), "cbSize = %u\n", in_bindinfo.cbSize);
+    ok(!in_bindinfo.pUnk, "pUnk = %p\n", in_bindinfo.pUnk);
+    ok(in_bindinfo.stgmedData.tymed == TYMED_NULL, "tymed = %u\n",
+       in_bindinfo.stgmedData.tymed);
+
+    ok(bindinfo.cbSize == sizeof(bindinfo), "cbSize = %u\n", bindinfo.cbSize);
+    ok(!bindinfo.pUnk, "pUnk = %p\n", bindinfo.pUnk);
+    ok(bindinfo.stgmedData.tymed == TYMED_NULL, "tymed = %u\n",
+       bindinfo.stgmedData.tymed);
+    ok(!bindinfo.stgmedData.u.pstm, "stm = %p\n",
+       bindinfo.stgmedData.u.pstm);
+    ok(!bindinfo.stgmedData.pUnkForRelease, "pUnkForRelease = %p\n",
+       bindinfo.stgmedData.pUnkForRelease);
+    ok(bindinfo.cbstgmedData == 3, "cbstgmedData = %u\n", bindinfo.cbstgmedData);
+
+    ReleaseBindInfo(&bindinfo);
+
+    /* Return HGLOBAL stgmed from GetBindInfo, it's not marshaled back */
+    rem_bindf = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA;
+    bindf = 0xdeadbeef;
+
+    memset(&bindinfo, 0, sizeof(bindinfo));
+    bindinfo.cbSize = sizeof(bindinfo);
+
+    memset(&rem_bindinfo, 0, sizeof(rem_bindinfo));
+    rem_bindinfo.cbSize = sizeof(rem_bindinfo);
+
+    rem_bindinfo.stgmedData.tymed = TYMED_HGLOBAL;
+
+    buf = GlobalAlloc(0, sizeof(5));
+    strcpy(buf, "test");
+    rem_bindinfo.stgmedData.u.hGlobal = buf;
+    rem_bindinfo.cbstgmedData = 5;
+
+    hres = IBindStatusCallback_GetBindInfo(bsc, &bindf, &bindinfo);
+    ok(hres == S_OK, "GetBindInfo failed: %08x\n", hres);
+    ok(bindf == rem_bindf, "bindf = %x, expected %x\n", bindf, rem_bindf);
+
+    ok(in_bindinfo.cbSize == sizeof(in_bindinfo), "cbSize = %u\n", in_bindinfo.cbSize);
+    ok(!in_bindinfo.pUnk, "pUnk = %p\n", in_bindinfo.pUnk);
+    ok(in_bindinfo.stgmedData.tymed == TYMED_NULL, "tymed = %u\n",
+       in_bindinfo.stgmedData.tymed);
+
+    ok(bindinfo.cbSize == sizeof(bindinfo), "cbSize = %u\n", bindinfo.cbSize);
+    ok(!bindinfo.pUnk, "pUnk = %p\n", bindinfo.pUnk);
+    ok(bindinfo.stgmedData.tymed == TYMED_NULL, "tymed = %u\n",
+       bindinfo.stgmedData.tymed);
+    ok(!bindinfo.stgmedData.u.pstm, "stm = %p\n",
+       bindinfo.stgmedData.u.pstm);
+    ok(!bindinfo.stgmedData.pUnkForRelease, "pUnkForRelease = %p\n",
+       bindinfo.stgmedData.pUnkForRelease);
+    ok(bindinfo.cbstgmedData == 5, "cbstgmedData = %u\n", bindinfo.cbstgmedData);
+
+    ReleaseBindInfo(&bindinfo);
+
+    /* Same with GetBindInfoEx */
+    hres = IBindStatusCallback_QueryInterface(bsc, &IID_IBindStatusCallbackEx, (void**)&callbackex);
+    if(SUCCEEDED(hres)) {
+        DWORD bindf2, reserved;
+
+        rem_bindf = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA;
+        bindf = bindf2 = reserved = 0xdeadbeef;
+
+        memset(&bindinfo, 0, sizeof(bindinfo));
+        bindinfo.cbSize = sizeof(bindinfo);
+        bindinfo.grfBindInfoF = 12;
+        bindinfo.dwBindVerb = 13;
+        bindinfo.cbstgmedData = 19;
+        bindinfo.dwOptions = 14;
+        bindinfo.dwOptionsFlags = 15;
+        bindinfo.dwCodePage = 16;
+        bindinfo.securityAttributes.nLength = 30;
+        bindinfo.securityAttributes.lpSecurityDescriptor = (void*)0xdead0001;
+        bindinfo.securityAttributes.bInheritHandle = 31;
+        bindinfo.iid.Data1 = 17;
+        bindinfo.pUnk = (IUnknown*)0xdeadbeef;
+        bindinfo.dwReserved = 18;
+        bindinfo.stgmedData.pUnkForRelease = &unk_in.IUnknown_iface;
+        unk_in.ref = 1;
+
+        memset(&rem_bindinfo, 0, sizeof(bindinfo));
+        rem_bindinfo.cbSize = sizeof(rem_bindinfo);
+        rem_bindinfo.szExtraInfo = extra_info_out = a2co("extra info out");
+        rem_bindinfo.grfBindInfoF = 22;
+        rem_bindinfo.dwBindVerb = 23;
+        rem_bindinfo.szCustomVerb = verb_out = a2co("custom verb out");
+        rem_bindinfo.cbstgmedData = 29;
+        rem_bindinfo.dwOptions = 24;
+        rem_bindinfo.dwOptionsFlags = 25;
+        rem_bindinfo.dwCodePage = 16;
+        rem_bindinfo.securityAttributes.nLength = 40;
+        rem_bindinfo.securityAttributes.lpSecurityDescriptor = (void*)0xdead0002;
+        rem_bindinfo.securityAttributes.bInheritHandle = 41;
+        rem_bindinfo.iid.Data1 = 27;
+        rem_bindinfo.pUnk = (IUnknown*)0xdeadbeef;
+        rem_bindinfo.dwReserved = 18;
+        rem_bindinfo.stgmedData.pUnkForRelease = &unk_out.IUnknown_iface;
+        unk_out.ref = 1;
+
+        hres = IBindStatusCallbackEx_GetBindInfoEx(callbackex, &bindf, &bindinfo, &bindf2, &reserved);
+        ok(hres == S_OK, "GetBindInfo failed: %08x\n", hres);
+        ok(bindf == rem_bindf, "bindf = %x, expected %x\n", bindf, rem_bindf);
+        ok(bindf2 == 11, "bindf2 = %x\n", bindf);
+        ok(reserved == 12, "reserved = %x\n", reserved);
+
+        ok(in_bindinfo.cbSize == sizeof(in_bindinfo), "cbSize = %u\n", in_bindinfo.cbSize);
+        ok(!in_bindinfo.szExtraInfo, "szExtraInfo = %s\n", wine_dbgstr_w(in_bindinfo.szExtraInfo));
+        ok(in_bindinfo.grfBindInfoF == 12, "cbSize = %u\n", in_bindinfo.grfBindInfoF);
+        ok(in_bindinfo.dwBindVerb == 13, "dwBindVerb = %u\n", in_bindinfo.dwBindVerb);
+        ok(!in_bindinfo.szCustomVerb, "szCustomVerb = %s\n", wine_dbgstr_w(in_bindinfo.szCustomVerb));
+        ok(in_bindinfo.cbstgmedData == 19, "cbstgmedData = %u\n", in_bindinfo.cbstgmedData);
+        ok(!in_bindinfo.dwOptions, "dwOptions = %u\n", in_bindinfo.dwOptions);
+        ok(!in_bindinfo.dwOptionsFlags, "dwOptionsFlags = %u\n", in_bindinfo.dwOptionsFlags);
+        ok(!in_bindinfo.dwCodePage, "dwCodePage = %u\n", in_bindinfo.dwCodePage);
+        ok(!in_bindinfo.iid.Data1, "iid.Data1 = %u\n", in_bindinfo.iid.Data1);
+        ok(!in_bindinfo.pUnk, "pUnk = %p\n", in_bindinfo.pUnk);
+        ok(!in_bindinfo.dwReserved, "dwReserved = %u\n", in_bindinfo.dwReserved);
+        ok(!in_bindinfo.securityAttributes.nLength, "securityAttributes.nLength = %u\n",
+           in_bindinfo.securityAttributes.nLength);
+        ok(!in_bindinfo.securityAttributes.lpSecurityDescriptor,
+           "securityAttributes.lpSecurityDescriptor = %p\n",
+           in_bindinfo.securityAttributes.lpSecurityDescriptor);
+        ok(!in_bindinfo.securityAttributes.bInheritHandle, "securityAttributes.bInheritHandle = %u\n",
+           in_bindinfo.securityAttributes.bInheritHandle);
+        ok(!in_bindinfo.stgmedData.pUnkForRelease, "pUnkForRelease = %p\n",
+           in_bindinfo.stgmedData.pUnkForRelease);
+
+        ok(bindinfo.cbSize == sizeof(rem_bindinfo), "cbSize = %u\n", rem_bindinfo.cbSize);
+        ok(!strcmp_wa(bindinfo.szExtraInfo, "extra info out"),
+           "szExtraInfo = %s\n", wine_dbgstr_w(bindinfo.szExtraInfo));
+        ok(bindinfo.grfBindInfoF == 22, "grfBindInfoF = %u\n", rem_bindinfo.grfBindInfoF);
+        ok(bindinfo.dwBindVerb == 23, "dwBindVerb = %u\n", bindinfo.dwBindVerb);
+        ok(bindinfo.szCustomVerb != verb_out, "szCustomVerb == inbuf\n");
+        ok(!strcmp_wa(bindinfo.szCustomVerb, "custom verb out"), "szCustomVerb = %s\n",
+           wine_dbgstr_w(bindinfo.szCustomVerb));
+        ok(bindinfo.cbstgmedData == 29, "cbstgmedData = %u\n", bindinfo.cbstgmedData);
+        ok(bindinfo.dwOptions == 24, "dwOptions = %u\n", bindinfo.dwOptions);
+        ok(bindinfo.dwOptionsFlags == 25, "dwOptionsFlags = %u\n", bindinfo.dwOptionsFlags);
+        ok(bindinfo.dwCodePage, "dwCodePage = %u\n", bindinfo.dwCodePage);
+        ok(!bindinfo.iid.Data1, "iid.Data1 = %u\n", bindinfo.iid.Data1);
+        ok(!bindinfo.pUnk, "pUnk = %p\n", bindinfo.pUnk);
+        ok(bindinfo.dwReserved == 18, "dwReserved = %u\n", bindinfo.dwReserved);
+        ok(bindinfo.securityAttributes.nLength == 30, "securityAttributes.nLength = %u\n",
+           bindinfo.securityAttributes.nLength);
+        ok(bindinfo.securityAttributes.lpSecurityDescriptor == (void*)0xdead0001,
+           "securityAttributes.lpSecurityDescriptor = %p\n",
+           bindinfo.securityAttributes.lpSecurityDescriptor);
+        ok(bindinfo.securityAttributes.bInheritHandle == 31, "securityAttributes.bInheritHandle = %u\n",
+           bindinfo.securityAttributes.bInheritHandle);
+        ok(bindinfo.stgmedData.pUnkForRelease == &unk_in.IUnknown_iface, "pUnkForRelease = %p\n",
+           bindinfo.stgmedData.pUnkForRelease);
+        ok(unk_out.ref == 1, "unk_out.ref = %u\n", unk_out.ref);
+
+        bindinfo.stgmedData.pUnkForRelease = NULL;
+        ReleaseBindInfo(&bindinfo);
+
+        zero.QuadPart = 0;
+        hres = IStream_Seek(binding_stream, zero, STREAM_SEEK_SET, NULL);
+        ok(hres == S_OK, "Seek failed: 0x%08x\n", hres);
+
+        /* Return IStream stgmed from GetBindInfoEx, it's not marshaled back */
+        rem_bindf = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA;
+        bindf = bindf2 = reserved = 0xdeadbeef;
+
+        memset(&bindinfo, 0, sizeof(bindinfo));
+        bindinfo.cbSize = sizeof(bindinfo);
+
+        memset(&rem_bindinfo, 0, sizeof(rem_bindinfo));
+        rem_bindinfo.cbSize = sizeof(rem_bindinfo);
+
+        rem_bindinfo.stgmedData.tymed = TYMED_ISTREAM;
+        rem_bindinfo.stgmedData.u.pstm = binding_stream;
+        rem_bindinfo.cbstgmedData = 3;
+        IStream_AddRef(binding_stream);
+
+        hres = IBindStatusCallbackEx_GetBindInfoEx(callbackex, &bindf, &bindinfo, &bindf2, &reserved);
+        ok(hres == S_OK, "GetBindInfoEx failed: %08x\n", hres);
+        ok(bindf == rem_bindf, "bindf = %x, expected %x\n", bindf, rem_bindf);
+
+        ok(in_bindinfo.cbSize == sizeof(in_bindinfo), "cbSize = %u\n", in_bindinfo.cbSize);
+        ok(!in_bindinfo.pUnk, "pUnk = %p\n", in_bindinfo.pUnk);
+        ok(in_bindinfo.stgmedData.tymed == TYMED_NULL, "tymed = %u\n",
+           in_bindinfo.stgmedData.tymed);
+
+        ok(bindinfo.cbSize == sizeof(bindinfo), "cbSize = %u\n", bindinfo.cbSize);
+        ok(!bindinfo.pUnk, "pUnk = %p\n", bindinfo.pUnk);
+        ok(bindinfo.stgmedData.tymed == TYMED_NULL, "tymed = %u\n",
+           bindinfo.stgmedData.tymed);
+        ok(!bindinfo.stgmedData.u.pstm, "stm = %p\n",
+           bindinfo.stgmedData.u.pstm);
+        ok(!bindinfo.stgmedData.pUnkForRelease, "pUnkForRelease = %p\n",
+           bindinfo.stgmedData.pUnkForRelease);
+        ok(bindinfo.cbstgmedData == 3, "cbstgmedData = %u\n", bindinfo.cbstgmedData);
+
+        ReleaseBindInfo(&bindinfo);
+
+        /* Same, but with pUnkForRelease, it's not marshaled back */
+        rem_bindf = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA;
+        bindf = bindf2 = reserved = 0xdeadbeef;
+
+        memset(&bindinfo, 0, sizeof(bindinfo));
+        bindinfo.cbSize = sizeof(bindinfo);
+
+        memset(&rem_bindinfo, 0, sizeof(rem_bindinfo));
+        rem_bindinfo.cbSize = sizeof(rem_bindinfo);
+
+        rem_bindinfo.stgmedData.tymed = TYMED_ISTREAM;
+        rem_bindinfo.stgmedData.u.pstm = binding_stream;
+        rem_bindinfo.stgmedData.pUnkForRelease = &unk_out.IUnknown_iface;
+        unk_out.ref = 1;
+        rem_bindinfo.cbstgmedData = 3;
+        IStream_AddRef(binding_stream);
+
+        hres = IBindStatusCallbackEx_GetBindInfoEx(callbackex, &bindf, &bindinfo, &bindf2, &reserved);
+        ok(hres == S_OK, "GetBindInfoEx failed: %08x\n", hres);
+        ok(bindf == rem_bindf, "bindf = %x, expected %x\n", bindf, rem_bindf);
+
+        ok(in_bindinfo.cbSize == sizeof(in_bindinfo), "cbSize = %u\n", in_bindinfo.cbSize);
+        ok(!in_bindinfo.pUnk, "pUnk = %p\n", in_bindinfo.pUnk);
+        ok(in_bindinfo.stgmedData.tymed == TYMED_NULL, "tymed = %u\n",
+           in_bindinfo.stgmedData.tymed);
+
+        ok(bindinfo.cbSize == sizeof(bindinfo), "cbSize = %u\n", bindinfo.cbSize);
+        ok(!bindinfo.pUnk, "pUnk = %p\n", bindinfo.pUnk);
+        ok(bindinfo.stgmedData.tymed == TYMED_NULL, "tymed = %u\n",
+           bindinfo.stgmedData.tymed);
+        ok(!bindinfo.stgmedData.u.pstm, "stm = %p\n",
+           bindinfo.stgmedData.u.pstm);
+        ok(!bindinfo.stgmedData.pUnkForRelease, "pUnkForRelease = %p\n",
+           bindinfo.stgmedData.pUnkForRelease);
+        ok(bindinfo.cbstgmedData == 3, "cbstgmedData = %u\n", bindinfo.cbstgmedData);
+
+        ReleaseBindInfo(&bindinfo);
+
+        /* Return HGLOBAL stgmed from GetBindInfoEx, it's not marshaled back */
+        rem_bindf = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA;
+        bindf = bindf2 = reserved = 0xdeadbeef;
+
+        memset(&bindinfo, 0, sizeof(bindinfo));
+        bindinfo.cbSize = sizeof(bindinfo);
+
+        memset(&rem_bindinfo, 0, sizeof(rem_bindinfo));
+        rem_bindinfo.cbSize = sizeof(rem_bindinfo);
+
+        rem_bindinfo.stgmedData.tymed = TYMED_HGLOBAL;
+
+        buf = GlobalAlloc(0, sizeof(5));
+        strcpy(buf, "test");
+        rem_bindinfo.stgmedData.u.hGlobal = buf;
+        rem_bindinfo.cbstgmedData = 5;
+
+        hres = IBindStatusCallbackEx_GetBindInfoEx(callbackex, &bindf, &bindinfo, &bindf2, &reserved);
+        ok(hres == S_OK, "GetBindInfoEx failed: %08x\n", hres);
+        ok(bindf == rem_bindf, "bindf = %x, expected %x\n", bindf, rem_bindf);
+
+        ok(in_bindinfo.cbSize == sizeof(in_bindinfo), "cbSize = %u\n", in_bindinfo.cbSize);
+        ok(!in_bindinfo.pUnk, "pUnk = %p\n", in_bindinfo.pUnk);
+        ok(in_bindinfo.stgmedData.tymed == TYMED_NULL, "tymed = %u\n",
+           in_bindinfo.stgmedData.tymed);
+
+        ok(bindinfo.cbSize == sizeof(bindinfo), "cbSize = %u\n", bindinfo.cbSize);
+        ok(!bindinfo.pUnk, "pUnk = %p\n", bindinfo.pUnk);
+        ok(bindinfo.stgmedData.tymed == TYMED_NULL, "tymed = %u\n",
+           bindinfo.stgmedData.tymed);
+        ok(!bindinfo.stgmedData.u.pstm, "stm = %p\n",
+           bindinfo.stgmedData.u.pstm);
+        ok(!bindinfo.stgmedData.pUnkForRelease, "pUnkForRelease = %p\n",
+           bindinfo.stgmedData.pUnkForRelease);
+        ok(bindinfo.cbstgmedData == 5, "cbstgmedData = %u\n", bindinfo.cbstgmedData);
+
+        ReleaseBindInfo(&bindinfo);
+
+        IBindStatusCallbackEx_Release(callbackex);
+    }else {
+        win_skip("IBindStatusCallbackEx not supported\n");
+    }
+
+    /* Test marshaling stgmed from OnDataAvailable */
+    memset(&in_stgmed, 0xcc, sizeof(in_stgmed));
+    stgmed.tymed = TYMED_ISTREAM;
+    stgmed.u.pstm = binding_stream;
+    stgmed.pUnkForRelease = NULL;
+
+    hres = IBindStatusCallback_OnDataAvailable(bsc, 1, 10, &formatetc, &stgmed);
+    ok(hres == S_OK, "OnDataAvailable failed: %08x\n", hres);
+
+    ok(in_stgmed.tymed == TYMED_ISTREAM, "tymed = %u\n", in_stgmed.tymed);
+    ok(in_stgmed.u.pstm != NULL, "pstm = NULL\n");
+    ok(!in_stgmed.pUnkForRelease, "pUnkForRelease = %p\n", in_stgmed.pUnkForRelease);
+
+    /* OnDataAvailable with both IStream and pUnkForRelease */
+    memset(&in_stgmed, 0xcc, sizeof(in_stgmed));
+    stgmed.tymed = TYMED_ISTREAM;
+    stgmed.u.pstm = binding_stream;
+    stgmed.pUnkForRelease = &unk_in.IUnknown_iface;
+    unk_in.ref = 1;
+
+    hres = IBindStatusCallback_OnDataAvailable(bsc, 1, 10, &formatetc, &stgmed);
+    ok(hres == S_OK, "OnDataAvailable failed: %08x\n", hres);
+
+    ok(in_stgmed.tymed == TYMED_ISTREAM, "tymed = %u\n", in_stgmed.tymed);
+    ok(in_stgmed.u.pstm != NULL, "pstm = NULL\n");
+    ok(in_stgmed.pUnkForRelease != NULL, "pUnkForRelease = %p\n", in_stgmed.pUnkForRelease);
+    ok(unk_in.ref > 1, "ref = %u\n", unk_in.ref);
+
+    /* OnDataAvailable with TYMED_ISTREAM, but NULL stream */
+    memset(&in_stgmed, 0xcc, sizeof(in_stgmed));
+    stgmed.tymed = TYMED_ISTREAM;
+    stgmed.u.pstm = binding_stream;
+    stgmed.pUnkForRelease = NULL;
+
+    hres = IBindStatusCallback_OnDataAvailable(bsc, 1, 10, &formatetc, &stgmed);
+    ok(hres == S_OK, "OnDataAvailable failed: %08x\n", hres);
+
+    ok(in_stgmed.tymed == TYMED_ISTREAM, "tymed = %u\n", in_stgmed.tymed);
+    ok(in_stgmed.u.pstm != NULL, "pstm = NULL\n");
+    ok(!in_stgmed.pUnkForRelease, "pUnkForRelease = %p\n", in_stgmed.pUnkForRelease);
+
+    /* OnDataAvailable with TYMED_NULL and pUnkForRelease */
+    memset(&in_stgmed, 0xcc, sizeof(in_stgmed));
+    stgmed.tymed = TYMED_NULL;
+    stgmed.u.pstm = binding_stream;
+    stgmed.pUnkForRelease = &unk_in.IUnknown_iface;
+    unk_in.ref = 1;
+
+    hres = IBindStatusCallback_OnDataAvailable(bsc, 1, 10, &formatetc, &stgmed);
+    ok(hres == S_OK, "OnDataAvailable failed: %08x\n", hres);
+
+    ok(in_stgmed.tymed == TYMED_NULL, "tymed = %u\n", in_stgmed.tymed);
+    ok(!in_stgmed.u.pstm, "pstm != NULL\n");
+    ok(in_stgmed.pUnkForRelease != NULL, "pUnkForRelease = %p\n", in_stgmed.pUnkForRelease);
+    ok(unk_in.ref == 1, "ref = %u\n", unk_in.ref);
+
+    IStream_Release(binding_stream);
+    IBindStatusCallback_Release(bsc);
+
+    TerminateThread(thread, 0);
+}
+
 START_TEST(misc)
 {
     HMODULE hurlmon;
@@ -1981,6 +2703,7 @@ START_TEST(misc)
         test_user_agent();
         test_MkParseDisplayNameEx();
         test_IsValidURL();
+        test_bsc_marshaling();
     }
 
     test_internet_features();