Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

передача массива из DLL [D7]


Страницы:   1 2

GanibalLector ©   (2005-12-04 17:33:00) 

Имеем такой вот массив:

type
  TBasic=packed record
    ID    : Word;        
    Group : Byte;      
    Code  : DWord;      
    Price : DWord;          
    Valid : DWord;      
    Line : String[24];  
end;
PBasicPLU = ^TBasicPLU;
TBasicPLU = array of TBasic;


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

Чтобы было более понятно,я покажу "это" на примере (только не с массивом,а с Integer):

procedure Test(var HMem:Integer);
var
    Value:Integer;
    P:Pointer;
begin
 Value:=3107;
 HMem:=GlobalAlloc(GMEM_DDESHARE or GMEM_MOVEABLE or GMEM_ZEROINIT,SizeOf(Value)+1);
 if HMem>0 then
 begin
   P:=GlobalLock(HMem);
   if Assigned(P) then System.Move(Value,P^,SizeOf(Value));
 end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var H:Integer;
    P:Pointer;
begin
Test(H);
if H>0 then
begin
  P:=GlobalLock(H);
  Caption:=IntToStr(PInteger(P)^);
  GlobalUnlock(H);
  GlobalFree(H);
end;
end;


Заранее большое спасибо!!!


Cash   (2005-12-06 21:17:00) [21]

whatsup! Вмешаться можно? А... всеравно влез!

Предворю:
 В код на последующих постах не вглядывался,
 поэтому, если что, не ругать!

Замечу:
To GanibalLector ©:
 С построением записи TBasic полностью согласен,
 это хорошо, что она не ранжирована.
 А вот с этим:


   PBasicPLU = ^TBasicPLU;
   TBasicPLU = array of TBasic;


 категорически не согласен!
 Словами это объясняется так:
 "создадим указатель на масив {TBasicPLU = array of TBasic;},
  и еще создадим указатель на этот указатель,
  и будем его использовать.", по моему это странно!
 Работать то будет, но каряво!
 Код TBasicPLU = array of TBasic,создаст
 открытый масив, при этом переменная этого типа будет указателем.
 С этим типом хорошо работать с помощью функций:
   SetLength(variable,len1,len2,.......,lenN); - она создает N-мерный масив. (выделяет память в ОП)
   Finalize(variable); - она эту память вычищает и ставит variable в nil.

 Удобно? Мне - да. Особенно при том, что я частнько юзаю
 плагины для своих прог, а там такое иногда надо. Передать указатель в
 длл-овую функцию удобнее чем саму переменную, главное запретить
 изменение этого указателя (если обратного не нужно).

 Память под масив лучше выделять в основной проге, указатель
 сформируется в ее адресном пространстве, создай функцию,
 которая возвращает число необходимых элементов.
 Вызвай SetLength(parray,Extern_GetCount) для создания масива.
 Передавай указатель созданного масива во внешнюю функцию,
 которая его заполнит.
 Для просмотра длины масива можно воспользоваться функцией
 High(parray), которая вернет максимальный индекс элемента
 масива, делай High(parray)+1 для получения размера масива.
 Данные можно получить легко, как будто обращаешся к статическому
 масиву, легко и удобно parray[0] .. parray[High(parray)].
 И еще, уж чего чего, а со строками надо быть поосторожнее.
 В Pascal можно задать максимум 255 символов для сток,
 а в Delphi? Тоже 255? или 552? А мож 65536? и так тоже можно!
 Сторка в Делфи тоже является указателем! Поэтому, присваивая
 новое строковое значение прога создает для нее адрес в текущей
 секции ОП, то есть в области длл-ки, а прога туда глядеть не может!
 Поэтому, либо используй PChar тип строк, либо юзай юнтит ShareMem
 и кидай borlandmm.dll рядом с exe-шником (ShareMem надо подключить
 и к длл-ке и к DPR файлу. С этим юнитом ваабще все проблемы с
 разделенной памятью отвалятся, она станет нераздельная для длл-ки
 и для проги).


GanibalLector   (2005-12-06 19:00:00) [22]

2 jack128 ©
>Бред. Если скомпилируешь с $R+ то глюки полезут из всех щелей.
Я этого не заметил.Код прекрасно отработал на D5 и D7.

>вообще мне не нравится твой подход
А я и не говорил,что это мой подход! Это подход Тенцера.Взято у него в книге. Также автор предлагает использовать ф-цию CoTaskMemAlloc указанную в [4].

З.Ы. И еще,код в [8] не рабочий.


GuAV   (2005-12-05 16:39:00) [23]

см. [2], [3], [4] +
Кроме уже указанных подходов следует также упомянуть enumeration callback функции. В функцию dll передается функция приложения, которая будет вызванна для каждой записи. Преимущества этого подхода:
+ Нет выделения памяти в одном месте и освобожения в другом. При желании, нет вообще динамического выделения памяти.
+ Есть примеры в WinAPI - EnumXXX
+ Line м.б. PChar - это снимет ограничения на длину.
+ Эта dll м.б. использована из приложения на Delphi, C, и даже VB (при соблюдении stdcall и ограничения стандартными типами).


