Skip to content

Commit

Permalink
Big shoutout to sonodima/microseh#13 for letting me clean up
Browse files Browse the repository at this point in the history
  • Loading branch information
RivenSkaye committed Oct 7, 2024
1 parent 35fc193 commit 7fd3e80
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 43 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
version = "0.1.0"

[dependencies]
fixedstr = {version = "0.5.7", features = ["std"]}
microseh = "1.0.4"
fixedstr = {version = "0.5.8", features = ["std"]}
microseh = "1.1"
windows = {version = "0.58", features = [
"Win32_Foundation",
"Win32_Security",
Expand Down
44 changes: 36 additions & 8 deletions src/err.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//! Handling these is recommended, but if you don't then either the MMF was never opened,
//! or it will be closed when the program ends.

use microseh::Exception;
use std::{borrow::Cow, error::Error as stderr, fmt};
use windows::core::{Error as WErr, HRESULT};

Expand Down Expand Up @@ -43,21 +44,48 @@ impl stderr for Error {
}
}

impl From<WErr> for Error {
fn from(value: WErr) -> Self {
match value.code().into() {
HRESULT(0) => Self::OS_OK(value),
HRESULT(19) => Self::WriteLocked,
impl From<HRESULT> for Error {
fn from(value: HRESULT) -> Self {
match value {
HRESULT(30) => Self::ReadLocked,
HRESULT(33) => Self::LockViolation,
HRESULT(19) => Self::WriteLocked,
HRESULT(8) => Self::NotEnoughMemory,
HRESULT(2) => Self::MMF_NotFound,
HRESULT(9) => Self::Uninitialized,
_ => Self::OS_Err(value),
HRESULT(2) => Self::MMF_NotFound,
HRESULT(33) => Self::LockViolation,
HRESULT(0) => Self::OS_OK(value.into()),
_ => Self::OS_Err(value.into()),
}
}
}

impl From<i32> for Error {
fn from(value: i32) -> Self {
Self::from(HRESULT(value))
}
}

impl From<u32> for Error {
fn from(value: u32) -> Self {
// pub struct HRESULT(pub i32) is `repr(transparent)`
Self::from(value as i32)
}
}

impl From<WErr> for Error {
fn from(value: WErr) -> Self {
// Extract the inner HRESULT
Self::from(value.code())
}
}

impl From<Exception> for Error {
fn from(value: Exception) -> Self {
// microseh::Exception::ExceptionCode is a `repr(u32)` enum. This is safe to cast.
Self::from(HRESULT(value.code() as u32 as i32))
}
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let text: Cow<str> = match self {
Expand Down
48 changes: 15 additions & 33 deletions src/mmf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
//! get things working.
//!
//! The internal implementation is built entirely around using [`fixedstr::zstr`] to keep references to strings alive
//! because for some reason everything goes to hell if you don't. MicroSEH is just as much a core component here, and
//! a macro is available for wrapping things that return errors from the windows crate.
//! Take a look at [`wrap_try!`] for more info.
//! ~~wdym don't pass refs to data you're dropping across the ffi boundary?~~
//! because for some reason everything goes to hell if you don't. [`microseh`] is just as much a core component here, as
//! it's a requirement to get the OS to play nice in the event of something going wrong and a structured exception being
//! thrown. This **does** mean that you, the consumer of this library, must ensure a clean exit and teardown upon
//! failure. No, a [`panic!`] does not suffice, ensure things get dropped and that the OS doesn't unwind your ass.
//!
//! While it would be possible to split things out further, using this much to ensure everything works smoothly helps
//! keeping this maintanable and usable. If you need a more minimal implementation, feel free to yank whatever you need
Expand All @@ -25,7 +25,7 @@ use microseh::try_seh;
use windows::{
core::Error as WErr,
Win32::{
Foundation::{SetLastError, HANDLE, WIN32_ERROR},
Foundation::HANDLE,
System::Memory::{UnmapViewOfFile, MEMORY_MAPPED_VIEW_ADDRESS},
},
};
Expand Down Expand Up @@ -88,25 +88,6 @@ pub trait Mmf {
fn write(&self, buffer: &[u8]) -> MMFResult<()>;
}

/// Replace the boilerplate for every time we need to call `try_seh`.
///
/// The function **must** return a result that can be converted into a [`MMFResult`]. If you're ever not certain it
/// does, manually wrap it in an `Ok()`. The resulting boilerplate is literally all it takes to further process the
/// code. The choice for a result is entirely based on the fact that most `windows-rs` calls return results, so it's
/// generally less effort this way.
macro_rules! wrap_try {
($func:expr, $res:ident) => {{
let mut $res: Result<_, _> = Err(WErr::empty());
let res2 = try_seh(|| $res = $func);
if let Err(e) = res2 {
// make sure the error code is set system-side
unsafe { SetLastError(WIN32_ERROR(e.code() as u32)) };
return Err(WErr::from_win32().into());
}
$res.map_err(MMFError::from)
}};
}

/// A simple struct wrapping a [Memory Mapped File](https://learn.microsoft.com/en-us/windows/win32/memory/creating-named-shared-memory).
///
/// It contains all the data required to create and keep alive a [`HANDLE`] to a Memory Mapped File. The [`HANDLE`] is
Expand Down Expand Up @@ -169,17 +150,19 @@ impl MemoryMappedFile {
// fuckin' windows
let mmf_name = PCSTR::from_raw(init_name.to_ptr());
let (dw_low, dw_high) = (size.get() + 4).split();

/*
// Safety: handled through microSEH and we check the last error status later. Failure here is failure there.
let handle = wrap_try!(
unsafe { CreateFileMappingA(INVALID_HANDLE_VALUE, None, PAGE_READWRITE, dw_high, dw_low, mmf_name) },
hndl
)?;
)?;*/
let handle = try_seh(|| unsafe {
CreateFileMappingA(INVALID_HANDLE_VALUE, None, PAGE_READWRITE, dw_high, dw_low, mmf_name)
})??;

// Unsafe because `MapViewOfFile` is marked as such, but it should return a NULL pointer when failing; and set
// the last error state correspondingly.
let map_view =
wrap_try!(unsafe { Ok(MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, size.get() + 4)) }, mapview)?;
let map_view = try_seh(|| unsafe { MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, size.get() + 4) })?;

// Explicit check to make sure we have something that works (later is now)
if unsafe { GetLastError() }.is_err() {
Expand Down Expand Up @@ -223,12 +206,11 @@ impl MemoryMappedFile {
let (dw_low, dw_high) = (size.get() + 4).split();

// Safety: Issues here are issues later, and we check for them later.
let handle = wrap_try!(unsafe { OpenFileMappingA(FILE_MAP_ALL_ACCESS.0, false, mmf_name) }, hndl)?;
let handle = try_seh(|| unsafe { OpenFileMappingA(FILE_MAP_ALL_ACCESS.0, false, mmf_name) })??;

// Unsafe because `MapViewOfFile` is marked as such, but it should return a NULL pointer when failing; and set
// the last error state correspondingly.
let map_view =
wrap_try!(unsafe { Ok(MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, size.get() + 4)) }, mmva)?;
let map_view = try_seh(|| unsafe { MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, size.get() + 4) })?;

// Explicit check to make sure we have something that works (later is now)
if unsafe { GetLastError() }.is_err() {
Expand Down Expand Up @@ -271,7 +253,7 @@ impl MemoryMappedFile {
/// Close the MMF. Don't worry about calling this, it's handled in [`Drop`].
pub fn close(&self) -> MMFResult<()> {
// Safety: microSEH handles the OS side of this error, and the match handles this end.
match wrap_try!(unsafe { CloseHandle(self.handle) }, res) {
match try_seh(|| unsafe { CloseHandle(self.handle) })?.map_err(MMFError::from) {
Err(MMFError::OS_OK(_)) | Ok(_) => Ok(()),
err => err.inspect_err(|e| eprintln!("Error closing MMF's handle: {:#?}", e)),
}
Expand Down Expand Up @@ -386,7 +368,7 @@ impl MemoryMappedView {
/// If you need to do or change something that causes unmapping of the view, and you do need to keep the relevant
/// data, it's best to open a new MMF before closing it. When the last handle to an MMF closes, it's destroyed.
fn unmap(&self) -> MMFResult<()> {
match wrap_try!(unsafe { UnmapViewOfFile(self.address) }, res) {
match try_seh(|| unsafe { UnmapViewOfFile(self.address) })?.map_err(MMFError::from) {
Err(MMFError::OS_OK(_)) | Ok(_) => Ok(()),
err => err.inspect_err(|e| eprintln!("Error unmapping the view of the MMF: {:#?}", e)),
}
Expand Down

0 comments on commit 7fd3e80

Please sign in to comment.