15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту


IDispatch::Invoke not using the apartment

ens@ORION.cb.att.com
Monday, January 13, 1997

Environment: MSVC 4.2-flat and MSVC 4.2b, Win NT4.0

Short version:
IDispatch::Invoke on an ActiveX control from a secondary thread is being
executed in the calling thread, not in the control's apartment.  Why?

Longer version:
1. I have two ActiveX controls: one created in the main thread as part
of a CFormView (the Viz control) and one created in a second UI thread
(the MThreadTest2 control).
2. the MThreadTest2 control calls the SetString method on the Viz
control.  In Viz::SetString, the Viz control fires the Bang event.  An
assertion failure results (see the call stack attached below).  

I have determined that Viz's SetString implementation is being executed
in the calling thread, and the event is being fired back into that
thread as well.  I thought that when MThreadTest2, running in a second
thread, made a call on Viz that call would be marshalled through to
Viz's apartment in the main thread.

Notes:
1. The MThreadTest2 bit may be superfluous information,  the same
behavior was observed when a thread message handler in the secondary
thread called Viz::SetString.  One tidbit of info that may be of
interest is that Viz's IDispatch pointer was passed from the main thread
to the secondary thread as the WPARAM of a posted thread message.  Do I
need to personally call CoMarshalInterface/CoUnMarshalInterface?
2. The second thread was created with ::AfxBeginThread and CRuntimeClass
of a CWinThread derivative.  I called ::AfxOleInit() from
CDataThread::InitInstance().

Any ideas?

Ed Schiebel
AT&T Network and Computing Services
eschiebel@att.com


AfxAssertFailedLine(char * 0x5f4cfd68, int 825) line 39 + 20 bytes
CWnd::AssertValid() line 825 + 71 bytes
CView::AssertValid() line 503
CFormView::AssertValid() line 307
CMTAfxView2::AssertValid() line 52
AfxAssertValidObject(const CObject * 0x004228f0 {CMTAfxView2
hWnd=0x0001064c}, char * 0x5f4d07f8, int 978) line 108
CDocument::AssertValid() line 979
COleDocument::AssertValid() line 772
CMTAfxDoc::AssertValid() line 115
AfxAssertValidObject(const CObject * 0x00422360 {CMTAfxDoc}, char *
0x5f5ae1d0, int 100) line 108
COleDocument::OnCmdMsg(unsigned int 1001, int -2, void * 0x018ef688,
AFX_CMDHANDLERINFO * 0x00000000) line 102
CView::OnCmdMsg(unsigned int 1001, int -2, void * 0x018ef688,
AFX_CMDHANDLERINFO * 0x00000000) line 171 + 33 bytes
COleControlSite::OnEvent(AFX_EVENT * 0x018ef688) line 875 + 38 bytes
COleControlSite::XEventSink::Invoke(COleControlSite::XEventSink * const
0x00422c60, long 1, const _GUID & {...}, const _GUID & {...}, unsigned
short 1, tagDISPPARAMS * 0x018ef760, tagVARIANT * 0x00000000,
tagEXCEPINFO * 0x018ef770, unsigned int * ...) lin
COleDispatchDriver::InvokeHelperV(long 1, unsigned short 1, unsigned
short 0, void * 0x00000000, unsigned char * 0x00000000, char *
0x018ef820) line 358 + 53 bytes
COleControl::FireEventV(long 1, unsigned char * 0x00000000, char *
0x018ef820) line 166 + 26 bytes
COleControl::FireEvent(COleControl * const 0x00301518 {CMTVizCtrl
hWnd=0x00000000}, long 1) line 177
CMTVizCtrl::FireBang() line 48 + 25 bytes
CMTVizCtrl::SetString(short 0, char * 0x00155a84) line 191
_AfxDispatchCall(void (void)* 0x013b7268, void (void)* 0x013b7268, void
(void)* 0x013b7268) line 43
COleDispatchImpl::Invoke(COleDispatchImpl * const 0x00301528, long 1,
const _GUID & {...}, unsigned long 0, unsigned short 1, tagDISPPARAMS *
0x018ef974, tagVARIANT * 0x00000000, tagEXCEPINFO * 0x018efa8c, unsigned
int * 0x018efa74) line 1401 + 28 bytes
COleDispatchDriver::InvokeHelperV(long 1, unsigned short 1, unsigned
short 0, void * 0x00000000, unsigned char * 0x018fa8a0, char *
0x018efb08) line 358 + 53 bytes
COleDispatchDriver::InvokeHelper(COleDispatchDriver * const 0x018efb38,
long 1, unsigned short 1, unsigned short 0, void * 0x00000000) line 493
DMTViz::SetString(short 0, char * 0x0042512c) line 25 + 31 bytes
CMThreadTest2::Update(IDispatch * 0x00301528) line 250
_AfxDispatchCall(void (void)* 0x018f8310, void (void)* 0x018f8310, void
(void)* 0x018f8310) line 43
COleDispatchImpl::Invoke(COleDispatchImpl * const 0x00301ba0, long 2,
const _GUID & {...}, unsigned long 0, unsigned short 1, tagDISPPARAMS *
0x018efc7c, tagVARIANT * 0x00000000, tagEXCEPINFO * 0x018efd7c, unsigned
int * 0x018efd64) line 1401 + 28 bytes
COleDispatchDriver::InvokeHelperV(long 2, unsigned short 1, unsigned
short 0, void * 0x00000000, unsigned char * 0x0040ab9c, char *
0x018efe28) line 358 + 53 bytes
COleControlSite::InvokeHelperV(long 2, unsigned short 1, unsigned short
0, void * 0x00000000, unsigned char * 0x0040ab9c, char * 0x018efe24)
line 908
CWnd::InvokeHelper(CWnd * const 0x00423b24 {CMThreadTest2
hWnd=0x00010652}, long 2, unsigned short 1, unsigned short 0, void *
0x00000000) line 358
CMThreadTest2::Update(IDispatch * 0x00301528) line 38 + 26 bytes
CDataThread::OnUpdate(unsigned int 3151144, long 0) line 77
CWinThread::DispatchThreadMessageEx(tagMSG * 0x00423a88 {msg=0x00000400
wp=0x00301528 lp=0x00000000}) line 652
CWinThread::PreTranslateMessage(tagMSG * 0x00423a88 {msg=0x00000400
wp=0x00301528 lp=0x00000000}) line 660 + 24 bytes
CWinThread::PumpMessage() line 859 + 34 bytes
CWinThread::Run() line 480 + 11 bytes





