在MFC应用程序中,经常需要使用WebBrowser控件来嵌入网页内容。通过调用ExecWB
方法,可以执行一些常见的浏览器命令,比如调整浏览器的缩放级别、选择浏览器中的所有文本等。此外,还可以通过调用queryInterface
来获取IDispatch
指针,进而调用IOleCommandTarget
接口的Exec
方法来执行“在本页查找”对话框或“查看源代码”命令。然而,有些对话框无法直接调用,比如“添加到收藏夹”对话框和“导入/导出向导”对话框。虽然可以通过IShellUIHelper
接口调用“AddFavorite”命令和“ImportExportFavorites”命令,但前者会弹出一个与应用程序主框架独立的非模态对话框,而后者的结果则相当简单。
本文将介绍一种创新但简单的方法,来调用Web浏览器中的模态“添加到收藏夹”对话框和模态“导入/导出向导”对话框。
这个想法来源于MSDN上的“WebBrowser Customization”示例中的IDocHostUIHandler::ShowContextMenu
演示。该演示向展示了如何手动构建IE的上下文菜单,并从中移除“查看源代码”选项。代码的关键点在于,在弹出上下文菜单后,它调用SendMessage()
API将TrackPopupMenu()
返回的菜单项ID发送到“Internet Explorer_Server”窗口,通过WM_COMMAND
消息。因此,如果能够获取到“添加到收藏夹”命令或“导入/导出向导”命令的菜单项ID,可能就能解决上述问题。
有些命令消息是由“Internet Explorer_Server”窗口处理的,而有些则是由其父窗口“Shell DocObject View”处理的。因此,首先需要从CHtmlView
中获取这两个窗口的窗口句柄。为了简化查找过程,借用了Paul DiLascia的可爱类CFindWnd
。
class CFindWnd {
private:
static BOOL CALLBACK FindChildClassHwnd(HWND hwndParent, LPARAM lParam) {
CFindWnd *pfw = (CFindWnd*)lParam;
HWND hwnd = FindWindowEx(hwndParent, NULL, pfw->m_classname, NULL);
if (hwnd) {
pfw->m_hWnd = hwnd;
return FALSE;
}
EnumChildWindows(hwndParent, FindChildClassHwnd, lParam);
return TRUE;
}
public:
LPCSTR m_classname;
HWND m_hWnd;
CFindWnd(HWND hwndParent, LPCSTR classname) : m_hWnd(NULL), m_classname(classname) {
FindChildClassHwnd(hwndParent, (LPARAM)this);
}
};
以下是如何调用命令的示例代码:
void CDemoView::InvokeShellDocObjCommand(int nID) {
CFindWnd FindIEWnd(m_wndBrowser.m_hWnd, "Shell DocObject View");
::SendMessage(FindIEWnd.m_hWnd, WM_COMMAND, MAKEWPARAM(LOWORD(nID), 0x0), 0);
}
void CDemoView::InvokeIEServerCommand(int nID) {
CFindWnd FindIEWnd(m_wndBrowser.m_hWnd, "Internet Explorer_Server");
::SendMessage(FindIEWnd.m_hWnd, WM_COMMAND, MAKEWPARAM(LOWORD(nID), 0x0), 0);
}
“Internet Explorer_Server”窗口处理的命令ID:
#define ID_IE_CONTEXTMENU_ADDFAV 2261
#define ID_IE_CONTEXTMENU_VIEWSOURCE 2139
#define ID_IE_CONTEXTMENU_REFRESH 6042
“Shell DocObject View”窗口处理的命令ID:
#define ID_IE_FILE_SAVEAS 258
#define ID_IE_FILE_PAGESETUP 259
#define ID_IE_FILE_PRINT 260
// ... 更多命令ID ...
void CDemoView::OnFavAddtofav() {
InvokeIEServerCommand(ID_IE_CONTEXTMENU_ADDFAV);
}