![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Типы в Dll [D7, WinXP]
!_SM_! © (2006-07-19 20:15:00)Есть DLL (моя) в ней описан тип
TInfo = record
Name, Author: String;
end;
есть процедура
procedure GetInfo(var Inf: TInfo); stdcall; export;
begin
Inf.Name:='Name';
Inf.Author:='Author';
end;
в ехе описан тот-же тип
TInfo = record
Name, Author: String;
end;
описана переменная
var GetInfo: procedure (var Inf: TInfo); stdcall;
из ехе динамически гружу dll'ку получаю указатель на процедуру, вызываю ее
HDll:=LoadLibrary(PChar(<путь и имя dll>));
if HDll<>0 then begin
@GetInfo:=GetProcAddress(HDll, 'GetInfo');
if @GetInfo=nil then FreeLibrary(HDll) else
begin
SetLength(Inf.Name, 50);
SetLength(Inf.Author, 50);
GetInfo(Inf);
ShowMessage(Inf.Name);
end;
end;
получаю AV. Как это обойти и желательно без использования ShareMem?
GL00m (2006-07-20 14:50:00) [1]to sniknik:
Черт! Видимо спать надо больше: я был уверен, что с ShortString я уже пробовал и было то же самое. Сейчас сделал и все получилось. Спасибо!
> память под myID не выделил, и данные в нее не записал, вместо
> этого присвоил указатель переменной из модуля
Память выделить - это через StrAlloc? А то, что я присвоил указатель, вместо записи данных - надо было делать StrPCopy(pm^.myID,'Hello') ?
(это я для самообразования на будущее =))
StriderMan (2006-07-20 14:41:00) [2]
> смотри разницу. (вернее ее отсутствие ;)
да, слушай, ты прав! Я и забыл что переменная процедурного типа ведет себя не как pointer.
Но все же меня смущает присвоение ей адреса процедуры через оператор взятия адреса (@).
Я обычно делаю как описал и никаких "собачек" не нужно. и проверка Assigned() вроде специально для этого придумана.
sniknik (2006-07-20 14:30:00) [3]> а проверка
>> @GetInfo <> nil
> всегда даст true. Ведь переменная GetInfo уже объявлена, адрес ее известен. чтобы в этом убедиться предлагаю маленький
> тест:
в свою очередь предлагаю немного изменить этот маленький тест... ;)
type
TInfo = record
Name, Author: String[50];
end;
TGetInfoProc = procedure (var Inf: TInfo); stdcall;
....
var
Form1: TForm1;
GetInfo1: procedure (var Inf: TInfo); stdcall; //это то что у него было
GetInfo2: TGetInfoProc; //это посоветовано тобой
//(правильно в принципе посоветовано, но причина зачем это делать немного "не та" должна быть)
procedure TForm1.Button1Click(Sender: TObject);
var
P: Pointer;
begin
ShowMessage(IntToStr(Integer(@P))+' : '+IntToStr(Integer(@GetInfo1))+' : '+IntToStr(Integer(@GetInfo2)));
end;
смотри разницу. (вернее ее отсутствие ;)
> Я опять встряну со своей проблемой, если не возражаете. =)
не парься, сделай на коротких строках, если не умееш с PChar работать. ([6])
> Что я не так делаю?
память под myID не выделил, и данные в нее не записал, вместо этого присвоил указатель переменной из модуля.
GL00m (2006-07-20 13:53:00) [4]Я опять встряну со своей проблемой, если не возражаете. =)
Переделал я немного свои функции, сделал выделение памяти:
type
TMyMsgUnpack=procedure(m:PChar;var pm:PMyMessage);
MyMessage = record
myID : PChar;
myCount : Integer;
end;
...
var
m : MyMessage;
pm : PMyMessage;
h : Integer;
f : TMyMsgUnpack
begin
pm := AllocMem(SizeOf(m));
h := LoadLibrary('file.dll');
@f := GetProcAddress(h,'MyMsgUnpack');
p := PChar('somedata');
f(p,pm);
FreeLibrary(h);
FreeMem(pm,SizeOf(m));
end;
Процедура MyMsgUnpack лежит в DLL-ке, в ней делается что-то типа:
pm^.myID := PChar('Hello');
pm^.myCount := 5;
Так вот в моей функции после строчкиf(p,pm);получается:
pm^.myID = 'Hello'
pm^.myCount = 5
, но сразу послеFreeLibrary(h);myID становится равным '', а myCount остается 5. Что я не так делаю?
StriderMan (2006-07-20 13:28:00) [5]
> как это не нужны? нужны, это же получение и проверка на
> существование экспортной функции (вполне можно "зацепить"
> и не свою dll где ее не будет), и эта проверка совсем не
> зависит от того как именно задана процедурная переменная.
> (через предварительно описанный тип, или непосредственно
> у переменной)
а проверка
> @GetInfo <> nil
всегда даст true. Ведь переменная GetInfo уже объявлена, адрес ее известен. чтобы в этом убедиться предлагаю маленький тест:
procedure TForm1.Button1Click(Sender: TObject);
var
P: Pointer;
begin
ShowMessage(IntToStr(integer(@P)));
end;
Вот более простой, прозрачный и правильный код
> GetInfo:=GetProcAddress(HDll, 'GetInfo');
> if Assigned(GetInfo) then
sniknik (2006-07-20 13:21:00) [6]> благодаря такому подохду не нужны вот эти пляски с бубном:
>> @GetInfo:=GetProcAddress(HDll, 'GetInfo');
>> if @GetInfo=nil then FreeLibrary(HDll) else
как это не нужны? нужны, это же получение и проверка на существование экспортной функции (вполне можно "зацепить" и не свою dll где ее не будет), и эта проверка совсем не зависит от того как именно задана процедурная переменная. (через предварительно описанный тип, или непосредственно у переменной)
а вот переписать в более понятном виде не помешает... типа
(писал тут, возможны ошибки, поправте если что)
procedure TForm1.FormCreate(Sender: TObject);
var SR: TSearchRec;
procedure AddInf(const FDll: String);
var
HDLL: Integer;
Inf: TInfo
begin
HDll:= LoadLibrary(PChar(FDll));
if HDll <= 32 then RaiseLastOSError;
try
@GetInfo:= GetProcAddress(HDll, 'GetInfo');
if @GetInfo <> nil then begin
GetInfo(Inf);
ShowMessage(Inf.Name);
end;
finally
FreeLibrary(HDll)
end;
end;
begin
if FindFirst('*.dll', faAnyFile, SR) = 0 then begin
repeat
AddInf(SR.Name);
until FindNext(SR) <> 0;
FindClose(SR);
end;
end;
StriderMan (2006-07-20 12:39:00) [7]
> !_SM_! (19.07.06 21:55) [7]
> > StriderMan © (19.07.06 21:11) [2]
> > type TGetInfoProc = procedure (var Inf: TInfo); stdcall;
> > var GetInfo: TGetInfoProc ;
> А это разве не одно и тоже
> var GetInfo: procedure (var Inf: TInfo); stdcall;
> ??????
по сути да. но это более правильный подход. (не ИМХО). Не зря ведь в делфи процедурные типы сделали?
благодаря такому подохду не нужны вот эти пляски с бубном:
> @GetInfo:=GetProcAddress(HDll, 'GetInfo');
> if @GetInfo=nil then FreeLibrary(HDll) else
GL00m (2006-07-20 11:13:00) [8]В общем-то нигде, это все. Т.е. просто
var rez : MyMessage; pmsg : PMyMessage;
Дальше тело, которое я написал:h := LoadLibrary(PChar('file.dll'));итд. В DLL-е в функции MyMsgUnpack сначала
var msg : MyMessage;
Потом
msg.myID := PChar('smthn');
а затем
Result := @msg
Причем, если в запись MyMessage добавить, например,i : Integer, то с ним все работает нормально, а вот с PChar-ом - AV.
Юрий Зотов (2006-07-20 02:37:00) [9]> GL00m (20.07.06 00:50) [14]
Где и как выделяется память под запись, адрес которой возвращает DLL и под строку в этой записи?
GL00m (2006-07-20 00:50:00) [10]Если не возражаете, я присоединюсь к дискуссии - у меня проблема со схожими исходными данными:
имеется
type MyMessage = record
myID : PChar;
end;
PMyMessage = ^MyMessage;
которые также описаны и в DLL-ке и в проекте. В длл-ке есть функцияMyMsgUnpack(s : PChar) : PMyMessage.
Я делаю все то же, что и выше описано:
var rez : MyMessage;
...
h := LoadLibrary(PChar('file.dll'));
@f := GetProcAddress(h,'MyMsgUnpack');
p := PChar(data);
pmsg := f(p);
rez := pmsg^;
FreeLibrary(h);
До этого момента все ОК. Но! Как только я делаю что-то вродеLabel1.Caption := rez.MyID;, то получаю AV и чё за фигня - не пойму. =( Не подскажете, куда копать?
!_SM_! (2006-07-19 23:08:00) [11]Все нормально с PChar'ом без ShareMem'a.
Спасибо всем.
!_SM_! (2006-07-19 23:07:00) [12]Все нормально с PChar'ом
!_SM_! (2006-07-19 23:05:00) [13]Блин, ведь остальные dll просто копии были (неправильные).... правильно.... сейчас попробую
сделал еще так
type TAvtInfo = record
Name, CopyR: PChar;
end;
сейчас попробую, отпишусь
!_SM_! (2006-07-19 23:04:00) [14]Блин, ведь остальные просто копии были.... правильно.... сейчас попробую
сделал еще так
type TAvtInfo = record
Name, CopyR: PChar;
end;
сейчас попробую, отпишусь
sniknik (2006-07-19 22:55:00) [15]> показывает 1 раз ShowMessage(Inf.Name);
> и AV.
одну dll исправил, и она первая находится, остальные нет...
!_SM_! (2006-07-19 21:58:00) [16]В библиотеке тоже переделал
TInfo = record
Name, Author: String[50];
end;
!_SM_! (2006-07-19 21:55:00) [17]
> StriderMan © (19.07.06 21:11) [2]
> type TGetInfoProc = procedure (var Inf: TInfo); stdcall;
> var GetInfo: TGetInfoProc ;
А это разве не одно и тоже
var GetInfo: procedure (var Inf: TInfo); stdcall;
??????
> sniknik © (19.07.06 21:23) [6]
Не работает.
Вот как делаю:
переделал
type
TInfo = record
Name, Author: String[50];
end;
TGetInfo = procedure (var Inf: TInfo); stdcall;
var GetInfo: TGetInfo;
........
procedure TForm1.FormCreate(Sender: TObject);
var SR: TSearchRec;
N, HDLL: Integer;
Inf: TInfo
procedure AddInf(const FDll: String);
begin
HDll:=LoadLibrary(PChar(FDll));
if HDll<>0 then begin
@GetInfo:=GetProcAddress(HDll, 'GetInfo');
if @GetInfo=nil then FreeLibrary(HDll)
else begin
GetInfo(Inf);
ShowMessage(Inf.Name);
end;
end else FreeLibrary(HDll);
end;
begin
N:=FindFirst('*.dll', faAnyFile, SR);
while N=0 do begin
AddInf(SR.Name);
N:=FindNext(SR);
end;
FindClose(SR);
end;
При условии что есть более 1 DLL (подходящих)
показывает 1 раз ShowMessage(Inf.Name);
и AV.
Если закомментировать
//GetInfo(Inf);
//ShowMessage(Inf.Name);
и вставить
ShowMessage( FDll);
то ошибки нет показываются все имена Dll'ек.
sniknik (2006-07-19 21:23:00) [18]> Как это обойти и желательно без использования ShareMem?
TInfo = record
Name, Author: String[50];
end;
ну и естественно убрать
SetLength(Inf.Name, 50);
SetLength(Inf.Author, 50);
StriderMan (2006-07-19 21:20:00) [19]а может вообще сделать
PInfo = ^TInfo;
и его использовать
Rial (2006-07-19 21:18:00) [20]Если тебе не нравится ShareMem, то не используй стринги.
Сделай хотябы
TInfo = record
Name, Author: PChar;
end;
С указателями проблем не будет.
Или вообще
TInfo = record
Name, Author: Pointer;
end;
Размещай даные строк в память по указателю, запоминай размер и передавай.