Гайд Обработка событий окна + ImGui [5]

kin4stat

mq-team · kin4@naebalovo.team
Автор темы
Всефорумный модератор
2,746
4,831
  1. Создание ASI-плагина с нуля
  2. Хуки – что это такое и как с ними работать
  3. Безопасная инициализация и работа с SAMP
  4. Работа с рендером и Directx9
  5. Обработка событий окна + ImGui
В этом гайде будет рассказано про работу с событиями окна, а также их передачей в ImGui

При использовании на других ресурсах необходимо указание авторства и ссылки на оригинальную темы!

Все действия производились на Visual Studio 2019 с параметром /std:c++17, в других версиях интерфейс может отличаться.

И так, начнем:

Для этого гайда нам не нужно никаким образом настраивать проект и что-то в него добавлять. Можно воспользоваться готовым из гайда [4]

В Windows существует множество различных возможностей по работе с окнами(на то она и Windows). И чтобы все было максимально гибко, Windows может отдавать высокоуровневые события окон, и низкоуровневые. Про низкоуровневые события сегодня и пойдет речь.

Все события окна Windows присылает в коллбэк WindowProc. Сюда входят нажатия мышкой, нажатиян на клавиатуру, перемещение окна, изменение его размера, сворачивание, разворачивание и еще куча других событий(около 2000, если не ошибаюсь).
У каждого приложения должен быть создан коллбэк на события окна, иначе вы не сможете создать окно. Т.к. мы работаем внутри готового окна, нам нужно перехватывать уже существующий обработчик событий.

Делать это можно несколькими способами:
  1. Штатными средствами Windows
  2. Перехват самого первого коллбэка(нужно знать его адрес)
  3. Перехват самого последнего зарегистрированного коллбэка
В этом гайде я покажу все три способа.
Начнем с того, что для всех способов, кроме 2 нам нужен хендл окна. В общем случае нам придется получать его через "костыли" поиском по имени окна(FindWindow)

Начнем с того, что нам нужно получить HWND нашего окна. В GTA:SA его можно вытащить из внутренней структуры движка игры. Но просто вытаскивать HWND из структуры игры не всегда хорошая затея, особенно если мы хотим перехватывать события еще до полного запуска игры. Поэтому мы перехватим функцию, создающую окно игры и будем вытаскивать HWND оттуда.

Перейдем к созданию хука
Чтобы не гонять в холостую код, мы сделаем проверку на то, существует ли уже окно игры, и если оно уже создано - будем брать hwnd оттуда.
(На самом деле показанное - почти бесполезно. Но я все же посчитал нужным показать это, лишним точно не будет)
Также можно перехватывать CreateWindow, но там чуть больше заморочек, но зато способ универсальный, и будет работать везде
C++:
// Сигнатура
using InitGameInstance = HWND(__cdecl*)(HINSTANCE);

kthook::kthook_signal<InitGameInstance> game_instance_init_hook{ 0x745560 };

HWND game_hwnd = []() {
    // Указатель на HWND внутри движка игры
    HWND* hwnd_ptr = *reinterpret_cast<HWND**>(0xC17054);
    if (hwnd_ptr != nullptr) {
        return *hwnd_ptr;
    }
    else {
        // Ставим коллбэк после выполенение оригинальной функции, т.к. нам нужен ее возврат
        game_instance_init_hook.after += [](const auto& hook, HWND& return_value, HINSTANCE inst) {
            // присваиваем нашей переменной значение, что вернула нам функция
            game_hwnd = return_value;
        };
        return HWND(0);
    }
}();

Наверное вы спросите, что произошло :D
game_hwnd - глобальная переменная. Все глобальные переменные инициализируются до перехода к основной функции программы(DllMain в нашем случае). А чтобы при инициализации переменной выполнился наш код с условиями - мы создаем лямбду, и сразу же ее вызываем, в итоге результат вызова нашей лямбды будет записан в переменную.
Теперь game_hwnd сама инициализируется, как только окно игры будет создано.

