【发布时间】:2017-01-20 23:56:13
【问题描述】:
我正在尝试创建一个可以从 MATLAB 调用的 COM 类,这意味着使用自动化作为 64 位 MATLAB 不支持“自定义接口”。我对 C++ 和 COM 都有些陌生,所以请多多包涵……
已经或多或少地了解了具有类型库的进程内服务器所需的所有注册表内容和标准 DLL 例程,并实现了 IUnknown、IDispatch 和其他测试方法(参见下面的代码)。
我可以在 MATLAB 中创建该类的实例,但是当调用测试方法 TwainerNull() 时,我在控制台上收到“无效指针”消息。当然,很难知道 MATLAB 内部发生了什么,所以我尝试直接从 C 程序中使用该类,这涉及到:
- 调用 OleInitialise() 和 CoCreateInstance() 以获取 IUnknown。
- 通过 IUnknown->QueryInterface() 获取 IDispatch
- 通过IDispatch->GetIDsOfNames()按名称获取测试方法的DISPID
- 将所述 DISPID 输入 IDispatch->Invoke()
结果一样,上面第4步返回E_POINTER。第4步的代码如下:
result = dispatch->lpVtbl->Invoke( dispatch, id, &IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, ¶ms, NULL, NULL, NULL );
Dispatch::Invoke 的 MSDN 页面表明最后三个参数可以为 NULL,但如果我正确使用它们,我会得到相同的结果。如果还不是很明显,id 是从 GetIDsOfNames() 获得的,params 是全零,因为测试方法没有参数并且不返回任何内容。
我 99% 确定失败是由于我使用/滥用 DispInvoke() 造成的,因为它是在 ITwainer::Invoke 中对 DispInvoke() 的调用,它正在返回 E_POINTER。我可以在 C 客户端中检索一个有效的 ITypeInfo 并在其中找到 IUnknown、IDispatch 和测试方法 TwainerNull 的所有方法。 MATLAB 似乎也能正确获取类型信息。
类实现如下。除非我遗漏了什么,否则它几乎完全是来自http://thrysoee.dk/InsideCOM+/ 和 MSDN 的示例代码。 '因为缺乏评论,工作正在进行中。
ITwainer::ITwainer( void )
{
RefCount = 1;
ITypeLib *DispatchTypeLib;
HRESULT result;
result = LoadTypeLib( L"a:\\trabajo\\twain\\twainer.tlb", &DispatchTypeLib );
if ( result != S_OK ) throw result;
result = DispatchTypeLib->GetTypeInfoOfGuid( IID_ITwainer, &DispatchTypeInfo );
DispatchTypeLib->Release();
if ( result != S_OK ) throw result
}
HRESULT __stdcall ITwainer::QueryInterface( REFIID riid, void **ppvObject )
{
/* input sanitation */
if ( !ppvObject ) return E_POINTER;
if ( IsEqualGUID( riid, IID_IUnknown ) ||
IsEqualGUID( riid, IID_IClassFactory ) ||
IsEqualGUID( riid, IID_IDispatch ) ||
IsEqualGUID( riid, IID_ITwainer ) )
{
*ppvObject = this;
return S_OK;
}
else
{
*ppvObject = NULL;
return E_NOINTERFACE;
}
}
ULONG __stdcall ITwainer::AddRef( void )
{
return RefCount++;
}
ULONG __stdcall ITwainer::Release( void )
{
RefCount--;
if ( RefCount < 0 ) RefCount = 0;
if ( RefCount == 0 )
{
/* FIXME this seems to happen to soon and cause explosions */
//delete this;
return 1;
}
else
{
return RefCount;
}
}
HRESULT __stdcall ITwainer::GetTypeInfoCount( UINT *pCountTypeInfo )
{
*pCountTypeInfo = 1;
return S_OK;
}
HRESULT __stdcall ITwainer::GetTypeInfo( UINT iTypeInfo, LCID lcid, ITypeInfo **ppITypeInfo )
{
if ( iTypeInfo != 0 ) return DISP_E_BADINDEX;
if ( !ppITypeInfo ) return E_POINTER;
DispatchTypeInfo->AddRef();
*ppITypeInfo = DispatchTypeInfo;
return S_OK;
}
HRESULT __stdcall ITwainer::GetIDsOfNames( REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId )
{
if ( !IsEqualGUID( riid, IID_NULL ) ) return DISP_E_UNKNOWNINTERFACE;
return DispGetIDsOfNames( DispatchTypeInfo, rgszNames, cNames, rgDispId );
}
HRESULT __stdcall ITwainer::Invoke( DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr )
{
if ( !IsEqualGUID( riid, IID_NULL ) ) return DISP_E_UNKNOWNINTERFACE;
return DispInvoke( this, DispatchTypeInfo, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr );
}
HRESULT __stdcall ITwainer::TwainerNull()
{
MessageBox( NULL, L"Foo", L"TwainerNull", 0 );
return S_OK;
}
上面的RefCount和DispatchTypeInfo是ITwainer的私有成员,TwainerNull()是没有参数或返回值的测试方法。类型库是从以下 IDL 编译的:
import "unknwn.idl";
import "oaidl.idl";
[ uuid(8A7FC6CF-5DCE-4c4f-B089-4C9EB0C4D868), version(0.1) ]
library Twainer
{
importlib("stdole2.tlb");
[ object, uuid(D2CE5EBB-9C5D-4101-B660-51BA4F62EA7B), dual ]
interface ITwainer : IDispatch {
HRESULT TwainerNull();
};
}
最后,我自己实现了 ITwainer::Invoke(只是在 dispIdMember 上的一个开关以直接调用该方法)而不是推迟到 DispInvoke()。这对于上面描述的类的简单 C 使用来说足够好,但会导致 MATLAB 爆炸......大概要正确实现 IDispatch::Invoke 而不是 switch 语句!
对于正确使用 DispInvoke() 的任何建议或指向适当参考资料的链接将不胜感激。已阅读 MSDN 页面和 http://thrysoee.dk/InsideCOM+/ch05c.htm 以及许多其他类似示例。请不要建议使用 VisualStudio 或类似的东西一起使用魔法,一旦我知道它对我隐藏了什么,下次将使用 whiz-bang 工具 X。谢谢!
【问题讨论】: