Skip to content

Commit

Permalink
IWeakReferenceSource support (#148)
Browse files Browse the repository at this point in the history
  • Loading branch information
tristanlabelle authored Mar 29, 2024
1 parent 2108d3f commit d7683d3
Show file tree
Hide file tree
Showing 22 changed files with 592 additions and 26 deletions.
6 changes: 5 additions & 1 deletion swiftwinrt/Resources/CWinRT/CppInteropWorkaround.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ struct StaticWCharArray_512 {
#define IInspectableVtbl IInspectableVtbl_Workaround
#define IMarshal IMarshal_Workaround
#define IMarshalVtbl IMarshalVtbl_Workaround
#define IWeakReference IWeakReference_Workaround
#define IWeakReferenceVtbl IWeakReferenceVtbl_Workaround
#define IWeakReferenceSource IWeakReferenceSource_Workaround
#define IWeakReferenceSourceVtbl IWeakReferenceSourceVtbl_Workaround
#define IActivationFactory IActivationFactory_Workaround
#define IActivationFactoryVtbl IActivationFactoryVtbl_Workaround

Expand Down Expand Up @@ -291,4 +295,4 @@ typedef struct IActivationFactoryVtbl
interface IActivationFactory
{
CONST_VTBL struct IActivationFactoryVtbl *lpVtbl;
};
};
8 changes: 8 additions & 0 deletions swiftwinrt/Resources/Support/CppInteropWorkaround.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ public typealias C_IMarshal = C_BINDINGS_MODULE.IMarshal_Workaround
public typealias C_IMarshalVtbl = C_BINDINGS_MODULE.IMarshalVtbl_Workaround
public typealias C_IActivationFactory = C_BINDINGS_MODULE.IActivationFactory_Workaround
public typealias C_IActivationFactoryVtbl = C_BINDINGS_MODULE.IActivationFactoryVtbl_Workaround
public typealias C_IWeakReference = C_BINDINGS_MODULE.IWeakReference_Workaround
public typealias C_IWeakReferenceVtbl = C_BINDINGS_MODULE.IWeakReferenceVtbl_Workaround
public typealias C_IWeakReferenceSource = C_BINDINGS_MODULE.IWeakReferenceSource_Workaround
public typealias C_IWeakReferenceSourceVtbl = C_BINDINGS_MODULE.IWeakReferenceSourceVtbl_Workaround
internal let CoCreateInstance = C_BINDINGS_MODULE.CoCreateInstance_Workaround
internal let UuidFromStringA = C_BINDINGS_MODULE.UuidFromStringA_Workaround
internal let RoActivateInstance = C_BINDINGS_MODULE.RoActivateInstance_Workaround
Expand All @@ -30,6 +34,10 @@ public typealias C_IMarshal = C_BINDINGS_MODULE.IMarshal
public typealias C_IMarshalVtbl = C_BINDINGS_MODULE.IMarshalVtbl
public typealias C_IActivationFactory = C_BINDINGS_MODULE.IActivationFactory
public typealias C_IActivationFactoryVtbl = C_BINDINGS_MODULE.IActivationFactoryVtbl
public typealias C_IWeakReference = C_BINDINGS_MODULE.IWeakReference
public typealias C_IWeakReferenceVtbl = C_BINDINGS_MODULE.IWeakReferenceVtbl
public typealias C_IWeakReferenceSource = C_BINDINGS_MODULE.IWeakReferenceSource
public typealias C_IWeakReferenceSourceVtbl = C_BINDINGS_MODULE.IWeakReferenceSourceVtbl
internal let CoCreateInstance = C_BINDINGS_MODULE.CoCreateInstance
internal let UuidFromStringA = C_BINDINGS_MODULE.UuidFromStringA
internal let RoActivateInstance = C_BINDINGS_MODULE.RoActivateInstance
Expand Down
42 changes: 22 additions & 20 deletions swiftwinrt/Resources/Support/IInspectable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,28 +79,30 @@ public enum __ABI_ {
public static func tryUnwrapFrom(raw pUnk: UnsafeMutableRawPointer?) -> AnyObject? {
tryUnwrapFromBase(raw: pUnk)
}

internal static func queryInterface(_ pUnk: UnsafeMutablePointer<C_IInspectable>?, _ riid: UnsafePointer<SUPPORT_MODULE.IID>?, _ ppvObject: UnsafeMutablePointer<UnsafeMutableRawPointer?>?) -> HRESULT {
guard let pUnk, let riid, let ppvObject else { return E_INVALIDARG }
ppvObject.pointee = nil
if riid.pointee == IUnknown.IID ||
riid.pointee == IInspectable.IID ||
riid.pointee == ISwiftImplemented.IID ||
riid.pointee == IAgileObject.IID {
_ = pUnk.pointee.lpVtbl.pointee.AddRef(pUnk)
ppvObject.pointee = UnsafeMutableRawPointer(pUnk)
return S_OK
}
let swiftObj = AnyWrapper.tryUnwrapFrom(raw: pUnk)
if let customQueryInterface = swiftObj as? CustomQueryInterface,
let result = customQueryInterface.queryInterface(riid.pointee) {
ppvObject.pointee = result.detach()
return S_OK
}
return E_NOINTERFACE
}
}

internal static var IInspectableVTable: C_IInspectableVtbl = .init(
QueryInterface: {
guard let pUnk = $0, let riid = $1, let ppvObject = $2 else { return E_INVALIDARG }
ppvObject.pointee = nil
if riid.pointee == IUnknown.IID ||
riid.pointee == IInspectable.IID ||
riid.pointee == ISwiftImplemented.IID ||
riid.pointee == IAgileObject.IID {
_ = pUnk.pointee.lpVtbl.pointee.AddRef(pUnk)
ppvObject.pointee = UnsafeMutableRawPointer(pUnk)
return S_OK
}
let swiftObj = AnyWrapper.tryUnwrapFrom(raw: pUnk)
if let customQueryInterface = swiftObj as? CustomQueryInterface,
let result = customQueryInterface.queryInterface(riid.pointee) {
ppvObject.pointee = result.detach()
return S_OK
}
return E_NOINTERFACE
},
QueryInterface: { AnyWrapper.queryInterface($0, $1, $2) },
AddRef: { AnyWrapper.addRef($0) },
Release: { AnyWrapper.release($0) },
GetIids: {
Expand Down Expand Up @@ -138,4 +140,4 @@ extension ComposableImpl where CABI == C_IInspectable {
let vtblPtr = withUnsafeMutablePointer(to: &__ABI_.IInspectableVTable) { $0 }
return .init(lpVtbl: vtblPtr)
}
}
}
82 changes: 82 additions & 0 deletions swiftwinrt/Resources/Support/IWeakReference.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import C_BINDINGS_MODULE

internal let IID_IWeakReference: IID = IID(Data1: 0x00000037, Data2: 0x0000, Data3: 0x0000, Data4: (0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46)) // 00000037-0000-0000-C000-000000000046

fileprivate extension IUnknownRef {
func copyTo(_ riid: REFIID?, _ ppvObj: UnsafeMutablePointer<LPVOID?>?) -> HRESULT {
self.borrow.pointee.lpVtbl.pointee.QueryInterface(self.borrow, riid, ppvObj)
}
}

func makeWeakReference(_ object: AnyObject, _ result: UnsafeMutablePointer<UnsafeMutableRawPointer?>) throws {
let wrapper = WeakReferenceWrapper(WeakReference(object))
try wrapper.toABI {
_ = $0.pointee.lpVtbl.pointee.AddRef($0)
result.pointee = UnsafeMutableRawPointer($0)
}
}

fileprivate class WeakReference {
public weak var target: AnyObject?
public init(_ target: AnyObject) {
self.target = target
}
}

fileprivate class WeakReferenceWrapper: WinRTAbiBridgeWrapper<IWeakReferenceBridge> {
init(_ weakReference: WeakReference){
super.init(IWeakReferenceBridge.makeAbi(), weakReference)
}

internal static func queryInterface(_ pUnk: UnsafeMutablePointer<C_IInspectable>?, _ riid: UnsafePointer<SUPPORT_MODULE.IID>?, _ ppvObject: UnsafeMutablePointer<UnsafeMutableRawPointer?>?) -> HRESULT {
guard let pUnk, let riid, let ppvObject else { return E_INVALIDARG }
switch riid.pointee {
case IID_IWeakReference:
_ = pUnk.pointee.lpVtbl.pointee.AddRef(pUnk)
return S_OK
default:
guard let obj = WeakReferenceWrapper.tryUnwrapFromBase(raw: pUnk) else { return E_NOINTERFACE }
let anyWrapper = __ABI_.AnyWrapper(obj)!
return __ABI_.AnyWrapper.queryInterface(try! anyWrapper.toABI { $0 }, riid, ppvObject)
}
}
}

fileprivate enum IWeakReferenceBridge: AbiBridge {
typealias CABI = C_IWeakReference
typealias SwiftProjection = WeakReference

static func makeAbi() -> C_IWeakReference {
return C_IWeakReference(lpVtbl: &IWeakReferenceVTable)
}

static func from(abi: ComPtr<C_IWeakReference>?) -> WeakReference? {
fatalError("Not needed")
}
}

fileprivate var IWeakReferenceVTable: C_IWeakReferenceVtbl = .init(
QueryInterface: { WeakReferenceWrapper.queryInterface($0, $1, $2) },
AddRef: { WeakReferenceWrapper.addRef($0) },
Release: { WeakReferenceWrapper.release($0) },
Resolve: { Resolve($0, $1, $2) }
)

fileprivate func Resolve(
_ this: UnsafeMutablePointer<C_IWeakReference>?,
_ riid: UnsafePointer<GUID_Workaround>?,
_ ppvObject: UnsafeMutablePointer<UnsafeMutablePointer<C_IInspectable>?>?) -> HRESULT {
guard let this, let riid, let ppvObject else { return E_INVALIDARG }
guard let weakReference = WeakReferenceWrapper.tryUnwrapFrom(abi: ComPtr(this)) else { return E_FAIL }
ppvObject.pointee = nil
// https://learn.microsoft.com/en-us/windows/win32/api/weakreference/nf-weakreference-iweakreference-resolve(refiid_iinspectable):
// "If you try to resolve a weak reference to a strong reference for an object that is no longer available,
// then IWeakReference::Resolve returns S_OK, but the objectReference parameter points to null.
guard let target = weakReference.target else { return S_OK }

let anyWrapper = __ABI_.AnyWrapper(target)!
var rawObject: UnsafeMutableRawPointer? = nil
let result = __ABI_.AnyWrapper.queryInterface(try! anyWrapper.toABI { $0 }, riid, &rawObject)
ppvObject.pointee = rawObject?.bindMemory(to: C_IInspectable.self, capacity: 1)
return result
}
71 changes: 71 additions & 0 deletions swiftwinrt/Resources/Support/IWeakReferenceSource.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import C_BINDINGS_MODULE

internal let IID_IWeakReferenceSource: IID = IID(Data1: 0x00000038, Data2: 0x0000, Data3: 0x0000, Data4: (0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46)) // 00000038-0000-0000-C000-000000000046

fileprivate extension IUnknownRef {
func copyTo(_ riid: REFIID?, _ ppvObj: UnsafeMutablePointer<LPVOID?>?) -> HRESULT {
self.borrow.pointee.lpVtbl.pointee.QueryInterface(self.borrow, riid, ppvObj)
}
}

func makeWeakReferenceSource(_ object: AnyObject, _ result: UnsafeMutablePointer<UnsafeMutableRawPointer?>) throws {
let wrapper = WeakReferenceSourceWrapper(object)
try wrapper.toABI {
_ = $0.pointee.lpVtbl.pointee.AddRef($0)
result.pointee = UnsafeMutableRawPointer($0)
}
}

fileprivate class WeakReferenceSourceWrapper: WinRTAbiBridgeWrapper<IWeakReferenceSourceBridge> {
init(_ object: AnyObject){
super.init(IWeakReferenceSourceBridge.makeAbi(), object)
}

internal static func queryInterface(_ pUnk: UnsafeMutablePointer<C_IInspectable>?, _ riid: UnsafePointer<SUPPORT_MODULE.IID>?, _ ppvObject: UnsafeMutablePointer<UnsafeMutableRawPointer?>?) -> HRESULT {
guard let pUnk, let riid, let ppvObject else { return E_INVALIDARG }
switch riid.pointee {
case IID_IWeakReferenceSource:
_ = pUnk.pointee.lpVtbl.pointee.AddRef(pUnk)
return S_OK
default:
guard let obj = WeakReferenceSourceWrapper.tryUnwrapFromBase(raw: pUnk) else { return E_NOINTERFACE }
let anyWrapper = __ABI_.AnyWrapper(obj)!
return __ABI_.AnyWrapper.queryInterface(try! anyWrapper.toABI { $0 }, riid, ppvObject)
}
}
}

fileprivate enum IWeakReferenceSourceBridge: AbiBridge {
typealias CABI = C_IWeakReferenceSource
typealias SwiftProjection = AnyObject

static func makeAbi() -> C_IWeakReferenceSource {
return C_IWeakReferenceSource(lpVtbl: &IWeakReferenceSourceVTable)
}

static func from(abi: ComPtr<C_IWeakReferenceSource>?) -> AnyObject? {
fatalError("Not needed")
}
}

fileprivate var IWeakReferenceSourceVTable: C_IWeakReferenceSourceVtbl = .init(
QueryInterface: { WeakReferenceSourceWrapper.queryInterface($0, $1, $2) },
AddRef: { WeakReferenceSourceWrapper.addRef($0) },
Release: { WeakReferenceSourceWrapper.release($0) },
GetWeakReference: { GetWeakReference($0, $1) }
)

fileprivate func GetWeakReference(
_ this: UnsafeMutablePointer<C_IWeakReferenceSource>?,
_ weakReference: UnsafeMutablePointer<UnsafeMutablePointer<C_IWeakReference>?>?) -> HRESULT {
guard let object = WeakReferenceSourceWrapper.tryUnwrapFrom(abi: ComPtr(this)) else { return E_FAIL }
guard let weakReference else { return E_INVALIDARG }
do {
var rawWeakReference: UnsafeMutableRawPointer? = nil
try makeWeakReference(object, &rawWeakReference)
weakReference.pointee = rawWeakReference?.bindMemory(to: C_IWeakReference.self, capacity: 1)
return S_OK
} catch {
return E_FAIL
}
}
7 changes: 4 additions & 3 deletions swiftwinrt/Resources/Support/WinRTWrapperBase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,12 @@ open class WinRTWrapperBase<CInterface, Prototype> {

fileprivate static func queryInterfaceBase(_ pUnk: UnsafeMutablePointer<CInterface>, _ riid: UnsafePointer<SUPPORT_MODULE.IID>, _ result: UnsafeMutablePointer<UnsafeMutableRawPointer?>) -> HRESULT {
guard let instance = tryUnwrapFromBase(raw: pUnk) else { return E_FAIL }
do
{
do {
switch riid.pointee {
case IID_IMarshal:
try makeMarshaler(IUnknownRef(ComPtr(pUnk)), result)
case IID_IWeakReferenceSource:
try makeWeakReferenceSource(instance as AnyObject, result)
default:
guard let customQI = instance as? CustomQueryInterface,
let iUnknownRef = customQI.queryInterface(riid.pointee) else { return E_NOINTERFACE }
Expand Down Expand Up @@ -252,4 +253,4 @@ public class ReferenceWrapperBase<I: ReferenceBridge>: WinRTAbiBridgeWrapper<I>
public static func tryUnwrapFrom(raw pUnk: UnsafeMutableRawPointer?) -> I.SwiftProjection? {
tryUnwrapFromBase(raw: pUnk)
}
}
}
3 changes: 3 additions & 0 deletions swiftwinrt/abi_writer.h
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,8 @@ namespace swiftwinrt
w.c_mod = settings.get_c_module_name();
write_preamble(w, /* swift_code: */ false);
w.write(R"(#pragma once
#define CINTERFACE
#include <wtypesbase.h>
#include <minwindef.h>
#include <winnt.h>
Expand All @@ -313,6 +315,7 @@ namespace swiftwinrt
#pragma clang diagnostic ignored "-Wmicrosoft-enum-forward-reference"
#include "CppInteropWorkaround.h" // TODO(WIN-860): Remove workaround once C++ interop issues with WinSDK.GUID are fixed.
#include <weakreference.h>
#include "MemoryBuffer.h" // IMemoryBufferByteAccess (C definition)
#include "robuffer.h" // IBufferByteAccess (C definition)
)");
Expand Down
2 changes: 2 additions & 0 deletions swiftwinrt/resources.rc
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ RawTyped RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\RawTyped.swift"
Runtime+Swift RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\Runtime+Swift.swift"
Swift+Extensions RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\Swift+Extensions.swift"
TrustLevel+Swift RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\TrustLevel+Swift.swift"
IWeakReference RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\IWeakReference.swift"
IWeakReferenceSource RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\IWeakReferenceSource.swift"
WinRTWrapperBase RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\WinRTWrapperBase.swift"
WinSDK+Extensions RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\WinSDK+Extensions.swift"
PropertyValue RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\PropertyValue.swift"
Expand Down
33 changes: 33 additions & 0 deletions tests/test_app/WeakReferenceTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import WinSDK
import XCTest
@_spi(WinRTInternal) import test_component
import Ctest_component
import Foundation

class WeakReferenceTests : XCTestCase {
class Target: IReferenceTarget {
func method() {}
}

public func testCreateAndResolve() throws {
let original = Target()
let weakReferencer = WeakReferencer(original)
let roundtrippedAny = try XCTUnwrap(weakReferencer.resolve())
let roundtripped = try XCTUnwrap(roundtrippedAny as? Target)
XCTAssertIdentical(roundtripped, original)
}

public func testNotStrong() throws {
var original: Target! = Target()
let weakReferencer = WeakReferencer(original)
original = nil
XCTAssertNil(try weakReferencer.resolve())
}
}

var weakReferenceTests: [XCTestCaseEntry] = [
testCase([
("testCreateAndResolve", WeakReferenceTests.testCreateAndResolve),
("testNotStrong", WeakReferenceTests.testNotStrong)
])
]
2 changes: 1 addition & 1 deletion tests/test_app/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ var tests: [XCTestCaseEntry] = [
("testUnicode", SwiftWinRTTests.testUnicode),
("testErrorInfo", SwiftWinRTTests.testErrorInfo),
])
] + valueBoxingTests + eventTests + collectionTests + aggregationTests + asyncTests + memoryManagementTests + bufferTests
] + valueBoxingTests + eventTests + collectionTests + aggregationTests + asyncTests + memoryManagementTests + bufferTests + weakReferenceTests

RoInitialize(RO_INIT_MULTITHREADED)
XCTMain(tests)
6 changes: 5 additions & 1 deletion tests/test_app/test_app.exe.manifest
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,9 @@
name="test_component.BufferTester"
threadingModel="both"
xmlns="urn:schemas-microsoft-com:winrt.v1" />
<activatableClass
name="test_component.WeakReferencer"
threadingModel="both"
xmlns="urn:schemas-microsoft-com:winrt.v1" />
</file>
</assembly>
</assembly>
3 changes: 3 additions & 0 deletions tests/test_component/Sources/CWinRT/include/Ctest_component.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// WARNING: Please don't edit this file. It was generated by Swift/WinRT v0.0.1

#pragma once
#define CINTERFACE

#include <wtypesbase.h>
#include <minwindef.h>
#include <winnt.h>
Expand All @@ -23,6 +25,7 @@
#pragma clang diagnostic ignored "-Wmicrosoft-enum-forward-reference"

#include "CppInteropWorkaround.h" // TODO(WIN-860): Remove workaround once C++ interop issues with WinSDK.GUID are fixed.
#include <weakreference.h>
#include "MemoryBuffer.h" // IMemoryBufferByteAccess (C definition)
#include "robuffer.h" // IBufferByteAccess (C definition)
#include "Windows.AI.MachineLearning.h"
Expand Down
Loading

0 comments on commit d7683d3

Please sign in to comment.