Программа на Delphi и Dll на C
От: B0BAH  
Дата: 17.07.03 12:42
Оценка:
Есть сабж. Я делаю следующее:
Кусок Dll на Си:


#include "math.h"

extern "C" __declspec(dllexport) void trans(float* ptr,int ik,int jk,float* pRez){
    //Транспонирование матрицы
    for(int i=0; i<ik;i++){
        for(int j=0;j<jk;j++){
            *(pRez+i+j*ik)=*(ptr+j+i*jk);
        }
    }
}


Есть ещё файл math.h:

#ifndef _MATH_H_
#define _MATH_H_  

extern "C" __declspec(dllexport) void trans(float* ptr,int ik,int jk,float* pRez);

#endif


Всё прекрасно компилируется и работает (если программа на си).

Нужно программу на Дельфи. Пишу:

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???
Re: Программа на Delphi и Dll на C
От: AkaSaint  
Дата: 17.07.03 12:56
Оценка:
Здравствуйте, 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.
Re[2]: Программа на Delphi и Dll на C
От: B0BAH  
Дата: 17.07.03 13:06
Оценка:
Здравствуйте, 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 — он использован в кавычках, поэтому пересечения со стандартным быть не должно.
Re: Программа на Delphi и Dll на C
От: yanys  
Дата: 17.07.03 13:20
Оценка:
Вы можете использовать статическую загрузку DLL. Для этого объявите в Вашей программе функцию, которую хотите вызвать с использованием ключевого слова external. Например так:
procedure trans(ptr : pointer; ik, jk : integer; pRez : pointer); cdecl; external 'math_dll.dll';

Здесь cdecl — означает использование соглашения о вызовах C. По умолчанию DLL созданные с использованием C-компилятора используют именно такое соглашение. Если вы заставили компилятор использовать другое соглашения, stdcall, например, то в объявлении укажите его. После того как Вы объявите фцнкцию из DLL таким образом, Вы сможете использовать эту функцию, как если бы она была в Вашей программе. Imho, этот способ чуть быстрее и удобнее, чем динамическая загрузка DLL с помощью LoadLibrary и вытаскивание адресов функций через GetProcAddress, как в приведенном Вами примере.
Re: Программа на Delphi и Dll на C
От: B0BAH  
Дата: 17.07.03 17:29
Оценка:
Так и не заработало.
Пробую самый простой вариант:

Процедура в Dll:
extern "C" __declspec(dllexport) void prob(float* pMat,float* pRez){
    pRez=pMat;
}


Должна вернуть переданный ей адрес массива.

На дельфи подключаю её предложенным в этом топике способом:

procedure prob(pMat:pointer;pRez:pointer); cdecl; external 'math_dll.dll';


Вызываю:
prob(@mass[0,0],@rez[0,0]);


И не работает. Т.е. компилируется, но возвращает пустой массив. Под отладсиком видно что адрес rez отличен от mass (он больше).
Re: Программа на Delphi и Dll на C
От: Olafpalmer Россия  
Дата: 18.07.03 07:18
Оценка:
Здравствуйте, B0BAH, Вы писали:
Мне кажется вот так делать надо:
Объявление:
BBA>
BBA>  trans:procedure(var ptr:float;ik,jk:integer;var pRez:float); stdcall;
BBA>

И вызов потом
BBA>
BBA>  trans(mass[0,0],5,5,rez[0,0]);
BBA>
Re[2]: Программа на Delphi и Dll на C
От: yanys  
Дата: 18.07.03 11:34
Оценка:
Здравствуйте, B0BAH, Вы писали:

BBA>Так и не заработало.

BBA>Пробую самый простой вариант:

BBA>Процедура в Dll:

BBA>
extern "C" __declspec(dllexport) void prob(float* pMat,float* pRez){
BBA>    pRez=pMat;
BBA>}


BBA>Должна вернуть переданный ей адрес массива.

Должна вернуть? Она у Вас void ябъявлена. И ещё, то что у вас написано в теле функции
pRez=pMat;

не имеет никакого смысла, оба указателя это локальные переменные и то что Вы присвоили один другому ничего не меняет. Если бы Вы, например, хотели поменять значение, накоторое указывает pRez, на значение, на которое указывает pMat, тогда надо было бы писать
*pRez = *pMat;
Re[3]: Программа на Delphi и Dll на C
От: B0BAH  
Дата: 18.07.03 12:55
Оценка:
Здравствуйте, yanys, Вы писали:

BBA>>Процедура в Dll:

BBA>>
extern "C" __declspec(dllexport) void prob(float* pMat,float* pRez){
BBA>>    pRez=pMat;
BBA>>}


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 байта и с плавающей запятой.
Re[3]: Программа на Delphi и Dll на C
От: AlexVinS Россия  
Дата: 18.07.03 15:15
Оценка:
Здравствуйте, 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 — он использован в кавычках, поэтому пересечения со стандартным быть не должно.


Умный человек знает не многое, но нужное
Re[4]: Программа на Delphi и Dll на C
От: B0BAH  
Дата: 18.07.03 15:34
Оценка:
Здравствуйте, AlexVinS, Вы писали:


AVS>Надо вначале тип определить:


AVS>
AVS>type PSingle = ^single;

AVS>trans:procedure(ptr:PSingle ;ik,jk:integer;var pRez:PSingle ); stdcall;

AVS>


Спасибо за предложенное Вами решение, такого я ещё не пробовал. Но решить мою проблему это не смогло если не писать var то компилируется но в rez получаю мусор, если оставить var то не компилируется и пишет: Types of actual and formal parametrs must be identical. Насколько я понял это переводится как Типы "актуальных" и формальных параметров должны быть одинаковыми, так у меня они одинаковые оба PSingle (теперь). Что самое интересное ошибка между последней скобкой и точкой с запятой может компилятор чего то не догоняет или я?
Re: Программа на Delphi и Dll на C
От: B0BAH  
Дата: 18.07.03 17:15
Оценка:
Всем спасибо за участие. Свою проблему я решил с помощью создания не двумерного, а одномерного массива длиной n*n, т.е. тот же массив только "в строчку". Кстати Dll трогать не пришлось, только замена массива на одномерный динамический.
Re[5]: Программа на Delphi и Dll на C
От: AlexVinS Россия  
Дата: 19.07.03 08:48
Оценка:
Здравствуйте, B0BAH, Вы писали:

BBA>Здравствуйте, AlexVinS, Вы писали:



AVS>>Надо вначале тип определить:


AVS>>
AVS>>type PSingle = ^single;

AVS>>trans:procedure(ptr:PSingle ;ik,jk:integer;var pRez:PSingle ); stdcall;

AVS>>


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]);

Но, с динамическими массивами это не пройдет, т.к. двумерный динамический массив не хранится в памяти одним блоком.
Если же у Вас имеются функции, которые работают с отдельными строками массива (т.е. с одномерными массивами), то можно передавать и адрес первого элемента.


Умный человек знает не многое, но нужное
Re[2]: Программа на Delphi и Dll на C
От: AlexVinS Россия  
Дата: 19.07.03 09:06
Оценка:
Здравствуйте, B0BAH, Вы писали:

BBA>Всем спасибо за участие. Свою проблему я решил с помощью создания не двумерного, а одномерного массива длиной n*n, т.е. тот же массив только "в строчку". Кстати Dll трогать не пришлось, только замена массива на одномерный динамический.

Раз проблема решилась то поздравляю
Если же придется еще сталкиваться с передачей многомерных дин. массивов в Си, то ищи решение в следующем направлении:
1) передавать в DLL сам массив (в Вашем примере: trans(mass,5,5,rez)
2) какому типу в Си соответствует array of array of single из Delphi ?
А дальше "твори, выдумывай, пробуй".


Умный человек знает не многое, но нужное
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.