И уже сейчас мы можем перехватывать обработчик событий окна.
Начнем с самого простого способа - 3
Перехватывать текущий обработчик событий можно где угодно, но я буду делать это внутри хука Present. Это можно было бы сделать даже внутри game_instance_init_hook.
Чтобы получить текущий обработчик - воспользуемся функцией GetWindowLongPtr.
И так, создадим хук и воспользуемся сигнатурой, объявленной внутри WINAPI:
C++:
kthook::kthook_simple<WNDPROC> wndproc_hook{};
Теперь, в инициализации directx9 мы будем устанавливать наш хук на последний зарегистрированный обработчик и повесим свой обработчик on_wndproc:
C++:
HRESULT __stdcall on_wndproc(const decltype(wndproc_hook)& hook, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    // вызываем оригинал
    return hook.get_trampoline()(hwnd, uMsg, wParam, lParam);
}
C++:
auto latest_wndproc_ptr = GetWindowLongPtrW(game_hwnd, GWLP_WNDPROC);
wndproc_hook.set_dest(latest_wndproc_ptr);
wndproc_hook.set_cb(&on_wndproc);
wndproc_hook.install();
В данном случае важно использовать функцию с постфиксом W. Сломать что-то своим кодом будет сложно, а вот править ImGui в будущем - не лучшая идея.
Перехват функции игры примерно такой же, и т.к. мне лень писать 1 строку кода - попробуйте сделать это сами, адрес игровой функции - 0x747EB0
Также нужно учитывать, что функция игры будет самой последней в цепочке, и поэтому если кто-то зарегистрирует свой обработчик позже - вы можете не получить событие.

Ну и последний способ - зарегистрировать свой обработчик средствами Windows.
kthook::kthook_simple<WNDPROC> wndproc_hook{}; - Эту строку и все связанные с ней нужно будет удалить.
Добавляем переменную для хранения прошлого обработчика.
C++:
WNDPROC old_wndproc{};

HRESULT __stdcall on_wndproc(const decltype(wndproc_hook)& hook, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    // вызываем оригинал
    return CallWindowProcA(old_wndproc, hwnd, uMsg, wParam, lParam);
}

Ну и теперь вместо кода с установкой хука на основе kthook, пишем что-то такое:
C++:
old_wndproc = reinterpret_cast<WNDPROC>(SetWindowLongPtrA(game_hwnd, GWLP_WNDPROC, reinterpret_cast<LONG>(&on_wndproc)));

Внимательнее на строке возврата, код ниже показан на примере первого примера(да, тавтология)

Теперь попробуем обработать нажатие клавиши и вывести сообщение в чат.
C++:
HRESULT __stdcall on_wndproc(const decltype(wndproc_hook)& hook, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
    // если событие - нажатие клавиши
    case WM_KEYDOWN: {
        // если кнопка F11 и клавиша не повторялась до этого(нажата в первый раз)
        if (wParam == VK_F11 && (HIWORD(lParam) & KF_REPEAT) != KF_REPEAT) {
            sampapi::v037r3::RefChat()->AddChatMessage("", 0xFFFFFFFF, "Привет из WNDPROC!");
        }
        break;
    }
    }
    // вызываем оригинал
    return hook.get_trampoline()(hwnd, uMsg, wParam, lParam);
}

Компилируем, запускаем, жмякаем F11 и видим сообщение в чате:
1641349685464.png

Но у нас все еще осталась проблема - ImGui не будет обрабатывать наши нажатия.
Чтобы это сделать - нужно объявить обработчик ImGui, добавив где-нибудь такую строку:
C++:
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

Ну, а теперь, чтобы все верно работало, нам нужно вызвать эту функцию внутри обработчика событий окон, и блокировать дальнейшую обработку клавиш, если ImGui хочет этого. Пример показан только ради показа, как это делать, в случае когда будут окна зависящие от булевой переменной - нужно будет обернуть это в if (window_opened)
C++:
ImGui_ImplWin32_WndProcHandler(hwnd, uMsg, wParam, lParam);
auto& io = ImGui::GetIO();
if (io.WantCaptureKeyboard || io.WantCaptureMouse) {
    return 1;
}

