如何让嵌入的Explorer IShellView可浏览(即触发BrowseObject事件)
我正在我的 Win32 应用程序中“嵌入 Windows 资源管理器”。 (从技术上讲,我正在托管 我的应用程序中文件夹的 ShellView,这就是 Windows 资源管理器所做的)。
问题是该视图永远不会调用 IShellBrowser.BrowseObject。 shell 视图不是要求我导航到新位置(通过 BrowseObject 事件),而是启动 Windows 资源管理器的副本来查看文件夹。
我希望默认的 shell 视图(俗称 DefView)是可浏览的。
示例代码教程
首先我们需要获取 IShellFolder
用于我想要显示的某些文件夹。最简单的文件夹是桌面文件夹,因为有一个 SHGetDesktopFolder API
:
folder: IShellFolder;
SHGetDesktopFolder({out} folder);
接下来我们要求桌面文件夹手我们<一个href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb774834%28v=vs.85%29.aspx" rel="nofollow noreferrer">IShellView:
view: IShellView;
folder.CreateViewObject(Self.Handle, IID_IShellView, {out}view);
现在我们有文件夹的 IShellView (而不是 IContextMenu 或 IExtractIcon),我们现在想要通过调用 IShellView.CreateViewWindow:
hostRect: TRect; //where the view is to display itself
folderSettings: TFolderSettings; //display settings for the view
hwndView: HWND; //the newly created view's window handle
folderSettings.ViewMode := FVM_DETAILS; //details mode please, rather than icon/list/etc
folderSettings.fFlags := 0;
hostRect := Rect(20, 20, 660, 500); //the view can position itself there
view.CreateViewWindow(nil, folderSettings, shellBrowser, {var}hostRect, {out}hView);
view.UIActivate(SVUIA_ACTIVATE_NOFOCUS);
瞧,可识别的 shell 列表视图,显示我的桌面:
包含上下文菜单处理程序:
除了当我单击打开< /强>,而不是向我发送 < code>BrowseObject 事件通过 IShellBrowser
我提供的界面,它会打开一个新窗口:
当我双击时也会发生同样的情况。
我怎样才能让 Microsoft 的 DefView 可以浏览?
更新 ShellBrowser 实现
创建IShellView
时,您必须为其提供一个实现IShellBrowser
的对象。这个 ShellBrowser 对象是视图与托管容器进行通信的方式。
在 15 个方法中,我只仔细查看了 4 个 - 其余的可以返回 E_NOTIMPL
(这当然可能是问题):
{IShellBrowser}
BrowseObject(PCUIDLIST_RELATIVE pidl, UINT wFlags);< /代码>
//通知Windows资源管理器浏览到另一个文件夹。 //这就是我想要的通知! 结果 := BrowseToAnotherFolder(pidl, wFlags);
GetControlWindow(UINT id, HWND *lphwnd);
//获取浏览器控件的窗口句柄。 //由于我没有工具栏、树、状态或进度窗口,因此返回 0 lphwnd := 0; 结果:= S_OK;
SendControlMsg(UINT id, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *pret);
//将控制消息发送到 Windows 中的工具栏或状态栏 //来自 MSDN:实施者须知 // 如果您的 Windows 资源管理器没有这些控件,您可以返回 E_NOTIMPL。 结果:= E_NOTIMPL;
GetViewStateStream(DWORD grfMode, IStream **ppStrm);
//获取可用于存储视图特定状态信息的IStream接口。 结果:= E_NOTIMPL; //我没有流可以给你
TranslateAcceleratorSB(LPMSG lpmsg, WORD wID);
//翻译用于浏览器框架的加速键击键 // 当视图处于活动状态时。 结果:= E_NOTIMPL; //我不会做任何翻译
OnViewWindowActive(IShellView *ppshv);
//当视图窗口或其子窗口之一时由 Shell 视图调用 // windows 获得焦点或变为活动状态。 结果:= S_OK; //我收到通知了,谢谢
QueryActiveShellView(IShellView **ppshv);
//检索当前活动(显示)的 Shell 视图对象。 ppshv := 查看; 结果:= S_OK; //我永远不会再看另一个,你知道的
EnableModelessSB(BOOL fEnable);
//告诉Windows资源管理器启用或禁用其非模式对话框。 结果:= S_OK; //您想启用非模式对话框吗?有趣的。
InsertMenusSB(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths);
//允许容器将其菜单组插入到组合中 // 查看或使用扩展命名空间时显示的菜单。 结果:= E_NOTIMPL; //我没有菜单
RemoveMenusSB(HMENU hmenuShared);
//允许容器删除其任何菜单元素 // 从就地复合菜单中释放所有关联资源。 结果:= E_NOTIMPL; //我没有菜单
SetMenuSB(HMENU hmenuShared, HOLEMENU HolemenuRes, HWND hwndActiveObject);
//在视图窗口中安装复合菜单。 结果:= E_NOTIMPL; //我没有菜单
SetStatusTextSB
//设置并显示有关就地对象的状态文本 //在容器的框架窗口状态栏中。 结果:= E_NOTIMPL; //我没有状态栏
SetToolbarItems(LPTBBUTTONSB lpButtons, UINT nButtons, UINT uFlags);
//将工具栏项添加到 Windows 资源管理器的工具栏。 结果:= E_NOTIMPL; //我没有工具栏
然后就是祖先 IOleWindow
:
GetWindow([out] HWND *phwnd);
//检索参与其中的窗口之一的句柄 // 就地激活(框架、文档、父级或就地对象窗口)。 phwnd := Self.Handle; //这又是你父母的句柄 结果:= S_OK;
`ContextSensitiveHelp([in] BOOL fEnterMode);
//确定是否应进入上下文相关帮助模式 //在就地激活会话期间。 结果:= S_OK; //好的,谢谢,我记下来
IServiceProvider
function TShellBrowser.QueryService(const rsid, iid: TGuid; out Obj): HResult;
var
sb: IShellBrowser;
s: string;
const
SID_SInPlaceBrowser: TGUID = '{1D2AE02B-3655-46CC-B63A-285988153BCA}';
SID_IShellBrowser: TGUID = '{000214E2-0000-0000-C000-000000000046}';
begin
{
This code is executed when you double click a folder.
It's needed to implement inline browsing.
If you double click a folder the default action of IShellBrowser is to open a new Windows Explorer.
To open the folder in the current window you must implement IServiceProvider.
http://blogs.msdn.com/b/ieinternals/archive/2009/12/30/windows-7-web-browser-control-will-not-browse-file-system.aspx
}
Result := E_NOINTERFACE; //Return $E_NOINTERFACE
OutputDebugString(PChar('TShellBrowser.QueryService: '+IIDToString(rsid)));
{
Make a small change to your application to enable the filesystem object to navigate in-place within the WebOC
when running on Windows 7.
To do so, your hosting application will implement the IServiceProvider interface,
and hand back the WebBrowser control’s SID_SShellBrowser when asked for SID_SInPlaceBrowser
}
if IsEqualGUID(rsid, SID_SInPlaceBrowser) then
begin
sb := Self as IShellBrowser;
Pointer(Obj) := Pointer(sb);
sb._AddRef;
Result := S_OK;
end;
end;
奖励阅读
- 前 MSFT Eric Law 将此行为记录为 Windows 7 中的即时更改,以及如何解决该问题。 存档(除非它不起作用)
- fogbit 有同样的问题
- 更多关于如何实现 IServiceProvider 来响应 SID_SInPlaceBrowser
另请参阅
i am "embedding Windows Explorer" in my Win32 application. (Technically i am hosting a ShellView of a folder in my application, which is what Windows Explorer does).
The problem is that the view is never calling IShellBrowser.BrowseObject. Rather than asking me to navigate to a new location (through the BrowseObject event), the shell view is launching a copy of Windows Explorer to view the folder.
i want the default shell view (colloquially known as DefView) to be browsable.
Sample code tutorial
First we need to get the IShellFolder
for some folder that i want to display. The simplest folder to get is the Desktop folder, since there is an SHGetDesktopFolder API
for it:
folder: IShellFolder;
SHGetDesktopFolder({out} folder);
Next we ask the desktop folder to hand us its IShellView:
view: IShellView;
folder.CreateViewObject(Self.Handle, IID_IShellView, {out}view);
Now that we have the IShellView of the folder (as opposed to the IContextMenu or IExtractIcon), we now want to show the shell view by calling IShellView.CreateViewWindow:
hostRect: TRect; //where the view is to display itself
folderSettings: TFolderSettings; //display settings for the view
hwndView: HWND; //the newly created view's window handle
folderSettings.ViewMode := FVM_DETAILS; //details mode please, rather than icon/list/etc
folderSettings.fFlags := 0;
hostRect := Rect(20, 20, 660, 500); //the view can position itself there
view.CreateViewWindow(nil, folderSettings, shellBrowser, {var}hostRect, {out}hView);
view.UIActivate(SVUIA_ACTIVATE_NOFOCUS);
et voila, the recognizable shell listview, showing my desktop:
complete with context menu handlers:
Except that when i click Open, rather than sending me a BrowseObject
event through the IShellBrowser
interface i supplied, it opens a new window:
The same happens when i double-click.
How can i get Microsoft's DefView to be browsable?
Update ShellBrowser implementation
When creating an IShellView
you must give it an object that implements IShellBrowser
. This ShellBrowser
object is how the view communicates back to the hosting container.
Of the 15 methods, i only look carefully at four - the rest can return E_NOTIMPL
(which of course may be the problem):
{IShellBrowser}
BrowseObject(PCUIDLIST_RELATIVE pidl, UINT wFlags);
//Informs Windows Explorer to browse to another folder. //This is the notification i want! Result := BrowseToAnotherFolder(pidl, wFlags);
GetControlWindow(UINT id, HWND *lphwnd);
//Gets the window handle to a browser control. //Since i don't have a toolbar, tree, status or progress windows, return 0 lphwnd := 0; Result := S_OK;
SendControlMsg(UINT id, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *pret);
//Sends control messages to either the toolbar or the status bar in a Windows //From MSDN: Notes to Implementers // If your Windows Explorer does not have these controls, you can return E_NOTIMPL. Result := E_NOTIMPL;
GetViewStateStream(DWORD grfMode, IStream **ppStrm);
//Gets an IStream interface that can be used for storage of view-specific state information. Result := E_NOTIMPL; //i'm don't have a stream to give you
TranslateAcceleratorSB(LPMSG lpmsg, WORD wID);
//Translates accelerator keystrokes intended for the browser's frame // while the view is active. Result := E_NOTIMPL; //i won't be doing any translating
OnViewWindowActive(IShellView *ppshv);
//Called by the Shell view when the view window or one of its child // windows gets the focus or becomes active. Result := S_OK; //i got the notification, thanks
QueryActiveShellView(IShellView **ppshv);
//Retrieves the currently active (displayed) Shell view object. ppshv := view; Result := S_OK; //i would never view another, you know that
EnableModelessSB(BOOL fEnable);
//Tells Windows Explorer to enable or disable its modeless dialog boxes. Result := S_OK; //You want to enable modeless dialog boxes? Interesting.
InsertMenusSB(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths);
//Allows the container to insert its menu groups into the composite // menu that is displayed when an extended namespace is being viewed or used. Result := E_NOTIMPL; //i no has menus
RemoveMenusSB(HMENU hmenuShared);
//Permits the container to remove any of its menu elements // from the in-place composite menu and to free all associated resources. Result := E_NOTIMPL; //i no has menus
SetMenuSB(HMENU hmenuShared, HOLEMENU holemenuRes, HWND hwndActiveObject);
//Installs the composite menu in the view window. Result := E_NOTIMPL; //i no has menus
SetStatusTextSB
//Sets and displays status text about the in-place object //in the container's frame-window status bar. Result := E_NOTIMPL; //i no has status bar
SetToolbarItems(LPTBBUTTONSB lpButtons, UINT nButtons, UINT uFlags);
//Adds toolbar items to Windows Explorer's toolbar. Result := E_NOTIMPL; //i no has toolbar
There's then the ancestor IOleWindow
:
GetWindow([out] HWND *phwnd);
//Retrieves a handle to one of the windows participating in // in-place activation (frame, document, parent, or in-place object window). phwnd := Self.Handle; //here's the handle, again, of your parent Result := S_OK;
`ContextSensitiveHelp([in] BOOL fEnterMode);
//Determines whether context-sensitive help mode should be entered //during an in-place activation session. Result := S_OK; //Ok, thanks, i'll make a note of it
IServiceProvider
function TShellBrowser.QueryService(const rsid, iid: TGuid; out Obj): HResult;
var
sb: IShellBrowser;
s: string;
const
SID_SInPlaceBrowser: TGUID = '{1D2AE02B-3655-46CC-B63A-285988153BCA}';
SID_IShellBrowser: TGUID = '{000214E2-0000-0000-C000-000000000046}';
begin
{
This code is executed when you double click a folder.
It's needed to implement inline browsing.
If you double click a folder the default action of IShellBrowser is to open a new Windows Explorer.
To open the folder in the current window you must implement IServiceProvider.
http://blogs.msdn.com/b/ieinternals/archive/2009/12/30/windows-7-web-browser-control-will-not-browse-file-system.aspx
}
Result := E_NOINTERFACE; //Return $E_NOINTERFACE
OutputDebugString(PChar('TShellBrowser.QueryService: '+IIDToString(rsid)));
{
Make a small change to your application to enable the filesystem object to navigate in-place within the WebOC
when running on Windows 7.
To do so, your hosting application will implement the IServiceProvider interface,
and hand back the WebBrowser control’s SID_SShellBrowser when asked for SID_SInPlaceBrowser
}
if IsEqualGUID(rsid, SID_SInPlaceBrowser) then
begin
sb := Self as IShellBrowser;
Pointer(Obj) := Pointer(sb);
sb._AddRef;
Result := S_OK;
end;
end;
Bonus Reading
- ex-MSFT Eric Law documents this behavior as an instantional change in Windows 7, and how to work around it. archive (Except it doesn't work)
- fogbit having the same problem
- more talk on how to implement IServiceProvider to respond to SID_SInPlaceBrowser
See also
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
在 Vista 或更高版本上,您可以切换到托管 ExplorerBrowser,从中您可以 QI IObjectWithSite 并传递一个使用 SID_SShellBrowser 或 SID_SInPlaceBrowser 等服务实现 IServiceProvier 的对象。
在 XP 上,您可能需要处理源自您的进程的 DDE 消息,因为当时默认的文件夹关联是 DDE。
On Vista or higher you can switch to hosting ExplorerBrowser, from which you can QI IObjectWithSite and pass an object that implements IServiceProvier with services like SID_SShellBrowser or SID_SInPlaceBrowser.
On XP you probably need to handle DDE messages originated from your process as the default folder association is DDE back then.
我用来重现此示例的一些缺失代码。
之后实现
IShellBrowser
接口并获取FShellBrowser
变量。使用
FShellBrowser
中:单击目录只会生成:
此代码的另一个问题是:
如何与资源管理器告知表单已调整大小并且您想要调整托管资源管理器的大小。
Explorer 浏览器具有
SetRect
方法。我同意使用 IExplorerBrowser。
Some missing code I used to reproduce this example.
After that implement the
IShellBrowser
interface and get theFShellBrowser
variable.use
FShellBrowser
in:Clicking a directory only generates:
Another problem with this code is:
How to communicate to Explorer that the form is resized and you want to resize the hosted Explorer.
Explorer browser has the
SetRect
method.I agree use IExplorerBrowser.