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

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

вызов DLL [D7, WinXP]


Страницы:   1 2

Цукор 5 ©   (2005-09-11 13:08:00) 

Есть библиотека :

library Project1;
uses
 SysUtils,
 Classes;

type MyRetVal=record
 Line1:PChar;
 Line2:PChar;
 Dept :Byte;
 Group:Byte;
end;

type  TVal=array of MyRetVal;

function ReadSales(Num1,Num2:Word; var Value:TVal):Boolean;stdcall;
 var I:Integer;
begin
 SetLength(Value,5);
 for I:=0 to Length(Value)-1 do
 begin
   Value[I].Line1:=PChar('Test1');
   Value[I].Line2:=PChar('Test2');
   Value[I].Dept :=$01;
   Value[I].Group:=$0a;
 end;
 Result:=True;
end;

Exports
 ReadSales;
{$R *.res}
begin
end.


Вызываю так :

procedure TForm1.Button1Click(Sender: TObject);
var hDLL:HWND;
    Val:TVal;
    I:Integer;
    ReadSales:function(Num1,Num2:Word;var Value:TVal):boolean;stdcall;
begin
hDLL := LoadLibrary ('Project1');
if hDLL<>0 then
begin
  @ReadSales:=GetProcAddress(hDLL,'ReadSales');
  if Assigned(@ReadSales) then
  ReadSales(1,5,Val);
  for I:=0 to Length(Val)-1 do
  begin
    Memo1.Lines.Add('L'+IntToStr(I)+'_'+Val[I].Line1);
    Memo1.Lines.Add('L'+IntToStr(I)+'_'+Val[I].Line2);
  end;
  // до это момента все прекрасно !
  FreeLibrary (hDLL);// а вот тут AV
end
else MessageDlg('Error!!!',mtError,[MbOk],0);
end;


Не могу понять в чем причина. Я уже и SetLength(Val,0) и Val=nil делал...не то :(
P.S. Да простят меня модераторы... я не знаю как назвать тему вопроса.


Цукор 5   (2005-09-13 00:37:00) [21]

> функция GetItem(idx: integer) передает i-й элемент Хосту. За один вызов - один элемент.

Все дело в том,что работая с устройством мне нужно его инициализировать и т.д. Мне проще и быстреесчитать блок.
При Вашем варианте я потеряю ОЧЕНЬ много времени на инициализацию и закрытие устройства.


Цукор 5   (2005-09-13 00:31:00) [22]

2 Defunct ©   (13.09.05 00:24) [17]
Спасибо конечно,но я сделал свой выбор.
http://www.delphimaster.ru/cgi-bin/forum.pl?id=1126539754&n=0

А Ваш вариант мне кажется некорректным.Зачем локальные массивы, Когда и так все чудесно работает?
З.Ы.Возможно я ошибаюсь ;)


Defunct   (2005-09-13 00:24:00) [23]

Цукор 5   (12.09.05 09:29) [14]
> Но ведь бытует мнение

И правильно бытует.
см. Наиль ©   (12.09.05 09:45) [15]
DLL считывает данные с устройства и хранит их в своем локальном массиве. функция GetItem(idx: integer) передает i-й элемент Хосту. За один вызов - один элемент.


simpson   (2005-09-12 11:37:00) [24]

> Где именно? Просмотрел толпу unit-ов...не нашел :(

Для таких целей обычно смотрят MSDN. :)
Пример работы с ф-ей GetDefaultPrinter:


function GetDefaultPrinter(pszBuffer: PChar; var cchBuffer: DWORD): BOOL;
 stdcall; external 'Winspool.drv' name 'GetDefaultPrinterA';

procedure TForm1.FormCreate(Sender: TObject);
var
 pszBuffer: PChar;
 cchBuffer: DWORD;
begin
 cchBuffer := 0;
 GetDefaultPrinter(nil, cchBuffer);
 if GetLastError = ERROR_INSUFFICIENT_BUFFER then
   begin
     pszBuffer := HeapAlloc(GetProcessHeap,
       HEAP_GENERATE_EXCEPTIONS or HEAP_ZERO_MEMORY, cchBuffer);
     try
       GetDefaultPrinter(pszBuffer, cchBuffer);
       Caption := String(pszBuffer);
     finally
       HeapFree(GetProcessHeap, 0, pszBuffer);
     end;
   end
 else
   RaiseLastOSError;
end;


Т. е. ф-я должна вернуть буфер данных. Вызывающей стороне его размер неизвестен. Для того, чтобы его определить, ф-я вызывается первый с первым параметром, равным nil.

Во втором параметре после первого вызова возвращается требуемый размер буфера данных. Затем вызывающая сторона выделяет необходимый объем памяти и после этого ф-я успешно возвращает данные.

Освобождает память также вызывющая сторона.

Такая методика работы с ф-ями, возвращающими некий, заранее неизвестный буфер данных, типична для WinAPI.


Наиль   (2005-09-12 09:45:00) [25]

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


Цукор 5   (2005-09-12 09:29:00) [26]

2 Defunct ©   (12.09.05 03:04) [13]
Насколько я понимаю,Вы предлагаете создать в DLL глобальную переменную типа  PMyRetVal. Но ведь бытует мнение,что в DLL глобальные переменные создавать не стоит!!!


Defunct   (2005-09-12 03:04:00) [27]

