前言
本人是在家中上網,經常有一些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調試通過)
本文配套源碼