Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2004.12.19;
Скачать: [xml.tar.bz2];

Вниз

Работа с классами в DLL   Найти похожие ветки 

 
Piter ©   (2004-11-28 13:08) [0]

Есть ли какие-нибудь ограничения на работу с классами в DLL? Обязательно нужны какие-нибудь модули?

Дело в том, что я пишу DLL и по ходу дела решил разработать класс для упрощения работы с общей для DLL памятью (через проецируемые в память файлы).
Но столкнулся с ошибкой работы DLL. В чем причина - просто загадка.

Данная DLL устанавливает хук, поэтому внедряется во многие процессы.
Если она внедряется в Delphi приложение - то все нормально. Экземпляр TGlobalDataManager успешно создается, выделяется глобальная память... Все это можно протрассировать из дебаггера - проблем нету.

Но если внедрение начинается в другие процессы - то библиотека их просто рушит (Explorer, Delphi, Диспетчер задач и т.д.).
Причем, судя по всему, дело не успевает дойти даже до DLLMain - процесс рушится еще на стадии внедрения библиотеки (возможно, происходит какая-то инициализация дельфового менеджера памяти или там еще чего - фиг знает).

Если не использовать TGlobalDataManager - то библиотека вполне себе успешно внедряется во все процессы, ничего не разрушая.

Вот юнит GlobalDataUtil, где и реализована работа с проецируемыми файлами (извините за объем, выкладываю все - я не знаю где ошибка, если она вообще есть):

unit GlobalDataUtil;

interface

uses Windows;

type
 TGlobalDataRecord = record
   MapHandle: THandle;
   Data: Pointer;
 end;

 TGlobalDataManager = class
 private
   FListDataRecord: array of TGlobalDataRecord;
   FCriticalSection: TRTLCriticalSection;
   procedure AddRecord(const AMaphandle:THandle; const AData: Pointer);
   procedure DeleteRecord(const Index: integer);
   function SearchIndexdByData(const AData: Pointer): integer;
   function CloseGlobalDataByIndex(const Index: integer): boolean;
 public
   constructor Create;
   destructor Destroy; override;
   function OpenGlobalData(const Size: integer; const UnicName: string): Pointer;
   function CloseGlobalData(const Data: Pointer): boolean;
 end;

implementation

{ TGlobalData }

constructor TGlobalDataManager.Create;
begin
 inherited;
 InitializeCriticalSection(FCriticalSection);
end;

destructor TGlobalDataManager.Destroy;
begin
 while Length(FListDataRecord)<>0 do
   CloseGlobalData(FListDataRecord[0].Data);
 DeleteCriticalSection(FCriticalSection);
 inherited;
end;

function TGlobalDataManager.OpenGlobalData(const Size: integer;
 const UnicName: string): Pointer;
var
 MapHandle: THandle;
begin
 EnterCriticalSection(FCriticalSection);
 try
   MapHandle := CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE,
       0, Size, PChar(UnicName));
   if GetLastError = ERROR_ALREADY_EXISTS then
     begin
       CloseHandle(MapHandle);
       Result := nil;
     end
   else
     begin
       Result := MapViewOfFile(MapHandle, FILE_MAP_ALL_ACCESS, 0, 0, Size);
       if Result <> nil then
         AddRecord(MapHandle, Result);
     end;
 finally
   LeaveCriticalSection(FCriticalSection);
 end;
end;

function TGlobalDataManager.CloseGlobalData(const Data: Pointer): boolean;
var
 i: integer;
begin
 EnterCriticalSection(FCriticalSection);
 Result := false;
 try
   i := SearchIndexdByData(Data);
   CloseGlobalDataByIndex(i);
 finally
   LeaveCriticalSection(FCriticalSection);
 end;
end;

procedure TGlobalDataManager.AddRecord(const AMaphandle: THandle;
 const  AData: Pointer);
begin
 SetLength(FListDataRecord, Length(FListDataRecord) + 1);
 with FListDataRecord[High(FListDataRecord)] do
   begin
     MapHandle := AMaphandle ;
     Data := AData;
   end;
end;

procedure TGlobalDataManager.DeleteRecord(const Index: integer);
var
 i: integer;
begin
 if ( index <= High(FListDataRecord) ) and ( index >= 0 ) then
   begin
     for i := index to High(FListDataRecord) - 1 do
       FListDataRecord[i] := FListDataRecord[i+1];
     SetLength(FListDataRecord, Length(FListDataRecord) - 1);
   end;
