Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Типы в Dll [D7, WinXP]


Страницы:   1 2

!_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;

Размещай даные строк в память по указателю, запоминай размер и передавай.


Страницы:   1 2

Наверх

Титульная страница Поиск, карта сайта Написать письмо