> Да,так все работает. Но в том то и дело мне нужно одной ф-цией все реализовать.Это связано с тем,что я работаю с неким устройством...а открывать\закрывать порты лишний раз ой как не хочеться.

Причем здесь устройство? DLL может считывать с устройства наперед все, что может потребоваться. Например, при вызове GetCount пусть DLL и прочитает из устройства все значения себе в массив. А Хост приложение потом их прочитает из DLL с помощью GetItem. (будет некое подобие синхро выборки).


Цукор 5   (2005-09-12 02:28:00) [28]

2 Наиль ©   (11.09.05 13:54) [11]
Да,так все работает. Но в том то и дело мне нужно одной ф-цией все реализовать.Это связано с тем,что я работаю с неким устройством...а открывать\закрывать порты лишний раз ой как не хочеться.

2 simpson ©   (11.09.05 13:44) [8]
>Обратите внимание на то, как это реализовано в WinAPI.
Где именно? Просмотрел толпу unit-ов...не нашел :(

2 begin...end ©   (11.09.05 13:48) [10]
по первому варианту
>function ReadSales(Num1, Num2: Word; Value: PMyRetVal; Count: Integer): Boolean; stdcall;
насколько я понимаю имелось:
function ReadSales(Num1, Num2: Word; Value: PMyRetVal; var Count: Integer): Boolean; stdcall;

пытался я дальше разбираться..вот что получил:
- при попытке выполнить ReadSales(1,2,Val,Count) получаю ошибку.Если же делать GetMem(Val,1024) и Freemem(Val) ошибки нет. НО!!! Ведь мне не известен реальный размер Val.
- как обратиться к некоторой ячейке Val.Line1 в основной программе???

З.Ы.Извините за (возможно) глупые вопросы. Никак не могу разобраться :( Может похожий пример имеется???


Наиль   (2005-09-11 13:54:00) [29]

В Dll извесно количество элементов массива.
В программе получаем его через какой-нибудь function GetCount:integer
Узнав количество мы можем поштучно в цикле забрать весь массив из Dll
через function GetItem(Index:integer):MyRetVal;

Что-то в этом роде.


begin...end   (2005-09-11 13:48:00) [30]

> Цукор 5   (11.09.05 13:35) [5]
> Хорошо,а как тогда быть???

Например, передавать указатель на первый элемент массива и количество его элементов.

type
 PMyRetVal = ^MyRetVal;

function ReadSales(Num1, Num2: Word; Value: PMyRetVal; Count: Integer): Boolean; stdcall;
var
 I: Integer;
begin
 for I := 1 to Count do
 begin
   Value^.Dept := $01;
   ...;
   Inc(Value) <-- Переход к следующему элементу
 end
 ...
end

Можно объявить ещё такой тип:

type
 PMyRetValArray = ^array [0..0] of MyRetVal;

и тогда в функции можно будет легко организовать доступ к любому "элементу массива":

PMyRetValArray(Value)[индекс_элемента].Dept := $01;

только нужно отключить Range Checking.


Цукор 5   (2005-09-11 13:45:00) [31]

2 Наиль ©   (11.09.05 13:41) [6]
Не понял. Пример заголовка ф-ции приведите.


simpson   (2005-09-11 13:44:00) [32]

Объявить указатель:
PMyRetVal = ^TMyRetVal;

и передавать в функцию его:

function ReadSales(Num1,Num2:Word; Value: PMyRetVal):Boolean;stdcall;


только тогда следите за тем, кто выделяет память под него, а кто - освобождает. Обратите внимание на то, как это реализовано в WinAPI.


Цукор 5   (2005-09-11 13:43:00) [33]

2  begin...end
Кстати,поставил ShareMem(из любопытсва) первым естественно. AV не возникает,но возникает ошибка при закрытии программы(Invalid pointer operation).Чудеса :(


Наиль   (2005-09-11 13:41:00) [34]

Поштучно.
Сначала запрашиваешь Count.
А потом по индексу считываешь элементы.


Цукор 5   (2005-09-11 13:35:00) [35]

2 begin...end ©   (11.09.05 13:30) [3]
Понял. Спасибо!!!
Хорошо,а как тогда быть??? По условию задачи мне нельзя пользоваться  ShareMem т.к. библиотека будет отдана не Delphi программистам. И в тоже время мне необходимо передать "массив" определенной структуры.


Наиль   (2005-09-11 13:33:00) [36]

Сравни значение hDLL сразу после загрузки и перед самым освобождением библиотеки. Скорее всего значения различаются. Если это так, то ищи место изменения.


begin...end   (2005-09-11 13:30:00) [37]

> Цукор 5   (11.09.05 13:26) [2]
> Мне она не нужна.

Она Вам нужна. On Windows, if a DLL exports routines that pass long strings or dynamic arraysas parameters or function results (whether directly or nested in records or objects), then the DLL and its client applications (or DLLs) must all use the ShareMem unit.


Цукор 5   (2005-09-11 13:26:00) [38]

2 Anton_K ©   (11.09.05 13:16) [1]
Мне она не нужна.Т.к. String-и я не передаю. Более того,мне нужен языконезависимый подход. А с ShareMem смысла в библиотеке я не вижу,т.к. использовать ее смогут только Delphi программисты.


Anton_K   (2005-09-11 13:16:00) [39]

Про ShareMem не забыли?


Страницы:   1 2

Наверх

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