首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Delphi:从导出接口/类的vc++ dll调用函数

Delphi:从导出接口/类的vc++ dll调用函数
EN

Stack Overflow用户
提问于 2010-02-15 14:07:52
回答 4查看 4K关注 0票数 1

我在访问用vc++编写的导出接口的dll时遇到了一些问题。首先,我尝试使用类,但经过一些谷歌搜索后,我找到了解决方案,这是我不可能的。我只想确定,通过使用其他语言,比如c++,插件接口可以被访问。

Delphi接口

代码语言:javascript
复制
IPlugIn = interface
  function GetName: WideString; stdcall;
end;

Delphi插件调用

代码语言:javascript
复制
procedure TForm1.Button5Click(Sender: TObject);
var
  hLib: Cardinal; 
  MLoadPlugIn: TLoadPlugIn;
  PlugIn: IPlugIn;
begin
  hLib := LoadLibrary('PluginB.dll');
  try
    if not(hLib = 0) then
    begin
      @MLoadPlugIn := GetProcAddress(hLib, 'LoadPlugIn');
      if not(@MLoadPlugIn = nil) then
      begin
        if MLoadPlugIn(PlugIn) then
          try
            ShowMessage(PlugIn.GetName); // here i get the access-violation using the vc++ plugin
          finally                        // i get the return value but the instance is not created
            PlugIn := nil;
          end;
      end
      else
        raise Exception.Create('');
    end;
  finally
    FreeLibrary(hLib);
  end;
end;

Delphi插件dll

代码语言:javascript
复制
  TMyPlugin = class(TInterfacedObject, IPlugIn)
  public
    function GetName: WideString; stdcall;
  end;

function TMyPlugin.GetName;
begin
  result := 'TMyPlugin';
end;

function LoadPlugIn(var PlugIn: IPlugIn): Boolean; stdcall;
begin
  try
    PlugIn := TMyPlugin.Create;
    result := True;
  except
    result := False;
  end;
end;

exports
  LoadPlugIn;

vc++插件dll

// IPlugIn

代码语言:javascript
复制
__interface //__declspec(uuid("E44BB34F-D13F-42D7-9479-4C79AF5C0D1B"))
IPlugIn : public IUnknown
{
 void _stdcall GetName(BSTR* result);
};

// TMyPlugIn头

代码语言:javascript
复制
class TMyPlugIn : public IPlugIn
{
public:
 // Constructor
 TMyPlugIn() : m_cRef(1) {}
 // Destructor
 ~TMyPlugIn() {}

 // Needed to implement IUnknown used by COM to acces your component
 HRESULT _stdcall QueryInterface(const IID& iid, void** ppv);
    ULONG _stdcall AddRef();
 ULONG _stdcall Release();

 void _stdcall GetName(BSTR* result);
private:
 long m_cRef ;
};

// TMyPlugIn cpp

代码语言:javascript
复制
HRESULT _stdcall TMyPlugIn::QueryInterface(const IID& iid, void** ppv)
{    
 if (iid == IID_IUnknown)
 {
  *ppv = static_cast<IPlugIn*>(this) ; 
 }
 else if (iid == IID_IPlugIn)
 {
  *ppv = static_cast<IPlugIn*>(this) ;
 }
 else
 {
  *ppv = NULL ;
  return E_NOINTERFACE ;
 }
 reinterpret_cast<IUnknown*>(*ppv)->AddRef() ;
 return S_OK ;
}

ULONG _stdcall TMyPlugIn::AddRef()
{
 return InterlockedIncrement(&m_cRef) ;
}

ULONG _stdcall TMyPlugIn::Release() 
{
 if (InterlockedDecrement(&m_cRef) == 0)
 {
  delete this ;
  return 0 ;
 }
 return m_cRef ;
}

void _stdcall TMyPlugIn::GetName(BSTR* result)
{
 string s1 = "PluginName";
 *result = A2WBSTR(s1.c_str());
}

// cpp插件的导出函数

代码语言:javascript
复制
extern "C" bool __declspec(dllexport) __stdcall LoadPlugIn(IPlugIn* PlugIn);
bool __declspec(dllexport) __stdcall LoadPlugIn(IPlugIn* PlugIn)
{
 PlugIn = new TMyPlugIn;
 return TRUE; 
}
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2010-02-15 22:19:14

您会得到访问冲突,因为以下代码

代码语言:javascript
复制
extern "C" bool __declspec(dllexport) __stdcall LoadPlugIn(IPlugIn* PlugIn);
bool __declspec(dllexport) __stdcall LoadPlugIn(IPlugIn* PlugIn)
{
 PlugIn = new TMyPlugIn;
 return TRUE; 
}

创建插件类的一个实例,并将地址写入堆栈,在那里它将很快被遗忘。回到Delphi程序中,原来的插件接口变量仍然是nil,所以对它调用一个方法就会崩溃。您需要模仿QueryInterface()所做的事情,如下所示:

代码语言:javascript
复制
extern "C" bool __declspec(dllexport) __stdcall LoadPlugIn(IPlugIn** PlugIn);
bool __declspec(dllexport) __stdcall LoadPlugIn(IPlugIn** PlugIn)
{
  *PlugIn = new TMyPlugIn;
  return TRUE; 
}

这将传递接口变量的地址,插件实例的地址将被写入变量。

票数 4
EN

Stack Overflow用户

发布于 2010-02-16 10:18:43

除了mghie说过的话,您还遇到了德尔福和C++之间定义不匹配的问题。

您的C++ GetName签名是:

代码语言:javascript
复制
 void _stdcall GetName(BSTR* result);

你的特尔菲签名是:

代码语言:javascript
复制
  function GetName: WideString; stdcall;

(至少)有两种可能的方法来解决这个问题。

1)如果您希望Delphi代码作为一个函数工作,那么确保它的安全性,并调整C++以匹配:

德尔菲:

代码语言:javascript
复制
  function GetName: WideString; safecall;

C++:

代码语言:javascript
复制
  HRESULT _stdcall GetName(BSTR* result);

2)修复Delphi以匹配现有的C++ defn:

代码语言:javascript
复制
  procedure GetName( var name: WideString );

我(个人)可能会走安全路线,因为我认为在德尔菲那边更干净.

票数 2
EN

Stack Overflow用户

发布于 2010-02-15 15:14:27

通常,您不应该跨DLL边界导出接口(实际上不应该导出对象),因为您不知道哪个内存管理器、运行时库和对象模型将位于任何一方。

也请参见这个关于DLL中异常的线程 (异常是对象)。

由于Delphi接口模型与COM接口模型和Visual C++可以导出COM对象。是二进制兼容的,所以您应该采用COM方式(was 阿德拉夫也建议)。

-耶伦

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2266417

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档