DWebBrowserEvent2的下沉事件会挂起导航

时间:2012-09-02 07:06:27

标签: internet-explorer winapi visual-c++ com automation

我在Sinking DWebBrowserEvents2 events appears to hang programmatic navigation就像JimEvans一样有问题,但我无法理解anwser,有人可以告诉我更多吗?

我正在通过plaint C ++操作IE Explorer。某些代码是从Codeproject复制的。但是当我处理事件时,我的IE挂起了,在主函数完成之前我无法获得DISPID_NAVIGETCOMPLETE事件。

我的代码如下:

#include <afxwin.h>
#include <afxdisp.h>
#include <iostream>
#include <MsHTML.h>
#include <Exdisp.h>
#include <ExDispid.h>

class IE_Events_Sinker : public DWebBrowserEvents2
{

public:
// No constructor or destructor is needed
// IUnknown methods
STDMETHODIMP QueryInterface(REFIID riid,void **ppvObject);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// IDispatch methods
STDMETHODIMP GetTypeInfoCount(UINT *pctinfo);
STDMETHODIMP GetTypeInfo(UINT iTInfo,LCID lcid,ITypeInfo **ppTInfo);
STDMETHODIMP GetIDsOfNames(REFIID riid,LPOLESTR *rgszNames,UINT cNames,LCID lcid,DISPID *rgDispId);
STDMETHODIMP Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,
    DISPPARAMS *pDispParams,VARIANT *pVarResult,EXCEPINFO *pExcepInfo,UINT   *puArgErr);
};

IE_Events_Sinker IESinker;

STDMETHODIMP IE_Events_Sinker::Invoke(DISPID dispIdMember, 
    REFIID riid, 
    LCID lcid, 
    WORD wFlags, 
    DISPPARAMS FAR* pDispParams, 
    VARIANT FAR* pVarResult, 
    EXCEPINFO FAR* pExcepInfo, 
    unsigned int FAR* puArgErr )
{
switch(dispIdMember)
{
case DISPID_NAVIGATEERROR:
    {
    //Extract the status code from the DISPPARAMS structure
    VARIANT * vt_statuscode = pDispParams->rgvarg[1].pvarVal;
    DWORD  dwStatusCode =  vt_statuscode->lVal;
    //Extract the event's IDispatch pointer
    IDispatch *pdispFiredEvent = pDispParams->rgvarg[4].pdispVal;
    printf("Status Code: %d\n", dwStatusCode);
    break;
    }
case DISPID_NAVIGATECOMPLETE2:
    printf("Navigate Complete!\n");
    break;

case DISPID_BEFORENAVIGATE2:
    printf("Before Navigate is fired!\n");
    break;

default:
    //MessageBox(NULL, L"A Message", NULL, NULL);
    printf("A Message !\n the dispIdMemberis %d\n", dispIdMember);
    break;
}

return S_OK;
}

STDMETHODIMP IE_Events_Sinker::QueryInterface(REFIID riid,void **ppvObject)
{
// Check if ppvObject is a valid pointer
if(IsBadWritePtr(ppvObject,sizeof(void*))) return E_POINTER;
// Set *ppvObject to NULL
(*ppvObject)=NULL;
// See if the requested IID matches one that we support
// If it doesn't return E_NOINTERFACE
if(!IsEqualIID(riid,IID_IUnknown) && !IsEqualIID(riid,IID_IDispatch) && !IsEqualIID(riid,DIID_DWebBrowserEvents2)) return E_NOINTERFACE;
// If it's a matching IID, set *ppvObject to point to the global EventSink object
(*ppvObject)=(void*)&IESinker;
return S_OK;
}

STDMETHODIMP_(ULONG) IE_Events_Sinker::AddRef()
{
return 1; // We always have just one static object
}

STDMETHODIMP_(ULONG) IE_Events_Sinker::Release()
{
return 1; // Ditto
}

// We don't need to implement the next three methods because we are just a pure event sink
// We only care about Invoke() which is what IE calls to notify us of events

STDMETHODIMP IE_Events_Sinker::GetTypeInfoCount(UINT *pctinfo)
{
UNREFERENCED_PARAMETER(pctinfo);

return E_NOTIMPL;
}

STDMETHODIMP IE_Events_Sinker::GetTypeInfo(UINT iTInfo,LCID lcid,ITypeInfo **ppTInfo)
{
UNREFERENCED_PARAMETER(iTInfo);
UNREFERENCED_PARAMETER(lcid);
UNREFERENCED_PARAMETER(ppTInfo);

return E_NOTIMPL;
}

