我试图创建一个可以从MATLAB调用的COM类,这意味着使用自动化,因为64位MATLAB不支持“自定义接口”。当谈到C ++和COM时,我有点绿,所以请耐心等待......
或多或少地了解了具有类型库的进程内服务器所需的所有注册表内容和标准DLL例程,并实现了IUnknown,IDispatch和其他测试方法(请参阅下面的代码)。
我可以在MATLAB中创建该类的实例但是当调用测试方法TwainerNull()时,我在控制台上得到“无效指针”消息。当然很难知道MATLAB中发生了什么,所以我尝试直接从C程序中使用该类,这涉及:
结果相同,上面的步骤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,但如果我正确使用它们会得到相同的结果。如果它不是很明显,则从GetIDsOfNames()获取id,并且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.谢谢!
答案 0 :(得分:0)
最终弄明白了...实现看起来足够正确但是TwainerNull()不在编译器生成的虚拟表中。 DispInvoke()显然是为“使用双接口”对象设计的,也就是说使用类似这样的东西 - > lpVtbl来调用成员。
为了将TwainerNull()放入虚拟表,我定义了从IDispatch派生的虚拟类,然后从该虚拟类派生了ITwainer的实现。
class foo:IDispatch
{
virtual HRESULT __stdcall TwainerNull() = 0;
}
ITwainer:foo
{
/* method implementations */
}
似乎工作,但看起来有点naff。正如问题中所述,我对C ++(来自C)很陌生。如果有一种更清洁的方法来实现这一点,我就是全部的耳朵!