Реализация доступа к VCL объектам другой программы (внешнего процесса)
Думаю, что через FindWindowA(), но программы, захватывающие HWND (ID) объекта (напр. InqSoft Window Scanner) не хотят видеть их HWND.
Как поступить в данной ситуации?
PS: Есть доступ к форме жругой программы через ResHacker (или Restorator)
Есть ли какие-нибудь методы реализации того, что мне необходимо сделать?
Можно ли как-то изменить ресурс формы, чтобы находило HWND?
Она включена в мой исходник "ProjBeam" (раздел "Другое" ). Проект называется TestLib, находится в папке \Projects\TestLib. На закладке "Сообщения Windows" кнопка с вызовом нужного диалога. Исходный код в файле "BSendMess.cpp" и "BSendMess.h".
У меня в программе есть TToolBar. Я смог из диалога TestLib нажать на кнопку в TToolBar. Разбирайся и у тебя всё получится. ;)
Я запустил долгий процесс в своеё программе.
Он использует шкалу TCGauge на панели TPanel с кнопкой TButton.
К сожадению "видны" только TPanel и TButton.
Надо думать ... ;)
Думаю, что через FindWindowA(), но программы, захватывающие HWND (ID) объекта (напр. InqSoft Window Scanner) не хотят видеть их HWND.
Как поступить в данной ситуации?
PS: Есть доступ к форме жругой программы через ResHacker (или Restorator)
Необходимо найти главное окно - а потом запустить перебор дочерних окон.
В МСДН написано по моему черным по белому:
This function does not search child windows.
Контрол окна - он чилд виндоус и есть.
PS: Можно какой-нибудь пример использования функций?
PS: Можно какой-нибудь пример использования функций?
ну написали же тэбэ - получил главное окно, затем перебираешь дочернии, затем у них дочернии и т.д.
http://www.google.ru/search?complete=1&hl=ru&q=enumchildwindows&lr=
да ну? а если посмотреть в comctrls.pas (TToolBar):)
PS: Можно какой-нибудь пример использования функций?
bool __stdcall ChildEnumerate(HWND hWind,LPARAM lParam){
SendMessage(hWind,WM_GETTEXT,sizeof(buff),(LPARAM)(LPCTSTR)buff);
GetClassName(hWind,buff2,sizeof(buff2));
Form1->mmListChildWind->Lines->Add(String(buff)+"-\t ClassName\t-"+String(buff2));
return true;
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::BitBtn1Click(TObject *Sender)
{
HWND hWind=NULL;
hWind = this->Handle;
SendMessage(hWind,WM_GETTEXT,sizeof(buff),(LPARAM)(LPCTSTR)buff);
GetClassName(hWind,buff2,sizeof(buff2));
Form1->mmListChildWind->Lines->Add(String(buff)+"-\t ClassName\t-"+String(buff2));
while(hWind){
EnumChildWindows(hWind,(WNDENUMPROC)ChildEnumerate,0);
hWind =GetNextWindow(hWind,GW_HWNDPREV);
}
}
//---------------------------------------------------------------------------
для TToolButton как для обычной TButton?
для TCGauge как для обычного TProgressBar?
Ни TToolButton, ни TCGauge - этих не нашло...
Как быть? Если бы вопрос был простой, я бы его к "Новичкам" отправил, а по поводу сканеров и т.д. - не зря писал, что они тоже не находят...
Я тут статейку про AMS прочитал - а что, если встроить в exe-файл той программы, где кнопки находятся, свой код, так зказать, доп. TProgressBar и Еигеещтб которыми я и буду оперировать, а при нажатии на наих "прыгать" на необходимый адрес, т.е. на процедуру TToolButton и TCGauge соответственно? Конечно - это извращенство, но что ж делать... Это так, на крайний случай.. ;)
PS: как заголовок темы поменять? :)
Класс TCGauge порождён от TGraphicControl, который , как написано в справке, является облегчённым вариантом TWinControl.
И если дальше в справку посмотреть, то у TGraphicControl нет свойства Handle! Вот поэтому нет окна объекта TCGauge.
Unit
Controls
Description
TGraphicControl supports simple lightweight controls that do not need the
ability to accept keyboard input or contain other controls. Since lightweight
controls do not wrap Windows screen objects, they are faster and user
fewer resources than controls based on TWinControl.
TGraphicControl provides a Canvas property for access to the control's
drawing surface and a virtual Paint method called in response to paint
requests received by the parent control.
PS: во блин капец...
Чтобы пользователю было удобно и чтобы не светить ПСР, я решил ее запускать через SW_HIDE, и нажимать TToolButton "Start". После нажатия ПСР выполняет действия и выводит статус выполения (заверщения) задачи на TCGauge. Чтобы как-то отображать статус в МП, я думал получать через, скажем, каждые 2 сек сообщения от TCGauge о статусе выполнения.
И сразу возникли две проблемы:
1. Не могу работать с TToolButton, так как не удается получить Handle.
2. Не могу работать с TCGauge, так как не удается получить Handle.
Пробовал через разные сканеры, но результат тот же - этих контролов не видно. Также не видно и TLabel... но это ладно - мне она не нужна...
Как нажать кнопку и получить статус выполнения?
Доп. информация: ПСР не зажата упаковщиками, не зашифрована, можно спокойно декомпилировать и получить нахвания всех процедур, форм и т.п. вещей.
Как быть?
для TToolButton как для обычной TButton?
нет они начинаются с TB_ и отправляются через SendMessage, например:
{
//TODO: Add your source code here
RECT rcButton;
int iWidth = 0;
int nCount = ::SendMessage(m_hWnd, TB_BUTTONCOUNT, 0, 0);
for(int i = 0; i < nCount; i++)
{
::SendMessage(m_hWnd, TB_GETITEMRECT, i, (LPARAM)&rcButton);
iWidth += (rcButton.right - rcButton.left);
}
return iWidth;
}
ЗЫ: МСДН читаем!
Найди окно TToolBar.
Определи его размер и вычисли координаты приблизительной середины
кнопки "Start". По этим координатам пошли сообщение о клике левой
кнопки мыши. Если ты скачал мой исходник, там всё есть для этого. ;)
А по поводу TCGauge, определив окно, в котором он отображается,
делай (периодически) копию картинки в окно своей программы.
Если в окне ПСР есть кнопка "Прервать" определи её координаты, и по
нажатию на аналогичную в твоей программе посылай сообщение
о клике левой кнопки мыши.
А, может проще, связаться с разработчиками ПСР и взять код?
Или это "большой секрет" ;)
Я думаю, что можно через SendMessage отправить горячую клавишу этой кнопки...
Кстати, а как быть с TCGauge?
но сдвинуть его, например влево, за границы экрана.
При этом, делая активным окно ПСР, чтобы оно ловило сообщения,
пользователь всёравно ничего не увидит.
Ни TToolButton, ни TCGauge - этих не нашло...
Как быть? Если бы вопрос был простой, я бы его к "Новичкам" отправил, а по поводу сканеров и т.д. - не зря писал, что они тоже не находят...
Я тут статейку про AMS прочитал - а что, если встроить в exe-файл той программы, где кнопки находятся, свой код, так зказать, доп. TProgressBar и Еигеещтб которыми я и буду оперировать, а при нажатии на наих "прыгать" на необходимый адрес, т.е. на процедуру TToolButton и TCGauge соответственно? Конечно - это извращенство, но что ж делать... Это так, на крайний случай.. ;)
PS: как заголовок темы поменять? :)
Для контрола который сам содержит дочерние окна перебор необходимо выполнять тоже.
TCGauge находит. Другое дело что он не отображает корректно имя класса - с этим разбирайся сам. Тем более что контрол присутствует в палитре билдера только как пример - и если ты его используешь то проблемы эти твои и только твои.
так вот что я подумал... менять заголовок формы или какой-либо кнопки, эдита на форме GCH при изменении значения TCGauge, т.е. в обработку функции, в которой изменяется значение TCGauge, воткнуть доп. код, которые будет изменять значение (или надпись) какого-нибудь контрола...? как такой вариант?
Или это "большой секрет"
Они - китайцы :) их программа работает с китайским оборудованием... Я пытался взять код - и взял... но дело в том, что оборудование обновляется, и программа тоже... в итоге у меня исходники версии 2.0, а уже 4.0... Дальше - больше, 5.0, 6.0 и т.д. Думаю, лучше придумать какой-нибудь алгоритм по управлению программой, чем постоянно просить исходники... тем более как у них просить, если у них только сайт - и все :) ни почты, ни телефона, ни города или провинции, ни адреса... только сайт с иероглифами...
Я вот уже третий раз думаю... может через ASM вставить свой код в программу? адрес процедур найти не сложно, т.к. можно разкомпилировать, останется только выучить ASM.. :) :)
Идея такая:
Надо внедрить свою длл в этот процесс (хук, удаленный поток) и попробовать обратится к Appplication, если получится то дальше дело техники. Есть же VCL Property Viewer, который в любой программе на БСБ/Делфи кажет все св-ва. Это только догадки как он на самом делеработает х.з.
Была еще такая идея - на примере ArtMoney - искать данные в памяти процесса... скорей всего, данные о прогрессе имеют постоянный адрес, так как смысла делать его динамическим нет...
Итак, какие варианты?
ищЁ раз...
1. Прикрутить Dll
2. Ты в адресном пространстве целевого процесса, у каждого VCL приложения есть глобальная переменная Application (тип ТApplication), у каждого Application есть св-во MainForm ... дальше продолжать?
а как ее прикрутить?
кстати, так, для расслабления:
Дураки учатся на своих ошибках, умные на чужих... Получается, что дураки учат умных... :)
Где-то случайно на форуме каком-то нашел...
а как ее прикрутить?
установить ловушку(hook), например:
http://rxlib.ru/Stat/shevelev/shev.htm
или
http://www.cyberguru.ru/programming/win32/win32-hooks.html
Только тебе не нужно перехватывать, что-либо (мышь, клаву и т.д.), тебе нужно чтобы dll подгрузилась в адресное пространство целевого процесса, для этого достаточно установить ловушку:
#define TESTDLL_API __declspec(dllexport)
#else
#define TESTDLL_API __declspec(dllimport)
#endif
...
TESTDLL_API void InstallHook(DWORD dwThreadId)
{
hHook = SetWindowsHookEx( WH_CBT, (HOOKPROC)HookProc, hDLL, dwThreadId );
}
а процедуре делать ничего не обязательно, она просто должна вызвать следующий(если есть) хук в цепочке:
{
return CallNextHookEx( hHook, nCode, wParam, lParam);
}
DLL
#include <vcl.h>
#include "CGAUGES.h"
#pragma argsused
// этими объектами можно управлять, как своими
// при изменении свойств объектов могут возникнуть проблемы с синхронизацией
// при чтении должно быть все нормально
TForm *MainForm=NULL;
TCGauge *CGauge1=NULL;
void Init()
{
TForm **pForm=(TForm**)GetProcAddress(GetModuleHandle(NULL), "_MainForm");
if(pForm==NULL)
{
/* ERROR: export not found */
}
else
{
MainForm=*pForm;
if(MainForm==NULL)
{
/* ERROR: form is not accessible */
}
else
{
CGauge1=(TCGauge*)MainForm->FindComponent("CGauge1");
if(CGauge1==NULL)
{
/* ERROR: CGauge not found */
}
else
{
CGauge1->Progress=50; // for test
}
}
}
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved)
{
switch(dwReason)
{
case DLL_PROCESS_ATTACH:
Init();
break;
}
return 1;
}
программа-стартер
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
HMODULE hRemoteModule=NULL;
void __fastcall TForm1::Button1Click(TObject *Sender)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
bool bSuccess=false;
memset(&si, 0, sizeof(si));
si.cb=sizeof(si);
if(!CreateProcess(NULL, "..\\__test2\\project1.exe", 0, 0, FALSE, 0, 0, 0, &si, &pi))
{
ShowMessage(SysErrorMessage(GetLastError()));
}
else
{
DWORD res=WaitForInputIdle(pi.hProcess, 10000); // 10 secs
if(res==WAIT_TIMEOUT)
{
if(MessageBoxA(Handle, "Превышен предел ожидания. Уничтожить процесс?", 0, MB_YESNO | 16)==IDYES)
TerminateProcess(pi.hProcess, 0);
}
else
{
String DllPath=ExtractFilePath(ExcludeTrailingBackslash(ExtractFilePath(Application->ExeName)));
DllPath+="\\__test1\\project2.dll";
LPVOID p=VirtualAllocEx(pi.hProcess, 0, DllPath.Length()+1, MEM_COMMIT, PAGE_READWRITE);
if(!p)
{
ShowMessage("Can't Write Memory To Process: "+SysErrorMessage(GetLastError()));
}
else
{
if(!WriteProcessMemory(pi.hProcess, p, DllPath.c_str(), DllPath.Length()+1, NULL))
{
ShowMessage("Can't write to process memory");
}
else
{
FARPROC fp=GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
if(!fp)
{
ShowMessage("Can't get address of kernel32.dll!LoadLibraryA");
}
else
{
HANDLE hThread=CreateRemoteThread(pi.hProcess, 0, 0, (LPTHREAD_START_ROUTINE)fp, (LPVOID)p, 0, NULL);
if(!hThread)
{
ShowMessage("Can't create remote thread: "+SysErrorMessage(GetLastError()));
}
else
{
res=WaitForSingleObject(hThread, 10000); // wait 10 secs
if(res==WAIT_TIMEOUT)
{
if(MessageBoxA(Handle, "Поток завис. Завершить его?", 0, 16 | MB_YESNO)==IDYES)
{
TerminateThread(hThread, 0);
}
}
GetExitCodeThread(hThread, (LPDWORD)&hRemoteModule);
if(hRemoteModule) bSuccess=true;
else ShowMessage("Инициализация DLL в удалённом процессе завершена с ошибкой");
CloseHandle(hThread);
} // hThread
} // (!fp)
} // write process memory
VirtualFreeEx(pi.hProcess, p, DllPath.Length()+1, MEM_FREE);
} // virtual alloc
} // WaitForInputIdle(...) == WAIT_TIMEOUT
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
} // CreateProcess
if(bSuccess) ShowMessage("Операция успешно завершена :)");
}
Что делаем: запускаем программу-стартер, нажимаем на кнопку - запускаем ПСР (программу стороннего разработчика) и пытаемся подключить библиотеку к этой программе, далее при успешном подключении изменяем положение TCGauge...
что тут не так и можно ли такое использовать? какие есть замечания или пожелания?
дело в чем - а вот не получается получить сообщение "Операция успешно завершена"... :( Выходит только "Инициализация DLL в удалённом процессе завершена с ошибкой"
да на здоровье, только внедрение с помощью удаленного потока не будет работать под Win9х. Хотя в этом случае удобно контроллировать запуск программы(китайской). Ну тут тебе решать.
...
// этими объектами можно управлять, как своими
// при изменении свойств объектов могут возникнуть проблемы с синхронизацией
// при чтении должно быть все нормально
а вот это интересное утверждение, это ты сам придумал или где-то представленный код подрезал? (просто такой уверенный комментарий, если у тебя не получилось, то откуда такая уверенность:))
и к _MainForm из каких соображений обращаемся?
Что делаем: запускаем программу-стартер, нажимаем на кнопку - запускаем ПСР (программу стороннего разработчика) и пытаемся подключить библиотеку к этой программе, далее при успешном подключении изменяем положение TCGauge...
ну это все понятно, только код я не стал проверять (пишешь не удобоваримо:), я привык к "к классикам" типа Рихтера)
что тут не так и можно ли такое использовать? какие есть замечания или пожелания?
можно-ли использовать - хз:(
пожелание - научись внедрять dll в "китайский" процесс, тогда скажу что делать дальше (есть одна идейка, но сформулировать пока не могу, надо переварить маленько)
код мне подкинули... подсказали, верней, и комментарии - тоже их... так что авторские права принадлежат не мне :)
Я получил таблицу экспорта, а форма эта фактически является главной формой всей ПСР...
А кто сейчас на Win 9x работает? :)
По началу не получилось из-за строки:
String DllPath=ExtractFilePath(ExcludeTrailingBackslash(ExtractFilePath(Application->ExeName)));
я ее не стал урезать, думал, что это перевод в какой-то специализированный формат пути, оказалось, что просто обрезание последнего каталога... :)
Идейка... мне нужны идейки :)
Респект и уважуха! Добил китайцев!
код мне подкинули... подсказали, верней, и комментарии - тоже их... так что авторские права принадлежат не мне :)
где, кто - тоже хочу с умными пацанами пообщаться
Я получил таблицу экспорта, а форма эта фактически является главной формой всей ПСР...
вообщем-то так и подумал, хотел еще предложить тебе дамп снять с "китайца"(правда немного другое интересовало). Не универсально получается, но главное работает (а вообще никто не мешает таблицу экспорта программно получать:))
А кто сейчас на Win 9x работает? :)
есть отморозки...
Идейка... мне нужны идейки :)
Ну а идейка такая, вот заинтересовал ты меня вчера своим вопросом ну я и создал тестовый пример, нагрузил туда контролов, состряпал dll-ку и "инжектор" и попыталься зацепить Application в тестовой программе(и через хук и через удаленный поток). Получилось не очень, говорит, что только св-ва Tag и Name есть у него (но это же фигня полная). Ну я взял и подгрузил на старте dll-ки пакетик и чудо произошло видит все и не падает, а вот объяснить не могу зачем так сделал. Что-то с инициализацией пакета связано очевидно т.е. надо подумать еще.
PS: чувствую, что конец уже не за горами - и тема будет разобрана полностью :)
PS: чувствую, что конец уже не за горами - и тема будет разобрана полностью :)
Сабклассим компоненты, форму или просто присваиваем свой обработчик нужного события, а результат шлем контроллирующему приложению чере WM_COPYDATA (или через МАП-файл + событие оповещения)
см. FAQ http://forum.codenet.ru/showthread.php?t=26390
Вопросы № 2, 27
получается, надо написать свою функцию MyFunc(), которую потом передать в обработчик компонента на форме? А как узать, в какой компонент? а если обновление TCGauge происходит вне обработки, например, нажатия, а во время выполения какой-либо функции, например __void fastcall SetGaugePos(int gPos)?
а почему TTimer нельзя воспользоваться? глупо?
PS: прошу "разжувать" - что делать дальше? :)
получается, надо написать свою функцию MyFunc(), которую потом передать в обработчик компонента на форме? А как узать, в какой компонент? а если обновление TCGauge происходит вне обработки, например, нажатия, а во время выполения какой-либо функции, например __void fastcall SetGaugePos(int gPos)?
а почему TTimer нельзя воспользоваться? глупо?
PS: прошу "разжувать" - что делать дальше? :)
Да придется использовать таймер или поток для опроса т.к. у TCGauge не событий вообще.