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