Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Система";
Текущий архив: 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.64 MB
Время: 0.048 c
3-87272
anatolyk
2003-02-08 20:53
2003.02.27
Работа с БД.


4-87749
markers
2003-01-14 07:41
2003.02.27
Как сделать недоступной форму?


6-87613
Beglec
2003-01-09 07:58
2003.02.27
Delphi + IE


1-87436
race1
2003-02-15 14:25
2003.02.27
tobject


14-87699
Viktor1
2003-02-11 16:58
2003.02.27
Братья!!! Как правильно перекомпилить VCL?





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский