c++內嵌IWebBrowser2功能整理
目的:加深對IWebBrowser2的理解,整理。方便以後學習和使用。也方便需要這反面的朋友少點彎路。
宣告:有些知識點是本人在使用和學習中藉助網路搜尋到的,所以難免會有雷同,我會盡量標註原著的出處,當然也可能找不到原著的出去了,如果牽扯到版權或者其他的可以通知我,我會跟你一同處理。本文的原始碼也有部分是來自網路和msdn上面,出處我也會盡量標明。
前言:前段時間,一個大學同學問我,在win下面c++怎麼獲取web的登陸狀態,是哪個使用者登陸的,我告訴他可以內嵌webbrowser實現,他叫我幫忙提供一個demo給他,所以我利用空餘時間幫忙做了一個win32的程式模擬登陸csdn的demo給他。後面他又說,不要模擬登陸,要使用者自己點選登陸,然後c++來獲取登陸是否成功,並且是哪個使用者在登陸。我說那就得改改程式,給他提了幾個點。最後也不知道他們公司的c++程式設計師 搞定沒。後面自己想了一下,覺得有必要把c++與IWebBrowser2的東西整理一下,自己也理一下,所以用了點時間添加了一個demo,並且寫這篇部落格。
正文
1.開發環境和知識點
程式碼是使用vs2008編譯,是win32的程式(不是mfc)。閱讀的時候需要你瞭解過簡單的win32程式設計,也會一些簡單的web基礎(html元素的概念),會根據ie瀏覽器查詢你需要的html元素。如果你具備這些 那麼閱讀和學習這個東西就很簡單了。
2.引用和借鑑的url
這個我會盡量列舉在這裡,找不到出去就沒法發了
以上就是我能記住的參考過的url了。下面的文章不在註明具體是那個知識點參照上面的url了。
3.win32模擬登陸csdn程式
原理說明:內嵌一個webbrowser的com元件,指定url就會把頁面加載出來(具體原理需要http通訊,獲取返回值,解析頁面,執行js等等),載入之後我們根據html標籤的id或者是name或者是tag等查詢你需要的IHTMLElement物件(id和name的查詢,可以用ie開啟url按f12鍵查詢元素,檢視html標籤具體資訊),接下來就是對IHTMLElement的操作,可以輸入資料,觸發點選事件等。點選之後等待頁面跳轉或者是重新載入,根據載入的新資料來判斷是否登入成功(一般登入成功會跳轉頁面,有些web會記錄cookie等其他資訊,這些都需要根據不同的web網站 做不同的判斷)
首先,需要一個基於對話方塊的win32程式,上面有2個按鈕(一個模擬登陸按鈕,一個導航按鈕)和3個輸入框(一個輸入url,一個輸入使用者名稱,一個輸入密碼)
程式碼段
int WINAPI _tWinMain(HINSTANCE hInst, HINSTANCE h0, LPTSTR lpCmdLine, int nCmdShow) { DialogBox(hInst,MAKEINTRESOURCE(IDD_DIALOG_MAIN),NULL,(DLGPROC)MainDialogProc); return TRUE; } // Windows 事件處理 LRESULT CALLBACK MainDialogProc(HWND hDlg,UINT message, WPARAM wParam,LPARAM lParam) { //訊息的處理,我想你要的就是這裡了 switch(message) { case WM_INITDIALOG: return DialogInit(hDlg,wParam,lParam); break; case WM_COMMAND: return CommandFun(hDlg,wParam,lParam); break; case WM_CLOSE://關閉在這裡 EndDialog(hDlg,TRUE); return TRUE; break; } return FALSE; } LRESULT DialogInit(HWND hDlg,WPARAM wParam,LPARAM lParam) { RECT rc; GetClientRect(hDlg, &rc); HWND hStaticOption = GetDlgItem(hDlg,IDC_STATIC_OPTION); RECT rc_Option; GetWindowRect(hStaticOption,&rc_Option); // SetDlgItemText(hDlg,IDC_EDIT_URL,_T("https://passport.csdn.net/account/login")); SetDlgItemText(hDlg,IDC_EDIT_NAME,_T("xxxxxxx")); SetDlgItemText(hDlg,IDC_EDIT_PWD,_T("xxxxxx")); // RECT webRc; webRc.left = 0; webRc.top = rc_Option.bottom; webRc.right = rc.right; webRc.bottom = rc.bottom; gWebAutoLogin = new WebAutoLogin(hDlg, webRc); gWebAutoLogin->Navigate(L"https://www.baidu.com/"); StartWaitWebLoad(hDlg); return TRUE; } LRESULT CommandFun(HWND hDlg,WPARAM wParam,LPARAM lParam) { //IDC_CLOSE 是我在對話方塊中加入的一個按鈕的ID 也可以關閉 if (LOWORD(wParam)==IDOK) { PostQuitMessage(0); } else if (LOWORD(wParam)==IDC_BTN_GO) { LoadWeb(hDlg); } else if (LOWORD(wParam)==IDC_BTN_LOGIN) { SimulateLogin(hDlg); } else { return FALSE; } return TRUE; } void LoadWeb(HWND hDlg) { wchar_t url[MAX_PATH*2+1] ={0}; GetDlgItemText(hDlg,IDC_EDIT_URL,url,MAX_PATH*2); std::wstring strUrl; strUrl.assign(url); gWebAutoLogin->Navigate(strUrl); StartWaitWebLoad(hDlg); } void SimulateLogin(HWND hDlg) { wchar_t name[MAX_PATH+1] ={0}; GetDlgItemText(hDlg,IDC_EDIT_NAME,name,MAX_PATH); wchar_t pswd[MAX_PATH+1] ={0}; GetDlgItemText(hDlg,IDC_EDIT_PWD,pswd,MAX_PATH); std::wstring userName; userName.assign(name); std::wstring password; password.assign(pswd); if (gWebAutoLogin->AutoLogin(hDlg,userName,password)) { bIsSimulateLogin = TRUE; StartWaitWebLoad(hDlg); } }
這裡面主要是用gWebAutoLogin物件,這個是簡單封裝了的一個WebAutoLogin類。
下面就是 WebAutoLogin類的實現主程式碼片段
WebAutoLogin::WebAutoLogin(HWND hwnd,RECT webRc)
{
LPOLESTR pszName=OLESTR("shell.Explorer.2");
m_WinContainer.Create(hwnd, webRc, 0,WS_CHILD |WS_VISIBLE);
m_WinContainer.CreateControl(pszName);
HRESULT hr = m_WinContainer.QueryControl(__uuidof(IWebBrowser2),(void**)&m_iWebBrowser);
if(FAILED(hr))
{
MessageBox(hwnd,_T("獲取IWebBrowser2 物件失敗!!!"),_T("錯誤"),MB_OK|MB_ICONERROR);
m_iWebBrowser = NULL;
}
}
WebAutoLogin::~WebAutoLogin(void)
{
if (NULL != m_iWebBrowser)
{
m_iWebBrowser->Release();
}
}
READYSTATE WebAutoLogin::ReadyState()
{
READYSTATE r = READYSTATE_UNINITIALIZED;
HRESULT hr = m_iWebBrowser->get_ReadyState(&r);
//printf("get_ReadyState = %d",r);
if (SUCCEEDED(hr) && r == READYSTATE_COMPLETE)
{
}
return r;
}
bool WebAutoLogin::AutoLogin(HWND hwnd,std::wstring userName,std::wstring password)
{
bool isLogin = false;
HRESULT hr = S_OK;
IHTMLElement *user_nameElet= GetHTMLElementByIdOrName(L"username");
if (user_nameElet!=0)
{
//轉換成CComBSTR
CComBSTR bStr =userName.c_str();
//輸入內容
hr = user_nameElet->put_innerText(bStr);
user_nameElet->Release();
}
else
{
MessageBox(hwnd,_T("獲取:使用者名稱HTMLElement 失敗!"),_T("錯誤"),MB_OK|MB_ICONERROR );
}
IHTMLElement *passwdElet= GetHTMLElementByIdOrName(L"password");
if (passwdElet!=0)
{
//轉換成CComBSTR
CComBSTR bStr = password.c_str();
//輸入內容
hr = passwdElet->put_innerText(bStr);
passwdElet->Release();
}
else
{
MessageBox(hwnd,_T("獲取:密碼HTMLElement 失敗!"),_T("錯誤"),MB_OK|MB_ICONERROR );
}
IHTMLElement *loginSubElet = GetHTMLElementByTag(L"input",L"value",L"登 錄");
if (loginSubElet!=0)
{
loginSubElet->click();
loginSubElet->Release();
isLogin = true;
}
else
{
MessageBox(hwnd,_T("獲取:登陸HTMLElement 失敗!"),_T("錯誤"),MB_OK|MB_ICONERROR );
}
return isLogin;
}
bool WebAutoLogin::LoginResult()
{
bool isLogin = false;
IDispatch *dispatch=0;
HRESULT hr = m_iWebBrowser->get_Document(&dispatch);
if ((S_OK==hr)&&(dispatch!=0))
{
IHTMLDocument2 *doc;
hr = dispatch->QueryInterface(IID_IHTMLDocument2,(void**)&doc);
dispatch->Release();
if ( S_OK == hr )
{
//登陸成功的 判斷方式可以用不同的 方法
BSTR bstrCookie;
hr = doc->get_cookie(&bstrCookie);
if (S_OK == hr)
{
_bstr_t bstr_t(bstrCookie);
std::string strCookie(bstr_t);
::SysFreeString(bstrCookie);
}
BSTR bstrReferrer;
hr = doc->get_referrer(&bstrReferrer);
if (S_OK == hr)
{
if(NULL != bstrReferrer)
{
_bstr_t bstr_t0(bstrReferrer);
std::string strReferrer(bstr_t0);
::SysFreeString(bstrReferrer);
}
}
BSTR bstrUrl;
hr = doc->get_URL(&bstrUrl);
if (S_OK == hr)
{
if(NULL != bstrUrl)
{
_bstr_t bstr_t(bstrUrl);
std::string strUrl(bstr_t);
if (0 == strcmp("http://www.csdn.net/",strUrl.c_str()))
{
isLogin = true;
}
// free the BSTR
::SysFreeString(bstrUrl);
}
}
}
doc->Release();
}
dispatch->Release();
return isLogin;
}
void WebAutoLogin::Navigate(std::wstring strUrl)
{
VARIANT varMyURL;
VariantInit(&varMyURL);
varMyURL.vt = VT_BSTR;
varMyURL.bstrVal = SysAllocString(strUrl.c_str());
m_iWebBrowser-> Navigate2(&varMyURL,0,0,0,0);
SysFreeString(varMyURL.bstrVal);
VariantClear(&varMyURL);
}
IHTMLElement * WebAutoLogin::GetHTMLElementByTag(std::wstring tagName,std::wstring PropertyName,
std::wstring macthValue)
{
IHTMLElement *retElement=0;
IDispatch *dispatch=0;
HRESULT hr = m_iWebBrowser->get_Document(&dispatch);
if ((S_OK==hr)&&(0 != dispatch))
{
IHTMLDocument2 *doc;
dispatch->QueryInterface(IID_IHTMLDocument2,(void**)&doc);
dispatch->Release();
IHTMLElementCollection* doc_all;
hr = doc->get_all(&doc_all); // this is like doing document.all
if (S_OK == hr)
{
VARIANT vKey;
vKey.vt=VT_BSTR;
vKey.bstrVal=SysAllocString(tagName.c_str());
VARIANT vIndex;
VariantInit(&vIndex);
hr = doc_all->tags(vKey,&dispatch); // this is like doing document.all["messages"]
//清理
SysFreeString(vKey.bstrVal);
VariantClear(&vKey);
VariantClear(&vIndex);
if ((S_OK == hr) && (0 != dispatch))
{
CComQIPtr< IHTMLElementCollection > all_tags = dispatch;
//hr = dispatch->QueryInterface(IHTMLElementCollection,(void **)&all_tags); // it's the caller's responsibility to release
if (S_OK == hr)
{
long nTagsCount=0; //
hr = all_tags->get_length( &nTagsCount);
if ( FAILED( hr ) )
{
return retElement;
}
for(long i=0; i<nTagsCount; i++)
{
CComDispatchDriver spInputElement; //取得第 i 項
hr = all_tags->item( CComVariant(i), CComVariant(i), &spInputElement );
if ( FAILED( hr ) )
continue;
CComVariant vValue;
hr = spInputElement.GetPropertyByName(PropertyName.c_str(), &vValue );
if (VT_EMPTY != vValue.vt)
{
LPCTSTR lpValue = vValue.bstrVal?
OLE2CT( vValue.bstrVal ) : NULL;
if(NULL == lpValue)
continue;
std::wstring cs = (LPCTSTR)lpValue;
if (0 == _tcscmp(cs.c_str(),macthValue.c_str()))
{
hr = spInputElement->QueryInterface(IID_IHTMLElement,(void **)&retElement);
if (S_OK == hr)
{
}
else
{
retElement = 0;
}
break;
}
}
//
//CComVariant vName,vVal,vType; //名,值,型別
//hr = spInputElement.GetPropertyByName( L"name", &vName );
//if( FAILED( hr ) ) continue;
//hr = spInputElement.GetPropertyByName( L"value", &vVal );
//if( FAILED( hr ) ) continue;
//hr = spInputElement.GetPropertyByName( L"type", &vType );
//if( FAILED( hr ) ) continue;
//LPCTSTR lpName = vName.bstrVal?
// OLE2CT( vName.bstrVal ) : _T("NULL"); //未知域名
//LPCTSTR lpVal = vVal.bstrVal?
// OLE2CT( vVal.bstrVal ) : _T("NULL"); //空值,未輸入
//LPCTSTR lpType = vType.bstrVal?
// OLE2CT( vType.bstrVal ) : _T("NULL"); //未知型別
}
}
else
{
retElement = 0;
}
dispatch->Release();
}
doc_all->Release();
}
doc->Release();
}
return retElement;
}
IHTMLElement * WebAutoLogin::GetHTMLElementByIdOrName(std::wstring idorName)
{
IHTMLElement *retElement=0;
IDispatch *dispatch=0;
HRESULT hr = m_iWebBrowser->get_Document(&dispatch);
if ((S_OK==hr)&&(0!=dispatch))
{
IHTMLDocument2 *doc;
dispatch->QueryInterface(IID_IHTMLDocument2,(void**)&doc);
dispatch->Release();
IHTMLElementCollection* doc_all;
hr = doc->get_all(&doc_all); // this is like doing document.all
if (S_OK == hr)
{
VARIANT vKey;
vKey.vt=VT_BSTR;
vKey.bstrVal=SysAllocString(idorName.c_str());
VARIANT vIndex;
VariantInit(&vIndex);
hr = doc_all->item(vKey,vIndex,&dispatch); // this is like doing document.all["messages"]
//清理
SysFreeString(vKey.bstrVal);
VariantClear(&vKey);
VariantClear(&vIndex);
if ((S_OK == hr) && (0 != dispatch))
{
hr = dispatch->QueryInterface(IID_IHTMLElement,(void **)&retElement); // it's the caller's responsibility to release
if (S_OK == hr)
{
}
else
{
retElement = 0;
}
dispatch->Release();
}
doc_all->Release();
}
doc->Release();
}
return retElement;
}
到這裡 一個模擬登陸csdn的就已經可以了,但是還是有幾個問題:1.頁面載入完成的判斷是用定時器查詢的這種處理不太好。2.這個需要把使用者名稱和賬號輸入到win32的介面,而不是web頁面(當然這也是為了自動登陸)。3.c++執行js或者是js能否執行c++函式,這個demo裡面沒有實現
4.IWebBrowser2的事件獲取以及c++與js的互動程式
原理說明:通過繼承IDispatch(或者是HTMLElementEvents2,DWebBrowserEvents2)實現對應的介面,來獲取IWebBrowser2的事件以及HTMLElement的事件(這個的具體原理得到msdn上面去找了,這裡就不細說了)。通過繼承IDocHostUIHandler(這個介面內容很豐富,不止js呼叫c++這點功能)來實現js呼叫c++的函式(具體原理這裡也不展開說了)
下面貼一下主程式碼,win32程式就不貼了和上一個demo差不多。
class WebBrowserSink: public DWebBrowserEvents2 這個類主要是監聽web的載入是否完成事件,以及下載進度事件,狀態等資訊等事件。
STDMETHODIMP WebBrowserSink::Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS *pDispParams,VARIANT *pVarResult,EXCEPINFO *pExcepInfo,UINT *puArgErr)
{
UNREFERENCED_PARAMETER(lcid);
UNREFERENCED_PARAMETER(wFlags);
UNREFERENCED_PARAMETER(pVarResult);
UNREFERENCED_PARAMETER(pExcepInfo);
UNREFERENCED_PARAMETER(puArgErr);
if(!IsEqualIID(riid,IID_NULL))
return DISP_E_UNKNOWNINTERFACE; // riid should always be IID_NULL
//LogTrace(L"WebBrowserSink::Invoke dispIdMember = %d",dispIdMember);
switch (dispIdMember)
{
case DISPID_BEFORENAVIGATE2:
if (NULL != m_pEventCallBack)
{
m_pEventCallBack->OnBeforeNavigate2(
(IDispatch*)pDispParams->rgvarg[6].byref,
(VARIANT*)pDispParams->rgvarg[5].pvarVal,
(VARIANT*)pDispParams->rgvarg[4].pvarVal,
(VARIANT*)pDispParams->rgvarg[3].pvarVal,
(VARIANT*)pDispParams->rgvarg[2].pvarVal,
(VARIANT*)pDispParams->rgvarg[1].pvarVal,
(VARIANT_BOOL*)pDispParams->rgvarg[0].pboolVal
);
}
break;
case DISPID_DOCUMENTCOMPLETE:
if (NULL != m_pEventCallBack)
{
m_pEventCallBack->OnDocumentComplete(
(IDispatch*)pDispParams->rgvarg[0].byref,
pDispParams->rgvarg[0].pvarVal->bstrVal
);
}
break;
case DISPID_PROGRESSCHANGE:
if (NULL != m_pEventCallBack)
{
}
break;
case DISPID_STATUSTEXTCHANGE:
if (NULL != m_pEventCallBack)
{
m_pEventCallBack->OnStatusTextChange(
(IDispatch*)pDispParams->rgvarg[0].byref,
pDispParams->rgvarg[0].bstrVal
);
}
break;
default:
break;
}
return S_OK;
}
其他地方呼叫的方式為:
void WebMonitor::RegisterIeEventDealer()
{
HRESULT hr;
// 宣告一個IConnectionPointContainer和IConnectionPoint例項。
CComPtr<IConnectionPointContainer> spConnectionPointContainer;
CComPtr<IConnectionPoint> spConnectionPointBrowserEvents;
// pWebBrowser2->QueryInterface(IID_IConnectionPointContainer,(void**)&spConnectionPointContainer);
// 利用 IWebBrowser2 介面的 QueryInterface 方法獲得 IConnectionPointContainer 介面
m_piWebBrowser->QueryInterface(IID_IConnectionPointContainer,(void**)&spConnectionPointContainer);
// 利用 IConnectionPointContainer 介面的 FindConnectionPoint 獲取 IID為DIID_DWebBrowserEvents2 的連線點
spConnectionPointContainer->FindConnectionPoint(DIID_DWebBrowserEvents2,&spConnectionPointBrowserEvents);
// 利用IID為DIID_DWebBrowserEvents2的連線點的Advise建立一個實現了DWebBrowserEvents2介面的接收器的例項和此連線點的連線。
// 第一個引數就是接收器的例項,必須是一個實現了DWebBrowserEvents2介面的類的例項
// 在這裡我們設定成this,也就是自己實現了DWebBrowserEvents2介面,這個是通過繼承CWebEventSink實現的
m_dwCookie = 0;
m_pWebBrowserSink = new WebBrowserSink(this);
hr = spConnectionPointBrowserEvents->Advise(m_pWebBrowserSink,&m_dwCookie);
if (SUCCEEDED(hr))
{
// Successfully advised
}
}
class HtmlElementSink: public HTMLElementEvents2這個類是對指定的 HTMLElement 物件進行事件的監聽。
STDMETHODIMP HtmlElementSink::Invoke(DISPID dispidMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS* pdispparams,
VARIANT* pvarResult,
EXCEPINFO* pexcepinfo,
UINT* puArgErr)
{
switch (dispidMember)
{
case DISPID_HTMLELEMENTEVENTS2_ONCLICK:
if (NULL != m_pEventCallBack)
{
m_pEventCallBack->OnClick();
}
break;
default:
break;
}
return S_OK;
}
呼叫方式,需要先獲取到指定的IHTMLElement *,再進行監聽繫結。
//監聽 頁面事件
void WebMonitor::ConnectHTMLElementEvent(IHTMLElement* pElem)
{
if (NULL == pElem)
{
return;
}
HRESULT hr;
IConnectionPointContainer* pCPC = NULL;
IConnectionPoint* pCP = NULL;
DWORD dwCookie;
// Check that this is a connectable object.
hr = pElem->QueryInterface(IID_IConnectionPointContainer, (void**)&pCPC);
if (SUCCEEDED(hr))
{
// 列舉 檢視一下 支援的 ConnectionPoint
//IEnumConnectionPoints *pECPS = NULL;
//hr = pCPC->EnumConnectionPoints(&pECPS);
//if (SUCCEEDED(hr))
//{
// IConnectionPoint* tmppCP = NULL;
// ULONG cFetched = 1;
// IID guid;
// while(SUCCEEDED(hr = pECPS->Next(1,&tmppCP,&cFetched))
// &&(cFetched>0))
// {
// tmppCP->GetConnectionInterface(&guid);
// }
//}
// Find the connection point.
//hr = pCPC->FindConnectionPoint(DIID_HTMLElementEvents2, &pCP);
hr = pCPC->FindConnectionPoint(DIID_HTMLButtonElementEvents, &pCP);
if (SUCCEEDED(hr))
{
// Advise the connection point.
// pUnk is the IUnknown interface pointer for your event sink
if(NULL == m_pHtmlElementSink)
{
m_pHtmlElementSink = new HtmlElementSink(this);
}
hr = pCP->Advise(m_pHtmlElementSink, &dwCookie);
if (SUCCEEDED(hr))
{
// Successfully advised
}
pCP->Release();
}
pCPC->Release();
}
}
接下來就是c++呼叫js函數了,這個程式碼比較簡單,給定一個js函式名稱和引數就可以利用 IHTMLDocument2 的JavaScript介面的invoke方法呼叫。
bool WebMonitor::ExecJsFun( const std::wstring& lpJsFun,const std::vector<std::wstring>& params )
{
if ( NULL == m_piWebBrowser )
return false;
CComPtr<IDispatch> pDoc;
HRESULT hr = m_piWebBrowser->get_Document(&pDoc);
if ( FAILED(hr) )
return false;
CComQIPtr<IHTMLDocument2> pDoc2=pDoc;
if ( NULL == pDoc2 )
return false;
CComQIPtr<IDispatch> pScript;
hr = pDoc2->get_Script(&pScript);
if ( FAILED(hr) )
return false;
DISPID id = NULL;
CComBSTR bstrFun(lpJsFun.c_str());
hr = pScript->GetIDsOfNames(IID_NULL, &bstrFun, 1, LOCALE_SYSTEM_DEFAULT, &id);
if ( FAILED(hr) )
return false;
DISPPARAMS dispParams;
memset(&dispParams, 0, sizeof(DISPPARAMS));
int nParamCount = params.size();
if (nParamCount > 0)
{
dispParams.cArgs =nParamCount;
dispParams.rgvarg =new VARIANT[nParamCount];
for (int i=0; i<nParamCount; ++i )
{
const std::wstring& str = params[nParamCount-1-i];
CComBSTR bstr(str.c_str());
bstr.CopyTo(&dispParams.rgvarg[i].bstrVal);
dispParams.rgvarg[i].vt = VT_BSTR;
}
}
EXCEPINFO execInfo;
memset(&execInfo, 0, sizeof(EXCEPINFO));
VARIANT vResult;
UINT uArgError = (UINT)-1;
hr = pScript->Invoke(id, IID_NULL, 0, DISPATCH_METHOD, &dispParams, &vResult, &execInfo, &uArgError);
delete[] dispParams.rgvarg;
if ( FAILED(hr) )
return false;
return true;
}
js呼叫c++函式,這個需要先繼承重寫IDocHostUIHandler並且需要在js程式碼用 window.external.CppCall(10);方式去呼叫。
主要實現IDocHostUIHandler 的 virtual HRESULT STDMETHODCALLTYPE GetExternal( /* [out] */ IDispatch **ppDispatch) = 0;方法,返回一個繼承IDispatch的ClientCall物件,ClientCall裡面需要實現對c++函式的id指定,以及呼叫引數的處理等。
下面是一些主要程式碼,這是註冊 TDocHostUIHandlerImpl物件。
void WebMonitor::RegisterUIHandlerToJs()
{
ICustomDoc *m_spCustDoc;
HRESULT hr;
CComPtr<IDispatch> pDoc;
hr = m_piWebBrowser->get_Document(&pDoc);
if ( FAILED(hr) )
return;
CComQIPtr<IHTMLDocument2> pDoc2=pDoc;
if ( NULL == pDoc2 )
return ;
hr = pDoc2-> QueryInterface(IID_ICustomDoc,(void**)&m_spCustDoc);
if(SUCCEEDED(hr))
{
if (NULL == m_pDocHostUIHandler)
{
m_pDocHostUIHandler = new TDocHostUIHandlerImpl();
}
hr = m_spCustDoc-> SetUIHandler(m_pDocHostUIHandler);
if (SUCCEEDED(hr))
{
// Successfully advised
}
}
}
ClientCall的主要程式碼片段:
class ClientCall:public IDispatch
{
HRESULT _stdcall GetIDsOfNames(
REFIID riid,
OLECHAR FAR* FAR* rgszNames,
unsigned int cNames,
LCID lcid,
DISPID FAR* rgDispId
)
{
if(lstrcmp(rgszNames[0], L"CppCall")==0)
{
//網頁呼叫window.external.CppCall時,會呼叫這個方法獲取CppCall的ID
*rgDispId = 100;
}
return S_OK;
}
HRESULT _stdcall Invoke(
DISPID dispIdMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS* pDispParams,
VARIANT* pVarResult,
EXCEPINFO* pExcepInfo,
unsigned int* puArgErr
)
{
if(dispIdMember == 100)
{
//網頁呼叫CppCall時,或根據獲取到的ID呼叫Invoke方法
CppCall(pDispParams->rgvarg[0].intVal);
}
return S_OK;
}
}
以上就是主要的程式碼段和一些說明。
5.後記
需要完整程式碼的可以下載:http://download.csdn.net/detail/nanjun520/9674092 不需要積分,有csdn賬號就行。
目前的demo都是在主ui執行緒裡面執行,這個在網路不好的情況下會阻塞主ui,大家在使用的時候可以考慮新增執行緒來解決,也可以查詢一下是否有其他的引數設定可以解決這個問題。
IWebBrowser2的功能挺多的,本文只是挑選了幾個 個人感覺常用的功能,其他的功能大家可以去深挖一下,IWebBrowser2->Navigate2 函式也可以配置post資料和引數,不過一般比較少這樣用。
作為碼農,需要學習的東西挺多的,基於興趣來學習還是比較有動力的。畢竟程式是碼農創造的,身為碼農,大家一起努力。 文章就寫到這來,有問題歡迎大家一起討論,如果文章或者是原始碼有錯誤 歡迎指正。