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

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

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

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

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

Ответ
 
Опции темы Поиск в этой теме
Старый 02.04.2010, 04:47   #1
sprofxx
Пользователь
 
Регистрация: 02.04.2010
Сообщений: 11
По умолчанию Передача строк из dll

Народ, как правильно передавать строки из длл в основную программу?

Изрыл весь интернет, так и не пойму до конца.

Вот что сам наваял после безумно долгих мучений:

Код:
//Код dll

...

procedure DllProc(str1: pointer); stdcall;
begin
  StrPCopy(PChar(str1^),'Текст из dll');
end;

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

...

procedure TForm1.Button1Click(Sender: TObject);
var Lib: Cardinal;
    DllProc: procedure(str1: pointer); stdcall;
    str:PChar;
begin
  Lib := LoadLibrary('MyDll.dll');
  if Lib <> 0 then
  try
    DllProc := GetProcAddress(Lib, 'DllProc');
    GetMem(str,25);
    DllProc(@str);
    Label1.Caption:=str;
    FreeMem(str);
  finally
    FreeLibrary(Lib);
  end;
end;

...
Здесь правильно используется память?

Возьмем например API функцию GetWindowText, одним из параметров в ней является переменная типа PChar (т.е. ссылка на Char'ы). Тогда каким образом функция вызванная из dll и получив этот указатель, сможет выделить место для этой переменной в области памяти основной программы так, чтобы после выгрузки dll переменная и ее значение еще были живы и вообще, как dll может управлять чужой областью памяти в этом случае? Как вообще работает эта API и подобные ей, в них даже не @PChar передается как у меня, а вообще просто PChar (тела подобных функций не нашел)?

Если можно, пожалуйста, приведите простейший код передачи строк из dll в основную прогу и обратно.

Последний раз редактировалось sprofxx; 02.04.2010 в 04:49.
sprofxx вне форума Ответить с цитированием
Старый 02.04.2010, 04:54   #2
Пепел Феникса
Старожил
 
Аватар для Пепел Феникса
 
Регистрация: 28.01.2009
Сообщений: 21,000
По умолчанию

Цитата:
GetWindowText
вообще то вы должны выделить память для нее, и вашим указателем на то куда запишет функцию нужные данные и есть тот указатель что вы передаете функцию, то есть вы выделяете память, и вы в этой памяти получаете результат
Код:
int GetWindowText(

    HWND hWnd,	// handle of window or control with text
    LPTSTR lpString,	// address of buffer for text
    int nMaxCount 	// maximum number of characters to copy
   );	
 

Parameters

hWnd

Identifies the window or control containing the text. 

lpString

Points to the buffer that will receive the text. 

nMaxCount

Specifies the maximum number of characters to copy to the buffer. If the text exceeds this limit, it is truncated.
так же внутрь функции передается размер вашей области памяти(точнее то число символов, которая наша область может в себя принять)

Код:
function DllProc:PChar;stdcall;
begin
  Result:='Текст из dll';
end;
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
Программа делает то что написал программист, а не то что он хотел.
Функции/утилиты ждут в параметрах то что им надо, а не то что вы хотите.
Пепел Феникса вне форума Ответить с цитированием
Старый 02.04.2010, 05:28   #3
sprofxx
Пользователь
 
Регистрация: 02.04.2010
Сообщений: 11
По умолчанию

Код:
function DllProc:PChar;stdcall;
begin
  Result:='Текст из dll';
end;
Здесь как я понимаю грубейшая ошибка. Мы создаем константу в пределах памяти dll и возвращаем на нее указатель, который присваивается указателю в основной программе, т.е. они начинают указывать на одну и ту же область памяти, которая находится в пределах dll! Указатель, возвращаемый функцией, указывает на область памяти, которая считается свободной — после того как значение строки ('Текст из dll') вышла за пределы области видимости, память, которую занимала эта строка, освободилась. Таким образом, выгрузив dll, освобождается память на которую указывает указатель в основной программе и значение теряется, что приводит к исключениям. Это можно проверить выгрузив dll до вывода значения переменной в лейбл. Или я не понимаю чего то?

Вопрос по GetWindowText остается открытым, интересно как именно функция заполняет переданный ей буфер.
sprofxx вне форума Ответить с цитированием
Старый 02.04.2010, 05:35   #4
Пепел Феникса
Старожил
 
Аватар для Пепел Феникса
 
Регистрация: 28.01.2009
Сообщений: 21,000
По умолчанию

Цитата:
Здесь как я понимаю грубейшая ошибка.
нет, это символьная константа находится в памяти только для чтения и грузится вместе с DLL ошибка может возникнуть только если DLL выгрузить из памяти.
в таком случае выделяем память в процедуре и возвращаем на нее указатель(предварительно заполнив)
но тогда в основной программе нужно освобождать её.
Цитата:
Таким образом, выгрузив dll, освобождается память на которую указывает указатель в основной программе и значение теряется, что приводит к исключениям.
тут вы правы.
но такой подход, не очень хорош, но я обьяснил выше
Цитата:
Указатель, возвращаемый функцией, указывает на область памяти, которая считается свободной — после того как значение строки ('Текст из dll') вышла за пределы области видимости, память, которую занимала эта строка, освободилась.
константа не копируется, ей не выделяется новая область памяти.
мы возвращаем PChar а не array of Char(и то статический)...ибо массив статический уже будет распределен динамически при входе в функцию.(в стеке, как локальная паременная)
Цитата:
Вопрос по GetWindowText остается открытым, интересно как именно функция заполняет переданный ей буфер.
блин да просто.
вы передаете вторым параметром указатель на область памяти(размер передается третим параметром)
и она по этому указателю пишет нужные данные, не превышая размера этой области.
но она не выделяем памяти.
или что вам не понятно?
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
Программа делает то что написал программист, а не то что он хотел.
Функции/утилиты ждут в параметрах то что им надо, а не то что вы хотите.

Последний раз редактировалось Пепел Феникса; 02.04.2010 в 05:39.
Пепел Феникса вне форума Ответить с цитированием
Старый 02.04.2010, 06:22   #5
FaTaL
Участник клуба
 
Аватар для FaTaL
 
Регистрация: 09.11.2007
Сообщений: 1,761
По умолчанию

А не легче тогда в DLL использовать не процедуру, а функцию, которая и возвратит в основную прогу строку?!
FaTaL вне форума Ответить с цитированием
Старый 02.04.2010, 06:45   #6
sprofxx
Пользователь
 
Регистрация: 02.04.2010
Сообщений: 11
По умолчанию

Хм... тогда если мы хотим независимости от dll, то реализовывается моим способом из первого поста (вы не ответили о правильности)?...

Вообще хотелось бы увидеть простейший пример, подобие использования GetWindowText.

код основной программы:

Код:
procedure TForm1.Button1Click(Sender: TObject);
var Lib: Cardinal;
    DllProc: function(str1:PChar):integer; stdcall;
    str:PChar;
begin
  Lib := LoadLibrary('MyDll.dll');
  if Lib <> 0 then
  try
    DllProc := GetProcAddress(Lib, 'DllProc');
    strNew(str);
    if DllProc(str)=1 then Label1.Caption:=str;
  finally
    FreeLibrary(Lib);
  end;
end;
код длл:

Код:
var s:PChar;

function DllProc(str1:PChar):integer;stdcall;
begin
   s:=PChar('Текст из dll');
   str1:=s;
   Result:=1;
end;
По идее должно тоже работать, ведь s тоже будет жить пока жива длл? Но оно все равно не работает... как правильно это, допишите плиз.....
sprofxx вне форума Ответить с цитированием
Старый 02.04.2010, 06:56   #7
sprofxx
Пользователь
 
Регистрация: 02.04.2010
Сообщений: 11
По умолчанию

Цитата:
Сообщение от FaTaL Посмотреть сообщение
А не легче тогда в DLL использовать не процедуру, а функцию, которая и возвратит в основную прогу строку?!
Я хочу чтобы значения переменных в основной программе не зависили от подключенных или отключенных dll.


При этом всем как используется GetWindowText... часто разработчики даже не выделяют память для переменной PChar, а просто объявляют ее, в моих экспериментах это вызывало исключения, поэтому я подумал что может сама функция как то умеет это делать. И еще не ясно как заполнять указатель переданный в функцию, каким оператором? str^:='бла бла' ведь не то....

Последний раз редактировалось sprofxx; 02.04.2010 в 07:09.
sprofxx вне форума Ответить с цитированием
Старый 02.04.2010, 07:00   #8
MaxNik2009
Форумчанин
 
Аватар для MaxNik2009
 
Регистрация: 17.09.2009
Сообщений: 294
По умолчанию

Попробуй вот так:
Код:
procedure TForm1.Button1Click(Sender: TObject);
var Lib: Cardinal;
    DllProc: function(str1:PChar):PChar; stdcall;
    str:string;
begin
  Lib := LoadLibrary('MyDll.dll');
  if Lib <> 0 then
  try
    @DllProc := GetProcAddress(Lib, 'DllProc');
    if @DllProc<> nil then 
    begin
      
      Label1.Caption:=StrPas(DllProc('Запрос');
    end;
  finally
    FreeLibrary(Lib);
  end;
end;


///// dll

function DllProc(str1:PChar):PChar;stdcall;
var
  s: string;
begin
   if StrPas(str1) = 'Запрос' then
   s:='Ответ';
   Result:=PChar(s);
end;
принимаю благодарности в письменном виде( весы слева)...
MaxNik2009 вне форума Ответить с цитированием
Старый 02.04.2010, 07:08   #9
Пепел Феникса
Старожил
 
Аватар для Пепел Феникса
 
Регистрация: 28.01.2009
Сообщений: 21,000
По умолчанию

Цитата:
По идее должно тоже работать, ведь s тоже будет жить пока жива длл? Но оно все равно не работает... как правильно это, допишите плиз.....
и не будет, либо добавьте var к параметру и создайте новую строку(strnew вроде. или GetMem + CopyMemory(или чтото иное для копирования, на ваш выбор))
но вообще это на самом деле тоже самое что я написал, только через Ж...
Цитата:
Хм... тогда если мы хотим независимости от dll, то реализовывается моим способом из первого поста (вы не ответили о правильности)?...
верно, практически.
вот вам пример функции подобной GetWindowText
Модуль программы
Код:
function GetConstLen:integer;stdcall;external 'My.Dll';
procedure GetConst(p:PChar);stdcall;external 'My.dll';
//я использую статическую линковку.
procedure TForm1.Button1Click(Sender: TObject);
var p:PChar;
begin
 GetMem(p,(GetConstLen+1)*SizeOf(Char));
{для совместимости со всеми версиями Делфи, ибо символ Юникода это 2 байта а не один.(+1для терминального нуля)}
 p[GetConstLen]:=#0;//конец строки на всякий случай ставим в 0(на инициализацию не надеюсь)
 //, также индексация с нуля.
 GetConst(p);//функция скопирует свою строку в наш буфер.
 Label1.Caption:=p;
 FreeMem(p);//освободили память.
end;
Код ДЛЛ.
Код:
const
 str='Hello World!';

function GetConstLen:integer;stdcall;
begin
 Result:=Length(str);
end;

procedure GetConst(p:PChar);stdcall;
begin
 StrCopy(P,str);
end;
и еще один вариант.(я б сделал наверно так(по крайнем мере при межпоточном обмене, я использовал чтото подобное)).
код проги.
Код:
function GetConst:PChar;stdcall;external 'My.dll'

procedure TForm1.Button1Click(Sender: TObject);
var p:PChar;
begin
 p:=GetConst;
 Label1.Caption:=p;
 StrDispose(p);//освободили память которую выделила нам ДЛЛ
end;
код ДЛЛ
Код:
function GetConst:PChar;stdcall;external 'My.dll'
begin
 Result:=StrNew('Hello World!');
end;
Цитата:
Я хочу чтобы значения переменных в основной программе не зависили от подключенных или отключенных dll.
выгружать ДЛЛ в середине работу программы на мой взгляд не очень хороший тон.
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
Программа делает то что написал программист, а не то что он хотел.
Функции/утилиты ждут в параметрах то что им надо, а не то что вы хотите.

Последний раз редактировалось Пепел Феникса; 02.04.2010 в 07:24. Причина: дополнил и исправил ширину
Пепел Феникса вне форума Ответить с цитированием
Старый 02.04.2010, 07:41   #10
sprofxx
Пользователь
 
Регистрация: 02.04.2010
Сообщений: 11
По умолчанию

благодарю, теперь все понятно!!!
sprofxx вне форума Ответить с цитированием
Ответ


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



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Передача указателя из Си++ в dll на delphi VladimirFF Общие вопросы C/C++ 0 22.02.2010 19:56
Передача StringGrid в DLL Adamrus Общие вопросы Delphi 7 30.11.2009 21:46
передача фрейма из dll BESS Компоненты Delphi 1 20.04.2009 18:54
Передача массива строк в функцию. Crasty Общие вопросы C/C++ 2 14.04.2009 13:29
Передача сообщения из программы в свою Dll SergeySK Общие вопросы Delphi 3 01.11.2007 14:42