Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/oleaut32/usrmarshal.c | 84 ++++++++++++++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 9 deletions(-)
diff --git a/dlls/oleaut32/usrmarshal.c b/dlls/oleaut32/usrmarshal.c index 5eeafb8e378..69c7c1cfdda 100644 --- a/dlls/oleaut32/usrmarshal.c +++ b/dlls/oleaut32/usrmarshal.c @@ -286,6 +286,12 @@ static unsigned interface_variant_size(ULONG *pFlags, REFIID riid, IUnknown *pun return size; }
+static ULONG interface_safearray_size(ULONG *pFlags, ULONG Start, REFIID riid, IUnknown *punk) +{ + TRACE("(%x,%d,%p)\n", *pFlags, Start, punk); + return Start + interface_variant_size(pFlags, riid, punk); +} + static ULONG wire_extra_user_size(ULONG *pFlags, ULONG Start, VARIANT *pvar) { if (V_ISARRAY(pvar)) @@ -733,22 +739,40 @@ static inline SF_TYPE SAFEARRAY_GetUnionType(SAFEARRAY *psa)
static DWORD elem_wire_size(LPSAFEARRAY lpsa, SF_TYPE sftype) { - if (sftype == SF_BSTR) + switch (sftype) + { + case SF_BSTR: + case SF_HAVEIID: + case SF_UNKNOWN: + case SF_DISPATCH: return sizeof(DWORD); - else if (sftype == SF_VARIANT) + + case SF_VARIANT: return sizeof(variant_wire_t) - sizeof(DWORD); - else + + default: return lpsa->cbElements; + } }
static DWORD elem_mem_size(wireSAFEARRAY wiresa, SF_TYPE sftype) { - if (sftype == SF_BSTR) + switch (sftype) + { + case SF_HAVEIID: + case SF_UNKNOWN: + case SF_DISPATCH: + return sizeof(void *); + + case SF_BSTR: return sizeof(BSTR); - else if (sftype == SF_VARIANT) + + case SF_VARIANT: return sizeof(VARIANT); - else + + default: return wiresa->cbElements; + } }
ULONG WINAPI LPSAFEARRAY_UserSize(ULONG *pFlags, ULONG StartingSize, LPSAFEARRAY *ppsa) @@ -795,6 +819,22 @@ ULONG WINAPI LPSAFEARRAY_UserSize(ULONG *pFlags, ULONG StartingSize, LPSAFEARRAY case SF_DISPATCH: case SF_UNKNOWN: case SF_HAVEIID: + { + IUnknown **lpUnk; + GUID guid; + + if (sftype == SF_HAVEIID) + SafeArrayGetIID(psa, &guid); + else if (sftype == SF_UNKNOWN) + memcpy(&guid, &IID_IUnknown, sizeof(guid)); + else + memcpy(&guid, &IID_IDispatch, sizeof(guid)); + + for (lpUnk = psa->pvData; ulCellCount; ulCellCount--, lpUnk++) + size = interface_safearray_size(pFlags, size, &guid, *lpUnk); + + break; + } FIXME("size interfaces\n"); break; case SF_VARIANT: @@ -870,7 +910,7 @@ unsigned char * WINAPI LPSAFEARRAY_UserMarshal(ULONG *pFlags, unsigned char *Buf Buffer += sizeof(ULONG);
hr = SafeArrayGetVartype(psa, &vt); - if (FAILED(hr)) vt = 0; + if ((psa->fFeatures & FADF_HAVEIID) || FAILED(hr)) vt = 0;
*(ULONG *)Buffer = (USHORT)psa->cLocks | (vt << 16); Buffer += sizeof(ULONG); @@ -916,8 +956,21 @@ unsigned char * WINAPI LPSAFEARRAY_UserMarshal(ULONG *pFlags, unsigned char *Buf case SF_DISPATCH: case SF_UNKNOWN: case SF_HAVEIID: - FIXME("marshal interfaces\n"); + { + IUnknown **lpUnk; + + for (lpUnk = psa->pvData; ulCellCount; ulCellCount--, lpUnk++) + { + if (sftype == SF_HAVEIID) + Buffer = interface_variant_marshal(pFlags, Buffer, &guid, *lpUnk); + else if (sftype == SF_UNKNOWN) + Buffer = interface_variant_marshal(pFlags, Buffer, &IID_IUnknown, *lpUnk); + else + Buffer = interface_variant_marshal(pFlags, Buffer, &IID_IDispatch, *lpUnk); + } + break; + } case SF_VARIANT: { VARIANT* lpVariant; @@ -1093,8 +1146,21 @@ unsigned char * WINAPI LPSAFEARRAY_UserUnmarshal(ULONG *pFlags, unsigned char *B case SF_DISPATCH: case SF_UNKNOWN: case SF_HAVEIID: - FIXME("marshal interfaces\n"); + { + IUnknown **lpUnk; + + for (lpUnk = (*ppsa)->pvData; cell_count; cell_count--, lpUnk++) + { + if (sftype == SF_HAVEIID) + Buffer = interface_variant_unmarshal(pFlags, Buffer, &guid, lpUnk); + else if (sftype == SF_UNKNOWN) + Buffer = interface_variant_unmarshal(pFlags, Buffer, &IID_IUnknown, lpUnk); + else + Buffer = interface_variant_unmarshal(pFlags, Buffer, &IID_IDispatch, lpUnk); + } + break; + } case SF_VARIANT: { VARIANT* lpVariant;
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/oleaut32/tests/usrmarshal.c | 189 +++++++++++++++++++++++++++++-- 1 file changed, 177 insertions(+), 12 deletions(-)
diff --git a/dlls/oleaut32/tests/usrmarshal.c b/dlls/oleaut32/tests/usrmarshal.c index 92d0da3403b..ddf7c2f36ab 100644 --- a/dlls/oleaut32/tests/usrmarshal.c +++ b/dlls/oleaut32/tests/usrmarshal.c @@ -36,6 +36,14 @@ # define V_U2(A) (*(A)) #endif
+typedef struct +{ + IUnknown IUnknown_iface; + ULONG refs; +} HeapUnknown; + +static const IUnknownVtbl HeapUnknown_Vtbl; + static inline SF_TYPE get_union_type(SAFEARRAY *psa) { VARTYPE vt; @@ -108,10 +116,23 @@ static ULONG get_cell_count(const SAFEARRAY *psa)
static DWORD elem_wire_size(LPSAFEARRAY lpsa, SF_TYPE sftype) { - if (sftype == SF_BSTR) - return sizeof(DWORD); - else - return lpsa->cbElements; + DWORD size; + + switch (sftype) + { + case SF_HAVEIID: + case SF_UNKNOWN: + case SF_DISPATCH: + case SF_BSTR: + size = sizeof(DWORD); + break; + + default: + size = lpsa->cbElements; + break; + } + + return size; }
static void check_safearray(void *buffer, LPSAFEARRAY lpsa) @@ -129,7 +150,8 @@ static void check_safearray(void *buffer, LPSAFEARRAY lpsa) return; }
- if(FAILED(SafeArrayGetVartype(lpsa, &vt))) + /* If FADF_HAVEIID is set, VT will be 0. */ + if((lpsa->fFeatures & FADF_HAVEIID) || FAILED(SafeArrayGetVartype(lpsa, &vt))) vt = 0;
sftype = get_union_type(lpsa); @@ -217,8 +239,9 @@ static void init_user_marshal_cb(USER_MARSHAL_CB *umcb,
static void test_marshal_LPSAFEARRAY(void) { + HeapUnknown *heap_unknown[10]; unsigned char *buffer, *next; - ULONG size, expected; + ULONG size, expected, size2; LPSAFEARRAY lpsa; LPSAFEARRAY lpsa2 = NULL; SAFEARRAYBOUND sab[2]; @@ -503,6 +526,154 @@ static void test_marshal_LPSAFEARRAY(void) ok(hr == S_OK, "got 0x%08x\n", hr); hr = SafeArrayDestroyDescriptor(lpsa); ok(hr == S_OK, "got 0x%08x\n", hr); + + /* Test an array of VT_UNKNOWN */ + sab[0].lLbound = 3; + sab[0].cElements = ARRAY_SIZE(heap_unknown); + + lpsa = SafeArrayCreate(VT_UNKNOWN, 1, sab); + + /* + * Calculate approximate expected size. Sizes are different between Windows + * versions, so this should calculate the smallest size that seems sane. + */ + expected = 60; + for (i = 0; i < sab[0].cElements; i++) + { + HeapUnknown *unk; + VARIANT v; + + unk = HeapAlloc(GetProcessHeap(), 0, sizeof(*unk)); + unk->IUnknown_iface.lpVtbl = &HeapUnknown_Vtbl; + unk->refs = 1; + + indices[0] = i + sab[0].lLbound; + heap_unknown[i] = unk; + hr = SafeArrayPutElement(lpsa, indices, &heap_unknown[i]->IUnknown_iface); + ok(hr == S_OK, "Failed to put unknown element hr 0x%x\n", hr); + ok(unk->refs == 2, "VT_UNKNOWN safearray elem %d, refcount %d\n", i, unk->refs); + + V_VT(&v) = VT_UNKNOWN; + V_UNKNOWN(&v) = &unk->IUnknown_iface; + expected += VARIANT_UserSize(&umcb.Flags, 0, &v) - 20; + } + + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE); + size = LPSAFEARRAY_UserSize(&umcb.Flags, 0, &lpsa); + ok(size >= expected || size >= (expected + 12 ), + "size should be at least %u bytes, not %u\n", expected, size); + + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE); + size2 = LPSAFEARRAY_UserSize(&umcb.Flags, 1, &lpsa); + ok(size2 == (size + sizeof(DWORD)) || size2 == (size + sizeof(DWORD) + 12), + "size should be %u bytes, not %u\n", size + (ULONG) sizeof(DWORD), size2); + + buffer = HeapAlloc(GetProcessHeap(), 0, size); + memset(buffer, 0xcc, size); + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_DIFFERENTMACHINE); + next = LPSAFEARRAY_UserMarshal(&umcb.Flags, buffer, &lpsa); + ok((next - buffer) <= size, "Marshaled %u bytes, expected at most %u\n", (ULONG) (next - buffer), size); + check_safearray(buffer, lpsa); +todo_wine + ok(heap_unknown[0]->refs == 3, "Unexpected refcount %d\n", heap_unknown[0]->refs); + + lpsa2 = NULL; + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_DIFFERENTMACHINE); + next = LPSAFEARRAY_UserUnmarshal(&umcb.Flags, buffer, &lpsa2); + ok((next - buffer) <= size, "Marshaled %u bytes, expected at most %u\n", (ULONG) (next - buffer), size); + ok(lpsa2 != NULL, "LPSAFEARRAY didn't unmarshal, result %p\n", next); + + for (i = 0; i < ARRAY_SIZE(heap_unknown); i++) + { + IUnknown *gotvalue = NULL; + + if (lpsa2) + { + indices[0] = i + sab[0].lLbound; + hr = SafeArrayGetElement(lpsa2, indices, &gotvalue); + ok(hr == S_OK, "Failed to get unk element at %d, hres 0x%x\n", i, hr); + if (hr == S_OK) + { + ok(gotvalue == &heap_unknown[i]->IUnknown_iface, "Interface %d mismatch, expected %p, got %p\n", + i, &heap_unknown[i]->IUnknown_iface, gotvalue); + IUnknown_Release(gotvalue); + } + } + } + + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE); + LPSAFEARRAY_UserFree(&umcb.Flags, &lpsa2); + + /* Set one of the elements to NULL, see how this effects size. */ + indices[0] = 3 + sab[0].lLbound; + hr = SafeArrayPutElement(lpsa, indices, NULL); + ok(hr == S_OK, "Failed to put unknown element hr 0x%x\n", hr); + + expected = 60; + for (i = 0; i < sab[0].cElements; i++) + { + VARIANT v; + + V_VT(&v) = VT_UNKNOWN; + V_UNKNOWN(&v) = (i != 3) ? &heap_unknown[i]->IUnknown_iface : NULL; + expected += VARIANT_UserSize(&umcb.Flags, 0, &v) - 20; + } + + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE); + size = LPSAFEARRAY_UserSize(&umcb.Flags, 0, &lpsa); + ok(size >= expected || size >= (expected + 12 ), + "size should be at least %u bytes, not %u\n", expected, size); + + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE); + size2 = LPSAFEARRAY_UserSize(&umcb.Flags, 1, &lpsa); + ok(size2 == (size + sizeof(DWORD)) || size2 == (size + sizeof(DWORD) + 12), + "size should be %u bytes, not %u\n", size + (ULONG) sizeof(DWORD), size2); + + buffer = HeapAlloc(GetProcessHeap(), 0, size); + memset(buffer, 0xcc, size); + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_DIFFERENTMACHINE); + next = LPSAFEARRAY_UserMarshal(&umcb.Flags, buffer, &lpsa); + ok((next - buffer) <= expected, "Marshaled %u bytes, expected at most %u bytes\n", (ULONG) (next - buffer), expected); + check_safearray(buffer, lpsa); + + lpsa2 = NULL; + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_DIFFERENTMACHINE); + next = LPSAFEARRAY_UserUnmarshal(&umcb.Flags, buffer, &lpsa2); + ok((next - buffer) <= expected, "Marshaled %u bytes, expected at most %u bytes\n", (ULONG) (next - buffer), expected); + ok(lpsa2 != NULL, "LPSAFEARRAY didn't unmarshal, result %p\n", next); + + for (i = 0; i < ARRAY_SIZE(heap_unknown); i++) + { + IUnknown *gotvalue = NULL; + + if (lpsa2) + { + indices[0] = i + sab[0].lLbound; + hr = SafeArrayGetElement(lpsa2, indices, &gotvalue); + ok(hr == S_OK, "Failed to get unk element at %d, hres 0x%x\n", i, hr); + if (hr == S_OK) + { + /* Our NULL interface. */ + if (i == 3) + ok(gotvalue == NULL, "Interface %d expected NULL, got %p\n", i, gotvalue); + else + { + ok(gotvalue == &heap_unknown[i]->IUnknown_iface, "Interface %d mismatch, expected %p, got %p\n", + i, &heap_unknown[i]->IUnknown_iface, gotvalue); + IUnknown_Release(gotvalue); + } + } + } + IUnknown_Release(&heap_unknown[i]->IUnknown_iface); + } + + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE); + LPSAFEARRAY_UserFree(&umcb.Flags, &lpsa2); + + ok(heap_unknown[0]->refs == 1, "Unexpected refcount %d\n", heap_unknown[0]->refs); + + hr = SafeArrayDestroy(lpsa); + ok(hr == S_OK, "got 0x%08x\n", hr); }
static void check_bstr(void *buffer, BSTR b) @@ -648,12 +819,6 @@ static void test_marshal_BSTR(void) SysFreeString(b); }
-typedef struct -{ - IUnknown IUnknown_iface; - ULONG refs; -} HeapUnknown; - static inline HeapUnknown *impl_from_IUnknown(IUnknown *iface) { return CONTAINING_RECORD(iface, HeapUnknown, IUnknown_iface);
On 11/23/21 10:16 PM, Connor McAdams wrote:
+static ULONG interface_safearray_size(ULONG *pFlags, ULONG Start, REFIID riid, IUnknown *punk) +{
- TRACE("(%x,%d,%p)\n", *pFlags, Start, punk);
- return Start + interface_variant_size(pFlags, riid, punk);
+}
You might as well add "Start" parameter to interface_variant_size(), and possibly rename it too, since it's no longer used only for variants.
@@ -916,8 +956,21 @@ unsigned char * WINAPI LPSAFEARRAY_UserMarshal(ULONG *pFlags, unsigned char *Buf case SF_DISPATCH: case SF_UNKNOWN: case SF_HAVEIID:
FIXME("marshal interfaces\n");
{
IUnknown **lpUnk;
for (lpUnk = psa->pvData; ulCellCount; ulCellCount--, lpUnk++)
{
if (sftype == SF_HAVEIID)
Buffer = interface_variant_marshal(pFlags, Buffer, &guid, *lpUnk);
else if (sftype == SF_UNKNOWN)
Buffer = interface_variant_marshal(pFlags, Buffer, &IID_IUnknown, *lpUnk);
else
Buffer = interface_variant_marshal(pFlags, Buffer, &IID_IDispatch, *lpUnk);
}
break;
}
sftype won't change between iterations, so you only need to set some "GUID *" once, and use a single helper call.
{
IUnknown **lpUnk;
for (lpUnk = (*ppsa)->pvData; cell_count; cell_count--, lpUnk++)
{
if (sftype == SF_HAVEIID)
Buffer = interface_variant_unmarshal(pFlags, Buffer, &guid, lpUnk);
else if (sftype == SF_UNKNOWN)
Buffer = interface_variant_unmarshal(pFlags, Buffer, &IID_IUnknown, lpUnk);
else
Buffer = interface_variant_unmarshal(pFlags, Buffer, &IID_IDispatch, lpUnk);
}
break;
}
Same.
@@ -795,6 +819,22 @@ ULONG WINAPI LPSAFEARRAY_UserSize(ULONG *pFlags, ULONG StartingSize, LPSAFEARRAY case SF_DISPATCH: case SF_UNKNOWN: case SF_HAVEIID:
{
IUnknown **lpUnk;
GUID guid;
if (sftype == SF_HAVEIID)
SafeArrayGetIID(psa, &guid);
else if (sftype == SF_UNKNOWN)
memcpy(&guid, &IID_IUnknown, sizeof(guid));
else
memcpy(&guid, &IID_IDispatch, sizeof(guid));
for (lpUnk = psa->pvData; ulCellCount; ulCellCount--, lpUnk++)
size = interface_safearray_size(pFlags, size, &guid, *lpUnk);
break;
} FIXME("size interfaces\n"); break;
FIXME should be removed now that you implement this part.