[Delphi 2006] Ошибка доступа в память при передаче данных в длл

kill-still

Собственно кросспост с кингдома:
http://www.delphikingdom.com/asp/answer.asp?IDAnswer=60806
День добрый уважаемые жители королевства.
Второй день бьюсь над багом и не могу понять, что происходит.
Есть класс, который получен из длл, в него передаются нек-е значения. код такой:
 
type

TOkofAISearchElementItem = record
OKOF:string;
OkofDescription:string;
TestHint:string;
end;

TOkofAISearchElement = record
assigned: boolean;
ItemsCount:integer;
data: array of TOkofAISearchElementItem;
end;

TOkofAISearchArray = array of TOkofAISearchElement;

{----------------------------------------------------}
Tdictout = record
name: array [1..28] of char; // Слово из словаря
value,dic_value,add_value: integer;
end;

pdictout = ^Tdictout;

Tisearch = record
okof: array [1..10] of char;
value,NumberOfElements:integer;
items: array of Tdictout;
end;

Tisearcharray = array of Tisearch;
isearch = ^Tisearcharray;
{---------------------------------------------------}
Var
ItemsPointer:isearch;
ItemsElements:Tisearcharray;

OkofAISearchArray:TOkofAISearchArray;

{---------------------------------------------------}
//процедура : (объект AIsearcher уже создан и инициализирован)

for I := firstrow to lastrow do
begin
OkofAISearchArray[i].assigned:=true; //говорим, что у нас элемент обсчитан
ItemsElements:=nil;
searchstring:=Form1.StringGrid1.Cells[Form1.StringGrid1.DSColCount-11,i]; //достаём из таблицы значение
AIsearcher.searchString(pchar(searchstring;//передаём его в класс
AIsearcher.filter(strtoint(OkofAISearchForm.Filter.Text; //обрезаем ненужные значения
NumberOfOutputItems:=AIsearcher.getCount;
ItemsPointer:=AIsearcher.getItems;
ItemsElements:=pointer(ItemsPointer);
if NumberOfOutputItems<>0 then
begin
SetLength(OkofAISearchArray[i].data,NumberOfOutputItems+1);
OkofAISearchArray[i].ItemsCount:=NumberOfOutputItems-1;
for j := 0 to NumberOfOutputItems-1 do
begin
findstring:=ItemsElements[j].okof;
delete(findstring,10,11); //удаляем (символ окончания строки в си)
OkofAISearchArray[i].data[j].OKOF:=findstring;
Form1.Classificator.GetDescr(findstring,AprFile.CalcDate,AprFile.CalcDate,AprFile.PPRegion,Koeff,descr,resultat);
//берём описание служебной процедурой
OkofAISearchArray[i].data[j].OkofDescription:=descr;
outputstring:='Hits: '+inttostr(ItemsElements[j].value)+' ОКОФ: '+findstring+' '+descr+' ';(* падает тут*)
{
for k := 0 to ItemsElements[j].NumberOfElements-1 do
begin
name:=(ItemsElements[j].items[k].name);
delete(name,pos(' ',namelength(name;
outputstring:=outputstring+' WORD: '+name+' val: '+inttostr(ItemsElements[j].items[k].value)+
' dicVal: '+inttostr(ItemsElements[j].items[k].dic_value)+' addVal: '+inttostr(ItemsElements[j].items[k].add_value); (*и тут*)
end; }
OkofAISearchArray[i].data[j].TestHint:=outputstring;
findstring:='';
name:='';
descr:='';
outputstring:='';
end;
end
else
begin
SetLength(OkofAISearchArray[i].data,2);
OkofAISearchArray[i].ItemsCount:=1;
OkofAISearchArray[i].data[0].OKOF:=' ';
OkofAISearchArray[i].data[0].OkofDescription:='Поиск не дал результатов';
end;
end;

Спустя какое-то время (если за раз много элементов кинуть (firstrow << lastrow или много раз по чуть-чуть) оа падает с ошибкой доступа в память. Такое впечатление, что при сложении строк она "наезжает" на недоступный кусок памяти.
Такой же пример на си работает без сбоев, т.е. дело не в длл.
Суть кода в том, чтобы из ItemsElements полученных из длл перенестиданные в нужный нам формат в OkofAISearchArray. Утечек памяти нет (проверял FastMM). Что происходит - понять не могу падает на простом сложении строчек.
Сишное описание выходного массива:
 
typedef struct _dictout{
char name[28]; // Слово из словаря
int value; // Номер словаря
int dic_value; // Коэффициент задаваемый для разных словарей
int add_value; // Коэффициент, зависящий от длины слова
} dictout;

typedef struct _intSearch{
char okof[10]; // Код ОКОФ
long value; // Итоговый балл
long wordCount; // Количество "слов"
dictout *items; // Массив структур со словами
}isearch;

SPARTAK3959

AIsearcher.getItems возвращает указатель, полученный от сишной dll'ки?
Какое выравнивание структур стоит в dll'ке и в программе на паскале?

kill-still

AIsearcher.getItems возвращает указатель, полученный от сишной dll'ки?
Какое выравнивание структур стоит в dll'ке и в программе на паскале?
Да, абсолютно верно.

TintSearch = class
public
procedure SetMinStemLen(len:integer); virtual; stdcall; abstract;
procedure SetBadSymbols(bs: pchar); virtual; stdcall; abstract;
procedure SetDictPath(dp: pchar); virtual; stdcall; abstract;
procedure SetLenWordsValues(list, listK: pchar); virtual; stdcall; abstract;
procedure SetDictValues(dictlistK: pchar); virtual; stdcall; abstract;
procedure searchString(s: pchar); virtual; stdcall; abstract;
function getCount: integer; virtual; stdcall; abstract;
function getItems: isearch; virtual; stdcall; abstract;
function stemka(str: pchar): pchar; virtual; stdcall; abstract;
procedure filter(border:integer); virtual; stdcall; abstract;
end;

TInitSearch = function : PintSearch; stdcall;
TDelSearch = procedure (ex : PintSearch); stdcall;
  IntSearchPointer:=InitSearch;
AIsearcher:=pointer(IntSearchPointer);
AIsearcher.SetMinStemLen(strtoint(OkofAISearchForm.MinStemLen.text;
AIsearcher.SetBadSymbols(pchar(OkofAISearchForm.BadSymbols.Text;
бла-бла-бла.

Сишное описание (только описание, доступа к полному коду у меня нет)

class intSearch{
public:
intSearch(void);
~intSearch(void);
virtual void __stdcall SetMinStemLen(long len);
virtual void __stdcall SetBadSymbols(char *bs);
virtual void __stdcall SetDictPath(char *dp);
virtual void __stdcall SetLenWordsValues(char *list, char *listK);
virtual void __stdcall SetDictValues(char *dictlistK);
virtual void __stdcall searchString(char *s);
virtual long __stdcall getCount(void);
virtual isearch * __stdcall getItems(void);
virtual char * __stdcall stemka(char *str);
};

typedef intSearch *(__stdcall *pFunc3void);
typedef void (__stdcall *pFunc4intSearch *ex);

Выравнивание стоит и там и там по единичке.
(Record field alingment =1)

kill-still

Любой из элементов, на котором произошло спотыкание можно рассчитать отдельно.
Вообше меня интересует правомерность строчки
ItemsElements:=pointer(ItemsPointer);  

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

SPARTAK3959

По-моему ты совершаешь 2 фатальные ошибки:
1. Ты используешь C++ класс из дельфи. Классы в C++ и дельфи не совместимы!
2. Ты считаешь, что указатели (на массивы) в C++ соотвествуют динамическим массивам в дельфи. Динамические массивы в дельфи имеют более сложную стуктуру.
Если это действительно так, то меня удивляет как твоя программа не падает на много раньше сложения строк.

SPARTAK3959

Если тебе надо использовать С++ класс из дельфи (ровно как и наоборот то это делается так:
пишется небольшая dll'ка на такой же версии компилятора С++ (иначе бинарной совместимости классов может не быть которая преобразует ООП-интерфейс в процедурный интерфейс. Затем эта библиотека используется в Delphi. При необходимости, можно сделать обратное преобразование процедурного интерфейса в ООП.

SPARTAK3959

А если тебя интересует какого хрена между классами в с++ и delphi нет совместимости - познакомься с технологией COM, которая ровно для этого и предназначена.

kill-still

1. Ты используешь C++ класс из дельфи. Классы в C++ и дельфи не совместимы!
Результаты однако прога выдаёт верные...

kill-still

Насчёт дллки - она на си потому что её пишут на стороне(1С) и мы покупаем её для использования в нашей проге.

kill-still

пишется небольшая dll'ка, которая преобразует ООП-интерфейс в процедурный интерфейс. При необходимости, можно сделать обратное преобразование процедурного интерфейса в ООП.
Ну, во первых это требует бОльших процессорных мощностей (преобразование туда-сюда а уже и так всё прилично подтормаживает(0.5-1сек на позицию во вторых, всё равно в итоге процедурный интерфейс в конечном счёте выдаст тот же самый динамический массив.

SPARTAK3959

Это не динамический массив, а массив c открытыми границами. Его можно определить так:
type
tarray=array [1..1000000] of dictout;
parray=^tarray;
но ни в коем случае как array of dictout.

SPARTAK3959

Если ее пишут на заказ, то скажите, что бы ее делали в виде COM-объектов.
Переходники - это пара call'ов, а потому замедлят работу незначительно.

SPARTAK3959

Результаты однако прога выдаёт верные...
Ну тогда и сегфолтов в твоей проге тоже нет, это так - наваждение.
Шварцнегер тоже может вставить квадратный брусок в круглое отверстие, но это не повод следовать его примеру, если у тебя нет столько силы.

kill-still

Хм, действительно, как-то по-разному у меня описано:
virtual isearch * __stdcall getItems(void);
>>>>>>>>
Tisearcharray = array of Tisearch;
isearch = ^Tisearcharray;
и
dictout *items;
>>>>>>
items: array of Tdictout;
Хотя с первого взгляда не зная си не поймёшь(а я в нём ни бум-бум).
попробую сейчас так:
TdictoutArray = array of Tdictout;
Pdictout = ^TdictoutArray;
............
items: Pdictout;

kill-still

переделал вот таким образом кусок:
              SetLength(tmpArray,ItemsElements[j].NumberOfElements);
tmpArray:=pointer(ItemsElements[j].items);
for k := 0 to ItemsElements[j].NumberOfElements-1 do
begin
name:=(tmpArray[k].name);
delete(name,pos(' ',namelength(name;
outputstring:=outputstring+' WORD: '+name+' val: '+inttostr(tmpArray[k].value)+
' dicVal: '+inttostr(tmpArray[k].dic_value)+' addVal: '+inttostr(tmpArray[k].add_value);
end;
tmpArray:=nil;

Всё равно падает =(

kill-still

Причём если оно упало, то считать перестёт. Перестаёт выдавать ошибку только если библиотеку перезагрузить
FreeLibrary(LibHandle);
LibHandle := LoadLibrary(fullpath);

SPARTAK3959

array of something и array [1..100000] of something - это 2 большие разницы. Первое всегда занимает 4 байта, поскольку является указателем на что-то типа vector в C++.

kill-still

Кажись разобрались. Я как-то не обратил сразу внимания на то, что он стал вылетать с другим эксэпшеном. Теперь он обсчитывает ГОРАЗДО больше строчек прежде чем упасть. И падает уже с аксесс виолэйшн дллке на передаче ей очередной строки.

pitrik2

Насчёт дллки - она на си
си и С++ - это разные языки
ты уж определись какнить :)
учитывая что есть классы, то скорее это С++
Оставить комментарий
Имя или ник:
Комментарий: