Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2003.02.27;
Скачать: CL | DM;

Вниз

Такого не может быть...   Найти похожие ветки 

 
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;
Скачать: CL | DM;

Наверх




Память: 0.53 MB
Время: 0.016 c
1-87391
Makhanev A.S.
2003-02-17 01:18
2003.02.27
ActionMainMenuBar...


14-87640
SergeN
2003-02-10 16:23
2003.02.27
Button-лопух?


14-87706
sergmu
2003-02-11 20:47
2003.02.27
Базы данных


3-87243
coba
2003-02-07 15:58
2003.02.27
Как правильно написать дату в запросе?


1-87485
serg111
2003-02-14 23:18
2003.02.27
getcolor