这次说的是如何在自己的NSE中将工具条按钮添加到Explorer中。SEF所包含的RarFS源码中有本文所讲的所有信息的参考实现,以及目前网上所能找到的所有公开和未公开的SFVM_*消息的含义的参数结构。 :)
第一个问题是:什么时候加入按钮。
微软有一个例子:CabView,在老的SDK里面有源码,新的SDK不再提供这个例子。它是自己用ListView实现了一个IShellView,在IShellView::CreateViewWindow函数中用SendControlMsg加入的。对于使用SHCreateShellFolderView(Ex)的程序来说,这个时机是我们得不到的。经试验,如果插入按钮时机不对,有两种可能的后果:
(1) 加入后不显示
(2) 整个工具条被清空
究竟什么时机加入合适呢?答案在IShellFolderViewCB::MessageSFVCB中。
这个接口是NSE和Explorer通讯的唯一合法接口。事件以类似消息的形式传递给NSE,消息名称以SFVM_开头。
微软只公布了很少几条消息,所幸我们需要的几条还都公开了。(有一条消息例外,后面会讲到。)
我们只需要依次处理这几条消息:
(1) SFVM_GETBUTTONINFO
传回按钮的总个数,包括separator。
(2) SFVM_GETBUTTONS
这时,Explorer已经为NSE分配好了各个TBBUTTON所需要的内存,只要顺序填入就可以。
(3) SFVM_INVOKECOMMAND
用户按下按钮,会通过这个消息通知NSE。
最重要的消息是SFVM_GETBUTTONS。这个消息是:
USHORT idCmdFirst = LOWORD(wParam);
USHORT cbtnMax = HIWORD(wParam);
TBBUTTON* ptbbutton = reinterpret_cast<TBBUTTON*>(lParam);
idCmdFirst是Explorer为你的NSE分配的command id范围下界,你的command id必须在0-255之间。每个TBBUTTON的idCommand必须是idCmdFirst+YourCommandID。比如命令IDC_ADDFILE = 1,idCmdFirst = 0x7200,那么idCommand就写成idCmdFirst + IDC_ADDFILE。将来在SFVM_INVOKECOMMAND处理函数中,Explorer传回的是1,而不是0x7201。
按钮的iBitmap和iString两个值的写法和普通的toolbar没什么区别。需要注意的是必须使用TB_ADDBITMAP和TB_ADDSTRING来添加按钮位图和文字。
最后要说的是,如果需要在某一个文件选中/取消选择的时机来修改按钮状态,比如checked/enabled等等,就需要处理SFVM_SELECTIONCHANGED消息。这个消息微软没有公开,它的值为8,wParam含义未知,lParam是一个指向SFVCB_SELECTINFO的指针,该结构定义为:
typedef struct tag_SFVCB_SELECTINFO
{
UINT uOldState; // 0
UINT uNewState; //LVIS_SELECTED, LVIS_FOCUSED,...
LPITEMIDLIST pidl;
} SFVCB_SELECTINFO;
{
UINT uOldState; // 0
UINT uNewState; //LVIS_SELECTED, LVIS_FOCUSED,...
LPITEMIDLIST pidl;
} SFVCB_SELECTINFO;
实际上,这个结构的数据对我们来说意义不大,我们通过IShellView::GetItemObject就可以得到同样的数据,而且内容更丰富。
你会遇到两个问题:
(1) 所有加进去的按钮最初都处于允许状态,即使没有指定TBSTATE_ENABLED也一样。
(2) 无法通过正常的SendControlMsg或者先取得工具条HWND然后SendMessage来改变其允许/禁止状态。你可以改变其checked/unchecked、normal/highlight状态,唯一不能改变的就是允许状态。
这两个问题本质相同。解决方法有两个:
(1) 使用自己的ShellView,然后通过SetToolbarItems来添加按钮;这样,你可以随意改变按钮的各种状态。
(2) 之所以无法修改允许状态,是因为Explorer判断了这些按钮的owner,如果是你添加进去的,就不让改。想办法骗过它!这里已经把原因解释得很清楚了,如果你还不知道怎么实现,就购买SEF的咨询服务吧。 :)))))