我在访问用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;
}
答案 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的内容。