![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
PChar результат функции из Dll
slavannnov © (2007-09-19 14:32:00)Из Dll библиотеки отдаю результат функции, как ссылку на запись, в которой содержится PChar.
Функция и типы примерно так:
PMyType = ^TMyType;
TMyType = record
Pst: LongWord;
Str: PChar;
Bool: Boolean;
Int: Integer;
end;
function GetResult(Data: ...): PMyType;
var
Back: TMyType;
begin
...
Back.Str := PChar('My string num = ' + IntToStr(10));
...
Result := @Back;
Вызываю из приложения - получаю AV. Если убрать IntToStr и возвращать просто строку, то все ок. А в случае с IntToStr, Format - всегда AV. Что тут не так?
Сергей М. (2007-09-20 10:12:00) [1]
> Однокамушкин (20.09.07 10:05) [28]
>
>
Надо было сразу уточнить, что речь у тебя идет именно о литералах, ибо из [26] это не очевидно.
> использование слова "сегмент" тоже вполне оправдано
В PE-файле как рез-те сборки модуля нет сегментов, там именно секции. И я вел речь именно об этом.
Однокамушкин (2007-09-20 10:05:00) [2]
> Сергей М. © (20.09.07 09:39) [27]
Не путайте типизированные константы и литералы (константы, размещённые прямо в тексте)
Сделал такую функцию:
procedure TForm1.Button1.Click(Sender: TObject);
var
P: PChar;
begin
P := 'Test';
...
end;
procedure TForm1.Button2.Click(Sender: TObject);
...
CPU Window показывает следующее:
1. Код метод Button1Click начинается с адреса 0044EB57
2. В переменную P заносится указатель 0044EB9C
3. Инструкция ret для Button1Click находится по адресу 0044EB98
4. Код метода Button2Click начинается с адреса 0044EBA4
5. Между Button1Click и Button2Click вижу "ничей" кусок кода, который содержит "Test"
Ещё комментарии нужны, или и так всё понятно?
P.S. Откомпилировал по вашему совету проект с включённой опцией Map file Detailed... Не нашёл там информации о размещении строки "Test" - может, подскажете, как искать? Зато нашёл в нём строку "Detailed map of segments" - так что использование слова "сегмент" тоже вполне оправдано...
Сергей М. (2007-09-20 09:39:00) [3]
> 1. Любая строковая константа размещается во время компиляции
> в сегменте кода
Не в сегменте, а в секции, и не кода, а инициализированных данных.
Убедись сам, изучив соответствующий *.map-файл.
Однокамушкин (2007-09-20 09:24:00) [4]Здесь ситуация следующая:
1. Любая строковая константа размещается во время компиляции в сегменте кода... Когда Pchar-у присваивается константа, соответствующая переменная становится указателем на соответсвующую часть сегмента кода... Понятно, что пока не выгружена длл, этот участок памяти никуда не денется, поэтому указатель останется актуальным
2. Когда вы присваиваете PChar-у указатель на вычисленную строку, эта строка размещается в динамической памяти, а указатель на неё в неявной переменной, которая финализируется после выхода из соответствующей функции, поэтому после выхода из функции PChar указывает на память, с которой неизвестно что случилось: то ли она возвращена системе (тогда av), то ли использована для других нужд (тогда будет мусор), то ли пока там ещё осталась строка... Точное поведение зависит от многих факторов, предсказать в каждом конретном случае сложно...
3. Проблемы со string-ом возникают потому, что у программы и у длл разные менеджеры памяти, и один менеджер не может освобождать память, которую выделил другой... Поэтому нужен ShareMem, который заставляет программу и длл использовать единый менеджер памяти, реализующийся borlandmm.dll... Или в новых версиях дельфей можно использовать SimpleShareMem - он позволяет программе и длл использовать единый менеджер памяти без подключения дополнительных библиотек... Кстати, совет использовать New или StrNew для передачи PChar-ов не слушайте, потому что придётся в программе вызывать Dispose или StrDispose и снова нарываться на те же грабли, когшда один менеджер памяти пытается освободить то, что выделено другим...
4. WideString является оболочкой над системным типом BSTR, который используется в OLE, и для этого типа в системе существует свой менеджер памяти, доступный через функции SysAllicString, SysFreeString и т.п. Поэтому при работе с WideString-ом дельфийский менеджер памяти не задействуется, и никаких проблем с передачей WideString-ов между программой и длл не возникает...
stanislav (2007-09-20 08:45:00) [5]Sharemem, нужно носить с приложением т.к. это dll Борланд. Да и dll с приложениями разработанными на других языках работать не будет.
Вот незнаю по поводу widestring, будет ли работать dll допустим c приложениями разработанными с помощью продуктом Microsoft.
Инс (2007-09-19 21:45:00) [6]
> [23] slavannnov (19.09.07 21:39)
Не нужно, только процессорное время на перекодировку в юникод и обратно потеряете.
slavannnov (2007-09-19 21:39:00) [7]Ну т.е. в случае если я буду использовать WideString мне не нужно беспокоиться о ShareMem?
Инс (2007-09-19 21:33:00) [8]
> Что такого получается в WideString?
Просто работа с ним идет через системный менеджер памяти, который само собой общий для библиотеки и приложения.
Loginov Dmitry (2007-09-19 21:31:00) [9]> использую обычный string - получаю неприятности.
ShareMem не забыл подключить там где надо?
slavannnov (2007-09-19 17:41:00) [10]Хорошо, спасибо. Но второй мой пример, все ок, казалось бы, но почему работает только с WideString, использую обычный string - получаю неприятности.
?
Сергей М. (2007-09-19 17:32:00) [11]
> ожидаемых тобой
ожидаем омутобой (времени жизни)
Сергей М. (2007-09-19 17:31:00) [12]
> OutData.Str := PChar('My string num = ' + IntToStr(10));
> При обращении к результату Str -- AV. Почему не работает?
По той же самой причине - несоответствию времени жизни возвращаемых тобой данных ожидаемых тобой. Тот адрес, что ты возвратил, уже не актуален на момент обращения по нему в ф-ции Showmessage()
slavannnov (2007-09-19 17:25:00) [13]Привожу код ещё раз, первый не работает. Что забыл и что не так не понимаю.
При обращении к результату Str -- AV. Почему не работает?
PMyType = ^TMyType;
TMyType = record
Pst: LongWord;
Str: PChar;
Bool: Boolean;
Int: Integer;
end;
DLL:
procedure GetResult(InData: TOtherRecord; var OutData: TMyType);
begin
...
OutData.Str := PChar('My string num = ' + IntToStr(10));
...
end;
EXE:
var
MyData: TMyData;
begin
// пытался тут выделать память под TMyData.str (PChar) -- но дохлый номер.
GetResult(OtherData, MyData);
with MyData do
begin
ShowMessage(StrPas(Str));
Этот же код спокойно работает. Почему? Что такого получается в WideString?
PMyType = ^TMyType;
TMyType = record
Pst: LongWord;
Str: WideString;
Bool: Boolean;
Int: Integer;
end;
DLL:
procedure GetResult(InData: TOtherRecord; OutData: PMyType);
begin
...
OutData^.Str := 'My string num = ' + IntToStr(10);
...
end;
EXE:
var
MyData: TMyData;
begin
GetResult(OtherData, @MyData);
with MyData do
begin
ShowMessage(Str);
Не знаю, что и предпологать, попытаюсь избавиться от PChar.
DVM (2007-09-19 17:20:00) [14]Кстати, а нужно ли именно PCHAR. Строка имеет какую то максимальную длину. Редко ведь когда границы бывают неизвестны. Может ее заменить на array [...] of char?
evvcom (2007-09-19 17:13:00) [15]1 пример тоже можно переписать "без указателей":
interface
procedure GetResult(Data: ...; varReturnData: TMyType);
implementation
...
var
RD: TMyType;
begin
GetResult(.., RD);
Но под PChar, все равно руками надо выделить память. Причем правила хорошего тона гласят, что освобождать память нужно там же, где и выделяешь.
Сергей М. (2007-09-19 16:05:00) [16]
> нужно ли под PChar внутри этой записи выделять память?
Если она к этому моменту не выделена тем или иным образом, то конечно же нужно.
slavannnov (2007-09-19 15:52:00) [17]Попробовал я выделять память и передавать так:
var
RD: PMyType;
begin
New(RD);
GetResult(.., RD);
with RD^ do
...
Но нужно ли под PChar внутри этой записи выделять память?
DLL:
procedure GetResult(Data: ...; ReturnData: PMyType);
Но ничего не получилось, AV.
---
В результате, работает только один вариант: как выше подсказали, заменил PChar в записе, на WideString.
Передавал в случае с WideString, так:
var
RD: TMyType;
begin
GetResult(.., @RD);
with RD do
...
И это работает. Но мне на самом деле одной работоспособности мало. Я хочу понять почему так получается?
Сергей М. (2007-09-19 15:15:00) [18]
> slavannnov (19.09.07 14:41) [4]
Фантазии с выделением/освобождением памяти, используемой для возврата рез-та, ограничены только одним - менеджеры памяти в вызывающем и вызываемом кодах должны быть одни и те же.
evvcom (2007-09-19 15:15:00) [19]
> Попробовал все способы исправить - не помогло.
Ну значит не все. Способ есть и не один. Показывай, что пробуешь, а то экстрасенсы в другой ветке тусуются.
Сергей М. (2007-09-19 15:12:00) [20]
> как лучше делать: для результата New или передавать по ссылке
> вход. параметром?
Да как угодно, но уж точно не так как ты пытался это делать, т.е. с пом. локальной переменной.