Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2009.09.06;
Скачать: CL | DM;

Вниз

Функция обратного вызова с переменным числом параметров.   Найти похожие ветки 

 
DVM ©   (2009-07-07 02:15) [0]

Имеется некая dll, написанная на С.
В качестве параметра одна из функций этой dll принимает указатель на функцию обратного вызова:

__declspec(dllexport) void Start(бла-бла, бла-бла,
   void (__cdecl *ShowDebug)(int Level, struct ItemStruct *Item, WCHAR *Message, ...),
   бла-бла, бла-бла);

, здесь бла-бла - прочие параметры функции Start, не имеющие отношения к делу. ShowDebug() и есть та функция обратного вызова, которую будет вызывать dll.

В функции ShowDebug переменное число параметров, что вызвало у меня затруднения при попытке прикрутить работу с этой dll из программы на Delphi.

Функция ShowDebug в программе на Delphi выглядит так:

procedure ShowDebug(Level: Integer; Item: PItemStruct; Msg: PWChar); cdecl;
begin
 //
end;

Очевидно, что так хоть и работает, но я теряю параметры после Msg. Исправление на традиционный для паскаля вид:

procedure ShowDebug(Level: Integer; Item: PItemStruct; Msg: PWChar; Arg: array of const); cdecl;
begin
 //
end;

Приводит к ошибке.

Конечно можно плюнуть на эти параметры и обойтись и без них, но ме все же интересно как мне их получить в программе на Delphi?


 
dmk ©   (2009-07-07 04:04) [1]

Возможно поможет
http://www.progz.ru/forum/index.php?showtopic=25598


 
SPeller ©   (2009-07-07 04:22) [2]

varargs ?


 
SPeller ©   (2009-07-07 04:23) [3]


> When importing a C function that takes a variable number
> of parameters, use the varargs directive. For example,
>
> function printf(Format: PChar): Integer; cdecl; varargs;
>


 
DVM ©   (2009-07-07 11:19) [4]


> SPeller

Близко, но не совсем то.

Дело в том, что я НЕ ИМПОРТИРУЮ функцию С с переменным числом параметров.
В моем случае DLL написанная на C должна вызывать функцию из программы на Delphi, передавая ей переменное число параметров.
Куда приткнуть varargs я че-то не понимаю.


 
clickmaker ©   (2009-07-07 12:30) [5]

> передавая ей переменное число параметров

а если 2 параметра: количество параметров и массив параметров следом?


 
DVM ©   (2009-07-07 12:43) [6]


> clickmaker ©


> а если 2 параметра: количество параметров и массив параметров
> следом?

Т.е. предлагаешь функцию описать так:

procedure ShowDebug(Level: Integer; Item: PItemStruct; Msg: PWChar; n: byte; arg: array of const); cdecl;
begin
 //
end;

Access Violation все равно.

Если программу писать на СИ, то в ней легко описать данную функцию так и все работает:

void __cdecl ShowDebug(int Level, struct ItemStruct *Item, WCHAR *Format, ...) {
va_list VarArgs;
....
}

Самое интересное, что во FreePascal конструкция вида тоже работает:

procedure ShowDebug(Level: Integer; Item: PItemStruct; Msg: PWChar; Arg: array of const); cdecl;
begin
//
end;

А В ДЕЛФИ НЕТ!!!


 
clickmaker ©   (2009-07-07 12:57) [7]

> [6] DVM ©   (07.07.09 12:43)

не совсем. При смешении языков нужно какие-то всем понятные типы использовать. Массив указателей, целых и т.п. Может, VARIANT? Только не дельфийский, а COMовский.


 
tesseract ©   (2009-07-07 13:04) [8]


>  arg: array of const); cdecl;


А чем ссылка-то просто не устраивает? Зачем open array гонять ? Если имеено массив непонятно чего нужно - то PSafeArray отлично подходит плюс ещё и потокобезопасен.


 
oxffff ©   (2009-07-07 13:09) [9]

Передача

> arg: array of const


Развернется в два параметра.
Указатель на данные + верхняя граница массива.
+ придется коректно заполнить TVarRec на стороне С.


 
DVM ©   (2009-07-07 13:11) [10]


> clickmaker ©


> При смешении языков нужно какие-то всем понятные типы использовать.

