1

我有一个问题,就像下沉 DWebBrowserEvents2 事件中的 JimEvans 似乎挂起编程导航但我无法理解答案,有人可以告诉我更多信息吗?

我正在通过普通 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();
}
4

1 回答 1

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);
于 2012-09-02T14:12:34.143 回答