Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Delphi программирование > Общие вопросы Delphi
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 04.03.2010, 18:54   #1
SkAndrew
Форумчанин
 
Регистрация: 05.04.2008
Сообщений: 244
Восклицание Проблема с вызовом динамической DLL

Добрый вечер!

Delphi 2007. Использую довольно распространенный пример создания динамической DLL:

код dll:

Код:
uses
  SysUtils,
  Classes;

{$R *.res}

{ определяем функцию как stdcall }
function GetSimpleText(LangRus: Boolean): PChar; stdcall;
begin
  {В зависимости от LangRus возвращаем русскую (True) либо английскую (False) фразу}
  if LangRus then
    Result := PChar('Здравствуй, мир!')
  else
    Result := PChar('Hello, world!');
end;

{ директива exports указывает, какие функции будут экспортированы этой DLL }
exports GetSimpleText;

begin
end.
код программы:

Код:
var
  Form1: TForm1;
  GetSimpleText : function(LangRus: Boolean): PChar;
  LibHandle: THandle;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  {"Чистим" адрес функции от "грязи"}
  @GetSimpleText := nil;
  {Пытаемся загрузить библиотеку}
  LibHandle := LoadLibrary('abbr.DLL');
  {Если все OK}
  if LibHandle >= 32 then begin
    {...то пытаемся получить адрес функции в библиотеке}
    @GetSimpleText := GetProcAddress(LibHandle,'GetSimpleText');
    {Если и здесь все OK}
    if @GetSimpleText <> nil then
      {...то вызываем эту функцию и показываем результат}
      ShowMessage(StrPas(GetSimpleText(True)));
  end;
  {И не забываем освободить память и выгрузить DLL}
  FreeLibrary(LibHandle);
end;
Но при клике по кнопке ShowMessage прекрасно показывает надпись, но потом закрывая ShowMessage получаю ошибку компиллирования. Мастера подскажите в чем причина? Спасибо.

мастера, ну может кто-нибудь может подсказать почему при закрытии окна сообщения компилятор выдает ошибку? что не так прописано в вызове или закрытии или объявлении DLL? Всем спасибо!

Может, не будем "апить", а следовать правилам и пользоваться кнопкой "Правка" ?

Последний раз редактировалось mihali4; 05.03.2010 в 00:32.
SkAndrew вне форума Ответить с цитированием
Старый 04.03.2010, 21:54   #2
0nni
Форумчанин
 
Аватар для 0nni
 
Регистрация: 24.07.2008
Сообщений: 279
По умолчанию

Код:
function GetSimpleText(LangRus: Boolean): PChar; stdcall;
Ошибка в том, что ты возвращаешь указатель на на локальную переменную.

Если ты внимательно читал комментарий, который был вставлен сразу после строчки library project1, заботливыми разработчиками дельфи, то знал бы что так делать нельзя.

Цитата:
{ Important note about DLL memory management: ShareMem must be the
first unit in your library's USES clause AND your project's (select
Project-View Source) USES clause if your DLL exports any procedures or
functions that pass strings as parameters or function results. This
applies to all strings passed to and from your DLL--even those that
are nested in records and classes. ShareMem is the interface unit to
the BORLNDMM.DLL shared memory manager, which must be deployed along
with your DLL. To avoid using BORLNDMM.DLL, pass string information
using PChar or ShortString parameters. }
А для решения проблемы, рекомендуют подключить в uses и программы и библиотеки ShareMem (самым первым).

Если не хочешь всюду таскать за собой BORLNDMM.DLL (расплата за ShareMem) рекомендую переделать функцию к такому виду:
Код:
function GetSimpleText(LangRus: Boolean; Buffer : PChar; BufferSize) : integer;
Сказал и загрустил от бесспорности своей правоты.
0nni вне форума Ответить с цитированием
Старый 04.03.2010, 22:14   #3
SkAndrew
Форумчанин
 
Регистрация: 05.04.2008
Сообщений: 244
Восклицание

Спасибо, только это не мой пример, а выложен он в интернете.

А как будет полностью выглядеть функция если ее переделать как Вы предлагаете?

Код:
function GetSimpleText(LangRus: Boolean; Buffer : PChar; BufferSize) : integer;
begin
  ???????????????????????
end;
Спасибо.
SkAndrew вне форума Ответить с цитированием
Старый 04.03.2010, 23:03   #4
0nni
Форумчанин
 
Аватар для 0nni
 
Регистрация: 24.07.2008
Сообщений: 279
По умолчанию

ну, вот функция в dll
Код:
function GetSimpleText(LangRus: Boolean; Buffer : PChar; BufferSize : Integer) : integer;
var
  MyString : string;
  CopyLength : Integer;
begin
   //предварительно формируем строку
   if LangRus then
    MyString :='Здравствуй, мир!'
  else
    MyString := 'Hello, world!';
  //Узнаем ее длинну
  CopyLength := Length(MyString);
  //если длинна больше длинны буфера то уменьшаем ее
  if CopyLength > BufferSize then CopyLength := BufferSize;
  //копируем в буфер то что полученную часть строки
  CopyMemory(Buffer, Pchar(MyString), CopyLength * sizeof(char));
  //Ну и можно вернуть число скопированных строк
  Result := CopyLength;