ens@ORION.cb.att.com
Wednesday, January 15, 1997

I have discovered the answer to my problem and in turn it introduced
another problem.  In short, one has to marshal interfaces between
threads, you can't just pass an IUnknown* as wParam.  If you are
interested, here is how (I believe) it's done.  Please tell me if I'm
wrong.

void CSomeClass::SendInterface()
{
    IUnknown* punk = m_someControl.GetControlUnknown();

    LONG putSize = 0;
    ::CoGetMarshalSizeMax(&putSize, IID_IUnknown, punk, MSHCTX_INPROC,
0, MSHLFLAGS_NORMAL);
    HGLOBAL hGlobal = ::GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT, putSize);

    IStream* pStream = 0;
    ::CreateStreamOnHGlobal(hGlobal, FALSE, &pStream)

    ::CoMarshalInterface(pStream, IID_IUnknown, punk, MSHCTX_INPROC, 0,
MSHLFLAGS_NORMAL);

    WPARAM wparam = reinterpret_cast(hGlobal);
    m_pAnotherThread->PostThreadMessage(WM_USER, wparam, 0);
}

Nowto unmarshal the interface on the other side:

void CMyThread::OnSomeMessage(WPARAM wparam, LPARAM /*ignored*/)
{
    HGLOBAL hGlobal = reinterpret_cast(wparam);
    IStream* pStream = 0;
    ::CreateStreamOnHGlobal(hGlobal, TRUE, &pStream);

    IDispatch* pdisp = 0;
    ::CoUnmarshalInterface(pStream, IID_IDispatch,
reinterpret_cast(&pdisp));
    pStream->Release();
    
    CSomeOleDispatchDriver d(pdisp);
    d.SomeMethod();		// executed in the other thread!
}