end;

function TGlobalDataManager.SearchIndexdByData(const AData: Pointer): integer;
var
 i: integer;
begin
 Result := -1;
 for i:=0 to High(FListDataRecord) do
   if FListDataRecord[i].Data = AData then
     begin
       Result := i;
       break;
     end;
end;

function TGlobalDataManager.CloseGlobalDataByIndex(
 const Index: integer): boolean;
begin
 Result := false;
 if ( Index >= 0 ) and ( Index <= High(FListDataRecord) )then
   with FListDataRecord[Index] do
   begin
     UnmapViewOfFile(Data);
     CloseHandle(MapHandle);
     DeleteRecord(Index);
     Result := true;
   end;
end;

end.


 
VMcL ©   (2004-11-28 13:13) [1]

>>Piter ©  (28.11.04 13:08)

>function OpenGlobalData(const Size: integer; const UnicName: string): Pointer;

Не вижу uses ShareMem. Возможно проблема из-за этого. Попробуйте переделать вызовы с использованием String на PChar.


 
VMcL ©   (2004-11-28 13:15) [2]

Да. И еще один момент. Сделайте все public методы виртуальными.


 
VMcL ©   (2004-11-28 13:18) [3]

Тороплюсь что-то.

Где и кем создается экземпляр TGlobalDataManager?


 
jack128 ©   (2004-11-28 15:03) [4]

VMcL ©   (28.11.04 13:13) [1]
зачем? Разве библиотека должна разделять строки с другими приложениями?


 
Piter ©   (2004-11-28 15:05) [5]

Информация о работе только с Delphi приложениями не верная.

Получается так, что эта библиотека работает нормально, если приложение само грузит эту библиотеку (например, библиотека в разделе импорта EXE"щника). Если же библиотека грузится по хуку, то происходит сбой.

Но почему происходит сбой? В тоже время, если не использовать класс TGlobalDataManager - то все, даже если библиотека грузится по хуку.

Ничего не могу понять...


 
Piter ©   (2004-11-28 15:19) [6]

VMcL ©   (28.11.04 13:13) [1]
Не вижу uses ShareMem


дык string"и не импортируются и не экспортируются библиотекой

Сделайте все public методы виртуальными

не помогло...

VMcL ©   (28.11.04 13:18) [3]
Где и кем создается экземпляр TGlobalDataManager?


создается он библиоткой при приходе DLL_PROCESS_ATTACH. Уничтожается соответственно при DLL_PROCESS_DETACH


 
Piter ©   (2004-11-28 15:34) [7]

Piter ©   (28.11.04 15:05) [5]
В тоже время, если не использовать класс TGlobalDataManager - то все, даже если библиотека грузится по хуку.


В тоже время, если не использовать класс TGlobalDataManager - то все нормально, даже если библиотека грузится по хуку.


 
Игорь Шевченко ©   (2004-11-28 16:28) [8]

Убрать динамические массивы


 
VMcL ©   (2004-11-28 16:46) [9]

>>Piter ©  (28.11.04 15:19) [6]

>создается он библиоткой при приходе DLL_PROCESS_ATTACH. Уничтожается соответственно при DLL_PROCESS_DETACH

Про это мне следовало спросить в первую очередь. Покажите код, который использует этот класс.


 
Piter ©   (2004-11-28 19:47) [10]

Игорь Шевченко ©   (28.11.04 16:28) [8]
Убрать динамические массивы


Заменил на связанный список. Не помогло.
Также все приложения рушатся по Run-Time error

P.S. Зато библиотека полегчала на 3 Kb :)


 
Piter ©   (2004-11-28 19:50) [11]

VMcL ©   (28.11.04 16:46) [9]


PGlobalRecord = ^TGlobalRecord ;
 TGlobalRecord = packed record
   HookProcessId: DWORD;
   HookHandle: HHOOK;
   WindowHandle: THandle;
   idMsg: LongWord;
 end;

...

procedure DLLEntryProc(dwReason: DWord);
begin
 case dwReason of
   DLL_PROCESS_ATTACH:
     begin
       ...
       GlobalDataManager := TGlobalDataManager.Create ;
       Ptr := GlobalDataManager.OpenGlobalData(SizeOf(TGlobalRecord),
         cUnicFileName) ;
       if ptr <> nil then
         begin
           GlobalRecord := PGlobalRecord(Ptr)^;
         end;
     end;
   DLL_PROCESS_DETACH:
     begin
       DeleteHook(True) ;
       if GlobalDataManager <> nil then
         GlobalDataManager.Free;
     end;
 end;
