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

时间:2010-02-15 14:07:52

标签: c++ delphi class dll interface

我在访问用vc ++编写的导出接口的dll时遇到了一些麻烦。首先,我尝试使用类,但经过一些谷歌搜索,我来到解决方案,这我不可能。我只想确保通过使用其他语言(如c ++)来访问插件界面。

Delphi界面

IPlugIn = interface
  function GetName: WideString; stdcall;
end;

Delphi插件调用

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

  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

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

// TMyPlugIn标题

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

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插件的导出函数

extern "C" bool __declspec(dllexport) __stdcall LoadPlugIn(IPlugIn* PlugIn);
bool __declspec(dllexport) __stdcall LoadPlugIn(IPlugIn* PlugIn)
{
 PlugIn = new TMyPlugIn;
 return TRUE; 
}

4 个答案:

答案 0 :(得分:4)

您获得了访问冲突,因为此代码

extern "C" bool __declspec(dllexport) __stdcall LoadPlugIn(IPlugIn* PlugIn);
bool __declspec(dllexport) __stdcall LoadPlugIn(IPlugIn* PlugIn)
{
 PlugIn = new TMyPlugIn;
 return TRUE; 
}

创建一个插件类的实例,并将地址写入堆栈,很快就会忘记它。回到Delphi程序,原始插件接口变量仍为nil,因此调用它上面的方法会崩溃。你需要模仿QueryInterface()的作用,如下所示:

extern "C" bool __declspec(dllexport) __stdcall LoadPlugIn(IPlugIn** PlugIn);
bool __declspec(dllexport) __stdcall LoadPlugIn(IPlugIn** PlugIn)
{
  *PlugIn = new TMyPlugIn;
  return TRUE; 
}

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

答案 1 :(得分:2)

除了mghie所说的,还有Delphi和C ++之间定义不匹配的问题

GetName的C ++签名是:

 void _stdcall GetName(BSTR* result);

您的Delphi签名是:

  function GetName: WideString; stdcall;

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

1)如果你想让Delphi代码作为一个函数工作,那就让它安全地调用并调整C ++来匹配:

的Delphi:

  function GetName: WideString; safecall;

C ++:

  HRESULT _stdcall GetName(BSTR* result);

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

  procedure GetName( var name: WideString );

我(个人)可能会选择安全通道,因为我觉得它在德尔福方面更清洁......

答案 2 :(得分:1)

通常,您不应跨DLL边界导出接口(并且就此而言:实际上不应导出对象),因为您不知道哪一个内存管理器,运行时库和对象模型都在哪一边。 / p>

另见this thread about exceptions in DLL's(例外是对象)。

由于Delphi接口模型与COM接口模型以及Visual C++ can export COM objects二进制兼容,因此您应该采用COM方式(也建议Adelf)。

- 的Jeroen

答案 3 :(得分:-1)

所有Delphi类父 - 来自VCL的TObject类。 如果你使用Borland C ++ Builder(VCL库) - 你可以用这种方式编写插件到Delphi。

对于其他情况......你应该阅读有关COM的内容。