Ну и теперь можем сделать тестовый пример:
C++:
ImGui::Begin("Window");

if (ImGui::Button("Click me!")) {
    sampapi::v037r3::RefChat()->AddChatMessage("", 0xFFFFFFFF, "Привет из ImGui!");
}

ImGui::End();

Конпелируем, запускаем, жмякаем по кнопочкам, и видим что все работает:
1641350938745.png

Но осталась одна проблема - ImGui расчитан на работу с wchar_t, который используется для UTF16 на windows, а обработчик событий нашего окна работает в CP_ACP кодировке. Чтобы это исправить, нам нужно конвертировать CP_ACP в UTF16 перед передачей в ImGui чтобы не видеть каракули при вводе.
Для этого, перед вызовом ImGui_ImplWin32_WndProcHandler, добавим такой код:
C++:
if (uMsg == WM_CHAR) {
    wchar_t wch;
    MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, reinterpret_cast<char*>(&wParam), 1, &wch, 1);
    wParam = wch;
}

C++:
#include <windows.h>
#include <string>
#include "d3d9.h"
#include "kthook/kthook.hpp"
#include "imgui.h"
#include "imgui_impl_dx9.h"
#include "imgui_impl_win32.h"
#include "sampapi/CChat.h"

extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

// Сигнатуры функций
using PresentSignature = HRESULT(__stdcall*)(IDirect3DDevice9*, const RECT*, const RECT*, HWND, const RGNDATA*);
using ResetSignature = HRESULT(__stdcall*)(IDirect3DDevice9*, D3DPRESENT_PARAMETERS*);
using InitGameInstance = HWND(__cdecl*)(HINSTANCE);

std::uintptr_t find_device(std::uint32_t Len) {
    static std::uintptr_t base = [](std::size_t Len) {
        std::string path_to(MAX_PATH, '\0');
        if (auto size = GetSystemDirectoryA(path_to.data(), MAX_PATH)) {
            path_to.resize(size);
            path_to += "\\d3d9.dll";
            std::uintptr_t dwObjBase = reinterpret_cast<std::uintptr_t>(LoadLibraryA(path_to.c_str()));
            while (dwObjBase++ < dwObjBase + Len) {
                if (*reinterpret_cast<std::uint16_t*>(dwObjBase + 0x00) == 0x06C7 &&
                    *reinterpret_cast<std::uint16_t*>(dwObjBase + 0x06) == 0x8689 &&
                    *reinterpret_cast<std::uint16_t*>(dwObjBase + 0x0C) == 0x8689) {
                    dwObjBase += 2;
                    break;
                }
            }
            return dwObjBase;
        }
        return std::uintptr_t(0);
    }(Len);
    return base;
}

void* get_function_address(int VTableIndex) {
    return (*reinterpret_cast<void***>(find_device(0x128000)))[VTableIndex];
}

kthook::kthook_signal<InitGameInstance> game_instance_init_hook{ 0x745560 };

HWND game_hwnd = []() {
    // Указатель на HWND внутри движка игры
    HWND* hwnd_ptr = *reinterpret_cast<HWND**>(0xC17054);
    if (hwnd_ptr != nullptr) {
        return *hwnd_ptr;
    }
    else {
        // Ставим коллбэк после выполенение оригинальной функции, т.к. нам нужен ее возврат
        game_instance_init_hook.after += [](const auto& hook, HWND& return_value, HINSTANCE inst) {
            // присваиваем нашей переменной значение, что вернула нам функция
            game_hwnd = return_value;
        };
        return HWND(0);
    }
}();

// Создаем хуки и сразу же инициализируем их на адреса в d3d9.dll
kthook::kthook_signal<PresentSignature> present_hook{ get_function_address(17) };
kthook::kthook_signal<ResetSignature> reset_hook{ get_function_address(16) };
kthook::kthook_simple<WNDPROC> wndproc_hook{};

