diff --git a/Cargo.toml b/Cargo.toml index f6e4a02..0c5d006 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,9 +11,25 @@ categories = ["api-bindings", "os::windows-apis"] include = ["/src/**/*", "/Cargo.toml", "/LICENSE-MIT", "/LICENSE-APACHE", "/build.rs", "/README.md"] [dependencies] -winapi = { version = "0.3", features = [ - "consoleapi", "errhandlingapi", "fileapi", "handleapi", "minwindef", "processthreadsapi", "std", "unknwnbase", "wincon", "winnt", -] } +libc = "0.2" + +[dependencies.winapi] +version = "0.3" +features = [ + "consoleapi", + "errhandlingapi", + "fileapi", + "handleapi", + "minwindef", + "processthreadsapi", + "std", + "unknwnbase", + "winbase", + "wincon", + "winerror", + "winuser", + "winnt", +] [dev-dependencies] rand = "0.4" diff --git a/src/error.rs b/src/error.rs index 4f720bf..bedd3c2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -3,16 +3,79 @@ // , at your option. // All files in the project carrying such notice may not be copied, modified, or distributed // except according to those terms. + +use libc::wcslen; +use std::error::Error as StdError; +use std::fmt; +use std::ffi::OsString; +use std::ptr; use std::result; +use std::slice; +use std::os::windows::ffi::OsStringExt; use winapi::shared::minwindef::DWORD; use winapi::um::errhandlingapi::GetLastError; -#[derive(Clone, Copy, Debug)] +use winapi::um::winbase::{self, FormatMessageW}; +use winapi::um::winnt::{self, MAKELANGID, WCHAR}; + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct Error(DWORD); + impl Error { - pub fn code(&self) -> u32 { self.0 } + pub fn code(&self) -> u32 { + self.0 + } + pub fn last() -> Result { Err(Error(unsafe { GetLastError() })) } + + #[must_use] + pub fn system_message(&self) -> OsString { + unsafe { + let mut error_text: *mut WCHAR = ptr::null_mut(); + + FormatMessageW( + winbase::FORMAT_MESSAGE_FROM_SYSTEM + | winbase::FORMAT_MESSAGE_ALLOCATE_BUFFER + | winbase::FORMAT_MESSAGE_IGNORE_INSERTS, + ptr::null_mut(), + self.code() as _, + MAKELANGID(winnt::LANG_NEUTRAL, winnt::SUBLANG_DEFAULT) as _, + &mut error_text as *mut _ as *mut _, + 0, + ptr::null_mut(), + ); + + let wchars = if !error_text.is_null() { + slice::from_raw_parts(error_text, wcslen(error_text)) + } else { + &[] + }; + + let message = OsString::from_wide(wchars); + + if !error_text.is_null() { + winbase::LocalFree(error_text as *mut _); + } + + message + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let desc = self.system_message().into_string().ok(); + f.pad(desc.as_ref().map(String::as_str).unwrap_or("Unknown error")) + } } +impl Into for Error { + fn into(self) -> u32 { + self.code() + } +} + +impl StdError for Error {} + pub type Result = result::Result; diff --git a/src/lib.rs b/src/lib.rs index a65f3ca..c017a01 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,8 +3,10 @@ // , at your option. // All files in the project carrying such notice may not be copied, modified, or distributed // except according to those terms. + #![cfg(windows)] extern crate winapi; +extern crate libc; // pub mod apc; pub mod com;