如何让嵌入的Explorer IShellView可浏览(即触发BrowseObject事件)

发布于 2024-12-08 17:21:35 字数 8298 浏览 2 评论 0原文

我正在我的 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 (而不是 IContextMenuIExtractIcon),我们现在想要通过调用 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;

奖励阅读


另请参阅

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:

enter image description here

complete with context menu handlers:

enter image description here

Except that when i click Open, rather than sending me a BrowseObject event through the IShellBrowser interface i supplied, it opens a new window:

enter image description here

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


See also

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

娇俏 2024-12-15 17:21:35

在 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.

终难愈 2024-12-15 17:21:35

我用来重现此示例的一些缺失代码。

TForm1 = class(TForm, IShellBrowser)

之后实现IShellBrowser接口并获取FShellBrowser变量。

self.GetInterface(IID_IShellBrowser, FShellBrowser)

使用 FShellBrowser 中:

FShellView.CreateViewWindow(FPreviousView, FFolderSettings, FShellBrowser, FHostRect, FViewHandle)

单击目录只会生成:

OnViewWindowActive

GetControlWindow

此代码的另一个问题是:
如何与资源管理器告知表单已调整大小并且您想要调整托管资源管理器的大小。
Explorer 浏览器具有 SetRect 方法。

我同意使用 IExplorerBrowser。

Some missing code I used to reproduce this example.

TForm1 = class(TForm, IShellBrowser)

After that implement the IShellBrowser interface and get the FShellBrowser variable.

self.GetInterface(IID_IShellBrowser, FShellBrowser)

use FShellBrowser in:

FShellView.CreateViewWindow(FPreviousView, FFolderSettings, FShellBrowser, FHostRect, FViewHandle)

Clicking a directory only generates:

OnViewWindowActive

GetControlWindow

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文