Форум: "Основная";
Текущий архив: 2005.10.02;
Скачать: [xml.tar.bz2];
Внизвызов DLL Найти похожие ветки
← →
Цукор 5 (2005-09-11 13:08) [0]Есть библиотека :
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. Да простят меня модераторы... я не знаю как назвать тему вопроса.
← →
Anton_K © (2005-09-11 13:16) [1]Про ShareMem не забыли?
← →
Цукор 5 (2005-09-11 13:26) [2]2 Anton_K © (11.09.05 13:16) [1]
Мне она не нужна.Т.к. String-и я не передаю. Более того,мне нужен языконезависимый подход. А с ShareMem смысла в библиотеке я не вижу,т.к. использовать ее смогут только Delphi программисты.
← →
begin...end © (2005-09-11 13:30) [3]> Цукор 5 (11.09.05 13:26) [2]
> Мне она не нужна.
Она Вам нужна. On Windows, if a DLL exports routines that pass long strings or dynamic arrays as 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.
← →
Наиль © (2005-09-11 13:33) [4]Сравни значение hDLL сразу после загрузки и перед самым освобождением библиотеки. Скорее всего значения различаются. Если это так, то ищи место изменения.
← →
Цукор 5 (2005-09-11 13:35) [5]2 begin...end © (11.09.05 13:30) [3]
Понял. Спасибо!!!
Хорошо,а как тогда быть??? По условию задачи мне нельзя пользоваться ShareMem т.к. библиотека будет отдана не Delphi программистам. И в тоже время мне необходимо передать "массив" определенной структуры.
← →
Наиль © (2005-09-11 13:41) [6]Поштучно.
Сначала запрашиваешь Count.
А потом по индексу считываешь элементы.
← →
Цукор 5 (2005-09-11 13:43) [7]2 begin...end
Кстати,поставил ShareMem(из любопытсва) первым естественно. AV не возникает,но возникает ошибка при закрытии программы(Invalid pointer operation).Чудеса :(
← →
simpson © (2005-09-11 13:44) [8]Объявить указатель:
PMyRetVal = ^TMyRetVal;
и передавать в функцию его:
function ReadSales(Num1,Num2:Word; Value: PMyRetVal):Boolean;stdcall;
только тогда следите за тем, кто выделяет память под него, а кто - освобождает. Обратите внимание на то, как это реализовано в WinAPI.
← →
Цукор 5 (2005-09-11 13:45) [9]2 Наиль © (11.09.05 13:41) [6]
Не понял. Пример заголовка ф-ции приведите.
← →
begin...end © (2005-09-11 13:48) [10]> Цукор 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.
← →
Наиль © (2005-09-11 13:54) [11]В Dll извесно количество элементов массива.
В программе получаем его через какой-нибудьfunction GetCount:integer
Узнав количество мы можем поштучно в цикле забрать весь массив из Dll
черезfunction GetItem(Index:integer):MyRetVal;
Что-то в этом роде.
← →
Цукор 5 (2005-09-12 02:28) [12]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 в основной программе???
З.Ы.Извините за (возможно) глупые вопросы. Никак не могу разобраться :( Может похожий пример имеется???
← →
Defunct © (2005-09-12 03:04) [13]> Да,так все работает. Но в том то и дело мне нужно одной ф-цией все реализовать.Это связано с тем,что я работаю с неким устройством...а открывать\закрывать порты лишний раз ой как не хочеться.
Причем здесь устройство? DLL может считывать с устройства наперед все, что может потребоваться. Например, при вызове GetCount пусть DLL и прочитает из устройства все значения себе в массив. А Хост приложение потом их прочитает из DLL с помощью GetItem. (будет некое подобие синхро выборки).
← →
Цукор 5 (2005-09-12 09:29) [14]2 Defunct © (12.09.05 03:04) [13]
Насколько я понимаю,Вы предлагаете создать в DLL глобальную переменную типа PMyRetVal. Но ведь бытует мнение,что в DLL глобальные переменные создавать не стоит!!!
← →
Наиль © (2005-09-12 09:45) [15]Нет не глобальную.
Функция GetItem будет передавать значение равное значению локальной переменной хранящейся в DLL.
← →
simpson © (2005-09-12 11:37) [16]> Где именно? Просмотрел толпу 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.
← →
Defunct © (2005-09-13 00:24) [17]Цукор 5 (12.09.05 09:29) [14]
> Но ведь бытует мнение
И правильно бытует.
см. Наиль © (12.09.05 09:45) [15]
DLL считывает данные с устройства и хранит их в своем локальном массиве. функция GetItem(idx: integer) передает i-й элемент Хосту. За один вызов - один элемент.
← →
Цукор 5 (2005-09-13 00:31) [18]2 Defunct © (13.09.05 00:24) [17]
Спасибо конечно,но я сделал свой выбор.
http://delphimaster.net/view/1-1126539754/
А Ваш вариант мне кажется некорректным.Зачем локальные массивы, Когда и так все чудесно работает?
З.Ы.Возможно я ошибаюсь ;)
← →
Цукор 5 (2005-09-13 00:37) [19]> функция GetItem(idx: integer) передает i-й элемент Хосту. За один вызов - один элемент.
Все дело в том,что работая с устройством мне нужно его инициализировать и т.д. Мне проще и быстрее считать блок.
При Вашем варианте я потеряю ОЧЕНЬ много времени на инициализацию и закрытие устройства.
← →
Defunct © (2005-09-13 00:41) [20]> Цукор 5 (13.09.05 00:37) [19]
Интересно, в каком месте?
← →
Цукор 5 (2005-09-13 00:43) [21]2 Defunct © (12.09.05 03:04) [13]
Хотя стоп...
>Например, при вызове GetCount пусть DLL и прочитает из устройства все значения себе в массив.
Этот вариант мне приемлим.Но я никак не могу понять,как это реализовать без глобальной переменной в DLL. Ведь одна ф-ция должна создать "массив",а вторая выбирать из него данные.
← →
Defunct © (2005-09-13 00:49) [22]> Цукор 5
экспортируем сл. функции:
OpenDevice;
ReadDevice;
CloseDevice;
и также для работы с массивом (буфером внутри dll)
GetItem;
SetItem;
Count;
далее используем их так
OpenDevice(..);
ReadDevice(..);
for i:=0 to Count-1 do
begin
zzz := GetItem;
end;
CloseDevice(..);
OpenDevice/ReadDevice/CloseDevice не обязательно экспортировать, их можно просто вызвать внутри функции Count.
← →
Defunct © (2005-09-13 00:52) [23]> Но я никак не могу понять,как это реализовать без глобальной переменной в DLL.
Очень просто, например так:implementation
var
DataToExport : Array of SomeType; <--- этот массив локален для модуля в котором он описан.
← →
Цукор 5 (2005-09-13 00:57) [24]2 Defunct © (13.09.05 00:49) [22]
Вероятно я не понял Вас,а Вы меня. С телом DLL я как-нибудь сам разберусь. Мне непонятно следующее.
Итак :
>ReadDevice(..); // считает весь блок данных с устройства в массив
> GetItem // передаст некую часть массива в хост-приложение
Две ф-ции.Одна создает массив,вторая выберает и передает. Где и как это все хранить? Вот я о чем.
← →
Цукор 5 (2005-09-13 01:01) [25]2 Defunct © (13.09.05 00:52) [23]
Хм... а откуда взялось implementation в DLL?
var
DataToExport : Array of SomeType;
> этот массив локален для модуля в котором он описан
Как по мне...так я считаю,что это глобальная переменная.Хорошо,а что ж тогда глобальная переменная???
← →
Германн © (2005-09-13 01:06) [26]2 Цукор 5
А что ты так сфокусировался на "глобальности" переменных в DLL?
← →
Defunct © (2005-09-13 01:07) [27]Цукор 5 (13.09.05 01:01) [25]
> Хм... а откуда взялось implementation в DLL?
Что значит откуда?
Кто мешает в теле DLL подключить модуль?library MyLibrary;
uses MyUnit;
exports
OpenDevice,
ReadDevice,
CloseDevice,
GetItem,
Count;
begin
end.
> так я считаю,что это глобальная переменная.
неправильно считаете.unit MyUnit;
interface
procedure OpenDevice;
procedure ReadDevice;
procedure CloseDevice;
function Count:integer;
function GetItem(idx:integer):TMyType;
implementation
var
DataToExport : Array of TMyType;
...
end.
DataToExport - не более чем локальная переменная модуля MyUnit.
← →
Цукор 5 (2005-09-13 01:11) [28]2 Defunct
Спасибо. Буду знать.Теперь вроде понятно.
← →
Defunct © (2005-09-13 01:17) [29]Цукор 5 (13.09.05 01:11) [28]
вот и гут ;>
ps: я не настаиваю именно на таком решении, возможно есть решение и лучше. Тем не менее очень рад, что вы поняли о чем речь ;>
← →
Цукор 5 (2005-09-13 01:29) [30]2 Defunct © (13.09.05 01:17) [29]
Кстати,а еще вопросик. Вопрос навеян тем,что библиотека будет передана не Delphi-программистам.
Вот я обьявил такую структуру :
type
TMyRetVal=record
Line1:String[25];
Line2:String[25];
Dept : Byte;
end;
А во всех ли языках есть String[25]? Или что-нибудь другое выдумывать??? Как Вы считаете?
← →
Цукор 5 (2005-09-13 01:40) [31]Понятно,что для решения [27] структура ДОЛЖНА быть такой:
type
TMyRetVal=record
Line1:PChar;
Line2:PChar;
Dept : Byte;
end;
я вот думаю про http://delphimaster.net/view/1-1126539754/. Там то нельзя указать Pchar ибо тогда я никогда не узнаю размер "массива"
З.Ы. Да простят меня модераторы за 2 вопроса в одной ветке.
← →
Германн © (2005-09-13 01:42) [32]2 Цукор 5 (13.09.05 01:29) [30]
Имхо, String[] - есть Pascal"ева конструкция. И её НЕТ в других языках!
Но array[] of char - найдется везде, имхо.
А PChar - найдется во всех, наиболее известных языках и компиляторах, которые работают в среде Win32. Имхо!
← →
Defunct © (2005-09-13 01:46) [33]обязательно поставьте packed record
> А во всех ли языках есть String[25]?
String[25] это просто массив Char (array[0..25] of char), у которого первый символ содержит количество используемых символов массива. Объявить такой массив можно imho во всех языках.
ps: может быть лучше String[31]? (по 32 байта на строку, как-то более цивильно будет, заодно все поля будут выровняны на границу dword)
← →
Цукор 5 (2005-09-13 01:47) [34]2 Германн © (13.09.05 01:42) [32]
Понял. Меняю все на array[] of char.Надежней будет(наверное:О )
Спасибо!
← →
Цукор 5 (2005-09-13 01:49) [35]Блин. [33]<>[32]. Так где же истина???
← →
Defunct © (2005-09-13 02:00) [36]Цукор 5 (13.09.05 01:49) [35]
Герман имел в виду, помоему то же самое.
String[..] это уже и так array[..] of char.
> Меняю все на array[] of char.
не стоит, в Delphi будет легче работать со String, в других языках объявят за вас.
← →
Defunct © (2005-09-13 02:06) [37]> в Delphi будет легче работать со String
читать так:
в Delphi будет легче работать со String[] чем с array[] of char.
← →
Defunct © (2005-09-13 02:23) [38]> Цукор 5
В [33] я отвечал на [30].
чтобы развеять сомнения относительно достоверности, привожу аналог структуры
TMyRetVal=record
Line1:String[31];
Line2:String[31];
Dept : Byte;
end;
на Cи:typedef SmallString<31> MyStr;
struct TMyRetVal
{
MyStr Line1;
MyStr Line2;
char Dept;
};
← →
Slym © (2005-09-13 09:36) [39]
type
PEntry=^TEntry;
TEntry=packed record
Line1:PChar;
Line2:PChar;
Dept :Byte;
Group:Byte;
end;
PEntries=^TEntries;
TEntries=array[0..MaxListSize - 1] of PEntry;
PEntryArray=^TEntryArray;
TEntryArray=packed record
Size:integer;
Entries:PEntries;
end;
function OpenDevice:PEntryArray;stdcall;
begin
New(result);
result.Size:=0;
result.Entries:=nil;
end;
function ClearEntryArray(Dev:PEntryArray):LongBool;stdcall;
var i:integer;
Entry:PEntry;
begin
for i:=0 to Dev.Size-1 do
begin
Entry:=Dev.Entries[i];
if Entry.Line1<>nil then
FreeMem(Entry.Line1);
if Entry.Line2<>nil then
FreeMem(Entry.Line2);
Dispose(Entry);
end;
FreeMem(Dev.Entries);
Dev.Size:=0;
end;
function CloseDevice(Dev:PEntryArray):LongBool;stdcall;
begin
ClearEntryArray(Dev);
Dispose(Dev);
end;
const Line="Test,Test";
function ReadDevice(Dev:PEntryArray):LongBool;stdcall;
var
i:integer;
Entry:PEntry;
begin
if Dev.Size>0 then
ClearEntryArray(Dev);
Dev.Size:=10;
GetMem(Dev.Entries,SizeOf(PEntry)*Dev.Size);
for i:=0 to Dev.Size-1 do
begin
New(Dev.Entries[i]);
Entry:=Dev.Entries[i];
GetMem(Entry.Line1,Length(Line)+1);
StrLCopy(Entry.Line1,PChar(Line),Length(Line));
GetMem(Entry.Line2,Length(Line)+1);
StrLCopy(Entry.Line2,PChar(Line),Length(Line));
Entry.Dept:=i;
Entry.Group:=Dev.Size-i;
end;
end;
procedure Usage;
var EntryArray:PEntryArray;
i:integer;
begin
EntryArray:=OpenDevice;
ReadDevice(EntryArray);
for i:=0 to EntryArray.Size-1 do
begin
MessageBox(0,EntryArray.Entries[i].Line1,EntryArray.Entries[i].Line2,0);
end;
CloseDevice(EntryArray);
end;
Страницы: 1 вся ветка
Форум: "Основная";
Текущий архив: 2005.10.02;
Скачать: [xml.tar.bz2];
Память: 0.57 MB
Время: 0.006 c