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

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> WideString в DLL 
:(
    Опции темы
aktuba
Дата 26.2.2007, 08:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Смышленный
***


Профиль
Группа: Завсегдатай
Сообщений: 1915
Регистрация: 24.4.2006
Где: Планета Земля

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



Не так давно я описывал одну проблему, возникшую в одном из проектов. Сегодня вот нашел немного инфы на эту тему, точнее на тему использования WideString в DLL. Думаю, это многим пригодится, т.к. ни в одной книге, ни на других форумах подобного не встречал.
Итак, вкратце: при использовании WideString НЕ НАДО никакого выделения памяти под строки, НЕ НАДО подключать никакие доп. модули и т.д. В общем не надо НИЧЕГО!!! Не верите? Попробуйте:

Код DLL:
Код

library tstdll;

function GetWideString: WideString; stdcall; export;
begin
  Result := 'Какой-то текст.';
end;

exports
  GetWideString;

begin
end.


Код программы:
Код

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  function GetWideString: WideString; stdcall; external 'tstdll.dll';

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage(GetWideString);
end;

end.


Все. Компилируем и проверяем. Странно... Работает  smile.

Теперь небольшое объяснение. Все прекрасно знают, что в EXE и в DLL менеджеры памяти разные и именно из-за этого и возникают проблемы при передаче строк из программы в dll и обратно. Но, при использовании WideString, используется не обычный менеджер памяти, а специальный - системный. Специальный менеджер сделан для совместимости с COM. И этот менеджер памяти используется один на всех, поэтому WideString можно безопасно передавать и без использования ShareMem, FastShareMem и т.д. - просто в этом случае вы будете терять время на перекодировку в Unicode и обратно. Теперь по поводу выделения/освобождения памяти под WideString. При работе с WideString используются функции SysAllocString/SysReallocString/SysFreeString, которые можно вызвать явно или, как делает Delphi, неявно. Плюс к этому, в отличии от AnsiString, где для определения момента освобождения памяти, занимаемой строкой, используется механизм подсчета ссылок, в WideString всегда используется явно. Т.е. при присваивании wstr1 := wstr2, для AnsiString это будет просто копирование указателя, а для WideString создание новой строки и копирование содержимого wstr2 в созданную строку.

Думаю, это многим поможет...


--------------------
user posted image
PM MAIL WWW Skype   Вверх
Alexeis
Дата 26.2.2007, 12:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

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



Цитата(aktuba @  26.2.2007,  08:32 Найти цитируемый пост)
в WideString всегда используется явно. Т.е. при присваивании wstr1 := wstr2, для AnsiString это будет просто копирование указателя, а для WideString создание новой строки и копирование содержимого wstr2 в созданную строку.


Мне кажется ты ошибаешся. Посмотрим реализацию следующего кода.
Код

procedure TForm1.FormCreate(Sender: TObject);
var
  s1, s2 : WideString;
  i, j   : Integer;
begin
  S1 := 'Some Text';
  S2 := S1;
end;


Смотрим, что же у нас выходит в ассемблере
Код

Unit1.pas.30: S1 := 'Some Text';
0044C896 8D45F8           lea eax,[ebp-$08]
0044C899 BAE0C84400       mov edx,$0044c8e0
0044C89E E8AD7CFBFF       call @WStrLAsg
Unit1.pas.31: S2 := S1;
0044C8A3 8D45F4           lea eax,[ebp-$0c]
0044C8A6 8B55F8           mov edx,[ebp-$08]
0044C8A9 E8A27CFBFF       call @WStrLAsg


Вызов WStrLAsg пустой переход к _LStrLAsg

Смотрим теперь на паскалевский вариант _LStrLAsg

Код

procedure _LStrLAsg(var dest; const source);
{$IFDEF PUREPASCAL}
var
  P: Pointer;
begin
  P := Pointer(source);
  _LStrAddRef(P);
  P := Pointer(dest);
  Pointer(dest) := Pointer(source);
  _LStrClr(P);
end;    


Что же мы видим? Увеличение счетчика ссылок на строку и копирование указателя на строку. Нет ни какого копирования содержимого строки. Просто ссылка и все. Причем используется стандарный менеджер памяти.

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


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
Quadr0
Дата 26.2.2007, 12:29 (ссылка)    |    (голосов: 0) Загрузка ... Загрузка ... Быстрая цитата Цитата


Unregistered











...

Это сообщение отредактировал(а) Quadr0 - 15.7.2011, 13:46
  Вверх
aktuba
Дата 26.2.2007, 12:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Смышленный
***


Профиль
Группа: Завсегдатай
Сообщений: 1915
Регистрация: 24.4.2006
Где: Планета Земля

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



Цитата

Мне кажется ты ошибаешся. Посмотрим реализацию следующего кода.


Да нет, мне кажется ты немного заблуждаешься. Или Delphi умнее, чем мы думаем и использует то, что необходимо в данной ситуации =).
Для примера:
Код

