参考了别人提供的方法,自己补充完整了一些,就算作翻译吧。
资源管理器嵌入插件实现说明
一. 核心思想
添加子窗口,使插件窗口与资源管理器的文件列表窗口共同拆分右侧窗口。
二. 技术实现
1. 创建程序
新建ATL程序,去除attributed选项(如果有)。
2. 添加方法
添加简单ATL对象MyBar,勾选IobjectWithSite。
.h文件中添加成员变量。
CComQIPtr
CComPtr
DWORD mCookie;
IDispatch *m_pdisp;
HWND m_hWndShellView;//文件列表控件指针
HWND m_hWndShellViewParent;//文件列表控件父窗口指针
TCHARm_CurrDir[MAX_PATH];//当前文件夹路径
HWND m_hOldWndShellViewParent; // 记录上一次父窗口句柄
为MyBar对象添加SetSite方法:STDMETHOD(SetSite)(IUnknown *pUnkSite);
该方法在BHO对象载入的时候会调用一次,这里可以保存Iunknown指针、判断是不是资源管理器在载入、建立事件响应连接点(用于接收浏览器消息)。
STDMETHODIMPCMyBar::SetSite(IUnknown *pUnkSite)
{
if (pUnkSite)
{
pUnkSite->QueryInterface(IID_IDispatch,(void**)&m_pdisp);
pUnkSite->QueryInterface(IID_IWebBrowser2,(void **)&mWebBrowser2);
//Receives the connection point for WebBrowser events
CComQIPtr
spCPC->FindConnectionPoint(DIID_DWebBrowserEvents2,&m_spWebBrowser);
//Pass the event handlers to the container
m_spWebBrowser->Advise(reinterpret_cast
return S_OK;
}
return E_FAIL;
}
添加invoke响应
STDMETHOD(Invoke)(DISPID dispidMember,REFIID riid, LCID lcid,
WORD wFlags, DISPPARAMS *pDispParams,
VARIANT * pvarResult,EXCEPINFO *pexcepinfo,
UINT * puArgErr);
STDMETHODIMPCHello::Invoke(DISPID dispidMember,REFIID riid, LCID lcid,
WORDwFlags, DISPPARAMS * pDispParams,
VARIANT * pvarResult,EXCEPINFO * pexcepinfo,
UINT * puArgErr)
{
USES_CONVERSION;
if(!pDispParams)
returnE_INVALIDARG;
switch(dispidMember)
{
caseDISPID_BEFORENAVIGATE2 :
break;
caseDISPID_NAVIGATECOMPLETE2:
break;
caseDISPID_DOCUMENTCOMPLETE: //完成打开文件夹时收到的消息,注意会收到2次
break;
caseDISPID_DOWNLOADBEGIN:
break;
caseDISPID_DOWNLOADCOMPLETE:
break;
caseDISPID_NEWWINDOW2:
break;
caseDISPID_WINDOWREGISTERED:
break;
caseDISPID_ONQUIT: //关闭浏览器收到的消息
break;
default:
break;
}
returnS_OK;
}
获取资源管理器窗口信息
添加一个成员函数,来实现资源管理器窗口信息的获取工作。
这个函数,最好在收到DISPID_DOCUMENTCOMPLETE消息时调用。
小提示:每次点击文件夹,进入不同路径时,都会响应DISPID_DOCUMENTCOMPLETE消息,且之前的窗口被销毁(资源管理器右侧文件列表窗口及其父窗口),创建新的窗口。
通过左侧导航窗口,反复点击同一个节点,即反复显示同一个路径,也会响应DISPID_DOCUMENTCOMPLETE消息,但窗口不被销毁。
void HwDISPID_DOCUMENTCOMPLETE();
void CMyBar::HwDISPID_DOCUMENTCOMPLETE()
{
// DISPID_DOCUMENTCOMPLETE: 完成打开文件夹时收到的消息,注意会收到二次
// 我们在第二次消息时做处理,如果第一次消息时做处理,会导致explorer闪退(原因暂时不明,可能和父窗口句柄有关).
……// 添加条件处理,不满足条件的,则返回。
IWebBrowserApp*pwba;
if(SUCCEEDED(m_pdisp->QueryInterface(IID_IWebBrowserApp, (void**)&pwba)))
{
IServiceProvider*psp;
if(SUCCEEDED(pwba->QueryInterface(IID_IServiceProvider, (void**)&psp)))
{
IShellBrowser*psb;
if(SUCCEEDED(psp->QueryService(SID_STopLevelBrowser,
IID_IShellBrowser,(void**)&psb)))
{
IShellView*psv;
if(SUCCEEDED(psb->QueryActiveShellView(&psv)))
{
psv->GetWindow(&m_hWndShellView);//保存shellview句柄
m_hWndShellViewParent= GetParent(m_hWndShellView);//保存shellview父窗口句柄
IFolderView*pfv;
if(SUCCEEDED(psv->QueryInterface(IID_IFolderView,
(void**)&pfv)))
{
IPersistFolder2*ppf2;
if(SUCCEEDED(pfv->GetFolder(IID_IPersistFolder2,
(void**)&ppf2)))
{
LPITEMIDLISTpidlFolder;
if(SUCCEEDED(ppf2->GetCurFolder(&pidlFolder)))
{
if(SHGetPathFromIDList(pidlFolder, m_CurrDir)) //保存当前窗口路径
{
}
CoTaskMemFree(pidlFolder);
}
ppf2->Release();
}
pfv->Release();
}
psv->Release();
}
psb->Release();
}
psp->Release();
}
pwba->Release();
}
// 窗口切换或新打开一个explorer时,才创建插件窗口,反复显示同一个路径窗口时,不创建,否则会闪退。
// 窗口切换或新打开explorer时,句柄会变化。
if(m_hWndShellViewParent!= m_hOldWndShellViewParent)
{
…….//调用创建插件窗口函数,也可自行添加条件,设定某些路径下才创建显示插件。
m_hOldWndShellViewParent= m_hWndShellViewParent;
}
}
创建插件窗口
在ATL项目中,添加dlg资源,设置style为child,其它属性根据需求进行设定,我都设置为false。
添加成员函数创建窗口。
小提示:如果创建的dlg类,是继承自CDialog,在使用时,先调用一句AFX_MANAGE_STATE(AfxGetStaticModuleState( ));防止出错。
void InitBar();
void CMyBar::InitBar()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
//以m_hWndShellView的父窗口作为父窗口,当切换目录时会自动销毁窗口
m_InfoBar = new CMyBarDlg;
m_InfoBar->Create(IDD_MyBarDlg,CWnd::FromHandle(m_hWndShellViewParent));
//可以调用movewindow调整我们的插件与资源管理器文件列表窗口的位置关系。
CRect ParentRect;
GetWindowRect(m_hWndShellViewParent,ParentRect);
MoveWindow(m_hWndShellView,0,30,abs(ParentRect.Width()),abs(ParentRect.Height()),TRUE);
MoveWindow(m_InfoBar->GetSafeHwnd(),0,0,abs(ParentRect.Width()),30,TRUE);
ExchangeWndProc();
}
使插件响应资源管理器窗口的变化
我们可以通过函数替换的方法,替换窗口过程函数,使得插件窗口可以响应到资源管理器窗口的变化,从而跟随改变窗口大小。
添加成员函数:
voidExchangeWndProc();
voidCHwDocBar::ExchangeWndProc()
{
// 替换窗口过程函数
SetProp(m_hWndShellView,_T("MyBarObj"),m_InfoBar);//保存对话框句柄到文件列表控件的属性表中
WNDPROC oldWndProc =(WNDPROC)::GetWindowLong(m_hWndShellView, GWL_WNDPROC);//获取原窗口过程
SetProp(m_hWndShellView, _T("MyPROPNAME_OLDPROC"),oldWndProc);//保存原窗口过程到文件列表控件的属性表中
SetWindowLong((HWND)m_hWndShellView,GWL_WNDPROC, (LONG)BrowserWndProc);//替换窗口过程
}
实现我们的窗口过程函数:
staticLRESULT CALLBACK BrowserWndProc(HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAMlParam);
LRESULTCALLBACK CHwDocBar::BrowserWndProc(HWND hWnd, //handle to window
UINTuMsg, //message identifier
WPARAMwParam, //first message parameter
LPARAMlParam) //second message parameter
{
LRESULT result = NULL;
WNDPROC pfnOldWndProc = (WNDPROC)::GetProp(hWnd,_T("MyPROPNAME_OLDPROC"));//读出原窗口过程
CHwDocBarDlg *dlg = (CHwDocBarDlg*)::GetProp(hWnd,_T("MyBarObj"));//读出对话框句柄
switch(uMsg)
{
case WM_SIZE:
{
if (dlg != NULL)
{
::MoveWindow(dlg->m_hWnd,0, 0, LOWORD(lParam), 30, TRUE);//控制对话框宽度
}
CallWindowProc(pfnOldWndProc,hWnd, uMsg, wParam, lParam);//调用原窗口过程
}
break;
case WM_WINDOWPOSCHANGED:
case WM_WINDOWPOSCHANGING:
{
LPWINDOWPOS pPos =(LPWINDOWPOS)lParam;
pPos->cy -= 30;
CallWindowProc(pfnOldWndProc,hWnd, uMsg, wParam, (LPARAM)pPos);//调用原窗口过程
}
break;
default:
returnCallWindowProc(pfnOldWndProc, hWnd, uMsg, wParam, lParam);//调用原窗口过程
}
return result;
}
3. 注册
打开我们创建的ATL对象MyBar的rgs文件。
在后面加上BHO的注册信息。
其中的ForceRemove后的一串编号,换成自己插件的CLSID。
HKLM
{
NoRemove SOFTWARE
{
NoRemove Microsoft
{
NoRemove Windows
{
NoRemoveCurrentVersion
{
NoRemoveExplorer
{
NoRemove'Browser Helper Objects'
{
ForceRemove'{1B69C6B6-01E4-443B-8F18-F8DB9AEC6D65}'
}
}
}
}
}
}
}