У меня почти то же самое было, только это работает)) Я понял, надо было в основной программе сразу писать процедуру из exports, без всяких name 'CBTProc'.. Большое спасибо Всё чаще, когда у меня какой-то затык, я лезу смотреть созданные мною темы..))
Кстати, чтобы не таскать с собой отдельно dll - только извлекать из ресурсов в нужный момент? Прилинковать как-нибудь нельзя?
--------------------
"Знаешь, стыдно - когда не видно, что услышал всё, что слушал.."
Можно (через Project->Resources->New->User Data, а потом - извлекать через TResourceStream + SaveToFile), но не нужно. Спокойная жизнь надоела? Хочется слышать матюгание антивирусов?
Ещё вопрос, относительно "обратной связи". Мне в основной программе нужно узнавать, как там дела у ловушки, что она поймала и т.п. Но т.к. я не нашёл приём экспортирования переменных (наверное, этого вообще нельзя делать), то написал функцию, которая возвращает значение переменной. Так:
Function retb:integer;stdcall begin result:=b; end;
Переменная b:integer=0; , описана глобально, когда ловушка что-то ловит - b присваивается число. В основной программе:
function retb:byte; stdcall; external mydlname;
Регистр названий одинаковый. В главной программе в таймере запрашиваю значение retb (интервал 100мс), но оно почему-то всегда равно 0, даже когда точно известно, что ловушка сработала..
--------------------
"Знаешь, стыдно - когда не видно, что услышал всё, что слушал.."
Переменная b:integer=0; , описана глобально, когда ловушка что-то ловит - b присваивается число.
Ага, размечтался Вот здесь посмотри, как возвращать данные из DLL в приложение: http://www.mustangpeak.net/hooks.htm (там внизу прилеплен архив с примером, который работает с MMF - Memory Mapped Files). Сегодня вечером поменяю видеоплату на своем компьютере (уже купил наконец-то ), тогда будет проще, начнется не теоретический разговор, а практический...
офигеть, из-за одной переменной столько кода непонятного! Неужели не предусмотрено что-то попроще? (хотя, гугл намекает, что нет). Может, можно описать во входных параметрах SetHook(var b:integer), а потом, в процедуре, присваивать? (да, так не нужно писать программы))
Вообще, самый элегантный метод - использовать txt в качестве посредника) Сработало - создал, принял - удалил..
--------------------
"Знаешь, стыдно - когда не видно, что услышал всё, что слушал.."
Вообще, самый элегантный метод - использовать txt в качестве посредника) Сработало - создал, принял - удалил..
Угу. Если получилось - удалил, ты имеешь в виду? Можно, конечно, и TXT-файлами пользоваться для обмена информацией. Но ты для начала задачу озвучь, что именно нужно тебе получать? Сколько окон было заблокировано? Сколько раз вообще вызывалась ловушка? Что именно? Может, найдем и более красивое решение
P.S.(Показать/Скрыть)
Я уже вернулся за свой компьютер, если что Теперь - с гигабайтом видеопамяти на борту ...
Сколько окон было заблокировано? Сколько раз вообще вызывалась ловушка?
Устанавливается один раз, в библиотеке массив из 5 элементов с идентефикаторами программ, нужно просто в момент отлова неугодного окна сообщать об этом основной программой, в виде переменной с её (программы) индексом.
Со строками, как мне обещали мануалы, проблем нет. По крайней мере, окна нормально определяются по строкам из массива. А вот текстовик что-то не создаётся, а messagebox появляется только наполовину - звуком, само окошко не показывается)
По-моему, здесь способ с текстовиком самый оптимальный. Именно потому, что я буду проверять в таймере его существование, а существовать он будет только при срабатывании ловушки. А почему может не получиться удалить? Права? У меня туда манифест вшит, для UAC
Сообщение отредактировано: Unconnected -
--------------------
"Знаешь, стыдно - когда не видно, что услышал всё, что слушал.."
Если нужно просто возвращать индекс заблокированного окна, вот тебе еще один способ, навскидку (не тестировал, но явных причин не работать - не вижу, да и получше будет, чем с txt-файлами). У тебя ж хендл твоего окна не меняется после того, как ты хук установил? Вот в SetHook передавай хендл своего окна (главного окна приложения, либо какой-то формы, я не знаю, что там у тебя), а при блокировке приложения ловушкой - PostMessage этому окну, "так мол и так - заблокировано приложение <и информация о нем>", а в оконной функции своего приложения лови эти сообщения и обрабатывай как нужно. Лучше будет, чем с текстовыми файлами, да еще по таймеру заморачиваться. К таймеру бы вообще привязываться не надо.
const wm_user=$0400; //в библиотеке эта константа почему-то была undeclared, юнит windows есть
begin postmessage(whan,$0400+50000,i,0);//+50000 взял, чтоб наверняка попасть в [0xC000 ; 0xFFFF]. end;
Ловлю:
function TForm1.KOLFormMessage(var Msg: tagMSG; var Rslt: Integer): Boolean; begin if msg.message=WM_USER+50000 then begin //...используем wparam result:=false; //дальше сообщению хода нет end; end;
Так надо? В KOL, кстати, очень удобная система работы с сообщениями, всё в одном обработчике.
--------------------
"Знаешь, стыдно - когда не видно, что услышал всё, что слушал.."
+50000 взял, чтоб наверняка попасть в [0xC000 ; 0xFFFF].
Это еще зачем? MSDN явно говорит:
Цитата
Message numbers in the fourth range (0xC000 through 0xFFFF) are defined at run time when an application calls the RegisterWindowMessage function to retrieve a message number for a string. All applications that register the same string can use the associated message number for exchanging messages. The actual message number, however, is not a constant and cannot be assumed to be the same between different sessions.
Ты регистрировал сообщения через RegisterWindowMessage?
Я б на твоем месте все-таки использовал интервал WM_USER .. 0x7FFF, ты же фактически из своей библиотеки посылаешь сообщение своему же приложению, то есть, никаких разночтений возникнуть не должно. Вот если будешь посылать чужому приложению - тогда да, надо договариваться, что это сообщение означает... Посылай
Очевидное-невероятное, блин! Передаю в sethook хэндл:
procedure SetTheHook(h:hwnd); stdcall; begin whan:=h; if HookHandle = 0 then begin HookHandle := SetWindowsHookEx(WH_CBT, @CBTProc, HInstance, 0); end; end;
Смотрю в этой процедуре h - всё верно, совпадает с хэндлом формы, параметр был передан. whan (whan:hwnd, описана глобально), соответственно, присвоено значение h. Это если смотреть сразу после присваивания, в Sethook (я смотрел путём вывода в текстовик). Но в процедуре CBTProc почему-то whan всегда равна нулю!!! На локализацию этой фигни я угрохал полдня )) (зато теперь знаю, что прототип функции действительно лучше не менять)). И вот сейчас сижу и думаю, почему глобальная переменная в одном месте нормальная, а в другом - нулевая.. Значение whan в библиотеке ТОЧНО нигде не трогается, уже всё обсмотрел..
Сообщение отредактировано: Unconnected -
--------------------
"Знаешь, стыдно - когда не видно, что услышал всё, что слушал.."
В общем, если не хочешь потерять еще три дня, и потом все-таки вернуться к тому, что я тебе написал в сообщении №25 - вернись сейчас. ТОЛЬКО MMF гарантируют тебе работу. Память должна быть общей (shared memory). Вот тебе еще один пример: Сайт из гуглокэша (начиная со слов "this is real working example". Проверено, действительно работает). Больше прописные истины повторять не буду. Хочешь экспериментировать - экспериментируй. Как надоест - скажешь.
Короче на данный момент я остановился на текстовике. Логика программы предполагает, что если она вообще работает, то текстовик хватит прав создать... Просто из-за одной переменной добавлять (и разбирать) кода столько, сколько наверное во всей программе нет - нерационально как-то, что ли.. Наверное, я ещё вернусь к этой теме, когда будет "рациональней"..
volvo, ещё раз спасибо за советы, без них я бы тут далеко не уехал
И всё же как-то странно, зачем нужна возможность создания глобальных переменных в dll, если такие дела с ними..
Сообщение отредактировано: Unconnected -
--------------------
"Знаешь, стыдно - когда не видно, что услышал всё, что слушал.."
function GetClName(myWnd: HWND): String; var Cl: array[0 .. pred(MAX_PATH)] of char; begin GetClassName(myWnd, Cl, MAX_PATH); result := cl; end;
function CBTProc(Code: integer; myWParam: WPARAM; myLParam: LPARAM): LRESULT; stdcall; begin if Code < 0 then begin Result := CallNextHookEx(RHookRec^.HookID, code, myWParam, myLParam); exit; end;
case Code of HCBT_CREATEWND: begin if pos('NOTEPAD', UpperCase(GetClName(myWParam))) > 0 then begin PostMessage(RHookRec^.AppWindow, MY_MESSAGE, myWParam, 0); result := 1; Exit; end; end; end; result := 0; end;
{$J+} procedure EntryPointProc(Reason: Integer); const hMapObject: THandle = 0; begin case reason of DLL_PROCESS_ATTACH: begin hMapObject := CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, SizeOf(THookRec), 'volvo_CBT'); rHookRec := MapViewOfFile(hMapObject, FILE_MAP_ALL_ACCESS, 0, 0, SizeOf(THookRec)); end;
DLL_PROCESS_DETACH: begin UnMapViewOfFile(rHookRec); CloseHandle(hMapObject); end;
DLL_THREAD_ATTACH, DLL_THREAD_DETACH: ; end; end;
exports SetTheHook, DelTheHook;
begin DllProc := @EntryPointProc; EntryPointProc(DLL_PROCESS_ATTACH); end.
Сколько кода добавлено? 10 строк? Если что - вот VCL-ный проект, который ставит хук и получает сообщение о том, что окно было заблокировано (проект с DLL-кой тоже вложен): hook_test.zip ( 73.85 килобайт )
Кол-во скачиваний: 472
Кажется понял, в библиотеке просто делается указатель на одну общую структуру, и какие-то пассы при инициализации) Спасибо, сделал по-человечески) Только такой способ кажется не подходит для динамической подгрузки DLL? Делаю так:
type Tsetthehook = procedure(Handle: HWND);stdcall; Tdelthehook = procedure;stdcall;
var setthehook:TSetthehook; delthehook:TDelthehook; DLLInstance : THandle;
Procedure loadlib; begin try DLLInstance := LoadLibrary(pchar(writepath+mydlname)); if DLLInstance = 0 then killmeplz; @setthehook := GetProcAddress(DLLInstance, 'SetTheHook'); if @setthehook <> nil then setthehook(form1.form.handle) else killmeplz; @delthehook := GetProcAddress(DLLInstance, 'DelTheHook'); finally FreeLibrary(DLLInstance); end; end;
, и хэндлы внутри библиотеки опять обращаются в нули...
Сообщение отредактировано: Unconnected -
--------------------
"Знаешь, стыдно - когда не видно, что услышал всё, что слушал.."
Только такой способ кажется не подходит для динамической подгрузки DLL?
Вообще-то DLL-ке все равно, как ее подгружают, хоть загрузкой процесса, хоть LoadLibrary - в любом случае работает ветка DLL_PROCESS_ATTACH. А вот чего ты творишь в программе - непонятно. Ты DLL отключаешь (FreeLibrary) ГДЕ? Сразу после того, как адреса процедур получил? Ну-ну...
Добавлено через 5 мин. P.S. Только что подключил через LoadLibrary:
Procedure loadlib; begin DLLInstance := LoadLibrary(DLLname); if DLLInstance = 0 then ShowMessage('Cannot Load DLL') else begin @setthehook := GetProcAddress(DLLInstance, 'SetTheHook'); @delthehook := GetProcAddress(DLLInstance, 'DelTheHook'); end; end;
procedure TForm1.FormCreate(Sender: TObject); begin loadlib; end;
procedure TForm1.FormDestroy(Sender: TObject); begin FreeLibrary(DLLInstance); end;
Блин, ковырялся с дллами - и заснул) И как я её только не отключаю (-ал) Насколько понимаю, после FreeLibrary библиотека должна исчезнуть из памяти, чтобы предоставить возможность удалить саму dll. Но она что-то такой возможности не представляет, даже после завершения программы. Более того, происходит такая странная вещь - dll даёт себя удалить где-то через минуту.. Я сначала думал, что это связано с использованием string, позаменял все string на shortstring (длиннее 255 символов строк нет там), - не помогло.
Читаю Гансмокера. Кажется, затянется - в каждой статье куча перекрестных ссылок) Говорит, использование хуков при блокировке загрузчика ОС приведёт к катастрофе)
--------------------
"Знаешь, стыдно - когда не видно, что услышал всё, что слушал.."
Насколько понимаю, после FreeLibrary библиотека должна исчезнуть из памяти, чтобы предоставить возможность удалить саму dll. Но она что-то такой возможности не представляет, даже после завершения программы.
Вот ты будешь смеяться, но: Unit1.pas ( 1.88 килобайт )
Кол-во скачиваний: 538
прекрасно удаляет ту самую DLL-ку, которая была в прошлом проекте, только теперь она запихана в ресурсы и появляется в папке только при старте программы. После завершения - исчезает (нет, нет, не через минуту - я б столько не прождал ). Что-то у тебя DLL не то делает, или ты мудришь с ее загрузкой. Откуда там взялись уже строки?
Цитата
И как я её только не отключаю (-ал)
А не надо ее "как только не отключать". Надо помнить, что каждый LoadLibrary или статическое подключение - это (+1) к счетчику ссылок, а каждое завершение процесса (при статическом подключении) или FreeLibrary - это (-1). До тех пор, пока счетчик этот не станет = 0, DLL выгружена не будет.