我在访问用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;
}发布于 2010-02-15 22:19:14
您会得到访问冲突,因为以下代码
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;
}这将传递接口变量的地址,插件实例的地址将被写入变量。
发布于 2010-02-16 10:18:43
除了mghie说过的话,您还遇到了德尔福和C++之间定义不匹配的问题。
您的C++ GetName签名是:
void _stdcall GetName(BSTR* result);你的特尔菲签名是:
function GetName: WideString; stdcall;(至少)有两种可能的方法来解决这个问题。
1)如果您希望Delphi代码作为一个函数工作,那么确保它的安全性,并调整C++以匹配:
德尔菲:
function GetName: WideString; safecall;C++:
HRESULT _stdcall GetName(BSTR* result);或
2)修复Delphi以匹配现有的C++ defn:
procedure GetName( var name: WideString );我(个人)可能会走安全路线,因为我认为在德尔菲那边更干净.
发布于 2010-02-15 15:14:27
通常,您不应该跨DLL边界导出接口(实际上不应该导出对象),因为您不知道哪个内存管理器、运行时库和对象模型将位于任何一方。
也请参见这个关于DLL中异常的线程 (异常是对象)。
由于Delphi接口模型与COM接口模型和Visual C++可以导出COM对象。是二进制兼容的,所以您应该采用COM方式(was 阿德拉夫也建议)。
-耶伦
https://stackoverflow.com/questions/2266417
复制相似问题