前言
本人是在家中上網,經常有一些BBS的密碼懶得記了,就用IE的自動密碼保存功能,這樣一來是方便了,但卻有一個麻煩,一旦機子不行了,想要重裝操作系統了,這些密碼卻也取不出了,還得重新申請,好麻煩!因此我就寫了一個工具,可以取得網頁密碼框的密碼.
因為網頁密碼框不是一般的EDIT控件,因此不能取得網頁密碼框的句柄.要實現這個功能,只好通過WebBrowser控件的有關COM接口了.因此取得這些接口是整個程序的關鍵.有兩種方法可以取得WebBrowser控件的接口,接下來我們會逐一介紹,並提供示例源代碼供大家參考。
示例程序運行效果圖
第一種方法:使用腳本語言和IE右鍵菜單
我們可以使用注冊表來控制IE右鍵菜單.當你裝了FlashGet(網際快車)時,你會發現IE右鍵菜單多了兩項:"使用網際快車下載"和"使用網際快車下載全部鏈接",而這時你打開注冊表,在HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\下有這兩個主鍵.這兩個主鍵下都有兩個值,一個是默認的串值,指定了選擇了這個菜單命令要打開的URL,IE在一個隱藏的窗口打開它,並這個隱藏窗口的external.menuArguments值設為當前窗口對象,執行完URL對話網頁包含的腳本程序該窗口自動關閉.另一個名稱是contexts是DWORD值,指定了在什麼情況下需要顯示這個菜單項.具體的值見下.
(0x1 << CONTEXT_MENU_DEFAULT) (等於 0x1) //缺省時顯示
(0x1 << CONTEXT_MENU_IMAGE) (等於 0x2) //右鍵點擊圖像時顯示該項
(0x1 << CONTEXT_MENU_CONTROL) (等於 0x4) //右鍵點擊表單元素時顯示該項
(0x1 << CONTEXT_MENU_TABLE) (等於 0x8) //右鍵點擊表格時顯示該項
(0x1 << CONTEXT_MENU_TEXTSELECT) (等於 0x10) //右鍵點擊高亮選擇的文本時顯示該項
(0x1 << CONTEXT_MENU_ANCHOR) (等於 0x20) //右鍵點擊鏈接時顯示該項
(0x1 << CONTEXT_MENU_UNKNOWN) (等於 0x40)//右鍵點擊網頁中除上以外的地方顯示該項
現在我們寫一段腳本程序以獲取密碼框的值.
Sub GetPassword() set srcEvent = external.menuArguments.event Set doc=external.menuArguments.document set ele=doc.elementFromPoint( srcEvent.clientX, srcEvent.clientY ) if ele.type ="password" then if ele.value="" then Alert("密碼為空") else Alert("密碼為:"+ele.value) end if end if end sub call GetPassword()
然後在注冊表HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\下新建一下主鍵,鍵名為"取得密碼",缺省值設為該htm文件的路徑,在該主鍵下另增一個DWORD值,值為4,表示只在右擊表單元素時顯示該項.關閉注冊表,重新啟動IE窗口,點擊一下密碼框,就會出現該項,點擊該項,彈出一個對話框,告訴你的密碼 。
第二種方法:使用VC來實現
由於VC知識庫是一個關於C++以及Visual C++的網站,與腳本語言沒什麼關系。所以我們要用另一種稍微復雜一點的方法來實現相同的事情,那就是用C++來做。在不同的進程中取得IE的Webbrowser控件的IHTMLDocument2接口,請參閱MSDN上的一篇文章,標題是:HOWTO: Get IHTMLDocument2 from a HWND(根據HWND取得IHTMLDocument2接口)(http://support.microsoft.com/default.aspx?scid=kb;EN-US;q249232).它的實現機理是向Webbrowser控件(窗口類名是"Internet Explorer_Server")發一個WM_HTML_GETOBJECT,然後把返回值傳給Microsoft Active Accessibility (MSAA) 函數ObjectFromLresult,這樣你會取得一個已經編排(Marshaling)過的COM接口.如下函數所示:
IHTMLDocument2* GetDocInterface(HWND hWnd) { // 我們需要顯示地裝載OLEACC.DLL,這樣我們才知道有沒有安裝MSAA HINSTANCE hInst = ::LoadLibrary( _T("OLEACC.DLL") ); IHTMLDocument2* pDoc2=NULL; if ( hInst != NULL ){ if ( hWnd != NULL ){ CComPtr spDoc=NULL; LRESULT lRes; /*由於WM_HTML_GETOBJECT非Windows標准消息,所以需要RegisterWindowMessage*/ UINT nMsg = ::RegisterWindowMessage( _T("WM_HTML_GETOBJECT") ); ::SendMessageTimeout( hWnd, nMsg, 0L, 0L, SMTO_ABORTIFHUNG, 1000, (DWORD*)&lRes ); /*取得ObjectFromLresult函數的地址*/ LPFNOBJECTFROMLRESULT pfObjectFromLresult = \ (LPFNOBJECTFROMLRESULT)::GetProcAddress( hInst, _T("ObjectFromLresult") ); if ( pfObjectFromLresult != NULL ){ HRESULT hr; hr=pfObjectFromLresult(lRes, IID_IHTMLDocument, 0, (void**)&spDoc); if ( SUCCEEDED(hr) ){ CComPtr spDisp; CComQIPtr spWin; spDoc->get_Script( &spDisp ); spWin = spDisp; spWin->get_document( &pDoc2 ); } } } ::FreeLibrary(hInst); } else{//如果沒有安裝MSAA AfxMessageBox(_T("請您安裝Microsoft Active Accessibility")); } return pDoc2; }
這樣,我們就取得了IHTMLDocument2*接口了,要取得密碼框的密碼還得一番周折,首先得構造一個基於對話框的MFC程序,增加一個按鈕,在主對話框類增加一個成員變量m_bCapture,在構造函數中初始化為FALSE. 然後處理該按鈕的Click事件:
void CXXXXDlg::OnGetHtmlClick() { SetCapture();//跟蹤鼠標 m_bCapture=TRUE; }
接著應該處理WM_LBUTTONUP消息:
void CXXXXDlg::OnLButtonUp(UINT nFlags, CPoint point) { if(m_bCapture){ m_bCapture=FALSE; ReleaseCapture();//釋放鼠標 static TCHAR buf[100]; POINT pt; GetCursorPos(&pt); HWND hwnd=::WindowFromPoint(pt); if(hwnd!=NULL){ ::GetClassName( hwnd, (LPTSTR)&buf, 100 ); if ( _tcscmp( buf, _T("Internet Explorer_Server") ) == 0 ){ POINT iept=pt; ::ScreenToClient(hwnd,&iept); GetPassword(GetDocInterface(hwnd),iept); } } } CDialog::OnLButtonUp(nFlags, point); }
GetPassword函數是這樣實現的,基本可以模仿VBScript的調用,但要復雜一些:
void GetPassword(IHTMLDocument2* pDoc2,POINT pt) { if(pDoc2==NULL)return; CComPtr pElement; HRESULT hr=pDoc2->elementFromPoint(pt.x,pt.y,&pElement);//取得鼠標所在的元素 if(SUCCEEDED(hr)){ CComPtr pPwdElement; hr=pElement->QueryInterface(IID_IHTMLInputTextElement, (void**)&pPwdElement);//是否有表單輸入元素 if(SUCCEEDED(hr)){ CComBSTR type; hr=pPwdElement->get_type(&type); if(SUCCEEDED(hr)){ if(type==_T("password")){//是密碼框嗎? CComBSTR pwd; hr=pPwdElement->get_value(&pwd); if(SUCCEEDED(hr)){ if(pwd.Length()!=0){//有密碼則顯示 CComBSTR msg=_T("密碼是:"); msg+=pwd; CString str(msg); AfxMessageBox(str); } else{ AfxMessageBox(_T("密碼為空!")); } } } } } } pDoc2->Release(); }
使用這種方法要注意:
如果程序在Windows95,98和NT 4.0 Service With Pack 4 or 5下運行必須要把Microsoft Active Accessibility (MSAA)運行時組件(RDK)與程序一起發布(Windows2000及Windows NT 4.0 Service With Pack 6中已經有了,所以不用);
這種方法只適用用於Internet Explorer (Programming) versions 4.0, 4.01, 4.01 SP1, 4.01 SP2, 5;
使用這種方法前要調用CoInitialize(NULL);然後應該相應地調用CoUninitialize();
Microsoft Active Accessibility (MSAA)可從 http://www.microsoft.com/enable/msaa/download.htm 下載 ;
附: 我們也可以使用Active Accessibility(MSAA)獲取IHTMLDocument2*接口,見下程序:
/* 函數名:GetDocInterfaceByMSAA 參數:hwnd,WebBrowser控件的窗口句柄 功能:取得hwnd對應的Webbrowser控件的IHTMLDocument2*接口. */ IHTMLDocument2* GetDocInterfaceByMSAA(HWND hwnd) { HRESULT hr; HINSTANCE hInst = ::LoadLibrary( _T("OLEACC.DLL") ); IHTMLDocument2* pDoc2=NULL; if ( hInst != NULL ){ if ( hwnd != NULL ){ //取得AccessibleObjectFromWindow函數 LPFNACCESSIBLEOBJECTFROMWINDOW pfAccessibleObjectFromWindow = (LPFNACCESSIBLEOBJECTFROMWINDOW)::GetProcAddress(hInst, _T("AccessibleObjectFromWindow")); if(pfAccessibleObjectFromWindow != NULL){ CComPtr spAccess; hr=pfAccessibleObjectFromWindow(hwnd,0, //取得Webbrowser控件的IAccessible接口 IID_IAccessible,(void**) &spAccess); if ( SUCCEEDED(hr) ){ CComPtr spServiceProv; hr=spAccess->QueryInterface(IID_IServiceProvider, (void**)&spServiceProv); if(hr==S_OK){ CComPtr spWin; hr=spServiceProv->QueryService(IID_IHTMLWindow2, IID_IHTMLWindow2, (void**)&spWin); /* 注意:並不是每次都能取得IHTMLWindow2接口,如果調用失敗, 可以嘗試取得IHTMLElement接口: CComPtr spElement; hr=spServiceProv->QueryService(IID_IHTMLElement, IID_IHTMLElement, (void**)&spElement); */ if(hr==S_OK) spWin->get_document(&pDoc2); } } } } ::FreeLibrary(hInst); } else{ AfxMessageBox(_T("請您安裝Microsoft Active Accessibility")); } return pDoc2; }
具體的例程可見本文提供的源代碼(在WINDOWS ME,IE 5.5,VC6.0調試通過)
本文配套源碼