Delphi: вызов функции из DLL vc ++, которая экспортирует интерфейс / класс - PullRequest
1 голос
/ 15 февраля 2010

У меня возникли проблемы с доступом к DLL, написанной на vc ++, которая экспортирует интерфейс. Сначала я попытался использовать классы, но после некоторого google-поиска я пришел к решению, что это невозможно. Я просто хочу убедиться, что к интерфейсу плагина можно получить доступ, используя другие языки, такие как c ++.

Delphi Interface

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

Delphi Plugin call

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 ]

4 голосов
/ 16 февраля 2010

Вы получаете нарушение прав доступа, потому что этот код

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

Это передает адрес переменной интерфейса, и адрес экземпляра плагина будет записан в переменную.

2 голосов
/ 16 февраля 2010

В дополнение к тому, что сказал mghie, у вас также есть проблема с несовпадающими определениями между Delphi и C ++

Ваша подпись C ++ для GetName:

 void _stdcall GetName(BSTR* result);

Ваша подпись Delphi:

  function GetName: WideString; stdcall;

Есть (как минимум) 2 возможных способа исправить это.

1) Если вы хотите, чтобы код Delphi работал как функция, сделайте его безопасным и настройте C ++ так, чтобы он соответствовал:

Delphi:

  function GetName: WideString; safecall;

C ++:

  HRESULT _stdcall GetName(BSTR* result);

или

2) исправьте Delphi в соответствии с существующим определением C ++:

  procedure GetName( var name: WideString );

Я (лично), вероятно, пошел бы по безопасному маршруту, так как я думаю, что на стороне Delphi он намного чище ...

1 голос
/ 15 февраля 2010

В общем, вы не должны экспортировать интерфейсы (и в этом отношении: объекты действительно не должны экспортироваться) через границы DLL, потому что вы не знаете, какой менеджер памяти, библиотека времени выполнения и объектная модель будут с обеих сторон.

См. Также эту ветку об исключениях в DLL (исключения являются объектами).

Поскольку модель интерфейса Delphi двоично совместима с моделью интерфейса COM, и Visual C ++ может экспортировать объекты COM * , вам следует пойти по пути COM (также было предложено Adelf ).

- Йерун

0 голосов
/ 15 февраля 2010

Все родительские классы Delphi - класс TObject из VCL. Если вы используете Borland C ++ Builder (библиотека VCL) - вы можете написать плагин для Delphi следующим образом.

Для других случаев .. вы должны прочитать о COM.

...