Форум: "Система";
Текущий архив: 2003.02.27;
Скачать: [xml.tar.bz2];
ВнизТакого не может быть... Найти похожие ветки
← →
VideoLord (2002-12-26 03:19) [0]... потому что просто не может быть никогда :-)
Вот задачка, два програмера над ней бились полночи и решения так и нет.
Нужно было написать процедуру определения, как долго не пользователь не двигал мышку. Для этого используется системный хук в DLL.
Теперь собственно проблема: в разных местах dll _глобальная_ переменная имеет разные значения (в одно и то же время)!
--- Код DLL ---
library idle;
uses windows, sysutils;
var g_dwLastTick : cardinal = 0; {та самая глобальная переменная, хранящая тик последнего касания мышки}
g_hHkMouse : hHook; {хендл хука}
{$R *.RES}
function MouseTracker(Code: integer; wParam: word; lParam: Longint): Longint; stdcall;
var dc: hdc; {нужен для иллюстрации глюка}
begin
if Code=HC_ACTION then g_dwLastTick:=GetTickCount; {обновляем время последнего касания мыши}
dc:=Getdc(0);
textout(dc, 100, 100, PChar(inttostr(g_dwLastTick)), length(inttostr(g_dwLastTick))); {выводим это время на экран}
ReleaseDC(0, dc);
Result:=CallNextHookEx(g_hHkMouse, Code, wParam, lParam); {вызываем следующий обработчик хука в цепочке}
end;
function LastUserAction : cardinal; stdcall; export;
var dc: hdc;
begin
dc:=GetDC(0);
textout(dc, 100, 200, PChar(inttostr(g_dwLastTick)), length(inttostr(g_dwLastTick))); {выводим это время на экран}
ReleaseDC(0, dc);
Result:=g_dwLastTick;
end;
procedure InstallTrap; stdcall; export;
begin
g_hHkMouse:=SetWindowsHookEx(WH_MOUSE, @MouseTracker, hInstance, 0); {устанавливаем хук}
g_dwLastTick:=GetTickCount;
end;
procedure KillTrap; stdcall; export;
begin
if g_hHkMouse<>0 then UnhookWindowsHookEx(g_hHkMouse); {убиваем хук}
end;
exports
InstallTrap, {установка хука}
KillTrap, {снятие хука}
LastUserAction; {функция возвращает время последнего касания мыши}
end.
--- Конец DLL ---
Есть приложение, на главной форме которого есть таймер, который с интервалом в 1 сек, вызывает функцию LastUserAction из DLL.
--- Приложение ---
function LastUserAction : cardinal; stdcall; external "idle.dll";
procedure InstallTrap; stdcall; external "idle.dll";
procedure KillTrap; stdcall; external "idle.dll";
procedure TForm1.IdleTimer(Sender: TObject);
begin
if (gettickcount-LastUserAction) > 5000 Then; {видимо юзер 5 секунд на кнопки не давил...}
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
InstallTrap;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
KillTrap;
end;
--- Конец кода приложения ---
Да. Так вот. Если форма приложения на экране есть (т.е. она visible), то на экран замечательно выводится два постоянно меняющихся значения тиков (нижнее значение с интервалом в одну секунду) если юзер шевелит мышой, или два одинаковых, если юзер пять секунд мышу не трогал.
НО !!!
если форма главного приложения убрана с экрана, т.е. hide, то при шевелении мышой верхнее значение изменяется как нужно, а вот нижнее остается постоянным. При этом естественно в этих двух строках отображаются _разные_ значения!
Так не бывает, вглядитесь в код, и там, и там выводится значение одной и той же глобальной переменной!!!
Все, пора видимо на пенсию...
Помогите, подскажите в чем дело, а то ведь свихнемся совсем от такого...
← →
Song (2002-12-26 08:44) [1]Неактивное окно не генерирует сообщений.
← →
VideoLord (2002-12-26 09:46) [2]Нет. В DLL-ку мы все таки попадаем, значит функция LastUserAction прекрасно вызывается. Это видно если в LastUserAction выводить текст все время в разных местах, например:
textout(dc, 100, Random(10)*30+100, PChar(inttostr(g_dwLastTick)), length(inttostr(g_dwLastTick)));
При этом, даже при свернутой форме приложения, срабатывает таймер (конечно, а как же он может не срабатывать) и раз в секунду вызывается эта LastUserAction. Но вот выводит она одно и то же число...
Вот незадача-то.
Это что, ошибка компилятора (если эту же DLL компилировать в Visual C, то такого глюка нет), или все таки в паскалевском коде какой-то глюк?
← →
Song (2002-12-26 09:50) [3]g_dwLastTick, хотя она у Вас глобальная переменная, будет хранить своё значение только в случае вызова от окон текущего процесса. Если же всё-таки окно будет неактивно, значит значение переменной потеряется. Чтобы выйти из ситуции надо хранить информарцию в памяти.
← →
Cobalt (2002-12-26 09:54) [4]Эта (подобная) проблема при работе с хуками давно освещена.
Данные, РАЗДЕЛЯЕМЫЕ между ДЛЛ хука и программой надо хранить в отбражаемом в память файле, а не в двух и более адресных пространствах!
См. CreateFileMapping и далее.
2 Song >в памяти
;-)
← →
Song (2002-12-26 09:57) [5]2Cobalt © (26.12.02 09:54)
Каламбур.. но однако оно так почти и есть :)
← →
dvp (2002-12-26 10:02) [6]Все дело в том, что для каждого процесса вызывается свой хук-обработчик(т.е. счетчик загрузки DLL увеличивается на 1 и для каждого процесса будут свои данные).В принципе тут нельзя использовать глобальные переменные(ни g_dwLastTick, ни g_hHkMouse ), а использовать например отображаемые в память файлы, о чем была соответствующая статья.
← →
VideoLord (2002-12-26 10:06) [7]Товарищи, но ведь в Visual C же все работает (почему? чем Delphi в этом смысле отличается?), тем более я из программы не обращаюсь напрямую к переменной, лежащей в DLL. Я вызываю функцию в DLL, которая возвращает мне значение этой переменной, тоже лежащей в DLL. Бред какой-то.
Что же получается, что значение глобальной переменной в DLL зависит от того, кто инициирует к ней обращение (хотя в обоих случаях это делает сама DLL, приложение только дает ей команду сделать это)?
И что, без создания файла в памяти тут никак не обойтись?
← →
VideoLord (2002-12-26 10:18) [8]Да, а та статья, о которой тут упоминали Мастера, говорит о случае, когда одну и ту же DLL используют ДВА или несколько процессов (конечно, в этом случае у каждого процесса данные DLL отображаются в разные области памяти и соответственно у меня существовали бы g_dwLastTick c разными значениями).
Но ведь у меня процесс-то один, та программа, которая инициировала установку хука в DLL.
← →
dvp (2002-12-26 10:20) [9]В том от и дело, что ты обращаешся к разным экземплярам DLL, когда у тебя была активна твоя программа, для ней загружалась одна копия, а когда ты свернул, загрузилась еще одна копия DLL для той программы, которая активна, с новыми данными.Это можно посмотреть в SystemInformation NU.
← →
Юрий Зотов (2002-12-26 10:32) [10]> VideoLord © (26.12.02 10:18)
Чтобы все стало ясно.
1. Запустите Вашу программу, которая ставит хук и пусть она работает.
2. В Delphi сделайте любую GUI-программу и поставьте в ней брейкпойнт.
3. Запустите ее. После остановки на брейкпойнте вызовите окно Events - И ВЫ УВИДИТЕ, ЧТО ВАША ПРОГРАММА, КОТОРАЯ НИЧЕГО НЕ ЗНАЛА НИ О КАКИХ ХУКАХ И НЕ СОБИРАЛАСЬ ИХ ИСПОЛЬЗОВАТЬ, ТЕМ НЕ МЕНЕЕ, ЗАГРУЗИЛА ВАШУ ЖЕ DLL С ХУКОМ.
После этого должно стать понятно, что глобальный хук используется ВСЕМИ процессами в системе (на то он и глобальный - ведь в справке сказано "на все потоки"). Соответственно, в адресном пространстве каждого процесса создается СВОЙ экземпляр локальных данных DLL. Вот чего не учли два программиста, которые "над ней бились полночи".
← →
Игорь Шевченко (2002-12-26 11:49) [11]Почему оно работает в Visual C - потому что в Visual C есть более тонкая настройка секция PE-файла, в частности, создание такой секции данных, которая разделяется между всеми экземплярами DLL. При этом, глобальные переменные доступны в единственном экземпляре со всеми изменениями. В Delphi стандартными средствами это сделать нельзя.
← →
VideoLord (2002-12-26 16:11) [12]Да. Проверил так и есть, как вы говорите. Честное слово, я в шоке.
То есть выхода получается 2:
1. Либо писать DLL на дельфи и не использовать в ней переменных, а вместо них использовать mem_maped-файлы
2. Писать на сях и не задумываться ни о чем.
Это большой камень в огород дельфи, я до вчерашней ночи был уверен, что не существует задачи, которую нельзя с одинаковым успехом написать на дельфи и на визуал_си...
В любом случае, огромное спасибо всем, кто помог советом!!!
← →
VideoLord (2002-12-26 17:33) [13]Тогда возникает еще один (теперь наверное простой) вопрос:
в DLL на си пишу следующее:
#pragma data_seg(".Idle")
HHOOK g_hHkKeyboard = NULL;
HHOOK g_hHkMouse = NULL;
DWORD g_dwLastTick = 0;
#pragma data_seg()
#pragma comment(linker, "/section:.Idle,rws")
__declspec(dllexport) DWORD LastUserAction()
{
return g_dwLastTick;
}
При попытке описания этой функции в программе дельфи:
function LastUserAction: dword; external "Idle.dll";
Программа высыпается при старте с сообщением: "Не могу найти точку входа в DLL (LastUserAction)"
Если описать так:
function LastUserAction: dword; external "Idle.dll" index 1;
То программа высыпается с сообщением "Internal exception 0xc0000040"
В чем здесь не так? :-)
← →
Ketmar (2002-12-26 19:07) [14]а stdcall кто писать будет?!
Satanas Nobiscum! 26-Dec-XXXVII A.S.
← →
VideoLord (2002-12-26 21:32) [15]Так?
function LastUserAction: dword; stdcall; external "Idle.dll";
Я так тоже делал. Высыпается точно также :-(
← →
Rouse_ (2002-12-26 22:14) [16]__stdcall(dllexport) DWORD LastUserAction()
Желаю успехов
← →
Игорь Шевченко (2002-12-27 10:27) [17]
>
> Программа высыпается при старте с сообщением: "Не могу найти
> точку входа в DLL (LastUserAction)"
А что при этом говорят DumpBin или TDump про эту DLL и точку входа ? Может, в манглинге собака порылась ?
← →
Ketmar (2002-12-27 10:45) [18]да, конечно. не stdcall, а cdecl. или см. Rouse_.
Satanas Nobiscum! 27-Dec-XXXVII A.S.
← →
VideoLord (2002-12-27 11:20) [19]__stdcall(dllexport) в VC писать нельзя, сразу ругань начинается.
cdecl я тоже уже писал, те же грабли...
а любой просмотрщик видит в этой dll-ке 3 нормальных функции
Ну смотрите сами, исходники dll и сама dll лежат на http://195.19.48.61/data/TrackUserIdle_src.zip
← →
Игорь Шевченко (2002-12-27 15:15) [20]Говорили же, что в манглинге дело:
Exports from IdleTrac.dll
3 exported name(s), 3 export addresse(s). Ordinal base is 1.
Sorted by Name:
RVA Ord. Hint Name
-------- ---- ---- ----
00001000 1 0000 ?IdleTrackerGetLastTickCount@@YAKXZ
00001085 2 0001 ?IdleTrackerInit@@YAHXZ
000010EE 3 0002 ?IdleTrackerTerm@@YAXXZ
В DLL в начале экпортируемых ф-ций ставь волшебное слово
extern "C"
Страницы: 1 вся ветка
Форум: "Система";
Текущий архив: 2003.02.27;
Скачать: [xml.tar.bz2];
Память: 0.51 MB
Время: 0.008 c