如何为游戏进程实现固定帧率限制功能(类Rivatuner)
无源码实现游戏进程固定帧率限制方案
要实现无源码的游戏帧率限制,核心是Hook游戏的图形渲染API交换缓冲区函数,在每帧渲染完成后插入精准延迟,通过控制帧间隔来固定帧率。常见的目标函数是DirectX的Present/Present1或OpenGL的SwapBuffers,这些函数是每帧画面输出的最后一步,是控制帧率的关键节点。
1. 注入Hook DLL到目标进程
你当前的代码已经能找到目标进程并打开句柄,接下来需要将包含Hook逻辑的DLL注入到目标进程中。可以通过CreateRemoteThread调用目标进程的LoadLibraryA来加载你的DLL:
// 替换你代码中的// Do stuff..部分 if (hProcess != NULL) { // 你的Hook DLL绝对路径 const char* dllPath = "C:\\Path\\To\\Your\\FrameLimitHook.dll"; LPVOID remoteMem = VirtualAllocEx(hProcess, NULL, strlen(dllPath) + 1, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (remoteMem != NULL) { // 将DLL路径写入目标进程内存 WriteProcessMemory(hProcess, remoteMem, dllPath, strlen(dllPath) + 1, NULL); // 创建远程线程加载DLL HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibraryA, remoteMem, 0, NULL); if (hRemoteThread != NULL) { WaitForSingleObject(hRemoteThread, INFINITE); CloseHandle(hRemoteThread); } VirtualFreeEx(hProcess, remoteMem, 0, MEM_RELEASE); } }
注意:OpenProcess无需PROCESS_ALL_ACCESS,仅需PROCESS_CREATE_THREAD、PROCESS_QUERY_INFORMATION、PROCESS_VM_OPERATION、PROCESS_VM_WRITE、PROCESS_VM_READ这些权限即可,更安全。
2. 在Hook DLL中实现帧率控制逻辑
以下以Direct3D 9的Present函数为例,使用微软Detours库简化Hook实现(也可自行编写inline hook):
2.1 定义函数指针与全局变量
#include <d3d9.h> #include <detours.h> #include <windows.h> #include <chrono> // 原始Present函数指针 typedef HRESULT(WINAPI* pPresent)(LPDIRECT3DDEVICE9, CONST RECT*, CONST RECT*, HWND, CONST RGNDATA*); pPresent OriginalPresent = NULL; // 目标帧率(可根据需求修改) const float targetFPS = 60.0f; // 目标帧间隔(毫秒) const float targetFrameTime = 1000.0f / targetFPS; // 上一帧时间戳 std::chrono::steady_clock::time_point lastFrameTime;
2.2 实现Hook的Present函数
HRESULT WINAPI HookedPresent(LPDIRECT3DDEVICE9 pDevice, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion) { // 计算当前帧与上一帧的时间差 auto currentTime = std::chrono::steady_clock::now(); auto elapsedTime = std::chrono::duration<float, std::milli>(currentTime - lastFrameTime).count(); // 补全帧间隔延迟 if (elapsedTime < targetFrameTime) { // 粗略Sleep减少CPU占用 if (targetFrameTime - elapsedTime > 1.0f) { Sleep((DWORD)(targetFrameTime - elapsedTime - 0.5f)); } // 高精度循环等待补全剩余时间 while (std::chrono::duration<float, std::milli>(std::chrono::steady_clock::now() - lastFrameTime).count() < targetFrameTime) { YieldProcessor(); } } // 更新上一帧时间戳 lastFrameTime = std::chrono::steady_clock::now(); // 调用原始Present函数 return OriginalPresent(pDevice, pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion); }
2.3 DLL入口函数初始化Hook
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: // 初始化时间戳 lastFrameTime = std::chrono::steady_clock::now(); // 启动Hook DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); OriginalPresent = (pPresent)DetourFindFunction("d3d9.dll", "Present"); DetourAttach(&(PVOID&)OriginalPresent, HookedPresent); DetourTransactionCommit(); break; case DLL_PROCESS_DETACH: // 卸载Hook DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourDetach(&(PVOID&)OriginalPresent, HookedPresent); DetourTransactionCommit(); break; } return TRUE; }
3. 关键注意事项
- 多API兼容:不同游戏可能使用D3D11、D3D12或OpenGL,需针对对应API实现Hook(如D3D11的
Present1、OpenGL的SwapBuffers)。 - 位数匹配:Hook DLL需与目标进程位数一致(32位游戏用32位DLL,64位游戏用64位DLL)。
- 权限要求:程序需以管理员权限运行,否则无法注入受保护的游戏进程。
- 精度优化:
Sleep精度约1ms,高帧率场景(如144FPS)需依赖高精度计时循环保证帧率稳定。
内容的提问来源于stack exchange,提问作者szefitoo
相关产品推荐
相关产品推荐

