我有一些MSVC ++编译的DLL,我已经创建了类似COM(lite)的接口(抽象的Delphi类)。其中一些类具有需要指向对象的指针的方法。这些C ++方法是使用 __ thiscall 调用约定(我无法更改)声明的,就像__stdcall一样,除了这个指针传递在ECX注册表上。
我在Delphi中创建类实例,然后将其传递给C ++方法。我可以在Delphi中设置断点并看到它在我的Delphi类中遇到暴露的__stdcall方法,但很快我得到一个STATUS_STACK_BUFFER_OVERRUN并且应用程序必须退出。是否可以在Delphi方面模拟/处理__thiscall?如果我传递一个由C ++系统实例化的对象,那么一切都很好,并调用该对象的方法(如预期的那样),但这没用 - 我需要传递Delphi对象。
编辑2010-04-19 18:12 这是更详细的内容:第一个 调用的方法(setLabel)退出时没有 错误(尽管它是一个存根方法)。该 第二种方法叫做(init),进入 然后当它试图读取时死亡 vol 参数。
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 Side
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寄存器有关?非常感谢帮助。
编辑2010-04-19 17:42 可能是ECX注册需要的 入境时保存并恢复一次 功能退出?是这个 C ++需要什么指针?我可能 刚刚达到目前的基础上 一些激烈的谷歌搜索。一世 找到something related,但它 似乎正在处理相反的问题 这个问题。
答案 0 :(得分:3)
让我们假设您已经创建了一个带有VMT的MSVC ++类,它完美地映射到Delphi类的VMT(我从未做过,我只相信您是可能的)。现在你可以从MSVC ++代码调用Delphi类的虚方法,唯一的问题是__thiscall调用约定。由于Delphi不支持__thiscall,可能的解决方案是在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;
答案 1 :(得分:1)
我认为你不能合理地期望这种方法有效。 C ++没有标准化的ABI,Delphi没有标准化的任何东西。您可能会找到一种方法来破解可行的方法,但无法保证它将继续使用未来版本的Delphi。
如果您可以修改MSVC方面的东西,您可以尝试使用COM(这正是COM的目的。)这将是丑陋和令人不快的,但我真的没有意识到你很开心现在......所以也许这会有所改善。
如果你不能这样做,看起来你要么必须编写一个thunking层,要么不使用Delphi。
答案 2 :(得分:1)
不要这样做。
正如Ori所说,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
。
是的,这很烦人,你必须从Delphi调用CBox_init
而不是init
,但这只是你需要处理的事情。当然,如果您愿意,可以将CBox_init
重命名为更合适的内容。
答案 3 :(得分:0)
您可以尝试使用C ++ Builder编译这些DLL,C ++ Builder支持与Delphi的互操作性语言支持。自BDS 2006版本以来,可以在Delphi中访问C ++ Builder中创建的组件,因此普通的旧类可以正常工作。
如果您打算仅使用MSVC,COM可能是两种环境之间接口的最佳方式。
答案 4 :(得分:0)
我创建了COM-like(lite)接口(抽象Delphi类)
为什么不使用常用的COM接口?在C ++和Delphi中,Thay保证是二进制兼容的。
唯一的问题是你无法避免Delphi中的AddRef / Release / QueryInterface。但是如果你将引用计数实现为什么都不做(如TComponent那样) - 那么你可以忽略C ++方面的这些方法。
答案 5 :(得分:0)
作为使用c ++ Builder建议的补充,由于版本/“构建人”异议的预算/可用性,这可能是一个问题
我建议使用一个简单的MSVC包装器来传递对Delphi dll的调用。 您可以选择是否使用COM。
您可以使用您编写的现有代码,而无需深入研究汇编程序以修复调用约定不匹配。