Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Delphi программирование > Общие вопросы Delphi
Регистрация

Восстановить пароль

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 08.03.2011, 22:19   #1
Genco
Пользователь
 
Аватар для Genco
 
Регистрация: 16.11.2010
Сообщений: 46
По умолчанию Файловое отображение портит работу DLL

Здравствуйте!
У меня хитрая проблема. Пишу dll, в ней несколько функций, часть относится к установке хука на мышь. Некая MouseHook - соотвественно, обработчик. Библиотека грузится из основного приложения статически. Для взаимодействия длл и основной программы создаю отображение файла в память таким образом:
Код:
 
const
  MFName: PChar = 'RoomMap';

type
  PGlobalDLLData = ^TGlobalDLLData;
  TGlobalDLLData = packed record
    MouseHook: THandle; // хендл хука, пока не используется 
    MsAppWnd: THandle; // хендл основного приложения
  end;

var
  msHook: THandle;
  MFHandle: THandle;
  WM_ROOM: Cardinal;
  GlobalData: PGlobalDLLData;  

//------------------------------------------------------------
procedure InitHandles(wnd: HWND); export; stdcall;
begin
 WM_ROOM:=RegisterWindowMessage('WM_ROOM');   //сообщение посылать чтобы
 MFHandle:= CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, SizeOf(TGlobalDLLData), MFName);
 if ( (MFHandle<>0) and (GetLastError() = ERROR_ALREADY_EXISTS) ) or (MFHandle=0) then
  begin
   CloseHandle(MFHandle);
   MessageBox(0, 'Can''t create FileMapping', 'Message from keyhook.dll', 0);
   Exit;
  end;

 GlobalData:= MapViewOfFile(MFHandle, FILE_MAP_ALL_ACCESS, 0, 0, SizeOf(TGlobalDLLData));
 if GlobalData = nil then
  begin
   CloseHandle(MFHandle);
   MessageBox(0, 'Can''t make MapViewOfFile', 'Message from keyhook.dll', 0);
   Exit;
  end;

 GlobalData^.MsAppWnd:=wnd;
 // ShowMessage('data ok '+IntToStr(GlobalData^.MsAppWnd));  //отладка
end;
Ну, соответственно, потом все ресурсы освобождаю, это понятно. Далее я пытаюсь использовать отображение в функции - обработчике, которая до этого работала. в той же dll. Пробую так:
Код:
function MouseHook(nCode, wParam, lParam: integer): Lresult; stdcall;
var
  msg: PMouseHookStruct;
  w: HWND;
  style: integer;
begin
  if nCode < 0 then
  begin
    result := CallNextHookEx(msHook, nCode, wParam, lParam);
    exit;
  end;
  msg := PMouseHookStruct(lParam);

  case wParam of
    WM_MBUTTONDOWN : pt := msg^.pt;
    WM_MBUTTONUP :
    begin
      if (GlobalData^.MsAppWnd<>0) then ShowMessage('YEAHH!!') // ShowMessage(IntToStr(GlobalData^.MsAppWnd))
      else ShowMessage('FUUU!!'); 
      SendMessage(GlobalData^.MsAppWnd,WM_ROOM,3,7);
      
      {Тут мой прекрасной работавший код}

    end;
  end;

  result := CallNextHookEx(msHook, nCode, wParam, lParam);
end;

procedure SetMouseHook;
begin
  msHook := SetWindowsHookEx(wh_mouse, @mouseHook, hInstance, 0);
  GlobalData^.MouseHook:=msHook;   // временно пользуюсь хендлом НЕ из отображения, пока оно не заработало, дублирую переменной 
  if msHook = 0 then MessageBox(0, 'Lagg!', 'MouseHook failed!', mb_ok);
  ShowMessage('hooked');
end;

procedure RemoveMouseHook;
begin
  UnhookWindowsHookEx(msHook);
  ShowMessage('Unhooked');
