Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > Delphi: Общие вопросы > Как получить данные из TStringGrid в "чужом" процессе |
Автор: Rrader 3.9.2008, 18:40 | ||||||||||||||
Внимание! Настоятельно рекомендую ознакомиться с материалом http://rcrrad.com/2009/06/27/tstringgrid-datagrabber/, если Вы озадачены получением данных из этой таблицы ![]() На этом форуме и на многих других не раз возникал вопрос о том, как получить данные из компонента TStringGrid, созданного в "чужом" процессе. API-функция GetWindowText не дает нужного результата, поскольку текстовые данные таблицы хранятся в памяти обособленно от каких-либо оконных API. Более того, все, что мы можем получить о таблице в "чужом" процессе, это лишь её дескриптор (Handle). Задача подобного рода является достаточно сложной, и стандартного её решения не существует. Хочу предложить Вам одно нестандартное, но эффективное решение. Задача о получении данных из TStringGrid прямым инжектом [align=justify]Особенности метода: 1) Используется прямое внедрение в процесс (без DLL) с запуском удаленного потока. Обычно данная техника обнаруживается антивирусами (практика показывает, что для людей, которым нужно получить данные из TStringGrid, это не является проблемой). Если мы откажемся от CreateRemoteThread, то перед нами встанет еще одна проблема - мы не сможем напрямую получить доступ к необходимым данным (которые могли бы передать через параметр CreateRemoteThread). Данная проблема имеет эффективное решение, о котором говорится во второй части статьи. 2) Для обеспечения универсальности в некоторых местах мы не можем использовать код Delphi, только ассемблер. Это связано с тем, что, во-первых, я стремился сделать код как можно более компактным (его можно портировать на WinAPI с минимальными модификациями), а во-вторых, что самое главное, сделать его работоспособным для разных версий Delphi. Код (работоспособность) зависим от версии Delphi, на котором скомпилировано целевое приложение. Что это значит? Дело в том, что от версии к версии в Delphi вносятся модификации не только в компилятор и IDE, но и в VCL. Стандартные классы расширяются, появляются новые методы и свойства. Используя знания анатомии класса Delphi, можно получить доступ ко многим членам класса без ограничений, но все это возможно только через ассемблер. Некоторые сведения об устройстве классов в Delphi В ассемблерном представлении классы почти ничем не отличаются от записей (records). Отличия следующие - каждая такая запись вбирает в себя не только свои собственные поля, но и поля всех предков. Также добавляется поле с адресом таблицы VMT - оно всегда первое по счёту. Рассмотрим пример:
Эквивалентная запись для класса TMyClassEx будет выглядеть следующим образом:
Для доступа к полям Вам будет необходим "указатель на класс". Загрузив его в регистр (скажем, EAX), можно организовать доступ к полям, учитывая порядок их объявления: [EAX + 0] - первое поле (VMT) [EAX + 4] - второе поле FField1 [EAX + 8] - третье поле FField2 [EAX + 12] - четвертое поле FField3 Здесь используются непосредственные значения при адресации (hard-coded values). При работе со встроенным ассемблером так делать не рекомендуется, но при работе с классами у нас нет другого выхода. Как видите, секций private и public больше нет - они имеют только высокоуровневую реализацию. Это касается всех секций класса. Реализация метода получения данных Давайте рассмотрим, как можно получить данные из удаленного процесса. У нас есть лишь только дескриптор целевой TStringGrid. Зная его, мы сможем определить PID процесса через функцию GetWindowThreadProcessId. Открыв процесс, мы сможем записать в него функцию (выделив память), и запустить ее на исполнение через создание удаленной нити (будем считать, что процесс разрешает нам выполнять все описанные действия). Нам также понадобятся дополнительные данные (см. ниже), которые можно передать через указатель в параметр функции. Следует учесть, что на момент создания нити данные для параметра уже должны быть подготовлены и размещены в чужом процессе (для этого используется функция VirtualAllocEx). Что следует размещать в данных? Ну, во-первых, дескриптор TStringGrid, так как он у нас один известный и будет использоваться ![]() Как работает функция DataGrabber Так называется удаленная функция, которая осуществляет всю работу по получению данных и их передаче. Получить данные - дело одно, а передать - совсем другое. Дело в том, что функция работает не в нашем процессе, но получает там строки, которые нужно передать в наш процесс. TStringGrid может быть очень большим, и для непрерывной передачи данных я выбрал такое средство IPC, как именованные каналы (Named Pipes). Работает функция в несколько этапов:
![]() Вот часть кода главного модуля: </div>
|
Автор: Qu1nt 3.9.2008, 18:54 |
Кто тебя научил так программировать?! Браво. |
Автор: Rrader 3.9.2008, 18:56 | ||
Задача о сабклассинге "чужих" окон прямым инжектом Оригинальный метод был предложен программистом по имени Robert Custer. Сабклассинг - это контроль сообщений окон, реализуемый посредством смены оконной процедуры. Следующий пример показывает, как можно осуществить удаленный сабклассинг без внедрения DLL. Пример будет менять оконную процедуру у родителя окна любого SysListView32 (TListView) так, чтобы элементы списка с нечетными номерами отображались красным цветом, а с четными - зеленым. Сначала стоит разобраться, как это сделать в своем приложении на чистом API и не очень ![]() Получив это сообщение, мы должны проанализировать параметр lParam, приведя его к типу TNMHdr (запись, содержащая данные сообщения). Если параметр code этой записи будет равен NM_CUSTOMDRAW, значит сейчас следует отрисовать элемент. Чтобы изменить цвет элемента, мы должны отправить значение CDRF_NOTIFYITEMDRAW как результат в ответ на WM_NOTIFY в том случае, когда член dwDrawStage записи PNMCustomDraw(PNMHdr(lParam))^ равен CDDS_PREPAINT. Затем мы снова получим WM_NOTIFY, и когда dwDrawStage будет равен CDDS_ITEMPREPAINT, то через член clrText записи PNMLVCustomDraw(PNMHdr(lParam))^ мы уже сможем изменить цвет на нужный. Индекс элемента передается в члене dwItemSpec записи PNMCustomDraw(PNMHdr(lParam))^. Доступ к данным из оконной процедуры Данные нам понадобятся, чтобы вызывать оригинальную оконную процедуру ![]() ![]() Привожу код главного модуля. Процедуры внедрения те же. Проект находится в аттаче. В примере меняется окно ListView у стандартного приложения "Сведения о системе" (через сабклассинг родителя). Будет затронут и TreeView, так как тот также использует схожую технику отрисовки. Пример проверен на работоспособность в Windows XP, Windows Vista, Windows 7.
Надеюсь, данный материал поможет Вам в программировании! |
Автор: THandle 3.9.2008, 19:19 |
Rrader, наконец-то!!! Молодец!!! Как ты вообще все это понимаешь я не представляю. Присоединяюсь. |
Автор: psychedeler 7.9.2008, 19:59 | ||||
День добрый! Пытаюсь вникнуть в суть, т.к. озадачился проблемой "чужого" StringGrid'a... Делаю на С++, поэтому с переходом от всего выше сказанного к С++ позникают проблемы. 1) PInjectData = ^TInjectData; TInjectData = packed record Что это означает? packed record, по-моему, сильно смахивает на С++ "struct", а ^ - это указатель (символ "*") И по мне кажется, что фрагмент
на С++ выглядит как
2) LEA EDX, (TInjectData)[EBX].FPipeName И это что такое? Могу я получить какое-нибудь пояснение? Перевод с Delphi на С++ дает ошибку синтаксиса при обработке 2-го случая. |
Автор: Rrader 8.9.2008, 14:28 | ||||||
|
Автор: froloff 11.6.2009, 10:40 | ||
Браво Rrader, давно не видел столь изящно написанного кода. Добавил поддержку C Builder 6.0.
И сохранение в файл (это думаю каждый сможет сделать на свой вкус). |
Автор: Rrader 11.6.2009, 10:52 |
froloff, спасибо ![]() На самом деле у меня в принципе готов код, который получает эти данные без внедрения и без зависимости от компилятора Delphi (C++Builder). Т.е. эта статья - примитив, метод громоздок (но иногда оправдан для конкретных задач, потому что универсальный метод сложнее реализовать). |
Автор: Rrader 27.6.2009, 17:08 |
Как и обещал: http://wmsdk.com/2009/06/27/tstringgrid-datagrabber/ |
Автор: hexware 10.6.2010, 13:09 |
ни у кого копии статьи не сохранилось? а то сайт wmsdk.com умер ( |
Автор: hhhhhhhhhhhh 20.1.2011, 23:52 |
Офигенный пример о сабклассинге чужих окон. А нельзя ли куски асемблера переписать на WinApi? Хотел подправить пример под свои нужды, но блин обломился... Не понятно в этом месте... |
Автор: Rrader 21.1.2011, 11:51 |
hhhhhhhhhhhh, можно, правда, придется использовать библиотеку DLL, если охота полностью отказаться от asm-вставок и зашитых вручную значений. http://wmsdk.com/2010/02/01/vcl-components-re-mastering-part-one/ - тут используется сабклассинг окна в "чужом" процессе, причем без asm. Там много кода, не относящегося к сабклассингу, надо посмотреть и взять лишь нужное ![]() P.S. Немного поправил код |
Автор: _ls_ 2.2.2017, 12:02 |
Подскажите, пожалуйста, смещения для дельфи 7. Вставил смещения от C Builder 6.0. Число строк/столбцов определят верно, но вместо текста знаки вопросов в основном. |
Автор: giao 23.6.2022, 23:00 | ||
Доброго времени суток! У кого-то есть рабочая ссылка на эту статью "Получение данных из TStringGrid (D3-DXE)"? |