A modern C++23 interface for MinHook, with C++20 module support. Provides a lightweight, type-safe, and expressive API with flexible error handling strategies. See the example for usage.
MMinHook can be installed via CMake's FetchContent module.
BUILD_SHARED_LIBSisOFFby default to build a static library; set toONfor a shared library.MMH_BUILD_MODULESisONby default to enable module support; set toOFFto disable.- Requires CMake 3.28.2+ and a C++23 compiler with C++20 modules support for module builds.
include(FetchContent)
FetchContent_Declare(
mminhook
GIT_REPOSITORY https://github.com/z3lx/mminhook.git
GIT_TAG main
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(mminhook)
add_executable(app)
target_link_libraries(app PRIVATE mmh::mmh)Check the header files containing the public interface for exact function signatures and template parameters, as they are self-documenting, and developers already familiar with MinHook will find them intuitive.
- If using C++20 modules, import the
mmhmodule withimport mmh;. - If not using modules, include the main header with
#include <mmh/Hook.hpp>.
- Use the
mmh::Hook<Ret, Args...>class template to instantiate an empty (default constructed) hook for a function with return typeRetand argument typesArgs.... Any method that manipulates the state of an empty hook will fail. - Use the
mmh::Hook<Ret, Args...>::Createstatic method to create a hook; its input parameters match those ofMH_CreateHookandMH_CreateHookApi, with an additional boolean parameter to specify whether to enable the hook immediately after creation. - Use the
mmh::Hook<Ret, Args...>::Enablemethod to enable or disable the hook. - Use the
mmh::Hook<Ret, Args...>::CallOriginalmethod to call the trampoline to the original function. - MinHook is initialized automatically when the first hook is created, and uninitialized when the last hook is destroyed.
- Excluding the creation of the hook, all methods are not thread-safe. Without proper synchronization, undefined behavior may occur.
- Methods that may fail provide both throwing and non-throwing variants; non-throwing methods use the
Try*suffix in their names. - Throwing methods throw
mmh::Exceptionon failure;mmh::Exception::whatreturns a non-owning string describing the error, andmmh::Exception::GetErrorreturns anmmh::Errorrepresenting the error code. - Non-throwing methods return a
mmh::Result<T>, which is an alias forstd::expected<T, E>;Tis the type of the return value on success, andEis of typemmh::Errorrepresenting the error code on failure. - If compiling without exceptions enabled, throwing methods will instead terminate the program.
Using the library to hook the GetAsyncKeyState function from the Windows API:
import mmh;
import std;
import <Windows.h>;
mmh::Hook<SHORT, int> hook {};
SHORT GetAsyncKeyStateHk(const int vKey) noexcept try {
const SHORT state = hook.CallOriginal(vKey);
std::println(
std::cout,
"GetAsyncKeyState called with vKey = 0x{0:02X}, "
"returned with state = 0x{1:04X}",
static_cast<unsigned int>(vKey),
static_cast<unsigned int>(state)
);
return state;
} catch (...) {
return 0;
}
int main() noexcept try {
hook = mmh::Hook<SHORT, int>::Create(
GetAsyncKeyState,
GetAsyncKeyStateHk,
true
);
GetAsyncKeyState(VK_RBUTTON);
return 0;
} catch (const mmh::Exception& e) {
std::println(
std::cerr,
"Caught mmh::Exception with error: {}",
e.what()
);
return 1;
}Possible output:
GetAsyncKeyState called with vKey = 0x02, returned with state = 0x0000
This project uses minhook, licensed under the BSD-2-Clause License.
This project is licensed under the MIT License. See the LICENSE file for more information.