在开发Web应用程序时,有时需要将当前网页保存为HTML文件,而不需要用户进行任何交互。本文将介绍如何利用Internet Explorer的API实现这一功能。虽然Internet Explorer的“另存为HTML”功能通常对用户是隐藏的,但通过一些编程技巧,可以在不显示任何对话框的情况下实现这一功能。
保存网页为HTML文件时,有多种格式可供选择:
以下是一个使用C++和Internet ExplorerAPI实现无界面保存HTML的示例代码:
LPDISPATCH lpDispatch = NULL;
IPersistFile *lpPersistFile = NULL;
// m_ctrl 是Web Browser控件的实例
lpDispatch = m_ctrl.get_Document();
lpDispatch->QueryInterface(IID_IPersistFile, (void**)&lpPersistFile);
lpPersistFile->Save(L"c:\\htmlpage.html", 0);
lpPersistFile->Release();
lpDispatch->Release();
这段代码展示了如何仅保存HTML代码,而不显示任何对话框。
虽然上述代码可以保存HTML代码,但如果想要获取包含图片等资源的完整HTML存档,就需要采用一些额外的技巧。
由于没有公开或已知的方法可以在不显示对话框的情况下请求这一功能,将通过拦截操作系统的窗口创建事件来实现。然后,请求Internet Explorer提供这一功能,并在不可见的情况下覆盖对话框中的文件路径。最后,模拟用户点击保存按钮来验证对话框并解除拦截。
以下是实现无界面保存HTML的详细工作流程:
以下是实现上述工作流程的示例代码:
// 安装SaveAs对话框钩子
g_hHook = SetWindowsHookEx(WH_CBT, CbtProc, NULL, GetCurrentThreadId());
if (!g_hHook) return false;
// 显示SaveAs对话框
g_bSuccess = false;
g_pWebBrowserSaveAs = this;
HRESULT hr = m_pWebBrowser->ExecWB(OLECMDID_SAVEAS, OLECMDEXECOPT_PROMPTUSER, NULL, NULL);
// 移除钩子
UnhookWindowsHookEx(g_hHook);
g_pWebBrowserSaveAs = NULL;
g_hHook = NULL;
在钩子回调函数中,检查新创建的窗口是否为对话框,并将其隐藏:
LRESULT CALLBACK CSaveAsWebbrowser::CbtProc(int nCode, WPARAM wParam, LPARAM lParam) {
switch (nCode) {
case HCBT_CREATEWND: {
HWND hWnd = (HWND)wParam;
LPCBT_CREATEWND pcbt = (LPCBT_CREATEWND)lParam;
LPCREATESTRUCT pcs = pcbt->lpcs;
if ((DWORD)pcs->lpszClass == 0x00008002) {
g_hWnd = hWnd;
pcs->x = -2 * pcs->cx; // 将对话框移出屏幕
}
break;
}
case HCBT_ACTIVATE: {
HWND hwnd = (HWND)wParam;
if (hwnd == g_hWnd) {
g_hWnd = NULL;
g_bSuccess = true;
if (g_pWebBrowserSaveAs->IsSaveAsEnabled()) {
g_pWebBrowserSaveAs->SaveAsDisable();
CSaveAsThread *newthread = new CSaveAsThread();
newthread->SetKeyWnd(hwnd);
newthread->Config(g_pWebBrowserSaveAs->GetFilename(), g_pWebBrowserSaveAs->GetSaveAsType());
newthread->StartThread();
}
}
break;
}
}
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
在新线程中,等待Internet Explorer的“另存为”对话框准备好并填充数据:
switch (::WaitForSingleObject(m_hComponentReadyEvent, m_WaitTime)) {
// ...
if (::IsWindowVisible(m_keyhwnd)) {
bSignaled = TRUE;
bContinue = FALSE;
}
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
bContinue = FALSE;
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// ...
}
// 重新启动SaveAs类,但现在一切都已准备好
if (bSignaled) {
CSaveAsWebbrowser surrenderNow;
surrenderNow.Config(GetFilename(), GetSaveAsType());
surrenderNow.UpdateSaveAs(m_keyhwnd);
}
// 结束线程,不再关心它
delete this;
最后,覆盖对话框中的文件路径和文件类型,并模拟用户点击保存按钮:
void CSaveAsWebbrowser::UpdateSaveAs(HWND hwnd) {
// 编辑框:文件路径(控件ID = 0x047c)
// 下拉组合框:文件类型(选项=完整页面;归档;仅HTML;文本)(控件ID = 0x0470)
// 保存按钮:控件ID = 0x0001
// 取消按钮:控件ID = 0x0002
// 在组合框中选择正确的项目
SendMessage(GetDlgItem(hwnd, 0x0470), CB_SETCURSEL, (WPARAM) m_nSaveType, 0);
SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(0x0470, CBN_CLOSEUP), (LPARAM) GetDlgItem(hwnd, 0x0470));
// 设置输出文件名
SetWindowText(GetDlgItem(hwnd, 0x047c), m_szFilename);
// 调用保存按钮
SendMessage(GetDlgItem(hwnd, 0x0001), BM_CLICK, 0, 0);
}
在上述代码中,有趣的是,为了选择想要的HTML类型(完整HTML、归档、仅代码或文本格式),不仅在组合框中选择了适当的条目,还向Internet Explorer发送了组合框关闭通知。这是因为这是Internet Explorer订阅的,以知道想要这种HTML。这种行为是通过尝试和错误得知的。
本文介绍了如何利用Internet Explorer的API实现无界面保存网页为HTML文件的技术。虽然在互联网上很少看到关于这个话题的文章,但很容易看出,对于开发Web应用程序的开发者来说,这是一个非常有吸引力的功能。