Модераторы: Poseidon, Snowy, bems, MetalFan
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Анамалия при передаче DLL указателя на структуру 
:(
    Опции темы
Caduser
Дата 31.1.2007, 07:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 71
Регистрация: 2.10.2006
Где: г. Хабаровск

Репутация: 1
Всего: 1



На первый взгляд, задача простая:
В EXE есть процедура которая вызывается из DLL, результат работы процедуры передается как указатель на структуру:
В EXE:
Код

type
  // описание некой структуры (это описание общее для EXE и DLL) 
  PParam= ^TParam;
  TParam = record
    Attr1 : PChar;     
    Attr2 : PChar;
    Attr3 : PChar;
  end;

procedure GetParam(Param: PParam); stdcall;
// эту процедуру будем вызывать из dll
var
  FPar: TParam;  // вспомогательная переменная (для демонстрации аномалии)
begin
  FPar.Attr1 := 'Атрибут1';   
  FPar.Attr2 := 'Атрибут2';  
  FPar.Attr3 := 'Атрибут3';

  // передаем значения переменной FPar в Param
  Param^.Attr1 := FPar.Attr1;
  Param^.Attr2 := FPar.Attr2;
  Param^.Attr3 := FPar.Attr3;
end;

exports
  GetParam;

В DLL:
Код

var
  FDLLPar: TParam;
begin  
  // вызываем процедуру EXE и передаём туда 
  // указатель переменной FDLLPar
  GetParam(Addr(FDLLPar));
  
  // при первом отображении все OK
  ShowMessage(
      Format('Attr1=%s, Attr2=%s, Attr3=%s',
                  [FDLLPar.Attr1,
                   FDLLPar.Attr2,
                   FDLLPar.Attr3
                  ]));

  // а вот тут наблюдается аномалия
  // при всех последующих обращениях к FDLLPar 
  // в полях будет содержаться мусор!!!
  ShowMessage(
      Format('Attr1=%s, Attr2=%s, Attr3=%s',
                  [FDLLPar.Attr1,
                   FDLLPar.Attr2,
                   FDLLPar.Attr3
                  ]));
end;

немогу понять почему только первое обращение к полям записи дает верный результат, а все последующие возвращают мусор???
Причем если в EXE в процедуре GetParam не использовать переменную FPar то эта аномалия пропадет и все будет ОК:
Код

procedure GetParam(Param: PParam); stdcall;
// эту процедуру будем вызывать из dll
var
  //FPar: TParam;  
begin
  //FPar.Attr1 := 'Атрибут1';   
  //FPar.Attr2 := 'Атрибут2';  
  //FPar.Attr3 := 'Атрибут3';
  // вот так все будет ОК
  Param^.Attr1 := 'Атрибут1';
  Param^.Attr2 := 'Атрибут2';
  Param^.Attr3 := 'Атрибут3';
end;


Это сообщение отредактировал(а) Caduser - 31.1.2007, 07:25
PM MAIL ICQ   Вверх
bazzzman
  Дата 31.1.2007, 08:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 62
Регистрация: 24.7.2006

Репутация: 0
Всего: 2



Цитата
немогу понять почему только первое обращение к полям записи дает верный результат, а все последующие возвращают мусор???

Чистое совпадение. Возможно на твоем компьютере это так на другом будет иначе. 

Вот это не верно в принципе:
Код

procedure GetParam(Param: PParam); stdcall;
// эту процедуру будем вызывать из dll
var
  FPar: TParam;  // вспомогательная переменная (для демонстрации аномалии)
begin
  FPar.Attr1 := 'Атрибут1';   
  FPar.Attr2 := 'Атрибут2';  
  FPar.Attr3 := 'Атрибут3';

  // передаем значения переменной FPar в Param
  Param^.Attr1 := FPar.Attr1;
  Param^.Attr2 := FPar.Attr2;
  Param^.Attr3 := FPar.Attr3;
end;


Т.к. FPar - локальная переменная и будет уничтожена после выхода из функции. Отсюда имеем разрушение сторок (они все PChar) и мусор. В справке Delphi 7 этот пример описывается как "самая распространенная ошибка с использованием PChar".
Я сам недавно напоролся на этот глюк  smile 


Можно сделать var FPar: PParam; и GetMem. Но придется после каждого вызова следить за освобождением памяти.
PM MAIL WWW   Вверх
Caduser
Дата 31.1.2007, 09:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 71
Регистрация: 2.10.2006
Где: г. Хабаровск

Репутация: 1
Всего: 1



Цитата(bazzzman @  31.1.2007,  08:34 Найти цитируемый пост)
FPar - локальная переменная и будет уничтожена после выхода из функции
 Совершенно верно! Скорее всего в этом причина моей ошибки.
Теперь вопрос как это корректно решить?
Цитата(bazzzman @  31.1.2007,  08:34 Найти цитируемый пост)
Можно сделать var FPar: PParam; и GetMem. Но придется после каждого вызова следить за освобождением памяти. 
Где нужно освобождать память этой переменной? Ведь при освобождении памяти мы опять получим уничтожение локальной переменной? Вообще, в DLL уже выделяется память под переменную FDLLPar: TParam при её объявлении, задача - заполнить поля с помощью процедуры GetParam.


Это сообщение отредактировал(а) Caduser - 31.1.2007, 09:25
PM MAIL ICQ   Вверх
MetalFan
Дата 31.1.2007, 09:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Аццкий Сотона
****


Профиль
Группа: Комодератор
Сообщений: 3815
Регистрация: 2.10.2006
Где: Moscow

Репутация: 62
Всего: 128



Цитата(bazzzman @  31.1.2007,  08:34 Найти цитируемый пост)
Я сам недавно напоролся на этот глюк

это не глюк, это вполне логичное поведение, имхо.

Цитата(Caduser @  31.1.2007,  09:08 Найти цитируемый пост)
Где нужно освобождать память этой переменной?

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



--------------------
There are always someone smarter than you...
PM MAIL   Вверх
Caduser
Дата 31.1.2007, 10:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 71
Регистрация: 2.10.2006
Где: г. Хабаровск

Репутация: 1
Всего: 1



Цитата(MetalFan @  31.1.2007,  09:51 Найти цитируемый пост)
в вызывающей программе.
вообще считается корректным выделять и освобождать память в одном месте.
т.е. перед вызовом функции из длл выделяешь память под буфер, передаешь сам буфер и его размер в функцию из длл,
в ней происходит его заполнение и в случае, если буфера хватило - возвращаешь 0, иначе - остаточный размер, необходимый для получения отстатка информации...

1. В "академических" примерах (см. вложение), в вызывающей программе память специально не выделяется, т.е. она уже выделяется при объявлении переменной:
Код

var
  FDLLPar: TParam;  // выделяется память
begin  
  GetParam(Addr(FDLLPar));

2. зачем передавать размер если он известен - это размер TParam?

Это сообщение отредактировал(а) Caduser - 31.1.2007, 10:22

Присоединённый файл ( Кол-во скачиваний: 4 )
Присоединённый файл  PENNIES.rar 30,45 Kb
PM MAIL ICQ   Вверх
MetalFan
Дата 31.1.2007, 11:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Аццкий Сотона
****


Профиль
Группа: Комодератор
Сообщений: 3815
Регистрация: 2.10.2006
Где: Moscow

Репутация: 62
Всего: 128



1. да, в том случае этого достаточно, там не передаются указатели на память.
2. тебе заранее неизвестно, какой объем памяти нужен под возвращаемые из длл строки. у тебя там PChar. под указатель(PChar) память выделилась (4 байта), а под саму строку - нет. вот тебе и AV.
вариант:
объяви строки в записи, как shortstring.
минусы: длина строки не более 255 символов. память будет кушать всегда 255 байт, даже если строка короче.
плюсы: не надо дополнительно выделть/освобождать память.


--------------------
There are always someone smarter than you...
PM MAIL   Вверх
Caduser
Дата 1.2.2007, 04:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 71
Регистрация: 2.10.2006
Где: г. Хабаровск

Репутация: 1
Всего: 1



Цитата(MetalFan @  31.1.2007,  11:14 Найти цитируемый пост)
объяви строки в записи, как shortstring.
минусы: длина строки не более 255 символов. память будет кушать всегда 255 байт, даже если строка короче.
плюсы: не надо дополнительно выделть/освобождать память. 

Сделал примерно так:
Код

const
  MAX_TEXT_LENGTH = 512;
type
  // описание некой структуры (это описание общее для EXE и DLL) 
  PParam= ^TParam;
  TParam = record
    Attr1 : array [0..MAX_TEXT_LENGTH-1] of Char;     
    Attr2 : array [0..MAX_TEXT_LENGTH-1] of Char;
    Attr3 : array [0..MAX_TEXT_LENGTH-1] of Char;
  end;
procedure GetParam(Param: PParam); stdcall;
// эту процедуру будем вызывать из dll
var
  FPar: TParam;  // вспомогательная переменная (для демонстрации аномалии)
begin
  StrPCopy(FPar.Attr1, 'Атрибут1');  
  StrPCopy(FPar.Attr2, 'Атрибут2');
  StrPCopy(FPar.Attr3, 'Атрибут3');  
  // передаем значения переменной FPar в Param
  StrPCopy(Param.Attr1, FPar.Attr1);  
  StrPCopy(Param.Attr2, FPar.Attr2);
  StrPCopy(Param.Attr3, FPar.Attr3);  
end;
exports
  GetParam;

теперь все работает отлично.
Большое спасибо, всем, за помощь!

Это сообщение отредактировал(а) Caduser - 1.2.2007, 04:33
PM MAIL ICQ   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: Общие вопросы"
SnowyMetalFan
bemsPoseidon
Rrader

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Литературу по Дельфи обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь
  • 90% ответов на свои вопросы можно найти в DRKB (Delphi Russian Knowledge Base) - крупнейшем в рунете сборнике материалов по Дельфи


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Snowy, MetalFan, bems, Poseidon, Rrader.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Delphi: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0469 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.