Войти

Показать полную графическую версию : Pascal exe и С++ dll


Server
05-09-2006, 20:59
Прошу помочь знающих людей, бо сам уже оскомиму набил-таки...
Значит имеется программа написанная на Delphi 7. В ней вызывается dll, следующим образом:
--------------------------------------------------
var
PlugExec:function(I,R,S,B:pointer;ISize,RSize,SSize,BSize:integer):boolean;
IntMas:array[0..72] of ^integer;
RealMas:array[0..4] of ^real;
StrMas:array[0..29] of ^shortstring;
BoolMas:array[0..4] of ^boolean;
begin
@PlugExec:=nil;
NewHandle:=LoadLibrary('C++Plugin.dll'));
if NewHandle <> 0 then begin
@PlugExec:=GetProcAddress(NewHandle,'PluginExec');
PlugExec(@IntMas,@RealMas,@StrMas,@BoolMas,length(IntMas),length(RealMas),length(StrMas),length(Bool Mas));
end;
FreeLibrary(NewHandle);
--------------------------------------------------
Теперь код С++ dll-ки (.NET):
--------------------------------------------------
extern "C" bool PASCAL EXPORT PluginExec(void *I, void *R, void *S, void *B, int ISize, int RSize, int SSize, int BSize){
int *IntMas;
IntMas=new int[ISize];
IntMas=dynamic_cast<int 32*>(I);
if (IntMas[5]==4)
...
return true; }
--------------------------------------------------
В результате выскакивает следуюзая ошибка в строке с dynamic_cast:
error C2059: syntax error : 'constant'

1. В чем проблема? Объявить const void *I - та же проблема.
2. Если из Delphi в ISize сидит к примеру 40, то в С++ я виду 1200000 или что-то в этом роде.
3. Адрес ячейки памяти I указывает на 0х000005 чего-то так, то есть область ОС. Указатели без типа несовместимы?

Заранее благодарен, за любую предоставленную информацию.

ivank
05-09-2006, 22:54
Server
Можно почитать документацию по dynamic_cast и понять, что а) он здесь не нужен б) синтаксис неправильный. Здесь хватит static_cast'а. Примерно так: IntMas = static_cast<int*>(i). Более того, поскольку фактически void* от int* ничем не отличается, а манглинг имён всё равно не применяется, то можно параметр I сразу сделать типа int*.

То что i=(void*)5 навевает на мысль, что конвенции вызова не совпадают и параметры лежат в обратном порядке (т.к. должно быть BSize=5). Что-то мне подсказывает, что PlugExec стоит объявить вот так:
PlugExec:function(I,R,S,B:pointer;ISize,RSize,SSize,BSize:integer):boolean; stdcall;
Либо убрать PASCAL из объявления функции в сишной реализации.

Server
05-09-2006, 23:07
ivank
Почему именно static надо применять? Массив я же создаю динамически.
Со static dll скомпилена. Массив динамически создается, но поскольку ISize равняется 1000000 и I указывает на системную область памяти - получаем Access Violation.

Экстрадиция Pascal из объявления функции - как мертвому припарки. Изменить exe код не представляется возможным. Там нет stdcall.

ivank
06-09-2006, 00:54
Server
Курите документацию. Как вы выделяете память не влияет на то, какой *_cast применять. Это зависит сугубо от типов, которые подвергаются преобразованию. Для преобразования void* в int* достаточно static_cast. dynamic_cast нужен при преобразованиях от указателя на предок класса к указателю на наследника.

Экстрадиция Pascal из объявления функции - как мертвому припарки. Изменить exe код не представляется возможным. Там нет stdcall.Слишком кратко, а поэтому непонятно.

Почему нельзя менять exe? Оптимально изменить именно его, чтобы использовалась конвенция stdcall, потому что про неё все (delphi и VC) всё прекрасно знают, так как она используется при вызове winapi. Это де-факто стандарт. Те, кто не используют эту конвенцию для вызова "инородных" функций - сами себе злобные буратины.

Я немножко погуглил и выяснил, что дельфи по умолчанию использует register конвенцию. То есть распихивает первые 3 параметры в регистры (eax, ecx, edx), остальные параметры кладутся на стек согласно паскалевской конвенции. Насколько мне известно VS ничего подобного не знает.

Поэтому, если exe менять нельзя для использования более адекватной конвенции, следует написать заглушку, которая будет принимать параметры согласно дельфёвым соглашениям (register), а вызывать функцию согласно согласно соглашениям, которые понимает VS (__cdecl, __stdcall, __pascal, или __fastcall). Если есть желание вставить такую заглушку непосредственно в dll, то придётся писать её на ассемблере.

Я, чтобы не мучиться, создал бы dll-заглушку на дельфи. В которой была бы одна функция PlugExec, которая принимает параметры в дельфёвой конвенции register, а вызывает сишный плагин уже с помощью stdcall. Соответственно в сишном плагине заменил бы соглашение для функции PlugExec с PASCAL на __stdcall.

upd
Повторюсь, резюмируя всё выше сказанное.

Либо используйте в exe и dll одно соглашение о вызовах, которое понимае и VS и delphi. К ним относятся: pascal/__pascal, cdecl/__cdecl, stdcall/__stdcall. Перед слышем дано название соглашеия в дельфе, после - в VS.

Либо (если exe менять нельзя) пишите заглушку, которая будет принимать парметры в дельфёвом соглашении register, а выдавать их в соглашении, понятном VS (сейчас у вас, судя по коду, сишная функция использует паскалевское соглашение).

Подробнее про различные соглашения о вызовах можно почитать в интернете. Погуглив на тему "delphi calling conventions".

Server
07-09-2006, 00:33
ivank
Спасибо, разобрался. Действительно нужно вызывать через соглашение stdcall. Получилось получить доступ ко всем параметрам главной программы, однако возник вопрос.
В программе на Delphi (этой же) используется тип shortstring. А какой для него аналог использовать в С++? char* не получается.

ivank
07-09-2006, 13:22
http://www.google.ru/search?hl=ru&q=delphi+shortstring+c

Первый байт - длина строки, начиная со второго - сама строка. Без завершающего нуля.

hasherfrog
25-09-2006, 11:48
Для продвинутого курения :]
Относительно DLL хорошая статья: http://www.progz.ru/articles.php?issue=9




© OSzone.net 2001-2012