BHO--嵌入资源管理器窗口

参考了别人提供的方法,自己补充完整了一些,就算作翻译吧。

资源管理器嵌入插件实现说明

一.           核心思想

添加子窗口,使插件窗口与资源管理器的文件列表窗口共同拆分右侧窗口。

二.           技术实现

1.     创建程序

新建ATL程序,去除attributed选项(如果有)。

 

2.     添加方法

添加简单ATL对象MyBar,勾选IobjectWithSite。

.h文件中添加成员变量

CComQIPtr mWebBrowser2;

CComPtrm_spWebBrowser;

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(mWebBrowser2);

              spCPC->FindConnectionPoint(DIID_DWebBrowserEvents2,&m_spWebBrowser);

              //Pass the event handlers to the container

              m_spWebBrowser->Advise(reinterpret_cast(this),&mCookie);

 

              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}'

                                          }

                                   }

                            }

                     }

              }

       }

}

 

 

你可能感兴趣的:(界面)