Delphi передать переменную в dll
← →
Andrey ( 2003-06-18 16:31 ) [0]
Собственно все бы хорошо, но. Надо по ссылке передавать, чтоб Dll-ка могла менять значения этих параметров. А возвращаемые значения естественно могут быть больше чем передаваемые.
Короче так. Есть у меня 5 переменных PChar в проэкте, нужно эти переменные передать в библиотеку. Она (библиотека) чего-то с ними делает, в результате после завершения работы библитеки в этих 5 переменных должны быть новые значения.
Вся работа с Dll-кой должна ограничится:
1. загрузил Dll
2. получил адрес проседуры в Dll
3. вызвал процедуру из Dll
4. по завершении работы процедуры выгрузил Dll
Вся проблемма в том что при передаче в Dll PChar-ов я в Dll немогу менять размер выделеный под них.
Глобальные переменные в Dll и borlndmm.dll не предлогать. Все больше склоняюсь к тому, что прийдется делать через GlobalLock.
После столь сумбурного описания задачи, наконец попробую сформулировать вопрос.
Как передать в Dll вкачестве параметра при вызове процедуры указатель на строку, и получить указатель на другую строку, большего размера, при этом не используя ни глобальные переменные в Dll, ни GlobalLock, ни borlndmm.dll?
N169 ( 2003-06-18 16:35 ) [1]
Передавать адрес некой переменной, гарантированно существующей в то время, как работает ф-я из dll, а в неё заносить адрес динамически выделенной строки.
Память выделять в контексте процесса ф-ей HeapAlloc(), как рекомендует MSDN.
Все дела!
panov ( 2003-06-18 16:36 ) [2]
Написать свой менеджер памяти, как в BorlndMM.dll
Не мучайся. Намного проще использовать ShareMem.pas
BorlndMM.dll занимает всего 25к.
Причина, по которой не хочешь пользоваться этим, несколько непонятна.
Если все же тебе надо испльзовать строки, то используй PChar, а под результат перед передачей параметра в функцию из DLL выделяй максимальный размер памяти, который может понадобиться функции.
← →han_malign ( 2003-06-18 16:44 ) [3]
function fff(const aData; var aSize: DWORD): DWORD;external "myDll";
.
var sz: DWORD;
.
sz:=0;
res:=fff(nil^,sz);
if(res=ERROR_MORE_DATA)then begin
GetMem(p,sz);
fff(p^,sz);
.
FreeMem(p);
end;
З.Ы. Уж не знаю насколько это нормально(я уже привык), но это стандартный подход WinApi.
Andrey ( 2003-06-18 16:59 ) [4]
>panov
Причина состоит в том что я этим самым BorlndMM никогда не пользовался, а сейчас начинается новый немаленький проэкт и просто страшно использовать непроверенные технологии.
>под результат . выделяй максимальный размер памяти
Чесно гооря ниэстетично :) Хотя конечно такой вариант имеет право на существование.
>han_malign
Хм. Неуверен, что это хороший стиль (хотя и видел такое в help-ах). К тому же свсем необязательно, что функция ссамого начала знает сколько под результат нужно места. Например функция которая показывает модальное окно в которое пользователь вводит текст врядли может предугадать сколько пользователь введет символов.
>N169
>HeapAlloc()
Я уже говорил: "Все больше склоняюсь к тому, что прийдется делать через GlobalLock. "
> Причина состоит в том что я этим самым BorlndMM никогда
> не пользовался, а сейчас начинается новый немаленький проэкт
> и просто страшно использовать непроверенные технологии.
Вот что странно - Delphi ты использовать не боишься, а borlndmm.dll для тебя непроверенный. Пользователи Borland"а во всем мире его уже много раз проверили, и ничего, довольны.
← →Andrey ( 2003-06-18 17:38 ) [6]
>Игорь Шевченко
А я очень долго после Clipper-а и Delphi неверил :)
Вот тут по поисковикам полазил с запросами типа "ошибка при использовании BorlndMM". немного флуда, а поделу почти ничего, значит можно использовать :) Хотя конечно осторожно. чтоб в любой момент при критическом сбое можно было любой модуль в проэкт намертво (через uses) вкомпилировать :)
← →nikkie ( 2003-06-18 17:45 ) [7]
>>N169
>>HeapAlloc()
>Я уже говорил: "Все больше склоняюсь к тому, что прийдется делать через GlobalLock. "
ты наверное GlobalLock не использовал никогда. имхо, HeapAlloc/HeapRealloc - самое красивое решение. другое имхо - при использовании borlndmm использовать эту dll из других языков проблематично будет.
Andrey ( 2003-06-20 13:14 ) [8]
>nikkie
Ну незнаю незнаю. Вопервых технология HeapAlloc/HeapRealloc выглядит для меня еще более темным лесом чем borlndmm (я API почти не использую, только через VCL стараюсь работать), вовторых пока не планируется подключать модули на других языках, а в третих. ну ненашел я красивого решения как передать в процедуру 4 строки и получить на их место 4 новых строки и все это с помощью HeapAlloc/HeapRealloc
Есть функция в DLL
Connect: function(Database: TIBDatabase;
UserName, UserPs, DBServer, DBPath: string): integer; stdcall;
Если через borlndmm то ничего менять ненадо, можно прямо так и вызывать
Result:=Connect(DM.Db, UserName, UserPs, DBServer, DBPath);
А вот если через HeapAlloc/HeapRealloc то нужно писать нечто типа:
var lUserName, lUserPs, lDBServer, lDBPath: Pointer;
begin
.
lUserName:=HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, Length(UserName));
/* и так для каждой переменной */
Result:=Connect(DM.Db, lUserName, lUserPs, lDBServer, lDBPath);
UserName:=PChar(lUserName);
HeapFree(GetProcessHeap, 0, lUserName);
/* и так для каждой переменной */
Конечно последний блок (UserName:=PChar(lUserName); HeapFree(. )) можно вынести в отдельную процедуру. Но мое IMHO, все это крайне не эстетично :)
Может я пропустил какой-то более простой вариант? Поправте меня пожалуста.
P.S. Глобальные переменные (UserName. ) нехочется изначально через HeapAlloc инициализировать, потму что работа с ними сильно усложнится.
Вместо
UserName:=EUserName.Text;
писать
UserName:=HeapReAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, UserName, Length(EUserName.Text)+1);
UserName:=EUserName.Text;
Andrey ( 2003-06-20 13:20 ) [9]
В догонку.
Наверно Игорю Шевченко. А как borlndmm с Runtime packages работает? В смысле поддерживает ли он их? Или может он только их и поддерживает?
← →DiamondShark ( 2003-06-20 13:31 ) [10]
> А как borlndmm с Runtime packages работает? В смысле поддерживает
> ли он их? Или может он только их и поддерживает?
А с ними он вообще не нужен.
Кстати, если проект задумывается целиком на Дельфи, почему бы не использовать пакеты?
Заморачиваться с ДЛЛ имеет смысл только в случае использования нескольких средств разработки
← →nikkie ( 2003-06-20 13:58 ) [11]
>Но мое IMHO, все это крайне не эстетично :)
А по-моему - нормально. Только надо еще из стринга в аллокированную память строку копировать и передавать указатели на PChar, а не сами PChar. Если тебе удобнее использовать string, то пишешь один раз и навсегда юнит с функциями-обертышами.
Кстати, у тебя dll собирается username и т.п. менять? Там что, формочки содержатся? Тогда разумно и о пакетах подумать. А если параметр не меняется, то и HeapAlloc не нужен - просто PChar(UserName) передаешь.
А с GlobalLock получится только больше кода.
GlobalAlloc
GlobalLock
CopyMemory
GlobalUnlock
вызов функции(HGLOBAL)
GlobalLock
s := .
GlobalUnlock
GlobalFree
Andrey ( 2003-06-20 14:41 ) [12]
>DiamondShark
>если проект задумывается целиком на Дельфи, почему бы не использовать пакеты?
:) хоть проэкт и задумывается на Delphi, но совсем отгораживатся от других языков тоже нехочется. Типа: "Конечно наш комплекс сможет использовать библиотеку написаную на любом языке, но максимальной эффективности можно дудет добится только с библиотекой написаной на Delphi" а не "К сожалению ваш комплекс несможет работать совместно с нашим через Dll" вот нечто подобное :) Хотя можно возразить, что для связи существует COM и т.д. но это уже не из той оперы.
А про пакеты всетаки надо подумать. И про связь с другими комплексами через COM тоже надо подумать :). И еще про столько мелочей надо подумать, что жалко свою голову становится :) А SQL-скрипт для формирования базы уже почти готов, и пора клиентов писать. ух блин.
Так, что-то я отвлекся.
>nikkie
>Только надо еще.
:) С технологией HeapAlloc/HeapRealloc дела никогда не имел, код не компилировал, Help почитал и на глазок набросал, естественно ошибки. Но проблемма не в этом, а в том, что прийдется под каждую глобальную переменную заводить локальный PChar, и сначала их всех инициировать, а потом назад в глобальные переменные значения возвращать.
>Кстати, у тебя dll собирается username и т.п. менять? Там что, формочки содержатся?
Угу, собирается менять. Угу, формочки содержатся. Вообще библиотека называется Login, а интерфейсная функция Connect. Далее из параметров функции смысл ясен :)
Про пакеты. Наверно Dll и само приложение будем собирать со включенным Runtime packages (это решит проблемму с глобальными переменными Application, Screen. ), но Dll всетаки останутся Dll-ками.
Все мечусь между HeapAlloc/HeapRealloc и borlndmm.
← →nikkie ( 2003-06-20 16:00 ) [13]
выбирай COM. серьезно.
← →DiamondShark ( 2003-06-20 16:10 ) [14]
> Andrey © (20.06.03 14:41)
Не пойму я никак. Зачем себе жизнь усложнять-то? Модульность в рамках проекта -- это одно, интерфейс со сторонними производителями -- это совершенно другое.
Чего бы для подзадач совершенно разного тиа не взять наиболее адекватные средства?
Нет. не понимаю.
Soft ( 2003-06-20 17:55 ) [15]
Мой совет. Пиши на Java, все проблемы сами собой отпадут.
← →Andrey ( 2003-06-20 21:22 ) [16]
>Soft
Ага, пока я на нем что-нибудь напишу и заказчики тоже отпадут :)
>DiamondShark
Ну не поймешь и ненадо. Если опыта в проэктировании средних (в смысле, не больших не малых, а именно средних) систем у меня нет, естественно я буду валить все в кучу. А опыт я приобрести могу только на практике. Естественно хочется написать что-нибудь более-менее универсальное, что потом ради предоставления интерфейса другим программам непришлось пол-проэкта перелопатить.
>Игорь Шевченко © (18.06.03 17:23)
Дополнение. Я больше не его (borlndmm) ошибок боюсь, а своих. Вдруг чего-то по глупости, незнанию сделаю. У меня работает, а у заказчиков нет.
>nikkie
COM это конечно хорошо. IMHO это из пушки по комарам.
Мне всего то хотелось узнать, какие методы передачи параметров существуют, какие лучше и почему.
Ладно, все, вопрос закрыт. Внутри комплекса использую borlndmm. Для комуникации с другими пиложениями по возможности буду использовать HeapAlloc/HeapRealloc. В крайнем случае COM.
Всем спасибо, камера стоп.
← →y-soft ( 2003-06-21 11:37 ) [17]
Всем спасибо, камера стоп
А не рановато ли?:)
Существует еще один вариант решения задачи (довольно широко, кстати, применяется в WinAPI. ), который не требует перераспределения памяти в Dll, а следовательно - и применения внешних менеджеров памяти:
Из Dll функция экспортируется примерно так:
function MyFunction(const Buffer; var BufferSize : DWORD) : BOOL;
В параметре Buffer передается указатель на буфер для записи результата;
В параметр BufferSize вызывающая программа записывает фактический размер буфера. Dll помещает сюда же фактический размер.
В случае, если размер буфера недостаточен (или возникли еще какие-то ошибки), функция устанавливает код ошибки с помощью SetLastError и возвращает False .
В случае неудачи по причине недостаточного размера буфера вызывающая программа выделяет под буфер необходимый размер памяти и повторно вызывает функцию.
← →Anatoly Podgoretsky ( 2003-06-21 11:49 ) [18]
Да это весьма распространенный в WinApi метода. Буфер+Размер+КодОшибки
← →Andrey ( 2003-06-23 10:33 ) [19]
>y-soft & Anatoly Podgoretsky
Вообще-то подобный метод уже предлогали panov (18.06.03 16:36) и han_malign (18.06.03 16:44) в разных вариациях.
Hi , как импортировать переменную из exe в dll?
Или как можно реализовать совместное использование переменной?
У меня есть dll, dllForm, Exe.
Хотела получать Handle загруженной DLL, после чего передать его формедлл, и при закрытии освобождать память.
А также сделать проверку на следующее, если хоть одна форма открыта, то память не может быть освобождена.
Думала применить какой нибудь счётчик, при создании формы +1 к счётчику, при закрытии -1, если при закрытии значение счётчика равно 1, то в этом случае освобождаем память от библиотеки.
Может быть нужно как то по другому, не знаю.
Как импортировать нестандартную DLL
Ну собственно вопрос сформулировал. С импортом стандартных билиотек вроде всё ясно. Есть.
Как импортировать стороннюю DLL в программу?
Здравствуйте! Интересуюсь как импортировать стороннюю DLL в программу так, что бы и на других ПК.
Как импортировать в проект DLL PublicKeyToken
Доброго времени суток. - Есть DLL библиотека подписанная токен ключом; - Есть приложения.
я когда-то тоже желал сделать подобный функционал. Тогда у меня знаний было намного меньше чем сейчас, но расскажу с какими штуковинами я тогда столкнулся и бросил все это нафиг, возможно сейчас если возьмусь снова за старую идею, получу что-то другое, но пока нету такой надобности ?)
во первых, чтобы нам работать с формами, нам надо тащить весь Application и частично VCL туда в dll, так как в дельфи все происходит этим механизмом
во вторых, если нам хочется передавать какие-то классы, нам надо на обеих сторонах иметь один и тот же класс и его описание, нам же надо иметь что-то в DLL известное и на стороне приложения, нам надо знать название описаных методов, так же вместо передачи переменной, нужно сделать механизм, например событие или же свойство
и чтобы избавииться от дублирования описания класса, лично я делал интерфейс и на его основе передавал класс
далее у меня не получилось контроля над формами, когда какую-то показываешь, ведь мы можем насоздавать куууучууу и их надо куда-то удалять и это надо делать же из dll, в ней же мы создаем форму, надо было иметь какой-то список или массив для хранения открытых/созданных, то что и делает Application, он контролирует в обычном приложении их
так что твой подход, возможно это и вывод неких умозаключений, что ты так сдлеала, как-то так и я делал тоже
Надо форма? Создал ее, проработал и закрыл, все ее нету больше, по сути она и не нужна уже
Передача строк в dll и вывод на форму
Здравствуйте! Подскажите, пожалуйста, как правильно организовать передачу строк в dll!! вот есть у.
Передача строк между Dll между Exe
Подскажите алгоритм предачи строк между динамической библиотекой и исполняемым файлом. Мне.
Передача данных в dll
Есть .dll: library dll; uses Windows, WinInet, SysUtils, TlHelp32, UrlMon, Dialogs; var .
Передача массива в dll
Не могли бы обьяснить как реализовать сабж?(возможно ссылку на теорию) Пробовал и в тупую.
Решение
Здесь проблема вот в чём. Вызывающая программа и DLL используют одно и тоже адресное пространство, поэтому в обмене данными можно применять указатели. Но вызывающую программу и DLL обслуживают два различных экземпляра менеджера памяти. И, соответственно, оба менеджера используют две раздельных "кучи" - т. е. две отдельные друг от друга области динамической памяти. Поэтому, если для некого объекта память была выделена, например, в DLL, потом указатель на него был передан в вызывающую программу, а затем, если вызывающая программа попытается осободить память из под этого объекта - то менеджер памяти вызывающей программы выполнит действия по особождению в неподконтрольном ему участке памяти - т. е. в области динамической памяти, которая обслуживается не им, а другим менеджером - связанным с DLL. Такие действия могут даже разрушить целостноть кучи, принадлежащей DLL.
---
Решать эту проблему можно поразному:
1. Использовать модуль ShareMem - этот модуль надо прописать самым первым в предложении Uses в модуле вызывающей программы и модуле DLL. Кроме этого вместе с приложением надо поставлять дополнительную DLL библиотеку: BORLNDMM.DLL. Эта библиотека, содержит альтернативный менеджер памяти Delphi.
Здесь могут быть, как раз, такие проблемы:
Есть и другие альтернативные менеджеры - в инете можно поискать. Они возможно более стабильные. А может и нет.
2. Не использовать динамические типы и указатели. Это часто весьма неудобно.
---
3. Третий вариант - на мой взгляд наиболее удобный.
Идея вот в чём - те динамические объекты, которые создаёт менеджер памяти, связанный с DLL, пускай этот же менеджер памяти и уничтожает. Для этого просто в раздел экспорта поместим процедуру для удаления "собственных" объектов.
Делается, например так:
Код DLL:
Здесь в ProcStr() параметр aSize имеет двойное назначение - при вызове функции через него следует передать размер входной строки, а при возвращении управления в вызывающую программу, через этот же параметр возвращается размер выходной строки.
---
Примеры приведены для случая, когда:
type String = type AnsiString;
- например, для Delphi7.
Для Delphi 2009/2010 надо скорректировать программу - там:
type String = type WideString.
Соответственно, длина строки символов и размер будут иметь различное значение. И пр.
Случился вынос мозга, если вызывать функции и процедуры из DLL то все понятно, через них можно передавать всякие там цифры, тексты и т.п.
А как в обратную сторону? У меня в exe есть глобальная переменная, текст, как мне передать её в процедуру в dll т.е. чтобы dll подхватила это и начала там че-то делать с этим.
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
Программа делает то что написал программист, а не то что он хотел.
Функции/утилиты ждут в параметрах то что им надо, а не то что вы хотите.
S:=GetFromExe(STR); //. Как переменную STR из exe-шника сюда задекларировать? |
вспомнить про аргументы функций и не искать ж**у себе. |
Какого жору в себе не искать?
Может получить в длл, значение переменной из exe? А что собственно такое длл? Это тот же кусок кода, который грубо говоря "Аттачиться" в exe при загрузке. Это как бы одно целом можно так сказать.
ну это не трудно, сперва узнаем адрес глобальной переменной в exe.
к примеру в длл, есть функция:
где твой адрес заместо: $00AA0C
stdcall - не обязательно писать, в настройках и так по умолчанию все стоит.
Как получить адрес переменной в exe?
Легко: x := Cardinal(@value); Value - где ваша переменная, в x - будет занесен адрес. Это как бы приведение подобно в C++ reinterpret_cast.
У меня почему-то нету типа PInteger?
ну это не трудно объявить вверху:
Вот теперь есть.
Важно! Ваша exe должна обязательно загрузить длл
хммм, не пойму зачем такой индийский код, вроде на правки, на инжекты различного типа не похоже.
Возможно экскременты какие-то от рук студентов (проект вынос мозга), а хотя ладно, занимайтесь (но что такое dll надо по учить еще по лучше). =)
Последний раз редактировалось VintProg; 30.07.2012 в 05:26 . Причина: Да борщик с капусткой, вариться туда сюда бегаю, дописываю ответы,но борщик не красный? Ладно разберусь сам буду отлаживать.
Походу я хотел иметь суперглобальную переменную, к которой раз и обратился, и её значение(integer) хоть куда хоть где пиши. Это значение бы хранилось всегда в одном месте. Наверное именно это.
Короче я не знаю че мудрил, я все сделал, Значение offset определяется в процедуре dll(оно неизвестно заранее), потом переносится в X уже в exe-шники чтобы я мог фигачить его в едиты, листбоксы и прочее.
Правда, можно сразу брать из dll, но че меня так прет я не знаю
Ах, да, увидел в чем мой тупик, в функции __WriteBytes есть строка Seek(f,Offset), ясень пень из глобальной offset берется, в которой уже лежит. А я хочу чтобы из X бралось, я туда новый адресс суну. Вот, надо перегнать из X обратно в offset.
← →Vilux ( 2004-02-25 15:38 ) [0]
Как мне передать в dll данные? Допустим есть глобальная переменная, как мне сделать, чтобы dll могла ей воспользоваться? Передать адрес этой переменной, но как?
← →PVOzerski ( 2004-02-25 15:47 ) [1]
Что значит как? В dll пишем функцию с параметром-указателем или параметром по ссылке (что различается только синтаксисом), вызываем эту ф-цию из приложения.
← →Skier ( 2004-02-25 15:50 ) [2]
>Vilux © (25.02.04 15:38)
Расскажи о задаче подробнее.
Digitman ( 2004-02-25 15:58 ) [3]
в Dll :
var
SomeGlobVarPtr: Pointer;
..
procedure SetGlobVarPtr(const Value: Pointer);
begin
SomeGlobVarPtr := Value;
end;
var
SomeGlobVar: Integer;
.
SetGlobVarPtr(@SomeGlobVar);
← →Vilux ( 2004-02-25 23:25 ) [4]
Вот кусок ниже. Т.к. код выполняется каждый раз в разном пространстве, то мне нужно, чтобы в функции был доступ к переменной, которая задается где-нибудь в головной программе
Например, мне нужно, чтобы из функции Key_Hook из DLL можно было прочитать значение переменной MyValue
Кусок основной программы
------------------
MyValue:=0;
hookhandle:=0;
hinstDLL := LoadLibrary(pchar(dir+"sendkey.dll"));
hkprcKeyboard := GetProcAddress(hinstDLL, "Key_Hook");
hookhandle:=SetWindowsHookEx(WH_KEYBOARD, hkprcKeyboard, hinstDLL, 0);
------------------
А это сама dll.
library sendket;
uses
SysUtils, Windows,dialogs,registry;
function Key_Hook(Code: integer; wParam: word; lParam: Longint): Longint;stdcall; export;
begin
.
end;
Гаврила ( 2004-02-25 23:37 ) [5]
>>Vilux © (25.02.04 23:25) [4]
Тебе же все написали, см ответ 3
в DLL заведи глобальную переменную
MyValuePtr : PInteger;
перед первым вызовом Key_Hook передай в DLL отдельной процедурой
@MyValue из основной программы
SetGlobVarPtr(@MyValue);
Реализация в DLL
procedure SetGlobVarPtr(Ptr: PInteger);
begin
MyValuePtr :=Ptr;
end;
после этого значение MyValuePtr^ в DLL всегда равно значению MyValue в exe, потому что она указывает на ту же область памяти.
Vilux ( 2004-02-29 11:17 ) [6]
Подскажите, где ошибка. мало с указателями работал
Основная программа.
var
capt,dir:string[250];
Foo: procedure(PtrCapt,PtrDir: PString);
begin
capt:="a";
dir:="b";
hookhandle:=0;
hinstDLL := LoadLibrary(pchar("sendkey.dll"));
Foo := GetProcAddress(hinstDLL, "SetGlobVarPtr");
Foo(@capt,@dd);
.
end;
Сама dll:
.
var
ss:string[250];
dirr,Capt : PString;
procedure SetGlobVarPtr(PtrCapt,PtrDir: PString);
begin
Capt :=PtrCapt;
dirr:=PtrDir;
ss:=PtrCapt^;end;
.
exports SetGlobVarPtr;
.
Vilux ( 2004-03-01 08:55 ) [7]
Разобрался в проблеме, те советы, что мне давали оказались неправильными, или я что-то не так понял, вот пример правильного решения моей проблемы, тем, кто с этим столкнется. Пример взят со статьи с Корлевства Дельфи
// Example2, Process2 //
// @Aleksey Pavlov //
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
< Private declarations >
public
< Public declarations >
end;
var
Form1: TForm1;
Hdll : HWND;
procedure TForm1.Button1Click(Sender: TObject);
var
hook: MyProcType;
begin
@hook:= nil; // инициализируем переменную hook
Hdll:= LoadLibrary(PChar("hook_dll2.dll")); < загрузка DLL >
if Hdll > HINSTANCE_ERROR then < если всё без ошибок, то >
begin
@hook:=GetProcAddress(Hdll, "hook"); < получаем указатель на необходимую процедуру>
Button2.Enabled:=True;
Button1.Enabled:=False;
hook(true);
end
else
ShowMessage("Ошибка при загрузке DLL !");
procedure TForm1.Button2Click(Sender: TObject);
var
hook: MyProcType;
begin
@hook:= nil; // инициализируем переменную hook
if Hdll > HINSTANCE_ERROR then
begin < если всё без ошибок, то >
@hook:=GetProcAddress(Hdll, "hook"); < получаем указатель на необходимую процедуру>
Button1.Enabled:=True;
Button2.Enabled:=False;
hook(false);
end;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
FreeLibrary(Hdll); < при закрытии формы - освобождаем DLL >
end;
--------------DLL-----------------------
// Example2; Second DLL //
// @Aleksey Pavlov //
// Demo of correct hook - DLL module //
uses
Windows,
Messages,
Forms;
const
MMFName: PChar = "MyMMF2"; // имя объекта файлового отображения
type
PGlobalDLLData = ^TGlobalDLLData;
TGlobalDLLData = packed record
SysHook: HWND; // дескриптор установленной ловушки
end;
var
GlobalData: PGlobalDLLData;
MMFHandle: THandle;
dialog box-е, message box-е, menu, или scroll bar-е>
function SysMsgProc(code : integer; wParam : word; lParam : longint) : longint; stdcall;
begin
if code = HC_ACTION then
begin
if TMsg(Pointer(lParam)^).message = WM_RBUTTONDOWN then
MessageBox(0, "HOOK2 working !", "Message from Exampel2/Process2", 0);
end;
Result:= CallNextHookEx(GlobalData^.SysHook, Code, wParam, lParam);
end;
procedure hook(switch : Boolean) export; stdcall;
begin
if switch=true then
begin
GlobalData^.SysHook := SetWindowsHookEx(WH_GETMESSAGE, @SysMsgProc, HInstance, 0);
if GlobalData^.SysHook <> 0 then
MessageBox(0, "HOOK2 установлен !", "Message from Exampel2/Process2", 0)
else
MessageBox(0, "HOOK2 установить не удалось !", "Message from Exampel2/Process2", 0);
if UnhookWindowsHookEx(GlobalData^.SysHook) then
MessageBox(0, "HOOK2 снят !", "Message from Exampel2/Process2", 0)
else
MessageBox(0, "HOOK2 снять не удалось !", "Message from Exampel2/Process2", 0);
procedure OpenGlobalData();
begin
// MMFHandle:= CreateFileMapping(DWord(-1), nil, PAGE_READWRITE, 0, SizeOf(TGlobalDLLData), MMFName); // можно так, но лучше: см. след. строку
MMFHandle:= CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, SizeOf(TGlobalDLLData), MMFName);
if MMFHandle = 0 then
MessageBox(0, "Can""t create FileMapping", "Message from Exampel2/Process2", 0);
на начало выделенного пространства>
GlobalData:= MapViewOfFile(MMFHandle, FILE_MAP_ALL_ACCESS, 0, 0, SizeOf(TGlobalDLLData));
if GlobalData = nil then
begin
CloseHandle(MMFHandle);
MessageBox(0, "Can""t make MapViewOfFile", "Message from Exampel2/Process2", 0);
end;
procedure CloseGlobalData();
begin
UnmapViewOfFile(GlobalData);
CloseHandle(MMFHandle);
end;
procedure DLLEntryPoint(dwReason: DWord); stdcall;
begin
case dwReason of
DLL_PROCESS_ATTACH: OpenGlobalData;
DLL_PROCESS_DETACH: CloseGlobalData;
end;
end;
> те советы, что мне давали оказались неправильными,
> или я что-то не так понял,
Во-первых, главная ошибка - в вопросе было произнесено слово "глобальный", но ни слова не было сказано ни о глобальных хуках, ни о том, что глобальность требуется на уровне всей системы. Поэтому, естественно, вопрос все поняли так, что нужна глобальность на уровне лишь одной DLL - так и отвечали. Поэтому, если говорить об изначально поставленном вопросе, то ответы [1] и [3] уже были абсолютно верными.
Далее, в [4] Вы уточнили вопрос, упомянув о глобальном хуке (а это уже СОВСЕМ другая задача). Но снова сделали ошибку, причем очень характерную. Состоит она в том, Вы спросили не о том, как решить проблему ИСТИННУЮ, а о том, как решить ту проблему, которую Вы себе представили. Вот смотрите сами:
"Т.к. код выполняется каждый раз в разном пространстве, то мне нужно, чтобы в функции был доступ к переменной, которая задается где-нибудь в головной программе"
Как же так? Вы же сами говорите, что код выполняется каждый раз в разном пространстве - а получить доступ хотите к переменной из пространства Вашей программы. Вот и получилась подмена понятий. Раз в разные пространства внедряется DLL - значит, и переменная должна быть именно в DLL, а не в программе, ведь верно? (о том, как и зачем нужно ее проецировать в страничную память системы - разговор отдельный, а сейчас только скажу, что это нужно всего лишь для ее правильной инициализации во всех копиях данных DLL).
Это и была вторая ошибка - навязывание отвечающим ошибочного видения проблемы и ошибочного подхода к ее решению. Но была еще и ошибка третья.
До сих пор Вы ни разу не упоминали о строках и указателях на них. И лишь в [6] всплыл тип PString (кстати, Вы все же не расшифровали, что такое PString, поэтому я буду предполагать наиболее вероятный, с моей точки зрения, вариант - что это объявленный в SysUtils указатель на длинную строку: PString = ^string). Вот теперь мы и подошли к вопросу - почему не работал вариант [6]. Ниже я убрал из кода все, не относящееся к сути.
var
Capt: string[250];
Foo: procedure(PtrCapt: PString);
begin
Сapt := "a";
Foo(@Capt);
В последней строке уже две ошибки. Во первых, Вы передаете адрес короткой строки вместо адреса длинной - то есть, получаете прямую адресацию вместо заказанной Вами же косвенной (вспомним, что длинная строка - это УЖЕ указатель, поэтому PString - это указатель на указатель). Во-вторых, здесь передается ссылка на служебный нулевой символ короткой строки. Идем дальше и смотрим, к чему же это приводит.
var
SS: string[250];
procedure SetGlobVarPtr(PtrCapt: PString);
begin
SS := PtrCapt^;
Далее программа честно пытается делает ей то, что Вы ей велели - она разыменовывает PtrCapt и хочет скопировать в строку SS 250 символов ASCIIZ-строки, которая находится по адресу, который сам записан по адресу $00000001. А эта область ей запрещена (нижние 4K под Win9x и нижние 64K под NT). Понятно, что система говорит "стой, не лезь куда не положено" - и генерит ошибку доступа к памяти. Приплыли.
Но даже если бы этот адрес был бы программе доступен, то, как видите, он все равно неверен - и поэтому все равно возникла бы ошибка. Точнее, программа работала бы, но работала неверно. И отловить причину вот ТАКОЙ ошибки было бы гораздо труднее - так что Вам еще повезло, что ошибка выскочила сама и так быстро.
← →Alex Konshin ( 2004-03-02 01:27 ) [11]
Насколько я вижу, все еще проще - переменная Foo не инициализирована.
← →Юрий Зотов ( 2004-03-02 01:44 ) [12]
Хм. мне казалось, что этого достаточно:
Foo := GetProcAddress(hinstDLL, "SetGlobVarPtr");
:о)
Alex Konshin ( 2004-03-02 08:25 ) [13]
А, точно, в оригинале это есть, а вот в твоем фрагменте этого нет.
Sorry.
Читайте также: