Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 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.008 c
1-1126069684
wertolet
2005-09-07 09:08
2005.10.02
компонент не удаляется в дизайнтайм


1-1126244691
yk
2005-09-09 09:44
2005.10.02
принудительная прокрутка пор Win 98


1-1126279571
Jolik
2005-09-09 19:26
2005.10.02
Как отловить закрытие файла?


1-1126003954
simpson
2005-09-06 14:52
2005.10.02
Динамический массив - константа


1-1126515348
реактор
2005-09-12 12:55
2005.10.02
не могу удалить COMBOBOX !!!???





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский