Internet explorer DWebBrowserEvent2的下沉事件将挂起导航

Internet explorer DWebBrowserEvent2的下沉事件将挂起导航,internet-explorer,winapi,visual-c++,com,automation,Internet Explorer,Winapi,Visual C++,Com,Automation,我有一个和JimEvans一样的问题,但我不能理解anwser,有人能告诉我更多吗 我正在用C++来操纵IE浏览器。有些代码是从Codeproject复制的。但是我的IE在处理事件时被挂起,在主函数完成之前,我无法获取DISPID_NAVIGETCOMPLETE事件 我的代码如下: #include <afxwin.h> #include <afxdisp.h> #include <iostream> #include <MsHTML.h> #in

我有一个和JimEvans一样的问题,但我不能理解anwser,有人能告诉我更多吗

我正在用C++来操纵IE浏览器。有些代码是从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();
}
#包括
#包括
#包括
#包括
#包括
#包括
类别IE_事件\u伸卡器:公共DWebBrowserEvents 2
{
公众:
//不需要构造函数或析构函数
//未知方法
STDMETHODIMP查询接口(refid-riid,void**ppvObject);
STDMETHODIMP(ULONG)AddRef();
STDMETHODIMP(ULONG)Release();
//IDispatch方法
STDMETHODIMP GetTypeInfoCount(UINT*pctinfo);
STDMETHODIMP GetTypeInfo(UINT-iTInfo、LCID-LCID、ITypeInfo**ppTInfo);
STDMETHODIMP GetIDsOfNames(refid-riid、LPOLESTR*rgszNames、UINT-cNames、LCID-LCID、DISPID*rgDispId);
STDMETHODIMP Invoke(DISPID DISPID member、refid riid、LCID LCID、WORD wFlags、,
DISPPARAMS*pDispParams,VARIANT*pVarResult,EXCEPINFO*pExcepInfo,UINT*puArgErr);
};
体育赛事,伸卡球,伸卡球,;
STDMETHODIMP IE_Events_Sinker::Invoke(DISPID DISPID成员,
雷菲德·里德,
LCID LCID,
单词wFlags,
DISPPARAMS FAR*pDispParams,
变量FAR*pVarResult,
除FAR*pExcepInfo外,
无符号整数(FAR*puArgErr)
{
交换机(dispIdMember)
{
案例DISPID\u导航错误:
{
//从DISPPARAMS结构中提取状态代码
变量*vt_statuscode=pDispParams->rgvarg[1].pvarVal;
DWORD dwStatusCode=vt\U statuscode->lVal;
//提取事件的IDispatch指针
IDispatch*pdispFiredEvent=pDispParams->rgvarg[4].pdispVal;
printf(“状态代码:%d\n”,dwStatusCode);
打破
}
案例DISPID_NAVIGATECOMPLETE2:
printf(“导航完成!\n”);
打破
导航前的案例显示2:
printf(“启动导航前!\n”);
打破
违约:
//MessageBox(空,L“A消息”,空,空);
printf(“消息!\n dispIdMember为%d\n”,dispIdMember);
打破
}
返回S_OK;
}
STDMETHODIMP IE_Events_Sinker::QueryInterface(refid riid,void**ppvObject)
{
//检查ppvObject是否为有效指针
if(IsBadWritePtr(ppvObject,sizeof(void*))返回E_指针;
//将*ppvObject设置为空
(*ppvObject)=空;
//查看请求的IID是否与我们支持的IID匹配
//如果它不返回E_NOINTERFACE
如果(!ISEqualID(riid,IID_IUnknown)和&!ISEqualID(riid,IID_IDispatch)和&!ISEqualID(riid,DIID_DWebBrowserEvents2))返回E_NOINTERFACE;
//如果是匹配的IID,则将*ppvObject设置为指向全局EventSink对象
(*ppvObject)=(void*)和IESinker;
返回S_OK;
}
STDMETHODIMP(ULONG)IE_事件_伸卡器::AddRef()
{
return 1;//我们总是只有一个静态对象
}
STDMETHODIMP(ULONG)IE_事件_伸卡器::发布()
{
返回1;//同上
}
//我们不需要实现接下来的三种方法,因为我们只是一个纯粹的事件接收器
//我们只关心Invoke(),IE调用它来通知我们事件
STDMETHODIMP IE_Events_Sinker::GetTypeInfoCount(UINT*pctinfo)
{
未引用的_参数(pctinfo);
返回E_NOTIMPL;
}
STDMETHODIMP IE_Events_Sinker::GetTypeInfo(UINT-iTInfo、LCID-LCID、ITypeInfo**ppTInfo)
{
未引用的_参数(iTInfo);
未引用的_参数(lcid);
未引用的_参数(ppTInfo);
返回E_NOTIMPL;
}
STDMETHODIMP IE_Events_Sinker::GetIDsOfNames(refid-riid、LPOLESTR*rgszNames、UINT-cNames、LCID-LCID、DISPID*rgDispId)
{
未引用的_参数(riid);
未引用的_参数(rgszNames);
未引用的_参数(cNames);
未引用的_参数(lcid);
未引用的_参数(rgDispId);
返回E_NOTIMPL;
}
int main()
{
HRESULT-hr;
IWebBrowser2*IWbr;
i连接点容器*pcpcContainer;
i连接点*m_p连接点;
共初始化(空);
//hr=CoCreateInstance(CLSID\u InternetExplorer,NULL,CLSCTX\u LOCAL\u服务器,IID\u IWebBrowser2,(void**)和IWbr);
hr=CoCreateInstance(CLSID\u InternetExplorer,NULL,CLSCTX\u LOCAL\u服务器,IID\u IWebBrowser2,(void**)和IWbr);
IWbr->put_可见(真);
hr=IWbr->QueryInterface(IID_IConnectionPointContainer,(void**)和PCPCContainer);
hr=pcContainer->FindConnectionPoint(DIID\u DWebBrowserEvents2和m\u pcConnectionPoint);
//重要的一点!
德沃德·穆德库奇;
m_pConnectionPoint->advice(&IESinker,&m_dwCookie);
cole是空的;
BSTR url2=SysAllocString(L)http://www.qq.com/");
IWbr->导航(url2,&vtEmpty,&vtEmpty,&vtEmpty,&vtEmpty,&vtEmpty);
睡眠(20000);
//hr=IWbr->Quit();
//while(失败(hr))
//{
//a++;
//hr=IWbr->Quit();
//}
//printf(“关闭%d次\n”,a);
m_pConnectionPoint->Unadvise(m_dwCookie);
m_p连接点->释放();
IWbr->Release();
pcContainer->Release();
coninitialize();
}

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

这就是问题的原因。相反,您应该等待,并且应该在等待时处理窗口消息。相反,它应该是一个循环,具有如下调度消息:

MSG Message;
while(PeekMessage(&Message, NULL, WM_NULL, WM_NULL, PM_REMOVE))
{
    TranslateMessage(&Message);
    DispatchMessage(&Message);
}
因此,
IE
COM
可以作为处理请求的一部分发布的窗口消息将到达目标窗口并得到处理。相反,您正在锁定线程,使其无法选择应该执行的操作。但是,上面的循环只发送一次消息,因此如果需要超时,可以将其作为一个无限循环,在处理过程中出现成功或失败的迹象时唤醒,或者作为一个事件,一个基于消息的循环,在消息上唤醒并返回睡眠状态,而不会浪费CPU周期

也就是说,您的
睡眠
20秒仍在处理窗口消息的情况可能是这样的(片段应该适合复制/粘贴以替换):

const ULONG nTimeoutTime=GetTickCount()+20*1000;//20秒后
const HANDLE hfakevent=CreateEvent(NULL、TRUE、FALSE、NULL);
MSG Message;
while(PeekMessage(&Message, NULL, WM_NULL, WM_NULL, PM_REMOVE))
{
    TranslateMessage(&Message);
    DispatchMessage(&Message);
}
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);