procedure TForm1.FormCreate(Sender: TObject);
var
  str1, str2: WideString;
begin
  str1 := 'Это текст';
  str2 := str1;
end;


В отличии от твоего кода - я использую русский текст. Вот что дает дебагер:
user posted image

Здесь видно, что не только адреса ссылок на переменные разные, но и адрес нахождения самих данных различается. Но, пойдем дальше...

Цитата

Вызов WStrLAsg пустой переход к _LStrLAsg


Снова не верно. Смотрим скрин:
user posted image

Снова видим, что никакого перехода к _LStrLAsg нет в природе. Все на уровне работы с WideString, ну и соответственно, используется SysRealloc, как я и написал в начале.

Цитата

Причем используется стандарный менеджер памяти.


Снова повторю - неверно. Для WideString используется системный менеджер памяти, что наглядно видно из скринов. Правда есть подозрение, что в предыдущих версиях Delphi используется стандартный... Я же говорю про BDS2006/TurboDelphi...

P.S.: пока писал - Quadr0 откоментил, но думаю скрины и пояснения многим помогут...

Это сообщение отредактировал(а) aktuba - 26.2.2007, 12:32


--------------------
user posted image
PM MAIL WWW Skype   Вверх
Alexeis
Дата 26.2.2007, 12:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

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



Цитата(aktuba @  26.2.2007,  12:31 Найти цитируемый пост)
Снова видим, что никакого перехода к _LStrLAsg нет в природе. Все на уровне работы с WideString, ну и соответственно, используется SysRealloc, как я и написал в начале.

  Все тестил на Delphi7. Там используется именно _LStrLAsg. В любом случае использование недокументированых возможностей сопрежено с большими проблемами совместимости версии. Раз в документации этого нет, значит Borland не обязяна сохранять эту систему в будущем. Непойму чем так усложняет жизнь простое указание модуля FastShareMem?


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
aktuba
Дата 26.2.2007, 13:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Смышленный
***


Профиль
Группа: Завсегдатай
Сообщений: 1915
Регистрация: 24.4.2006
Где: Планета Земля

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



Цитата

Все тестил на Delphi7. Там используется именно _LStrLAsg. В любом случае использование недокументированых возможностей сопрежено с большими проблемами совместимости версии. Раз в документации этого нет, значит Borland не обязяна сохранять эту систему в будущем. Непойму чем так усложняет жизнь простое указание модуля FastShareMem?


Сейчас посмотрел на Delphi 7, там используется тоже самое, что и на BDS, т.е. к _LStrLAsg перехода нет. К сожалению не могу выложить скрин, т.к. D7 установлен на ноуте без инета... 

Цитата

Раз в документации этого нет, значит Borland не обязяна сохранять эту систему в будущем.


Вот выдержка из хелпа D7:
Цитата

... On Win32, WideString is compatible with the COM BSTR type.

Note
Under Win32, WideString values are not reference-counter. ...



--------------------
user posted image
PM MAIL WWW Skype   Вверх
Alexeis
Дата 26.2.2007, 13:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

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



Упс не догядел. Это был фрагмент линуксового кода  smile 
Да уж и в семерке
Код

procedure TForm1.btn1Click(Sender: TObject);
var
 // str1, str2: AnsiString;
  str1, str2: WideString;
  p1, p2, p3  : Pointer;
begin
  str1 := 'Это текст';
  str2 := str1;
  p1 :=  PWideChar(str1);
  p2 :=  PWideChar(str2);


p1 и p2 указывают на разные области памяти, тогда как

Код

procedure TForm1.btn1Click(Sender: TObject);
var
  str1, str2: AnsiString;
  p1, p2, p3  : Pointer;
begin
  str1 := 'Это текст';
  str2 := str1;
  p1 :=  PChar(str1);
  p2 :=  PChar(str2);


Указывают на одну.

Добавлено @ 13:31 
Да пожалуй основываясь на 
Цитата

On Win32, if a DLL exports routines that pass long strings or dynamic arrays as parameters or function results (whether directly or nested in records or objects), then the DLL and its client applications (or DLLs) must all use the ShareMem unit. The same is true if one application or DLL allocates memory with New or GetMem which is deallocated by a call to Dispose or FreeMem in another module. ShareMem should always be the first unit listed in any program or library uses clause where it occurs.


Можно смело не использовать ни каких FastshareMem.


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
aktuba
Дата 26.2.2007, 13:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Смышленный
***


Профиль
Группа: Завсегдатай
Сообщений: 1915
Регистрация: 24.4.2006
Где: Планета Земля

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



Alexeis, не хочется снова указывать ошибку, а приходится =((( - во втором коде замени AnsiString на WideString и все встанет на места. p1 и p2 будут указывать на разные области памяти....


--------------------
user posted image
PM MAIL WWW Skype   Вверх
Alexeis
Дата 26.2.2007, 13:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

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



Тогда еще небольшое дополнение к
Цитата(aktuba @  26.2.2007,  08:32 Найти цитируемый пост)
Но, при использовании WideString, используется не обычный менеджер памяти, а специальный - системный. Специальный менеджер сделан для совместимости с COM. И этот менеджер памяти используется один на всех, поэтому WideString можно безопасно передавать и без использования ShareMem, FastShareMem и т.д.


Функция SysReAllocStringLen вообще виндовая и не принадлежит самому приложению. Память выделяется
и уничтожается внешним, как для DLL так и для программы, менеджером памяти. 
Код

Const
  oleaut = 'oleaut32.dll';

function SysReAllocStringLen(var S: WideString; P: PWideChar;
  Len: Integer): LongBool; stdcall;
  external oleaut name 'SysReAllocStringLen';





--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
Alexeis
Дата 26.2.2007, 14:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

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



В связи с этим хочется развеять еще один скользкий вопрос. Считается, что использование Pchar вместо AnsiString позволяет также избавиться от проблем с менеджером памяти. Однако часто можно увидеть ситуацию, когда память для Pchar выделяется при помощи GetMem или New. Такой ход является очень опасным и допустим, только в случае если память выделенная GetMem освобождается в том же модуле, что и была выделена иначе нужно использовать HeapAlloc, GlobalAlloc.


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
Alexeyt
Дата 26.2.2007, 16:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 332
Регистрация: 15.9.2006
Где: около Москвы

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



aktuba
Alexeis
Спасибо за информацию.
Очень полезно (у меня есть кое-какие DLL).
PM WWW   Вверх
MetalFan
Дата 23.3.2007, 08:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


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


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

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



Цитата(aktuba @  26.2.2007,  08:32 Найти цитируемый пост)
ни в одной книге, ни на других форумах подобного не встречал.

http://delphiworld.narod.ru/base/delphi_com.html
мотаем до самого конца....
ы?


--------------------
There are always someone smarter than you...
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: Общие вопросы"
SnowyMetalFan
bemsPoseidon
Rrader

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

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

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

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


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

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


 




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


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

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