use alloc::{
string::{FromUtf8Error, String, ToString},
vec::Vec,
};
use core::{fmt, ptr};
use winapi::{
shared::minwindef::DWORD,
um::{errhandlingapi, winbase::*},
};
#[derive(Debug, Clone, Copy, Hash)]
pub enum Win32Function {
MultiByteToWideChar,
WideCharToMultiByte,
GetModuleHandleExA,
UnregisterClassA,
RegisterClassExA,
GetClassInfoExA,
CreateWindowExA,
GetWindowPlacement,
SetWindowPlacement,
SetWindowTextA,
InvalidateRect,
MoveToEx,
LineTo,
SetDCBrushColor,
SetDCPenColor,
Arc,
SetArcDirection,
Rectangle,
Ellipse,
ShowWindow,
UpdateWindow,
CreateCompatibleBitmap,
BeginPaint,
CreateCompatibleDC,
CreateBitmap,
GetObjectA,
BitBlt,
InitCommonControlsEx,
GetMessageA,
SetWindowLongPtrA,
GetWindowLongPtrA,
ScreenToClient,
GetCursorPos,
CreatePen,
CreateBrush,
Other(&'static str),
}
impl fmt::Display for Win32Function {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match *self {
Self::CreatePen => "CreatePen",
Self::CreateBrush => "CreateBrush",
Self::GetCursorPos => "GetCursorPos",
Self::ScreenToClient => "ScreenToClient",
Self::GetWindowLongPtrA => "GetWindowLongPtrA",
Self::SetWindowLongPtrA => "SetWindowLongPtrA",
Self::GetMessageA => "GetMessageA",
Self::MultiByteToWideChar => "MultiByteToWideChar",
Self::WideCharToMultiByte => "WideCharToMultiByte",
Self::GetModuleHandleExA => "GetModuleHandleExA",
Self::UnregisterClassA => "UnregisterClassA",
Self::RegisterClassExA => "RegisterClassExA",
Self::GetClassInfoExA => "GetClassInfoExA",
Self::CreateWindowExA => "CreateWindowExA",
Self::GetWindowPlacement => "GetWindowPlacement",
Self::SetWindowPlacement => "SetWindowPlacement",
Self::SetWindowTextA => "SetWindowTextA",
Self::InvalidateRect => "InvalidateRect",
Self::MoveToEx => "MoveToEx",
Self::LineTo => "LineTo",
Self::SetDCBrushColor => "SetDCBrushColor",
Self::SetDCPenColor => "SetDCPenColor",
Self::Arc => "Arc",
Self::SetArcDirection => "SetArcDirection",
Self::Rectangle => "Rectangle",
Self::Ellipse => "Ellipse",
Self::ShowWindow => "ShowWindow",
Self::UpdateWindow => "UpdateWindow",
Self::CreateCompatibleBitmap => "CreateCompatibleBitmap",
Self::BeginPaint => "BeginPaint",
Self::CreateCompatibleDC => "CreateCompatibleDC",
Self::CreateBitmap => "CreateBitmap",
Self::GetObjectA => "GetObjectA",
Self::BitBlt => "BitBlt",
Self::InitCommonControlsEx => "InitCommonControlsEx",
Self::Other(s) => s,
}
)
}
}
#[derive(Debug, Clone)]
pub enum Error {
Unreachable,
StaticMsg(&'static str),
Win32 {
code: DWORD,
message: String,
function: Win32Function,
},
Utf8(FromUtf8Error),
ExpiredWeakPtr,
NoGDIStorage,
AlreadyHadGDIStorage,
}
impl fmt::Display for Error {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::Unreachable => f.pad("This error should not have been able to be reached"),
Error::StaticMsg(s) => f.pad(s),
Error::Win32 {
code,
message,
function,
} => write!(f, "{} threw error code {}: {}", function, code, message),
Error::Utf8(u) => fmt::Display::fmt(u, f),
Error::ExpiredWeakPtr => f.pad("Attempted to upgrade a dead Weak pointer"),
Error::NoGDIStorage => f.pad("No GDI storage was found in the device context"),
Error::AlreadyHadGDIStorage => {
f.pad("GDI storage already exists within the device context")
}
}
}
}
impl From<Error> for fmt::Error {
fn from(_f: Error) -> Self {
Self
}
}
impl From<FromUtf8Error> for Error {
fn from(futf8: FromUtf8Error) -> Self {
Self::Utf8(futf8)
}
}
pub type Result<T> = core::result::Result<T, Error>;
pub fn win32_error(function: Win32Function) -> Error {
let error = unsafe { errhandlingapi::GetLastError() };
const ERROR_BUFFER_SIZE: usize = 256;
let mut error_buffer = Vec::with_capacity(ERROR_BUFFER_SIZE);
let len = unsafe {
FormatMessageA(
FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_ARGUMENT_ARRAY,
ptr::null(),
error,
0,
error_buffer.as_mut_ptr(),
(ERROR_BUFFER_SIZE + 1) as DWORD,
ptr::null_mut(),
)
};
if len == 0 {
return Error::Win32 {
code: error,
message: "No error message detected".to_string(),
function,
};
}
unsafe { error_buffer.set_len(len as usize) };
match String::from_utf8(error_buffer.into_iter().map(|i| i as u8).collect()) {
Ok(s) => Error::Win32 {
code: error,
message: s,
function,
},
Err(e) => e.into(),
}
}