-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce COMEmbeddingEx to replace COMEmbedderWithDelegatedImplement…
…ation (#420)
- Loading branch information
1 parent
490dec8
commit 34eb952
Showing
18 changed files
with
318 additions
and
166 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import COM_ABI | ||
import COM_PrivateABI | ||
|
||
extension COMEmbedding { | ||
fileprivate static func getUnmanagedEmbedderUnsafe<ABIStruct>(_ this: UnsafeMutablePointer<ABIStruct>) -> Unmanaged<AnyObject> { | ||
this.withMemoryRebound(to: SWRT_COMEmbedding.self, capacity: 1) { | ||
let opaquePointer = UnsafeMutableRawPointer(bitPattern: $0.pointee.swiftEmbedderAndFlags & ~SWRT_COMEmbeddingFlags_Mask) | ||
assert(opaquePointer != nil, "Bad COM object embedding. The embedder pointer is nil.") | ||
return Unmanaged<AnyObject>.fromOpaque(opaquePointer!) | ||
} | ||
} | ||
|
||
fileprivate static func getIUnknownUnsafe<ABIStruct>(_ this: UnsafeMutablePointer<ABIStruct>) -> IUnknown { | ||
// IUnknown can either be implemented by the embedder or by a separately stored implementer. | ||
let opaquePointer = this.withMemoryRebound(to: SWRT_COMEmbedding.self, capacity: 1) { | ||
if ($0.pointee.swiftEmbedderAndFlags & SWRT_COMEmbeddingFlags_ExternalImplementerIsIUnknown) == 0 { | ||
// The embedder implements IUnknown. | ||
return UnsafeMutableRawPointer(bitPattern: $0.pointee.swiftEmbedderAndFlags & ~SWRT_COMEmbeddingFlags_Mask) | ||
} else { | ||
// The separately stored external implementer implements IUnknown. | ||
return $0.withMemoryRebound(to: SWRT_COMEmbeddingEx.self, capacity: 1) { | ||
$0.pointee.swiftImplementer_retained | ||
} | ||
} | ||
} | ||
|
||
assert(opaquePointer != nil, "Bad COM object embedding. The IUnknown pointer is nil.") | ||
return Unmanaged<AnyObject>.fromOpaque(opaquePointer!).takeUnretainedValue() as! IUnknown | ||
} | ||
|
||
/// Gets the Swift object that provides the implementation for the given COM interface, | ||
/// assuming that it is an embedded COM interface, and otherwise crashes. | ||
public static func getImplementerUnsafe<ABIStruct, Implementer>( | ||
_ this: UnsafeMutablePointer<ABIStruct>, type: Implementer.Type = Implementer.self) -> Implementer { | ||
let opaquePointer = this.withMemoryRebound(to: SWRT_COMEmbedding.self, capacity: 1) { | ||
if ($0.pointee.swiftEmbedderAndFlags & SWRT_COMEmbeddingFlags_ExternalImplementer) == 0 { | ||
// The embedder is the implementer. | ||
return UnsafeMutableRawPointer(bitPattern: $0.pointee.swiftEmbedderAndFlags & ~SWRT_COMEmbeddingFlags_Mask) | ||
} else { | ||
// An external implementer is stored separately. | ||
return $0.withMemoryRebound(to: SWRT_COMEmbeddingEx.self, capacity: 1) { | ||
$0.pointee.swiftImplementer_retained | ||
} | ||
} | ||
} | ||
|
||
assert(opaquePointer != nil, "Bad COM object embedding. The implementer pointer is nil.") | ||
return Unmanaged<AnyObject>.fromOpaque(opaquePointer!).takeUnretainedValue() as! Implementer | ||
} | ||
|
||
public static func getImplementer<ABIStruct, Implementer>( | ||
_ this: UnsafeMutablePointer<ABIStruct>, type: Implementer.Type = Implementer.self) -> Implementer? { | ||
do { | ||
_ = try COMInterop(this).queryInterface(uuidof(SWRT_COMEmbedding.self)) | ||
} catch { | ||
return nil | ||
} | ||
|
||
return getImplementerUnsafe(this, type: type) | ||
} | ||
} | ||
|
||
|
||
internal func uuidof(_: SWRT_COMEmbedding.Type) -> COMInterfaceID { | ||
.init(0x33934271, 0x7009, 0x4EF3, 0x90F1, 0x02090D7EBD64) | ||
} | ||
|
||
public enum IUnknownVirtualTable { | ||
public static func AddRef<ABIStruct>(_ this: UnsafeMutablePointer<ABIStruct>?) -> UInt32 { | ||
guard let this else { | ||
assertionFailure("COM this pointer was null") | ||
return 0 | ||
} | ||
|
||
let unmanaged = COMEmbedding.getUnmanagedEmbedderUnsafe(this) | ||
_ = unmanaged.retain() | ||
// Best effort refcount | ||
return UInt32(_getRetainCount(unmanaged.takeUnretainedValue())) | ||
} | ||
|
||
public static func Release<ABIStruct>(_ this: UnsafeMutablePointer<ABIStruct>?) -> UInt32 { | ||
guard let this else { | ||
assertionFailure("COM this pointer was null") | ||
return 0 | ||
} | ||
|
||
let unmanaged = COMEmbedding.getUnmanagedEmbedderUnsafe(this) | ||
let oldRetainCount = _getRetainCount(unmanaged.takeUnretainedValue()) | ||
unmanaged.release() | ||
// Best effort refcount | ||
return UInt32(oldRetainCount - 1) | ||
} | ||
|
||
public static func QueryInterface<ABIStruct>( | ||
_ this: UnsafeMutablePointer<ABIStruct>?, | ||
_ iid: UnsafePointer<SWRT_Guid>?, | ||
_ ppvObject: UnsafeMutablePointer<UnsafeMutableRawPointer?>?) -> SWRT_HResult { | ||
guard let this, let iid, let ppvObject else { return COMError.toABI(hresult: HResult.invalidArg) } | ||
ppvObject.pointee = nil | ||
|
||
return COMError.toABI { | ||
let id = GUIDBinding.fromABI(iid.pointee) | ||
let this = IUnknownPointer(OpaquePointer(this)) | ||
let reference = id == uuidof(SWRT_COMEmbedding.self) | ||
? IUnknownReference(addingRef: this) | ||
: try COMEmbedding.getIUnknownUnsafe(this)._queryInterface(id) | ||
ppvObject.pointee = UnsafeMutableRawPointer(reference.detach()) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import COM_ABI | ||
import COM_PrivateABI | ||
|
||
/// Use as a stored property in a Swift object to embed a COM object | ||
/// representation which shares its reference count and delegates the implementation. | ||
public struct COMEmbeddingEx: ~Copyable { | ||
private var abi: SWRT_COMEmbeddingEx | ||
|
||
public static var null: COMEmbeddingEx { .init() } | ||
|
||
private init() { | ||
self.abi = .init() | ||
} | ||
|
||
public init(virtualTable: UnsafeRawPointer, embedder: AnyObject, externalImplementer: AnyObject) { | ||
// The don't reference count the embedder since this object is part of it. | ||
// Do reference the implementer since it's an object external to the embedder. | ||
// They can't be the same or we'd have a reference cycle. | ||
assert(externalImplementer !== embedder, "The implementer object should be external to the embedder object.") | ||
self.abi = .init( | ||
base: .init( | ||
virtualTable: virtualTable, | ||
swiftEmbedderAndFlags: UInt(bitPattern: Unmanaged.passUnretained(embedder).toOpaque()) | ||
| SWRT_COMEmbeddingFlags_ExternalImplementer | ||
| (embedder is IUnknown ? 0 : SWRT_COMEmbeddingFlags_ExternalImplementerIsIUnknown)), | ||
swiftImplementer_retained: Unmanaged<AnyObject>.passRetained(externalImplementer).toOpaque()) | ||
} | ||
|
||
deinit { | ||
if let implementerOpaquePointer = abi.swiftImplementer_retained { | ||
Unmanaged<AnyObject>.fromOpaque(implementerOpaquePointer).release() | ||
} | ||
} | ||
|
||
public mutating func asUnknownPointer() -> IUnknownPointer { | ||
withUnsafeMutablePointer(to: &abi) { | ||
IUnknownPointer(OpaquePointer($0)) | ||
} | ||
} | ||
|
||
public mutating func toCOM() -> IUnknownReference { .init(addingRef: asUnknownPointer()) } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.