HRESULT __stdcall on_wndproc(const decltype(wndproc_hook)& hook, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
    // если событие - нажатие клавиши
    case WM_KEYDOWN: {
        // если кнопка F11 и клавиша не повторялась до этого(нажата в первый раз)
        if (wParam == VK_F11 && (HIWORD(lParam) & KF_REPEAT) != KF_REPEAT) {
            sampapi::v037r3::RefChat()->AddChatMessage("", 0xFFFFFFFF, "Привет из WNDPROC!");
        }
        break;
    }
    }
    if (uMsg == WM_CHAR) {
        wchar_t wch;
        MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, reinterpret_cast<char*>(&wParam), 1, &wch, 1);
        wParam = wch;
    }
    ImGui_ImplWin32_WndProcHandler(hwnd, uMsg, wParam, lParam);
    auto& io = ImGui::GetIO();
    if (io.WantCaptureKeyboard || io.WantCaptureMouse) {
        return 1;
    }
    // вызываем оригинал
    return hook.get_trampoline()(hwnd, uMsg, wParam, lParam);
}

std::optional<HRESULT> on_present(const decltype(present_hook)& hook, IDirect3DDevice9* device_ptr, const RECT*, const RECT*, HWND, const RGNDATA*) {
    static bool ImGui_inited = false;
    if (!ImGui_inited) {
        // Создаем имгуи контекст
        ImGui::CreateContext();
        // Инициализируем OS зависимую часть(обрабатывает открытие шрифтов, обработку нажатия клавиш и т.д.)
        ImGui_ImplWin32_Init(game_hwnd);
        // Инициализируем render framework зависимую часть(обрабатывает отрисовку на экране, создание текстур шрифтов и т.д.)
        ImGui_ImplDX9_Init(device_ptr);

        auto latest_wndproc_ptr = GetWindowLongPtrA(game_hwnd, GWLP_WNDPROC);
        wndproc_hook.set_dest(latest_wndproc_ptr);
        wndproc_hook.set_cb(&on_wndproc);
        wndproc_hook.install();
        ImGui_inited = true;
    }
    // Инициализируем render часть для нового кадра
    ImGui_ImplDX9_NewFrame();
    // Инициализируем OS часть для нового кадра
    ImGui_ImplWin32_NewFrame();
    // Создаем новый кадр внутри ImGui
    ImGui::NewFrame();

    // получаем дравлист
    auto drawlist = ImGui::GetBackgroundDrawList();
    
    // Вычисляем размер текста
    std::string text{ "Hello from kin4!" };
    ImVec2 text_size = ImGui::CalcTextSize(text.c_str());
    // Рисуем прямоугольник с от 0;0 до text_size + 20; text_size + 20 белого цвета и закруглением 5 пикселей
    drawlist->AddRectFilled(ImVec2(0, 0), ImVec2(text_size.x + 20.0f, text_size.y + 20.0f), 0xFFFFFFFF, 5.0f);
    
    // Вычисляем позицию текста
    ImVec2 pos{ 10.0f, 10.0f };
    ImVec4 text_color{ 1.0f, 0.0f, 0.0f, 1.0f };
    // Рисуем текст
    drawlist->AddText(pos, ImGui::GetColorU32(text_color), text.c_str());

    ImGui::Begin("Window");

    if (ImGui::Button("Click me!")) {
        sampapi::v037r3::RefChat()->AddChatMessage("", 0xFFFFFFFF, "Привет из ImGui!");
    }

    ImGui::End();

    // Завершаем кадр ImGui
    ImGui::EndFrame();
    // Рендерим ImGuiв внутренний буффер
    ImGui::Render();
    // Отдаем Directx внутренний буффер на рендер
    ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData());
    return std::nullopt; // не нужно прерывать выполнение
}

std::optional<HRESULT> on_lost(const decltype(reset_hook)& hook, IDirect3DDevice9* device_ptr, D3DPRESENT_PARAMETERS* parameters) {
    ImGui_ImplDX9_InvalidateDeviceObjects();
    return std::nullopt; // не нужно прерывать выполнение
}

