Перейти к содержанию

Antonshka

Пользователи+
  • Постов

    410
  • Зарегистрирован

  • Посещение

  • Победитель дней

    16

Сообщения, опубликованные Antonshka

  1. Может попробовать создать вторую DLL, вспомогательную. Назовем ее B. Основную же DLL, назовем А

    В как и А, загружается в процесс. Но В загружается на постоянную основу, - она пребудет в процессе до его завершения. Да и ладно, он же маленькая.

    Задача В, выгружать любую указанную DLL.

    Когда хотим выгрузить А, то А вызывает единственную функцию в B, функцию Unload(HANDLE A, HINSTANCE A). Эта функция принимает хендл потока А, и инстанс DLL А.

    В этой функции создается поток P (причем сразу же вызывается для P CloseHandle).

    А, после возврата из Unload(), выходит через свой return 0.

    Поток P, в цикле, проверяет состояние объекта потока A, на STILL_ACTIVE. Как только этот флаг будет сброшен, P вызывает FreeLibrary для DLL А. Далее P закрывает хендл объекта потока А, и наконец, сам завершается.

  2. Привет всем!

    Я решил отвлечься на время от написания библиотеки и попробовать себя в написании небольшой DLL. Я давно планировал это сделать. На С++.

    И первая трудность, с которой я столкнулся, это правильная выгрузка DLL. Вот так, только самое начало, а уже проблема.

     

    На просторах интернета я нашел исходники одного приложения. В нем имеется такой код

    Спойлер
    #include "Main.h"
    
    DWORD WINAPI RunCT(LPVOID lpArg)
    {
      g_mainHandle = new Main();
      if (g_mainHandle->Initialize())
        g_mainHandle->Run();
      delete g_mainHandle;
      FreeLibraryAndExitThread(g_dllHandle, 0);
      return 0;
    }
    
    DWORD WINAPI DllMain(_In_ HINSTANCE hInstance, _In_ DWORD fdwReason, _In_ LPVOID lpvReserved)
    {
      if (fdwReason == DLL_PROCESS_ATTACH)
      {
        g_dllHandle = hInstance;
        CreateThread(NULL, NULL, RunCT, NULL, NULL, NULL);
      }
      return 1;
    }

     

     

    Какие ошибки в этом коде вы видите?

    1 - по совету Джефри Рихтера, нужно стараться всегда использовать _beginthreadex, а не CreateThread

    выписка из книги Джефри Рихтера

    Спойлер

    CreateThread — это Windows-функция, создающая поток. Но никогда не вы-
    зывайте ее, если Вы пишете код на C/C++. Вместо нее Вы должны использо-
    вать функцию _beginthreadex из библиотеки Visual C++. (Если Вы работаете с
    другим компилятором, он должен поддерживать свой эквивалент функции
    CreateThread.) Что именно делает _beginthreadex и почему это так важно, я
    объясню потом.

    2 - в коде из интернета, вызывается FreeLibraryAndExitThread

    выписка из книги Джефри Рихтера

    Спойлер

    DLL можно выгрузить и с помощью другой функции:
    VOID FreeLibraryAndExitThread(
    HINSTANCE hinstDll,
    DWORD dwExitCode);
    Она реализована в Kernel32.dll так:
    VOID FreeLibraryAndExitThread(HINSTANCE hinstDll, DWORD dwExitCode) {
    FreeLibrary(hinstDll);
    ExitThread(dwExitCode);
    }

    Код из интернета, вызывая FreeLibraryAndExitThread, по сути вначале вызывает FreeLibrary, и весь модуль DLL выгружается, а затем, вызывает ExitThread, и поток завершается, и уже не возвращается в процедуру, из которой была вызвана FreeLibraryAndExitThread. А так как поток уже не возвращается, то -

    выписка из книги Джефри Рихтера

    Спойлер

    Поток можно завершить принудительно, вызвав:

    VOID ExitThread(DWORD dwExitCode);
    При этом освобождаются все ресурсы операционной системы, выделенные дан-
    ному потоку, но C/C++-ресурсы (например, объекты, созданные из C++-классов) не
    очищаются. Именно поэтому лучше возвращать управление из функции потока, чем
    самому вызывать функцию ExitThread. (Подробнее на эту тему см. раздел «Функция
    ExitProcess» в главе 4.)

    В коде из интернета объект класса Main конечно хоть и удаляется через delete, но есть же еще и другие вещи -

    выписка из книги Джефри Рихтера

    Спойлер

    Явные вызовы ExitProcess и ExitThread — распространенная ошибка, которая
    мешает правильной очистке ресурсов. В случае ExitThread процесс продолжа-
    ет работать, но при этом весьма вероятна утечка памяти или других ресурсов.

     

     

    Итак, согласно Рихтеру, мне нужно использовать _beginthreadex, и нужно дать потоку завершиться самому, то есть выйти из свой процедуры через return 0.

    Так как же мне тогда произвести выгрузку DLL? Оставив процесс запущенным.

     

    Еще один интересный факт, обнаруженный в ходе теста, - если поток создан через CreateThread, то вызов FreeLibraryAndExitThread отрабатывает нормально, в том плане что модуль DLL сразу же выгружается. Но если поток создан через _beginthreadex, то вызов FreeLibraryAndExitThread не выгружает модуль DLL вообще. Да и было бы неправильно, создать поток через _beginthreadex, а звершить его через ExitThread (который в FreeLibraryAndExitThread), вместо _endthreadex.

  3. Интересная задача. А если вначале отключить интернет, затем запустить стим и саму игру, и затем, когда осталась скажем одна минута, перевести компьютерные системные часы на минут пять назад?

    Если время в игре также переведется на пять минут назад, то по идее игра считывает системное время. А если нет, то по идее игра ведет свой собственный отсчет от момента запуска игры, прибавив предварительно офсет из файла сохранения.

    Скорее всего значение шифруется. Если получится у тебя найти, напиши, интересно, что там и как.

     

    Еще помню, где-то в WinAPI мне попадалась на глаза функция возвращающая время работы процесса, то есть например игры. Может с ней поработать еще. Или не с ней конкретно, а с той которую она вызывает, из Kernel.

     

  4. 13 часов назад, JackNewMan сказал:

    Как-то неудобно немного. Возможно я чего-то не знаю или что-то делаю неправильно.

    Дело в том, что у компьютерного процессора всего несколько регистров. RAX, RBX, RCX, и еше некоторые.

    Для сравнения, - у человека есть всего две руки. Человек может одновременно держать в руках всего две вещи. Чтобы взять третью, необходимо положить первую или вторую. И так по очереди. Чтобы узнать, что держит человек сейчас, в данную миллисекунду, нужно подойти к нему именно в данную миллисекунду, и посмотреть.  В следующую миллисекунду, человек уже возможно будет держать что-то другое, и мы уже не увидим и не узнаем, что было до этого.

    Так и процессор, постоянно меняет содержимое своих регистров. А дело дебаггера, подойти к процессору, и как-бы поставить его на паузу, не давая ему взять что-то другое.

     

     

    Человек может вести запись того, что он держал в своих руках последние 5, 10, 20, минут. Но специально человек этого не делает.

    9 часов назад, Garik66 сказал:

    для этих целей проще ИМХО воспользоваться опцией СЕ трассировка

    Трассировка, это и есть запись процессором того, что он держал в своих регистрах в последние, указанные нами, секунды. Или не секунды, - я забыл, что-там указывается.

  5. Привет всем.

    На днях я захотел нарисовать скругленный прямоугольник средствами стандартного GDI. Его я всегда использовал и использую. Но, как оказалось, GDI не очень хорош в этом плане. Скругленные края получаются зубчатыми.

    Я решил попробовать GDI+, с которым я не знаком вообще, и который мне всегда был интересен. Я открыл сайт MSDN, начал читать все подряд. Читаю и думаю, -"вот же оно, зачем я использовал стандартный GDI, перепишу теперь все на GDI+!".

    Но, не долго я радовался. Тестирование, и информация из интернета, показали, - GDI+ медленнее GDI.

    GDI+ не использует Hardware acceleration, он использует Software acceleration. Конечно, у GDI+ есть много преимуществ, как-то, Font, который не зависит от разрешения монитора, альфа смешивание, кривые, трансформации.

     

    Думается мне теперь, что лучшая схема использования этих двух GDI, это их комбинирование. Там где стандартный GDI не справляется, то есть не имеет способности, нужно использовать GDI+.

    Всегда использовать по максимуму стандартный GDI, а GDI+, использовать только в случае острой необходимости.

     

    Вот один тест для GDI и GDI+ на градиентную заливку. Замер скорости отрисовки.

    Спойлер

    12/04/22  11:49:56 - [INFO] GDI gradient elapsed_ms = 10
    12/04/22  11:49:56 - [INFO] Gdiplus gradient elapsed_ms = 12
    12/04/22  11:49:56 - [INFO] GDI solid brush elapsed_ms = 0
    12/04/22  11:49:57 - [INFO] GDI gradient elapsed_ms = 3
    12/04/22  11:49:57 - [INFO] Gdiplus gradient elapsed_ms = 6
    12/04/22  11:49:57 - [INFO] GDI solid brush elapsed_ms = 0
    12/04/22  11:49:57 - [INFO] GDI gradient elapsed_ms = 2
    12/04/22  11:49:57 - [INFO] Gdiplus gradient elapsed_ms = 8
    12/04/22  11:49:57 - [INFO] GDI solid brush elapsed_ms = 0
    12/04/22  11:49:59 - [INFO] GDI gradient elapsed_ms = 1
    12/04/22  11:49:59 - [INFO] Gdiplus gradient elapsed_ms = 5
    12/04/22  11:49:59 - [INFO] GDI solid brush elapsed_ms = 0
    12/04/22  11:50:01 - [INFO] GDI gradient elapsed_ms = 1
    12/04/22  11:50:01 - [INFO] Gdiplus gradient elapsed_ms = 5
    12/04/22  11:50:01 - [INFO] GDI solid brush elapsed_ms = 0
    12/04/22  11:50:01 - [INFO] GDI gradient elapsed_ms = 1
    12/04/22  11:50:01 - [INFO] Gdiplus gradient elapsed_ms = 4
    12/04/22  11:50:01 - [INFO] GDI solid brush elapsed_ms = 0

     

    Это код.

    Спойлер
    LRESULT onPaintForMainWindow(WGW::OnPaintEvent* eventParam) {
    	RECT clientRect{ 0 };
    	GetClientRect(eventParam->hWnd, &clientRect);
    
    	//GDI gradient.
    	auto begin = std::chrono::steady_clock::now();
    	WGW::GdiGraphics::gradientRectangle(eventParam->compDC, //Пример из книги программирование графики для Windows - Фень Юань
    		clientRect.left,
    		clientRect.top,
    		clientRect.right,
    		clientRect.bottom,
    		RGB(100, 77, 155), RGB(103, 168, 179), 45);
    	auto end = std::chrono::steady_clock::now();
    	auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin);
    	WGW::GUILogger::write(WGW::LoggerNotifyType::Info, L"GDI gradient elapsed_ms = %d\n", elapsed_ms);
    
    	//Gdiplus gradient.
    	Gdiplus::LinearGradientBrush linGrBrush(
    		Gdiplus::Point(0, 0),
    		Gdiplus::Point(clientRect.right - clientRect.left, clientRect.bottom - clientRect.top),
    		Gdiplus::Color(255, 100, 77, 155),
    		Gdiplus::Color(255, 103, 168, 100));
    	Gdiplus::Graphics graphics(eventParam->compDC);
    	begin = std::chrono::steady_clock::now();
    	graphics.FillRectangle(&linGrBrush, 0, 0, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top);
    	end = std::chrono::steady_clock::now();
    	elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin);
    	WGW::GUILogger::write(WGW::LoggerNotifyType::Info, L"Gdiplus gradient elapsed_ms = %d\n", elapsed_ms);
    	
    	//GDI solid brush.
    	begin = std::chrono::steady_clock::now();
    	HBRUSH brush = CreateSolidBrush(RGB(100, 77, 155));
    	begin = std::chrono::steady_clock::now();
    	FillRect(eventParam->compDC, &clientRect, brush);
    	end = std::chrono::steady_clock::now();
    	elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin);
    	WGW::GUILogger::write(WGW::LoggerNotifyType::Info, L"GDI solid brush elapsed_ms = %d\n", elapsed_ms);
    	DeleteBrush(brush);
    
    
    	CopyRect(eventParam->updateRect, &clientRect);
    	return FALSE;
    }

     

     

    В этом коде вначале рисуется градиентная заливка стандартным GDI. Код, который отвечает за эту заливку, взят из книги Фень Юань, и приведен ниже. Затем идет заливка GDI+. И наконец, простая заливка, не градиентная, стандартным GDI. Простую заливку средствами GDI+ я не стал тестировать.

    Сильно заметно, как при резком изменении размера окна, правый его край отстает на 1 сантиметр, это при градиентной заливке средствами GDI+. При стандартном GDI, отставание в 5 - 7 раз меньше.

     

    Градиентная заливка из книги Фень Юань

    Спойлер
    	BOOL GdiGraphics::gradientRectangle(HDC hDC, INT x0, INT y0, INT x1, INT y1, COLORREF c0, COLORREF c1, INT angle) {
    		TRIVERTEX vert[4] = {
    			{ x0, y0,  r16(c0), g16(c0), b16(c0), 0 },
    			{ x1, y1,  r16(c1), g16(c1), b16(c1), 0 },
    			{ x0, y1,  r16(c0, c1), g16(c0, c1), b16(c0, c1), 0 },
    			{ x1, y0,  r16(c0, c1), g16(c0, c1), b16(c0, c1), 0 }
    		};
    		ULONG index[] = { 0, 1, 2, 0, 1, 3 };
    		switch (angle % 180) {
    		case 0:
    			return GradientFill(hDC, vert, 2, index, 1, GRADIENT_FILL_RECT_H);
    		case 45:
    			return GradientFill(hDC, vert, 4, index, 2, GRADIENT_FILL_TRIANGLE);
    		case 90:
    			return GradientFill(hDC, vert, 2, index, 1, GRADIENT_FILL_RECT_V);
    		case 135:
    			vert[0].x = x1;
    			vert[3].x = x0;
    			vert[1].x = x0;
    			vert[2].x = x1;
    			return GradientFill(hDC, vert, 4, index, 2, GRADIENT_FILL_TRIANGLE);
    		default:
    			return GradientFill(hDC, vert, 2, index, 1, GRADIENT_FILL_RECT_H);
    		}
    		return FALSE;
    	}

     

     

  6. В 01.12.2022 в 19:16, JackNewMan сказал:

    Вроде и смотрю на картинку, но как вы на это окно вышли не имею ни малейшего понятия. 😃 Вот я в ассемблере, куда тыкать дальше?

    В теории можно подключать дебаггер, триггерить инструкцию и в дебаггере смотреть что к этой инструкции пристаёт и уже через ту инструкцию смотреть регистры этой, также её триггернув, но это извращение если честно.

    Значение регистров возможно узнать только при подключении отладчика. Так устроена работа системы.

  7. А, понятно. У тебя постоянное домножение.

    Мне больше нравится эффект нитро, - машина ускоряется.пока зажата клавиша. Если к этому прикрутить еще и принудительные: динамическое изменение FOV, увеличение значения тахометра, увеличение частот вращения задних колес, так чтобы дым пошел, небольшое приподнятие передка машины через углы поворота, чуть-чуть тряски камеры, через DirectSound звук закиси азота, - было бы забавно.

     

    Кстати, у тебя скрипт можно объединить в один. Так как у тебя инструкции рядом. Я в своем время также как и ты для каждой инструкции писал отдельный скрипт, потом мне это надоело.

    Спойлер
    cf3.exe+3EB828: D9 00           - fld dword ptr [eax]
    cf3.exe+3EB82A: D9 1B           - fstp dword ptr [ebx]
    cf3.exe+3EB82C: D9 40 04        - fld dword ptr [eax+04]
    cf3.exe+3EB82F: D9 5B 04        - fstp dword ptr [ebx+04]
    cf3.exe+3EB832: D9 40 08        - fld dword ptr [eax+08]
    cf3.exe+3EB835: D9 5B 08        - fstp dword ptr [ebx+08]

     

     

  8. 56 минут назад, Partizan сказал:

    Согласень так лучше смотрелось бы. Вопрос реализации.

    Показал бы как хоть на той же МТХ.

    А про горячие кнопки....???? Смысл?

    Да, это для плавности. Домножение должно осуществляться в самой игровой функции, самим игровым потоком. Через FPU или SSE.

    А горячие клавиши луа лишь устанавливают или сбрасывают флаги для такого осуществления.

    Можно также обойтись без луа клавиш. Вместо них для тех же целей можно вызывать из игровой функции GetAsyncKeyState.

     

    А про то как использовать и углы поворота, - я подзабыл. Кажется синус и косинус Yaw умноженные на Velocity.

  9. Я про домножение velocity узнал из трейнера флинга, кажется 🙊. Помню как включал эту опцию и изучал место инъекции. У него, насколько я помню, ускорение было такое же резкое как и у тебя. Но как по мне, лучше дать самому потоку плавно домножать velocity. Так смотрится более естественно. А горячие клавиши поставить на флаги.

  10. 1 час назад, Xipho сказал:

    А в чем, собственно, проблема?

    Сам в недоумении. По видимому все должно быть просто. Может устал. Неделю днями не слезал с проекта.

     

    1 час назад, Xipho сказал:

    Тогда псевдокод (саму процедуру рисовки прямоугольника опускаю, чисто сам алгоритм) будет примерно следующий

    У тебя в примере не задействован plotCount?

    Ты наверно понял уже что это за прямоугольники? Это меню главного окна. При сжимании главного окна программы, итемы(элементы) меню переносятся.

    Я же делаю полностью самодельное меню, вот меня занесло то 🙆‍♂️.

    Завтра попробую применить твой пример, спасибо. Книгу не читал такую. Скачал себе, по виду дельная. Тоже спасибо 🙂.

     

    Это на каком языке? Синтаксис с двоеточиями не знакомый.

    void plotRect(r: Rect, xdest, ydest: int);
  11. Привет, никто не сталкивался с такой задачей?

    Спойлер

    spacer.png

     

    Синие прямоугольники нужно разместить в красном. Как на картинке, в правом красном прямоугольнике.

    Изначально, когда синие прямоугольники выстроены в прямую линию, их позиции и размеры известны.

    Чувствую шестым чувством, что задача простая, но что-то на ум пока ничего не приходит.

     

    Да, и перенос должен быть до того момента, когда в каждой линии останется по одному синему прямоугольнику.

  12. 15 минут назад, Partizan сказал:

    Положу это сюда. Может кому пригодится.

    В скрипте нужно еще учитывать углы поворота транспорта. Для лучшего эффекта. Иначе при движении по дуге и при использовании спидхака транспорт будет уезжать с дороги. Это насколько я помню.

    • Плюс 2
  13. Обычно большинство параметров техники хранится в памяти игры рядом с частотами вращения колес.

    Когда колесо раскручивается вперед, частота его вращения в памяти во float будет увеличиваться и будет больше нуля. Тоже самое наоборот.

    Чтобы понять какие параметры/адреса техники на что влияют, можно сделать такой трюк,

    - найти технику с изначально большой скоростью,

    - скопировать блок памяти с параметрами в блокнот,

    - загрузить другую технику, с изначально малой скоростью,

    - записать сохраненные байты из блокнота в память, на тот же начальный адрес, можно найти какой-нибудь адрес как ориентир,

    - если техника после этого поедет быстрее, значит повезло, можно теперь брать блоки меньшими порциями, чтобы выйти на такие параметры/адреса

    которые реально влияют на скорость.

    • Плюс 1
  14. Вот выписка с MSDN Resource leak

    Спойлер

    This could be due to the fact that, behind the scenes, when an application requests a pure color brush, Windows not only creates it, but it also stores its handle in a dedicated cache. In this case, even if the application calls DeleteObject on the handle it receives for the brush, Windows doesn't really delete it. Therefore, if the application requests a brush of the same color again, the same handle is returned.

    Мы можем обнулить созданный нами дескриптор кисти, но не можем обнулить тот дескриптор кисти, который помешен в dedicated cache.

    Обнуление также никак не влияет на удаление самого объекта кисти в кеше. Он там будет сидеть до следующего вызова CreateSolidBrush, с таким же цветом как и у нее.

    Это все что я понял на данный момент, из собственных тестов, и из интернета.

     

    Я нигде не нашел функцию очистки такого специального кеша. Впрочем, я долго не искал. Если это норма для WIndows, то пусть так и будет.

    Просто для обнаружения утечки GDI объектов, приходится в обязательном порядке несколько раз создавать и удалять какой-либо контрол.

  15. 9 часов назад, Xipho сказал:

    А если после вызова DeleteObject обнулить переменную, в которой этот объект лежал, счетчик уменьшится?

    Нет, не уменьшится.

    Мысль у тебя хорошая, я помню где-то в интернете мне попадалась она уже. Но я не помню, для какого она была контекста.

    В каких случаях такие обнуления имеют эффект?

     

    По поводу счетчика и кеша, вот ответ от Raymond Chen

    Спойлер

    As noted in this article, GDI caches solid color brushes. What you're seeing is that the brush was logically deleted, but is still physically present in the cache

    The behavior is not documented because it is not contractual. It is an implementation detail. (Some versions of Windows do not have a solid color brush cache.)

     

  16. 3 часа назад, Antonshka сказал:

    Сколько еще таких особенностей скрывает Micosoft...

    ImageList, также имеет подобный "остаточный" эффект. После одного такого ImageList'a, остается 2 битмапа и две кисти. И это нормально.

    В общем, при поиске утечек GDI объектов, нужно смотреть на постоянное увеличение их количества, а не на единоразовое.

  17. Сегодня обнаружил интересный момент, связанный с утечкой GDI объектов.

    Известно, что после того как созданный нами объект GDI нам больше не нужен, он должен быть удален специальной WinAPI функцией DeleteObject. Если этого не сделать, то произойдет утечка.

    Для определения текущего количества GDI объектов для процесса, есть также специальная функция, GetGuiResources. Есть в интернете и специальная программа, которая позволяет определить не только общее количество GDI объектов, но и распределить их по типу. Название программы - GDIView.

     

    Так вот, интересный момент связан с кистями и регионами. Даже если удалить их с помощью функции DeleteObject, количество объектов, возвращаемое функцией GetGuiResources, не измениться. Все потому, что система их кеширует.

    Это не значит, что не нужно вызывать DeleteObject. Нет, ее нужно вызывать, но нужно просто всегда учитывать такую особенность реализации.

     

    Например, вызовем функцию CreateRectRgn. Эта функция создаст для нас регион, увеличив при этом количество GDI объектов на 1. Позднее, вызвав для этого региона DeleteObject, количество объектов GDI не измениться. Однако, если вызвать функцию CreateRoundRectRgn, а затем DeleteObject, то в таком случае, количество GDI объектов все же уменьшиться на 1.

     

    Получается, что это правило, правило кеширования и не уменьшения количества, относиться к кистям и регионам, созданным лишь определенными WinAPI функциями. Какие именно это функции, для кистей и регионов, нужно выяснять своими тестами.

     

    GdiFlush никак не изменяет кеш таких объектов.

    Скорее всего, Microsoft реализовала такую особенность не просто так. Но, это конечно не айс, с точки зрения определения утечки. Впрочем, реальная утечка объектов, проявляет себя как постоянное увеличения количества, когда как при кешировании, увеличение происходит всего один раз, в момент первого создания. По этому признаку, можно смело определить, что утечки, как таковой, нет.

     

    Сколько еще таких особенностей скрывает Micosoft...

  18. 16 минут назад, Xipho сказал:

    Хорошее исследование провел, а у меня так руки и не дошли за эти выходные

    А я и вовсе уже как две недели занимаюсь всем чем ни попадя. Но только не библиотекой.

    То почитаю это, то почитаю то. То поиграю с иконками, то с файлами .bmp.

    Иной раз досадно, что работа так часто приостанавливается. Но, программа для человека, а не человек для программы.

     

    У тебя на видео ты изменяешь SourceConstantAlpha глобально, применительно ко всей картинке. А я бы еще хотел протестировать альфа смешивание считающееся с альфа каналом каждого пикселю. Чтобы в итоге выглядело как например PNG картинки наложенные друг на друга.

    Тебе не приходилось такое проворачивать?

  19. Иконка рисовалась с артефактами из-за того что ее Bitmap использует альфа-канал.

     

    Иконка, которую я тестировал, имеет 32 бита на пиксель, - 32bpp. Если бы эта иконка имела 24bpp, то для ее рисования нужно было бы использовать функцию BitBlt. Вначале для маски, с SRCAND, затем для изображения, с SRCINVERT. Но так как моя иконка 32bpp, с альфа каналом для каждого пикселя, то для ее вывода нужно использовать AlphaBlend(). Ее использует и DrawIconEx.

    Фукнция AlphaBlend не использует маску иконки вообще. Также эта функция ожидает что значение пикселя является premultiplied.

    Спойлер

    Note that the APIs use premultiplied alpha, which means that the red, green and blue channel values in the bitmap must be premultiplied with the alpha channel value. For example, if the alpha channel value is x, the red, green and blue channels must be multiplied by x and divided by 0xff prior to the call.

     

    Если значение пикселя будет не premultiplied, тогда получатся артефакты.

    Существует всего два вида представления пикселя с альфа каналом. Premultiplied и нет. Моя иконка была без "pre". Стоило мне перевести каждый ее пиксел в pre, как все артефакты исчезли.

    Я пробовал создавать свою собственную иконку в программе IcoFx. В формате 32bpp с альфа. Но эта программа сохраняет иконку без "pre". Что также сбило меня с толку.

    Моя тестовая иконка была скачана с сайта иконок. Получается что они там все без "pre".

     

    Примечательно, что точно определить является ли иконка "pre" или не "pre" невозможно. Можно лишь сделать предположение. По видимому DrawIconEx этим и занимается.

     

    R = R * A / 255

    G = G * A / 255

    B = B * A / 255

    Исходя из этой формулы преобразования пикселя из не "pre" в "pre", можно точно сказать что R и B и G всегда меньше A (alpha). Значит, скорее всего, эта иконка с "pre". Никакого преобразования в "pre" делать не нужно. Перед вызовом функции AlphaBlend.

    Но если мы пройдя по каждому пикселю обнаружим что хотя бы один пиксель имеет значение R или B или G канала большим чем A, то скорее всего эта иконка не "pre".

  20. 2 часа назад, MaxSerro сказал:

    Да, так и есть, все по методичке с канала "Михаил Ремизов", с видео, посвященным ESP-hack. Я ориентировался на код, предоставленный им, и подстроил под себя. А есть альтернативы им?

    Я не знаю. Я "зеленый", как и ты. @Xipho, скорее всего знает.

     

    2 часа назад, MaxSerro сказал:

    Спасибо за советы. Много новых, пока что не известных для меня функций, нужно поизучать. Не знаешь ли, снимал ли кто-то уже какие-нибудь видео по взлому с применением этих функций? Интересно посмотреть на наглядную демонстрацию их работы.

    Для меня они тоже новые, я только вчера о них узнал.

    Я себе недавно скачал книги, "Windows Internals Seventh Edition Part 1 Марк Руссинович", в ней про Kernel. И еще, "the windows 2000 device driver book", в ней про написание драйвера. Книга про Windows 2000, надо буде поискать посвежее. Но это у меня на будущее. Пока что они просто лежат.

     

    Из моих размышлений, - чтобы игра смогла установить хук на OpenProcess или на ReadProcessMemory, она должна быть запущена в режиме Kernel, а не в User.  Либо она должна запустить свой собственный драйвер, для этой цели, для хука. И то, она хукает не эти функции, а те что с префиксом NT. Вызывая OpenProcess, мы в итоге вызываем NtOpenProcess.

    По логике, чтобы без опаски использовать в своем приложении OpenProcess, нужно

    - убедиться что игра не устанавливает хук на нее

    - если же устанавливает, то тогда писать свой драйвер, который каким-то образом хукнет хукнутый OpenProcess, и твое приложение будет использовать хукнутый хук.  В обход хуку игры.

     

    Это все мои фантазии и догадки. Нужно конечно же читать про все это. А я сейчас мыслями в GDI.

  21. 10 часов назад, MaxSerro сказал:

    2) Чит никак бы не спалили, потому что моя программа не встраивает чужеродный код в игру, а лишь имитирует нажатие клавиш.

    У тебя чтение через OpenProcess и ReadProcessMemory? При желании их же может перехватить игра и использовать для определения.

     

    13 часов назад, MaxSerro сказал:

    Для своевременного нажатия кнопки S необходима отточенная реакция, что очень почитается на турнирах по данной игре.

    Твоя программа при запуске создает второй поток, создает объект событие (с автосбросом), устанавливает SetWindowsHookExW на игру с флагом WH_KEYBOARD_LL (чтобы не было нужды в инжекте DLL).

    Либо вместо SetWindowsHookExW, для большей гарантии от бана, использует Raw Input и флаг RIDEV_INPUTSINK.

    Главный поток использует GetMessage, а не PeekMessage. Главный поток только активирует/деактивирует чит.

    При нажатии клавиши сброса бомбы, твоя программа проверяет что  бомбы еще есть, и если есть, устанавливает событие в signaled. Поток пробуждается от сна (этот сон не тратит процессорное время), и начинает в цикле считывать значение. Если бомбы закончились, поток сам прерывает цикл считывание и возвращается на объект событие. Если бомбы еще есть но была нажата клавиша S, то поток также сам прерывает цикл и возвращается на объект событие. Определять была ли нажата клавиша S можно через тот же зарегистрированный Raw Input или через GetAsyncKeyState.

    Такие мысли, чтобы уменьшить нагрузку на CPU.

     

    • Плюс 1
  22. 7 часов назад, Xipho сказал:

    Зачем это предварительное расшаркивание? SelectObject возвращает замещенный объект

    Это от привычки. Бывали случаи что новые GDI объекты выбирались мною в DC не сразу. Плюс чтобы просто не запутаться. Взял один раз все стандартные объекты DC, и далее уже не задумываясь выбираешь свои собственные. Потом в конце восстанавливаешь.

     

    7 часов назад, Xipho сказал:

    Дальше. Если мне не изменяет память, при создании GDI структур их нельзя вчистую нулями заполнять. У большинства из них есть поле dwSize, которое нужно заполнять, чтобы функции, использующие эти структуры, корректно отрабатывали. Но тут это не точно.

    У меня VS ругается, если нет инициализации. Я все переменные инициализирую.

    А так, да, MSDN обычно пишет в описании функций что параметр-структура должен быть с заполненным полем cbSize. Главное не проморгать.

     

    7 часов назад, Xipho сказал:

    Для загрузки иконок вроде как есть LoadIcon, заточенный специально под иконки

    LoadIcon я использовал на первых порах. У нее есть ограничения на размер иконки. Плюс она замещена на LoadImage.

    Note  This function has been superseded by the LoadImage function.

     

    7 часов назад, Xipho сказал:

    Ну а артефакты говорят о том, что ты неправильно работаешь с маской. В случае с иконками, помнится, использовал режим SRCPAINT. Но это для старых иконок, где фон, по сути, был черным.

    Для старых иконок используется тоже AND и XOR, то-есть SRCAND и SRCINVERT. Raymond Chen

    Вообще все, Фень, Щупак, MSDN, Raymond, пишут что AND и XOR. Но видимо есть еще какой-то хитрый момент.

     

    8 часов назад, Xipho сказал:

    По остальному - на выхах, если будет время, попробую освежить контекст в голове по рисованию с GDI. Но что-то ты явно с маской делаешь не так.

    Я тоже, так просто эту иконку я не оставлю. Raymond Chen написал четыре хорошие статьи по иконкам, нужно почитать.

     

×
×
  • Создать...

Важная информация

Находясь на нашем сайте, Вы автоматически соглашаетесь соблюдать наши Условия использования.