Проблема с DLL
От: andr2510  
Дата: 06.12.06 17:52
Оценка:
Цель — сконнектить pascal и c#

Для теста делаю:
library....

Function aaaa(Integer num):pchar;cdecl;
begin 
    result:="1234";
end;
Export
    aaaa();


C#
[DLLImport("aaa.dll")]
static public string aaaa(int num);


Все красиво работает, получаем строку 1234

Теперь приводим к тому что было на паскале. А был класс, который грузил файл, создавал в себе массив классов, каждый элемент
которого грузился из отдельного файла файла.

Примерно такая структура

Class a1
public
    name:string;
end;

Class a2
public 
    MainName:string;
    Mass:array of class a1;
end;


Именяем функцию DLL

Function aaaa(Integer num):pchar;cdecl;
var
    elema2:a2;
begin 
    elema2:=a2.create();
    //на этом этапе класс грзится из файла, загружает массив
    result:=PChar(elema2.mass[num].name);
end;


Так вот что непонятно. Никаких ошибок. Создается класс. Заполняется массив. Но код на С# получает пустые строки.
Если я напишу
...
    result:=Pchar(elema2.MainName);
...

То получаю нужный результат. Можно подумать что поле (name) класса a1 не заполняются. Но когда я запускаю отладку из Delphi,
или просто делаю run (указав в свойствах проекта, что нужно запускать exe-шник, сделанный на c#), то имена тут-же появляются
все работает так как должно быть.
Почему?
И воторой вопрос. Для отладки я добавлял ShowMessage(<текст>). Так вот, если я добавляю в функцию aaaa все работает — сообщения
появляются во всех случаях, а если я добавляю в конструкторы классов то сообщения выводятся только когда я запускаю из дельфей.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re: Проблема с DLL
От: AlexZu Россия  
Дата: 06.12.06 21:24
Оценка:
Здравствуйте, andr2510, Вы писали:

A>Цель — сконнектить pascal и c#

A>Так вот что непонятно. Никаких ошибок. Создается класс. Заполняется массив. Но код на С# получает пустые строки.
A>
A>...
A>    result:=Pchar(elema2.MainName);
A>...
A>


Я не знаю что такое PChar в паскале (понятно, что указатель на строку, но каким образом выделяется память под строки?), но маршалер по умолчанию имеет некоторое требование
Автор: AlexZu
Дата: 05.12.06
для памяти, возвращаемой из неуправляемого кода.

ps (лирика): забавно, что за последние несколько дней появляются однотипные вопросы связанные с interop, сессия что-ли?
Re: Проблема с DLL
От: SQWO  
Дата: 08.12.06 08:14
Оценка:
Здравствуйте, andr2510, Вы писали:

A>Цель — сконнектить pascal и c#


Если Вы не ограничены указанными прототипами функций,
то я бы предложил следующее решение:

Pascal
library....

procedure aaaa(num:integer; pBuf:pChar; BufLen:integer; var ReadLen:integer);stdcall;
var
  res: string;
begin 
  // Делаете нужную Вам работу,
  // главное предположение - результат работы помещяется в res

  ... //менеджер памяти паскаля выдели память под строку результата

  //
  ReadLen := Length(res);      
  if (pBuf <> nil) then
    StrPLCopy(pBuf,res,BufLen); //копируем результат из "памяти паскаля" в буфер "памяти программы"
end;                            //теперь менеджер памяти паскаля может и удалить память выделенную 
                                //в процедуре
Export
    aaaa();



C#
[DLLImport("aaa.dll")]
static public void aaaa(int num, StringBuilder pBuf, int BufLen, ref int ReadLen);



Вызывать соответственно так


int           BufLen = 1000;
StringBuilder Res = new StringBuilder( BufLen ); //Менеджер памяти NET выделил буфер

int ReadLen = 0;

aaaa( ... , Res, Res.Capacity, ReadLen); //в процедуре значение результата будет скопировано в буфер

if (ReadLen > Res.Capacity) then //если выделенный буфер мал, то выделим сколько реально надо и повторим
{
  Res.Capacity = ReadLen+1;
  aaaa( ... , Res, Res.Capacity, ReadLen);
}



И упаси Вас Вирт выделять память в одном модуле, а освобождать в другом.
В Вашем примере память выделялась в DLL, а освобождаться должна была
в вызывающей программе. Это есть ЗЛО!
И вот почему: Как Паскаль так и NET имеют собственные менеджеры памяти,
а каждый менеджер памяти работает только с той памятью, которую он сам же и выделял.
И что по-вашему должен делать менеджер память NET с куском, который он не выделял?

Рассмотрим Ваш пример подробнее:
строка
result:=PChar(elema2.mass[num].name);

1 Менеджер памяти паскаля выделил память под строку elema2.mass[num].name.
2 на выходе из процедуры он обнаружит, что эта строка никому не нужна!
т.к. она не передается ЯВНО как выходной параметр или результат функции
(преобразование к PChar как раз и сбивает компилятор с толку)
3 скорее всего компилятор при выходе из функции освободит память из под строки
и вызывающая программа получит адрес в памяти где КОГДА-ТО лежал правильный результат
А то что вы в тестах и под отладчиком получаете верные данные — это просто совпадение.

В предлагаемом решении память под результат явно выделяется в вызывающем модуле (программе)
а в DLL значение результата просто копируется в выделенный буфер.
Приэтом оба менеджера памяти "довольны". см коментарии к коду

ЗЫ А что, современных программистов вообще не учат, что такое память?
Re[2]: Проблема с DLL
От: andr2510  
Дата: 12.12.06 19:31
Оценка:
1. Спасибо за исчерпывающий ответ — открыл занавеску над незнанием
2. Ошибка, в которй по разному выдавались значения — ошибка того, кто писал слассы. Они ссылались на локальные файлы с относительными именами, и не вызвали эксепшенов. Короче проблема решилась путем трассировки чужого модуля и давания по рукам
3. Современных программистов может быть и учат.... а нас, окончивших Мгиэм, которые по программе знают азы работы на C(заметь-те не на C++) — не учат. Мы сами доходим до этого с помощью ВАС — более опытных в этом. То что с памятью нужно грамотно работать, я знаю. То что разные системы(среды) по разному будут работать с памятью — тоже ясно. Но никакого конкретного представления нет. Тем более с Managed кодом раотаю около 1-го месяца, а до этого с паскакалем и сями, в которых все предельно ясно, с работой по памяти
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.