end;
Функции экспортирую, вызываю в приложении в правильном порядке, всё как надо, отлаживался MessageBox'ами.
Так вот, рабочий код таковым быть перестает (не вызывается), сообщение с куском глобальной информации тоже не вылезает. Никакого краха не происходит, данные даже не портятся, хендлы из отображения непосредственно перед UnmapViewOfFile можно посмотреть/вывести, будут правильные, но обработчик тихо умирает в 100% случаев.
Подскажите, пожалуйста, что не так?
Бложегов не имею, но найти меня можно в уютном междусобойчике тут: http://coverduck.ru
"Форум программистов, анархистов, анонимусов и просто любителей с интересом пообщаться"
Genco вне форума Ответить с цитированием
Старый 08.03.2011, 22:27   #2
Пепел Феникса
Старожил
 
Аватар для Пепел Феникса
 
Регистрация: 28.01.2009
Сообщений: 21,000
По умолчанию

помоему вы забыли, что длл с хуком грузится в чужой процесс, и в итоге ваши экспортные функции работают с той длл что подгружена к вам, а не к той что обрабатывает хук в чужом приложении.
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
Программа делает то что написал программист, а не то что он хотел.
Функции/утилиты ждут в параметрах то что им надо, а не то что вы хотите.
Пепел Феникса вне форума Ответить с цитированием
Старый 08.03.2011, 22:42   #3
Genco
Пользователь
 
Аватар для Genco
 
Регистрация: 16.11.2010
Сообщений: 46
По умолчанию

Ну так я же ведь затем и создал объект файлового отображения (везде примерно это и рекомендуется, кажется):
1) пишу хендлы на страницу памяти
2) обращаюсь туда из длл

Откуда бы ни грузилась длл, она будет лезть в универсальное место и что-то делать, считав оттуда данные. До этого функция - перехватчик работала причем (ловила мышь), значит можно было ожидать, что в тех же случаях она вдобавок поработает с глобальными данными. Ан нет, пролет.

Это нормально? Я сейчас попытаюсь хендл хука тоже в отображение поместить, но не думаю, что результат будет.

// Да, вот ща везде подставил GlobalData^.MouseHook - программа глючит и летит и не закрывается.
Бложегов не имею, но найти меня можно в уютном междусобойчике тут: http://coverduck.ru
"Форум программистов, анархистов, анонимусов и просто любителей с интересом пообщаться"

Последний раз редактировалось Genco; 08.03.2011 в 23:00. Причина: Попробовал иначе
Genco вне форума Ответить с цитированием
Старый 08.03.2011, 23:14   #4
Пепел Феникса
Старожил
 
Аватар для Пепел Феникса
 
Регистрация: 28.01.2009
Сообщений: 21,000
По умолчанию

так так так, стоп.
это весь код длл?

помните одну вещь, что в другом процессе другой экземпляр длл, и он ничего не знает о том что было сделано в вашем процессе. НИЧЕГО.

поэтому идет так:
при загрузке получается два экземпляра модуля(не *.pas модуль, а исполняемый модуль)
модуль А в вашем процессе, и модуль Б в процессе жертве.
действия:
1)вы иницилизуете модуль А, в нем открываете проекцию и заполняете переменные.
2)далее ставите хук и модуль Б начинает работу.
3)в модуле Б все переменные имеют базовое значение, в итоге он не знает ни о проекции открытой в А, ни о глобальный переменных там же.
итог обломный.

если вам надо чтоб модуль Б при своей инициализации нашел например окно вашей программы(а та через DuplicateHandle передала ему хендл на проекцию ответом на сообщение), то используйте DllEntryPoint.
да кстати, хендлы тоже надо корректно переносить с одного процесса в другой.



помоему стоит подучить основы организации процессов в Win32, читать Рихтера(разработка эффективных Win32 приложений) главы про ДЛЛ и виртуальное адресное пространство, и помоему еще про процессы пригодится(а советовал бы прочесть всю книгу, ибо я не жалею что потратил на нее время)
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
Программа делает то что написал программист, а не то что он хотел.
Функции/утилиты ждут в параметрах то что им надо, а не то что вы хотите.

Последний раз редактировалось Пепел Феникса; 08.03.2011 в 23:16.
Пепел Феникса вне форума Ответить с цитированием
Старый 09.03.2011, 01:20   #5
Genco
Пользователь
 
Аватар для Genco
 
Регистрация: 16.11.2010
Сообщений: 46
По умолчанию

Точно! Немного подумал и понял, о чем вы. Спасибо большое!!
В промежуточно-отладочном варианте сделал примерно так:
Код:
library hooker;

uses
  windows, messages,dialogs,sysutils;

const
  MFName: PChar = 'RoomMap';

type
  PGlobalDLLData = ^TGlobalDLLData;
  TGlobalDLLData = packed record
    MouseHook: THandle; // äåñêðèïòîð óñòàíîâëåííîé ëîâóøêè
    MsAppWnd: HWND; // äåñêðèïòîð íàøåãî ïðèëîæåíèÿ
  end;

var
  pt: TPoint;
  MFHandle: THandle;
  WM_ROOM: Cardinal;
  GlobalData: PGlobalDLLData;

function MouseHook(nCode, wParam, lParam: integer): Lresult; stdcall;
var
  msg: PMouseHookStruct;
  w: HWND;
  style: integer;
begin
  if nCode < 0 then
  begin
    result := CallNextHookEx(GlobalData^.MouseHook, nCode, wParam, lParam);
    exit;
  end;
  msg := PMouseHookStruct(lParam);

  case wParam of
    WM_MBUTTONDOWN : pt := msg^.pt;
    WM_MBUTTONUP :
    begin

      SendMessage(GlobalData^.MsAppWnd,WM_ROOM,3,7);
      
      w := WindowFromPoint(pt);
      style:=GetClassLong(w,GCL_STYLE) xor CS_NOCLOSE;
      SetLastError(0);
      SetClassLong(w,GCL_STYLE,style);
      SetWindowPos(w, HWND_NOTOPMOST, 0,0,0,0,SWP_FRAMECHANGED or SWP_NOMOVE or SWP_NOSIZE or SWP_NOZORDER);
      if GetLastError<>0 then MessageBox(w,'FAIL','sds',MB_OK);
   end;
  end;

  result := CallNextHookEx(GlobalData^.MouseHook, nCode, wParam, lParam);
end;

procedure SetMouseHook(wnd: HWND);
begin
  GlobalData^.MsAppWnd:=wnd;
  GlobalData^.MouseHook := SetWindowsHookEx(wh_mouse, @mouseHook, hInstance, 0);
  if GlobalData^.MouseHook = 0 then MessageBox(0, 'Lagg!', 'MouseHook failed!', mb_ok);
  ShowMessage('hooked '+inttostr(GlobalData^.MouseHook));
end;

procedure RemoveMouseHook;
begin
  UnhookWindowsHookEx(GlobalData^.MouseHook );
  ShowMessage('Unhooked');
end;

procedure InitAllHandles(); 
begin
 WM_ROOM:=RegisterWindowMessage('WM_ROOM');
 MFHandle:= CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, SizeOf(TGlobalDLLData), MFName);
 if (MFHandle=0) then
  begin
   CloseHandle(MFHandle);
   MessageBox(0, 'Can''t create FileMapping', 'Message from keyhook.dll', 0);
   Exit;
  end;

 GlobalData:= MapViewOfFile(MFHandle, FILE_MAP_ALL_ACCESS, 0, 0, SizeOf(TGlobalDLLData));
 if GlobalData = nil then
  begin
   CloseHandle(MFHandle);
   MessageBox(0, 'Can''t make MapViewOfFile', 'Message from keyhook.dll', 0);
   Exit;
  end;

end;


procedure ReleaseAllHandles();
begin
 ShowMessage('released '+IntToStr(GlobalData^.MsAppWnd));
 UnmapViewOfFile(GlobalData);
 CloseHandle(MFHandle);
end;

exports
  SetMouseHook  name 'SetMouseHook',
  RemoveMouseHook  name 'RemoveMouseHook';
  ReleaseAllHandles name 'ReleaseAllHandles';

procedure DLLEntryPoint(dwReason: DWord); stdcall;
begin
  case dwReason of
    DLL_PROCESS_ATTACH: InitAllHandles;
    DLL_PROCESS_DETACH: ReleaseAllHandles;
  end;
end;

begin
  DLLProc:= @DLLEntryPoint; 
  DLLEntryPoint(DLL_PROCESS_ATTACH);
end.
Но мне теперь что, эту библиотеку только динамически вызывать? Просто беглая проверка показала, что ReleaseAllHandles не отрабатывает при статической загрузке почему-то, приходится явно вызывать в основном коде.

З.Ы. Мда, матчасть и правда возьмусь подтянуть.
Бложегов не имею, но найти меня можно в уютном междусобойчике тут: http://coverduck.ru
"Форум программистов, анархистов, анонимусов и просто любителей с интересом пообщаться"

Последний раз редактировалось Genco; 09.03.2011 в 01:26. Причина: поправил огрех
Genco вне форума Ответить с цитированием
Старый 09.03.2011, 01:22   #6
Пепел Феникса
Старожил
 
Аватар для Пепел Феникса
 
Регистрация: 28.01.2009
Сообщений: 21,000
По умолчанию

ReleaseAllHandles отрабатывает в вашем процессе(модуль А) но не в чужом(не в модуле Б)
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
Программа делает то что написал программист, а не то что он хотел.
Функции/утилиты ждут в параметрах то что им надо, а не то что вы хотите.
Пепел Феникса вне форума Ответить с цитированием
Старый 09.03.2011, 01:32   #7
Genco
Пользователь
 
Аватар для Genco
 
Регистрация: 16.11.2010
Сообщений: 46
По умолчанию

Ну это с одной стороны терпимо, удалить объект достаточно всего 1 раз)
Но... Т.е. грузить динамически строго, чтобы срабатывал DLL_PROCESS_DETACH и было "по науке"?
Бложегов не имею, но найти меня можно в уютном междусобойчике тут: http://coverduck.ru
"Форум программистов, анархистов, анонимусов и просто любителей с интересом пообщаться"
Genco вне форума Ответить с цитированием
Старый 09.03.2011, 01:50   #8
Пепел Феникса
Старожил
 
Аватар для Пепел Феникса
 
Регистрация: 28.01.2009
Сообщений: 21,000
По умолчанию

в чужом процессе да, в своем в принципе можно и в статике оставить, но тогда DLL_PROCESS_DETACH произойдет при уничтожении приложения.

поэтому лучше всетаки динамика, чтоб оно происходило и там и там одинакого.
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
Программа делает то что написал программист, а не то что он хотел.
Функции/утилиты ждут в параметрах то что им надо, а не то что вы хотите.
Пепел Феникса вне форума Ответить с цитированием
Старый 09.03.2011, 02:03   #9
Genco
Пользователь
 
Аватар для Genco
 
Регистрация: 16.11.2010
Сообщений: 46
По умолчанию

Ещё раз спасибо! Теперь всю систему понял, как писать.
Бложегов не имею, но найти меня можно в уютном междусобойчике тут: http://coverduck.ru
"Форум программистов, анархистов, анонимусов и просто любителей с интересом пообщаться"
Genco вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Модуль GRAPH портит русский при запуске .exe файла! Nemo-237 Паскаль, Turbo Pascal, PascalABC.NET 3 06.03.2011 22:59
Excel портит файлы? Random2 Софт 9 19.10.2010 16:00
Что-то портит операционку Yura_S Компьютерное железо 14 17.08.2010 12:48
Ошибка при выхове функций Bass.dll из другой DLL SalasAndriy Общие вопросы Delphi 7 21.10.2009 23:36
Отображение скрытых файлов и папок и отображение розширения всех файлов beegl Общие вопросы Delphi 10 14.12.2008 22:02