在VCKBASE 混了這麼久竟然沒有寫出一篇文章,想想很是慚愧,每當在這裡看到一篇好文,這種感覺尤甚,總結我在程序員加油站中的一些技術點寫了這個文章(雖然程序員加油站還要繼續開發,但是由於時間關系不知道什麼時候能完成),如果有時間我還會寫一些文章的,我的寫作水平可能很差,希望讀者能夠包涵。
程序原理:
一、在IE菜單中加入菜單項
在注冊表HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt項下建立一個新項,項的名稱即為出現在菜單中的標題
將新建項的默認值設定為一個URL地址,當用戶點擊菜單項後,IE就會調用URL指向的頁面中的腳本。
二、如何控制菜單項在合適的時候顯示
下面再介紹一下上面注冊項中Contexts項的作用,通過該項可以制定菜單項在右鍵點擊IE中的什麼對象時出現,它可以為以下值的“或”組合
對象 值 缺省 0x1 圖片 0x2 控件 0x4 表單域 0x8 選擇文本 0x10 錨點 0x20 超鏈接 0x22
例如上面我們希望菜單項在用戶點擊圖片或者超鏈接時出現,那麼我們就將值設置為
dword:00000022
既在點擊圖片 或者 錨點時出現菜單。一個錨點是頁面中描述一個超鏈接的對象。如果不設置Contexts 項,則菜單項會在點擊任何對象時出現在右鍵菜單中。
注:
一二部分我引用了《如何在IE右鍵菜單中添加菜單項以及如何添加IE任務欄按鈕》這篇文章的部分內容,詳細內容請看:
http://www.csdn.net/develop/read_article.asp?id=3621
三、編輯點擊菜單項執行的script腳本
這個腳本的文件名和1中的鏈接文件一致這個是我用的腳本:
<script language="VBScript">
Sub OnContextMenu()
set nc=CreateObject("Test2.testa.1")
nc.GetHtmlText()
end sub
Call OnContextMenu()
</script>
//看到網海拾貝中這樣用
<script language="VBScript">
Sub OnContextMenu()
NCWEBPAGE=1
NCSELWEBPAGE=2
NCSELTEXT=3
NCALLTEXT=4
NCIMAGE=5
NCALLIMAGE=6
NCALLLINK=7
NCALLLINKTITLE=8
NCSELSOURCECODE=9
NCSOURCECODE=10
On Error Resume Next
set nc=CreateObject("NcActive.NcCollect")
if err<>0 then
MsgBox("網海拾貝沒有正確安裝")
else
//但是這個參數至今不知道如何在控件中得到
call Nc.Gethtmldoc(NcSelText,external.menuArguments.document)
end if
end sub
Call OnContextMenu()
</script>
四、在注冊表中加入
HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall
\\程序員加油站加入ArticlePath - 文檔存盤路徑(string),ArticleNumber -文檔序號(dword)。
五、寫一個ATL的DLL,就是上面腳本中調用的那個對象,提供一個接口GetHtmlText(),看到有些其他程序使用external.menuArguments.document做為參數,可是我沒試驗成功,無法直接獲得其中的document所以只能用笨方法了,取得當前窗口,然後取得IE子窗口的句柄,然後取得document指針,取得選取的內容,然後保存網頁,並下載圖片。
下面就介紹一下ATL組件的制作,主要技術包括ATL編程,IE編程,注冊表操作。
1,用ATL COM模板創建一個工程
2,加入一個接口testa
3,加入Urlmon.Lib 和 #include <urlmon.h>
4,加入接口函數GetHtmlText()
實現如下:
STDMETHODIMP Ctesta::GetHtmlText()
{
//保存網頁內容的目錄
char chFilePath[MAX_PATH];
DWORD Number = 0;
CRegistry reg;
reg.Open(HKEY_LOCAL_MACHINE,
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\程序員加油站\\");
BOOL ret = reg.ReadDWORD("ArticleNumber",&Number);
if(!ret)
return S_FALSE;
//讀取保存網頁文件的目錄
ret = reg.ReadString("ArticlePath",chFilePath);
if(!ret)
return S_FALSE;
//取得當前活動窗口的窗口句柄
HWND hWnd = GetActiveWindow();
CoInitialize( NULL );
//顯式裝載 MSAA 判斷是否被安裝
HINSTANCE hInst = ::LoadLibrary( _T("OLEACC.DLL") );
if ( hInst != NULL )
{
if ( hWnd != NULL )
{
HWND hWndChild=NULL;
// 取得當前窗口的IE子窗口指針
::EnumChildWindows( hWnd, EnumChildProc, (LPARAM)&hWndChild );
if ( hWndChild )
{
//定義IE文檔
CComPtr pHTMLDoc;
LRESULT lRes;
//由於WM_HTML_GETOBJECT非Windows標准消息,所以需要RegisterWindowMessage
UINT nMsg = ::RegisterWindowMessage( _T("WM_HTML_GETOBJECT") );
::SendMessageTimeout( hWndChild, nMsg, 0L, 0L, SMTO_ABORTIFHUNG, 1000, (DWORD*)&lRes );
LPFNOBJECTFROMLRESULT pfObjectFromLresult = (LPFNOBJECTFROMLRESULT)::GetProcAddress( hInst, _T("ObjectFromLresult") );
if ( pfObjectFromLresult != NULL )
{
HRESULT hr;
//獲取網頁的IHTMLDocument2接口
hr = (*pfObjectFromLresult)( lRes, IID_IHTMLDocument, 0, (void**)&pHTMLDoc );
if ( SUCCEEDED(hr) )
{
CComPtr pSelObj;
CComPtr pTxtRange;
//根據IHTMLDocument2指針取得IHTMLSelectionObject接口指針
pHTMLDoc->get_selection(&pSelObj);
//再獲得IHTMLTxtRange指針
hr = pSelObj->createRange((IDispatch**)&pTxtRange);
if(!CheckResult(hr,"pTxtRange"))
return hr;
//選擇所有被選擇的內容
pTxtRange->select();
BSTR bstrTxt,bstrTxt1;
char strPath[MAX_PATH];
char *strTxt = NULL;
char*strTxt1 = NULL;
//取得主站域名
CComPtr pLocation;
pHTMLDoc->get_location(&pLocation);
pLocation->get_hostname(&bstrTxt1);
strTxt1 = _com_util::ConvertBSTRToString(bstrTxt1);
sprintf(strPath,"http://%s",strTxt1);
SaveCharToFile(strTxt1,debugpath);
//取得選中的內容
pTxtRange->get_htmlText(&bstrTxt);
strTxt = _com_util::ConvertBSTRToString(bstrTxt);
//下載內容中的圖片資源,並修改相應鏈接
std::string webpage;
char chSavePath[MAX_PATH];
sprintf(chSavePath,"%s\\T%ld.htm",chFilePath,Number);
CreateAllDirectory(chSavePath);
//取得所有圖片資源並保存網頁
if(CheckData(strTxt,strPath,chFilePath,Number,webpage) == FALSE)
SaveCharToFile(strTxt,chSavePath,TRUE);
else
SaveCharToFile(webpage.c_str(),chSavePath,TRUE);
BOOL ret = reg.WriteDWORD("ArticleNumber",++Number);
//釋放內存
if(strTxt1)
delete[] strTxt1;
if(strTxt)
delete[] strTxt;
SysFreeString(bstrTxt1); // 用完釋放
SysFreeString(bstrTxt); // 用完釋放
}
}
}
} // else Internet Explorer is not running
::FreeLibrary( hInst );
} // else Active Accessibility is not installed
CoUninitialize();
return S_OK;
}
在取得選取網頁內容之後,需要對HTML內容進行重新編輯以獲得正確的顯示,CheckData函數便是做這個工作的這個函數對HTML內容中圖像信息的地址進行編輯,如果圖像保存在本地則使用本地圖像,如果圖像沒有下載保持原樣。
BOOL CheckData(const LPCTSTR data,
const LPCTSTR host,
const LPCTSTR path,
DWORD Number,
std::string &outstring)
{
char chImgPath[1024];//圖像路徑 !:由於有些圖像路徑可能會很長所以申請內存多一點
char chImgSrc[MAX_PATH];//圖像地址
char chDownLoadPath[MAX_PATH];//下載圖像文件路徑
char chWriteImgSrc[MAX_PATH];//圖像文件路徑
memset(chImgPath,0,1024);
memset(chImgSrc,0,MAX_PATH);
memset(chDownLoadPath,0,MAX_PATH);
memset(chWriteImgSrc,0,MAX_PATH);
char dirname[20];//根目錄名
ltoa(Number,dirname,10);
try{
//查找是否有<img標記
const char *p1 = FindString(data,"<img");
const char *p2 = FindString(p1,"src=\"");
const char *p3 = FindString(p2+5,"\"");
const char *p4 = FindString(p1,">");
if(p1 == NULL||p2 == NULL||p3 == NULL||p4 == NULL)
return FALSE;
//找到一個圖像文件標記
//拷貝圖像鏈接之前的文字
int n = p4-p1+1;
strncpy(chImgPath,p1,n);
outstring.append(data,p2-data+5);
//提取圖像鏈接
n = p3-p2-5;
strncpy(chImgSrc,p2+5,n);
if(FindString(chImgSrc,"http://") == NULL){
if(FindString(chImgSrc,".."))
strcpy(chImgSrc,&chImgSrc[2]);
sprintf(chDownLoadPath,"%s%s",host,chImgSrc);
sprintf(chWriteImgSrc,"%s//%s%s",path,dirname,chImgSrc);
}else{
strcpy(chDownLoadPath,chImgSrc);
const char *p5 = FindString(chImgSrc+7,"/");
sprintf(chWriteImgSrc,"%s\\%s%s",path,dirname,&chImgSrc[p5-chImgSrc]);
}
char Output[MAX_PATH];
sprintf(Output,"圖像地址:%s\r\n存盤地址:%s\r\n主機地址:%s\r\n",chImgSrc,chWriteImgSrc,host);
SaveCharToFile(Output,debugpath);
n = strlen(chWriteImgSrc);
for(int i=0;i<n;i++){
if(chWriteImgSrc[i] == ''''/'''')
chWriteImgSrc[i] = ''''\\'''';
}
//在下載之前先建立保存圖像文件的路徑
CreateAllDirectory(chWriteImgSrc);
//下載圖像文件
HRESULT hr = URLDownloadToFile( NULL, chDownLoadPath, chWriteImgSrc, 0, NULL);
if( SUCCEEDED(hr))//
{
const char *p6 = FindString(chDownLoadPath+7,"/");
sprintf(chWriteImgSrc,"%s%s",dirname,&chDownLoadPath[p6-chDownLoadPath]);
//將存盤的路徑保存進字符串
}else{
strcpy(chWriteImgSrc,chDownLoadPath);
//沒有下載成功將原始路徑保存進
}
outstring.append(chWriteImgSrc);
outstring.append(p3,p4-p3+1);
BOOL ret = CheckData(p4+1,host,path,Number,outstring);
if(!ret){
outstring.append(p4+1);
}
}catch(...){
return FALSE;
}
return TRUE;
}
其他幾個輔助函數這裡簡單介紹,詳細內容請參看源代碼
函數:void SaveCharToFile
功能:將字符串保存為給定文件名
const LPCTSTR data,//[IN] 給定待保存的數據
const LPCTSTR saveFileName,//[IN]給定文件名
BOOL flag = FALSE//[IN]追加寫入還是覆蓋標志
函數:const char *FindString
功能:在給定字符串中查找給定字符串
const LPCTSTR source,//[IN] 給定源字符串的數據
const LPCTSTR key)//[IN]待查字符串
函數:void CreateAllDirectory
功能:創建給定路徑的所有未建目錄
const char* AllPath)//[IN] 需要創建的詳細目錄
例如 輸入為:"d:\a\b\c\d"則在D盤下創建相應a,b,c,d相應目錄
好了,到此為止,我們已經做成一個取得網頁選取內容的並保存在本地的程序,你可以舉一反三,將它應用到更有趣更有用的領域。
這個程序是我研究了一周才寫完的,其中取得domument的方法我覺得很笨,可我現在沒有找到好辦法,希望能夠拋磚引玉,有更好的方法別忘了告訴我,我的Email:[email protected]歡迎來信討論。
本文配套源碼