STDMETHODIMP IE_Events_Sinker::GetIDsOfNames(REFIID riid,LPOLESTR *rgszNames,UINT     cNames,LCID lcid,DISPID *rgDispId)
{
UNREFERENCED_PARAMETER(riid);
UNREFERENCED_PARAMETER(rgszNames);
UNREFERENCED_PARAMETER(cNames);
UNREFERENCED_PARAMETER(lcid);
UNREFERENCED_PARAMETER(rgDispId);

return E_NOTIMPL;
}

int main()
{
HRESULT hr;
IWebBrowser2* IWbr;

IConnectionPointContainer* pCPContainer;
IConnectionPoint* m_pConnectionPoint;

CoInitialize(NULL);

//hr = CoCreateInstance(CLSID_InternetExplorer, NULL, CLSCTX_LOCAL_SERVER, IID_IWebBrowser2, (void**)&IWbr);
hr = CoCreateInstance(CLSID_InternetExplorer, NULL, CLSCTX_LOCAL_SERVER, IID_IWebBrowser2, (void**)&IWbr);
IWbr->put_Visible(TRUE);

hr = IWbr->QueryInterface(IID_IConnectionPointContainer, (void**)&pCPContainer);

hr = pCPContainer->FindConnectionPoint(DIID_DWebBrowserEvents2, &m_pConnectionPoint);

//Important Point!
DWORD m_dwCookie;
m_pConnectionPoint->Advise(&IESinker, &m_dwCookie);

COleVariant vtEmpty;
BSTR url2 = SysAllocString(L"http://www.qq.com/");
IWbr->Navigate(url2, &vtEmpty, &vtEmpty, &vtEmpty, &vtEmpty);

Sleep(20000);
//hr = IWbr->Quit();
//while(FAILED(hr))
//{
    //a++;
    //hr = IWbr->Quit();
//}
//printf("shut down %d times\n", a);

m_pConnectionPoint->Unadvise(m_dwCookie);

m_pConnectionPoint->Release();
IWbr->Release();
pCPContainer->Release();

CoUninitialize();
}

1 个答案:

答案 0 :(得分:3)

问题以及您所指的问题中的答案是关于在您的活动的线程上处理窗口消息,即所谓的“消息泵”。你这样做:

Sleep(20000);

这就是问题的原因。相反,你应该等待,你应该在等待时处理窗口消息。它应该是一个循环调用这样的消息:

MSG Message;
while(PeekMessage(&Message, NULL, WM_NULL, WM_NULL, PM_REMOVE))
{
    TranslateMessage(&Message);
    DispatchMessage(&Message);
}

因此IECOM作为处理请求的一部分而发布的窗口消息将到达目标窗口并将被处理。相反,你是锁定线程,没有选择做它应该做的事情。上面的循环只调度一次消息,所以如果你需要超时,你可以把它作为一个无限循环唤醒处理成功或失败的一些指示,或唤醒的基于事件和消息的循环消息然后回退睡觉而不浪费CPU周期。

也就是说,你的Sleep 20秒仍在处理窗口消息可能是这个(该代码片段应该适合复​​制/粘贴以进行替换):

const ULONG nTimeoutTime = GetTickCount() + 20 * 1000; // In 20 seconds
const HANDLE hFakeEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
for(; ; )
{
    const LONG nWaitTime = nTimeoutTime - GetTickCount();
    if(nWaitTime <= 0)
        break; // Timeout
    const DWORD nWaitResult = MsgWaitForMultipleObjects(1, &hFakeEvent, FALSE, nWaitTime, QS_ALLINPUT | QS_ALLPOSTMESSAGE);
    //ATLTRACE(_T("nWaitResult 0x%x\n"), nWaitResult);
    //ATLASSERT(nWaitResult == WAIT_OBJECT_0 + 1 || nWaitResult == WAIT_TIMEOUT);
    if(nWaitResult == WAIT_TIMEOUT)
        break; // Timeout
    MSG Message;
    while(PeekMessage(&Message, NULL, WM_NULL, WM_NULL, PM_REMOVE))
    {
        //ATLTRACE(_T("Message.hwnd 0x%p, Message.message 0x%04x\n"), Message.hwnd, Message.message);
        TranslateMessage(&Message);
        DispatchMessage(&Message);
    }
}
CloseHandle(hFakeEvent);