end;
А вот ее вызов
Код:
var
  //формируешь буфер, будем считать что 256 символов хватит
  buff : array[0..255] of char;
begin
  //заполняем весь буфер нулями
  ZeroMemory(@buff, sizeof(buff));
  //вызываем функцию - передавая ей указатель на буфер и его размер (последний символ под #0 оставим)
  GetSimpleText(true, @buff, 255);
  //выведем результат
  writeln(buff);
  Readln;
end.
Сказал и загрустил от бесспорности своей правоты.
0nni вне форума Ответить с цитированием
Старый 06.03.2010, 01:48   #5
NervniiJ
Похмел
Пользователь
 
Регистрация: 10.01.2010
Сообщений: 16
По умолчанию

В данном случае ошибка всеголишь в том, что не указана директива обратного вызова у прототипа функции GetSimpleText.

SkAndrew, вот здесь
Код:
var
  Form1: TForm1;
  GetSimpleText : function(LangRus: Boolean): PChar; stdcall; // должна быть!

0nni, пиши есче, сила с тобой.

Последний раз редактировалось NervniiJ; 06.03.2010 в 01:53.
NervniiJ вне форума Ответить с цитированием
Старый 06.03.2010, 08:43   #6
0nni
Форумчанин
 
Аватар для 0nni
 
Регистрация: 24.07.2008
Сообщений: 279
По умолчанию

На*соглашение о вызове не обратил внимание, это тоже ошибка. Однако возвращать таким образом строки можно только*при*соглашении cdecl но ни как не при pascal и stdcall;
Сказал и загрустил от бесспорности своей правоты.
0nni вне форума Ответить с цитированием
Старый 10.03.2010, 08:13   #7
NervniiJ
Похмел
Пользователь
 
Регистрация: 10.01.2010
Сообщений: 16
По умолчанию

Цитата:
Однако возвращать таким образом строки можно только*при*соглашении cdecl но ни как не при pascal и stdcall;
Где, ты тут, увидел "строки"?
NervniiJ вне форума Ответить с цитированием
Старый 10.03.2010, 08:45   #8
Пепел Феникса
Старожил
 
Аватар для Пепел Феникса
 
Регистрация: 28.01.2009
Сообщений: 21,000
По умолчанию

Цитата:
На*соглашение о вызове не обратил внимание, это тоже ошибка. Однако возвращать таким образом строки можно только*при*соглашении cdecl но ни как не при pascal и stdcall;
не вижу связи.
Цитата:
Ошибка в том, что ты возвращаешь указатель на на локальную переменную.
нет в этом ошибки.
это указатель на строку символов являющейся константой.
и в стеке она не хранится.

ошибка вылетала изза того что при смещении указателя стека при возврате из процедуры, было произведенно смещение, а Делфи этого не ожидала от ДЛЛ(по fastcall на один параметр стек не используется) и потому нарушался стек, вот вам и вылет.

а про локальную переменную вот пример
Код:
function bla:Pinteger;
var i:integer;
begin
 i:=random(500);
 Result:=@i;
end;
вот это уже указатель на локальную переменную.
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
Программа делает то что написал программист, а не то что он хотел.
Функции/утилиты ждут в параметрах то что им надо, а не то что вы хотите.

Последний раз редактировалось Пепел Феникса; 10.03.2010 в 08:49.
Пепел Феникса вне форума Ответить с цитированием
Старый 10.03.2010, 09:29   #9
uberchel
Участник клуба
 
Аватар для uberchel
 
Регистрация: 19.01.2009
Сообщений: 1,453
По умолчанию

Да уж легче загрузить статически и не париться )
uberchel вне форума Ответить с цитированием
Старый 11.03.2010, 17:07   #10
0nni
Форумчанин
 
Аватар для 0nni
 
Регистрация: 24.07.2008
Сообщений: 279
По умолчанию

Строка, на которую указывает PChar хранится не в стеке, это верно, а в куче, только кучи это разные. Почему же GetWindowText имеет вид не
*char GetWindowText (HWND handle) или программисты MS решили сделать жизнь программистам веселее?
Как вариант, можно вернуть указатель на массив символов, выделенный через LocalAlloc(), и освободить через LocalFree() уже после вызова.
Сказал и загрустил от бесспорности своей правоты.
0nni вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Проблема с вызовом функции B DLL doniyor Мультимедиа в Delphi 2 05.11.2009 16:14
Проблема выделения динамической памяти в С++ oxygen90 Помощь студентам 3 14.09.2009 21:47
Проблема с динамической работой с изображением на экране andreyasu Win Api 3 15.06.2009 21:44
Проблема с вызовом exec() из php на linux Fatum123 PHP 6 13.04.2009 22:36
проблемы с вызовом окна логина. terion БД в Delphi 3 15.11.2006 00:53