void on_reset(const decltype(reset_hook)& hook, HRESULT& return_value, IDirect3DDevice9* device_ptr, D3DPRESENT_PARAMETERS* parameters) {

}


BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH: {
        DisableThreadLibraryCalls(hModule);
        present_hook.before += on_present;
        reset_hook.before += on_lost;
        reset_hook.after += on_reset;
        break;
    }
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
 

loganhackerdff

Известный
868
519
если хукать по 0x747EB0, то не приходят WM_SETFOCUS и WM_KILLFOCUS, мб ещё что-то, но гайд топовый
ну хукать типо onBefore, я urmem oм хукал

Зато есть хороший способ, он ещё позволяет при отгрузке dllки убрать хук, это Subclass -> https://docs.microsoft.com/en-us/windows/win32/api/commctrl/nf-commctrl-setwindowsubclass
 
Последнее редактирование:

AugustTN

Известный
1,369
470
can anyone release this guide source code for me please :)
C++:
#include <windows.h>
#include <string>
#include "d3d9.h"
#include "kthook/kthook.hpp"
#include "imgui.h"
#include "imgui_impl_dx9.h"
#include "imgui_impl_win32.h"
#include "sampapi/CChat.h"

extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

// Сигнатуры функций
using PresentSignature = HRESULT(__stdcall*)(IDirect3DDevice9*, const RECT*, const RECT*, HWND, const RGNDATA*);
using ResetSignature = HRESULT(__stdcall*)(IDirect3DDevice9*, D3DPRESENT_PARAMETERS*);
using InitGameInstance = HWND(__cdecl*)(HINSTANCE);

std::uintptr_t find_device(std::uint32_t Len) {
    static std::uintptr_t base = [](std::size_t Len) {
        std::string path_to(MAX_PATH, '\0');
        if (auto size = GetSystemDirectoryA(path_to.data(), MAX_PATH)) {
            path_to.resize(size);
            path_to += "\\d3d9.dll";
            std::uintptr_t dwObjBase = reinterpret_cast<std::uintptr_t>(LoadLibraryA(path_to.c_str()));
            while (dwObjBase++ < dwObjBase + Len) {
                if (*reinterpret_cast<std::uint16_t*>(dwObjBase + 0x00) == 0x06C7 &&
                    *reinterpret_cast<std::uint16_t*>(dwObjBase + 0x06) == 0x8689 &&
                    *reinterpret_cast<std::uint16_t*>(dwObjBase + 0x0C) == 0x8689) {
                    dwObjBase += 2;
                    break;
                }
            }
            return dwObjBase;
        }
        return std::uintptr_t(0);
    }(Len);
    return base;
}

void* get_function_address(int VTableIndex) {
    return (*reinterpret_cast<void***>(find_device(0x128000)))[VTableIndex];
}

kthook::kthook_signal<InitGameInstance> game_instance_init_hook{ 0x745560 };

HWND game_hwnd = []() {
    // Указатель на HWND внутри движка игры
    HWND* hwnd_ptr = *reinterpret_cast<HWND**>(0xC17054);
    if (hwnd_ptr != nullptr) {
        return *hwnd_ptr;
    }
    else {
        // Ставим коллбэк после выполенение оригинальной функции, т.к. нам нужен ее возврат
        game_instance_init_hook.after += [](const auto& hook, HWND& return_value, HINSTANCE inst) {
            // присваиваем нашей переменной значение, что вернула нам функция
            game_hwnd = return_value;
        };
        return HWND(0);
    }
}();

// Создаем хуки и сразу же инициализируем их на адреса в d3d9.dll
kthook::kthook_signal<PresentSignature> present_hook{ get_function_address(17) };
kthook::kthook_signal<ResetSignature> reset_hook{ get_function_address(16) };
kthook::kthook_simple<WNDPROC> wndproc_hook{};

