cdylib-shim 0.1.0

A macro for creating dynamic library shims that can intercept and modify function calls
use std::{
    ffi::{CStr, CString},
    path::Path,
    ptr::NonNull,
};

use winapi::{
    shared::minwindef::{HMODULE, MAX_PATH},
    um::{
        libloaderapi::{FreeLibrary, GetProcAddress, LoadLibraryA},
        sysinfoapi::GetSystemDirectoryA,
    },
};

pub struct Library(HMODULE);

impl Library {
    pub fn load_system(path: impl AsRef<Path>) -> Option<Self> {
        unsafe {
            let mut system_directory = [0; MAX_PATH];
            let result = GetSystemDirectoryA(
                system_directory.as_mut_ptr(),
                size_of_val(&system_directory) as _,
            );

            if result == 0 {
                return None;
            }

            let system_directory = CStr::from_ptr(system_directory.as_ptr()).to_str().ok()?;
            let path = Path::new(system_directory).join(path);
            Self::load(path)
        }
    }

    pub fn load(path: impl AsRef<Path>) -> Option<Self> {
        unsafe {
            let path = CString::new(path.as_ref().as_os_str().as_encoded_bytes()).ok()?;
            let module = LoadLibraryA(path.as_ptr());

            if module.is_null() {
                return None;
            }

            Some(Self(module))
        }
    }

    pub fn get(&self, name: &str) -> Option<NonNull<()>> {
        unsafe {
            let name = CString::new(name).ok()?;
            let address = GetProcAddress(self.0, name.as_ptr());
            NonNull::new(address.cast())
        }
    }
}

impl Drop for Library {
    fn drop(&mut self) {
        unsafe { FreeLibrary(self.0) };
    }
}