在很多 application 中,我使用了 browser + html. 好处很多,比如完成复杂的图文显示,预览,打印等等功能。在 VB6 或 C# 中通过 html Document 来控制browser 很简单,但在 mfc 中稍微有些复杂,于是我封装了 IHtmelDocument2 接口,用 exception 来捕获错误,这样,在 mfc 中使用IHtmelDocument2 变得相当简单了。
例如,我的网上赚钱程序中一段自动 login 的代码:
void CAutoSearchView::OnFileLogin()
{
try {
m_Doc.Navigate(m_strLogin[0]);
IHTMLElementCollection* pAll = m_Doc.GetAll();
IHTMLElementCollection* pInputs = m_Doc.GetCollectionTags(pAll, "input");
m_Doc.SetInputText(pInputs, m_strLogin[1], m_strLogin[2]);
m_Doc.SetInputText(pInputs, m_strLogin[3], m_strLogin[4]);
IHTMLElement* pSubmit = m_Doc.GetCollectionItem(pInputs, m_strLogin[5]);
if (pSubmit != NULL) {
pSubmit->click();
}
}
catch(...)
{
ShowInformation("Login error");
}
}
Source code of HtmlDocument.H
#pragma once
#include "mshtml.h"
#include "comdef.h"
#include <afxdisp.h>
namespace HtmlFunctions
{
typedef HRESULT (__stdcall IHTMLDocument2::*PFUNC_DOC_COLLECTION)(IHTMLElementCollection**);
typedef HRESULT (__stdcall IHTMLDocument2::*PFUNC_DOC_ELEMENT)(IHTMLElement**);
typedef HRESULT (__stdcall IHTMLDocument2::*PFUNC_DOC_GETTEXT)(BSTR*);
typedef HRESULT (__stdcall IHTMLDocument2::*PFUNC_DOC_PUTTEXT)(BSTR);
typedef HRESULT (__stdcall IHTMLElement::*PFUNC_ELEMENT_COLLECTION)(IHTMLElementCollection**);
typedef HRESULT (__stdcall IHTMLElement::*PFUNC_ELEMENT_ELEMENT)(IHTMLElement**);
typedef HRESULT (__stdcall IHTMLElement::*PFUNC_ELEMENT_GETTEXT)(BSTR*);
typedef HRESULT (__stdcall IHTMLElement::*PFUNC_ELEMENT_PUTTEXT)(BSTR);
};
using namespace HtmlFunctions;
class CHtmlDocument
{
CHtmlView* m_pHtmlView;
IHTMLDocument2* m_pHtmlDoc;
IHTMLElement* m_pHtmlElement;
IHTMLElementCollection* m_pHtmlElementCollection;
IHTMLElementCollection* m_pAllElements;
CString m_strText;
static void DoEvents();
public:
bool m_bOnNavigateCompleted;
bool m_bEnableException;
public:
CHtmlDocument();
~CHtmlDocument(void);
private:
IHTMLElementCollection* GetCollection(PFUNC_DOC_COLLECTION pDocFunction);
IHTMLElement* GetElement(PFUNC_DOC_ELEMENT pDocFunction);
CString& GetString(IHTMLElement* pHtmlElement,
PFUNC_ELEMENT_GETTEXT pDocFunction);
void PutString(IHTMLElement* pHtmlElement,
PFUNC_ELEMENT_PUTTEXT pDocFunction, LPCTSTR strText);
public:
IHTMLElementCollection* GetCollectionTags(IHTMLElementCollection* pElementCollection,
LPCTSTR strKey);
IHTMLElement* GetCollectionItem(IHTMLElementCollection* pElementCollection,
LPCTSTR strKey, int index=0);
IHTMLElement* GetCollectionItem(IHTMLElementCollection* pElementCollection,
int index);
int GetCollectionLength(IHTMLElementCollection* pElementCollection);
public:
IHTMLDocument2* CHtmlDocument::GetHtmlDocument();
void SetHtmlView(CHtmlView* pHtmlView);
bool Navigate(LPCTSTR strUrl);
bool SetInputText(IHTMLElementCollection* pCollection, LPCTSTR strKey, LPCTSTR text);
bool SubmitForm(LPCTSTR strForm);
IHTMLElementCollection* GetAll(void) { return GetCollection(IHTMLDocument2::get_all); }
IHTMLElement* GetBody(void) { return GetElement(IHTMLDocument2::get_body); }
};
CPP File:
#include "StdAfx.h"
#include "./htmldocument.h"
#define THROWEXCEPTION if (m_bEnableException) ::AfxThrowUserException()
#define CHECK(s) if ((s) != S_OK) THROWEXCEPTION
#define NotNULL(p) if (p == NULL) THROWEXCEPTION
#define CHECK_RET(s) if ((s) != S_OK) return false
#define NULL_RET(p) if (p == NULL) return false
CHtmlDocument::CHtmlDocument()
{
m_pHtmlView = NULL;
m_bEnableException = true;
}
CHtmlDocument::~CHtmlDocument(void)
{
}
void CHtmlDocument::SetHtmlView(CHtmlView* pHtmlView)
{
ASSERT(pHtmlView);
m_pHtmlView = pHtmlView;
}
IHTMLDocument2* CHtmlDocument::GetHtmlDocument()
{
ASSERT(m_pHtmlView);
try {
IDispatch* pDisp = m_pHtmlView->GetHtmlDocument();
if (pDisp != NULL ) {
CHECK(pDisp->QueryInterface(IID_IHTMLDocument2, (void**) &m_pHtmlDoc));
}
NotNULL(m_pHtmlDoc);
}
catch (...) {
return NULL;
}
return m_pHtmlDoc;
}
/**************************************************************************************************
IHTMLDocument2 Functions
**************************************************************************************************/
IHTMLElementCollection*
CHtmlDocument::GetCollection(PFUNC_DOC_COLLECTION pDocFunction)
{
NotNULL(m_pHtmlDoc);
CHECK((m_pHtmlDoc->*pDocFunction)(&m_pHtmlElementCollection));
NotNULL(m_pHtmlElementCollection);
return m_pHtmlElementCollection;
}
IHTMLElement* CHtmlDocument::GetElement(PFUNC_DOC_ELEMENT pDocFunction)
{
NotNULL(m_pHtmlDoc);
CHECK((m_pHtmlDoc->*pDocFunction)(&m_pHtmlElement));
NotNULL(m_pHtmlElement);
return m_pHtmlElement;
}
/**************************************************************************************************
IHTMLElementCollection Functions
**************************************************************************************************/
IHTMLElementCollection* CHtmlDocument::GetCollectionTags
(
IHTMLElementCollection* pElementCollection, LPCTSTR strKey
)
{
NotNULL(pElementCollection);
IDispatch* pDisp;
CHECK(pElementCollection->tags(COleVariant(strKey), &pDisp));
if (pDisp == NULL) return NULL;
CHECK(pDisp->QueryInterface(IID_IHTMLElementCollection, (void**) &m_pHtmlElementCollection));
return m_pHtmlElementCollection;
}
IHTMLElement* CHtmlDocument::GetCollectionItem
(
IHTMLElementCollection* pElementCollection, LPCTSTR strKey, int index
)
{
NotNULL(pElementCollection);
IDispatch* pDisp;
CHECK(pElementCollection->item(COleVariant(strKey), COleVariant((long) index), &pDisp));
if (pDisp == NULL) return NULL;
CHECK(pDisp->QueryInterface(IID_IHTMLElement, (void**) &m_pHtmlElement));
return m_pHtmlElement;
}
IHTMLElement* CHtmlDocument::GetCollectionItem(IHTMLElementCollection* pElementCollection, int index)
{
NotNULL(pElementCollection);
IDispatch* pDisp;
CHECK(pElementCollection->item(COleVariant((long) index), COleVariant((long) 0), &pDisp));
if (pDisp == NULL) return NULL;
CHECK(pDisp->QueryInterface(IID_IHTMLElement, (void**) &m_pHtmlElement));
return m_pHtmlElement;
}
int CHtmlDocument::GetCollectionLength(IHTMLElementCollection* pElementCollection)
{
NotNULL(pElementCollection);
long length;
CHECK(pElementCollection->get_length(&length));
return length;
}
/**************************************************************************************************
IHTMLElement Functions
**************************************************************************************************/
CString& CHtmlDocument::GetString(IHTMLElement* pHtmlElement, PFUNC_ELEMENT_GETTEXT pDocFunction)
{
BSTR str;
CHECK((pHtmlElement->*pDocFunction)(&str));
m_strText = str;
return m_strText;
}
void CHtmlDocument::PutString(IHTMLElement* pHtmlElement, PFUNC_ELEMENT_PUTTEXT pDocFunction, LPCTSTR strText)
{
CHECK((pHtmlElement->*pDocFunction)(_bstr_t(strText)));
}
/**************************************************************************************************
**************************************************************************************************/
bool CHtmlDocument::SetInputText(IHTMLElementCollection* pCollection, LPCTSTR strKey, LPCTSTR text)
{
NULL_RET(pCollection);
IDispatch* pDisp;
IHTMLInputTextElement* pElement;
CHECK_RET(pCollection->item(COleVariant(strKey), COleVariant((long) 0), &pDisp));
if (pDisp == NULL) return false;
CHECK_RET(pDisp->QueryInterface(IID_IHTMLInputTextElement, (void**) &pElement));
NULL_RET(pElement);
CHECK_RET(pElement->put_value(_bstr_t(text)));
return true;
}
bool CHtmlDocument::Navigate(LPCTSTR strUrl)
{
m_bOnNavigateCompleted = false;
m_pHtmlDoc = NULL;
m_pHtmlElementCollection = NULL;
m_pHtmlElement = NULL;
m_pHtmlView->Navigate2(strUrl);
DWORD timetick = ::GetTickCount();
while (! m_bOnNavigateCompleted) {
DoEvents();
::Sleep(10);
if (::GetTickCount() > timetick + 20000) {
THROWEXCEPTION;
return false;
}
}
GetHtmlDocument();
return true;
}
void CHtmlDocument::DoEvents()
{
MSG msg;
if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
::PostMessage( NULL, WM_QUIT, 0, 0);
}
if (msg.message != WM_COMMAND) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}
}
bool CHtmlDocument::SubmitForm(LPCTSTR strForm)
{
return false;
}