Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preserve the IRestrictedErrorInfo after reading it. #154

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 22 additions & 16 deletions swiftwinrt/Resources/Support/Error.swift
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,7 @@ public var E_XAMLPARSEFAILED : WinSDK.HRESULT {
HRESULT(bitPattern: 0x802B000A)
}

private func getErrorDescription(expecting hr: HRESULT) -> String? {
var errorInfo: UnsafeMutablePointer<IRestrictedErrorInfo>?
guard GetRestrictedErrorInfo(&errorInfo) == S_OK else { return nil }
defer {
_ = errorInfo?.pointee.lpVtbl.pointee.Release(errorInfo)
}

private func getErrorDescription(restrictedErrorInfo: UnsafeMutablePointer<IRestrictedErrorInfo>) -> String? {
var errorDescription: BSTR?
var restrictedDescription: BSTR?
var capabilitySid: BSTR?
Expand All @@ -116,16 +110,14 @@ private func getErrorDescription(expecting hr: HRESULT) -> String? {
SysFreeString(restrictedDescription)
SysFreeString(capabilitySid)
}
var resultLocal: HRESULT = S_OK
_ = errorInfo?.pointee.lpVtbl.pointee.GetErrorDetails(
errorInfo,
var hr: HRESULT = S_OK
guard restrictedErrorInfo.pointee.lpVtbl.pointee.GetErrorDetails(
restrictedErrorInfo,
&errorDescription,
&resultLocal,
&hr,
&restrictedDescription,
&capabilitySid)
&capabilitySid) == S_OK else { return nil }

guard resultLocal == hr else { return nil }

// Favor restrictedDescription as this is a more user friendly message, which
// is intended to be displayed to the caller to help them understand why the
// api call failed. If it's not set, then fallback to the generic error message
Expand All @@ -135,7 +127,7 @@ private func getErrorDescription(expecting hr: HRESULT) -> String? {
} else if SysStringLen(errorDescription) > 0 {
return String(decodingCString: errorDescription!, as: UTF16.self)
} else {
return nil
return hrToString(hr)
}
}

Expand Down Expand Up @@ -164,7 +156,21 @@ public struct Error : Swift.Error, CustomStringConvertible {
public let hr: HRESULT

public init(hr: HRESULT) {
self.description = getErrorDescription(expecting: hr) ?? hrToString(hr)
// RoGetMatchingRestrictedErrorInfo creates an IRestrictedErrorInfo with a generic message if it can't find one for the given HRESULT.
var restrictedErrorInfo: UnsafeMutablePointer<IRestrictedErrorInfo>?
if RoGetMatchingRestrictedErrorInfo(hr, &restrictedErrorInfo) == S_OK, let restrictedErrorInfo {
defer { _ = restrictedErrorInfo.pointee.lpVtbl.pointee.Release(restrictedErrorInfo) }

// From the docs: https://learn.microsoft.com/en-us/windows/win32/api/roerrorapi/nf-roerrorapi-getrestrictederrorinfo
// > GetRestrictedErrorInfo transfers ownership of the error object to the caller and clears the error state for the thread.
// Assume that RoGetMatchingRestrictedErrorInfo also clears the error state,
// but for crash reporting purposes, it's useful to preserve the it.
_ = SetRestrictedErrorInfo(restrictedErrorInfo)

self.description = getErrorDescription(restrictedErrorInfo: restrictedErrorInfo) ?? hrToString(hr)
} else {
self.description = hrToString(hr)
}
self.hr = hr
}
}
Expand Down
34 changes: 34 additions & 0 deletions tests/test_app/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,39 @@ class SwiftWinRTTests : XCTestCase {
}
}

public func testIRestrictedErrorInfo() {
let message = "You are doing a bad thing"
do {
let classy = Class()
try classy.fail(message)
} catch {
var restrictedErrorInfo: UnsafeMutablePointer<IRestrictedErrorInfo>?
guard GetRestrictedErrorInfo(&restrictedErrorInfo) == S_OK, let restrictedErrorInfo else {
XCTFail("Failed to get error info")
return
}
defer { _ = restrictedErrorInfo.pointee.lpVtbl.pointee.Release(restrictedErrorInfo) }

var errorDescription: BSTR?
var hr: HRESULT = S_OK
var restrictedDescription: BSTR?
var capabilitySid: BSTR?
defer {
SysFreeString(errorDescription)
SysFreeString(restrictedDescription)
SysFreeString(capabilitySid)
}
guard restrictedErrorInfo.pointee.lpVtbl.pointee.GetErrorDetails(
restrictedErrorInfo, &errorDescription, &hr, &restrictedDescription, &capabilitySid) == S_OK,
let restrictedDescription else {
XCTFail("Failed to get error description")
return
}

XCTAssertEqual(String(decodingCString: restrictedDescription, as: UTF16.self), message)
}
}

public func testNoExcept() throws {
let classy = Class()
classy.noexceptVoid()
Expand All @@ -459,6 +492,7 @@ var tests: [XCTestCaseEntry] = [
("testStructWithIReference", SwiftWinRTTests.testStructWithIReference),
("testUnicode", SwiftWinRTTests.testUnicode),
("testErrorInfo", SwiftWinRTTests.testErrorInfo),
("testIRestrictedErrorInfo", SwiftWinRTTests.testIRestrictedErrorInfo),
])
] + valueBoxingTests + eventTests + collectionTests + aggregationTests + asyncTests + memoryManagementTests + bufferTests + weakReferenceTests

Expand Down
Loading