Re: Connection point interface failing miserably

Jon Kale <[email protected]> Thu, 20 Feb 2003 15:53:05 -0000
Newsgroups gmane.comp.windows.devel.vbcom
Message-ID <[email protected]>
VB generally gets *very* unhappy if you fire an event on a thread other
than the one that the initial registration was on. Why? Well, VB likes
to be in an STA, which means that your interface pointer is
apartment-bound: you've got to marshall the relevant interface pointer
across apartments to the calling thread before calling on it. There's
two ways of doing this: the complicated, NT-only way involves the GIT
(the global interface table) while the simple, 9x-and-NT way involves
posting messages from your event-raising thread to a hidden window. See
Q206076 and Q196026 respectively for details
--
Jon

> -----Original Message-----
> From: Magnus Werner [mailto:[email protected]]=20
> Sent: 20 February 2003 3:08 pm
> To: [email protected]
> Subject: [VBCOM] Connection point interface failing miserably
>=20
>=20
> I've implemented a simple ActiveX COM server using ATL. It=20
> implements a connection point interface that will be used to=20
> call back to a VB client. For some reason (that I'd very much=20
> like to know) there is a catastrophic failure when firing the=20
> event. The pDispatch->Invoke causes an Access violation in=20
> VB6. Why is that? I'd be most grateful for any suggestions=20
> how to solve this.
>=20
> Regards,
> Magnus Werner
>=20
> // IDLTest.idl : IDL source for IDLTest.dll
> //
>=20
> // This file will be processed by the MIDL tool to
> // produce the type library (IDLTest.tlb) and marshalling code.
>=20
> import "oaidl.idl";
> import "ocidl.idl";
>  [
>   object,
>   uuid(80C10722-4741-4EEC-A1C8-8DC4A97159EF),
>   dual,
>   helpstring("IExpImp Interface"),
>   pointer_default(unique)
>  ]
>  interface IExpImp : IDispatch
>  {
>   [id(1), helpstring("method Export")] HRESULT Export([in]=20
> short Delay);
>   [id(2), helpstring("method Cancel")] HRESULT Cancel();
>  };
>=20
> [
>  uuid(152812E3-0A28-42DB-8F22-1A5C75CF01CB),
>  version(1.0),
>  helpstring("IDLTest 1.0 Type Library")
> ]
> library IDLTESTLib
> {
>  importlib("stdole32.tlb");
>  importlib("stdole2.tlb");
>=20
>  [
>   uuid(A006CA98-3C7D-441A-865E-F64C42B4B0A6),
>   helpstring("IExpImpEvents Interface")
>  ]
>  dispinterface IExpImpEvents
>  {
>   properties:
>   methods:
>   [id(1), helpstring("method OnProgress")] HRESULT OnProgress=20
> ([in] long Progress);
>   [id(2), helpstring("method OnComplete")] HRESULT OnComplete ();
>   [id(3), helpstring("method OnCancel")] HRESULT OnCancel();
>  };
>=20
>  [
>   uuid(1EE8B0E4-C43F-45C7-BD94-D50809E47BB9),
>   helpstring("ExpImp Class")
>  ]
>  coclass ExpImp
>  {
>   [default] interface IExpImp;
>   [default, source] dispinterface IExpImpEvents;
>  };
> };
>=20
> =
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
>=20
> // ExpImp.h : Declaration of the CExpImp
>=20
> #ifndef __EXPIMP_H_
> #define __EXPIMP_H_
>=20
> #include "resource.h"       // main symbols
> #include "IDLTestCP.h"
>=20
> //////////////////////////////////////////////////////////////
> //////////////
> /
> // CExpImp
> class ATL_NO_VTABLE CExpImp :
>  public CComObjectRootEx<CComSingleThreadModel>,
>  public CComCoClass<CExpImp, &CLSID_ExpImp>,
>  public ISupportErrorInfo,
>  public IConnectionPointContainerImpl<CExpImp>,
>  public IDispatchImpl<IExpImp, &IID_IExpImp,=20
> &LIBID_IDLTESTLib>,  public CProxy_IExpImpEvents< CExpImp >, =20
> public IProvideClassInfo2Impl< &CLSID_ExpImp,=20
> &DIID_IExpImpEvents, &LIBID_IDLTESTLib, 1, 0 > {
> public:
>  CExpImp()
>  {
>  }
>=20
> DECLARE_REGISTRY_RESOURCEID(IDR_EXPIMP)
>=20
> DECLARE_PROTECT_FINAL_CONSTRUCT()
>=20
> BEGIN_COM_MAP(CExpImp)
>  COM_INTERFACE_ENTRY(IExpImp)
>  COM_INTERFACE_ENTRY(IDispatch)
>  COM_INTERFACE_ENTRY(ISupportErrorInfo)
>  COM_INTERFACE_ENTRY(IConnectionPointContainer)
>  COM_INTERFACE_ENTRY_IMPL(IConnectionPointContainer)
>  COM_INTERFACE_ENTRY(IProvideClassInfo2)
>  COM_INTERFACE_ENTRY(IProvideClassInfo)
> END_COM_MAP()
> BEGIN_CONNECTION_POINT_MAP(CExpImp)
> CONNECTION_POINT_ENTRY(DIID_IExpImpEvents)
> END_CONNECTION_POINT_MAP()
>=20
>=20
> // ISupportsErrorInfo =20
> STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);
>=20
> // IExpImp
> public:
>  STDMETHOD(Cancel)();
>  STDMETHOD(Export)(/*[in]*/ short Delay);
>=20
>  void run();
>=20
> private:
>  short m_sDelay;
>  HANDLE m_hThread;
>  DWORD m_dwThreadId;
>  bool m_bCanceled;
> };
>=20
> #endif //__EXPIMP_H_=20
> =
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
>=20
> // ExpImp.cpp : Implementation of CExpImp
> #include "stdafx.h"
> #include "IDLTest.h"
> #include "ExpImp.h"
>=20
> DWORD WINAPI ThreadProc(
>   LPVOID lpParameter   // thread data
> )
> {
>  CExpImp *pCExpImp =3D static_cast<CExpImp*>(lpParameter);
>=20
>  pCExpImp->run();
>=20
>  return 0; // Thread exit code
> }
>=20
> //////////////////////////////////////////////////////////////
> //////////////
> /
> // CExpImp
>=20
> STDMETHODIMP CExpImp::InterfaceSupportsErrorInfo(REFIID riid)
> {
>  static const IID* arr[] =3D
>  {
>   &IID_IExpImp
>  };
>  for (int i=3D0; i < sizeof(arr) / sizeof(arr[0]); i++)
>  {
>   if (InlineIsEqualGUID(*arr[i],riid))
>    return S_OK;
>  }
>  return S_FALSE;
> }
>=20
> STDMETHODIMP CExpImp::Export(short Delay)
> {
>  m_bCanceled =3D false;
>  m_hThread =3D ::CreateThread(NULL,
>   // Default security descriptor
>          0,
>       // Default stack size
>=20
> &ThreadProc,    // Thread start routing
>=20
> static_cast<LPVOID>(this), // Arguments to start routine
>          0,
>       // Creation flags
>=20
> &m_dwThreadId);
>  if (m_hThread =3D=3D NULL)
>   return HRESULT_FROM_WIN32(GetLastError());
>=20
>  return S_OK;
> }
>=20
> void CExpImp::run()
> {
>  CoInitialize(0);
>  for (int i=3D0; i<101; i++)
>  {
>   if (m_bCanceled)
>   {
>    HRESULT hr =3D Fire_OnCancel();
>    CoUninitialize();
>    ::ExitThread(1);
>   }
>   HRESULT hr =3D Fire_OnProgress(i);
>  }
>=20
>  HRESULT hr =3D Fire_OnComplete();
>  CoUninitialize();
> }
>=20
> STDMETHODIMP CExpImp::Cancel()
> {
>  m_bCanceled =3D true;
>=20
>  return S_OK;
> }=20
> =
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
>=20
> Private WithEvents ProgressHandler As IDLTESTLib.ExpImp
> Private Sub CancelButton_Click()
>     ProgressHandler.Cancel
> End Sub
>=20
> Private Sub Form_Load()
>     On Error GoTo error
>     Set ProgressHandler =3D New ExpImp
>     Exit Sub
> error:
>     MsgBox Err.Description
> End Sub
>=20
> Private Sub OKButton_Click()
>    ProgressHandler.Export 2
>    'For i =3D 1 To 100
>    '     ProgressBar1.Value =3D i
>    'Next
> End Sub
>=20
> Private Sub ProgressHandler_OnCancel()
>     'TBD
> End Sub
>=20
> Private Sub ProgressHandler_OnComplete()
>     'TBD
> End Sub
>=20
> Private Sub ProgressHandler_OnProgress(ByVal Progress As Long)
>     ProgressBar1.Value =3D Progress
> End Sub
>=20
> You can read messages from the VBCOM archive, unsubscribe=20
> from VBCOM, or subscribe to other DevelopMentor lists at=20
> http://discuss.develop.com.
>=20

You can read messages from the VBCOM archive, unsubscribe from VBCOM, or
subscribe to other DevelopMentor lists at http://discuss.develop.com.