Форум по Delphi программированию

Delphi Sources



Вернуться   Форум по Delphi программированию > Все о Delphi > [ "Начинающим" ]
Ник
Пароль
Регистрация <<         Правила форума         >> FAQ Пользователи Календарь Поиск Сообщения за сегодня Все разделы прочитаны

Ответ
 
Опции темы Поиск в этой теме Опции просмотра
  #1  
Старый 02.10.2012, 12:18
Сергей77 Сергей77 вне форума
Прохожий
 
Регистрация: 14.05.2012
Сообщения: 31
Репутация: 10
По умолчанию Передача массива из DLL в основную программу

В DLL у меня происходят вычисления и в основную программу передается массив записей.
Но, почему то после успешной отработки DLL я не могу работать с возвращенными данными

Собственно код...

Код:
Общий класс основной программы и DLL
...
type
  TKoef = Record
    name: PChar;
    abbr: PChar;
    val_bgn: real;
    val_end: real;
    delta: real;
    criterion: boolean;
    cr_min: real;
    cr_max: real;
    dev_bgn: boolean;
    dev_end: boolean;
    dev_bgn_val: real;
    dev_end_val: real;
    info: PChar;
    valid: boolean;
  end;

Основная программа
...
type
  TArr = array of TKoef;
  TPArr = ^TArr;
...
procedure TfMain.mModuleCalcClick(Sender: TObject);
var
  Arr: TArr;
  PArr: TPArr;
begin
    try
      try
        hDLL := LoadLibrary(PAnsiChar(FN));
        @GetModuleData := nil;
        if hDLL >= 32 then begin
          @GetModuleData := GetProcAddress(hDLL,'GetModuleData');
          if (@GetModuleData<>nil) then begin
            SetLength(Arr,5);
            PArr := Addr(Arr);
            if GetModuleData(ORG, PArr) then begin
---> вот здесь я пытаюсь работать с Arr, но вылетают ошибки
            end;
          end;
        end;
      finally
        @GetModuleData := nil;
        FreeLibrary(hDLL);
      end;
    except
    end;
end;
...

DLL
...
const
  KOEF_CLOUNT = 5;

type
  TArr = array[1..KOEF_CLOUNT] of TKoef;
  TPArr = ^TArr;
...
function GetModuleData(dllORG: TOrganization; aPArr:TPArr): Boolean; stdcall;
begin
  with aPArr^[1] do begin
  ...
  end;
  ...
  with aPArr^[5] do begin
  ...
  end;
end;
...
Ответить с цитированием
  #2  
Старый 02.10.2012, 12:59
icWasya icWasya вне форума
Местный
 
Регистрация: 09.11.2010
Сообщения: 499
Репутация: 10
По умолчанию

Ну так в основной программе
Код:
array of TKoeff
, а в DLL -
Код:
array [1..of KOEF_CLOUNT] TKoeff

В основной программе надо
Код:
type  
  TArr = array of TKoef;
  TPArr = ^TKoef; // <<==-- Вот
  TGetModuleData = function (dllORG: TOrganization; aPArr:TPArr): Boolean; stdcall; 

  ...
  PArr := @(Arr[0]); //<<==--  Вот         
  if GetModuleData(ORG, PArr) 
 ...
Ответить с цитированием
Эти 2 пользователя(ей) сказали Спасибо icWasya за это полезное сообщение:
OTVET (22.01.2013), Сергей77 (02.10.2012)
  #3  
Старый 02.10.2012, 13:11
Аватар для YVitaliy
YVitaliy YVitaliy вне форума
Местный
 
Регистрация: 14.12.2011
Сообщения: 481
Версия Delphi: Borland Delphi7
Репутация: 17
По умолчанию

И еще в процедурах, которые возвращают данные в параметрах, обьявляй эти параметры после var.
Код:
function GetModuleData(dllORG: TOrganization;var aPArr:TPArr): Boolean; stdcall;
Ответить с цитированием
Эти 2 пользователя(ей) сказали Спасибо YVitaliy за это полезное сообщение:
OTVET (22.01.2013), Сергей77 (02.10.2012)
  #4  
Старый 02.10.2012, 15:18
Сергей77 Сергей77 вне форума
Прохожий
 
Регистрация: 14.05.2012
Сообщения: 31
Репутация: 10
По умолчанию

Всем спасибо.
Сделал так: передача массива не через указатель на массив, а собственно передача динамического массива. И в программе и в ДЛЛ стал использовать единый тип (динамический массив).
Все работает.
Ответить с цитированием
  #5  
Старый 02.10.2012, 16:53
icWasya icWasya вне форума
Местный
 
Регистрация: 09.11.2010
Сообщения: 499
Репутация: 10
По умолчанию

Цитата:
Сообщение от YVitaliy
И еще в процедурах, которые возвращают данные в параметрах, обьявляй эти параметры после var.
Код:
function GetModuleData(dllORG: TOrganization;var aPArr:TPArr): Boolean; stdcall;

А вот это вредный совет.
Ответить с цитированием
  #6  
Старый 02.10.2012, 16:55
icWasya icWasya вне форума
Местный
 
Регистрация: 09.11.2010
Сообщения: 499
Репутация: 10
По умолчанию

Цитата:
Сообщение от Сергей77
Всем спасибо.
Сделал так: передача массива не через указатель на массив, а собственно передача динамического массива. И в программе и в ДЛЛ стал использовать единый тип (динамический массив).
Все работает.

А для такого способа не забывать внимательно читать комментарий, который Delphi пишет в заготовке проекта DLL.
Ответить с цитированием
  #7  
Старый 02.10.2012, 17:20
Сергей77 Сергей77 вне форума
Прохожий
 
Регистрация: 14.05.2012
Сообщения: 31
Репутация: 10
По умолчанию

Не совсем понял о чем речь...
Ответить с цитированием
  #8  
Старый 02.10.2012, 17:55
Аватар для YVitaliy
YVitaliy YVitaliy вне форума
Местный
 
Регистрация: 14.12.2011
Сообщения: 481
Версия Delphi: Borland Delphi7
Репутация: 17
По умолчанию

Цитата:
Сообщение от icWasya
А вот это вредный совет.
И чем же вредный? Если есть иной (менее вредный) выход (а он, судя по комментарию, есть) - выкладывай, возьму на заметку . Посоветовал то, что сам использую. Конечно есть out, но это не выход. (в смысле, из положения )

Насчет поста о комментарии в DLL - я тоже ничего не понял. Там, кажись, написано о строках, а не динамических массивах. ИМХО все из-за совместимости dll для разных языков программирования. И ТС использует PChar, String нигде нет.
Ответить с цитированием
  #9  
Старый 03.10.2012, 10:00
icWasya icWasya вне форума
Местный
 
Регистрация: 09.11.2010
Сообщения: 499
Репутация: 10
По умолчанию

В алгоритме, который привёл автор топика, в DLL передаётся указатель на уже выделенную память, в DLL она только заполняется. Поэтому лишний var тут не нужен. Var может понадобится в случае, когда передаётся структура и в DLL она будет изменена. Посмотрите модуль windows.pas. Во многих случаях, когда в API функции требуется указатель на переменную или структуру, в паскале используется синтаксис в var. А когда нужен массив - используются указатели.

По поводу комментария - механизм работы со строками и динамическими массивами один и тот же.
Ответить с цитированием
  #10  
Старый 03.10.2012, 13:04
Сергей77 Сергей77 вне форума
Прохожий
 
Регистрация: 14.05.2012
Сообщения: 31
Репутация: 10
По умолчанию

Спасибо за информацию.

Сейчас вычисления в DLL проходят нормально и результаты я использую в основной программе, но при закрытии программы вылетает ошибка чтения памяти. Может надо какой ресурс очистить?

Код ниже

Это общий класс DLL и основной программы
Код:
unit uExchange;

interface

uses SysUtils;

const
  F1_COUNT = 67;
  F2_COUNT = 21;
  F5_COUNT = 10;

type

  TKoef = Record
    name: String;
    abbr: String;
    val_bgn: Currency;
    val_end: Currency;
    delta: Currency;
    criterion: boolean;
    cr_type: integer;
    cr_min: Currency;
    cr_max: Currency;
    dev_bgn: boolean;
    dev_end: boolean;
    dev_bgn_val: Currency;
    dev_end_val: Currency;
    info: String;
    valid: boolean;
  end;

  TMKoef = array of TKoef;

  TModule = Record
    id: integer;
    koef_count: integer;
    mKoef: TMKoef;
    isCalc: Boolean;
  end;

  TOrganization = record
    FileName: String;
    Name: String;
    Address: String;
    Ruk: String;
    RukPhone: String;
    Buh: String;
    BuhPhone: String;
    INN: String;
    KPP: String;
    EMail: String;
    BankName: String;
    BankBIK: String;
    BankKS: String;
    OKPO: String;
    OKVED: String;
    Information: String;
    f1: array[1..4,1..F1_COUNT] of String;
    f2: array[1..4,1..F2_COUNT] of String;
    f5: array[1..4,1..F5_COUNT] of String;
    modules: array of TModule;
  end;

implementation
...
end.

Это DLL
Код:
library module1;

uses uExchange, SysUtils;

function GetModuleData(var O: TOrganization; module_number:integer): Boolean; stdcall;
begin
  with O.modules[module_number].mKoef[0] do begin
  ...
  end;
  with O.modules[module_number].mKoef[1] do begin
  ...
  end;
  Result := True;
end;

exports GetModuleData;

begin

end.

И код программы
Код:
  public
    ORG: TOrganization;
...
  GetModuleData: function(var O: TOrganization; module_number: integer): Boolean; stdcall;
...
procedure TfMain.mModuleCalcClick(Sender: TObject);
begin
    try
      try
        hDLL := LoadLibrary(PAnsiChar(FN));
        @GetModuleData := nil;
        if hDLL >= 32 then begin
          @GetModuleData := GetProcAddress(hDLL,'GetModuleData');
          if (@GetModuleData<>nil) then begin
            GetModuleData(ORG, 0) then begin
          end;
        end;
      finally
        @GetModuleData := nil;
        FreeLibrary(hDLL);
      end;
    except
    end;
end;
...
Ответить с цитированием
Этот пользователь сказал Спасибо Сергей77 за это полезное сообщение:
OTVET (22.01.2013)
  #11  
Старый 03.10.2012, 15:07
Аватар для YVitaliy
YVitaliy YVitaliy вне форума
Местный
 
Регистрация: 14.12.2011
Сообщения: 481
Версия Delphi: Borland Delphi7
Репутация: 17
По умолчанию

to icWasya
Цитата:
механизм работы со строками и динамическими массивами один и тот же

Ну, если так, то сделай следующее: Обьяви динамический массив размером 5 и строку длиной 5 символов, затем попробуй обратится к массив[5] элементу и строка[5] элементу, а также массив[0] элементу и строка[0] элементу - скажи что получилось. И как dll должна реагировать на 0 элемент строки. Как я уже писал, в разных языках программирования (отчасти Delphi и C) строки отличаются друг от друга, и в С строка оканчивается на NULL(для признака последнего символа), в Delphi же в 0 индексе содержится информация о длне строки. Так что эта заметка к динамическим массивам отношения не имеет.

to Сергей77 А если запускать не из среды программирования, вылеты тоже наблюдаются?
Ответить с цитированием
  #12  
Старый 03.10.2012, 15:24
Сергей77 Сергей77 вне форума
Прохожий
 
Регистрация: 14.05.2012
Сообщения: 31
Репутация: 10
По умолчанию

Да. При завершении работы выскакивает, что программа некорректные действия и та же ошибка памяти.
Ответить с цитированием
  #13  
Старый 03.10.2012, 15:37
icWasya icWasya вне форума
Местный
 
Регистрация: 09.11.2010
Сообщения: 499
Репутация: 10
По умолчанию

>но при закрытии программы вылетает ошибка чтения памяти.
Ну вот как раз на такой случай и нужно прочитать комментарий в заготовке DLL. На этот раз у Вас в передаваемой структуре находятся строки, при работе с которыми возможно перевыделение памяти.
Ответить с цитированием
Этот пользователь сказал Спасибо icWasya за это полезное сообщение:
Сергей77 (03.10.2012)
  #14  
Старый 03.10.2012, 15:39
Аватар для YVitaliy
YVitaliy YVitaliy вне форума
Местный
 
Регистрация: 14.12.2011
Сообщения: 481
Версия Delphi: Borland Delphi7
Репутация: 17
По умолчанию

Ну да, как уже раньше писалось - у тебя в классах используются String, а их для отправки в DLL использовать не рекомендуется. Поменяй String на PChar или ShortString, или, как пишет в комментарии при создании DLL из визарда, пропиши ShareMem первим в разделе Uses, но тогда твоя прога будет зависимая от BORLANDMM.DLL. Выбирай меньшее из двух зол
Ответить с цитированием
Эти 2 пользователя(ей) сказали Спасибо YVitaliy за это полезное сообщение:
OTVET (22.01.2013), Сергей77 (03.10.2012)
  #15  
Старый 03.10.2012, 16:22
Сергей77 Сергей77 вне форума
Прохожий
 
Регистрация: 14.05.2012
Сообщения: 31
Репутация: 10
По умолчанию

Спасибы.
Ваши ответы мне помогли.
Ответить с цитированием
Ответ


Delphi Sources

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

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB-коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход


Часовой пояс GMT +3, время: 21:35.


 

Сайт

Форум

FAQ

RSS лента

Прочее

 

Copyright © Форум "Delphi Sources" by BrokenByte Software, 2004-2023

ВКонтакте   Facebook   Twitter