jack128   (2005-12-05 13:58:00) [24]

GanibalLector ©   (05.12.05 13:28) [10]
  GetMem(Q,SizeOf(TBasic)*SizeArr); // вот в чем дело !!!

Бред. Если скомпилируешь с $R+ то глюки полезут из всех щелей.  Оставь в покое динамические массивы, если не знаешь как они работают.


GanibalLector   (2005-12-05 13:28:00) [25]

2 jack128
Спасибо!!!
А я тем временем сделал так :

function Test(var HMem:Integer):Word;
var Value:TBasicPLU;
    P:Pointer;
    I:Integer;
begin
 Result:=0;
 SetLength(Value,10);
 for I:=0 to Length(Value)-1 do
 begin
   Value[I].Id:=I;
   Value[I].Line:='Test'+IntToStr(I);
 end;
 HMem:=GlobalAlloc(GMEM_DDESHARE or GMEM_MOVEABLE or GMEM_ZEROINIT,
        SizeOf(TBasic)*Length(Value)+1);
 if HMem>0 then
 begin
   P:=GlobalLock(HMem);
   if Assigned(P) then
   begin
     System.Move(Value,P^,SizeOf(TBasic)*Length(Value));
     Result:=Length(Value);
   end;
 end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var H:Integer;
    P:Pointer;
    Q:PBasicPLU;
    SizeArr:Word;
    I:Integer;
begin
 SizeArr:=Test(H);
 if (H>0) and (SizeArr>0) then
 begin
   P:=GlobalLock(H);
    GetMem(Q,SizeOf(TBasic)*SizeArr);// вот в чем дело !!!
   Q^:=PBasicPLU(P)^;
     for i:=0 to SizeArr-1 do
     begin
       Memo1.Lines.Add(Q^[i].Line);
       Memo1.Lines.Add(IntToStr(Q^[i].Id));
     end;
   FreeMem(Q);
   GlobalUnlock(H);
   GlobalFree(H);
 end;
end;


jack128   (2005-12-05 12:33:00) [26]

jack128 ©   (05.12.05 12:32) [8]
for i:=0 to Len - 1do


jack128   (2005-12-05 12:32:00) [27]

GanibalLector ©   (05.12.05 12:21) [6]
TBasicPLU = array of TBasic

GanibalLector ©   (05.12.05 12:21) [6]
  Q:=PBasicPLU(P)^;
  //Caption:=IntToStr(Length(Q)); тут вообще ужас :(


Ты где так научился?? Бред написал - ужас получил, всё закономерно.
procedure Test(var HMem:Integer);
var Value:TBasicPLU;
   P:Pointer;
   I:Integer;
begin
SetLength(Value,10);
for I:=0 to Length(Value)-1 do
begin
  Value[I].Id:=I;
  Value[I].Line:='Test'+IntToStr(I);
end;
HMem:=GlobalAlloc(GMEM_DDESHARE or GMEM_MOVEABLE or GMEM_ZEROINIT,
       SizeOf(TBasic)*Length(Value)+SizeOf(Integer));
if HMem>0 then
begin
  P:=GlobalLock(HMem);
  if Assigned(P) then
  begin
    PInteger(P)^ := Length(Value); // Пишем длину массива
    System.Move(Value,Pointer(PChar(P) + SizeOf(Integer))^,SizeOf(TBasic)*Length(Value));
    ClobalUnlock(P);
  end;
end;
end;

...
type
 TBasicArray = array[0..(MaxInt div SizeOf(TBasic)) - 1] of TBasic;
 PBasicArray = ^TBasicArray;
var
 Arr: PBasicArray;
 Len: Integer;
begin
Q:=nil;
Test(H);
if H>0 then
begin
  P:=GlobalLock(H);
  Len := PInteger(P)^; // получаем длину массива.
  Arr := Pointer(PChar(P) + SizeOf(Integer));
  Caption:=IntToStr(Len));
  for i:=0 to Len do
  begin
    Memo1.Lines.Add(Arr[i].Line);
    Memo1.Lines.Add(IntToStr(Arr[i].Id));
  end;
  GlobalUnlock(H);
  GlobalFree(H);
end;


PS
Вообще мне не нравится твой подход.(c)Я


GanibalLector   (2005-12-05 12:23:00) [28]

2 begin...end ©   (04.12.05 18:27) [2]
>Типичная схема для многих API-функций. Почему бы и здесь не сделать так же?

Кстати,напомните оные. Запамятовал :( Хоть погляжу в генофонде...


GanibalLector   (2005-12-05 12:21:00) [29]

Я продолжу свои мысли...вдруг кому-то будет интересно ;)
Так вот,усложним пример в [0] и будем передавать структуру.

