C++/CX exposes `IPropertyValue` through `Platform::Details::CreateValue`, which returns a `Platform::Object` (which just seems to be a `IPropertyValue` object + C++ RTTI) containing the boxed value.
Apps don't seem to call `CreateValue` directly with a typecode. The code for boxing values gets generated when a value type gets assigned to a `Platform::Object` variable, as documented [here.](https://learn.microsoft.com/en-us/cpp/extensions/boxing-cpp-component-extens...) The generated code will:
1. Call `__abi_make_type_id` with a `struct __abi_type_descriptor *` value, which is just a typecode + string pair. This returns a `Platform::Type` object. 2. Call `Platform::Type::GetTypeCode` on the returned `Platform::Type` object to get back the typecode. 3. Call `Platform::Details::CreateValue` with the typecode and a pointer to the actual value that needs to be boxed.
Apart from the usual WinRT methods, `Platform::Type` obejcts also impement the `IEquatable` and `IPrintable` interfaces. I don't know what their vtables look like, though.
The `Platform::Type` and `Platform::Object` classes also have associated RTTI (can be demonstrated by calling `__RTtypeid`), which we'll also need to emit at some point in the future.
-- v8: vccorlib140: Use the correct symbol name for InitControlBlock on i386 and arm. vccorlib140: Implement Platform::Details::CreateValue. vccorlib140: Add stub for Platform::Details::CreateValue. vccorlib140: Implement Platform::Type::{Equals, GetTypeCode, ToString, FullName::get}. vccorlib140: Implement __abi_make_type_id.
From: Vibhav Pant vibhavp@gmail.com
--- dlls/vccorlib140/tests/Makefile.in | 2 +- dlls/vccorlib140/tests/vccorlib.c | 165 +++++++++++++++++++++++++++++ dlls/vccorlib140/vccorlib.c | 48 +++++++++ dlls/vccorlib140/vccorlib140.spec | 22 ++-- 4 files changed, 225 insertions(+), 12 deletions(-)
diff --git a/dlls/vccorlib140/tests/Makefile.in b/dlls/vccorlib140/tests/Makefile.in index b75d820bc8c..9d20ff64424 100644 --- a/dlls/vccorlib140/tests/Makefile.in +++ b/dlls/vccorlib140/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = vccorlib140.dll -IMPORTS = ole32 uuid +IMPORTS = combase ole32 uuid
SOURCES = \ vccorlib.c diff --git a/dlls/vccorlib140/tests/vccorlib.c b/dlls/vccorlib140/tests/vccorlib.c index 199481ff779..bef246f2760 100644 --- a/dlls/vccorlib140/tests/vccorlib.c +++ b/dlls/vccorlib140/tests/vccorlib.c @@ -27,6 +27,9 @@ #include "activation.h" #include "objbase.h" #include "weakreference.h" +#define WIDL_using_Windows_Foundation +#include "windows.foundation.h" +#include "winstring.h" #include "wine/test.h"
#define DEFINE_EXPECT(func) \ @@ -103,6 +106,12 @@ DEFINE_EXPECT(PostInitialize); DEFINE_EXPECT(PreUninitialize); DEFINE_EXPECT(PostUninitialize);
+struct __abi_type_descriptor +{ + const WCHAR *name; + int type_id; +}; + static HRESULT (__cdecl *pInitializeData)(int); static void (__cdecl *pUninitializeData)(int); static HRESULT (WINAPI *pGetActivationFactoryByPCWSTR)(const WCHAR *, const GUID *, void **); @@ -111,6 +120,11 @@ static void *(__cdecl *pAllocate)(size_t); static void (__cdecl *pFree)(void *); static void *(__cdecl *pAllocateWithWeakRef)(ptrdiff_t, size_t); static void (__thiscall *pReleaseTarget)(void *); +static void *(WINAPI *p___abi_make_type_id)(const struct __abi_type_descriptor *desc); +static bool (__cdecl *p_platform_type_Equals_Object)(void *, void *); +static int (__cdecl *p_platform_type_GetTypeCode)(void *); +static HSTRING (__cdecl *p_platform_type_ToString)(void *); +static HSTRING (__cdecl *p_platform_type_get_FullName)(void *);
static BOOL init(void) { @@ -137,6 +151,13 @@ static BOOL init(void) pFree = (void *)GetProcAddress(hmod, "?Free@Heap@Details@Platform@@SAXPAX@Z"); pAllocateWithWeakRef = (void *)GetProcAddress(hmod, "?Allocate@Heap@Details@Platform@@SAPAXII@Z"); pReleaseTarget = (void *)GetProcAddress(hmod, "?ReleaseTarget@ControlBlock@Details@Platform@@AAAXXZ"); + p___abi_make_type_id = (void *)GetProcAddress(hmod, + "?__abi_make_type_id@@YAP$AAVType@Platform@@ABU__abi_type_descriptor@@@Z"); + p_platform_type_Equals_Object = (void *)GetProcAddress(hmod, "?Equals@Type@Platform@@U$AAA_NP$AAVObject@2@@Z"); + p_platform_type_GetTypeCode = (void *)GetProcAddress(hmod, + "?GetTypeCode@Type@Platform@@SA?AW4TypeCode@2@P$AAV12@@Z"); + p_platform_type_ToString = (void *)GetProcAddress(hmod, "?ToString@Type@Platform@@U$AAAP$AAVString@2@XZ"); + p_platform_type_get_FullName = (void *)GetProcAddress(hmod, "?get@FullName@Type@Platform@@Q$AAAP$AAVString@3@XZ"); #else if (sizeof(void *) == 8) { @@ -147,6 +168,15 @@ static BOOL init(void) pFree = (void *)GetProcAddress(hmod, "?Free@Heap@Details@Platform@@SAXPEAX@Z"); pAllocateWithWeakRef = (void *)GetProcAddress(hmod, "?Allocate@Heap@Details@Platform@@SAPEAX_K0@Z"); pReleaseTarget = (void *)GetProcAddress(hmod, "?ReleaseTarget@ControlBlock@Details@Platform@@AEAAXXZ"); + p___abi_make_type_id = (void *)GetProcAddress(hmod, + "?__abi_make_type_id@@YAPE$AAVType@Platform@@AEBU__abi_type_descriptor@@@Z"); + p_platform_type_Equals_Object = (void *)GetProcAddress(hmod, + "?Equals@Type@Platform@@UE$AAA_NPE$AAVObject@2@@Z"); + p_platform_type_GetTypeCode = (void *)GetProcAddress(hmod, + "?GetTypeCode@Type@Platform@@SA?AW4TypeCode@2@PE$AAV12@@Z"); + p_platform_type_ToString = (void *)GetProcAddress(hmod, "?ToString@Type@Platform@@UE$AAAPE$AAVString@2@XZ"); + p_platform_type_get_FullName = (void *)GetProcAddress(hmod, + "?get@FullName@Type@Platform@@QE$AAAPE$AAVString@3@XZ"); } else { @@ -157,6 +187,14 @@ static BOOL init(void) pFree = (void *)GetProcAddress(hmod, "?Free@Heap@Details@Platform@@SAXPAX@Z"); pAllocateWithWeakRef = (void *)GetProcAddress(hmod, "?Allocate@Heap@Details@Platform@@SAPAXII@Z"); pReleaseTarget = (void *)GetProcAddress(hmod, "?ReleaseTarget@ControlBlock@Details@Platform@@AAEXXZ"); + p___abi_make_type_id = (void *)GetProcAddress(hmod, + "?__abi_make_type_id@@YGP$AAVType@Platform@@ABU__abi_type_descriptor@@@Z"); + p_platform_type_Equals_Object = (void *)GetProcAddress(hmod, "?Equals@Type@Platform@@U$AAA_NP$AAVObject@2@@Z"); + p_platform_type_GetTypeCode = (void *)GetProcAddress(hmod, + "?GetTypeCode@Type@Platform@@SA?AW4TypeCode@2@P$AAV12@@Z"); + p_platform_type_ToString = (void *)GetProcAddress(hmod, "?ToString@Type@Platform@@U$AAAP$AAVString@2@XZ"); + p_platform_type_get_FullName = (void *)GetProcAddress(hmod, + "?get@FullName@Type@Platform@@Q$AAAP$AAVString@3@XZ"); } #endif ok(pGetActivationFactoryByPCWSTR != NULL, "GetActivationFactoryByPCWSTR not available\n"); @@ -165,6 +203,11 @@ static BOOL init(void) ok(pFree != NULL, "Free not available\n"); ok(pAllocateWithWeakRef != NULL, "AllocateWithWeakRef not available\n"); ok(pReleaseTarget != NULL, "ReleaseTarget not available\n"); + ok(p___abi_make_type_id != NULL, "__abi_make_type_id not available\n"); + ok(p_platform_type_Equals_Object != NULL, "Platform::Type::Equals not available\n"); + ok(p_platform_type_GetTypeCode != NULL, "Platform::Type::GetTypeCode not available\n"); + ok(p_platform_type_ToString != NULL, "Platform::Type::ToString not available\n"); + ok(p_platform_type_get_FullName != NULL, "Platform::Type::FullName not available\n");
init_thiscall_thunk();
@@ -610,6 +653,127 @@ static void test_AllocateWithWeakRef(void) ok(count == 0, "got count %lu\n", count); }
+#define check_interface(o, i) check_interface_(__LINE__, (o), (i)) +static void check_interface_(int line, void *obj, const GUID *iid) +{ + HRESULT hr; + void *out; + + hr = IUnknown_QueryInterface((IUnknown *)obj, iid, &out); + ok_(__FILE__, line)(hr == S_OK, "got hr %#lx\n", hr); + if (SUCCEEDED(hr)) IUnknown_Release((IUnknown *)out); +} + +/* Additional interfaces implemented by Platform::Object. */ +DEFINE_GUID(IID_IEquatable,0xde0cbaec,0xe077,0x4e92,0x84,0xbe,0xc6,0xd4,0x8d,0xca,0x30,0x06); +DEFINE_GUID(IID_IPrintable,0xde0cbaeb,0x8065,0x4a45,0x96,0xb1,0xc9,0xd4,0x43,0xf9,0xba,0xb3); + +static void test___abi_make_type_id(void) +{ + const struct __abi_type_descriptor desc = {L"foo", 0xdeadbeef}, desc2 = {L"foo", 0xdeadbeef}, desc3 = {NULL, 1}; + void *type_obj, *type_obj2; + const WCHAR *buf; + int typecode; + HSTRING str; + ULONG count; + bool equals; + HRESULT hr; + + type_obj = p___abi_make_type_id(&desc); + todo_wine ok(type_obj != NULL, "got type_obj %p\n", type_obj); + if (!type_obj) + { + skip("__abi_make_type_id failed\n"); + return; + } + + todo_wine check_interface(type_obj, &IID_IInspectable); + todo_wine check_interface(type_obj, &IID_IClosable); + todo_wine check_interface(type_obj, &IID_IMarshal); + todo_wine check_interface(type_obj, &IID_IAgileObject); + todo_wine check_interface(type_obj, &IID_IEquatable); + todo_wine check_interface(type_obj, &IID_IPrintable); + + hr = IInspectable_GetRuntimeClassName(type_obj, &str); + ok(hr == S_OK, "got hr %#lx\n", hr); + buf = WindowsGetStringRawBuffer(str, NULL); + todo_wine ok(buf && !wcscmp(buf, L"Platform.Type"), "got buf %s\n", debugstr_w(buf)); + WindowsDeleteString(str); + + equals = p_platform_type_Equals_Object(type_obj, type_obj); + todo_wine ok(equals, "got equals %d\n", equals); + equals = p_platform_type_Equals_Object(type_obj, NULL); + todo_wine ok(!equals, "got equals %d\n", equals); + + typecode = p_platform_type_GetTypeCode(type_obj); + todo_wine ok(typecode == 0xdeadbeef, "got typecode %d\n", typecode); + + str = p_platform_type_ToString(type_obj); + buf = WindowsGetStringRawBuffer(str, NULL); + todo_wine ok(buf && !wcscmp(buf, L"foo"), "got buf %s\n", debugstr_w(buf)); + WindowsDeleteString(str); + + str = p_platform_type_get_FullName(type_obj); + todo_wine ok(str != NULL, "got str %p\n", str); + buf = WindowsGetStringRawBuffer(str, NULL); + todo_wine ok(buf && !wcscmp(buf, L"foo"), "got buf %s != %s\n", debugstr_w(buf), debugstr_w(L"foo")); + WindowsDeleteString(str); + + type_obj2 = p___abi_make_type_id(&desc); + todo_wine ok(type_obj2 != NULL, "got type_obj %p\n", type_obj); + ok(type_obj2 != type_obj, "got type_obj2 %p\n", type_obj2); + + todo_wine check_interface(type_obj2, &IID_IInspectable); + todo_wine check_interface(type_obj2, &IID_IClosable); + todo_wine check_interface(type_obj2, &IID_IMarshal); + todo_wine check_interface(type_obj2, &IID_IAgileObject); + todo_wine check_interface(type_obj2, &IID_IEquatable); + todo_wine check_interface(type_obj2, &IID_IPrintable); + + equals = p_platform_type_Equals_Object(type_obj2, type_obj); + todo_wine ok(equals, "got equals %d\n", equals); + + count = IInspectable_Release(type_obj); + ok(count == 0, "got count %lu\n", count); + + equals = p_platform_type_Equals_Object(NULL, NULL); + todo_wine ok(equals, "got equals %d\n", equals); + + type_obj = p___abi_make_type_id(&desc2); + todo_wine ok(type_obj != NULL, "got type_obj %p\n", type_obj); + + /* Platform::Type::Equals only seems to compare the value of the __abi_type_descriptor pointer. */ + equals = p_platform_type_Equals_Object(type_obj, type_obj2); + todo_wine ok(!equals, "got equals %d\n", equals); + + count = IInspectable_Release(type_obj); + ok(count == 0, "got count %lu\n", count); + + type_obj = p___abi_make_type_id(&desc3); + todo_wine ok(type_obj != NULL, "got type_obj %p\n", type_obj); + + equals = p_platform_type_Equals_Object(type_obj, type_obj2); + todo_wine ok(!equals, "got equals %d\n", equals); + + typecode = p_platform_type_GetTypeCode(type_obj); + todo_wine ok(typecode == 1, "got typecode %d\n", typecode); + + str = p_platform_type_ToString(type_obj); + ok(str == NULL, "got str %s\n", debugstr_hstring(str)); + + str = p_platform_type_get_FullName(type_obj); + ok(str == NULL, "got str %s\n", debugstr_hstring(str)); + + count = IInspectable_Release(type_obj); + ok(count == 0, "got count %lu\n", count); + + count = IInspectable_Release(type_obj2); + ok(count == 0, "got count %lu\n", count); + + typecode = p_platform_type_GetTypeCode(NULL); + ok(typecode == 0, "got typecode %d\n", typecode); +} + START_TEST(vccorlib) { if(!init()) @@ -621,4 +785,5 @@ START_TEST(vccorlib) test_Allocate(); test_AllocateWithWeakRef_inline(); test_AllocateWithWeakRef(); + test___abi_make_type_id(); } diff --git a/dlls/vccorlib140/vccorlib.c b/dlls/vccorlib140/vccorlib.c index e225e7ce5a7..9cccd2533d6 100644 --- a/dlls/vccorlib140/vccorlib.c +++ b/dlls/vccorlib140/vccorlib.c @@ -225,3 +225,51 @@ void __thiscall control_block_ReleaseTarget(struct control_block *weakref) if ((object = InterlockedCompareExchangePointer((void *)&weakref->object, NULL, weakref->object))) Free(object); } + +struct __abi_type_descriptor +{ + const WCHAR *name; + int type_id; +}; + +static const char *debugstr_abi_type_descriptor(const struct __abi_type_descriptor *desc) +{ + if (!desc) return "(null)"; + + return wine_dbg_sprintf("{%s, %d}", debugstr_w(desc->name), desc->type_id); +} + +void *WINAPI __abi_make_type_id(const struct __abi_type_descriptor *desc) +{ + FIXME("(%s) stub\n", debugstr_abi_type_descriptor(desc)); + + return NULL; +} + +bool __cdecl platform_type_Equals_Object(void *this, void *object) +{ + FIXME("(%p, %p) stub\n", this, object); + + return false; +} + +int __cdecl platform_type_GetTypeCode(void *this) +{ + FIXME("(%p) stub\n", this); + + return 0; +} + +HSTRING __cdecl platform_type_ToString(void *this) +{ + FIXME("(%p) stub\n", this); + + return NULL; +} + +HSTRING __cdecl platform_type_get_FullName(void *type) +{ + FIXME("(%p) stub\n", type); + + return NULL; +} diff --git a/dlls/vccorlib140/vccorlib140.spec b/dlls/vccorlib140/vccorlib140.spec index 8ff8b9b09fc..97f2b4d3cc7 100644 --- a/dlls/vccorlib140/vccorlib140.spec +++ b/dlls/vccorlib140/vccorlib140.spec @@ -12,8 +12,8 @@ @ stub -arch=win64 ?Equals@OnePhaseConstructedAttribute@CompilerServices@Runtime@Platform@@QE$AAA_NPE$AAVObject@4@@Z @ stub -arch=win32 ?Equals@STAThreadAttribute@Platform@@Q$AAA_NP$AAVObject@2@@Z @ stub -arch=win64 ?Equals@STAThreadAttribute@Platform@@QE$AAA_NPE$AAVObject@2@@Z -@ stub -arch=win32 ?Equals@Type@Platform@@U$AAA_NP$AAVObject@2@@Z -@ stub -arch=win64 ?Equals@Type@Platform@@UE$AAA_NPE$AAVObject@2@@Z +@ cdecl -arch=win32 ?Equals@Type@Platform@@U$AAA_NP$AAVObject@2@@Z(ptr ptr) platform_type_Equals_Object +@ cdecl -arch=win64 ?Equals@Type@Platform@@UE$AAA_NPE$AAVObject@2@@Z(ptr ptr) platform_type_Equals_Object @ stub -arch=win32 ?Equals@ValueType@Platform@@Q$AAA_NP$AAVObject@2@@Z @ stub -arch=win64 ?Equals@ValueType@Platform@@QE$AAA_NPE$AAVObject@2@@Z @ stub -arch=win32 ?Equals@char16@default@@QAA_NP$AAVObject@Platform@@@Z @@ -177,8 +177,8 @@ @ stub -arch=win64 ?GetType@uint64@default@@QEAAPE$AAVType@Platform@@XZ @ stub -arch=win32 ?GetType@uint8@default@@QAAP$AAVType@Platform@@XZ @ stub -arch=win64 ?GetType@uint8@default@@QEAAPE$AAVType@Platform@@XZ -@ stub -arch=win32 ?GetTypeCode@Type@Platform@@SA?AW4TypeCode@2@P$AAV12@@Z -@ stub -arch=win64 ?GetTypeCode@Type@Platform@@SA?AW4TypeCode@2@PE$AAV12@@Z +@ cdecl -arch=win32 ?GetTypeCode@Type@Platform@@SA?AW4TypeCode@2@P$AAV12@@Z(ptr) platform_type_GetTypeCode +@ cdecl -arch=win64 ?GetTypeCode@Type@Platform@@SA?AW4TypeCode@2@PE$AAV12@@Z(ptr) platform_type_GetTypeCode @ stub -arch=i386 ?GetWeakReference@Details@Platform@@YGPAU__abi_IUnknown@@Q$ADVObject@2@@Z @ stub -arch=arm ?GetWeakReference@Details@Platform@@YAPAU__abi_IUnknown@@Q$ADVObject@2@@Z @ stub -arch=win64 ?GetWeakReference@Details@Platform@@YAPEAU__abi_IUnknown@@QE$ADVObject@2@@Z @@ -246,8 +246,8 @@ @ stub -arch=win64 ?<Dispose>@String@Platform@@UE$AAAXXZ @ stub -arch=win32 ??0FailureException@Platform@@Q$AAA@P$AAVString@1@@Z @ stub -arch=win64 ??0FailureException@Platform@@QE$AAA@PE$AAVString@1@@Z -@ stub -arch=win32 ?ToString@Type@Platform@@U$AAAP$AAVString@2@XZ -@ stub -arch=win64 ?ToString@Type@Platform@@UE$AAAPE$AAVString@2@XZ +@ cdecl -arch=win32 ?ToString@Type@Platform@@U$AAAP$AAVString@2@XZ(ptr) platform_type_ToString +@ cdecl -arch=win64 ?ToString@Type@Platform@@UE$AAAPE$AAVString@2@XZ(ptr) platform_type_ToString @ stub -arch=win32 ?ToString@ValueType@Platform@@Q$AAAP$AAVString@2@XZ @ stub -arch=win64 ?ToString@ValueType@Platform@@QE$AAAPE$AAVString@2@XZ @ stub -arch=win32 ?ToString@char16@default@@QAAP$AAVString@Platform@@XZ @@ -347,9 +347,9 @@ @ stub -arch=i386 ?__abi_cast_String_to_Object@__abi_details@@YGP$AAVObject@Platform@@P$AAVString@3@@Z @ stub -arch=arm ?__abi_cast_String_to_Object@__abi_details@@YAP$AAVObject@Platform@@P$AAVString@3@@Z @ stub -arch=win64 ?__abi_cast_String_to_Object@__abi_details@@YAPE$AAVObject@Platform@@PE$AAVString@3@@Z -@ stub -arch=i386 ?__abi_make_type_id@@YGP$AAVType@Platform@@ABU__abi_type_descriptor@@@Z -@ stub -arch=arm ?__abi_make_type_id@@YAP$AAVType@Platform@@ABU__abi_type_descriptor@@@Z -@ stub -arch=win64 ?__abi_make_type_id@@YAPE$AAVType@Platform@@AEBU__abi_type_descriptor@@@Z +@ stdcall -arch=i386 ?__abi_make_type_id@@YGP$AAVType@Platform@@ABU__abi_type_descriptor@@@Z(ptr) __abi_make_type_id +@ stdcall -arch=arm ?__abi_make_type_id@@YAP$AAVType@Platform@@ABU__abi_type_descriptor@@@Z(ptr) __abi_make_type_id +@ stdcall -arch=win64 ?__abi_make_type_id@@YAPE$AAVType@Platform@@AEBU__abi_type_descriptor@@@Z(ptr) __abi_make_type_id @ stub -arch=win32 ??0IntPtr@Platform@@QAA@PAX@Z @ stub -arch=win64 ??0IntPtr@Platform@@QEAA@PEAX@Z @ stub -arch=i386 ?__abi_translateCurrentException@@YGJ_N@Z @@ -365,8 +365,8 @@ @ stub ?get@CurrentAllocationId@Heap@Details@Platform@@SAHXZ @ stub ?get@Empty@Rect@Foundation@Windows@@SA?AV234@XZ @ stub ?get@Empty@Size@Foundation@Windows@@SA?AV234@XZ -@ stub -arch=win32 ?get@FullName@Type@Platform@@Q$AAAP$AAVString@3@XZ -@ stub -arch=win64 ?get@FullName@Type@Platform@@QE$AAAPE$AAVString@3@XZ +@ cdecl -arch=win32 ?get@FullName@Type@Platform@@Q$AAAP$AAVString@3@XZ(ptr) platform_type_get_FullName +@ cdecl -arch=win64 ?get@FullName@Type@Platform@@QE$AAAPE$AAVString@3@XZ(ptr) platform_type_get_FullName @ stub -arch=win32 ?get@HasInverse@Matrix3D@Media3D@Media@Xaml@UI@Windows@@QAA_NXZ @ stub -arch=win64 ?get@HasInverse@Matrix3D@Media3D@Media@Xaml@UI@Windows@@QEAA_NXZ @ stub -arch=win32 ??0InvalidArgumentException@Platform@@Q$AAA@P$AAVString@1@@Z
From: Vibhav Pant vibhavp@gmail.com
--- dlls/vccorlib140/private.h | 54 ++++++++++++ dlls/vccorlib140/tests/vccorlib.c | 32 +++---- dlls/vccorlib140/vccorlib.c | 139 +++++++++++++++++++++++++++++- 3 files changed, 207 insertions(+), 18 deletions(-) create mode 100644 dlls/vccorlib140/private.h
diff --git a/dlls/vccorlib140/private.h b/dlls/vccorlib140/private.h new file mode 100644 index 00000000000..d87ba1ac91d --- /dev/null +++ b/dlls/vccorlib140/private.h @@ -0,0 +1,54 @@ +/* + * Copyright 2025 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define DEFINE_IINSPECTABLE_(pfx, iface_type, impl_type, impl_from, iface_mem, expr) \ + static inline impl_type *impl_from(iface_type *iface) { return CONTAINING_RECORD(iface, impl_type, iface_mem); } \ + static HRESULT WINAPI pfx##_QueryInterface(iface_type *iface, const GUID *iid, void **out) \ + { \ + impl_type *impl = impl_from(iface); \ + return IInspectable_QueryInterface((IInspectable *)(expr), iid, out); \ + } \ + static ULONG WINAPI pfx##_AddRef(iface_type *iface) \ + { \ + impl_type *impl = impl_from(iface); \ + return IInspectable_AddRef((IInspectable *)(expr)); \ + } \ + static ULONG WINAPI pfx##_Release(iface_type *iface) \ + { \ + impl_type *impl = impl_from(iface); \ + return IInspectable_Release((IInspectable *)(expr)); \ + } \ + static HRESULT WINAPI pfx##_GetIids(iface_type *iface, ULONG *iid_count, IID **iids) \ + { \ + impl_type *impl = impl_from(iface); \ + return IInspectable_GetIids((IInspectable *)(expr), iid_count, iids); \ + } \ + static HRESULT WINAPI pfx##_GetRuntimeClassName(iface_type *iface, HSTRING *class_name) \ + { \ + impl_type *impl = impl_from(iface); \ + return IInspectable_GetRuntimeClassName((IInspectable *)(expr), class_name); \ + } \ + static HRESULT WINAPI pfx##_GetTrustLevel(iface_type *iface, TrustLevel *trust_level) \ + { \ + impl_type *impl = impl_from(iface); \ + return IInspectable_GetTrustLevel((IInspectable *)(expr), trust_level); \ + } +#define DEFINE_IINSPECTABLE(pfx, iface_type, impl_type, base_iface) \ + DEFINE_IINSPECTABLE_(pfx, iface_type, impl_type, impl_from_##iface_type, iface_type##_iface, &impl->base_iface) +#define DEFINE_IINSPECTABLE_OUTER(pfx, iface_type, impl_type, outer_iface) \ + DEFINE_IINSPECTABLE_(pfx, iface_type, impl_type, impl_from_##iface_type, iface_type##_iface, impl->outer_iface) diff --git a/dlls/vccorlib140/tests/vccorlib.c b/dlls/vccorlib140/tests/vccorlib.c index bef246f2760..cafe95faf77 100644 --- a/dlls/vccorlib140/tests/vccorlib.c +++ b/dlls/vccorlib140/tests/vccorlib.c @@ -680,30 +680,30 @@ static void test___abi_make_type_id(void) HRESULT hr;
type_obj = p___abi_make_type_id(&desc); - todo_wine ok(type_obj != NULL, "got type_obj %p\n", type_obj); + ok(type_obj != NULL, "got type_obj %p\n", type_obj); if (!type_obj) { skip("__abi_make_type_id failed\n"); return; }
- todo_wine check_interface(type_obj, &IID_IInspectable); - todo_wine check_interface(type_obj, &IID_IClosable); - todo_wine check_interface(type_obj, &IID_IMarshal); - todo_wine check_interface(type_obj, &IID_IAgileObject); + check_interface(type_obj, &IID_IInspectable); + check_interface(type_obj, &IID_IClosable); + check_interface(type_obj, &IID_IMarshal); + check_interface(type_obj, &IID_IAgileObject); todo_wine check_interface(type_obj, &IID_IEquatable); todo_wine check_interface(type_obj, &IID_IPrintable);
hr = IInspectable_GetRuntimeClassName(type_obj, &str); ok(hr == S_OK, "got hr %#lx\n", hr); buf = WindowsGetStringRawBuffer(str, NULL); - todo_wine ok(buf && !wcscmp(buf, L"Platform.Type"), "got buf %s\n", debugstr_w(buf)); + ok(buf && !wcscmp(buf, L"Platform.Type"), "got buf %s\n", debugstr_w(buf)); WindowsDeleteString(str);
equals = p_platform_type_Equals_Object(type_obj, type_obj); todo_wine ok(equals, "got equals %d\n", equals); equals = p_platform_type_Equals_Object(type_obj, NULL); - todo_wine ok(!equals, "got equals %d\n", equals); + ok(!equals, "got equals %d\n", equals);
typecode = p_platform_type_GetTypeCode(type_obj); todo_wine ok(typecode == 0xdeadbeef, "got typecode %d\n", typecode); @@ -720,13 +720,13 @@ static void test___abi_make_type_id(void) WindowsDeleteString(str);
type_obj2 = p___abi_make_type_id(&desc); - todo_wine ok(type_obj2 != NULL, "got type_obj %p\n", type_obj); + ok(type_obj2 != NULL, "got type_obj %p\n", type_obj); ok(type_obj2 != type_obj, "got type_obj2 %p\n", type_obj2);
- todo_wine check_interface(type_obj2, &IID_IInspectable); - todo_wine check_interface(type_obj2, &IID_IClosable); - todo_wine check_interface(type_obj2, &IID_IMarshal); - todo_wine check_interface(type_obj2, &IID_IAgileObject); + check_interface(type_obj2, &IID_IInspectable); + check_interface(type_obj2, &IID_IClosable); + check_interface(type_obj2, &IID_IMarshal); + check_interface(type_obj2, &IID_IAgileObject); todo_wine check_interface(type_obj2, &IID_IEquatable); todo_wine check_interface(type_obj2, &IID_IPrintable);
@@ -740,20 +740,20 @@ static void test___abi_make_type_id(void) todo_wine ok(equals, "got equals %d\n", equals);
type_obj = p___abi_make_type_id(&desc2); - todo_wine ok(type_obj != NULL, "got type_obj %p\n", type_obj); + ok(type_obj != NULL, "got type_obj %p\n", type_obj);
/* Platform::Type::Equals only seems to compare the value of the __abi_type_descriptor pointer. */ equals = p_platform_type_Equals_Object(type_obj, type_obj2); - todo_wine ok(!equals, "got equals %d\n", equals); + ok(!equals, "got equals %d\n", equals);
count = IInspectable_Release(type_obj); ok(count == 0, "got count %lu\n", count);
type_obj = p___abi_make_type_id(&desc3); - todo_wine ok(type_obj != NULL, "got type_obj %p\n", type_obj); + ok(type_obj != NULL, "got type_obj %p\n", type_obj);
equals = p_platform_type_Equals_Object(type_obj, type_obj2); - todo_wine ok(!equals, "got equals %d\n", equals); + ok(!equals, "got equals %d\n", equals);
typecode = p_platform_type_GetTypeCode(type_obj); todo_wine ok(typecode == 1, "got typecode %d\n", typecode); diff --git a/dlls/vccorlib140/vccorlib.c b/dlls/vccorlib140/vccorlib.c index 9cccd2533d6..cdbf42d7fef 100644 --- a/dlls/vccorlib140/vccorlib.c +++ b/dlls/vccorlib140/vccorlib.c @@ -25,9 +25,13 @@ #include "roapi.h" #include "weakreference.h" #include "winstring.h" +#define WIDL_using_Windows_Foundation +#include "windows.foundation.h" #include "wine/asm.h" #include "wine/debug.h"
+#include "private.h" + WINE_DEFAULT_DEBUG_CHANNEL(vccorlib);
HRESULT __cdecl InitializeData(int type) @@ -232,6 +236,118 @@ struct __abi_type_descriptor int type_id; };
+struct platform_type +{ + IInspectable IInspectable_iface; + IClosable IClosable_iface; + IUnknown *marshal; + const struct __abi_type_descriptor *desc; + LONG ref; +}; + +static inline struct platform_type *impl_from_IInspectable(IInspectable *iface) +{ + return CONTAINING_RECORD(iface, struct platform_type, IInspectable_iface); +} + +static HRESULT WINAPI platform_type_QueryInterface(IInspectable *iface, const GUID *iid, void **out) +{ + struct platform_type *impl = impl_from_IInspectable(iface); + + TRACE("(%p, %s, %p)\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_IUnknown) || + IsEqualGUID(iid, &IID_IInspectable) || + IsEqualGUID(iid, &IID_IClosable) || + IsEqualGUID(iid, &IID_IAgileObject)) + { + IClosable_AddRef((*out = &impl->IInspectable_iface)); + return S_OK; + } + if (IsEqualGUID(iid, &IID_IMarshal)) + return IUnknown_QueryInterface(impl->marshal, iid, out); + + ERR("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI platform_type_AddRef(IInspectable *iface) +{ + struct platform_type *impl = impl_from_IInspectable(iface); + TRACE("(%p)\n", iface); + return InterlockedIncrement(&impl->ref); +} + +static ULONG WINAPI platform_type_Release(IInspectable *iface) +{ + struct platform_type *impl = impl_from_IInspectable(iface); + ULONG ref = InterlockedDecrement(&impl->ref); + + TRACE("(%p)\n", iface); + + if (!ref) + { + IUnknown_Release(impl->marshal); + Free(impl); + } + return ref; +} + +static HRESULT WINAPI platform_type_GetIids(IInspectable *iface, ULONG *count, GUID **iids) +{ + FIXME("(%p, %p, %p) stub\n", iface, count, iids); + return E_NOTIMPL; +} + +static HRESULT WINAPI platform_type_GetRuntimeClassName(IInspectable *iface, HSTRING *name) +{ + static const WCHAR *str = L"Platform.Type"; + + TRACE("(%p, %p)\n", iface, name); + return WindowsCreateString(str, wcslen(str), name); +} + +static HRESULT WINAPI platform_type_GetTrustLevel(IInspectable *iface, TrustLevel *level) +{ + FIXME("(%p, %p) stub\n", iface, level); + return E_NOTIMPL; +} + +static const IInspectableVtbl platform_type_vtbl = +{ + /* IUnknown */ + platform_type_QueryInterface, + platform_type_AddRef, + platform_type_Release, + /* IInspectable */ + platform_type_GetIids, + platform_type_GetRuntimeClassName, + platform_type_GetTrustLevel +}; + +DEFINE_IINSPECTABLE(platform_type_closable, IClosable, struct platform_type, IInspectable_iface); + +static HRESULT WINAPI platform_type_Close(IClosable *iface) +{ + FIXME("(%p) stub\n", iface); + return E_NOTIMPL; +} + +static const IClosableVtbl platform_type_closable_vtbl = +{ + /* IUnknown */ + platform_type_closable_QueryInterface, + platform_type_closable_AddRef, + platform_type_closable_Release, + /* IInspectable */ + platform_type_closable_GetIids, + platform_type_closable_GetRuntimeClassName, + platform_type_closable_GetTrustLevel, + /* ICloseable */ + platform_type_Close +}; + static const char *debugstr_abi_type_descriptor(const struct __abi_type_descriptor *desc) { if (!desc) return "(null)"; @@ -241,9 +357,28 @@ static const char *debugstr_abi_type_descriptor(const struct __abi_type_descript
void *WINAPI __abi_make_type_id(const struct __abi_type_descriptor *desc) { - FIXME("(%s) stub\n", debugstr_abi_type_descriptor(desc)); + /* TODO: + * Emit RTTI for Platform::Type. + * Implement IEquatable and IPrintable. + * Throw a COMException if CoCreateFreeThreadedMarshaler fails. */ + struct platform_type *obj; + HRESULT hr;
- return NULL; + TRACE("(%s)\n", debugstr_abi_type_descriptor(desc)); + + obj = Allocate(sizeof(*obj)); + obj->IInspectable_iface.lpVtbl = &platform_type_vtbl; + obj->IClosable_iface.lpVtbl = &platform_type_closable_vtbl; + obj->desc = desc; + obj->ref = 1; + hr = CoCreateFreeThreadedMarshaler((IUnknown *)&obj->IInspectable_iface, &obj->marshal); + if (FAILED(hr)) + { + FIXME("CoCreateFreeThreadedMarshaler failed: %#lx\n", hr); + Free(obj); + return NULL; + } + return &obj->IInspectable_iface; }
bool __cdecl platform_type_Equals_Object(void *this, void *object)
From: Vibhav Pant vibhavp@gmail.com
--- dlls/vccorlib140/tests/vccorlib.c | 19 ++++++++----------- dlls/vccorlib140/vccorlib.c | 31 +++++++++++++++++++------------ 2 files changed, 27 insertions(+), 23 deletions(-)
diff --git a/dlls/vccorlib140/tests/vccorlib.c b/dlls/vccorlib140/tests/vccorlib.c index cafe95faf77..2f32eee36d2 100644 --- a/dlls/vccorlib140/tests/vccorlib.c +++ b/dlls/vccorlib140/tests/vccorlib.c @@ -701,22 +701,22 @@ static void test___abi_make_type_id(void) WindowsDeleteString(str);
equals = p_platform_type_Equals_Object(type_obj, type_obj); - todo_wine ok(equals, "got equals %d\n", equals); + ok(equals, "got equals %d\n", equals); equals = p_platform_type_Equals_Object(type_obj, NULL); ok(!equals, "got equals %d\n", equals);
typecode = p_platform_type_GetTypeCode(type_obj); - todo_wine ok(typecode == 0xdeadbeef, "got typecode %d\n", typecode); + ok(typecode == 0xdeadbeef, "got typecode %d\n", typecode);
str = p_platform_type_ToString(type_obj); buf = WindowsGetStringRawBuffer(str, NULL); - todo_wine ok(buf && !wcscmp(buf, L"foo"), "got buf %s\n", debugstr_w(buf)); + ok(buf && !wcscmp(buf, L"foo"), "got buf %s\n", debugstr_w(buf)); WindowsDeleteString(str);
str = p_platform_type_get_FullName(type_obj); - todo_wine ok(str != NULL, "got str %p\n", str); + ok(str != NULL, "got str %p\n", str); buf = WindowsGetStringRawBuffer(str, NULL); - todo_wine ok(buf && !wcscmp(buf, L"foo"), "got buf %s != %s\n", debugstr_w(buf), debugstr_w(L"foo")); + ok(buf && !wcscmp(buf, L"foo"), "got buf %s != %s\n", debugstr_w(buf), debugstr_w(L"foo")); WindowsDeleteString(str);
type_obj2 = p___abi_make_type_id(&desc); @@ -731,13 +731,13 @@ static void test___abi_make_type_id(void) todo_wine check_interface(type_obj2, &IID_IPrintable);
equals = p_platform_type_Equals_Object(type_obj2, type_obj); - todo_wine ok(equals, "got equals %d\n", equals); + ok(equals, "got equals %d\n", equals);
count = IInspectable_Release(type_obj); ok(count == 0, "got count %lu\n", count);
equals = p_platform_type_Equals_Object(NULL, NULL); - todo_wine ok(equals, "got equals %d\n", equals); + ok(equals, "got equals %d\n", equals);
type_obj = p___abi_make_type_id(&desc2); ok(type_obj != NULL, "got type_obj %p\n", type_obj); @@ -756,7 +756,7 @@ static void test___abi_make_type_id(void) ok(!equals, "got equals %d\n", equals);
typecode = p_platform_type_GetTypeCode(type_obj); - todo_wine ok(typecode == 1, "got typecode %d\n", typecode); + ok(typecode == 1, "got typecode %d\n", typecode);
str = p_platform_type_ToString(type_obj); ok(str == NULL, "got str %s\n", debugstr_hstring(str)); @@ -769,9 +769,6 @@ static void test___abi_make_type_id(void)
count = IInspectable_Release(type_obj2); ok(count == 0, "got count %lu\n", count); - - typecode = p_platform_type_GetTypeCode(NULL); - ok(typecode == 0, "got typecode %d\n", typecode); }
START_TEST(vccorlib) diff --git a/dlls/vccorlib140/vccorlib.c b/dlls/vccorlib140/vccorlib.c index cdbf42d7fef..0dffa02572e 100644 --- a/dlls/vccorlib140/vccorlib.c +++ b/dlls/vccorlib140/vccorlib.c @@ -381,30 +381,37 @@ void *WINAPI __abi_make_type_id(const struct __abi_type_descriptor *desc) return &obj->IInspectable_iface; }
-bool __cdecl platform_type_Equals_Object(void *this, void *object) +bool __cdecl platform_type_Equals_Object(struct platform_type *this, struct platform_type *object) { - FIXME("(%p, %p) stub\n", this, object); + TRACE("(%p, %p)\n", this, object);
- return false; + return this == object || (this && object && this->desc == object->desc); }
-int __cdecl platform_type_GetTypeCode(void *this) +int __cdecl platform_type_GetTypeCode(struct platform_type *this) { - FIXME("(%p) stub\n", this); + TRACE("(%p)\n", this);
- return 0; + return this->desc->type_id; }
-HSTRING __cdecl platform_type_ToString(void *this) +HSTRING __cdecl platform_type_ToString(struct platform_type *this) { - FIXME("(%p) stub\n", this); + HSTRING str = NULL; + HRESULT hr; + + TRACE("(%p)\n", this);
- return NULL; + /* TODO: Throw a COMException if this fails */ + hr = WindowsCreateString(this->desc->name, this->desc->name ? wcslen(this->desc->name) : 0, &str); + if (FAILED(hr)) + FIXME("WindowsCreateString failed: %#lx\n", hr); + return str; }
-HSTRING __cdecl platform_type_get_FullName(void *type) +HSTRING __cdecl platform_type_get_FullName(struct platform_type *type) { - FIXME("(%p) stub\n", type); + TRACE("(%p)\n", type);
- return NULL; + return platform_type_ToString(type); }
From: Vibhav Pant vibhavp@gmail.com
--- dlls/vccorlib140/tests/vccorlib.c | 195 ++++++++++++++++++++++++++++++ dlls/vccorlib140/vccorlib.c | 7 ++ dlls/vccorlib140/vccorlib140.spec | 6 +- 3 files changed, 205 insertions(+), 3 deletions(-)
diff --git a/dlls/vccorlib140/tests/vccorlib.c b/dlls/vccorlib140/tests/vccorlib.c index 2f32eee36d2..8e7b6bce481 100644 --- a/dlls/vccorlib140/tests/vccorlib.c +++ b/dlls/vccorlib140/tests/vccorlib.c @@ -125,6 +125,7 @@ static bool (__cdecl *p_platform_type_Equals_Object)(void *, void *); static int (__cdecl *p_platform_type_GetTypeCode)(void *); static HSTRING (__cdecl *p_platform_type_ToString)(void *); static HSTRING (__cdecl *p_platform_type_get_FullName)(void *); +static void *(WINAPI *pCreateValue)(int type, const void *);
static BOOL init(void) { @@ -158,6 +159,7 @@ static BOOL init(void) "?GetTypeCode@Type@Platform@@SA?AW4TypeCode@2@P$AAV12@@Z"); p_platform_type_ToString = (void *)GetProcAddress(hmod, "?ToString@Type@Platform@@U$AAAP$AAVString@2@XZ"); p_platform_type_get_FullName = (void *)GetProcAddress(hmod, "?get@FullName@Type@Platform@@Q$AAAP$AAVString@3@XZ"); + pCreateValue = (void *)GetProcAddress(hmod, "?CreateValue@Details@Platform@@YAP$AAVObject@2@W4TypeCode@2@PBX@Z"); #else if (sizeof(void *) == 8) { @@ -177,6 +179,8 @@ static BOOL init(void) p_platform_type_ToString = (void *)GetProcAddress(hmod, "?ToString@Type@Platform@@UE$AAAPE$AAVString@2@XZ"); p_platform_type_get_FullName = (void *)GetProcAddress(hmod, "?get@FullName@Type@Platform@@QE$AAAPE$AAVString@3@XZ"); + pCreateValue = (void *)GetProcAddress(hmod, + "?CreateValue@Details@Platform@@YAPE$AAVObject@2@W4TypeCode@2@PEBX@Z"); } else { @@ -195,6 +199,8 @@ static BOOL init(void) p_platform_type_ToString = (void *)GetProcAddress(hmod, "?ToString@Type@Platform@@U$AAAP$AAVString@2@XZ"); p_platform_type_get_FullName = (void *)GetProcAddress(hmod, "?get@FullName@Type@Platform@@Q$AAAP$AAVString@3@XZ"); + pCreateValue = (void *)GetProcAddress(hmod, + "?CreateValue@Details@Platform@@YGP$AAVObject@2@W4TypeCode@2@PBX@Z"); } #endif ok(pGetActivationFactoryByPCWSTR != NULL, "GetActivationFactoryByPCWSTR not available\n"); @@ -208,6 +214,7 @@ static BOOL init(void) ok(p_platform_type_GetTypeCode != NULL, "Platform::Type::GetTypeCode not available\n"); ok(p_platform_type_ToString != NULL, "Platform::Type::ToString not available\n"); ok(p_platform_type_get_FullName != NULL, "Platform::Type::FullName not available\n"); + ok(pCreateValue != NULL, "CreateValue not available\n");
init_thiscall_thunk();
@@ -771,6 +778,193 @@ static void test___abi_make_type_id(void) ok(count == 0, "got count %lu\n", count); }
+enum typecode +{ + TYPECODE_BOOLEAN = 3, + TYPECODE_CHAR16 = 4, + TYPECODE_UINT8 = 6, + TYPECODE_INT16 = 7, + TYPECODE_UINT16 = 8, + TYPECODE_INT32 = 9, + TYPECODE_UINT32 = 10, + TYPECODE_INT64 = 11, + TYPECODE_UINT64 = 12, + TYPECODE_SINGLE = 13, + TYPECODE_DOUBLE = 14, + TYPECODE_DATETIME = 16, + TYPECODE_STRING = 18, + TYPECODE_TIMESPAN = 19, + TYPECODE_POINT = 20, + TYPECODE_SIZE = 21, + TYPECODE_RECT = 22, + TYPECODE_GUID = 23, +}; + +static void test_CreateValue(void) +{ + union value { + UINT8 uint8; + UINT16 uint16; + UINT32 uint32; + UINT64 uint64; + FLOAT single; + DOUBLE dbl; + boolean boolean; + GUID guid; + DateTime time; + TimeSpan span; + Point point; + Size size; + Rect rect; + }; + static const struct { + int typecode; + union value value; + PropertyType exp_winrt_type; + SIZE_T size; + } test_cases[] = { + {TYPECODE_BOOLEAN, {.boolean = true}, PropertyType_Boolean, sizeof(boolean)}, + {TYPECODE_CHAR16, {.uint16 = 0xbeef}, PropertyType_Char16, sizeof(UINT16)}, + {TYPECODE_UINT8, {.uint8 = 0xbe}, PropertyType_UInt8, sizeof(UINT8)}, + {TYPECODE_INT16, {.uint16 = 0xbeef}, PropertyType_Int16, sizeof(INT16)}, + {TYPECODE_UINT16, {.uint16 = 0xbeef}, PropertyType_UInt16, sizeof(UINT16)}, + {TYPECODE_INT32, {.uint32 = 0xdeadbeef}, PropertyType_Int32, sizeof(INT32)}, + {TYPECODE_UINT32, {.uint32 = 0xdeadbeef}, PropertyType_UInt32, sizeof(UINT32)}, + {TYPECODE_INT64, {.uint64 = 0xdeadbeefdeadbeef}, PropertyType_Int64, sizeof(INT64)}, + {TYPECODE_UINT64, {.uint64 = 0xdeadbeefdeadbeef}, PropertyType_UInt64, sizeof(UINT64)}, + {TYPECODE_SINGLE, {.single = 2.71828}, PropertyType_Single, sizeof(FLOAT)}, + {TYPECODE_DOUBLE, {.dbl = 2.7182818284}, PropertyType_Double, sizeof(DOUBLE)}, + {TYPECODE_DATETIME, {.time = {0xdeadbeefdeadbeef}}, PropertyType_DateTime, sizeof(DateTime)}, + {TYPECODE_TIMESPAN, {.span = {0xdeadbeefdeadbeef}}, PropertyType_TimeSpan, sizeof(TimeSpan)}, + {TYPECODE_POINT, {.point = {2.71828, 3.14159}}, PropertyType_Point, sizeof(Point)}, + {TYPECODE_SIZE, {.size = {2.71828, 3.14159}}, PropertyType_Size, sizeof(Size)}, + {TYPECODE_RECT, {.rect = {2.71828, 3.14159, 23.140692, 0.20787}}, PropertyType_Rect, sizeof(Rect)}, + {TYPECODE_GUID, {.guid = IID_IInspectable}, PropertyType_Guid, sizeof(GUID)}, + }; + static const int null_typecodes[] = {0, 1, 2, 5,15, 17}; + PropertyType type; + const WCHAR *buf; + ULONG count, i; + HSTRING str; + HRESULT hr; + void *obj; + + hr = pInitializeData(1); + ok(hr == S_OK, "got hr %#lx.\n", hr); + + for (i = 0; i < ARRAY_SIZE(test_cases); i++) + { + union value value = {0}; + type = PropertyType_Empty; + + winetest_push_context("test_cases[%lu]", i); + + obj = pCreateValue(test_cases[i].typecode, &test_cases[i].value); + todo_wine ok(obj != NULL, "got value %p\n", obj); + if (!obj) continue; + + check_interface(obj, &IID_IInspectable); + check_interface(obj, &IID_IPropertyValue); + + hr = IPropertyValue_get_Type(obj, &type); + ok(hr == S_OK, "got hr %#lx\n", hr); + ok(type == test_cases[i].exp_winrt_type, "got type %d != %d\n", type, test_cases[i].exp_winrt_type); + + switch (test_cases[i].exp_winrt_type) + { + case PropertyType_Boolean: + hr = IPropertyValue_GetBoolean(obj, &value.boolean); + break; + case PropertyType_Char16: + hr = IPropertyValue_GetChar16(obj, &value.uint16); + break; + case PropertyType_UInt8: + hr = IPropertyValue_GetUInt8(obj, &value.uint8); + break; + case PropertyType_Int16: + hr = IPropertyValue_GetInt16(obj, (INT16 *)&value.uint16); + break; + case PropertyType_UInt16: + hr = IPropertyValue_GetUInt16(obj, &value.uint16); + break; + case PropertyType_Int32: + hr = IPropertyValue_GetInt32(obj, (INT32 *)&value.uint32); + break; + case PropertyType_UInt32: + hr = IPropertyValue_GetUInt32(obj, &value.uint32); + break; + case PropertyType_Int64: + hr = IPropertyValue_GetInt64(obj, (INT64 *)&value.uint64); + break; + case PropertyType_UInt64: + hr = IPropertyValue_GetUInt64(obj, &value.uint64); + break; + case PropertyType_Single: + hr = IPropertyValue_GetSingle(obj, &value.single); + break; + case PropertyType_Double: + hr = IPropertyValue_GetDouble(obj, &value.dbl); + break; + case PropertyType_DateTime: + hr = IPropertyValue_GetDateTime(obj, &value.time); + break; + case PropertyType_TimeSpan: + hr = IPropertyValue_GetTimeSpan(obj, &value.span); + break; + case PropertyType_Point: + hr = IPropertyValue_GetPoint(obj, &value.point); + break; + case PropertyType_Size: + hr = IPropertyValue_GetSize(obj, &value.size); + break; + case PropertyType_Rect: + hr = IPropertyValue_GetRect(obj, &value.rect); + break; + case PropertyType_Guid: + hr = IPropertyValue_GetGuid(obj, &value.guid); + break; + DEFAULT_UNREACHABLE; + } + + ok(hr == S_OK, "got hr %#lx\n", hr); + ok(!memcmp(&test_cases[i].value, &value, test_cases[i].size), "got unexpected value\n"); + count = IPropertyValue_Release(obj); + ok(count == 0, "got count %lu\n", count); + + winetest_pop_context(); + } + + hr = WindowsCreateString(L"foo", 3, &str); + ok(hr == S_OK, "got hr %#lx\n", hr); + obj = pCreateValue(TYPECODE_STRING, &str); + WindowsDeleteString(str); + todo_wine ok(obj != NULL || broken(obj == NULL), "got obj %p\n", obj); /* Returns NULL on i386 Windows 10. */ + if (obj) + { + type = PropertyType_Empty; + hr = IPropertyValue_get_Type(obj, &type); + ok(hr == S_OK, "got hr %#lx\n", hr); + ok(type == PropertyType_String, "got type %d\n", type); + str = NULL; + hr = IPropertyValue_GetString(obj, &str); + ok(hr == S_OK, "got hr %#lx\n", hr); + ok(str != NULL, "got str %p\n", str); + buf = WindowsGetStringRawBuffer(str, NULL); + ok(buf && !wcscmp(buf, L"foo"), "got buf %s\n", debugstr_w(buf)); + WindowsDeleteString(str); + count = IPropertyValue_Release(obj); + ok(count == 0, "got count %lu\n", count); + } + + for (i = 0; i < ARRAY_SIZE(null_typecodes); i++) + { + obj = pCreateValue(null_typecodes[i], &i); + ok(obj == NULL, "got obj %p\n", obj); + } + + pUninitializeData(0); +} + START_TEST(vccorlib) { if(!init()) @@ -783,4 +977,5 @@ START_TEST(vccorlib) test_AllocateWithWeakRef_inline(); test_AllocateWithWeakRef(); test___abi_make_type_id(); + test_CreateValue(); } diff --git a/dlls/vccorlib140/vccorlib.c b/dlls/vccorlib140/vccorlib.c index 0dffa02572e..d62366edb99 100644 --- a/dlls/vccorlib140/vccorlib.c +++ b/dlls/vccorlib140/vccorlib.c @@ -415,3 +415,10 @@ HSTRING __cdecl platform_type_get_FullName(struct platform_type *type)
return platform_type_ToString(type); } + +void *WINAPI CreateValue(int typecode, const void *val) +{ + FIXME("(%d, %p) stub\n", typecode, val); + + return NULL; +} diff --git a/dlls/vccorlib140/vccorlib140.spec b/dlls/vccorlib140/vccorlib140.spec index 97f2b4d3cc7..faecd3b8519 100644 --- a/dlls/vccorlib140/vccorlib140.spec +++ b/dlls/vccorlib140/vccorlib140.spec @@ -519,9 +519,9 @@ @ stub -arch=win64 ?CreateException@Exception@Platform@@SAPE$AAV12@H@Z @ stub -arch=win32 ?CreateException@Exception@Platform@@SAP$AAV12@HP$AAVString@2@@Z @ stub -arch=win64 ?CreateException@Exception@Platform@@SAPE$AAV12@HPE$AAVString@2@@Z -@ stub -arch=i386 ?CreateValue@Details@Platform@@YGP$AAVObject@2@W4TypeCode@2@PBX@Z -@ stub -arch=arm ?CreateValue@Details@Platform@@YAP$AAVObject@2@W4TypeCode@2@PBX@Z -@ stub -arch=win64 ?CreateValue@Details@Platform@@YAPE$AAVObject@2@W4TypeCode@2@PEBX@Z +@ stdcall -arch=i386 ?CreateValue@Details@Platform@@YGP$AAVObject@2@W4TypeCode@2@PBX@Z(long ptr) CreateValue +@ stdcall -arch=arm ?CreateValue@Details@Platform@@YAP$AAVObject@2@W4TypeCode@2@PBX@Z(long ptr) CreateValue +@ stdcall -arch=win64 ?CreateValue@Details@Platform@@YAPE$AAVObject@2@W4TypeCode@2@PEBX@Z(long ptr) CreateValue @ stub ?EnableFactoryCache@@YAXXZ @ stub -arch=win32 ?EnumerateAllocatedObjects@Heap@Details@Platform@@SAXP$AAVHeapEntryHandler@23@@Z @ stub -arch=win64 ?EnumerateAllocatedObjects@Heap@Details@Platform@@SAXPE$AAVHeapEntryHandler@23@@Z
From: Vibhav Pant vibhavp@gmail.com
--- dlls/vccorlib140/tests/vccorlib.c | 5 +- dlls/vccorlib140/vccorlib.c | 130 +++++++++++++++++++++++++++++- 2 files changed, 130 insertions(+), 5 deletions(-)
diff --git a/dlls/vccorlib140/tests/vccorlib.c b/dlls/vccorlib140/tests/vccorlib.c index 8e7b6bce481..7750794d93e 100644 --- a/dlls/vccorlib140/tests/vccorlib.c +++ b/dlls/vccorlib140/tests/vccorlib.c @@ -860,8 +860,7 @@ static void test_CreateValue(void) winetest_push_context("test_cases[%lu]", i);
obj = pCreateValue(test_cases[i].typecode, &test_cases[i].value); - todo_wine ok(obj != NULL, "got value %p\n", obj); - if (!obj) continue; + ok(obj != NULL, "got value %p\n", obj);
check_interface(obj, &IID_IInspectable); check_interface(obj, &IID_IPropertyValue); @@ -938,7 +937,7 @@ static void test_CreateValue(void) ok(hr == S_OK, "got hr %#lx\n", hr); obj = pCreateValue(TYPECODE_STRING, &str); WindowsDeleteString(str); - todo_wine ok(obj != NULL || broken(obj == NULL), "got obj %p\n", obj); /* Returns NULL on i386 Windows 10. */ + ok(obj != NULL || broken(obj == NULL), "got obj %p\n", obj); /* Returns NULL on i386 Windows 10. */ if (obj) { type = PropertyType_Empty; diff --git a/dlls/vccorlib140/vccorlib.c b/dlls/vccorlib140/vccorlib.c index d62366edb99..0d34f2fcfa6 100644 --- a/dlls/vccorlib140/vccorlib.c +++ b/dlls/vccorlib140/vccorlib.c @@ -416,9 +416,135 @@ HSTRING __cdecl platform_type_get_FullName(struct platform_type *type) return platform_type_ToString(type); }
+enum typecode +{ + TYPECODE_BOOLEAN = 3, + TYPECODE_CHAR16 = 4, + TYPECODE_UINT8 = 6, + TYPECODE_INT16 = 7, + TYPECODE_UINT16 = 8, + TYPECODE_INT32 = 9, + TYPECODE_UINT32 = 10, + TYPECODE_INT64 = 11, + TYPECODE_UINT64 = 12, + TYPECODE_SINGLE = 13, + TYPECODE_DOUBLE = 14, + TYPECODE_DATETIME = 16, + TYPECODE_STRING = 18, + TYPECODE_TIMESPAN = 19, + TYPECODE_POINT = 20, + TYPECODE_SIZE = 21, + TYPECODE_RECT = 22, + TYPECODE_GUID = 23, +}; + +static const char *debugstr_typecode(int typecode) +{ + static const char *str[] = { + [TYPECODE_BOOLEAN] = "Boolean", + [TYPECODE_CHAR16] = "char16", + [TYPECODE_UINT8] = "uint8", + [TYPECODE_INT16] = "int16", + [TYPECODE_UINT16] = "uint16", + [TYPECODE_INT32] = "int32", + [TYPECODE_UINT32] = "uint32", + [TYPECODE_INT64] = "int64", + [TYPECODE_UINT64] = "uint64", + [TYPECODE_SINGLE] = "float32", + [TYPECODE_DOUBLE] = "double", + [TYPECODE_DATETIME] = "DateTime", + [TYPECODE_STRING] = "String", + [TYPECODE_POINT] = "Point", + [TYPECODE_SIZE] = "Size", + [TYPECODE_RECT] = "Rect", + [TYPECODE_GUID] = "Guid", + }; + if (typecode > ARRAY_SIZE(str) || !str[typecode]) return wine_dbg_sprintf("%d", typecode); + return wine_dbg_sprintf("%s", str[typecode]); +} + void *WINAPI CreateValue(int typecode, const void *val) { - FIXME("(%d, %p) stub\n", typecode, val); + IPropertyValueStatics *statics; + IInspectable *obj; + HRESULT hr;
- return NULL; + TRACE("(%s, %p)\n", debugstr_typecode(typecode), val); + + hr = GetActivationFactoryByPCWSTR(RuntimeClass_Windows_Foundation_PropertyValue, &IID_IPropertyValueStatics, + (void **)&statics); + if (FAILED(hr)) + { + FIXME("GetActivationFactoryByPCWSTR failed: %#lx\n", hr); + return NULL; + } + switch (typecode) + { + case TYPECODE_BOOLEAN: + hr = IPropertyValueStatics_CreateBoolean(statics, *(boolean *)val, &obj); + break; + case TYPECODE_CHAR16: + hr = IPropertyValueStatics_CreateChar16(statics, *(WCHAR *)val, &obj); + break; + case TYPECODE_UINT8: + hr = IPropertyValueStatics_CreateUInt8(statics, *(BYTE *)val, &obj); + break; + case TYPECODE_UINT16: + hr = IPropertyValueStatics_CreateUInt16(statics, *(UINT16 *)val, &obj); + break; + case TYPECODE_INT16: + hr = IPropertyValueStatics_CreateInt16(statics, *(INT16 *)val, &obj); + break; + case TYPECODE_INT32: + hr = IPropertyValueStatics_CreateInt32(statics, *(INT32 *)val, &obj); + break; + case TYPECODE_UINT32: + hr = IPropertyValueStatics_CreateUInt32(statics, *(UINT32 *)val, &obj); + break; + case TYPECODE_INT64: + hr = IPropertyValueStatics_CreateInt64(statics, *(INT64 *)val, &obj); + break; + case TYPECODE_UINT64: + hr = IPropertyValueStatics_CreateUInt64(statics, *(UINT64 *)val, &obj); + break; + case TYPECODE_SINGLE: + hr = IPropertyValueStatics_CreateSingle(statics, *(FLOAT *)val, &obj); + break; + case TYPECODE_DOUBLE: + hr = IPropertyValueStatics_CreateDouble(statics, *(DOUBLE *)val, &obj); + break; + case TYPECODE_DATETIME: + hr = IPropertyValueStatics_CreateDateTime(statics, *(DateTime *)val, &obj); + break; + case TYPECODE_STRING: + hr = IPropertyValueStatics_CreateString(statics, *(HSTRING *)val, &obj); + break; + case TYPECODE_TIMESPAN: + hr = IPropertyValueStatics_CreateTimeSpan(statics, *(TimeSpan *)val, &obj); + break; + case TYPECODE_POINT: + hr = IPropertyValueStatics_CreatePoint(statics, *(Point *)val, &obj); + break; + case TYPECODE_SIZE: + hr = IPropertyValueStatics_CreateSize(statics, *(Size *)val, &obj); + break; + case TYPECODE_RECT: + hr = IPropertyValueStatics_CreateRect(statics, *(Rect *)val, &obj); + break; + case TYPECODE_GUID: + hr = IPropertyValueStatics_CreateGuid(statics, *(GUID *)val, &obj); + break; + default: + obj = NULL; + hr = S_OK; + break; + } + + IPropertyValueStatics_Release(statics); + if (FAILED(hr)) + { + FIXME("Failed to create IPropertyValue object: %#lx\n", hr); + return NULL; + } + return obj; }
From: Vibhav Pant vibhavp@gmail.com
--- dlls/vccorlib140/vccorlib140.spec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/dlls/vccorlib140/vccorlib140.spec b/dlls/vccorlib140/vccorlib140.spec index faecd3b8519..d146b07d30e 100644 --- a/dlls/vccorlib140/vccorlib140.spec +++ b/dlls/vccorlib140/vccorlib140.spec @@ -182,7 +182,8 @@ @ stub -arch=i386 ?GetWeakReference@Details@Platform@@YGPAU__abi_IUnknown@@Q$ADVObject@2@@Z @ stub -arch=arm ?GetWeakReference@Details@Platform@@YAPAU__abi_IUnknown@@Q$ADVObject@2@@Z @ stub -arch=win64 ?GetWeakReference@Details@Platform@@YAPEAU__abi_IUnknown@@QE$ADVObject@2@@Z -@ stub -arch=win32 ?InitControlBlock@ControlBlock@Details@Platform@@AAEXPAX_N11@Z +@ stub -arch=i386 ?InitControlBlock@ControlBlock@Details@Platform@@AAEXPAX_N11@Z +@ stub -arch=arm ?InitControlBlock@ControlBlock@Details@Platform@@AAAXPAX_N11@Z @ stub -arch=win64 ?InitControlBlock@ControlBlock@Details@Platform@@AEAAXPEAX_N11@Z @ cdecl ?InitializeData@Details@Platform@@YAJH@Z(long) InitializeData @ stub -arch=win32 ?Intersect@Rect@Foundation@Windows@@QAAXV123@@Z
On Tue Aug 26 11:39:27 2025 +0000, Vibhav Pant wrote:
Just a hypothetical, but since C++/CX seems to be an odd mix of both COM and C++ RTTI, maybe there's some code out there that assumes that the vtables are laid out in a certain manner? Prodding around with QueryInterface indicates that the layout seems to be `IInspectableVtbl, IClosableVtbl`, with IAgileObject pointing to IInspectable. As a result, I have separated out the IInspectable and IClosable implementations.
That's unexpected, could you please share the test that leads to this conclusion?
On Tue Aug 26 11:50:17 2025 +0000, Piotr Caban wrote:
That's unexpected, could you please share the test that leads to this conclusion?
Sure, here's what I tried:
```c void *agile, *unk, *inspectable, *closable;
IUnknown_QueryInterface((IUnknown *)type_obj, &IID_IAgileObject, &agile); IUnknown_QueryInterface((IUnknown *)type_obj, &IID_IUnknown, &unk); IUnknown_QueryInterface((IUnknown *)type_obj, &IID_IInspectable, &inspectable); IUnknown_QueryInterface((IUnknown *)type_obj, &IID_IClosable, &closable); ok(agile == unk && unk == inspectable && closable == ((char *)inspectable + sizeof(IInspectable)), "got agile=%p, unk=%p, inspectable=%p, closable=%p\n", agile, unk, inspectable, closable); ```
Piotr Caban (@piotr) commented about dlls/vccorlib140/vccorlib.c:
[TYPECODE_CHAR16] = "char16",
[TYPECODE_UINT8] = "uint8",
[TYPECODE_INT16] = "int16",
[TYPECODE_UINT16] = "uint16",
[TYPECODE_INT32] = "int32",
[TYPECODE_UINT32] = "uint32",
[TYPECODE_INT64] = "int64",
[TYPECODE_UINT64] = "uint64",
[TYPECODE_SINGLE] = "float32",
[TYPECODE_DOUBLE] = "double",
[TYPECODE_DATETIME] = "DateTime",
[TYPECODE_STRING] = "String",
[TYPECODE_POINT] = "Point",
[TYPECODE_SIZE] = "Size",
[TYPECODE_RECT] = "Rect",
[TYPECODE_GUID] = "Guid",
I'm not sure if this code is portable. Do you know if similar initialization is used anywhere else in Wine?
On Tue Aug 26 12:47:49 2025 +0000, Piotr Caban wrote:
I'm not sure if this code is portable. Do you know if similar initialization is used anywhere else in Wine?
`grep -rIP '^\s*[.*='` returns a lot, for example https://gitlab.winehq.org/wine/wine/-/blob/master/tools/tools.h#L624 https://gitlab.winehq.org/wine/wine/-/blob/master/dlls/mshtml/htmlevent.c#L3.... It's standard since C99.
(It is, however, not portable to C++, so we should avoid it in include/.)
On Tue Aug 26 11:59:47 2025 +0000, Vibhav Pant wrote:
Sure, here's what I tried:
void *agile, *unk, *inspectable, *closable; IUnknown_QueryInterface((IUnknown *)type_obj, &IID_IAgileObject, &agile); IUnknown_QueryInterface((IUnknown *)type_obj, &IID_IUnknown, &unk); IUnknown_QueryInterface((IUnknown *)type_obj, &IID_IInspectable, &inspectable); IUnknown_QueryInterface((IUnknown *)type_obj, &IID_IClosable, &closable); ok(agile == unk && unk == inspectable && closable == ((char *)inspectable + sizeof(IInspectable)), "got agile=%p, unk=%p, inspectable=%p, closable=%p\n", agile, unk, inspectable, closable);
Can we get back to initial version of the patch (where there's no separate vtable for IInspectable interface? It's not clear how it will look when RTTI is added. I'm expecting that the macros will need to be completely rewritten and I prefer to keep the code simple for now.
On Tue Aug 26 13:59:24 2025 +0000, Piotr Caban wrote:
Can we get back to initial version of the patch (where there's no separate vtable for IInspectable interface? It's not clear how it will look when RTTI is added. I'm expecting that the macros will need to be completely rewritten and I prefer to keep the code simple for now.
Sure. FWIW, I was able to emit RTTI locators for the IInspectable and IClosable vtables, and get `__RTtypeid` working in a rough draft by using the same RTTI macros in msvcrt. As the COM vtables have already been defined, all I needed to do was position the locator before the COM vtable struct.