Notice that I marshaled IUnknown and unmarshaled IDispatch, pretty cool
huh? 
One important note: you have to pass the HGLOBAL via the thread message,
not the IStream* 
(since you'd have to marshal the IStream*.  I know this sounds obvious,
but...)

NEW PROBLEM!  
Everything was working great until... 
1. I created an object via CoCreateInstance and attached its IDispatch
to a COleDispatchDriver wrapper the class
wiz created for me.
2. I tried marshaling COleDispatchDriver::m_lpDispatch
3. When I unmarshal it (as either IDispatch or IUnknown), I get back an
IUnknown pointer that the MSVC variables window tells me is a
COleDispatchImpl (FYI, the IUnknown marshaled from the ole control shows
up as a vanilla IUnkown*)

Any functions called from this unmarshaled interface get executed in the
calling thread, not the objects home apartment.

I have tried taking COleDiapatchDriver::m_lpDispatch and
QueryInterfacing it for IID_IUnknown and marshaling that.
I have tried marshaling and unmarshaling as IDispatch and IUnknown.
I always get the same results.

What is being put on the Stream that carries (too much) type information
across thread boundaries?


Ed Schiebel
AT&T Network and Computing Services
eschiebel@att.com

>----------
>From: 	ens@edisun.cb.att.com[SMTP:ens@edisun.cb.att.com]
>Sent: 	Monday, January 13, 1997 3:30 PM
>To: 	'mfc-l'
>Subject: 	IDispatch::Invoke not using the apartment
>
>Environment: MSVC 4.2-flat and MSVC 4.2b, Win NT4.0
>
>Short version:
>IDispatch::Invoke on an ActiveX control from a secondary thread is being
>executed in the calling thread, not in the control's apartment.  Why?
>
>Longer version:
>1. I have two ActiveX controls: one created in the main thread as part
>of a CFormView (the Viz control) and one created in a second UI thread
>(the MThreadTest2 control).
>2. the MThreadTest2 control calls the SetString method on the Viz
>control.  In Viz::SetString, the Viz control fires the Bang event.  An
>assertion failure results (see the call stack attached below).  
>
>I have determined that Viz's SetString implementation is being executed
>in the calling thread, and the event is being fired back into that
>thread as well.  I thought that when MThreadTest2, running in a second
>thread, made a call on Viz that call would be marshalled through to
>Viz's apartment in the main thread.
>
>Notes:
>1. The MThreadTest2 bit may be superfluous information,  the same
>behavior was observed when a thread message handler in the secondary
>thread called Viz::SetString.  One tidbit of info that may be of
>interest is that Viz's IDispatch pointer was passed from the main thread
>to the secondary thread as the WPARAM of a posted thread message.  Do I
>need to personally call CoMarshalInterface/CoUnMarshalInterface?
>2. The second thread was created with ::AfxBeginThread and CRuntimeClass
>of a CWinThread derivative.  I called ::AfxOleInit() from
>CDataThread::InitInstance().
>
>Any ideas?
>
>Ed Schiebel
>AT&T Network and Computing Services
>eschiebel@att.com
>
>
>AfxAssertFailedLine(char * 0x5f4cfd68, int 825) line 39 + 20 bytes
>CWnd::AssertValid() line 825 + 71 bytes
>CView::AssertValid() line 503
>CFormView::AssertValid() line 307
>CMTAfxView2::AssertValid() line 52
>AfxAssertValidObject(const CObject * 0x004228f0 {CMTAfxView2
>hWnd=0x0001064c}, char * 0x5f4d07f8, int 978) line 108
>CDocument::AssertValid() line 979
>COleDocument::AssertValid() line 772
>CMTAfxDoc::AssertValid() line 115
>AfxAssertValidObject(const CObject * 0x00422360 {CMTAfxDoc}, char *
>0x5f5ae1d0, int 100) line 108
>COleDocument::OnCmdMsg(unsigned int 1001, int -2, void * 0x018ef688,
>AFX_CMDHANDLERINFO * 0x00000000) line 102
>CView::OnCmdMsg(unsigned int 1001, int -2, void * 0x018ef688,
>AFX_CMDHANDLERINFO * 0x00000000) line 171 + 33 bytes
>COleControlSite::OnEvent(AFX_EVENT * 0x018ef688) line 875 + 38 bytes
>COleControlSite::XEventSink::Invoke(COleControlSite::XEventSink * const
>0x00422c60, long 1, const _GUID & {...}, const _GUID & {...}, unsigned
>short 1, tagDISPPARAMS * 0x018ef760, tagVARIANT * 0x00000000,
>tagEXCEPINFO * 0x018ef770, unsigned int * ...) lin
>COleDispatchDriver::InvokeHelperV(long 1, unsigned short 1, unsigned
>short 0, void * 0x00000000, unsigned char * 0x00000000, char *
>0x018ef820) line 358 + 53 bytes
>COleControl::FireEventV(long 1, unsigned char * 0x00000000, char *
>0x018ef820) line 166 + 26 bytes
>COleControl::FireEvent(COleControl * const 0x00301518 {CMTVizCtrl
>hWnd=0x00000000}, long 1) line 177
>CMTVizCtrl::FireBang() line 48 + 25 bytes
>CMTVizCtrl::SetString(short 0, char * 0x00155a84) line 191
>_AfxDispatchCall(void (void)* 0x013b7268, void (void)* 0x013b7268, void
>(void)* 0x013b7268) line 43
>COleDispatchImpl::Invoke(COleDispatchImpl * const 0x00301528, long 1,
>const _GUID & {...}, unsigned long 0, unsigned short 1, tagDISPPARAMS *
>0x018ef974, tagVARIANT * 0x00000000, tagEXCEPINFO * 0x018efa8c, unsigned
>int * 0x018efa74) line 1401 + 28 bytes
>COleDispatchDriver::InvokeHelperV(long 1, unsigned short 1, unsigned
>short 0, void * 0x00000000, unsigned char * 0x018fa8a0, char *
>0x018efb08) line 358 + 53 bytes
>COleDispatchDriver::InvokeHelper(COleDispatchDriver * const 0x018efb38,
>long 1, unsigned short 1, unsigned short 0, void * 0x00000000) line 493
>DMTViz::SetString(short 0, char * 0x0042512c) line 25 + 31 bytes
>CMThreadTest2::Update(IDispatch * 0x00301528) line 250
>_AfxDispatchCall(void (void)* 0x018f8310, void (void)* 0x018f8310, void
>(void)* 0x018f8310) line 43
>COleDispatchImpl::Invoke(COleDispatchImpl * const 0x00301ba0, long 2,
>const _GUID & {...}, unsigned long 0, unsigned short 1, tagDISPPARAMS *
>0x018efc7c, tagVARIANT * 0x00000000, tagEXCEPINFO * 0x018efd7c, unsigned
>int * 0x018efd64) line 1401 + 28 bytes
>COleDispatchDriver::InvokeHelperV(long 2, unsigned short 1, unsigned
>short 0, void * 0x00000000, unsigned char * 0x0040ab9c, char *
>0x018efe28) line 358 + 53 bytes
>COleControlSite::InvokeHelperV(long 2, unsigned short 1, unsigned short
>0, void * 0x00000000, unsigned char * 0x0040ab9c, char * 0x018efe24)
>line 908
>CWnd::InvokeHelper(CWnd * const 0x00423b24 {CMThreadTest2
>hWnd=0x00010652}, long 2, unsigned short 1, unsigned short 0, void *
>0x00000000) line 358
>CMThreadTest2::Update(IDispatch * 0x00301528) line 38 + 26 bytes
>CDataThread::OnUpdate(unsigned int 3151144, long 0) line 77
>CWinThread::DispatchThreadMessageEx(tagMSG * 0x00423a88 {msg=0x00000400
>wp=0x00301528 lp=0x00000000}) line 652
>CWinThread::PreTranslateMessage(tagMSG * 0x00423a88 {msg=0x00000400
>wp=0x00301528 lp=0x00000000}) line 660 + 24 bytes
>CWinThread::PumpMessage() line 859 + 34 bytes
>CWinThread::Run() line 480 + 11 bytes
>
>
>
>




| Вернуться в корень Архива |