HRESULT __stdcall on_wndproc(const decltype(wndproc_hook)& hook, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
    // если событие - нажатие клавиши
    case WM_KEYDOWN: {
        // если кнопка F11 и клавиша не повторялась до этого(нажата в первый раз)
        if (wParam == VK_F11 && (HIWORD(lParam) & KF_REPEAT) != KF_REPEAT) {
            sampapi::v037r3::RefChat()->AddChatMessage("", 0xFFFFFFFF, "Привет из WNDPROC!");
        }
        break;
    }
    }
    if (uMsg == WM_CHAR) {
        wchar_t wch;
        MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, reinterpret_cast<char*>(&wParam), 1, &wch, 1);
        wParam = wch;
    }
    ImGui_ImplWin32_WndProcHandler(hwnd, uMsg, wParam, lParam);
    auto& io = ImGui::GetIO();
    if (io.WantCaptureKeyboard || io.WantCaptureMouse) {
        return 1;
    }
    // вызываем оригинал
    return hook.get_trampoline()(hwnd, uMsg, wParam, lParam);
}

std::optional<HRESULT> on_present(const decltype(present_hook)& hook, IDirect3DDevice9* device_ptr, const RECT*, const RECT*, HWND, const RGNDATA*) {
    static bool ImGui_inited = false;
    if (!ImGui_inited) {
        // Создаем имгуи контекст
        ImGui::CreateContext();
        // Инициализируем OS зависимую часть(обрабатывает открытие шрифтов, обработку нажатия клавиш и т.д.)
        ImGui_ImplWin32_Init(game_hwnd);
        // Инициализируем render framework зависимую часть(обрабатывает отрисовку на экране, создание текстур шрифтов и т.д.)
        ImGui_ImplDX9_Init(device_ptr);

        auto latest_wndproc_ptr = GetWindowLongPtrA(game_hwnd, GWLP_WNDPROC);
        wndproc_hook.set_dest(latest_wndproc_ptr);
        wndproc_hook.set_cb(&on_wndproc);
        wndproc_hook.install();
        ImGui_inited = true;
    }
    // Инициализируем render часть для нового кадра
    ImGui_ImplDX9_NewFrame();
    // Инициализируем OS часть для нового кадра
    ImGui_ImplWin32_NewFrame();
    // Создаем новый кадр внутри ImGui
    ImGui::NewFrame();

    // получаем дравлист
    auto drawlist = ImGui::GetBackgroundDrawList();
    
    // Вычисляем размер текста
    std::string text{ "Hello from kin4!" };
    ImVec2 text_size = ImGui::CalcTextSize(text.c_str());
    // Рисуем прямоугольник с от 0;0 до text_size + 20; text_size + 20 белого цвета и закруглением 5 пикселей
    drawlist->AddRectFilled(ImVec2(0, 0), ImVec2(text_size.x + 20.0f, text_size.y + 20.0f), 0xFFFFFFFF, 5.0f);
    
    // Вычисляем позицию текста
    ImVec2 pos{ 10.0f, 10.0f };
    ImVec4 text_color{ 1.0f, 0.0f, 0.0f, 1.0f };
    // Рисуем текст
    drawlist->AddText(pos, ImGui::GetColorU32(text_color), text.c_str());

    ImGui::Begin("Window");

    if (ImGui::Button("Click me!")) {
        sampapi::v037r3::RefChat()->AddChatMessage("", 0xFFFFFFFF, "Привет из ImGui!");
    }

    ImGui::End();

    // Завершаем кадр ImGui
    ImGui::EndFrame();
    // Рендерим ImGuiв внутренний буффер
    ImGui::Render();
    // Отдаем Directx внутренний буффер на рендер
    ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData());
    return std::nullopt; // не нужно прерывать выполнение
}

std::optional<HRESULT> on_lost(const decltype(reset_hook)& hook, IDirect3DDevice9* device_ptr, D3DPRESENT_PARAMETERS* parameters) {
    ImGui_ImplDX9_InvalidateDeviceObjects();
    return std::nullopt; // не нужно прерывать выполнение
}

void on_reset(const decltype(reset_hook)& hook, HRESULT& return_value, IDirect3DDevice9* device_ptr, D3DPRESENT_PARAMETERS* parameters) {

}


BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH: {
        DisableThreadLibraryCalls(hModule);
        present_hook.before += on_present;
        reset_hook.before += on_lost;
        reset_hook.after += on_reset;
        break;
    }
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
??
 

SADFI2259X

Участник
92
76
C++:
#include <windows.h>
#include <string>
#include "d3d9.h"
#include "kthook/kthook.hpp"
#include "imgui.h"
#include "imgui_impl_dx9.h"
#include "imgui_impl_win32.h"
#include "sampapi/CChat.h"

extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

// Сигнатуры функций
using PresentSignature = HRESULT(__stdcall*)(IDirect3DDevice9*, const RECT*, const RECT*, HWND, const RGNDATA*);
using ResetSignature = HRESULT(__stdcall*)(IDirect3DDevice9*, D3DPRESENT_PARAMETERS*);
using InitGameInstance = HWND(__cdecl*)(HINSTANCE);

std::uintptr_t find_device(std::uint32_t Len) {
    static std::uintptr_t base = [](std::size_t Len) {
        std::string path_to(MAX_PATH, '\0');
        if (auto size = GetSystemDirectoryA(path_to.data(), MAX_PATH)) {
            path_to.resize(size);
            path_to += "\\d3d9.dll";
            std::uintptr_t dwObjBase = reinterpret_cast<std::uintptr_t>(LoadLibraryA(path_to.c_str()));
            while (dwObjBase++ < dwObjBase + Len) {
                if (*reinterpret_cast<std::uint16_t*>(dwObjBase + 0x00) == 0x06C7 &&
                    *reinterpret_cast<std::uint16_t*>(dwObjBase + 0x06) == 0x8689 &&
                    *reinterpret_cast<std::uint16_t*>(dwObjBase + 0x0C) == 0x8689) {
                    dwObjBase += 2;
                    break;
                }
            }
            return dwObjBase;
        }
        return std::uintptr_t(0);
    }(Len);
    return base;
}

void* get_function_address(int VTableIndex) {
    return (*reinterpret_cast<void***>(find_device(0x128000)))[VTableIndex];
}

kthook::kthook_signal<InitGameInstance> game_instance_init_hook{ 0x745560 };

HWND game_hwnd = []() {
    // Указатель на HWND внутри движка игры
    HWND* hwnd_ptr = *reinterpret_cast<HWND**>(0xC17054);
    if (hwnd_ptr != nullptr) {
        return *hwnd_ptr;
    }
    else {
        // Ставим коллбэк после выполенение оригинальной функции, т.к. нам нужен ее возврат
        game_instance_init_hook.after += [](const auto& hook, HWND& return_value, HINSTANCE inst) {
            // присваиваем нашей переменной значение, что вернула нам функция
            game_hwnd = return_value;
        };
        return HWND(0);
    }
}();

// Создаем хуки и сразу же инициализируем их на адреса в d3d9.dll
kthook::kthook_signal<PresentSignature> present_hook{ get_function_address(17) };
kthook::kthook_signal<ResetSignature> reset_hook{ get_function_address(16) };
kthook::kthook_simple<WNDPROC> wndproc_hook{};

HRESULT __stdcall on_wndproc(const decltype(wndproc_hook)& hook, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
    // если событие - нажатие клавиши
    case WM_KEYDOWN: {
        // если кнопка F11 и клавиша не повторялась до этого(нажата в первый раз)
        if (wParam == VK_F11 && (HIWORD(lParam) & KF_REPEAT) != KF_REPEAT) {
            sampapi::v037r3::RefChat()->AddChatMessage("", 0xFFFFFFFF, "Привет из WNDPROC!");
        }
        break;
    }
    }
    if (uMsg == WM_CHAR) {
        wchar_t wch;
        MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, reinterpret_cast<char*>(&wParam), 1, &wch, 1);
        wParam = wch;
    }
    ImGui_ImplWin32_WndProcHandler(hwnd, uMsg, wParam, lParam);
    auto& io = ImGui::GetIO();
    if (io.WantCaptureKeyboard || io.WantCaptureMouse) {
        return 1;
    }
    // вызываем оригинал
    return hook.get_trampoline()(hwnd, uMsg, wParam, lParam);
}

std::optional<HRESULT> on_present(const decltype(present_hook)& hook, IDirect3DDevice9* device_ptr, const RECT*, const RECT*, HWND, const RGNDATA*) {
    static bool ImGui_inited = false;
    if (!ImGui_inited) {
        // Создаем имгуи контекст
        ImGui::CreateContext();
        // Инициализируем OS зависимую часть(обрабатывает открытие шрифтов, обработку нажатия клавиш и т.д.)
        ImGui_ImplWin32_Init(game_hwnd);
        // Инициализируем render framework зависимую часть(обрабатывает отрисовку на экране, создание текстур шрифтов и т.д.)
        ImGui_ImplDX9_Init(device_ptr);

        auto latest_wndproc_ptr = GetWindowLongPtrA(game_hwnd, GWLP_WNDPROC);
        wndproc_hook.set_dest(latest_wndproc_ptr);
        wndproc_hook.set_cb(&on_wndproc);
        wndproc_hook.install();
        ImGui_inited = true;
    }
    // Инициализируем render часть для нового кадра
    ImGui_ImplDX9_NewFrame();
    // Инициализируем OS часть для нового кадра
    ImGui_ImplWin32_NewFrame();
    // Создаем новый кадр внутри ImGui
    ImGui::NewFrame();

    // получаем дравлист
    auto drawlist = ImGui::GetBackgroundDrawList();
   
    // Вычисляем размер текста
    std::string text{ "Hello from kin4!" };
    ImVec2 text_size = ImGui::CalcTextSize(text.c_str());
    // Рисуем прямоугольник с от 0;0 до text_size + 20; text_size + 20 белого цвета и закруглением 5 пикселей
    drawlist->AddRectFilled(ImVec2(0, 0), ImVec2(text_size.x + 20.0f, text_size.y + 20.0f), 0xFFFFFFFF, 5.0f);
   
    // Вычисляем позицию текста
    ImVec2 pos{ 10.0f, 10.0f };
    ImVec4 text_color{ 1.0f, 0.0f, 0.0f, 1.0f };
    // Рисуем текст
    drawlist->AddText(pos, ImGui::GetColorU32(text_color), text.c_str());

    ImGui::Begin("Window");

    if (ImGui::Button("Click me!")) {
        sampapi::v037r3::RefChat()->AddChatMessage("", 0xFFFFFFFF, "Привет из ImGui!");
    }

    ImGui::End();

    // Завершаем кадр ImGui
    ImGui::EndFrame();
    // Рендерим ImGuiв внутренний буффер
    ImGui::Render();
    // Отдаем Directx внутренний буффер на рендер
    ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData());
    return std::nullopt; // не нужно прерывать выполнение
}

std::optional<HRESULT> on_lost(const decltype(reset_hook)& hook, IDirect3DDevice9* device_ptr, D3DPRESENT_PARAMETERS* parameters) {
    ImGui_ImplDX9_InvalidateDeviceObjects();
    return std::nullopt; // не нужно прерывать выполнение
}

void on_reset(const decltype(reset_hook)& hook, HRESULT& return_value, IDirect3DDevice9* device_ptr, D3DPRESENT_PARAMETERS* parameters) {

}


BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH: {
        DisableThreadLibraryCalls(hModule);
        present_hook.before += on_present;
        reset_hook.before += on_lost;
        reset_hook.after += on_reset;
        break;
    }
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
??
no i mean in visual studio can you send me the project in zip or rar file please ?
 

AnWu

Известный
Всефорумный модератор
4,777
5,400
@kin4stat
C++:
    case DLL_PROCESS_DETACH: {
        ImGui_ImplDX9_Shutdown();
        ImGui_ImplWin32_Shutdown();
        ImGui::DestroyContext();
        break;
    }