DispInvoke() returns E_POINTER

DispInvoke() returns E_POINTER

本文关键字:POINTER returns DispInvoke      更新时间:2023-10-16

我试图创建一个可以从MATLAB调用的COM类,这意味着使用自动化作为64位MATLAB不支持"自定义接口"。当涉及到c++和COM时,我有点生疏,所以请原谅…

我已经或多或少地了解了所有注册表的东西和带有类型库的进程内服务器所需的标准DLL例程,并实现了IUnknown, IDispatch和一个额外的测试方法(见下面的代码)。

我可以在MATLAB中创建类的实例,但当测试方法TwainerNull()被调用时,我在控制台上得到"无效指针"消息。当然,很难知道MATLAB内部发生了什么,所以我尝试直接从C程序中使用类,其中涉及:

  1. 调用oleinitialize()和CoCreateInstance()来获取IUnknown。
  2. 通过IUnknown->QueryInterface()获取IDispatch
  3. 通过IDispatch按名称获取测试方法的DISPID ->GetIDsOfNames()
  4. 输入DISPID到IDispatch->Invoke()

结果是一样的,上面的第4步返回E_POINTER。步骤4的代码如下:

result = dispatch->lpVtbl->Invoke( dispatch, id, &IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL );

Dispatch::Invoke的MSDN页面表明最后三个参数可以为NULL,尽管如果正确使用它们可以得到相同的结果。如果还不是很明显,id是从GetIDsOfNames()中获得的,并且params都是零,因为测试方法没有参数,也不返回任何东西。

我99%肯定失败是在某种程度上我使用/滥用了disinvoke(),因为它是在ITwainer::Invoke中调用disinvoke(),下面返回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 myself(只是打开dispIdMember来直接调用方法),而不是遵从dispidinvoke()。这对于上面描述的类的简单C使用来说足够好,但会导致MATLAB爆炸…大概要正确实现IDispatch::Invoke比switch语句要复杂得多!

对于正确使用disinvoke()的任何建议或适当参考材料的链接将非常感谢。已经阅读了MSDN页面和http://thrysoee.dk/InsideCOM+/ch05c.htm以及许多其他类似的例子。请不要建议使用VisualStudio或类似的魔法,下次我知道它隐藏了什么,我会使用whiz-bang工具X。谢谢!

终于想通了…实现看起来足够正确,但TwainerNull()不在编译器生成的虚拟表中。显然,disinvoke()是为使用"双接口"对象而设计的,也就是说,它使用类似于->lpVtbl的东西来调用成员。

为了将TwainerNull()放入虚拟表中,我定义了一个派生自IDispatch的虚拟类,然后从这个虚拟类派生出ITwainer的实现。

class foo:IDispatch
{
    virtual HRESULT __stdcall TwainerNull() = 0;
}
ITwainer:foo
{
    /* method implementations */
}

似乎工作,但看起来有点无聊。正如问题中所述,我是c++的新手(来自C)。如果有更干净的方法来实现这一点,我将洗耳恭听!