Pasar una clase Delphi a un C ++ función / método que espera una clase con métodos __thiscall

StackOverflow https://stackoverflow.com/questions/2667925

Pregunta

Tengo un poco de MSVC ++ compilado DLL para el cual he creado COM-como (lite) Interfaces de Delphi (clases abstractas). Algunas de esas clases tienen métodos que necesitan punteros a objetos. Estos métodos de C ++ se declaran con la __ thiscall convención de llamada (que pueden no cambio), que es igual que __stdcall, excepto una se pasa este puntero en el registro ECX.

I crear la instancia de clase en Delphi, a continuación, pasarlo al método C ++. Puedo establecer puntos de interrupción en Delphi y verlo golpear los métodos __stdcall expuestas en mi clase de Delphi, pero pronto conseguir un STATUS_STACK_BUFFER_OVERRUN y la aplicación tiene que salir. ¿Es posible emular / trato con __thiscall en el lado Delphi de las cosas? Si paso un objeto instanciado por el sistema C ++ entonces todo está bien, y son llamados métodos de ese objeto (como era de esperar), pero esto no sirve para nada -. Tengo que pasar objetos Delphi

  

Editar 2010-04-19 18:12 Esto es lo que ocurre con más detalle: La primera   (método llamado setLabel) salidas sin   error (aunque su método stub a). los   segundo método llamado (init), entra   luego muere cuando se intenta leer el    vol parámetro.

C ++ Side

#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 lateral

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;

Me hubiera esperado media usando stdcall solo a trabajar, pero algo es echar a perder, no está seguro de lo que, tal vez algo que ver con el registro ECX está utilizando? La ayuda sería muy apreciada.

  

Editar 2010-04-19 17:42 ¿Podría ser que las necesidades de registro ECX sea   conservado a la entrada y una vez restaurado   se cierra la función? Es el este   puntero requerido por C ++? Probablemente soy   acaba de llegar en el momento sobre la base de   algunas búsquedas de Google intensos. yo   encontrado relacionados algo, pero   parece que se trata de la inversa   de este problema.

¿Fue útil?

Solución

Supongamos que usted ha creado una clase MSVC ++ con VMT que se asigna a la perfección en la VMT de una clase Delphi (nunca he hecho, acabo de creer que es posible). Ahora se puede llamar a los métodos virtuales de una clase de Delphi de código MSVC ++, el único problema es __thiscall convención de llamada. Desde __thiscall no está soportada en Delphi, la solución posible es utilizar métodos virtuales de proxy en el lado Delphi:

ACTUALIZADO

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;

Otros consejos

No creo que se puede esperar razonablemente que esto funcione. C ++ no tiene una ABI estandarizada, y Delphi no tiene una nada estandarizado. Es posible encontrar una manera de hackear algo que funciona, pero no hay garantía de que seguirá trabajando con las futuras versiones de Delphi.

Si se puede modificar el lado MSVC de cosas que usted podría tratar de usar COM (esto es exactamente lo COM fue diseñado para hacer.) Será feo y desagradable, pero realmente no tiene la sensación de que uno se divierte ahora ... Así que tal vez eso sería una mejora.

Si usted no puede hacer eso, parece que ya sea que usted tiene que escribir una capa de procesador o no utilizar Delphi.

No hacer esto .

Como se ha mencionado Ori, el C ++ ABI no está estandarizada. Usted no puede y no debe esperar que esto funcione, y si usted se las arregla algo, será un truco increíblemente no portátil.

La forma estándar de bootstrapping C ++ llamadas de función a través de límites del lenguaje es el uso de funciones estáticas donde se pasa de forma explícita en un parámetro 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" En realidad, el método estático técnicamente debe ser declarada, ya que los métodos estáticos de clase no están garantizados para ser implementado con la C ABI, pero casi todos los compiladores en existencia lo hacen.)

No sé Delphi en absoluto, por lo que no sé cuál es la forma correcta de manejar esto en el lado de las cosas Delphi es, pero esto es lo que hay que hacer en el lado C ++. Si Delphi se apoya la cdecl convención de llamada, entonces se puede quitar el STDCALL anteriormente.

Sí, esto es molesto en el que usted tiene que llamar CBox_init en lugar de init de Delphi, pero eso es sólo algo que se tiene que tratar. Puede cambiar el nombre CBox_init a algo más adecuado si se quiere, por supuesto.

Puede tratar de compilar los archivos DLL con C ++ Builder lugar, C ++ Builder cuenta con soporte de idiomas para la interoperabilidad con Delphi. Desde la versión BDS 2006, los componentes hechos en C ++ Builder se puede acceder en Delphi, clases de edad tan claro funcionarían bien.

Si usted tiene la intención de utilizar solamente MSVC, COM es tal vez la mejor manera de interfaz entre los dos entornos.

  

He creado COM-como interfaces (lite) (clases abstractas Delphi)

¿Por qué no utilizar las interfaces COM habituales? Thay están garantizados para ser compatitible binario en C ++ y Delphi.

El único problema es que no se puede evitar AddRef / Liberación / QueryInterface en Delphi. Pero si va a implementar el recuento de referencias que no hacer nada (como TComponent hacer) -. A continuación, puede simplemente ignorar estos métodos desde el lado C ++

Como complemento al uso c ++ Builder sugerencia, que puede ser un problema debido a un presupuesto / disponibilidad de la versión / "acumulación" objeciones individuo

Me sugiere un simple MSVC envoltorio que pases sobre las llamadas a la DLL de Delphi (s). Puede optar por utilizar COM o no en ese momento.

Puede utilizar el código existente que ha escrito más o menos como está, sin tener que ahondar en ensamblador para arreglar los desajustes convenciones de llamada.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top