Но дело в том, что DLL использует описанный выше метод передачи параметров, на который я повлиять не могу. Точнее можно переделать DLL, благо исходники доступны, но не хотелось бы, т.к. она чужая и решать переделывать ее или нет должен ее автор.

Как я думаю, что сишная DLL все же эти параметры моей функции передает, т.к. при таком описании:

procedure ShowDebug(Level: Integer; Item: PItemStruct; Msg: PWChar); cdecl;

ошибок не возникает, но откуда их забрать?


 
DVM ©   (2009-07-07 13:15) [11]


> tesseract ©   (07.07.09 13:04) [8]


> Зачем open array гонять ?

Его как раз гонять не получается, т.к. с ним сразу AV


> Если имеено массив непонятно чего нужно - то PSafeArray
> отлично подходит плюс ещё и потокобезопасен.

т.е. ты предлагаешь последний параметр функции сделать PSafeArray?


> oxffff ©   (07.07.09 13:09) [9]


> + придется коректно заполнить TVarRec на стороне С.

Ннереально. т.к. DLL неприкасаема, будем считать.


 
oxffff ©   (2009-07-07 13:17) [12]


> > oxffff ©   (07.07.09 13:09) [9]
>
>
> > + придется коректно заполнить TVarRec на стороне С.
>
> Ннереально. т.к. DLL неприкасаема, будем считать.


Поэтому у тебя и AV.


 
DVM ©   (2009-07-07 13:20) [13]


> oxffff ©


> Поэтому у тебя и AV.

Да это понятно. Но, думаю, решение можно найти.


 
oxffff ©   (2009-07-07 13:21) [14]


> DVM ©   (07.07.09 13:11) [10]


Какой параметр говорит сколько параметров передано  

int Level
struct* ItemStruct

?


 
oxffff ©   (2009-07-07 13:22) [15]


> Но, думаю, решение можно найти.


Конечно. Сейчас найдем. ;)


 
DVM ©   (2009-07-07 13:24) [16]


> oxffff ©


> Какой параметр говорит сколько параметров передано  

В том то и дело, что никакой из этих параметров не связан с количеством передаваемых далее параметров.


 
oxffff ©   (2009-07-07 13:25) [17]


> DVM ©   (07.07.09 13:24) [16]


Так не бывает. Как ты узнаешь где граница?


 
tesseract ©   (2009-07-07 13:27) [18]


> т.е. ты предлагаешь последний параметр функции сделать PSafeArray?


Я вообще предлагал тупо указателем объявлять, но можно и SafeArray. Правда придёться выяснять как он в с-ях оперируется.


> ошибок не возникает, но откуда их забрать?


Из памяти конечно :-)


 
DVM ©   (2009-07-07 13:30) [19]


> oxffff ©   (07.07.09 13:25) [17]


> Как ты узнаешь где граница?

Дело в том, что в Delphi я никак не узнаю, потому как в функцию Delphi передать переменное количество параметров из DLL на СИ у меня бы не получилось.

Если бы программа была на СИ, то там бы я делал так:

void __cdecl ShowDebug(int Level, struct ItemStruct *Item, WCHAR *Format, ...) {
 va_list VarArgs;
 ....
 va_start(VarArgs,Format);
 vswprintf_s(......., Format,VarArgs);
 va_end(VarArgs);
}

О том, сколько элементов в VarArgs очевидно делается предположение по количеству символов для подстановки в Format


 
DVM ©   (2009-07-07 13:31) [20]


> tesseract ©   (07.07.09 13:27) [18]


> Я вообще предлагал тупо указателем объявлять

Это тоже первое, что пришло мне в голову. И это прокатывает. Но как потом разобрать какие параметры там вот вопрос.


 
oxffff ©   (2009-07-07 13:36) [21]


> DVM ©   (07.07.09 13:30) [19]


Технически тебе никто не мешает сделать ASM вставки.

Транслируя вызов в

вызов delphi функции с более адекватной сигнатурой например.

Level:integer;Item:pItemStruct;VariableLengthDATA:pointer) ;

Дальше ты можешь ее анализировать как хочешь. Это можно сделать.


 
oxffff ©   (2009-07-07 13:44) [22]

to DVM

Вот пример.

procedure NativeDelphiProc(a:integer;p:pinteger);
var i:integer;
begin
showmessage(inttostr(a));
{$POINTERMATH ON}
for i := 0 to 2 do
 begin
 showmessage(inttostr(p[i]));
 end;
{$POINTERMATH OFF}
end;

procedure VarProc(a:integer;b:integer;c:integer;d:integer);cdecl;
asm
mov eax,[esp+8];
lea edx,[esp+12]
call NativeDelphiProc;
end;

procedure TForm2.FormCreate(Sender: TObject);
begin
//Вызов со стороны C++
VarProc(1,2,3,4);
end;

P.S.Пойду прогуляться. Скоро вернусь к диалогу. :)


 
DVM ©   (2009-07-07 13:51) [23]


> oxffff ©   (07.07.09 13:44) [22]

Спасибо


 
oxffff ©   (2009-07-07 15:34) [24]


> DVM ©   (07.07.09 13:51) [23]


Вопрос исчерпан?


 
DVM ©   (2009-07-07 15:52) [25]


> oxffff ©   (07.07.09 15:34) [24]


> Вопрос исчерпан?

Нет, не совсем, я ждал возвращения с прогулки :) Направление движения понятно, как я и догадывался без ASM тут не обойтись.

В примере [22] мне не совсем понятно, где же это самое переменное количество параметров.
Функция из программы на СИ вызвается ровно с теми параметрами, которые объявлены в Pas коде. Их там ровно 4 и передается 4. Но как поправить PAS код, чтобы передавать 5 6 и т.д. параметров. Лучше на примере моих конкретных функций:

Еще раз функция в программе на паскале:

procedure ShowDebug(Level: Integer; Item: PItemStruct; Msg: PWChar); cdecl;
begin
//
end;


 
oxffff ©   (2009-07-07 16:04) [26]


> DVM ©   (07.07.09 15:52) [25]


У тебя вызов Delphi из С++ с передачей различного числа параметров.
Технически параметры могут быть произвольного типа. Тогда нужно использовать

procedure NativeDelphiProc(a:integer;RawParams:Pointer) для варьируемых параметров произвольного типа ;

Если варьируемые параметры одного типа,
   то тогда RawParams:PsomeType);

А дальше обращаешься к параметрам как
     RawParams[0],
     RawParams[1]
      RawParams[2] и т.д.

То есть у функции есть обязательная часть и необязательная(варьируемая часть). К обязательным параметрам как обычно. А к не обязательным через массив(для удобства).


 
oxffff ©   (2009-07-07 16:06) [27]


> DVM ©   (07.07.09 15:52) [25]


Напиши сигнатуру параметров обязательной части.
и сигнатуру параметров необязательной части.


 
DVM ©   (2009-07-07 16:22) [28]


> Напиши сигнатуру параметров обязательной части.

Собственно, вот эти три параметра выше - они обязательные
Я думаю расписывать PItemStruct нет смысла, так как это все одно указатель,
PWChar тоже указатель, фактически PWideChar.

Необязательные параметры - это либо указатели на строки, либо целые числа, либо числа UINT64 либо Double. И их переменное количество. По идее, они потом должны быть в функции ShowDebug подставлены в строку Msg так как это делает функция Format.


 
oxffff ©   (2009-07-07 17:21) [29]


> DVM ©   (07.07.09 16:22) [28]


Тогда указатель на rawData.
Где ты сам парсишь на составные параметры.

Только как найдешь метаописания типов?
В format за тебя делает компилятор через TVarRec.


 
Игорь Шевченко ©   (2009-07-07 17:50) [30]

если функция cdecl, ты ее можешь объявлять только с обязательными параметрами, включая msg, но с модификатором cdecl.

После этого, в начале своей программы делаешь ассемблерную вставку и имитируешь работу printf, разбирая msg, в котором, как я больше чем уверен, написаны спецификации формата вывода в стиле printf. После этого, зная, каким спецификациям соответствуют какие типы (а главное, размер значений этих типов), аккуратно получаешь адреса остальных параметров через @Msg + вычисленное смещение для конкретного аргумента.


 
DVM ©   (2009-07-07 17:51) [31]


> oxffff ©


> Только как найдешь метаописания типов?

Можно попробовать полагаться на их описания в Msg, там же указан тип, например %d. Это конечно не очень удобно и не очень надежно, но все лучше чем ничего.
P.S. Все больше склоняюсь к тому, чтобы переделать dll таким образом чтобы она сама подставляла эти параметры в строку MSG. Или сделать еще одну dll на СИ, которая бы стала промежуточным звеном между программой на паскале и этой первоначальной dll.


 
DVM ©   (2009-07-07 18:02) [32]


> Игорь Шевченко ©   (07.07.09 17:50) [30]


> в котором, как я больше чем уверен, написаны спецификации
> формата вывода в стиле printf.

Именно так.


> ты ее можешь объявлять только с обязательными параметрами,
>  включая msg, но с модификатором cdecl.

Т.е. мое объявление как:

procedure ShowDebug(Level: Integer; Item: PItemStruct; Msg: PWChar); cdecl;

Оно в данном случае и есть единственно верное?


> После этого, в начале своей программы делаешь ассемблерную
> вставку

Вот с этим то у меня проблема.


 
palva ©   (2009-07-07 18:24) [33]

Можно ведь и без ассемблера обойтись. Взять адрес последнего описанного параметра и наращивать при помощи обычной целой арифметики. Выйдем на хвост необязательных параметров. Вот здесь предлагается некий код. Думаю, полезно его посмотреть, чтобы не наткнуться на подводные камни.
http://stackoverflow.com/questions/298373/how-can-a-function-with-varargs-retrieve-the-contents-of-the-stack/298603


 
DVM ©   (2009-07-07 18:46) [34]


> palva ©


> Взять адрес последнего описанного параметра и наращивать
> при помощи обычной целой арифметики.

Это мысль. Попробую, спасибо за ссылку.


 
Игорь Шевченко ©   (2009-07-07 19:13) [35]


> Или сделать еще одну dll на СИ, которая бы стала промежуточным
> звеном между программой на паскале и этой первоначальной
> dll.


Это лучше всего


 
oxffff ©   (2009-07-07 22:35) [36]


> DVM ©   (07.07.09 17:51) [31]
>
> > oxffff ©
>
>
> > Только как найдешь метаописания типов?
>
> Можно попробовать полагаться на их описания в Msg, там же
> указан тип, например %d. Это конечно не очень удобно и не
> очень надежно, но все лучше чем ничего.


Тогда у тебя все есть чтобы решить эту задачу.


 
DVM ©   (2009-07-07 23:13) [37]


> oxffff ©


> Тогда у тебя все есть чтобы решить эту задачу.

Да, теперь есть. Да собственно по ссылке [33] практически и есть решение моей задачи, разве что строка Msg у меня сложнее.

--------------------------

Всем спасибо за помощь. На мой взгляд, вопрос был заслуживающий внимания.


 
Игорь Шевченко ©   (2009-07-08 00:11) [38]

DVM ©   (07.07.09 23:13) [37]


> На мой взгляд, вопрос был заслуживающий внимания


Безусловно.

Но наиболее правильным решением в данном случае является написание промежуточной DLL на С, которая преобразовывает callback с переменными числом параметров в callback с фиксированным числом параметров, используя для этого функцию swprintf


 
Германн ©   (2009-07-08 00:53) [39]


> Игорь Шевченко ©   (08.07.09 00:11) [38]
>
> DVM ©   (07.07.09 23:13) [37]
>
>
> > На мой взгляд, вопрос был заслуживающий внимания
>
>
> Безусловно.
>

И что ещё было наиболее естественно (и правильно на сём форуме) вопрос был помещен в "Прочее".


 
DVM ©   (2009-07-08 12:13) [40]


> Игорь Шевченко ©   (08.07.09 00:11) [38]


> Но наиболее правильным решением в данном случае является
> написание промежуточной DLL

Написал промежуточную Dll, все отлично заработало.



Страницы: 1 2 вся ветка

Текущий архив: 2009.09.06;
Скачать: CL | DM;

Наверх




Память: 0.59 MB
Время: 0.013 c
9-1182099522
nuflin
2007-06-17 20:58
2009.09.06
как запустить исходник quake2


15-1247003708
Petr V. Abramov
2009-07-08 01:55
2009.09.06
Всем поклонникам "Любэ" посвящается :)


2-1246361382
IrinaIrina
2009-06-30 15:29
2009.09.06
StringGrid записать в таблицу SQL


15-1246518647
stas
2009-07-02 11:10
2009.09.06
3D фото


15-1246981094
Дмитрий С
2009-07-07 19:38
2009.09.06
Как оптимальнее организовать оповещения о событиях