开发者

Delphi: Calling a function from a vc++ dll that exports a interface / class

i have some trouble accessing a dll written in vc++ that exports an interface. First i tried to use classes, but after some google-search i came to the solution, that this i not possible. I just want to make sure, that the plugin interface can accessed, by using other languages like c++.

Delphi Interface

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

Delphi Plugin call

procedure TForm1.Button开发者_开发技巧5Click(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 plugin 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++ plugin dll

// IPlugIn

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

// TMyPlugIn header

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());
}

// the export function from the cpp plugin

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


You get the access violation because this code

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

creates an instance of your plugin class and writes the address to the stack, where it quickly will be forgotten. Back in the Delphi program the original plugin interface variable is still nil, so calling a method on it crashes. You need to mimic what QueryInterface() does, like so:

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

This passes the address of the interface variable, and the address of the plugin instance will be written to the variable.


In addition to what mghie has said, you also have a problem with mismatched definitions between Delphi and C++

Your C++ signature for GetName is:

 void _stdcall GetName(BSTR* result);

Your Delphi signature is:

  function GetName: WideString; stdcall;

There are (at least) 2 possible ways to fix this.

1) If you want the Delphi code to work as a function, then make it safecall and adjust the C++ to match:

Delphi:

  function GetName: WideString; safecall;

C++:

  HRESULT _stdcall GetName(BSTR* result);

or

2) fix the Delphi to match the existing C++ defn:

  procedure GetName( var name: WideString );

I (personally) would probably go the safecall route, as I think it is much cleaner on the Delphi side...


In general, you should not export interfaces (and for that matter: objects should really not be exported) across DLL boundaries because you do not know which memory manager, run-time library and object model will be on either side.

See also this thread about exceptions in DLL's (exceptions are objects).

Since the Delphi interface model is binary compatible with the COM interface model, and Visual C++ can export COM objects, you should go the COM way (was Adelf also suggested).

--jeroen


All Delphi classes parent - TObject class from VCL. If you use Borland C++ Builder(VCL library) - you can write plugin to Delphi with this way.

For another cases.. you should read about COM.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