Всё прекрасно компилируется и работает (если программа на си).
Нужно программу на Дельфи. Пишу:
procedure TForm1.FormCreate(Sender: TObject);
var
hDll:THandle;
trans:procedure(ptr:pointer;ik,jk:integer;var pRez:pointer); stdcall;
mass:array of array of single;
rez:array of array of single;
begin
hDll:=LoadLibrary('math_dll.dll');
if hDll<>0 then
begin
@trans:=GetProcAddress(hDll,'trans');
if Assigned(@trans)=false then
ShowMessage('Функция trans не найдена!');
SetLength(mass,5,5);
SetLength(rez,5,5);
//Заполняем здесь массив случайными числами (здесь ошибки быть не может :))
trans(@mass[0,0],5,5,@rez[0,0]);
Итак далее...
Короче не работает, с этой функцией вообще не компилируется, пишет Types of actual and formal parametrs must be identical, с другими функциями компилируется но Access violation! Функция загружается нормально. Что не правильно? И каким образом ещё можно в программе на Delphi загрузить Dll написанную на C???
Здравствуйте, B0BAH, Вы писали:
BBA>extern "C" __declspec(dllexport) void trans(float* ptr,int ik,int jk,float* pRez){
BBA>Есть ещё файл math.h:
Я думаю, это не очень хорошая идея — называть свой загловочный файл так же, как стандартный сишный. Могут быть недоразумения.
BBA> trans:procedure(ptr:pointer;ik,jk:integer;var pRez:pointer); stdcall;
1) Почему один указатель var, а другой — нет? По-моему, var тут не нужен.
2) Почему ты объявляешь нетипизированный указатель? Надежнее сделать указатель на Single.
Здравствуйте, AkaSaint, Вы писали:
BBA>> trans:procedure(ptr:pointer;ik,jk:integer;var pRez:pointer); stdcall;
AS>1) Почему один указатель var, а другой — нет? По-моему, var тут не нужен. AS>2) Почему ты объявляешь нетипизированный указатель? Надежнее сделать указатель на Single.
Если я пишу так: trans:procedure(ptr:^single;ik,jk:integer;var pRez:^single); stdcall;
то компилятор указуваетна ошибку: Identifier expected but '^' found в месте между ^ и single, поэтому от такого указателя я отказался и использовал нетипизированный.
Насчёт math.h — он использован в кавычках, поэтому пересечения со стандартным быть не должно.
Вы можете использовать статическую загрузку DLL. Для этого объявите в Вашей программе функцию, которую хотите вызвать с использованием ключевого слова external. Например так:
Здесь cdecl — означает использование соглашения о вызовах C. По умолчанию DLL созданные с использованием C-компилятора используют именно такое соглашение. Если вы заставили компилятор использовать другое соглашения, stdcall, например, то в объявлении укажите его. После того как Вы объявите фцнкцию из DLL таким образом, Вы сможете использовать эту функцию, как если бы она была в Вашей программе. Imho, этот способ чуть быстрее и удобнее, чем динамическая загрузка DLL с помощью LoadLibrary и вытаскивание адресов функций через GetProcAddress, как в приведенном Вами примере.
BBA>Должна вернуть переданный ей адрес массива.
Должна вернуть? Она у Вас void ябъявлена. И ещё, то что у вас написано в теле функции
pRez=pMat;
не имеет никакого смысла, оба указателя это локальные переменные и то что Вы присвоили один другому ничего не меняет. Если бы Вы, например, хотели поменять значение, накоторое указывает pRez, на значение, на которое указывает pMat, тогда надо было бы писать
BBA>>Должна вернуть переданный ей адрес массива. Y>Должна вернуть? Она у Вас void ябъявлена. И ещё, то что у вас написано в теле функции Y>
Y>pRez=pMat;
Y>
Y>не имеет никакого смысла, оба указателя это локальные переменные и то что Вы присвоили один другому ничего не меняет. Если бы Вы, например, хотели поменять значение, накоторое указывает pRez, на значение, на которое указывает pMat, тогда надо было бы писать Y>
Y>*pRez = *pMat;
Y>
Извините, возможно я немного не корректно выразился (или неполно). В общем вернуть эта процедура должна во второй параметр, т.е. вызываем её так:
prob(@mass[0,0],@rez[0,0]);
и в массива mass хранится исходный массив, а в массиве rez должен соответственно появиться результат (по крайней мере я так думаю). А присваивание:
pRez = pMat;
в моём понимании должно изменить значение указателя pRez (адрес) на pMat и после окончания работы процедуры адрес @rez[0,0] должен стать равен @mass[0,0]. Или я всё таки не прав? Тогда укажите пожалуйста на ошибки в реализации процедуры trans (если они есть), она должна возвращать транспонированную матрицу. Повторюсь: при прогоне под отладчиком в Си она прекрасно работает. А при вызове из Dll в Дельфи нет (возвращает мусор, хотя некоторые числа похожи на те что в исходной матрице). Возможно всё дело в типах? Но насколько я знаю float в Си это single в Дельфи. По крайней мере они ода по 4 байта и с плавающей запятой.
Здравствуйте, B0BAH, Вы писали:
BBA>Здравствуйте, AkaSaint, Вы писали: BBA>Если я пишу так: trans:procedure(ptr:^single;ik,jk:integer;var pRez:^single); stdcall; BBA>то компилятор указуваетна ошибку: Identifier expected but '^' found в месте между ^ и single, поэтому от такого указателя я отказался и использовал нетипизированный.
Надо вначале тип определить:
type PSingle = ^single;
trans:procedure(ptr:PSingle ;ik,jk:integer;var pRez:PSingle ); stdcall;
BBA>Насчёт math.h — он использован в кавычках, поэтому пересечения со стандартным быть не должно.
Спасибо за предложенное Вами решение, такого я ещё не пробовал. Но решить мою проблему это не смогло если не писать var то компилируется но в rez получаю мусор, если оставить var то не компилируется и пишет: Types of actual and formal parametrs must be identical. Насколько я понял это переводится как Типы "актуальных" и формальных параметров должны быть одинаковыми, так у меня они одинаковые оба PSingle (теперь). Что самое интересное ошибка между последней скобкой и точкой с запятой может компилятор чего то не догоняет или я?
Всем спасибо за участие. Свою проблему я решил с помощью создания не двумерного, а одномерного массива длиной n*n, т.е. тот же массив только "в строчку". Кстати Dll трогать не пришлось, только замена массива на одномерный динамический.
BBA>Спасибо за предложенное Вами решение, такого я ещё не пробовал. Но решить мою проблему это не смогло если не писать var то компилируется но в rez получаю мусор, если оставить var то не компилируется и пишет: Types of actual and formal parametrs must be identical. Насколько я понял это переводится как Типы "актуальных" и формальных параметров должны быть одинаковыми, так у меня они одинаковые оба PSingle (теперь). Что самое интересное ошибка между последней скобкой и точкой с запятой может компилятор чего то не догоняет или я?
Этот код решает проблему с ошибкой Identifier expected but '^' found.
А чтобы исправить ошибку Types of actual and formal parametrs must be identical, надо писать так:
var pRes:PSingle;
{...}
pRes:=nil;
trans(@mass[0,0],5,5,pRes);
Но, в этом случае, память под массив результатов должна динамически выделяться в DLL (и надо организовать ее освобождение). Но, если mass динамический то, скомпилируется, но AV гарантировано. (см. ниже)
Если же память выделяется в самом приложении то и указатель на массив результатов по адресу передавать не нужно, т.е можно писать trans(@mass[0,0],5,5,@rez[0,0]);
Но, с динамическими массивами это не пройдет, т.к. двумерный динамический массив не хранится в памяти одним блоком.
Если же у Вас имеются функции, которые работают с отдельными строками массива (т.е. с одномерными массивами), то можно передавать и адрес первого элемента.
Здравствуйте, B0BAH, Вы писали:
BBA>Всем спасибо за участие. Свою проблему я решил с помощью создания не двумерного, а одномерного массива длиной n*n, т.е. тот же массив только "в строчку". Кстати Dll трогать не пришлось, только замена массива на одномерный динамический.
Раз проблема решилась то поздравляю
Если же придется еще сталкиваться с передачей многомерных дин. массивов в Си, то ищи решение в следующем направлении:
1) передавать в DLL сам массив (в Вашем примере: trans(mass,5,5,rez)
2) какому типу в Си соответствует array of array of single из Delphi ?
А дальше "твори, выдумывай, пробуй".