type
  TBasic=packed record
    Id:Byte;
    Line : String[24];  
end;
PBasic = ^TBasic;

procedure Test(var HMem:Integer);
var Value:TBasic;
    P:Pointer;
begin
 Value.Id:=56;
 Value.Line:='Test';
 HMem:=GlobalAlloc(GMEM_DDESHARE or GMEM_MOVEABLE or GMEM_ZEROINIT,SizeOf(TBasic)+1);
 if HMem>0 then
 begin
   P:=GlobalLock(HMem);
   if Assigned(P) then System.Move(Value,P^,SizeOf(TBasic));
 end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var H:Integer;
    P:Pointer;
    Q:TBasic;
begin
Test(H);
if H>0 then
begin
  P:=GlobalLock(H);
  Q:=PBasic(P)^;
    Memo1.Lines.Add(Q.Line);
    Memo1.Lines.Add(IntToStr(Q.Id));
  GlobalUnlock(H);
  GlobalFree(H);
end;
end;


Данный код работает ;)

Ну,а теперь перейдем уже к массиву определенной  структуры.Т.е. к основной и заветной цели [0]. Итак :

type
  TBasic=packed record
    Id:Byte;
    Line : String[24];  
end;
PBasicPLU = ^TBasicPLU;
TBasicPLU = array of TBasic;
procedure Test(var HMem:Integer);
var Value:TBasicPLU;
    P:Pointer;
    I:Integer;
begin
 SetLength(Value,10);
 for I:=0 to Length(Value)-1 do
 begin
   Value[I].Id:=I;
   Value[I].Line:='Test'+IntToStr(I);
 end;
 HMem:=GlobalAlloc(GMEM_DDESHARE or GMEM_MOVEABLE or GMEM_ZEROINIT,
        SizeOf(TBasic)*Length(Value)+1);
 if HMem>0 then
 begin
   P:=GlobalLock(HMem);
   if Assigned(P) then System.Move(Value,P^,SizeOf(TBasic)*Length(Value));
 end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var H:Integer;
    P:Pointer;
    Q:TBasicPLU;
    i:Integer;
begin
 Q:=nil;
 Test(H);
 if H>0 then
 begin
   P:=GlobalLock(H);
   Q:=PBasicPLU(P)^;
   //Caption:=IntToStr(Length(Q)); тут вообще ужас :(
   for i:=0 to 9 do
   begin
     Memo1.Lines.Add(Q[i].Line);
     Memo1.Lines.Add(IntToStr(Q[i].Id));
   end;
   GlobalUnlock(H);
   GlobalFree(H);
 end;
end;


Как Вы понимаете этот код не работает.Даже не вижу причин для этого :(
И еще...если вместо цикла поставить

 Memo1.Lines.Add(IntToStr(Q[2].Id));
 Memo1.Lines.Add(Q[2].Line);

то результат будет правильным,но только на значениях от 2 до 9.
Может память выделяю неверно???


GanibalLector   (2005-12-04 21:56:00) [30]

2 begin...end ©   (04.12.05 18:27) [2]
>Типичная схема для многих API-функций. Почему бы и здесь не сделать так же?
Хорошо...Вот бы на код посмотреть ;)


Набережных С.   (2005-12-04 20:12:00) [31]

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

Или использовать IMalloc или функции-оболочки вокруг его системной реализации CoTaskMemAlloc etc

Или делать COM-сервер и реализовать интерфейс-перечислитель.

Или использовать IMardhal и реализовать объект, передаваемый "по значению".

И все эти способы, как и [2], дают большую или меньшую незаисимость от среды разработки.


Юрий Зотов   (2005-12-04 19:38:00) [32]

А еще проще использовать ShareMem и SetLength. Не создавая самому себе лишних проблем.


begin...end   (2005-12-04 18:27:00) [33]

> GanibalLector ©   (04.12.05 17:33)

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

Функции, находящейся в DLL, передаются 2 параметра -- указатель на заранее выделенную область памяти, в которую нужно поместить данные, и длина этой области (var-параметр). Если указатель равен nil, то функция ничего не помещает в буфер, а во втором параметре возвращает требуемую длину буфера.

EXE вызывает функцию вначале с nil-указателем, получая необходимую длину буфера, выделяет буфер и вызывает функцию снова -- с указателем на готовый буфер.

Типичная схема для многих API-функций. Почему бы и здесь не сделать так же?


jack128   (2005-12-04 18:05:00) [34]

GanibalLector ©   (04.12.05 17:33)
Но вот не получается с кодом :(

Хорошее объяснение. И главное очень подробное.

ps Почему GlobalLock вызываешь два раза, а GlobalUnlock - только один??

pps Вообще мне не нравится твой подход.


Страницы:   1 2

Наверх

Титульная страница Поиск, карта сайта Написать письмо