0
votes

Am trying to create a COM class which can be called from MATLAB, which means using automation as 64-bit MATLAB does not support 'custom interfaces'. I'm a bit green when it comes to both C++ and COM, so please bear with...

Have more or less got my head around all the registry stuff and standard DLL routines required for an in-process server with a type library and have implemented IUnknown, IDispatch, and an additional test method (see code below).

I can create an instance of the class in MATLAB but when the test method TwainerNull() is invoked I get 'Invalid Pointer' message on the console. Of course it's hard to know what happens inside MATLAB so I went through the exercise of trying to use the class directly from a C program, which involved:

  1. Calling OleInitialise() and CoCreateInstance() to get an IUnknown.
  2. Getting an IDispatch via IUnknown->QueryInterface()
  3. Getting the DISPID of the test method by name via IDispatch->GetIDsOfNames()
  4. Feeding said DISPID into IDispatch->Invoke()

The result is the same, step 4 above returns E_POINTER. The code for step 4 is as follows:

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

The MSDN page for Dispatch::Invoke indicates that the last three parameters can be NULL although I get the same result if I use them properly. If it's not already obvious, id is obtained from GetIDsOfNames() and params is all zeros as the test method has no arguments and returns nothing.

I am 99% certain that the failure is somehow in my use/abuse of DispInvoke() as it is the call to DispInvoke() in ITwainer::Invoke below which is returning E_POINTER. I can retrieve valid a ITypeInfo in the C client and find all the methods of IUnknown, IDispatch, and the test method TwainerNull therein. MATLAB also appears to get the type information correctly.

The class implementation is below. It's pretty much entirely the example code from http://thrysoee.dk/InsideCOM+/ and MSDN unless I've missed something. 'scuse the lack of commenting, is a work in progress.

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;                                                                                                                                                  
}                                                                                                                                                               

In the above RefCount and DispatchTypeInfo are private members of ITwainer and TwainerNull() is the test method with no parameters or return value. The type library was compiled from the following 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();                                                                                                                                    
    };                                                                                                                                                          
}                                                                                                                                                               

Finally, I have had a play with implementing ITwainer::Invoke myself (just a switch on dispIdMember to call the method directly) rather than deferring to DispInvoke(). This works well enough for the simple C use of the class described above but causes MATLAB to explode... Presumably there is much more to implementing IDispatch::Invoke correctly than a switch statement!

Any advice or links to appropriate reference material for the correct use of DispInvoke() would be greatly appreciated. Have read the MSDN page and http://thrysoee.dk/InsideCOM+/ch05c.htm and many other similar examples. Please no suggestions to use VisualStudio or similar to magic it all together, will use whiz-bang tool X next time once I know what it is hiding from me. THanks!

1

1 Answers

0
votes

Have figured it out in the end... The implementation appears correct enough but TwainerNull() was not in the virtual table generated by the compiler. DispInvoke() is apparently designed for use "with dual interface" objects, which is to say that is uses something like this->lpVtbl to invoke members.

To get TwainerNull() into the virtual table, I defined virtual class which derives from IDispatch and then derived the implementation of ITwainer from that virtual class.

class foo:IDispatch
{
    virtual HRESULT __stdcall TwainerNull() = 0;
}

ITwainer:foo
{
    /* method implementations */
}

Seems to work, but looks a bit naff. As stated in the question, I am pretty new to C++ (coming from C). If there's a cleaner way to achieve this I'm all ears!