Created
November 14, 2022 19:44
-
-
Save DraperDanMan/db9648a0d228cb93aedad1de7d9b1bd2 to your computer and use it in GitHub Desktop.
An example of a rudementary DLL hot reloading setup. If you're using an IDE and run your project in DEBUG it may lock the dll from re-building all dlls while running. The solution is to run in release and attach to the main app with symbols. This will cause symbols to only be loaded for the dlls when they're actually loaded.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include "dll_loader.h" | |
| #include <iostream> | |
| static std::vector<DLLInfo> g_libraries; | |
| static std::string GetPlatformFolder() | |
| { | |
| #ifdef NDEBUG | |
| return R"(\x64\Release\)"; | |
| #else | |
| return R"(\x64\Debug\)"; | |
| #endif | |
| } | |
| static std::string GetCurrentDir() | |
| { | |
| char dir[MAX_PATH]; | |
| if (GetCurrentDirectoryA( sizeof(dir), dir ) == 0) | |
| { | |
| std::cout << "Failed to get current directory" << std::endl; | |
| } | |
| return { dir }; | |
| } | |
| static std::string MoveActiveDLL( const DLLInfo& info ) | |
| { | |
| const std::string active_filename = "~" + info.dll_filename; | |
| std::cout << "Copying " << info.dll_filename << " to " << active_filename << std::endl; | |
| std::string path_root = GetCurrentDir(); | |
| path_root.append( GetPlatformFolder() ); | |
| const std::string cur_path = path_root + info.dll_filename; | |
| std::string new_path = path_root + active_filename; | |
| if (!CopyFileA( cur_path.c_str(), new_path.c_str(), FALSE )) | |
| { | |
| const DWORD err = GetLastError(); | |
| std::cout << "Failed to copy temporary DLL due to error code: " << err << std::endl; | |
| } | |
| return new_path; | |
| } | |
| static void LoadOrReloadDll( DLLInfo &info ) | |
| { | |
| if ( nullptr != info.dll ) | |
| { | |
| FreeLibrary( info.dll ); | |
| } | |
| const std::string active_path = MoveActiveDLL( info ); | |
| info.dll = LoadLibraryA( active_path.c_str() ); | |
| if ( nullptr == info.dll ) | |
| { | |
| std::cout << "Failed to load " << info.dll_filename << std::endl; | |
| return; | |
| } | |
| std::cout << "Loaded DLL " << info.dll_filename << ", Hooking Functions..." << std::endl; | |
| info.dll_hook( info.dll ); | |
| std::cout << "Hooking Complete DLL." << std::endl; | |
| } | |
| static bool ShouldReloadDLL( DLLInfo &info ) | |
| { | |
| std::string full_path = GetCurrentDir(); | |
| full_path.append( GetPlatformFolder() ); | |
| full_path.append( info.dll_filename ); | |
| const auto dll_time = std::filesystem::last_write_time( full_path ); | |
| if (dll_time != info.dll_filetime) | |
| { | |
| info.dll_filetime = dll_time; | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool RegisterDll( const std::string &filename, hook_callback init_callback ) | |
| { | |
| std::string fullPath = GetCurrentDir(); | |
| fullPath.append( GetPlatformFolder() ); | |
| fullPath.append( filename ); | |
| if ( !std::filesystem::is_regular_file( fullPath )) | |
| { | |
| return false; | |
| } | |
| DLLInfo info; | |
| info.dll_filename = filename; | |
| info.dll_hook = init_callback; | |
| g_libraries.push_back( info ); | |
| std::cout << "Registered DLL: " << info.dll_filename << std::endl; | |
| return true; | |
| } | |
| void LoadOrReloadDLLs() | |
| { | |
| for( auto &lib : g_libraries ) | |
| { | |
| if (ShouldReloadDLL( lib )) | |
| { | |
| LoadOrReloadDll( lib ); | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #pragma once | |
| #include <filesystem> | |
| #include <windows.h> | |
| // A callback type to help with knowing what you need to provide | |
| // to the callback | |
| typedef void (CALLBACK* hook_callback)( HINSTANCE dll ); | |
| struct DLLInfo | |
| { | |
| HINSTANCE dll = nullptr; | |
| std::filesystem::file_time_type dll_filetime; | |
| hook_callback dll_hook = nullptr; | |
| std::string dll_filename; | |
| }; | |
| /** | |
| * \brief Registers a DLL to be loaded or reloaded by LoadOrReloadDLLs(). | |
| * The DLL is NOT loaded until the first call to LoadOrReloadDLLs(). | |
| * \param filename The file name of the dll eg. game.dll | |
| * \param init_callback a callback that is fired every time the DLL is loaded, | |
| * this should GetProcAddress for the functions you're loading and | |
| * any initialization. | |
| * \return Returns true on a successful register and false if it fails. | |
| */ | |
| bool RegisterDll( const std::string &filename, hook_callback init_callback ); | |
| /** | |
| * \brief Looks through the list of registered dll's and checks if they have been modified, | |
| * This should be called at least once to load your DLL, and called whenever we want | |
| * to check if they need to be reloaded. | |
| */ | |
| void LoadOrReloadDLLs(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <windows.h> | |
| #include <iostream> | |
| #include "dll_loader.h" | |
| typedef void (CALLBACK* init_fib_func)(int start); | |
| typedef int (CALLBACK* get_next_fib_func)(); | |
| static init_fib_func init_fib; | |
| static get_next_fib_func get_next_fib; | |
| void HookFibFuncs( const HINSTANCE dll ) | |
| { | |
| init_fib = (init_fib_func)GetProcAddress( dll, "init_fib" ); | |
| get_next_fib = (get_next_fib_func)GetProcAddress( dll, "get_next_fib" ); | |
| } | |
| int main(int argc, char* argv[]) | |
| { | |
| bool shouldExit = false; | |
| std::string input; | |
| RegisterDll( "fib.dll", HookFibFuncs ); | |
| float dt = 0; | |
| while ( !shouldExit ) | |
| { | |
| LoadOrReloadDLLs(); | |
| const int val = get_next_fib(); | |
| std::cout << "next fib: " << val << std::endl; | |
| std::cin >> input; | |
| if ( input == "q" ) | |
| { | |
| shouldExit = true; | |
| } | |
| dt += .1f; | |
| } | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment