在 Internet Explorer 窗口中添加状态栏窗格并在原地取消网页密码的显示






4.63/5 (12投票s)
本文演示了如何向 Internet Explorer 窗口添加状态栏窗格,以及如何使用它来管理网页密码字段。

引言
在浏览网页时,Internet Explorer 状态栏指示器非常有用,并允许访问几个内置应用程序,例如弹出窗口阻止程序、网络钓鱼过滤器或安全小程序。与 IE 工具栏按钮不同,微软并未向开发者说明如何向 Internet Explorer 窗口添加状态栏窗格。
本文展示了如何使用子类化技巧添加状态栏窗格。
首先,找到并子类化状态栏窗口,然后创建一个窗格窗口(作为 STATIC 控件)。接下来,选择任何现有的窗格,调整其大小以插入一个新的窗格,并将窗格窗口放置在所需的现有窗格上方。
新的状态窗格应与任何典型的窗格完全相同地绘制和主题化(适用于 XP 或 Vista),并且应处理鼠标单击以防止来自其后方原生窗格的通知。
通用步骤
- 创建一个从 Internet Explorer 内部启动的浏览器助手对象
extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/) { if (dwReason == DLL_PROCESS_ATTACH) { TCHAR szLoader[MAX_PATH]; memset(szLoader,0,MAX_PATH); GetModuleFileName(NULL, szLoader, MAX_PATH); _tcslwr(szLoader); if (_tcsstr(szLoader,_T("iexplore.exe")) == 0 && _tcsstr(szLoader,_T("regsvr32.exe")) == 0) return FALSE;
- 找到状态栏窗口并子类化它
LONG nBrowser = NULL; m_pWebBrowser2->get_HWND(&nBrowser); if(nBrowser) { g_hIcon = LoadIcon(_Module.m_hInst, MAKEINTRESOURCE(IDI_ICON_START)); TCHAR szClassName[MAX_PATH]; HWND hWndStatusBar = NULL; // looking for a TabWindowClass window in IE7 // the last one should be parent for statusbar HWND hTabWnd = GetWindow((HWND)nBrowser, GW_CHILD); if(hTabWnd) { while(hTabWnd) { memset(szClassName,0,MAX_PATH); GetClassName(hTabWnd, szClassName, MAX_PATH); if(_tcscmp(szClassName, _T("TabWindowClass")) == 0) nBrowser = (LONG)hTabWnd; hTabWnd = GetWindow(hTabWnd, GW_HWNDNEXT); } } HWND hWnd = GetWindow((HWND)nBrowser, GW_CHILD); if(hWnd) { while(hWnd) { memset(szClassName,0,MAX_PATH); GetClassName(hWnd, szClassName, MAX_PATH); if(_tcscmp(szClassName,_T("msctls_statusbar32")) == 0) { if(hWnd) hWndStatusBar = hWnd; break; } hWnd = GetWindow(hWnd, GW_HWNDNEXT); } } if(hWndStatusBar) { g_pWndProcStatus = (WNDPROC)SetWindowLong(hWndStatusBar, GWL_WNDPROC, (LPARAM)(WNDPROC)NewStatusProc); ...
- 创建一个窗格窗口作为 STATIC 控件并子类化它
HWND hWndNewPane = CreateWindowEx(NULL, _T("static"),_T(""),WS_CHILD | WS_VISIBLE,0,0,0,0,hWndStatusBar,(HMENU)3333,_Module.m_hInst,NULL); if(hWndNewPane && IsWindow(hWndNewPane)) { HFONT hFont = (HFONT)SendMessage(hWndStatusBar, WM_GETFONT, 0, 0); SendMessage(hWndNewPane, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0)); GetUxtheme(hWndNewPane); g_pWndProcPane = (WNDPROC)SetWindowLong(hWndNewPane, GWL_WNDPROC, (LPARAM)(WNDPROC)PaneWindowProc); ...
- 处理状态栏的重要消息
static LRESULT CALLBACK NewStatusProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { ... switch (message) { case SB_SIMPLE: // hide the pane when the user scrolls the menu ::ShowWindow(g_hWndNewPane, !(BOOL)wParam); break; case WM_SYSCOLORCHANGE: // query updated XP theme GetUxtheme(g_hWndNewPane); break; ... case SB_SETPARTS: // resize the existing pane and move the new pane over { if(lParam && wParam) { unsigned int nParts = wParam; HLOCAL hLocal = LocalAlloc(LHND, sizeof(int) * (nParts+1)); LPINT lpParts = (LPINT)LocalLock(hLocal); memcpy(lpParts, (void*)lParam, wParam*sizeof(int)); g_nNewPaneWidth = 32; unsigned nCurWidth = 0; for(unsigned j=0;j<nParts-1;j++) { nCurWidth = lpParts[j+1]-lpParts[j]; if(g_nNewPaneWidth > nCurWidth && nCurWidth>1) g_nNewPaneWidth = nCurWidth; } g_nNewPaneWidth += 2; if(g_nNewPanePosition<nParts) { for(unsigned i=0;i<g_nNewPanePosition;i++) lpParts[i] -= g_nNewPaneWidth; } LRESULT hRet = CallWindowProc(g_pWndProcStatus, hWnd, message, wParam, (LPARAM)lpParts); CRect rcPane; SendMessage(hWnd, SB_GETRECT, g_nNewPanePosition, (LPARAM)&rcPane); if(IsWindow(g_hWndNewPane)) MoveWindow(g_hWndNewPane, lpParts[g_nNewPanePosition] - g_nNewPaneWidth, rcPane.top, g_nNewPaneWidth, rcPane.Height(), TRUE); LocalFree(hLocal); return hRet; } } break; default: break; }
- 处理新窗格的
WM_PAINT
消息
static LRESULT CALLBACK PaneWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_PAINT: { PAINTSTRUCT ps; HDC hDC = BeginPaint(hWnd, &ps); CRect rcClient; GetClientRect(hWnd, &rcClient); int nDrawEdge = 0; // not themed if(g_hTheme == NULL) { FillRect(hDC, &rcClient, (HBRUSH)(COLOR_BTNFACE+1)); DrawEdge(hDC,&rcClient,BDR_RAISEDINNER,BF_LEFT); rcClient.left += 3; nDrawEdge = 3; DrawEdge(hDC,&rcClient,BDR_SUNKENOUTER,BF_RECT); } else { DRAWTHEMEBACKGROUND pfnDrawThemeBackground = (DRAWTHEMEBACKGROUND)::GetProcAddress (g_hUxtheme, "DrawThemeBackground"); if(pfnDrawThemeBackground) pfnDrawThemeBackground(g_hTheme, hDC, SP_PANE, NULL, &rcClient, NULL); // copy separator picture from right to left int nHeight = rcClient.Height(); int nWidth = rcClient.Width() - 2; for(int i=0;i<2;i++) { for(int j=0;j<nHeight;j++) SetPixel(hDC,i,j,GetPixel(hDC,i+nWidth,j)); } } if(g_hIcon) DrawIconEx(hDC,(rcClient.Width() - 16)/2 + nDrawEdge, (rcClient.Height() - 16)/2, g_hIcon,16,16,NULL,NULL,DI_NORMAL); EndPaint(hWnd, &ps); return 0; } ...
默认情况下,Internet Explorer 状态栏包含几个窗格。第一个窗格(左侧)是一个分隔符,可以显示菜单提示和已访问的网页通知。第二个窗格通常是不可见的,包含一个进度控件。当 Web 导航开始时,此窗格变为可见并可以显示下载进度。接下来的窗格可以被 Internet Explorer 的内置工具占用,例如弹出窗口阻止程序或网络钓鱼过滤器。
例如,我决定将新窗格放置在第三个位置,并保留足够的空间来显示一个小图标
UINT g_nNewPaneWidth = 20;
UINT g_nNewPanePosition = 2;
因此,现在窗格已完成并准备好使用。
在附带本文的示例代码中,有一个新创建的窗格用于访问名为 Asterisk Master 的密码显示应用程序,该应用程序已发布在 我的网站上。

当 Internet Explorer 完成打开任何网页后,Asterisk Master 开始搜索密码输入框,并将其替换为普通的文本输入框,因此所有隐藏字符都变得可读。
密码输入框可以在 HTML 中通过 type=password
属性定义。
<INPUT id=inputPassword type=password value="" name=passwd>
如果在运行时将 type=password
属性替换为 type=text
,所有星号遮蔽的字符都将变得可读。
因此,订阅 IWebBrowser2
事件,并在触发 DISPID_DOWNLOADCOMPLETE
时处理已访问的网页
STDMETHODIMP CFieldManager::Invoke(DISPID dispidMember, REFIID riid, LCID lcid,
WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pvarResult,
EXCEPINFO* pExcepInfo, UINT* puArgErr)
{
if (!pDispParams)
return E_INVALIDARG;
switch (dispidMember)
{
case DISPID_DOWNLOADBEGIN:
g_hIcon = LoadIcon(_Module.m_hInst, MAKEINTRESOURCE(IDI_ICON_START));
RedrawWindow(g_hWndNewPane,NULL,NULL,RDW_INVALIDATE|RDW_UPDATENOW);
break;
case DISPID_DOWNLOADCOMPLETE:
m_bPasswordsFound = FALSE;
ShowPasswords();
if(m_bPasswordsFound)
{
if(g_bUnmaskNeeded)
g_hIcon = LoadIcon(_Module.m_hInst, MAKEINTRESOURCE(IDI_ICON_UNLOCK));
else
g_hIcon = LoadIcon(_Module.m_hInst, MAKEINTRESOURCE(IDI_ICON_LOCK));
}
else
g_hIcon = LoadIcon(_Module.m_hInst, MAKEINTRESOURCE(IDI_ICON_START));
RedrawWindow(g_hWndNewPane,NULL,NULL,RDW_INVALIDATE|RDW_UPDATENOW);
break;
default:
break;
}
接下来,ShowPassword()
函数扫描网页中的密码字段,并在需要时替换 type
属性。
请随时发布错误、问题或请求。