NN – WordPress

October 22, 2008

COM and sessions

Filed under: com — by NN @ 8:24 pm

It turns out that you cannot use COM server running in one session from COM client in other session.

Also you cannot use COM server from other user when you use No registry COM approach.

What can you do ?

One solution is to marshal and unmarshal interface in a global memory.

If you can read russian you can read it here and if you don’t read russian here :)

In short the approach is simple: It creates a global shared memory with access to all, calls CoMarshalInterface in the server side, then in the client side it calls CoUnmarshalInterface and retrieves IClassFactory.

This should work for multiple users and sessions. But requires some code change, you cannot call the regular CoCreateInstance.

 

The other solution is to use Session Monikers, but it works only for Windows 2000 and above, of course you do not have sessions in Windows 9x so don’t panic :)

In this approach you can specify a specific session which you are interested in.

 

The last stage is to make this shitness work.

Enjoy with COM.

October 3, 2008

Registration free COM Part 2

Filed under: Uncategorized — by NN @ 6:56 am

There are several issues I did not mention.

One of them is IDispatch support.

Can IDispatch work without registration ? The answer is yes, of course.

Now I will talk about ATL::IDispatchImpl class for implementing IDispatch. I am not going to write about whole investigation, just the points to understand it.

At first use it didn’t work.

How come ? The answer is simple if we look for IDispatchImpl declaration:
 

template <class T, const IID* piid = &__uuidof(T), const GUID* plibid = &CAtlModule::m_libid, WORD wMajor = 1,
WORD wMinor = 0, class tihclass = CComTypeInfoHolder>
class ATL_NO_VTABLE IDispatchImpl : public T

 

What is it CComTypeInfoHolder ? It is a helper class for providing information about type library.

In GetTI method it calls LoadRegTypeLib which goes to Registry!
 

hRes = LoadRegTypeLib(*m_plibid, m_wMajor, m_wMinor, lcid, &pTypeLib);

  

Hmm, the solution is simple, write our NoRegistryComTypeInfoHolder which will call LoadTypeLib and load type library from the file instead of using the registry.

Btw, ATL uses TLB from resource if possible, so you do not have to change anything in this case.

 

The tricky part to implement our class is the strange code in ATL::IDispatchImpl:
 

#ifdef _ATL_DLL_IMPL
  // Do not cache type info if it is used in atl71.dll
  IDispatchImpl() : _tih(piid, plibid, wMajor, wMinor)
  {
  }
 

  virtual ~IDispatchImpl()
  {
  }

protected:
  _tihclass _tih;
  HRESULT GetTI(LCID lcid, ITypeInfo** ppInfo)
  {
    return _tih.GetTI(lcid, ppInfo);
  }

#else

protected:
  static _tihclass _tih;
  static HRESULT GetTI(LCID lcid, ITypeInfo** ppInfo)
  {
    return _tih.GetTI(lcid, ppInfo);
  }

#endif

};

#ifndef _ATL_DLL_IMPL

template <class T, const IID* piid, const GUID* plibid, WORD wMajor, WORD wMinor, class tihclass>
typename IDispatchImpl<T, piid, plibid, wMajor, wMinor, tihclass>::_tihclass
IDispatchImpl<T, piid, plibid, wMajor, wMinor, tihclass>::_tih =
{piid, plibid, wMajor, wMinor, NULL, 0, NULL, 0};

#endif

WTF ??

It seems that our class must not implement constructor unless _ATL_DLL_IMPL is defined and then it must implement it. In addition the class must have only POD types because of initialization list when _ATL_DLL_IMPL is not defined.

Here a mess comes over. We need an additional function in our class to set TLB path varibale.
Again we cannot use object in our class :(
I use ATL::CComTypeInfoHolder to decrease coding.
Here comes a class:


struct NoRegistryComTypeInfoHolder
{
  // Aggregate this first to not break the compilation
  CComTypeInfoHolder type_holder_;

  // Our data
  ATL::CComBSTR* tlb_path_;

#ifdef _ATL_DLL_IMPL
  // Constructor
  NoRegistryComTypeInfoHolder(const GUID* pguid, const GUID* plibid, WORD wMajor, WORD wMinor)
  : type_holder_(pguid, plibid, wMajor, wMinor)
  {
  }
#endif // _ATL_DLL_IMPL

  HRESULT EnsureTI(LCID lcid)
  {
    HRESULT hr = S_OK;
    if (m_pInfo == NULL || m_pMap == NULL)
      hr = GetTI(lcid);
    return hr;
  }

  HRESULT GetTI(LCID lcid)
  {
    // Here comes almost the same code as CComTypeInfoHolder but we use LoadTypeLib
    // ...

    // Do not forget to add our Cleanup function to term functions.

    // Path to TLB wasn't set !!!
    if(!tlb_path_)
      return E_FAIL;
  }

  void SetTLBPath(ATL::CComBSTR tlb_path)
  {
    delete tlb_path_;
    tlb_path_ = new ATL::CComBSTR(tlb_path);
  }

private:
  // This function is called by the module on exit
  // It is registered through _pAtlModule->AddTermFunc()
  static void __stdcall Cleanup(DWORD_PTR dw)
  {
    ATLASSERT(dw != 0);
    if (dw == 0)
      return;

    NoRegistryComTypeInfoHolder* p = reinterpret_cast dw;
    delete p->tlb_path_;
    tlb_path_ = NULL;
  }
public:

// Delegate calls
  HRESULT Invoke(IDispatch* p, DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr)
  {
    // We must call our EnsureTI
    HRESULT hRes = EnsureTI(lcid);
    if (m_pInfo != NULL)
      return type_holder_.Invoke(p, dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexceptinfo, puArgErr);
    return hRes;
  }

  // ... There are some other functions to delegate
};

Now use it instead of the default class and you have IDispatch without need for registry but you must supply tlb file.

Just great !! You can use your code with IDispatch without any problem.  TLB is not really needed for implementing IDispatch but it makes life so simple !

Powered by WordPress.com