See the question and my original answer on StackOverflow

With COM, you should never cast a COM interface into another COM interface like this:

STDMETHODIMP CInspectionCore::SendEvent(IDispatch *pEventData)
{
    IEventData *pIEventData = (IEventData *)pEventData; // wrong!
}

Instead you must use QueryInterface, this works fine:

STDMETHODIMP CInspectionCore::SendEvent(IDispatch *pEventData)
{
    IEventData* pIEventData;
    HRESULT hr = pEventData->QueryInterface(&pIEventData);
    if (FAILED(hr)) // etc.
}

In some cases (in fact often), raw casting may work which can give the false impression that it's ok.

In your case, it doesn't work because you use different threads which creates implicit proxies (COM apartments, etc.). You can see that if you breakpoint in SendEvent have a look at the call stack when it's called, it's all COM marshaling stuff.