Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2009.09.06;
Скачать: [xml.tar.bz2];

Вниз

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

 
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;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.56 MB
Время: 0.007 c
15-1247208288
Внук
2009-07-10 10:44
2009.09.06
Пятничная задачка


2-1246866808
smirnoff
2009-07-06 11:53
2009.09.06
Вопрос по AnsiChar


15-1247043689
Гость
2009-07-08 13:01
2009.09.06
Администрирование


2-1246931961
MonoLife
2009-07-07 05:59
2009.09.06
Прозрачность TBitmap.


15-1246825805
Юрий
2009-07-06 00:30
2009.09.06
С днем рождения ! 6 июля 2009 понедельник





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский