Вызов функции из DLL (DLL на Dephi)
От: AlexFilan  
Дата: 02.05.11 14:34
Оценка:
Помогите разобраться с вызовом функции.
Есть длл-ка на Delphi, возникла трудность с вызовом именно одной функции, которая принимает в качестве параметров структуры.

{TParam - Входные данные следующей структуры:}
type TParam = record  
  Cmd   :Byte;
  Sm    :PChar;
  Cr    :PChar; {3+1 байт, если '', то по умолчанию} 
  Tr2   :PChar; {если '',то не учитывается, max 48 байт} 
  Add   :PChar; {если nil или '', то не учитывается. Max 184 байт} 
end; 

{TResult – Выходные данные следующей структуры:}
type TResult = record  
  Cmd   :Byte;
  St    :Byte;
  Sm    :PChar; {12+1 байт}
  Cd    :PChar; {max 48 байт}
  Cr    :PChar; {3+1 байт}
  RN    :PChar; {12+1 байт}
  DT    :PChar; {19+1 байт}
  TI    :PChar; {8+1 байт}
  CN    :PChar; {5+1 байт}
  AC    :PChar; {6+1 байт}
  HC    :PChar; {2+1 байт}
  HT    :PChar; {32+1 байт}
  ED    :PChar; {4+1 байт}
  TO    :DWord; 
  TLV   :PChar; {129+1 байт}
end;

Function Func(p1:Byte; var p: TParam; var r: TResult) : Byte; stdcall;


Вот как я вызываю функцию:


typedef struct TParam {
  uchar Cmd;
  char Sm[13];
  char Cr[4];
  char Tr2[48];
  char Add[184];
};

typedef struct TResult {
  uchar Cmd;
  uchar St;
  char Sm[13];
  char Cd[48];
  char Cr[4];
  char RN[13];
  char DT[20];
  char TI[9];
  char CN[6];
  char AC[7];
  char HC[3];
  char HT[33];
  char ED[5];
  ulong TO; 
  char TLV[130];
};

extern "C" {
  typedef uchar __declspec(dllimport)(__stdcall *Func)(uchar ver, TParam, TResult*);
}

...

TParam p;
p.Cmd = 0x20;
strcpy(p.Sm, "100");
strcpy(p.Cr, "");
strcpy(p.Tr2, "");
strcpy(p.Add, "");

TResult r;

uchar ret = lib.Func(4, p, &r); {здесь вываливается}

...

uchar Lib::Func(uchar ver, TParam p, TResult* r) {
  Func function = (Func)lib.resolve("Func");
  if( function) {
    return function(ver, p, r);
  }
  return -1;
}


Как правильно переписать эти структуры на С++, что бы они были съедобны для DLL?
Спасибо!
Re: Вызов функции из DLL (DLL на Dephi)
От: savitar  
Дата: 02.05.11 17:02
Оценка:
Здравствуйте, AlexFilan, Вы писали:


typedef struct TResult {
...
  char *Sm;
...
};

TResult r;
r.Sm = new char[13]; или malloc или чем пользуешьмя.


нужно объявлять в структурах указатели, а не массивы, ну и выделять память.
и еще нужно чтобы выравнивание структур в Delphi и C чтобы совпадало, по умолчанию в Delphi 4 (кажется). т.е. нужно в C сказать #pragma pack(4)
Re: Вызов функции из DLL (DLL на Dephi)
От: Pavel Dvorkin Россия  
Дата: 02.05.11 17:14
Оценка:
Здравствуйте, AlexFilan, Вы писали:

AF>Помогите разобраться с вызовом функции.

AF>Есть длл-ка на Delphi, возникла трудность с вызовом именно одной функции, которая принимает в качестве параметров структуры.

AF>[pascal]

AF>{TParam — Входные данные следующей структуры:}
AF>type TParam = record
AF> Cmd :Byte;
AF> Sm :PChar;

Я лет 10 не писал на Delphi, но сдается мне, что PChar — это не массив, а указатель, то есть char*

http://www.delphisources.ru/pages/faq/faq_delphi_basics/PChar.php.html

Из этого следует, что в С++ все PChar надо заменить на char*. Входным присвоить значения. Выходным должна паскалевская функция присвоить значения. Кто удалять будет эти строки в Паскале и когда — твоя проблема

Но это еще не все.

Есть такое понятие — выравнивание. Из-за него Cmd имеет смещение в структуре 0, а Sm — отнюдь не 1, а 4. Можно, впрочем, сделать, чтобы была 1

#pragma pack(push)
#pragma pack(1)
// здесь все структуры, для которых это надо
#pragma pack(pop)
// восстановлен стандартный режим

Проблема в том, что выравнивание нужно сделать не на 1, а на то же значение, которое в Паскале, а какое оно там — я не знаю.



AF>

typedef struct TParam {
  uchar Cmd;
  char* Sm;
  char* Cr;
  char* Tr2;
  char* Add;
};

AF>extern "C" {
AF>  typedef uchar __declspec(dllimport)(__stdcall *Func)(uchar ver, TParam, TResult*);

А точно Паскалевская __srdcall ?

AF>}

AF>...

TParam p;
p.Cmd = 0x20;
p.Sm = (char*)malloc(13);
strcpy(p.Sm, "100");
// и т.д.

AF>
With best regards
Pavel Dvorkin
Re: Вызов функции из DLL (DLL на Dephi)
От: AlexFilan  
Дата: 02.05.11 20:19
Оценка:
Вот что получилось:
#pragma pack(push)
#pragma pack(1)

typedef struct TInfo {
  int Open;
  char *SerNo;
  char *Curr;
  char *Currs;
  char *Opers;
};

typedef struct TParam { 
  uchar Cmd;
  char *Sm;
  char *Cr;
  char *Tr2;
  char *Add;
};

typedef struct TResult {
  uchar Code;
  uchar State;
  char *Summa;
  char *Card;
  char *Currency;
  char *RefNo;
  char *DateTime;
  char *TerminalId;
  char *CheckNo;
  char *AutCode;
  char *HostCode;
  char *HostText;
  char *ExparyDate;
  ulong Trans_Op;
  char *TLV_Block;
};

#pragma pack(pop)

extern "C" {
    typedef uchar __declspec(dllimport)(__stdcall *Func)(int, TParam, TResult*);
    typedef uchar __declspec(dllimport)(__stdcall *GetData)(TInfo*);
}

int main(int argc, char* argv[]) {

  HMODULE hLib = LoadLibrary("srv.dll");
  if (!hLib) return 0;

  GetData funcGetData = (GetData)GetProcAddress(hLib, "GetData");
  if (!funcGetData) return 0;

  TInfo info;
  info.SerNo    = (char*)malloc(13);
  info.Curr    = (char*)malloc(4);
  info.Currs    = (char*)malloc(24);
  info.Opers    = (char*)malloc(64);

  funcGetData(&info);

  free(info.SerNo);
  free(info.Curr);
  free(info.Currs);
  free(info.Opers);

  Func func = (Func)GetProcAddress(hLib, "Func");
  if (!func) return 0;

  TParam p;
  p.Code    = 0x2A;
  p.Summa    = (char*)malloc(13);
  p.Currency    = (char*)malloc(4);
  p.Track2    = (char*)malloc(48);
  p.AddInfo    = (char*)malloc(184);
  strcpy(p.Summa, "100");
  strcpy(p.Currency, "");
  strcpy(p.Track2, "");
  strcpy(p.AddInfo, "");

  TResult r;
  r.Summa    = (char*)malloc(13);
  r.Card    = (char*)malloc(48);
  r.Currency    = (char*)malloc(4);
  r.RefNo    = (char*)malloc(13);
  r.DateTime    = (char*)malloc(20);
  r.TerminalId    = (char*)malloc(9);
  r.CheckNo    = (char*)malloc(6);
  r.AutCode    = (char*)malloc(7);
  r.HostCode    = (char*)malloc(3);
  r.HostText    = (char*)malloc(33);
  r.ExparyDate    = (char*)malloc(5);
  r.TLV_Block    = (char*)malloc(130);

  func(4, p, &r);

  ...

  FreeLibrary(hLib);

  return 0;
}


Вызов Функции func по прежнему не проходит.
Но вызов функции GetData происходит корректно. Вот ее паскалевское описание:

FUNCTION NAME: GetData 

type TInfo = record 
  Open   : Boolean;
  SerNo  : PChar;  {12+1 байт} 
  Curr   : PChar;  {3+1 байт} 
  Currs  : PChar;  {max 24 байт} 
  Opers  : PChar;  {max 64 байт} 
end; 

Function GetData (var Info:TInfo):Byte;stdcall;
Re[2]: Вызов функции из DLL (DLL на Dephi)
От: AlexFilan  
Дата: 02.05.11 20:23
Оценка:
Вот еще:
после вызова функции func — останавливается на:
void __cdecl _unlock ( int locknum )
{
        /*
         * leave the critical section.
         */
        LeaveCriticalSection( _locktable[locknum].lock );
}


из mlock.c
Re[2]: Вызов функции из DLL (DLL на Dephi)
От: savitar  
Дата: 02.05.11 20:36
Оценка:
Здравствуйте, AlexFilan, Вы писали:

#pragma pack(1) — нужна только в том случае если паскалевская структура объявлена так:
TResult = packed record
...
end;

или в настройках проекта стоит выравнивание в 1 байт. в Delphi7 например оно поумолчанию 8.
Re: Вызов функции из DLL (DLL на Dephi)
От: McQwerty Россия  
Дата: 03.05.11 11:08
Оценка:
Здравствуйте, AlexFilan, Вы писали:

AF>Помогите разобраться с вызовом функции.

AF>Есть длл-ка на Delphi, возникла трудность с вызовом именно одной функции, которая принимает в качестве параметров структуры.

AF>
AF>Function Func(p1:Byte; var p: TParam; var r: TResult) : Byte; stdcall;
AF>


AF>Вот как я вызываю функцию:


AF>
AF>extern "C" {
AF>  typedef uchar __declspec(dllimport)(__stdcall *Func)(uchar ver, TParam, TResult*);
AF>}
AF>uchar ret = lib.Func(4, p, &r); {здесь вываливается}
AF>


AF>Как правильно переписать эти структуры на С++, что бы они были съедобны для DLL?


Если мне не изменяет мой склероз, то объявление var — это передача не по значению, а по ссылке.
Соответственно, прототип поменяется:

typedef uchar __declspec(dllimport)(__stdcall *Func_A)(uchar ver, TParam&, TResult*);
typedef uchar __declspec(dllimport)(__stdcall *Func_B)(uchar ver, TParam*, TResult*);


Разница между вариантами А и Б в способе вызова. В случае А — код можно оставить без изменений, а в случае Б — придётся взять адрес (как, собственное, и делается для третьего параметра).
А: uchar ret = lib.Func(4, p, &r);
Б: uchar ret = lib.Func(4, &p, &r);
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.