From 42aaad43db98690fc59df7178231b6e3de155ccb Mon Sep 17 00:00:00 2001 From: Oleg Dubinskiy Date: Sun, 15 Jun 2025 16:08:51 +0200 Subject: [PATCH] [OLEAUT32][OLEAUT32_WINETEST] Import LPSAFEARRAY user marshal interface marshaling support from Wine (#8121) Import the following Wine commits: oleaut32: Implement LPSAFEARRAY user marshal interface marshaling. https://gitlab.winehq.org/wine/wine/-/commit/a1f2b44a1bf9ecbcf772aa8509f87c2be1ccc415 oleaut32/tests: Add tests for LPSAFEARRAY user marshal interface marshaling. https://gitlab.winehq.org/wine/wine/-/commit/b2574278f7ab6709b920f562bb03f23073827327 containing the implementation itself and a test for it. Fixes: 1) improperly displayed comboboxes with OS type and version selection, 2) failure to save/load virtual machine settings in/from XML file in VirtualBox 3.0.0 - 4.0.36 versions and 3) improperly displayed comboboxes in 3DMark2001. As result, this also allows to create and start a virtual machine(s) in VirtualBox up to 3.0.8 version. For newer VirtualBox versions, another fix is required. CORE-8418, CORE-11254, CORE-14507, CORE-17980, CORE-18496, CORE-20015 --- dll/win32/oleaut32/usrmarshal.c | 174 ++++++++++++++++ .../rostests/winetests/oleaut32/usrmarshal.c | 191 ++++++++++++++++++ 2 files changed, 365 insertions(+) diff --git a/dll/win32/oleaut32/usrmarshal.c b/dll/win32/oleaut32/usrmarshal.c index 163b48f6f46..43c30088cfd 100644 --- a/dll/win32/oleaut32/usrmarshal.c +++ b/dll/win32/oleaut32/usrmarshal.c @@ -268,7 +268,11 @@ static unsigned int get_type_alignment(ULONG *pFlags, VARTYPE vt) } /* WdtpInterfacePointer_UserSize takes care of 2 additional DWORDs to store marshalling buffer size */ +#ifdef __REACTOS__ +static unsigned interface_user_size(ULONG *pFlags, ULONG Start, REFIID riid, IUnknown *punk) +#else static unsigned interface_variant_size(ULONG *pFlags, REFIID riid, IUnknown *punk) +#endif { ULONG size = 0; @@ -283,7 +287,11 @@ static unsigned interface_variant_size(ULONG *pFlags, REFIID riid, IUnknown *pun } size += sizeof(ULONG); TRACE("wire-size extra of interface variant is %d\n", size); +#ifdef __REACTOS__ + return Start + size; +#else return size; +#endif } static ULONG wire_extra_user_size(ULONG *pFlags, ULONG Start, VARIANT *pvar) @@ -304,13 +312,29 @@ static ULONG wire_extra_user_size(ULONG *pFlags, ULONG Start, VARIANT *pvar) case VT_VARIANT | VT_BYREF: return VARIANT_UserSize(pFlags, Start, V_VARIANTREF(pvar)); case VT_UNKNOWN: +#ifdef __REACTOS__ + return interface_user_size(pFlags, Start, &IID_IUnknown, V_UNKNOWN(pvar)); +#else return Start + interface_variant_size(pFlags, &IID_IUnknown, V_UNKNOWN(pvar)); +#endif case VT_UNKNOWN | VT_BYREF: +#ifdef __REACTOS__ + return interface_user_size(pFlags, Start, &IID_IUnknown, *V_UNKNOWNREF(pvar)); +#else return Start + interface_variant_size(pFlags, &IID_IUnknown, *V_UNKNOWNREF(pvar)); +#endif case VT_DISPATCH: +#ifdef __REACTOS__ + return interface_user_size(pFlags, Start, &IID_IDispatch, (IUnknown*)V_DISPATCH(pvar)); +#else return Start + interface_variant_size(pFlags, &IID_IDispatch, (IUnknown*)V_DISPATCH(pvar)); +#endif case VT_DISPATCH | VT_BYREF: +#ifdef __REACTOS__ + return interface_user_size(pFlags, Start, &IID_IDispatch, (IUnknown*)*V_DISPATCHREF(pvar)); +#else return Start + interface_variant_size(pFlags, &IID_IDispatch, (IUnknown*)*V_DISPATCHREF(pvar)); +#endif case VT_RECORD: FIXME("wire-size record\n"); return Start; @@ -324,7 +348,11 @@ static ULONG wire_extra_user_size(ULONG *pFlags, ULONG Start, VARIANT *pvar) } /* helper: called for VT_DISPATCH variants to marshal the IDispatch* into the buffer */ +#ifdef __REACTOS__ +static unsigned char* interface_user_marshal(ULONG *pFlags, unsigned char *Buffer, +#else static unsigned char* interface_variant_marshal(ULONG *pFlags, unsigned char *Buffer, +#endif REFIID riid, IUnknown *punk) { TRACE("pFlags=%d, Buffer=%p, pUnk=%p\n", *pFlags, Buffer, punk); @@ -345,7 +373,11 @@ static unsigned char* interface_variant_marshal(ULONG *pFlags, unsigned char *Bu } /* helper: called for VT_DISPATCH / VT_UNKNOWN variants to unmarshal the buffer */ +#ifdef __REACTOS__ +static unsigned char *interface_user_unmarshal(ULONG *pFlags, unsigned char *Buffer, +#else static unsigned char *interface_variant_unmarshal(ULONG *pFlags, unsigned char *Buffer, +#endif REFIID riid, IUnknown **ppunk) { DWORD ptr; @@ -459,16 +491,32 @@ unsigned char * WINAPI VARIANT_UserMarshal(ULONG *pFlags, unsigned char *Buffer, Pos = VARIANT_UserMarshal(pFlags, Pos, V_VARIANTREF(pvar)); break; case VT_UNKNOWN: +#ifdef __REACTOS__ + Pos = interface_user_marshal(pFlags, Pos, &IID_IUnknown, V_UNKNOWN(pvar)); +#else Pos = interface_variant_marshal(pFlags, Pos, &IID_IUnknown, V_UNKNOWN(pvar)); +#endif break; case VT_UNKNOWN | VT_BYREF: +#ifdef __REACTOS__ + Pos = interface_user_marshal(pFlags, Pos, &IID_IUnknown, *V_UNKNOWNREF(pvar)); +#else Pos = interface_variant_marshal(pFlags, Pos, &IID_IUnknown, *V_UNKNOWNREF(pvar)); +#endif break; case VT_DISPATCH: +#ifdef __REACTOS__ + Pos = interface_user_marshal(pFlags, Pos, &IID_IDispatch, (IUnknown*)V_DISPATCH(pvar)); +#else Pos = interface_variant_marshal(pFlags, Pos, &IID_IDispatch, (IUnknown*)V_DISPATCH(pvar)); +#endif break; case VT_DISPATCH | VT_BYREF: +#ifdef __REACTOS__ + Pos = interface_user_marshal(pFlags, Pos, &IID_IDispatch, (IUnknown*)*V_DISPATCHREF(pvar)); +#else Pos = interface_variant_marshal(pFlags, Pos, &IID_IDispatch, (IUnknown*)*V_DISPATCHREF(pvar)); +#endif break; case VT_RECORD: FIXME("handle BRECORD by val\n"); @@ -590,16 +638,32 @@ unsigned char * WINAPI VARIANT_UserUnmarshal(ULONG *pFlags, unsigned char *Buffe Pos = VARIANT_UserUnmarshal(pFlags, Pos, V_VARIANTREF(pvar)); break; case VT_UNKNOWN: +#ifdef __REACTOS__ + Pos = interface_user_unmarshal(pFlags, Pos, &IID_IUnknown, &V_UNKNOWN(pvar)); +#else Pos = interface_variant_unmarshal(pFlags, Pos, &IID_IUnknown, &V_UNKNOWN(pvar)); +#endif break; case VT_UNKNOWN | VT_BYREF: +#ifdef __REACTOS__ + Pos = interface_user_unmarshal(pFlags, Pos, &IID_IUnknown, V_UNKNOWNREF(pvar)); +#else Pos = interface_variant_unmarshal(pFlags, Pos, &IID_IUnknown, V_UNKNOWNREF(pvar)); +#endif break; case VT_DISPATCH: +#ifdef __REACTOS__ + Pos = interface_user_unmarshal(pFlags, Pos, &IID_IDispatch, (IUnknown**)&V_DISPATCH(pvar)); +#else Pos = interface_variant_unmarshal(pFlags, Pos, &IID_IDispatch, (IUnknown**)&V_DISPATCH(pvar)); +#endif break; case VT_DISPATCH | VT_BYREF: +#ifdef __REACTOS__ + Pos = interface_user_unmarshal(pFlags, Pos, &IID_IDispatch, (IUnknown**)V_DISPATCHREF(pvar)); +#else Pos = interface_variant_unmarshal(pFlags, Pos, &IID_IDispatch, (IUnknown**)V_DISPATCHREF(pvar)); +#endif break; case VT_RECORD: FIXME("handle BRECORD by val\n"); @@ -733,22 +797,68 @@ static inline SF_TYPE SAFEARRAY_GetUnionType(SAFEARRAY *psa) static DWORD elem_wire_size(LPSAFEARRAY lpsa, SF_TYPE sftype) { +#ifdef __REACTOS__ + switch (sftype) + { + case SF_BSTR: + case SF_HAVEIID: + case SF_UNKNOWN: + case SF_DISPATCH: +#else if (sftype == SF_BSTR) +#endif return sizeof(DWORD); +#ifdef __REACTOS__ + + case SF_VARIANT: +#else else if (sftype == SF_VARIANT) +#endif return sizeof(variant_wire_t) - sizeof(DWORD); +#ifdef __REACTOS__ + + default: +#else else +#endif return lpsa->cbElements; +#ifdef __REACTOS__ + } +#endif } static DWORD elem_mem_size(wireSAFEARRAY wiresa, SF_TYPE sftype) { +#ifdef __REACTOS__ + switch (sftype) + { + case SF_HAVEIID: + case SF_UNKNOWN: + case SF_DISPATCH: + return sizeof(void *); + + case SF_BSTR: +#else if (sftype == SF_BSTR) +#endif return sizeof(BSTR); +#ifdef __REACTOS__ + + case SF_VARIANT: +#else else if (sftype == SF_VARIANT) +#endif return sizeof(VARIANT); +#ifdef __REACTOS__ + + default: +#else else +#endif return wiresa->cbElements; +#ifdef __REACTOS__ + } +#endif } ULONG WINAPI LPSAFEARRAY_UserSize(ULONG *pFlags, ULONG StartingSize, LPSAFEARRAY *ppsa) @@ -795,8 +905,28 @@ ULONG WINAPI LPSAFEARRAY_UserSize(ULONG *pFlags, ULONG StartingSize, LPSAFEARRAY case SF_DISPATCH: case SF_UNKNOWN: case SF_HAVEIID: +#ifdef __REACTOS__ + { + IUnknown **lpUnk; + GUID guid; + + if (sftype == SF_HAVEIID) + SafeArrayGetIID(psa, &guid); + else if (sftype == SF_UNKNOWN) + guid = IID_IUnknown; + else + guid = IID_IDispatch; + + for (lpUnk = psa->pvData; ulCellCount; ulCellCount--, lpUnk++) + size = interface_user_size(pFlags, size, &guid, *lpUnk); + +#else FIXME("size interfaces\n"); +#endif break; +#ifdef __REACTOS__ + } +#endif case SF_VARIANT: { VARIANT* lpVariant; @@ -870,7 +1000,11 @@ unsigned char * WINAPI LPSAFEARRAY_UserMarshal(ULONG *pFlags, unsigned char *Buf Buffer += sizeof(ULONG); hr = SafeArrayGetVartype(psa, &vt); +#ifdef __REACTOS__ + if ((psa->fFeatures & FADF_HAVEIID) || FAILED(hr)) vt = 0; +#else if (FAILED(hr)) vt = 0; +#endif *(ULONG *)Buffer = (USHORT)psa->cLocks | (vt << 16); Buffer += sizeof(ULONG); @@ -916,8 +1050,28 @@ unsigned char * WINAPI LPSAFEARRAY_UserMarshal(ULONG *pFlags, unsigned char *Buf case SF_DISPATCH: case SF_UNKNOWN: case SF_HAVEIID: +#ifdef __REACTOS__ + { + IUnknown **lpUnk; + const GUID *iid; + + if (sftype == SF_HAVEIID) + iid = &guid; + else if (sftype == SF_UNKNOWN) + iid = &IID_IUnknown; + else + iid = &IID_IDispatch; + + for (lpUnk = psa->pvData; ulCellCount; ulCellCount--, lpUnk++) + Buffer = interface_user_marshal(pFlags, Buffer, iid, *lpUnk); + +#else FIXME("marshal interfaces\n"); +#endif break; +#ifdef __REACTOS__ + } +#endif case SF_VARIANT: { VARIANT* lpVariant; @@ -1093,8 +1247,28 @@ unsigned char * WINAPI LPSAFEARRAY_UserUnmarshal(ULONG *pFlags, unsigned char *B case SF_DISPATCH: case SF_UNKNOWN: case SF_HAVEIID: +#ifdef __REACTOS__ + { + IUnknown **lpUnk; + const GUID *iid; + + if (sftype == SF_HAVEIID) + iid = &guid; + else if (sftype == SF_UNKNOWN) + iid = &IID_IUnknown; + else + iid = &IID_IDispatch; + + for (lpUnk = (*ppsa)->pvData; cell_count; cell_count--, lpUnk++) + Buffer = interface_user_unmarshal(pFlags, Buffer, iid, lpUnk); + +#else FIXME("marshal interfaces\n"); +#endif break; +#ifdef __REACTOS__ + } +#endif case SF_VARIANT: { VARIANT* lpVariant; diff --git a/modules/rostests/winetests/oleaut32/usrmarshal.c b/modules/rostests/winetests/oleaut32/usrmarshal.c index 97c05024e52..24b3a4c1e7c 100644 --- a/modules/rostests/winetests/oleaut32/usrmarshal.c +++ b/modules/rostests/winetests/oleaut32/usrmarshal.c @@ -36,6 +36,16 @@ # define V_U2(A) (*(A)) #endif +#ifdef __REACTOS__ +typedef struct +{ + IUnknown IUnknown_iface; + ULONG refs; +} HeapUnknown; + +static const IUnknownVtbl HeapUnknown_Vtbl; +#endif + static HRESULT (WINAPI *pSafeArrayGetIID)(SAFEARRAY*,GUID*); static HRESULT (WINAPI *pSafeArrayGetVartype)(SAFEARRAY*,VARTYPE*); static HRESULT (WINAPI *pVarBstrCmp)(BSTR,BSTR,LCID,ULONG); @@ -112,10 +122,27 @@ static ULONG get_cell_count(const SAFEARRAY *psa) static DWORD elem_wire_size(LPSAFEARRAY lpsa, SF_TYPE sftype) { +#ifdef __REACTOS__ + switch (sftype) + { + case SF_HAVEIID: + case SF_UNKNOWN: + case SF_DISPATCH: + case SF_BSTR: +#else if (sftype == SF_BSTR) +#endif return sizeof(DWORD); +#ifdef __REACTOS__ + + default: +#else else +#endif return lpsa->cbElements; +#ifdef __REACTOS__ + } +#endif } static void check_safearray(void *buffer, LPSAFEARRAY lpsa) @@ -136,7 +163,12 @@ static void check_safearray(void *buffer, LPSAFEARRAY lpsa) if (!pSafeArrayGetVartype || !pSafeArrayGetIID) return; +#ifdef __REACTOS__ + /* If FADF_HAVEIID is set, VT will be 0. */ + if((lpsa->fFeatures & FADF_HAVEIID) || FAILED(SafeArrayGetVartype(lpsa, &vt))) +#else if(FAILED(pSafeArrayGetVartype(lpsa, &vt))) +#endif vt = 0; sftype = get_union_type(lpsa); @@ -224,8 +256,15 @@ static void init_user_marshal_cb(USER_MARSHAL_CB *umcb, static void test_marshal_LPSAFEARRAY(void) { +#ifdef __REACTOS__ + HeapUnknown *heap_unknown[10]; +#endif unsigned char *buffer, *next; +#ifdef __REACTOS__ + ULONG size, expected, size2; +#else ULONG size, expected; +#endif LPSAFEARRAY lpsa; LPSAFEARRAY lpsa2 = NULL; SAFEARRAYBOUND sab[2]; @@ -525,6 +564,156 @@ 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); + +#ifdef __REACTOS__ + /* 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); +#endif } static void check_bstr(void *buffer, BSTR b) @@ -670,11 +859,13 @@ static void test_marshal_BSTR(void) SysFreeString(b); } +#ifndef __REACTOS__ typedef struct { IUnknown IUnknown_iface; ULONG refs; } HeapUnknown; +#endif static inline HeapUnknown *impl_from_IUnknown(IUnknown *iface) {