NIOCore: handle error description on Windows (#2219)
We would previously attempt to convert an error to a description without consideration for the provenance on Windows. This would result in a failure if the error code was not from `errno`. Account for the source of the error and translate it appropriately. We can not retrieve descriptions on errors that may originate from Windows or WinSock as well as the C library.
This commit is contained in:
parent
8b160c3681
commit
48916a49af
|
@ -14,7 +14,20 @@
|
|||
|
||||
#if os(Windows)
|
||||
import ucrt
|
||||
import func WinSDK.FormatMessageW
|
||||
import func WinSDK.LocalFree
|
||||
import let WinSDK.FORMAT_MESSAGE_ALLOCATE_BUFFER
|
||||
import let WinSDK.FORMAT_MESSAGE_FROM_SYSTEM
|
||||
import let WinSDK.FORMAT_MESSAGE_IGNORE_INSERTS
|
||||
import let WinSDK.LANG_NEUTRAL
|
||||
import let WinSDK.SUBLANG_DEFAULT
|
||||
import typealias WinSDK.DWORD
|
||||
import typealias WinSDK.WCHAR
|
||||
import typealias WinSDK.WORD
|
||||
|
||||
internal func MAKELANGID(_ p: WORD, _ s: WORD) -> DWORD {
|
||||
return DWORD((s << 10) | p)
|
||||
}
|
||||
#elseif os(Linux) || os(Android)
|
||||
import Glibc
|
||||
#elseif os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
|
@ -107,13 +120,49 @@ private func reasonForError(errnoCode: CInt, reason: String) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
#if os(Windows)
|
||||
private func reasonForWinError(_ code: DWORD) -> String {
|
||||
let dwFlags: DWORD = DWORD(FORMAT_MESSAGE_ALLOCATE_BUFFER)
|
||||
| DWORD(FORMAT_MESSAGE_FROM_SYSTEM)
|
||||
| DWORD(FORMAT_MESSAGE_IGNORE_INSERTS)
|
||||
|
||||
var buffer: UnsafeMutablePointer<WCHAR>?
|
||||
// We use `FORMAT_MESSAGE_ALLOCATE_BUFFER` in flags which means that the
|
||||
// buffer will be allocated by the call to `FormatMessageW`. The function
|
||||
// expects a `LPWSTR` and expects the user to type-pun in this case.
|
||||
let dwResult: DWORD = withUnsafeMutablePointer(to: &buffer) {
|
||||
$0.withMemoryRebound(to: WCHAR.self, capacity: 2) {
|
||||
FormatMessageW(dwFlags, nil, code,
|
||||
MAKELANGID(WORD(LANG_NEUTRAL), WORD(SUBLANG_DEFAULT)),
|
||||
$0, 0, nil)
|
||||
}
|
||||
}
|
||||
guard dwResult > 0, let message = buffer else {
|
||||
return "unknown error \(code)"
|
||||
}
|
||||
defer { LocalFree(buffer) }
|
||||
return String(decodingCString: message, as: UTF16.self)
|
||||
}
|
||||
#endif
|
||||
|
||||
extension IOError: CustomStringConvertible {
|
||||
public var description: String {
|
||||
return self.localizedDescription
|
||||
}
|
||||
|
||||
public var localizedDescription: String {
|
||||
#if os(Windows)
|
||||
switch self.error {
|
||||
case .errno(let errno):
|
||||
return reasonForError(errnoCode: errno, reason: self.failureDescription)
|
||||
case .windows(let code):
|
||||
return reasonForWinError(code)
|
||||
case .winsock(let code):
|
||||
return reasonForWinError(DWORD(code))
|
||||
}
|
||||
#else
|
||||
return reasonForError(errnoCode: self.errnoCode, reason: self.failureDescription)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue