-
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.
Fix error pattern to capture and set the COM error info when crossing…
… ABI boundaries (#250) Rename `COMError` to `ErrorWithHResult` and `HResult.Error` to `COMError`. Rename `throwIfFailed` and `catch` to `fromABI` and `toABI`, the former capturing the error info and the latter setting it back. Added `COMError` and `WinRTError` initializers taking a message and constructing an error info internally.
- Loading branch information
1 parent
db7cd44
commit 571f96f
Showing
54 changed files
with
601 additions
and
244 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
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
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
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
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
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
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 |
---|---|---|
@@ -1,4 +1,115 @@ | ||
/// Protocol for errors which result from a COM error HRESULT. | ||
public protocol COMError: Error { | ||
import COM_ABI | ||
|
||
/// Protocol for errors with an associated HResult value. | ||
public protocol ErrorWithHResult: Error { | ||
var hresult: HResult { get } | ||
} | ||
|
||
public protocol COMErrorProtocol: ErrorWithHResult { | ||
/// Gets the error info for | ||
var errorInfo: IErrorInfo? { get } | ||
|
||
/// Converts this COM error to its ABI representation, including thread-local COM error information. | ||
func toABI(setErrorInfo: Bool) -> HResult.Value | ||
} | ||
|
||
extension COMErrorProtocol { | ||
/// Converts this COM error to its ABI representation, including thread-local COM error information. | ||
public func toABI() -> HResult.Value { toABI(setErrorInfo: true) } | ||
} | ||
|
||
/// Captures a failure from a COM API invocation (HRESULT + optional IErrorInfo). | ||
public struct COMError: COMErrorProtocol, CustomStringConvertible { | ||
public static let fail = Self(hresult: HResult.fail) | ||
public static let illegalMethodCall = Self(hresult: HResult.illegalMethodCall) | ||
public static let invalidArg = Self(hresult: HResult.invalidArg) | ||
public static let notImpl = Self(hresult: HResult.notImpl) | ||
public static let noInterface = Self(hresult: HResult.noInterface) | ||
public static let pointer = Self(hresult: HResult.pointer) | ||
public static let outOfMemory = Self(hresult: HResult.outOfMemory) | ||
|
||
public let hresult: HResult // Invariant: isFailure | ||
public let errorInfo: IErrorInfo? | ||
|
||
public init(hresult: HResult, errorInfo: IErrorInfo? = nil) { | ||
assert(hresult.isFailure) | ||
self.hresult = hresult | ||
self.errorInfo = errorInfo | ||
} | ||
|
||
public init(hresult: HResult, description: String?) { | ||
self.init(hresult: hresult, errorInfo: description.map { DescriptiveErrorInfo(description: $0) }) | ||
} | ||
|
||
public var description: String { | ||
if let errorInfo, let description = try? errorInfo.description { return description } | ||
return hresult.description | ||
} | ||
|
||
public func toABI(setErrorInfo: Bool = true) -> HResult.Value { | ||
if setErrorInfo { try? Self.setErrorInfo(errorInfo) } | ||
return hresult.value | ||
} | ||
|
||
/// Throws any failure HRESULTs as COMErrors, optionally capturing the COM thread error info. | ||
@discardableResult | ||
public static func fromABI(captureErrorInfo: Bool = true, _ hresult: HResult.Value) throws -> HResult { | ||
let hresult = HResult(hresult) | ||
guard hresult.isFailure else { return hresult } | ||
guard captureErrorInfo else { throw COMError(hresult: hresult) } | ||
|
||
let errorInfo = try? Self.getErrorInfo() | ||
if let swiftErrorInfo = errorInfo as? SwiftErrorInfo, swiftErrorInfo.hresult == hresult { | ||
// This was originally a Swift error, throw it as such. | ||
throw swiftErrorInfo.error | ||
} | ||
|
||
throw COMError(hresult: hresult, errorInfo: errorInfo) | ||
} | ||
|
||
/// Catches any thrown errors from a provided closure, converting it to an HRESULT and optionally setting the COM thread error info state. | ||
public static func toABI(setErrorInfo: Bool = true, _ body: () throws -> Void) -> HResult.Value { | ||
do { try body() } | ||
catch { return toABI(error: error, setErrorInfo: setErrorInfo) } | ||
return HResult.ok.value | ||
} | ||
|
||
public static func toABI(error: Error, setErrorInfo: Bool = true) -> HResult.Value { | ||
// If the error already came from COM/WinRT, propagate it | ||
if let comError = error as? any COMErrorProtocol { return comError.toABI(setErrorInfo: setErrorInfo) } | ||
|
||
// Otherwise, create a new error info and set it | ||
return SwiftErrorInfo(error: error).toABI(setErrorInfo: setErrorInfo) | ||
} | ||
|
||
public static func toABI(hresult: HResult, description: String? = nil) -> HResult.Value { | ||
guard hresult.isFailure else { return hresult.value } | ||
try? Self.setErrorInfo(description.map { DescriptiveErrorInfo(description: $0) }) | ||
return hresult.value | ||
} | ||
|
||
public static func getErrorInfo() throws -> IErrorInfo? { | ||
var errorInfo: UnsafeMutablePointer<SWRT_IErrorInfo>? | ||
defer { IErrorInfoProjection.release(&errorInfo) } | ||
try fromABI(captureErrorInfo: false, COM_ABI.SWRT_GetErrorInfo(/* dwReserved: */ 0, &errorInfo)) | ||
return IErrorInfoProjection.toSwift(consuming: &errorInfo) | ||
} | ||
|
||
public static func setErrorInfo(_ errorInfo: IErrorInfo?) throws { | ||
var errorInfo = try IErrorInfoProjection.toABI(errorInfo) | ||
defer { IErrorInfoProjection.release(&errorInfo) } | ||
try fromABI(captureErrorInfo: false, COM_ABI.SWRT_SetErrorInfo(/* dwReserved: */ 0, errorInfo)) | ||
} | ||
|
||
private final class DescriptiveErrorInfo: COMPrimaryExport<IErrorInfoProjection>, IErrorInfoProtocol { | ||
private let _description: String | ||
public init(description: String) { self._description = description } | ||
|
||
// IErrorInfo | ||
public var guid: GUID { get throws { throw COMError.fail } } | ||
public var source: String? { get throws { throw COMError.fail } } | ||
public var description: String? { self._description } | ||
public var helpFile: String? { get throws { throw COMError.fail } } | ||
public var helpContext: UInt32 { get throws { throw COMError.fail } } | ||
} | ||
} |
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.