diff --git a/dlls/windows.devices.geolocation.geolocator/main.c b/dlls/windows.devices.geolocation.geolocator/main.c index 4ea40ce3405..92c62c0feb7 100644 --- a/dlls/windows.devices.geolocation.geolocator/main.c +++ b/dlls/windows.devices.geolocation.geolocator/main.c @@ -34,7 +34,9 @@ struct geolocator { IGeolocator IGeolocator_iface; IWeakReferenceSource IWeakReferenceSource_iface; - LONG ref; + IWeakReference IWeakReference_iface; + LONG ref_public; + LONG ref_weak; }; static inline struct geolocator *impl_from_IGeolocator(IGeolocator *iface) @@ -73,16 +75,18 @@ static HRESULT WINAPI geolocator_QueryInterface(IGeolocator *iface, REFIID iid, static ULONG WINAPI geolocator_AddRef(IGeolocator *iface) { struct geolocator *impl = impl_from_IGeolocator(iface); - ULONG ref = InterlockedIncrement(&impl->ref); + ULONG ref = InterlockedIncrement(&impl->ref_public); TRACE("iface %p increasing refcount to %lu.\n", iface, ref); + IWeakReference_AddRef(&impl->IWeakReference_iface); return ref; } static ULONG WINAPI geolocator_Release(IGeolocator *iface) { struct geolocator *impl = impl_from_IGeolocator(iface); - ULONG ref = InterlockedDecrement(&impl->ref); + ULONG ref = InterlockedDecrement(&impl->ref_public); TRACE("iface %p decreasing refcount to %lu.\n", iface, ref); + IWeakReference_Release(&impl->IWeakReference_iface); return ref; } @@ -207,6 +211,77 @@ static const struct IGeolocatorVtbl geolocator_vtbl = geolocator_remove_StatusChanged, }; +static inline struct geolocator *impl_from_IWeakReference(IWeakReference *iface) +{ + return CONTAINING_RECORD(iface, struct geolocator, IWeakReference_iface); +} + +static HRESULT WINAPI weak_reference_QueryInterface(IWeakReference *iface, REFIID iid, void **out) +{ + struct geolocator *impl = impl_from_IWeakReference(iface); + + TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_IUnknown) || + IsEqualGUID(iid, &IID_IWeakReference)) + { + *out = &impl->IWeakReference_iface; + IInspectable_AddRef(*out); + return S_OK; + } + + FIXME("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI weak_reference_AddRef(IWeakReference *iface) +{ + struct geolocator *impl = impl_from_IWeakReference(iface); + ULONG ref = InterlockedIncrement(&impl->ref_weak); + TRACE("iface %p increasing refcount to %lu.\n", iface, ref); + return ref; +} + +static ULONG WINAPI weak_reference_Release(IWeakReference *iface) +{ + struct geolocator *impl = impl_from_IWeakReference(iface); + ULONG ref = InterlockedDecrement(&impl->ref_weak); + if (!ref) + free(impl); + return ref; +} + +static HRESULT WINAPI weak_reference_Resolve(IWeakReference *iface, REFIID iid, IInspectable **out) +{ + struct geolocator *impl = impl_from_IWeakReference(iface); + HRESULT hr; + LONG ref; + + TRACE("iface %p, iid %s, out %p stub.\n", iface, debugstr_guid(iid), out); + + *out = NULL; + + do + { + if (!(ref = ReadNoFence(&impl->ref_public))) + return S_OK; + } while (ref != InterlockedCompareExchange(&impl->ref_public, ref + 1, ref)); + + hr = IGeolocator_QueryInterface(&impl->IGeolocator_iface, iid, (void **)out); + InterlockedDecrement(&impl->ref_public); + return hr; +} + +static const struct IWeakReferenceVtbl weak_reference_vtbl = +{ + weak_reference_QueryInterface, + weak_reference_AddRef, + weak_reference_Release, + /* IWeakReference methods */ + weak_reference_Resolve, +}; + static inline struct geolocator *impl_from_IWeakReferenceSource(IWeakReferenceSource *iface) { return CONTAINING_RECORD(iface, struct geolocator, IWeakReferenceSource_iface); @@ -232,8 +307,12 @@ static ULONG WINAPI weak_reference_source_Release(IWeakReferenceSource *iface) static HRESULT WINAPI weak_reference_source_GetWeakReference(IWeakReferenceSource *iface, IWeakReference **ref) { - FIXME("iface %p, ref %p stub.\n", iface, ref); - return E_NOTIMPL; + struct geolocator *impl = impl_from_IWeakReferenceSource(iface); + + TRACE("iface %p, ref %p stub.\n", iface, ref); + *ref = &impl->IWeakReference_iface; + IWeakReference_AddRef(*ref); + return S_OK; } static const struct IWeakReferenceSourceVtbl weak_reference_source_vtbl = @@ -321,7 +400,9 @@ static HRESULT WINAPI factory_ActivateInstance(IActivationFactory *iface, IInspe impl->IGeolocator_iface.lpVtbl = &geolocator_vtbl; impl->IWeakReferenceSource_iface.lpVtbl = &weak_reference_source_vtbl; - impl->ref = 1; + impl->IWeakReference_iface.lpVtbl = &weak_reference_vtbl; + impl->ref_public = 1; + impl->ref_weak = 1; *instance = (IInspectable *)&impl->IGeolocator_iface; return S_OK; diff --git a/dlls/windows.devices.geolocation.geolocator/tests/geolocator.c b/dlls/windows.devices.geolocation.geolocator/tests/geolocator.c index 058098a5946..ca12f0d3a9d 100644 --- a/dlls/windows.devices.geolocation.geolocator/tests/geolocator.c +++ b/dlls/windows.devices.geolocation.geolocator/tests/geolocator.c @@ -54,7 +54,11 @@ void test_basic(void) IActivationFactory *factory; IInspectable *inspectable; IGeolocator *geolocator; + IGeolocator *geolocator2; IWeakReferenceSource *weak_reference_source; + IWeakReference *weak_reference; + IUnknown* unknown; + void *dummy; HSTRING str; HRESULT hr; @@ -91,9 +95,47 @@ void test_basic(void) hr = IGeolocator_QueryInterface(geolocator, &IID_IWeakReferenceSource, (void **)&weak_reference_source); ok(hr == S_OK && weak_reference_source, "got hr %#lx.\n", hr); + + hr = IWeakReferenceSource_GetWeakReference(weak_reference_source, &weak_reference); + ok(hr == S_OK && weak_reference, "got hr %#lx.\n", hr); IWeakReferenceSource_Release(weak_reference_source); + hr = IWeakReference_Resolve(weak_reference, &IID_IUnknown, (IInspectable **)&unknown); + ok(hr == S_OK && unknown, "got hr %#lx.\n", hr); + hr = IWeakReference_Resolve(weak_reference, &IID_IGeolocator, (IInspectable **)&geolocator2); + ok(hr == S_OK && geolocator2, "got hr %#lx.\n", hr); + hr = IWeakReference_Resolve(weak_reference, &IID_IInspectable, &inspectable); + ok(hr == S_OK && inspectable, "got hr %#lx.\n", hr); + ok((void *)inspectable == (void *)geolocator, "Interfaces are not the same\n"); + ok((void *)unknown == (void *)geolocator, "Interfaces are not the same\n"); + IUnknown_Release(unknown); + IGeolocator_Release(geolocator2); + geolocator2 = 0; + IInspectable_Release(inspectable); + inspectable = 0; + + dummy = (void *)0xdeadbeef; + hr = IWeakReference_Resolve(weak_reference, &IID_IWeakReference, (IInspectable **)&dummy); + ok(hr == E_NOINTERFACE && !dummy, "got hr %#lx.\n", hr); + + check_interface(weak_reference, &IID_IUnknown); + check_interface(weak_reference, &IID_IWeakReference); + hr = IWeakReference_QueryInterface(weak_reference, &IID_IGeolocator, &dummy); + ok(hr == E_NOINTERFACE && !dummy, "got hr %#lx.\n", hr); + hr = IWeakReference_QueryInterface(weak_reference, &IID_IAgileObject, &dummy); + ok(hr == E_NOINTERFACE && !dummy, "got hr %#lx.\n", hr); + hr = IWeakReference_QueryInterface(weak_reference, &IID_IInspectable, &dummy); + ok(hr == E_NOINTERFACE && !dummy, "got hr %#lx.\n", hr); + + /* Free geolocator, weak reference should fail to resolve now */ IGeolocator_Release(geolocator); + + hr = IWeakReference_Resolve(weak_reference, &IID_IGeolocator, (IInspectable **)&geolocator2); + ok(hr == S_OK && !geolocator2, "got hr %#lx.\n", hr); + hr = IWeakReference_Resolve(weak_reference, &IID_IWeakReference, (IInspectable **)&dummy); + ok(hr == S_OK && !dummy, "got hr %#lx.\n", hr); + + IWeakReference_Release(weak_reference); IActivationFactory_Release(factory); }