![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
передача массива из DLL [D7]
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 Вообще мне не нравится твой подход.