 |

05.01.2022, 06:54
|
|
Флудер
Регистрация: 06.11.2017
Сообщений: 2,759
С нами:
4483143
Репутация:
183
|
|
- Создание ASI-плагина с нуля
- Хуки – что это такое и как с ними работать
- Безопасная инициализация и работа с SAMP
- Работа с рендером и Directx9
- Обработка событий окна + ImGui
В этом гайде будет рассказано про работу с событиями окна, а также их передачей в ImGui
При использовании на других ресурсах необходимо указание авторства и ссылки на оригинальную темы!
Все действия производились на Visual Studio 2019 с параметром /std:c++17, в других версиях интерфейс может отличаться.
И так, начнем:
Для этого гайда нам не нужно никаким образом настраивать проект и что-то в него добавлять. Можно воспользоваться готовым из гайда [4]
В Windows существует множество различных возможностей по работе с окнами(на то она и Windows). И чтобы все было максимально гибко, Windows может отдавать высокоуровневые события окон, и низкоуровневые. Про низкоуровневые события сегодня и пойдет речь.
Все события окна Windows присылает в коллбэк WindowProc. Сюда входят нажатия мышкой, нажатиян на клавиатуру, перемещение окна, изменение его размера, сворачивание, разворачивание и еще куча других событий(около 2000, если не ошибаюсь).
У каждого приложения должен быть создан коллбэк на события окна, иначе вы не сможете создать окно. Т.к. мы работаем внутри готового окна, нам нужно перехватывать уже существующий обработчик событий.
Делать это можно несколькими способами:
- Штатными средствами Windows
- Перехват самого первого коллбэка(нужно знать его адрес)
- Перехват самого последнего зарегистрированного коллбэка
В этом гайде я покажу все три способа.
Начнем с того, что для всех способов, кроме 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
)
;
}
}
(
)
;
Наверное вы спросите, что произошло
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 строку кода - попробуйте сделать это сами, адрес игровой функции -
Также нужно учитывать, что функция игры будет самой последней в цепочке, и поэтому если кто-то зарегистрирует свой обработчик позже - вы можете не получить событие.
Ну и последний способ - зарегистрировать свой обработчик средствами 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 хочет этого. Пример показан только ради показа, как это делать, в случае когда будут окна зависящие от булевой переменной - нужно будет обернуть это в
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
;
}
|
|
|

31.08.2022, 00:35
|
|
Постоянный
Регистрация: 24.07.2017
Сообщений: 867
С нами:
4633764
Репутация:
148
|
|
|
|
|

15.11.2022, 18:17
|
|
Познающий
Регистрация: 05.09.2021
Сообщений: 92
С нами:
2468408
Репутация:
18
|
|
can anyone release this guide source code for me please 
|
|
|

15.11.2022, 18:34
|
|
Познавший АНТИЧАТ
Регистрация: 14.06.2021
Сообщений: 1,354
С нами:
2587494
Репутация:
88
|
|
Сообщение от 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
;
}
??
|
|
|

17.11.2022, 15:28
|
|
Познающий
Регистрация: 05.09.2021
Сообщений: 92
С нами:
2468408
Репутация:
18
|
|
Сообщение от 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 ?
|
|
|

07.01.2023, 23:57
|
|
Флудер
Регистрация: 08.11.2017
Сообщений: 4,787
С нами:
4480376
Репутация:
183
|
|
@kin4stat
C++:
Код:
case
DLL_PROCESS_DETACH
:
{
ImGui_ImplDX9_Shutdown
(
)
;
ImGui_ImplWin32_Shutdown
(
)
;
ImGui
::
DestroyContext
(
)
;
break
;
}
|
|
|

29.06.2023, 16:57
|
|
Познающий
Регистрация: 05.09.2021
Сообщений: 92
С нами:
2468408
Репутация:
18
|
|
how to see if the game is inited ?
|
|
|

11.07.2025, 02:39
|
|
Познавший АНТИЧАТ
Регистрация: 27.11.2020
Сообщений: 1,431
С нами:
2874035
Репутация:
183
|
|
Сообщение от AnWu
@kin4stat
C++:
Код:
case
DLL_PROCESS_DETACH
:
{
ImGui_ImplDX9_Shutdown
(
)
;
ImGui_ImplWin32_Shutdown
(
)
;
ImGui
::
DestroyContext
(
)
;
break
;
}
Это помогает отгрузить без краша? У меня нет 
|
|
|

11.07.2025, 04:00
|
|
Флудер
Регистрация: 02.02.2019
Сообщений: 5,070
С нами:
3831395
Репутация:
183
|
|
Сообщение от Smeruxa
Это помогает отгрузить без краша? У меня нет
ImGui_ImplDX9_InvalidateDeviceObjects в детач плагина вставь. Должно помочь
|
|
|
|
 |
|
Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
|
|
|
|