HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt 建立項目,預設值指向腳本頁面 URL。Contexts DWORD 值決定右鍵點擊何種元素時顯示 (位元組合):預設(0x1)、圖片(0x2)、控制項(0x4)、表單(0x8)、選取文字(0x10)、錨點(0x20)、超連結(0x40)。例:圖片+超連結 = 0x22。external.menuArguments.document (嘗試失敗,改用視窗枚舉法)。IHTMLDocument2 → 解析選取範圍 → 下載圖片資源 → 修正連結 → 儲存本地。
// 選單項
HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\「我的采集工具」
預設值 = "file:///C:/Path/To/Script.htm"
Contexts = dword:00000022 ; 圖片+錨點
// 解除安裝資訊
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\程序員加油站
ArticlePath = "儲存目錄" (字串)
ArticleNumber = 0 (DWORD, 遞增序號)
GetHtmlText()
STDMETHODIMP Ctesta::GetHtmlText() {
char chFilePath[MAX_PATH];
DWORD Number = 0;
CRegistry reg;
reg.Open(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\程序員加油站\\");
if(!reg.ReadDWORD("ArticleNumber", &Number) || !reg.ReadString("ArticlePath", chFilePath))
return S_FALSE;
HWND hWnd = GetActiveWindow(); // 取得前景視窗
CoInitialize(NULL);
// 載入 MSAA (OLEACC.DLL) 取得 ObjectFromLresult
HINSTANCE hInst = ::LoadLibrary(_T("OLEACC.DLL"));
if(hInst && hWnd) {
HWND hWndChild = NULL;
::EnumChildWindows(hWnd, EnumChildProc, (LPARAM)&hWndChild); // 找 IE 子視窗
if(hWndChild) {
CComPtr pHTMLDoc;
LRESULT lRes;
UINT nMsg = ::RegisterWindowMessage(_T("WM_HTML_GETOBJECT"));
::SendMessageTimeout(hWndChild, nMsg, 0, 0, SMTO_ABORTIFHUNG, 1000, (DWORD*)&lRes);
LPFNOBJECTFROMLRESULT pfObjectFromLresult = (LPFNOBJECTFROMLRESULT)::GetProcAddress(hInst, _T("ObjectFromLresult"));
if(pfObjectFromLresult) {
HRESULT hr = (*pfObjectFromLresult)(lRes, IID_IHTMLDocument, 0, (void**)&pHTMLDoc);
if(SUCCEEDED(hr)) {
CComPtr pSelObj;
CComPtr pTxtRange;
pHTMLDoc->get_selection(&pSelObj);
hr = pSelObj->createRange((IDispatch**)&pTxtRange);
if(FAILED(hr)) return hr;
pTxtRange->select(); // 選取所有選中內容
BSTR bstrTxt, bstrTxt1;
pTxtRange->get_htmlText(&bstrTxt); // 取得選區 HTML
char* strTxt = _com_util::ConvertBSTRToString(bstrTxt);
// 取得主機域名 (用於相對路徑轉絕對)
CComPtr pLocation;
pHTMLDoc->get_location(&pLocation);
pLocation->get_hostname(&bstrTxt1);
char* strTxt1 = _com_util::ConvertBSTRToString(bstrTxt1);
char strPath[MAX_PATH];
sprintf(strPath, "https://%s", strTxt1);
// 儲存檔案路徑
char chSavePath[MAX_PATH];
sprintf(chSavePath, "%s\\T%ld.htm", chFilePath, Number);
CreateAllDirectory(chSavePath);
// 處理圖片資源並修正連結
std::string webpage;
if(!CheckData(strTxt, strPath, chFilePath, Number, webpage))
SaveCharToFile(strTxt, chSavePath, TRUE);
else
SaveCharToFile(webpage.c_str(), chSavePath, TRUE);
reg.WriteDWORD("ArticleNumber", ++Number);
// 清理
delete[] strTxt1; delete[] strTxt;
SysFreeString(bstrTxt1); SysFreeString(bstrTxt);
}
}
}
::FreeLibrary(hInst);
}
CoUninitialize();
return S_OK;
}
CheckData:解析 HTML、下載圖片、修正 src 路徑
BOOL CheckData(const LPCTSTR data, const LPCTSTR host, const LPCTSTR path,
DWORD Number, std::string& outstring) {
// 遞迴查找
標籤
const char* p1 = FindString(data, "
");
if(!p1||!p2||!p3||!p4) return FALSE;
// 複製標籤前段
int n = p4 - p1 + 1;
char chImgPath[1024];
strncpy(chImgPath, p1, n);
outstring.append(data, p2 - data + 5);
// 提取圖片 URL
n = p3 - p2 - 5;
char chImgSrc[MAX_PATH];
strncpy(chImgSrc, p2+5, n);
char chDownLoadPath[MAX_PATH], chWriteImgSrc[MAX_PATH];
char dirname[20]; ltoa(Number, dirname, 10);
// 判斷相對/絕對路徑並構建下載/儲存路徑
if(FindString(chImgSrc, "https://") == 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]);
}
// 建立目錄並下載
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); // 下載失敗保留原路徑
}
// 路徑分隔符統一為反斜線
for(int i=0; chWriteImgSrc[i]; i++)
if(chWriteImgSrc[i]=='/') chWriteImgSrc[i]='\\';
outstring.append(chWriteImgSrc);
outstring.append(p3, p4-p3+1);
// 遞迴處理剩餘內容
if(!CheckData(p4+1, host, path, Number, outstring))
outstring.append(p4+1);
return TRUE;
}
SaveCharToFile(data, filename, append):字串存檔 (覆寫/附加)。FindString(source, key):字串查找 (封裝 strstr)。CreateAllDirectory(path):遞迴建立多層目錄 (如 d:\a\b\c\d)。WM_HTML_GETOBJECT → ObjectFromLresult。若知曉更優雅方式 (如直接從 external.menuArguments 取得) 請告知。../、./、協議相對 //)。本方案實現「右鍵選取 → 自動儲存 HTML 與圖片資源到本地」,耗時一周完成。核心價值在於 IE 擴展入口 + ATL 組件 DOM 操作 + 資源下載修正 的完整鏈路。可延伸應用於離線閱讀、內容歸檔、素材采集等場景。
作者 Email: lgtest@sina.com,歡迎交流。