Передайте класс Delphi функции/методу C++, которая ожидает класс с __ thiscall методы

У меня есть некоторый MSVC ++, скомпилировал DLL, для которого я создал подобные COM (облегченные) интерфейсы (абстрактные классы Delphi). Некоторые из тех классов имеют методы, для которых нужны указатели на объекты. Эти методы C++ объявляются с __ thiscall соглашение о вызовах (который я не могу изменить), который является, точно так же, как __ stdcall, кроме этого указателя передается регистру ECX.

Я создаю экземпляр класса в Delphi, затем передаю его на метод C++. Я могу установить точки останова в Delphi и видеть, что он поражает выставленный __ stdcall методы в моем классе Delphi, но скоро я получаю STATUS_STACK_BUFFER_OVERRUN, и приложение должно выйти. Действительно ли возможно эмулировать/иметь дело с __ thiscall на стороне Delphi вещей? Если я передаю объект, который инстанцирует система C++ затем, все хорошо, и что методы объекта называют (как ожидался бы), но это бесполезно - я должен передать объекты Delphi.

Отредактируйте 19.04.2010 18:12, Это - то, что происходит более подробно: первый метод, названный (setLabel), выходит без ошибки (хотя это - тупиковый метод). Второй метод, названный (init), входит, затем умирает, когда он пытается считать vol параметр.

Сторона C++

#define SHAPES_EXPORT __declspec(dllexport) // just to show the value
class SHAPES_EXPORT CBox
{
  public:
    virtual ~CBox() {}
    virtual void init(double volume) = 0;
    virtual void grow(double amount) = 0;
    virtual void shrink(double amount) = 0;
    virtual void setID(int ID = 0) = 0;
    virtual void setLabel(const char* text) = 0;
};

Сторона Delphi

IBox = class
public
  procedure destroyBox; virtual; stdcall; abstract;
  procedure init(vol: Double); virtual; stdcall; abstract;
  procedure grow(amount: Double); virtual; stdcall; abstract;
  procedure shrink(amount: Double); virtual; stdcall; abstract;
  procedure setID(val: Integer); virtual; stdcall; abstract;
  procedure setLabel(text: PChar); virtual; stdcall; abstract; 
end;

TMyBox = class(IBox)
protected
  FVolume: Double;
  FID: Integer;
  FLabel: String; //
public
  constructor Create;
  destructor Destroy; override;
  // BEGIN Virtual Method implementation
  procedure destroyBox; override; stdcall;             // empty - Dont need/want C++ to manage my Delphi objects, just call their methods
  procedure init(vol: Double); override; stdcall;      // FVolume := vol;
  procedure grow(amount: Double); override; stdcall;   // Inc(FVolume, amount);
  procedure shrink(amount: Double); override; stdcall; // Dec(FVolume, amount);
  procedure setID(val: Integer); override; stdcall;    // FID := val;
  procedure setLabel(text: PChar); override; stdcall;  // Stub method; empty.
  // END Virtual Method implementation      
  property Volume: Double read FVolume;
  property ID: Integer read FID;
  property Label: String read FLabel;
end;

У меня была бы половина ожидаемого использовать stdcall один для работы, но что-то портит, не уверенный что, возможно, что-то, чтобы сделать с используемым регистром ECX? Справка значительно ценилась бы.

Отредактируйте 19.04.2010 17:42, могло случиться так, что регистр ECX должен быть сохранен на записи и восстановлен, после того как функция выходит? Этот указатель требуется C++? Я, вероятно, просто достигаю в данный момент на основе некоторых интенсивных поисков Google. Я нашел, что что-то имело отношение, но это, кажется, имеет дело с реверсом этой проблемы.

6
задан Atorian 19 April 2010 в 15:14
поделиться

5 ответов

Предположим, вы создали класс MSVC ++ с VMT, который идеально отображается в VMT класса Delphi (я никогда этого не делал, я просто верю вам, что это возможно) . Теперь вы можете вызывать виртуальные методы класса Delphi из кода MSVC ++, единственная проблема - это соглашение о вызове __thiscall. Поскольку __thiscall не поддерживается в Delphi, возможным решением является использование виртуальных методов прокси на стороне Delphi:

ОБНОВЛЕНО

type
  TTest = class
    procedure ECXCaller(AValue: Integer);
    procedure ProcProxy(AValue: Integer); virtual; stdcall;
    procedure Proc(AValue: Integer); stdcall;
  end;

implementation

{ TTest }

procedure TTest.ECXCaller(AValue: Integer);
asm
  mov   ecx,eax
  push  AValue
  call  ProcProxy
end;

procedure TTest.Proc(AValue: Integer);
begin
  ShowMessage(IntToStr(AValue));
end;

procedure TTest.ProcProxy(AValue: Integer);
asm
   pop  ebp            // !!! because of hidden delphi prologue code
   mov  eax,[esp]      // return address
   push eax
   mov  [esp+4],ecx    // "this" argument
   jmp  Proc
end;
3
ответ дан 17 December 2019 в 04:44
поделиться

Не думаю, что у вас есть основания ожидать, что это сработает. В C ++ нет стандартизированного ABI, а в Delphi нет ничего стандартизированного. Вы можете найти способ взломать что-то, что работает, но нет гарантии, что это продолжит работать с будущими версиями Delphi.

Если вы можете изменить сторону MSVC, вы можете попробовать использовать COM (это именно то, для чего был разработан COM). Это будет уродливо и неприятно, но я действительно не понимаю, что вам весело. сейчас ... Так что, может быть, это было бы улучшением.

Если вы не можете этого сделать, похоже, что вам придется либо написать промежуточный слой, либо не использовать Delphi.

1
ответ дан 17 December 2019 в 04:44
поделиться

Я создал COM-подобные (облегченные) интерфейсы (абстрактные классы Delphi)

Почему вы не используете обычные COM-интерфейсы? Тэй гарантированно совместим с двоичными кодами C ++ и Delphi.

Единственная проблема в том, что вы не можете избежать AddRef / Release / QueryInterface в Delphi. Но если вы реализуете подсчет ссылок как ничего не делающего (как это делает TComponent), то вы можете просто игнорировать эти методы со стороны C ++.

0
ответ дан 17 December 2019 в 04:44
поделиться

Вы Вместо этого можно попробовать скомпилировать эти библиотеки DLL с помощью C ++ Builder, C ++ Builder имеет языковую поддержку для взаимодействия с Delphi. Начиная с версии BDS 2006, компоненты, созданные в C ++ Builder, доступны в Delphi, поэтому простые старые классы будут работать нормально.

Если вы собираетесь использовать только MSVC, COM, возможно, лучший способ взаимодействия между двумя средами.

0
ответ дан 17 December 2019 в 04:44
поделиться

Не делайте этого .

Как упоминал Ори, C ++ ABI не стандартизирован. Вы не можете и не должны ожидать, что это сработает, и если вы что-то сделаете, это будет невероятно непереносимый взлом.

Стандартный способ начальной загрузки вызовов функций C ++ через языковые границы заключается в использовании статических функций, в которых вы явно передаете параметр this :

class SHAPES_EXPORT CBox
{
  public:
    virtual void init(double volume) = 0;
    static void STDCALL CBox_init(CBox *_this, double volume) { _this->init(volume); }
    // etc. for other methods
};

(Фактически, статический метод должен быть технически объявлен extern "C" , поскольку не гарантируется, что методы статического класса будут реализованы с помощью C ABI, но почти все существующие компиляторы делают это.)

Я вообще не знаю Delphi, поэтому не знаю Не знаю, как правильно с этим справиться на стороне Delphi, но это то, что вам нужно сделать на стороне C ++. Если Delphi поддерживает соглашение о вызовах cdecl, вы можете удалить STDCALL выше.

Да, это раздражает, потому что вам нужно вызвать CBox_init вместо init из Delphi, но с этим вам придется иметь дело. Вы можете, конечно, переименовать CBox_init во что-нибудь более подходящее.

1
ответ дан 17 December 2019 в 04:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: