Цель — сконнектить 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>>
Здравствуйте, andr2510, Вы писали:
A>Цель — сконнектить pascal и c#
A>Так вот что непонятно. Никаких ошибок. Создается класс. Заполняется массив. Но код на С# получает пустые строки.
A>A>...
A> result:=Pchar(elema2.MainName);
A>...
A>
Я не знаю что такое
PChar в паскале (понятно, что указатель на строку, но каким образом выделяется память под строки?), но маршалер по умолчанию имеет
некоторое требованиеАвтор: AlexZu
Дата: 05.12.06
для памяти, возвращаемой из неуправляемого кода.
ps (лирика): забавно, что за последние несколько дней появляются однотипные вопросы связанные с interop, сессия что-ли?
Здравствуйте, 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 значение результата просто копируется в выделенный буфер.
Приэтом оба менеджера памяти "довольны". см коментарии к коду
ЗЫ А что, современных программистов вообще не учат, что такое память?
1. Спасибо за исчерпывающий ответ — открыл занавеску над незнанием
2. Ошибка, в которй по разному выдавались значения — ошибка того, кто писал слассы. Они ссылались на локальные файлы с относительными именами, и не вызвали эксепшенов. Короче проблема решилась путем трассировки чужого модуля и давания по рукам
3. Современных программистов может быть и учат.... а нас, окончивших Мгиэм, которые по программе знают азы работы на C(заметь-те не на C++) — не учат. Мы сами доходим до этого с помощью ВАС — более опытных в этом. То что с памятью нужно грамотно работать, я знаю. То что разные системы(среды) по разному будут работать с памятью — тоже ясно. Но никакого конкретного представления нет. Тем более с Managed кодом раотаю около 1-го месяца, а до этого с паскакалем и сями, в которых все предельно ясно, с работой по памяти
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>