ANTICHAT

ANTICHAT (https://forum.antichat.xyz/index.php)
-   С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby (https://forum.antichat.xyz/forumdisplay.php?f=24)
-   -   Обработка событий окна + ImGui [5] (https://forum.antichat.xyz/showthread.php?t=1415851)

kin4stat 05.01.2022 06:54

  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

game_instance_init_hook
{
0x745560
}
;
HWND game_hwnd
=
[
]
(
)
{
// Указатель на HWND внутри движка игры
HWND
*
hwnd_ptr
=
*
reinterpret_cast

(
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_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_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

(
SetWindowLongPtrA
(
game_hwnd
,
GWLP_WNDPROC
,
reinterpret_cast

(
&
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 и видим сообщение в чате:

Цитата:

Сообщение от Спойлер

Но у нас все еще осталась проблема - 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
(
)
;



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

Цитата:

Сообщение от Спойлер

Но осталась одна проблема - 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

(
&
wParam
)
,
1
,
&
wch
,
1
)
;
wParam
=
wch
;
}



Цитата:

Сообщение от Спойлер


C++:





Код:

#include
#include
#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

(
LoadLibraryA
(
path_to
.
c_str
(
)
)
)
;
while
(
dwObjBase
++

(
dwObjBase
+
0x00
)
==
0x06C7
&&
*
reinterpret_cast

(
dwObjBase
+
0x06
)
==
0x8689
&&
*
reinterpret_cast

(
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

(
find_device
(
0x128000
)
)
)
[
VTableIndex
]
;
}
kthook
::
kthook_signal

game_instance_init_hook
{
0x745560
}
;
HWND game_hwnd
=
[
]
(
)
{
// Указатель на HWND внутри движка игры
HWND
*
hwnd_ptr
=
*
reinterpret_cast

(
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

present_hook
{
get_function_address
(
17
)
}
;
kthook
::
kthook_signal

reset_hook
{
get_function_address
(
16
)
}
;
kthook
::
kthook_simple

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

(
&
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

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

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 31.08.2022 00:35

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

ну хукать типо onBefore, я urmem oм хукал

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

SADFI2259X 15.11.2022 18:17

can anyone release this guide source code for me please :)

AugustTN 15.11.2022 18:34

Цитата:

Сообщение от SADFI2259X

can anyone release this guide source code for me please :)

C++:





Код:

#include
#include
#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

(
LoadLibraryA
(
path_to
.
c_str
(
)
)
)
;
while
(
dwObjBase
++

(
dwObjBase
+
0x00
)
==
0x06C7
&&
*
reinterpret_cast

(
dwObjBase
+
0x06
)
==
0x8689
&&
*
reinterpret_cast

(
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

(
find_device
(
0x128000
)
)
)
[
VTableIndex
]
;
}
kthook
::
kthook_signal

game_instance_init_hook
{
0x745560
}
;
HWND game_hwnd
=
[
]
(
)
{
// Указатель на HWND внутри движка игры
HWND
*
hwnd_ptr
=
*
reinterpret_cast

(
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

present_hook
{
get_function_address
(
17
)
}
;
kthook
::
kthook_signal

reset_hook
{
get_function_address
(
16
)
}
;
kthook
::
kthook_simple

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

(
&
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

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

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 17.11.2022 15:28

Цитата:

Сообщение от Loocking

C++:





Код:

#include
#include
#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

(
LoadLibraryA
(
path_to
.
c_str
(
)
)
)
;
while
(
dwObjBase
++

(
dwObjBase
+
0x00
)
==
0x06C7
&&
*
reinterpret_cast

(
dwObjBase
+
0x06
)
==
0x8689
&&
*
reinterpret_cast

(
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

(
find_device
(
0x128000
)
)
)
[
VTableIndex
]
;
}
kthook
::
kthook_signal

game_instance_init_hook
{
0x745560
}
;
HWND game_hwnd
=
[
]
(
)
{
// Указатель на HWND внутри движка игры
HWND
*
hwnd_ptr
=
*
reinterpret_cast

(
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

present_hook
{
get_function_address
(
17
)
}
;
kthook
::
kthook_signal

reset_hook
{
get_function_address
(
16
)
}
;
kthook
::
kthook_simple

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

(
&
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

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

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 07.01.2023 23:57

@kin4stat

C++:





Код:

case
DLL_PROCESS_DETACH
:
{
ImGui_ImplDX9_Shutdown
(
)
;
ImGui_ImplWin32_Shutdown
(
)
;
ImGui
::
DestroyContext
(
)
;
break
;
}


SADFI2259X 29.06.2023 16:57

how to see if the game is inited ?

Smeruxa 11.07.2025 02:39

Цитата:

Сообщение от AnWu

@kin4stat

C++:





Код:

case
DLL_PROCESS_DETACH
:
{
ImGui_ImplDX9_Shutdown
(
)
;
ImGui_ImplWin32_Shutdown
(
)
;
ImGui
::
DestroyContext
(
)
;
break
;
}



Это помогает отгрузить без краша? У меня нет :)

ARMOR 11.07.2025 04:00

Цитата:

Сообщение от Smeruxa

Это помогает отгрузить без краша? У меня нет :)

ImGui_ImplDX9_InvalidateDeviceObjects в детач плагина вставь. Должно помочь


Время: 22:02