end;


 
Игорь Шевченко ©   (2004-11-28 22:48) [12]


> В тоже время, если не использовать класс TGlobalDataManager
> - то все, даже если библиотека грузится по хуку.


Не используй класс TGlobalDataManager


 
Xaker ©   (2004-11-28 23:37) [13]

Piter ©   (28.11.04 15:19) [6]

> Уничтожается соответственно при DLL_PROCESS_DETACH

а DLL_PROCESS_DETACH вообще вызывается при выгрузки ДЛЛ ? - у меня никогда в делфе что-то не вызывалось ...


 
DrPass ©   (2004-11-28 23:41) [14]

Вызывается. Если предварительно установить DllProc на обработчик


 
Piter ©   (2004-11-29 00:13) [15]

Xaker ©   (28.11.04 23:37) [13]
а DLL_PROCESS_DETACH вообще вызывается при выгрузки ДЛЛ ?


естественно. А ты думаешь просто так MS пишет об этом? :)

Игорь Шевченко ©   (28.11.04 22:48) [12]
Не используй класс TGlobalDataManager


Игорь, почему вы злой? Вы хотите об этом поговорить?...

Блин, ну ясен пень ведь что это не ответ. В чем причина то? Нельзя классы использовать в DLL что ли? Вряд ли...
Весь код модуля перед вами (правда, сейчс я переделал без динамических массивов, но поведение осталось).


 
VMcL ©   (2004-11-29 07:25) [16]

>>Piter ©  (29.11.04 00:13) [15]

MS пишет это для своего компилятора. См. [14].

Покажите объявление переменной GlobalRecord и как она далее используется. Есть ли проверка, что GlobalRecord <> nil?


 
Игорь Шевченко ©   (2004-11-29 14:52) [17]

VMcL ©   (29.11.04 07:25) [16]


> MS пишет это для своего компилятора.


Нет, это документированное поведение системы вне зависимости от компиляторов.


 
VMcL ©   (2004-11-29 14:56) [18]

>>Игорь Шевченко ©  (29.11.04 14:52) [17]

AFAIR, в Delphi без этого
DllProc := @DLLEntryProc;
DLLEntryProc при аттаче к процессу не вызовется. Так что выходит, что от компилятора это зависит. Borland не дает прямого доступа к собственно процедуре точки входа напрямую, только посредством DllProc.


 
Piter ©   (2004-11-29 16:42) [19]

VMcL ©   (29.11.04 7:25) [16]
Покажите объявление переменной GlobalRecord


а чего тут показывать? Глобальная переменная типа TGlobalRecord

и как она далее используется

Напрямую используется. Например, в экспортируемой функции может быть написано:

function Send(Flag: boolean): boolean; stdcall;
begin
 Result := true;
 SendMessage(GlobalRecord.WindowHandle, idMsg, x, y);
end;


это я чисто к примеру...

P.S. Что-то я стал сомневаться - ведь верно, что переменные библиотеки одни и теже для различных потоков?

Ну например, в библиотеке есть глобальная переменная I.
Если функцию Send библиотеки вызовет один поток, то при исполнении эта переменная окажется той же самой, если бы функцию Send вызвал другой поток?
То есть, переменные одни для всех потоков. Верно?


 
VMcL ©   (2004-11-30 18:52) [20]

>>Piter ©  (29.11.04 16:42) [19]

>Напрямую используется. Например, в экспортируемой функции может быть написано:

Теперь в связи с этим вопрос. У Вас, если GetLastError = ERROR_ALREADY_EXISTS, то OpenGlobalData вернет nil, а переменная GlobalRecord инициализируется, только если результат этой функциии не nil:
if ptr <> nil then
Собственно вопрос: чем будет инициализорована переменная GlobalRecord в таком случае? Подозреваю, что нулями.

>ведь верно, ччто переменные библиотеки одни и теже для различных потоков

Если они объявлены не как threadvar, а как обычно - var, то для потоков одного и того же процесса - они являются общими. То есть, если одна нить изменит значение такой переменной, то вторая, прочитав его позже, получит именно это новое значение. Для разных же процессов, само собой, они не являются общими.


 
Piter ©   (2004-11-30 22:00) [21]

VMcL ©   (30.11.04 18:52) [20]
Собственно вопрос: чем будет инициализорована переменная GlobalRecord в таком случае? Подозреваю, что нулями


