From a4c9dc2830b1d1e43d5ca6de2fa841528b348b36 Mon Sep 17 00:00:00 2001 From: Andrea Venuta Date: Sat, 19 Aug 2023 09:20:02 +0200 Subject: [PATCH] Expose `hooks::common` (#122) * Make DummyHwnd public and move Fence to DX12 * Move hook-implementor traits to `common` and client traits to `hooks` * Make `mh` module public * Appease Clippy --- examples/dx11_host.rs | 2 +- examples/renderers/dx11.rs | 2 +- examples/renderers/dx12.rs | 2 +- examples/renderers/dx9.rs | 2 +- src/hooks/common/mod.rs | 136 ++++++++++++++--------------------- src/hooks/common/wnd_proc.rs | 2 +- src/hooks/dx11.rs | 7 +- src/hooks/dx12.rs | 62 ++++++++++++++-- src/hooks/mod.rs | 60 +++++++++------- src/lib.rs | 3 +- src/renderers/imgui_dx9.rs | 8 +-- tests/harness/dx11.rs | 2 +- tests/harness/dx12.rs | 2 +- 13 files changed, 159 insertions(+), 131 deletions(-) diff --git a/examples/dx11_host.rs b/examples/dx11_host.rs index f6e2d0ff..b60c9840 100644 --- a/examples/dx11_host.rs +++ b/examples/dx11_host.rs @@ -84,7 +84,7 @@ pub fn main(_argc: i32, _argv: *const *const u8) { println!( "{}", String::from_utf8_lossy(std::slice::from_raw_parts( - diqm.pDescription as *const u8, + diqm.pDescription, diqm.DescriptionByteLength )) ); diff --git a/examples/renderers/dx11.rs b/examples/renderers/dx11.rs index c4401504..1aac54d8 100644 --- a/examples/renderers/dx11.rs +++ b/examples/renderers/dx11.rs @@ -88,7 +88,7 @@ pub fn main(_argc: i32, _argv: *const *const u8) { eprintln!( "{}", String::from_utf8_lossy(std::slice::from_raw_parts( - diqm.pDescription as *const u8, + diqm.pDescription, diqm.DescriptionByteLength )) ); diff --git a/examples/renderers/dx12.rs b/examples/renderers/dx12.rs index 5c12cdad..610a39fc 100644 --- a/examples/renderers/dx12.rs +++ b/examples/renderers/dx12.rs @@ -178,7 +178,7 @@ pub fn main(_argc: i32, _argv: *const *const u8) { println!( "{}", String::from_utf8_lossy(std::slice::from_raw_parts( - diqm.pDescription as *const u8, + diqm.pDescription, diqm.DescriptionByteLength )) ); diff --git a/examples/renderers/dx9.rs b/examples/renderers/dx9.rs index 687133ec..c4ea02a0 100644 --- a/examples/renderers/dx9.rs +++ b/examples/renderers/dx9.rs @@ -156,7 +156,7 @@ pub fn main(_argc: i32, _argv: *const *const u8) { eprintln!( "{}", String::from_utf8_lossy(std::slice::from_raw_parts( - diqm.pDescription as *const u8, + diqm.pDescription, diqm.DescriptionByteLength )) ); diff --git a/src/hooks/common/mod.rs b/src/hooks/common/mod.rs index 2e3ad2b9..a5d5b84c 100644 --- a/src/hooks/common/mod.rs +++ b/src/hooks/common/mod.rs @@ -1,8 +1,7 @@ +use std::mem; use std::ptr::null; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::{hint, mem}; -use imgui::{Context, Io, Key, Ui}; +use imgui::Key; use tracing::debug; use windows::core::PCWSTR; use windows::w; @@ -15,49 +14,49 @@ use windows::Win32::UI::WindowsAndMessaging::{ CS_VREDRAW, HCURSOR, HICON, HWND_MESSAGE, WNDCLASSEXW, WS_OVERLAPPEDWINDOW, }; -pub(crate) use self::wnd_proc::*; -use crate::hooks::Hooks; +pub use crate::hooks::common::wnd_proc::*; +use crate::hooks::ImguiRenderLoop; -mod wnd_proc; +pub mod wnd_proc; -/// Holds information useful to the render loop which can't be retrieved from -/// `imgui::Ui`. -pub struct ImguiRenderLoopFlags { - /// Whether the hooked program's window is currently focused. - pub focused: bool, -} - -/// Implement your `imgui` rendering logic via this trait. -pub trait ImguiRenderLoop { - /// Called once at the first occurrence of the hook. Implement this to - /// initialize your data. - fn initialize(&mut self, _ctx: &mut Context) {} - - /// Called every frame. Use the provided `ui` object to build your UI. - fn render(&mut self, ui: &mut Ui, flags: &ImguiRenderLoopFlags); - - /// Called during the window procedure. - fn on_wnd_proc(&self, _hwnd: HWND, _umsg: u32, _wparam: WPARAM, _lparam: LPARAM) {} - - /// If this function returns true, the WndProc function will not call the - /// procedure of the parent window. - fn should_block_messages(&self, _io: &Io) -> bool { - false - } +pub type WndProcType = + unsafe extern "system" fn(hwnd: HWND, umsg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT; - fn into_hook(self) -> Box +/// Generic trait for platform-specific hooks. +/// +/// Implement this if you are building a custom renderer. +/// +/// Check out first party implementations ([`crate::hooks::dx9`], +/// [`crate::hooks::dx11`], [`crate::hooks::dx12`], [`crate::hooks::opengl3`]) +/// for guidance on how to implement the methods. +pub trait Hooks { + fn from_render_loop(t: T) -> Box where - T: Hooks, - Self: Send + Sync + Sized + 'static, - { - T::from_render_loop(self) - } + Self: Sized, + T: ImguiRenderLoop + Send + Sync + 'static; + + /// Find the hook target functions addresses, initialize the data, create + /// and enable the hooks. + /// + /// # Safety + /// + /// Is most definitely UB. + unsafe fn hook(&self); + + /// Cleanup global data and disable the hooks. + /// + /// # Safety + /// + /// Is most definitely UB. + unsafe fn unhook(&mut self); } -pub(crate) type WndProcType = - unsafe extern "system" fn(hwnd: HWND, umsg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT; - -pub(crate) trait ImguiWindowsEventHandler { +/// Implement this if you are building a custom renderer. +/// +/// Check out first party implementations ([`crate::hooks::dx9`], +/// [`crate::hooks::dx11`], [`crate::hooks::dx12`], [`crate::hooks::opengl3`]) +/// for guidance on how to implement the methods. +pub trait ImguiWindowsEventHandler { fn io(&self) -> &imgui::Io; fn io_mut(&mut self) -> &mut imgui::Io; @@ -97,53 +96,21 @@ pub(crate) trait ImguiWindowsEventHandler { } } -/// Spin-loop based synchronization struct. +/// A RAII dummy window. /// -/// Call [`Fence::lock`] in a thread to indicate some operation is in progress, -/// and [`Fence::wait`] on a different thread to create a spin-loop that waits -/// for the lock to be dropped. -pub(crate) struct Fence(AtomicBool); - -impl Fence { - pub(crate) const fn new() -> Self { - Self(AtomicBool::new(false)) - } +/// Registers a class and creates a window on instantiation. +/// Destroys the window and unregisters the class on drop. +pub struct DummyHwnd(HWND, WNDCLASSEXW); - /// Create a [`FenceGuard`]. - pub(crate) fn lock(&self) -> FenceGuard<'_> { - FenceGuard::new(self) - } - - /// Wait in a spin-loop for the [`FenceGuard`] created by [`Fence::lock`] to - /// be dropped. - pub(crate) fn wait(&self) { - while self.0.load(Ordering::SeqCst) { - hint::spin_loop(); - } - } -} - -/// A RAII implementation of a spin-loop for a [`Fence`]. When this is dropped, -/// the wait on a [`Fence`] will terminate. -pub(crate) struct FenceGuard<'a>(&'a Fence); - -impl<'a> FenceGuard<'a> { - fn new(fence: &'a Fence) -> Self { - fence.0.store(true, Ordering::SeqCst); - Self(fence) +impl Default for DummyHwnd { + fn default() -> Self { + Self::new() } } -impl<'a> Drop for FenceGuard<'a> { - fn drop(&mut self) { - self.0 .0.store(false, Ordering::SeqCst); - } -} - -pub(crate) struct DummyHwnd(HWND, WNDCLASSEXW); - impl DummyHwnd { - pub(crate) fn new() -> Self { + pub fn new() -> Self { + // The window procedure for the class just calls `DefWindowProcW`. unsafe extern "system" fn wnd_proc( hwnd: HWND, msg: u32, @@ -153,6 +120,7 @@ impl DummyHwnd { DefWindowProcW(hwnd, msg, wparam, lparam) } + // Create and register the class. let wndclass = WNDCLASSEXW { cbSize: mem::size_of::() as u32, style: CS_HREDRAW | CS_VREDRAW, @@ -169,6 +137,8 @@ impl DummyHwnd { }; debug!("{:?}", wndclass); unsafe { RegisterClassExW(&wndclass) }; + + // Create the window. let hwnd = unsafe { CreateWindowExW( Default::default(), @@ -190,13 +160,15 @@ impl DummyHwnd { Self(hwnd, wndclass) } - pub(crate) fn hwnd(&self) -> HWND { + // Retrieve the window handle. + pub fn hwnd(&self) -> HWND { self.0 } } impl Drop for DummyHwnd { fn drop(&mut self) { + // Destroy the window and unregister the class. unsafe { DestroyWindow(self.0); UnregisterClassW(self.1.lpszClassName, self.1.hInstance); diff --git a/src/hooks/common/wnd_proc.rs b/src/hooks/common/wnd_proc.rs index 298ccd15..31dd5545 100644 --- a/src/hooks/common/wnd_proc.rs +++ b/src/hooks/common/wnd_proc.rs @@ -232,7 +232,7 @@ fn handle_input(io: &mut Io, state: u32, WPARAM(wparam): WPARAM, LPARAM(lparam): //////////////////////////////////////////////////////////////////////////////// #[must_use] -pub(crate) fn imgui_wnd_proc_impl( +pub fn imgui_wnd_proc_impl( hwnd: HWND, umsg: u32, WPARAM(wparam): WPARAM, diff --git a/src/hooks/dx11.rs b/src/hooks/dx11.rs index 268650d6..289e8ed8 100644 --- a/src/hooks/dx11.rs +++ b/src/hooks/dx11.rs @@ -31,11 +31,8 @@ use windows::Win32::UI::WindowsAndMessaging::SetWindowLongA; use windows::Win32::UI::WindowsAndMessaging::SetWindowLongPtrA; use windows::Win32::UI::WindowsAndMessaging::*; -use crate::hooks::common::{ - imgui_wnd_proc_impl, DummyHwnd, ImguiRenderLoop, ImguiRenderLoopFlags, - ImguiWindowsEventHandler, WndProcType, -}; -use crate::hooks::Hooks; +use crate::hooks::common::{imgui_wnd_proc_impl, DummyHwnd, ImguiWindowsEventHandler, WndProcType}; +use crate::hooks::{Hooks, ImguiRenderLoop, ImguiRenderLoopFlags}; use crate::mh::{MhHook, MhHooks}; use crate::renderers::imgui_dx11; diff --git a/src/hooks/dx12.rs b/src/hooks/dx12.rs index be939c7a..f7d93814 100644 --- a/src/hooks/dx12.rs +++ b/src/hooks/dx12.rs @@ -3,8 +3,8 @@ use std::ffi::c_void; use std::mem::{self, ManuallyDrop}; use std::ptr::{null, null_mut}; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; -use std::thread; use std::time::{Duration, Instant}; +use std::{hint, thread}; use imgui::Context; use once_cell::sync::OnceCell; @@ -33,14 +33,62 @@ use windows::Win32::UI::WindowsAndMessaging::SetWindowLongA; use windows::Win32::UI::WindowsAndMessaging::SetWindowLongPtrA; use windows::Win32::UI::WindowsAndMessaging::*; -use crate::hooks::common::{ - imgui_wnd_proc_impl, DummyHwnd, Fence, ImguiRenderLoop, ImguiRenderLoopFlags, - ImguiWindowsEventHandler, WndProcType, -}; -use crate::hooks::Hooks; +use crate::hooks::common::{imgui_wnd_proc_impl, DummyHwnd, ImguiWindowsEventHandler, WndProcType}; +use crate::hooks::{Hooks, ImguiRenderLoop, ImguiRenderLoopFlags}; use crate::mh::{MhHook, MhHooks}; use crate::renderers::imgui_dx12::RenderEngine; +//////////////////////////////////////////////////////////////////////////////// +// Utilities +//////////////////////////////////////////////////////////////////////////////// + +/// Spin-loop based synchronization struct. +/// +/// Call [`Fence::lock`] in a thread to indicate some operation is in progress, +/// and [`Fence::wait`] on a different thread to create a spin-loop that waits +/// for the lock to be dropped. +struct Fence(AtomicBool); + +impl Fence { + const fn new() -> Self { + Self(AtomicBool::new(false)) + } + + /// Create a [`FenceGuard`]. + fn lock(&self) -> FenceGuard<'_> { + FenceGuard::new(self) + } + + /// Wait in a spin-loop for the [`FenceGuard`] created by [`Fence::lock`] to + /// be dropped. + fn wait(&self) { + while self.0.load(Ordering::SeqCst) { + hint::spin_loop(); + } + } +} + +/// A RAII implementation of a spin-loop for a [`Fence`]. When this is dropped, +/// the wait on a [`Fence`] will terminate. +struct FenceGuard<'a>(&'a Fence); + +impl<'a> FenceGuard<'a> { + fn new(fence: &'a Fence) -> Self { + fence.0.store(true, Ordering::SeqCst); + Self(fence) + } +} + +impl<'a> Drop for FenceGuard<'a> { + fn drop(&mut self) { + self.0 .0.store(false, Ordering::SeqCst); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Type aliases +//////////////////////////////////////////////////////////////////////////////// + type DXGISwapChainPresentType = unsafe extern "system" fn(This: IDXGISwapChain3, SyncInterval: u32, Flags: u32) -> HRESULT; @@ -106,7 +154,7 @@ unsafe fn print_dxgi_debug_messages() { debug!( "[DIQ] {}", String::from_utf8_lossy(std::slice::from_raw_parts( - diqm.pDescription as *const u8, + diqm.pDescription, diqm.DescriptionByteLength - 1 )) ); diff --git a/src/hooks/mod.rs b/src/hooks/mod.rs index 2b40a827..d560ad9f 100644 --- a/src/hooks/mod.rs +++ b/src/hooks/mod.rs @@ -5,7 +5,12 @@ //! //! [`imgui`]: https://docs.rs/imgui/0.8.0/imgui/ -pub(crate) mod common; +use imgui::{Context, Io, Ui}; +use windows::Win32::Foundation::{HWND, LPARAM, WPARAM}; + +pub use crate::hooks::common::Hooks; + +pub mod common; #[cfg(feature = "dx11")] pub mod dx11; #[cfg(feature = "dx12")] @@ -15,33 +20,40 @@ pub mod dx9; #[cfg(feature = "opengl3")] pub mod opengl3; -pub use common::{ImguiRenderLoop, ImguiRenderLoopFlags}; +/// Holds information useful to the render loop which can't be retrieved from +/// `imgui::Ui`. +pub struct ImguiRenderLoopFlags { + /// Whether the hooked program's window is currently focused. + pub focused: bool, +} + +/// Implement your `imgui` rendering logic via this trait. +pub trait ImguiRenderLoop { + /// Called once at the first occurrence of the hook. Implement this to + /// initialize your data. + fn initialize(&mut self, _ctx: &mut Context) {} -/// Generic trait for platform-specific hooks. -pub trait Hooks { - fn from_render_loop(t: T) -> Box + /// Called every frame. Use the provided `ui` object to build your UI. + fn render(&mut self, ui: &mut Ui, flags: &ImguiRenderLoopFlags); + + /// Called during the window procedure. + fn on_wnd_proc(&self, _hwnd: HWND, _umsg: u32, _wparam: WPARAM, _lparam: LPARAM) {} + + /// If this function returns true, the WndProc function will not call the + /// procedure of the parent window. + fn should_block_messages(&self, _io: &Io) -> bool { + false + } + + fn into_hook(self) -> Box where - Self: Sized, - T: ImguiRenderLoop + Send + Sync + 'static; - - /// Find the hook target functions addresses, initialize the data, create - /// and enable the hooks. - /// - /// # Safety - /// - /// Is most definitely UB. - unsafe fn hook(&self); - - /// Cleanup global data and disable the hooks. - /// - /// # Safety - /// - /// Is most definitely UB. - unsafe fn unhook(&mut self); + T: Hooks, + Self: Send + Sync + Sized + 'static, + { + T::from_render_loop(self) + } } -pub fn initialize() {} - #[inline] fn loword(l: u32) -> u16 { (l & 0xffff) as u16 diff --git a/src/lib.rs b/src/lib.rs index 385c0568..fa53960f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -113,11 +113,10 @@ //! ``` #![allow(clippy::needless_doctest_main)] -mod mh; - pub mod hooks; #[cfg(feature = "inject")] pub mod inject; +pub mod mh; pub mod renderers; /// Utility functions. diff --git a/src/renderers/imgui_dx9.rs b/src/renderers/imgui_dx9.rs index 1a89ca3b..9b598615 100644 --- a/src/renderers/imgui_dx9.rs +++ b/src/renderers/imgui_dx9.rs @@ -317,8 +317,8 @@ impl Renderer { } unsafe fn lock_buffers<'v, 'i>( - vb: &'v mut IDirect3DVertexBuffer9, - ib: &'i mut IDirect3DIndexBuffer9, + vb: &'v IDirect3DVertexBuffer9, + ib: &'i IDirect3DIndexBuffer9, vtx_count: usize, idx_count: usize, ) -> Result<(&'v mut [CustomVertex], &'i mut [DrawIdx])> { @@ -351,8 +351,8 @@ impl Renderer { unsafe fn write_buffers(&mut self, draw_data: &DrawData) -> Result<()> { let (mut vtx_dst, mut idx_dst) = Self::lock_buffers( - &mut self.vertex_buffer.0, - &mut self.index_buffer.0, + &self.vertex_buffer.0, + &self.index_buffer.0, draw_data.total_vtx_count as usize, draw_data.total_idx_count as usize, )?; diff --git a/tests/harness/dx11.rs b/tests/harness/dx11.rs index 8342b3f0..0d8154d8 100644 --- a/tests/harness/dx11.rs +++ b/tests/harness/dx11.rs @@ -132,7 +132,7 @@ impl Dx11Harness { eprintln!( "{}", String::from_utf8_lossy(std::slice::from_raw_parts( - diqm.pDescription as *const u8, + diqm.pDescription, diqm.DescriptionByteLength )) ); diff --git a/tests/harness/dx12.rs b/tests/harness/dx12.rs index f89925aa..207cfb99 100644 --- a/tests/harness/dx12.rs +++ b/tests/harness/dx12.rs @@ -194,7 +194,7 @@ impl Dx12Harness { println!( "{}", String::from_utf8_lossy(std::slice::from_raw_parts( - diqm.pDescription as *const u8, + diqm.pDescription, diqm.DescriptionByteLength )) );