Форум: "Основная";
Текущий архив: 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.043 c