этот код я не привел, но если глобальную память создать нельзя - то тогда пишется ExitCode:=1 - то есть библиотека просто не загружается.

VMcL ©   (30.11.04 18:52) [20]
Если они объявлены не как threadvar, а как обычно - var, то для потоков одного и того же процесса - они являются общими


Для разных же процессов, само собой, они не являются общими.

ну правильно. Я с таким расчетом и делал...


 
jack128 ©   (2004-11-30 22:08) [22]

Piter ©   (29.11.04 16:42) [19]
То есть, переменные одни для всех потоков. Верно?

Верно.  Тоже для EXE.  для можно пользоваться threadvar, как для dll - не знаю, смотри доку.


 
jack128 ©   (2004-11-30 22:09) [23]

jack128 ©   (30.11.04 22:08) [22]
Тоже для EXE.  для EXE можно пользоваться threadvar


 
Piter ©   (2004-11-30 23:28) [24]

Все... решилась проблема. Я как всегда жутко ступил.

VMcL ©   (30.11.04 18:52) [20]

Спасибо огромное! Ты меня навел на мысль - и проблема решилась.

Я почему-то подумал, что ERROR_ALREADY_EXISTS устанавливается только если CreateFileMapping вызывался с данныи UnicName в данном процессе. А ведь на самом деле ERROR_ALREADY_EXISTS будет установлен и в случае, если CreateFileMapping с данным уникальным именем создавался и в каком-то другом процессе! Туплю, туплю...

Из-за этого логика работы TGlobalManager была неверная.

Хотя с другой стороны, и windows ведет себя не особо  правильно, нету защиты от дурака (ногами не пинайте, с себя я ответственность не снимаю).

Получается вот какая ситуаци - библиотека ставит хук. По событию хука эта библиотека загружается в адресное пространство процесса, где произошло событие хука и управление передается хуковой функции! НО! В моем случае получалось, что библиотека отказывалсь загружаться (ExitCode := 1).

То есть, библиотека не загружалась, но windows таки вызывал ЧТО-ТО вместо хуковой процедуры (хуковой функции ведь в удаленном ВАП не может быть, раз библиотека не загрузилась). Видимо, у windows просто нет обработки такой ситуации, что библиотека установила хук, а потом отказалась грузиться по хуку.

Ну это ладно, проехали. Я понял в чем был неправ.

Теперь остается делема - а что делать, если не удалось выделить/получить глобальную память? С одной стороны, продолжение работы библиотеки бесполезно, она просто не функциональна! С другой стороны, отказ загружаться приведет к run-time ошибке в удаленном процессе, как выяснилось. Вероятно, единственный вариант - тупо грузится и ничего не делать?


 
Piter ©   (2004-11-30 23:28) [25]

jack128 ©   (30.11.04 22:09) [23]
для EXE можно пользоваться threadvar


Да, это я знаю...


 
VMcL ©   (2004-12-01 07:40) [26]

>>Piter ©  (30.11.04 23:28) [25]

>Вероятно, единственный вариант - тупо грузится и ничего не делать?

Думаю, да. В начале хуковой функции проверять, проинициализирована ли библиотека. Если нет, то просто вызывать CallNextHookEx и выходить из функции:
if not Initialized then    // или др. условие
begin
  Result := CallNextHookEx(...);
  Exit;
end;


Правда, тогда может быть непонятно, что передавать в качестве параметра HHOOK hhk (если GlobalRecord не проинициализирована).


 
Piter ©   (2004-12-01 16:57) [27]

VMcL ©   (01.12.04 7:40) [26]
Правда, тогда может быть непонятно, что передавать в качестве параметра HHOOK hhk


да не может быть, а просто эта информация неизвестна... Можно ноль передать - на w2k и выше отработает. А в 9x нет...



Страницы: 1 вся ветка

Форум: "Основная";
Текущий архив: 2004.12.19;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.54 MB
Время: 0.047 c
8-1095743439
DelphiN!
2004-09-21 09:10
2004.12.19
Компонент для перетикания изображения


1-1102432913
_Дельфин_
2004-12-07 18:21
2004.12.19
Как создать архиватор?


1-1102255656
Andrick
2004-12-05 17:07
2004.12.19
Создание конструктора класса


1-1102032607
Garfunkel
2004-12-03 03:10
2004.12.19
Сворачивание в трей при загрузке Windows


14-1101370125
ИМХО
2004-11-25 11:08
2004.12.19
Слово о В.И.Ленине (Ульянове)





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский